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