1 /++
2 A module for programming open graphics shaders.
3 
4 Macros:
5     LREF = <a href="#$1">$1</a>
6     HREF = <a href="$1">$2</a>
7 
8 Authors: $(HREF https://github.com/TodNaz,TodNaz)
9 Copyright: Copyright (c) 2020 - 2021, TodNaz.
10 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
11 +/
12 module tida.shader;
13 
14 import tida.gl;
15 
16 enum
17 {
18     Vertex = 0, /// Vertex shader type
19     Fragment, /// Fragment shader type
20     Program /// Program shader type
21 }
22 
23 /++
24 Whether the shader type is a program.
25 +/
26 template isShaderProgram(int type)
27 {
28     enum isShaderProgram = (type == Program);
29 }
30 
31 /++
32 Whether the shader type is a shader unit.
33 +/
34 template isShaderUnite(int type)
35 {
36     enum isShaderUnite = !isShaderProgram!type;
37 }
38 
39 /++
40 Whether the shader type is valid to use.
41 +/
42 template isValidTypeShader(int type)
43 {
44     enum isValidTypeShader = (type < 3);
45 }
46 
47 /++
48 Converts types into a convenient representation for the open graphics library.
49 +/
50 template glTypeShader(int type)
51 {
52     static assert(isValidTypeShader!type, "Invalid type shader!");
53 
54     static if (type == Vertex)
55         enum glTypeShader = GL_VERTEX_SHADER;
56     else
57     static if (type == Fragment)
58         enum glTypeShader = GL_FRAGMENT_SHADER;
59 }
60 
61 private string formatError(string error) @safe pure
62 {
63     import std.array : replace;
64 
65     error = error.replace("error","\x1b[1;91merror\x1b[0m");
66 
67     return error;
68 }
69 
70 /++
71 A description object for a shader program or program unit.
72 +/
73 class Shader(int type)
74 if (isValidTypeShader!type)
75 {
76     import std..string : toStringz;
77     import std.conv : to;
78 
79     enum Type = type;
80 
81 private:
82     uint glid;
83 
84     static if (isShaderProgram!type)
85     {
86         Shader!Vertex vertex;
87         Shader!Fragment fragment;
88     } 
89 
90 public @trusted:
91     this()
92     {
93         static if (isShaderUnite!type)
94             glid = glCreateShader(glTypeShader!type);
95         else
96             glid = glCreateProgram();
97     }
98 
99     /// Shader identificator
100     @property uint id()
101     {
102         return glid;
103     }
104 
105     /++
106     Binds the shader source directly to the shader itself into memory by compiling it.
107 
108     Params:
109         source = Shader source code.
110 
111     Throws: `Exception` If the shader contains errors.
112                         All errors are displayed in a preformatted message.
113     +/
114     static if (isShaderUnite!type)
115     Shader!type bindSource(string source)
116     {
117         const int len = cast(const(int)) source.length;
118         glShaderSource(glid, 1, [source.ptr].ptr, &len);
119         glCompileShader(glid);
120 
121         char[] error;
122         int result;
123         int lenLog;
124 
125         glGetShaderiv(glid, GL_COMPILE_STATUS, &result);
126         if (!result)
127         {
128             glGetShaderiv(glid, GL_INFO_LOG_LENGTH, &lenLog);
129             error = new char[](lenLog);
130             glGetShaderInfoLog(glid, lenLog, null, error.ptr);
131 
132             throw new Exception("Shader compile error:\n" ~ error.to!string.formatError);
133         }
134 
135         return this;
136     }
137 
138     /++
139     Binds the source code to the shader directly from the specified file.
140 
141     Params:
142         path = The path to the shader source code.
143 
144     Throws: `Exception` if the file is not found.
145     +/
146     static if (isShaderUnite!type)
147     Shader!type fromFile(string path)
148     {
149         import std.file : readText;
150 
151         return bindSource(readText(path));
152     }
153 
154     /++
155     Binds a shader to the program.
156 
157     Params:
158         shader = Fragment/Vertex shader.
159     +/
160     static if (isShaderProgram!type)
161     Shader!type attach(T)(T shader)
162     if (isShaderUnite!(T.Type) && (shader.Type == Vertex || shader.Type == Fragment))
163     {
164         static if (T.Type == Vertex)
165             vertex = shader;
166         else
167         static if (T.Type == Fragment)
168             fragment = shader;
169 
170         glAttachShader(glid, shader.id);
171         return this;
172     }
173 
174     /++
175     Links two compiled shaders into a program.
176 
177     Throws: `Exception` If linking two shaders did not succeed. The error
178                         will be written directly in the exception message.
179     +/
180     static if (isShaderProgram!type)
181     Shader!type link()
182     {
183         import std.conv : to;
184 
185         glLinkProgram(glid);
186 
187         int status;
188         glGetProgramiv(glid, GL_LINK_STATUS, &status);
189         if (!status)
190         {
191             int lenLog;
192             char[] error;
193             glGetProgramiv(glid, GL_INFO_LOG_LENGTH, &lenLog);
194             error = new char[](lenLog);
195 
196             glGetProgramInfoLog(glid, lenLog, null, error.ptr);
197 
198             throw new Exception(error.to!string.formatError);
199         }
200         
201         return this;
202     }
203 
204     /++
205     Uses a shader program.
206     +/
207     static if (isShaderProgram!type)
208     void using()
209     {
210         glUseProgram(glid);
211     }
212 
213     /++
214     Associates a generic vertex attribute index with a named attribute variable
215 
216     Params:
217         index = Specifies the index of the generic vertex attribute to be bound.
218         name =  Specifies a null terminated string containing the name of the vertex shader
219                 attribute variable to which index is to be bound.
220 
221     See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindAttribLocation.xhtml, glBindAttribLocation)
222     +/
223     static if (isShaderProgram!Type)
224     void bindAttribLocation(uint index, string name)
225     {
226         glBindAttribLocation(glid, index, name.ptr);
227     }
228 
229     /++
230     Enable or disable a generic vertex attribute array
231 
232     Params:
233         name = Vertex attribute name.
234 
235     See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray)
236     +/
237     static if (isShaderProgram!Type)
238     void enableVertex(string name)
239     {
240         glEnableVertexAttribArray(getAttribLocation(name));
241     }
242 
243     /++
244     Enable or disable a generic vertex attribute array
245 
246     Params:
247         id = Vertex identificator.
248 
249     See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray)
250     +/
251     static if (isShaderProgram!Type)
252     void enableVertex(uint id)
253     {
254         glEnableVertexAttribArray(id);
255     }
256 
257     /++
258     Enable or disable a generic vertex attribute array
259 
260     Params:
261         name = Vertex attribute name.
262 
263     See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray)
264     +/
265     static if (isShaderProgram!Type)
266     void disableVertex(string name)
267     {
268         glDisableVertexAttribArray(getAttribLocation(name));
269     }
270 
271     /++
272     Enable or disable a generic vertex attribute array
273 
274     Params:
275         id = Vertex identificator.
276 
277     See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray)
278     +/
279     static if (isShaderProgram!Type)
280     void disableVertex(uint id)
281     {
282         glDisableVertexAttribArray(id);
283     }
284 
285     /++
286     Returns the location of an attribute variable
287 
288     Params:
289         name =  Points to a null terminated string containing the name of the
290                 attribute variable whose location is to be queried.
291 
292     See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetAttribLocation.xhtml, glGetAttribLocation)
293     +/
294     static if(isShaderProgram!Type)
295     int getAttribLocation(string name)
296     {
297         return glGetAttribLocation(glid, name.ptr);
298     }
299 
300     import tida.color, tida.vector;
301 
302     /++
303     Return uniform identificator (location)
304 
305     Params:
306         name = Uniform name.
307     +/
308     static if (isShaderProgram!Type)
309     uint getUniformLocation(string name)
310     {
311         return glGetUniformLocation(glid, name.ptr);
312     }
313 
314     /++
315         Sets the color of a 4D vector type uniform.
316 
317         Params:
318             name = The name of the uniform variable.
319             color = Color structure.
320     +/
321     static if (isShaderProgram!Type)
322     void setUniform(string name, Color!ubyte color)
323     {
324         auto uid = glGetUniformLocation(glid, name.ptr);
325         glUniform4f(uid, color.rf, color.gf, color.bf, color.af);
326     }
327 
328     /++
329         Sends a float value to the uniform.
330 
331         Params:
332             name = The name of the uniform variable.
333             value = Variable value.
334     +/
335     static if (isShaderProgram!Type)
336     void setUniform(string name, float value)
337     {
338         auto uid = glGetUniformLocation(glid, name.ptr);
339         glUniform1f(uid, value);
340     }
341 
342     /++
343         Passes a 4-by-4 variable matrix to the uniform.
344 
345         Params:
346             name = The name of the uniform variable.
347             value = Matrix.
348     +/
349     static if (isShaderProgram!Type)
350     void setUniform(string name, float[4][4] value)
351     {
352         auto uid = glGetUniformLocation(glid, name.ptr);
353         glUniformMatrix4fv(uid, 1, false, value[0].ptr);
354     }
355 
356     /++
357         Passes a 3-by-3 variable matrix to the uniform.
358 
359         Params:
360             name= The name of the uniform variable.
361             value = Matrix.
362     +/
363     static if (isShaderProgram!Type)
364     void setUniform(string name, float[3][3] value)
365     {
366         auto uid = glGetUniformLocation(glid, name.ptr);
367         glUniformMatrix3fv(uid, 1, false, cast(const(float)*) value);
368     }
369 
370     ///
371     static if (isShaderProgram!Type)
372     void setUniformMat(int Type)(string name, float* matPTR)
373     {
374         auto uid = glGetUniformLocation(glid, name.ptr);
375 
376         static if (Type == 4)
377         {
378             glUniformMatrix4fP(uid, matPTR);
379         }else
380         static if (Type == 3)
381         {
382             glUniformMatrix3fP(uid, matPTR);
383         }
384     }
385 
386     /// Returns the number of variable uniforms in the program.
387     static if (isShaderProgram!Type)
388     uint lengthUniforms()
389     {
390         int len;
391         glGetProgramiv(glid, GL_ACTIVE_UNIFORMS, &len);
392 
393         return len;
394     }
395 
396     /// Destroys the structure of a shader or program.
397     void destroy()
398     {
399         if(glid != 0)
400         {
401             static if (isShaderProgram!Type)
402             {
403                 glDeleteProgram(glid);
404             }else
405             static if (isShaderUnite!Type)
406             {
407                 glDeleteShader(glid);
408             }
409 
410             glid = 0;
411         }
412     }
413 
414     ~this()
415     {
416         destroy();
417     }
418 }