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