1 module hoekjed.kern.verver;
2 import bindbc.opengl;
3 import hoekjed.kern;
4 import std.array : replace;
5 import std.conv;
6 
7 class VerverFout : Exception {
8 	this(string melding) {
9 		super("Fout in Verver:\n" ~ melding);
10 	}
11 }
12 
13 class Verver {
14 	public struct BronPaar{
15 		string hoekV, snipperV;
16 	}
17 
18 	public static Verver[BronPaar] ververs;
19 	static Verver huidig = null;
20 
21 	HoekVerver hoekV;
22 	SnipperVerver snipperV;
23 	protected uint verwijzing;
24 
25 	public static immutable Vec!4 plaatsvervangerkleur = {
26 		[250.0 / 255.0, 176.0 / 255.0, 22.0 / 255.0, 1]
27 	};
28 
29 	@property public static Verver plaatsvervanger() {
30 		static Verver voorbeeld;
31 		if (voorbeeld is null)
32 			voorbeeld = Verver.laad(kleur_hoekverver, kleur_snipperverver);
33 		voorbeeld.zetUniform("kleur", plaatsvervangerkleur);
34 		return voorbeeld;
35 	}
36 
37 	final void gebruik() {
38 		if (huidig is this)
39 			return;
40 		glUseProgram(verwijzing);
41 		huidig = this;
42 	}
43 
44 	private this(){}
45 
46 	private this(HoekVerver hoekV, SnipperVerver snipperV) {
47 		this.hoekV = hoekV;
48 		this.snipperV = snipperV;
49 		this.verwijzing = glCreateProgram();
50 		glAttachShader(verwijzing, hoekV.verwijzing);
51 		glAttachShader(verwijzing, snipperV.verwijzing);
52 		glLinkProgram(verwijzing);
53 
54 		int volbracht;
55 		glGetProgramiv(verwijzing, GL_LINK_STATUS, &volbracht);
56 		if (volbracht == 0)
57 			throw new VerverFout(
58 					"Kon Verver " ~ verwijzing.to!string ~ " niet samenstellen:\n" ~ krijg_foutmelding());
59 	}
60 
61 	/// Laadt Ververs met gegeven verversbestanden. Hergebruikt (deel)ververs indien mogelijk.
62 	public static Verver laad(string hoekV, string snipperV){
63 		Verver verver = Verver.ververs.get(BronPaar(hoekV, snipperV), null);
64 		if(verver is null){
65 			HoekVerver hV = HoekVerver.ververs.get(hoekV, new HoekVerver(hoekV));
66 			SnipperVerver sV = SnipperVerver.ververs.get(snipperV, new SnipperVerver(snipperV));
67 			verver = new Verver(hV, sV);
68 			Verver.ververs[BronPaar(hoekV, snipperV)] = verver;
69 		}
70 		return verver;
71 	}
72 
73 	void zetUniform(Zicht zicht) {
74 		this.zetUniform("projectieM", zicht.projectieM);
75 		this.zetUniform("zichtM", zicht.zichtM);
76 	}
77 
78 	void zetUniform(V : Mat!(L, 1, S), uint L, S)(string naam, V waarde)
79 			if (L >= 1 && L <= 4) { // zet Vec
80 		const int uniformplek = glGetUniformLocation(verwijzing, naam.ptr);
81 		if (uniformplek == -1)
82 			return foutmelding_ontbrekende_uniform(naam);
83 
84 		enum string waardes = "waarde.x" ~ (L == 1 ? "" : ",waarde.y" ~ (L == 2
85 					? "" : ",waarde.z" ~ (L == 3 ? "" : ",waarde.w")));
86 		enum string soort = is(S == uint) ? "ui" : (is(S == int)
87 					? "i" : (is(S == float) ? "f" : (is(S == double) ? "d" : "")));
88 		static assert(soort != "", "Soort " ~ S ~ " niet ondersteund voor zetUniform.");
89 		mixin("glProgramUniform" ~ L.to!string ~ soort ~ "(verwijzing, uniformplek, " ~ waardes ~ ");");
90 	}
91 
92 	void zetUniform(V : Mat!(L, 1, S)[], uint L, S)(string naam, V waarde)
93 			if (L >= 1 && L <= 4) { // zet Vec[]
94 		const int uniformplek = glGetUniformLocation(verwijzing, naam.ptr);
95 		if (uniformplek == -1)
96 			foutmelding_ontbrekende_uniform(naam);
97 
98 		enum string soort = is(S == uint) ? "ui" : (is(S == int)
99 					? "i" : (is(S == float) ? "f" : (is(S == double) ? "d" : "")));
100 		static assert(soort != "", "Soort " ~ S ~ " niet ondersteund voor zetUniform.");
101 		mixin("glProgramUniform" ~ L.to!string ~ soort
102 				~ "v(verwijzing, uniformplek, cast(uint) waarde.length, cast(" ~ S.stringof ~ "*) waarde.ptr);");
103 	}
104 
105 	void zetUniform(V : Mat!(R, K, nauwkeurigheid), uint R, uint K)(string naam, V waarde)
106 			if (R > 1 && R <= 4 && K > 1 && K <= 4) { // Zet Mat
107 		const int uniformplek = glGetUniformLocation(verwijzing, naam.ptr);
108 		if (uniformplek == -1)
109 			return foutmelding_ontbrekende_uniform(naam);
110 
111 		mixin("glProgramUniformMatrix" ~ (R == K ? K.to!string : (K.to!string ~ "x" ~ R.to!string)) ~ (
112 				is(nauwkeurigheid == float) ? "f" : "d") ~ "v(verwijzing, uniformplek, 1, true, waarde[0].ptr);");
113 	}
114 
115 	void zetUniform(V : Mat!(R, K, nauwkeurigheid)[], uint R, uint K)(string naam, V waarde)
116 			if (R > 1 && R <= 4 && K > 1 && K <= 4) { // Zet Mat[]
117 		const int uniformplek = glGetUniformLocation(verwijzing, naam.ptr);
118 		if (uniformplek == -1)
119 			return foutmelding_ontbrekende_uniform(naam);
120 
121 		mixin("glProgramUniformMatrix" ~ (R == K ? K.to!string : (K.to!string ~ "x" ~ R.to!string)) ~ (
122 				is(nauwkeurigheid == float)
123 				? "f" : "d") ~ "v(verwijzing, uniformplek, waarde.length, true, waarde.ptr);");
124 	}
125 
126 	private string krijg_foutmelding() {
127 		int lengte;
128 		glGetProgramiv(this.verwijzing, GL_INFO_LOG_LENGTH, &lengte);
129 		char[] melding = new char[lengte];
130 		glGetProgramInfoLog(this.verwijzing, lengte, null, melding.ptr);
131 		return cast(string) melding.idup;
132 	}
133 
134 	private void foutmelding_ontbrekende_uniform(string naam) {
135 		import std.stdio;
136 
137 		stderr.writeln(
138 				"Verver " ~ verwijzing.to!string ~ " kon uniform " ~ naam
139 				~ " niet vinden.\n" ~ krijg_foutmelding());
140 	}
141 
142 	public static string kleur_hoekverver = `
143 #version 460
144 
145 layout(location=0)in vec3 h_plek;
146 layout(location=1)in vec3 h_normaal;
147 layout(location=2)in vec2 h_beeldplek;
148 
149 uniform mat4 projectieM;
150 uniform mat4 zichtM;
151 uniform mat4 tekenM;
152 
153 out vec4 gl_Position;
154 
155 void main(){
156 	gl_Position = projectieM * zichtM * tekenM * vec4(h_plek, 1.0);
157 }
158 `;
159 
160 	public static string kleur_snipperverver = `
161 #version 460
162 
163 uniform vec4 kleur;
164 
165 out vec4 u_kleur;
166 
167 void main(){
168 	u_kleur = kleur;
169 }
170 `;
171 }
172 
173 alias HoekVerver = DeelVerver!GL_VERTEX_SHADER;
174 alias SnipperVerver = DeelVerver!GL_FRAGMENT_SHADER;
175 
176 class DeelVerver(uint soort) {
177 	protected uint verwijzing;
178 
179 	static DeelVerver!(soort)[string] ververs;
180 
181 	private string krijg_foutmelding() {
182 		int lengte;
183 		glGetShaderiv(this.verwijzing, GL_INFO_LOG_LENGTH, &lengte);
184 		char[] melding = new char[lengte];
185 		glGetShaderInfoLog(this.verwijzing, lengte, null, &melding[0]);
186 		return cast(string) melding.idup;
187 	}
188 
189 	this(string bestand) {
190 		import std.file : readText, exists;
191 
192 		this.verwijzing = glCreateShader(soort);
193 		string bron;
194 		if (exists(bestand)) // Gegeven bestand is een verwijzing naar een bestand met verfinhoud.
195 			bron = readText(bestand);
196 		else // Gegeven bestand is verfinhoud.
197 			bron = bestand;
198 		bron = bron.replace("nauwkeurigheid", nauwkeurigheid.stringof);
199 		static if (is(nauwkeurigheid == double)) {
200 			bron = bron.replace(" vec", " dvec");
201 			bron = bron.replace(" mat", " dmat");
202 		}
203 		auto p = bron.ptr;
204 		glShaderSource(verwijzing, 1, &p, null);
205 		glCompileShader(verwijzing);
206 
207 		int volbracht;
208 		glGetShaderiv(verwijzing, GL_COMPILE_STATUS, &volbracht);
209 		if (volbracht == 0)
210 			throw new VerverFout("Kon DeelVerver " ~ verwijzing.to!string ~ " niet bouwen:\n" ~ cast(
211 					string) krijg_foutmelding());
212 
213 		this.ververs[bestand] = this;
214 	}
215 }