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