1 module prova.graphics.shaderprograms.shaderprogram;
2 
3 import prova.graphics,
4        prova.math,
5        std.conv,
6        std.file,
7        std.stdio,
8        std..string;
9 
10 ///
11 enum DrawMode : uint {
12   POINTS = 0,
13   LINES = 1,
14   LINE_LOOP = 2,
15   LINE_STRIP = 3,
16   TRIANGLES = 4,
17   TRIANGLE_STRIP = 5,
18   TRIANGLE_FAN = 6
19 }
20 
21 ///
22 class ShaderProgram
23 {
24   private static uint currentId = -1;
25   private uint[] shaders;
26   private int _id;
27 
28   ///
29   this()
30   {
31     _id = glCreateProgram();
32   }
33 
34   ///
35   final @property int id()
36   {
37     return _id;
38   }
39 
40   private void useProgram()
41   {
42     if(currentId == _id)
43       return;
44 
45     currentId = _id;
46     glUseProgram(_id);
47   }
48 
49   ///
50   final uint getAttribute(string name)
51   {
52     const uint location = glGetAttribLocation(_id, toStringz(name));
53 
54     if(location == -1)
55       throw new Exception("Couldn't find attribute: " ~ name);
56 
57     return location;
58   }
59 
60   ///
61   final uint getUniform(string name)
62   {
63     const uint location = glGetUniformLocation(_id, toStringz(name));
64 
65     if(location == -1)
66       throw new Exception("Couldn't find uniform: " ~ name);
67 
68     return location;
69   }
70 
71   ///
72   final void setVector2(string name, Vector2 vector)
73   {
74     useProgram();
75 
76     const uint location = getUniform(name);
77     glUniform2f(location, vector.x, vector.y);
78   }
79 
80   ///
81   final void setVector3(string name, Vector3 vector)
82   {
83     useProgram();
84 
85     const uint location = getUniform(name);
86     glUniform3f(location, vector.x, vector.y, vector.z);
87   }
88 
89   ///
90   final void setVector4(string name, Vector4 vector)
91   {
92     useProgram();
93 
94     const uint location = getUniform(name);
95     glUniform4f(location, vector.x, vector.y, vector.z, vector.w);
96   }
97 
98   ///
99   final void setVector4(string name, Rect rect)
100   {
101     useProgram();
102 
103     const uint location = getUniform(name);
104     glUniform4f(location, rect.left, rect.top, rect.width, rect.height);
105   }
106 
107   ///
108   final void setVector4(string name, Color color)
109   {
110     useProgram();
111 
112     const uint location = getUniform(name);
113     glUniform4f(location, color.r, color.g, color.b, color.a);
114   }
115 
116   ///
117   final void setMatrix(string name, Matrix matrix)
118   {
119     useProgram();
120 
121     const uint location = getUniform(name);
122     glUniformMatrix4fv(location, 1, true, matrix.array[0].ptr);
123   }
124 
125   ///
126   final void setTexture(int sampler, Texture texture)
127   {
128     useProgram();
129 
130     glActiveTexture(GL_TEXTURE0 + sampler);
131     texture.bind();
132   }
133 
134   ///
135   final void setTexture(int sampler, uint texture)
136   {
137     useProgram();
138 
139     glActiveTexture(GL_TEXTURE0 + sampler);
140     Texture.bind(texture);
141   }
142 
143   ///
144   final void setTexture(string name, Texture texture)
145   {
146     setTexture(name, texture.id);
147   }
148 
149   ///
150   final void setTexture(string name, uint texture)
151   {
152     useProgram();
153 
154     const uint location = getUniform(name);
155     glActiveTexture(location);
156     Texture.bind(texture);
157   }
158 
159   ///
160   final void drawMesh(Mesh mesh, RenderTarget target, DrawMode mode)
161   {
162     target.bindFrameBuffer();
163     useProgram();
164 
165     glBindVertexArray(mesh.VAO);
166     glDrawElements(mode, mesh.indexCount, GL_UNSIGNED_INT, null);
167   }
168 
169   ///
170   final void drawMesh(Mesh mesh, uint frameBufferId, DrawMode mode)
171   {
172     RenderTarget.bindFrameBuffer(frameBufferId);
173     useProgram();
174 
175     glBindVertexArray(mesh.VAO);
176     glDrawElements(mode, mesh.indexCount, GL_UNSIGNED_INT, null);
177   }
178 
179   ///
180   final void loadVertexShader(string sourceFile)
181   {
182     loadShader(GL_VERTEX_SHADER, sourceFile);
183   }
184 
185   ///
186   final void loadFragmentShader(string sourceFile)
187   {
188     loadShader(GL_FRAGMENT_SHADER, sourceFile);
189   }
190 
191   ///
192   final void attachVertexShader(string source)
193   {
194     compileShader(GL_VERTEX_SHADER, source);
195   }
196 
197   ///
198   final void attachFragmentShader(string source)
199   {
200     compileShader(GL_FRAGMENT_SHADER, source);
201   }
202 
203   ///
204   final void link()
205   {
206     glLinkProgram(_id);
207 
208     //Check for errors
209     int programSuccess;
210     glGetProgramiv(_id, GL_LINK_STATUS, &programSuccess);
211 
212     if(programSuccess == true)
213       return;
214 
215     printProgramLog();
216     throw new Exception("Error linking program");
217   }
218 
219   ///
220   final void loadShader(uint shaderType, string sourceFile)
221   {
222     string contents = readText(sourceFile);
223 
224     compileShader(shaderType, contents);
225   }
226 
227   private void compileShader(uint shaderType, string source)
228   {
229     uint shader = glCreateShader(shaderType);
230 
231     const char*[] shaderSource = [toStringz(source)];
232 
233     glShaderSource(shader, 1, shaderSource.ptr, null);
234     glCompileShader(shader);
235 
236     int successfulCompilation;
237     glGetShaderiv(shader, GL_COMPILE_STATUS, &successfulCompilation);
238 
239     if(successfulCompilation != true) {
240       printShaderLog(shader);
241       throw new Exception("Unable to compile shader");
242     }
243 
244     glAttachShader(_id, shader);
245 
246     shaders ~= shader;
247   }
248 
249   ///
250   final void printProgramLog()
251   {
252     int infoLogLength = 0;
253     int maxLength = 0;
254 
255     glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &maxLength);
256 
257     char[] infoLog;
258     infoLog.length = maxLength;
259 
260     glGetProgramInfoLog(_id, maxLength, &infoLogLength, infoLog.ptr);
261 
262     if(infoLogLength > 0)
263       writeln(infoLog);
264   }
265 
266   ///
267   final void printShaderLog(uint shader)
268   {
269     int infoLogLength = 0;
270     int maxLength = 0;
271     
272     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
273     
274     char[] infoLog;
275     infoLog.length = maxLength;
276 
277     glGetShaderInfoLog(shader, maxLength, &infoLogLength, infoLog.ptr);
278 
279     if(infoLogLength > 0)
280       writeln(infoLog);
281   }
282 
283   ~this()
284   {
285     foreach(uint shader; shaders) {
286       glDetachShader(_id, shader);
287       glDeleteShader(shader);
288     }
289 
290     glDeleteProgram(_id);
291   }
292 }