1 // This file belongs to the "MiniCore" game engine.
2 // Copyright (C) 2012 Jussi Lind <jussi.lind@iki.fi>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 // MA 02110-1301, USA.
18 //
19
20 #include "mcglshaderprogram.hh"
21 #include "mcglscene.hh"
22
23 #ifdef __MC_GLES__
24 #include "mcshadersGLES.hh"
25 #elif defined(__MC_GL30__)
26 #include "mcshaders30.hh"
27 #else
28 #include "mcshaders.hh"
29 #endif
30
31 #include <MCLogger>
32 #include <MCTrigonom>
33
34 #include <cassert>
35 #include <exception>
36
37 MCGLShaderProgram * MCGLShaderProgram::m_activeProgram = nullptr;
38
39 std::vector<MCGLShaderProgram *> MCGLShaderProgram::m_programStack;
40
MCGLShaderProgram()41 MCGLShaderProgram::MCGLShaderProgram()
42 : m_scene(MCGLScene::instance())
43 , m_viewProjectionMatrixPending(false)
44 , m_viewMatrixPending(false)
45 , m_diffuseLightPending(false)
46 , m_specularLightPending(false)
47 , m_ambientLightPending(false)
48 {
49 #ifdef __MC_QOPENGLFUNCTIONS__
50 initializeOpenGLFunctions();
51 #endif
52 initUniformNameMap();
53
54 m_program = glCreateProgram();
55 m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
56 m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
57 }
58
MCGLShaderProgram(const std::string & vertexShaderSource,const std::string & fragmentShaderSource)59 MCGLShaderProgram::MCGLShaderProgram(
60 const std::string & vertexShaderSource, const std::string & fragmentShaderSource)
61 : m_scene(MCGLScene::instance())
62 , m_viewProjectionMatrixPending(false)
63 , m_viewMatrixPending(false)
64 , m_diffuseLightPending(false)
65 , m_specularLightPending(false)
66 , m_ambientLightPending(false)
67 {
68 #ifdef __MC_QOPENGLFUNCTIONS__
69 initializeOpenGLFunctions();
70 #endif
71 initUniformNameMap();
72
73 m_program = glCreateProgram();
74 m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
75 m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
76
77 addVertexShaderFromSource(vertexShaderSource);
78 addFragmentShaderFromSource(fragmentShaderSource);
79 link();
80 }
81
initUniformNameMap()82 void MCGLShaderProgram::initUniformNameMap()
83 {
84 // Map uniform enums to uniform names used in the shaders
85 m_uniforms[AmbientLightColor] = "ac";
86 m_uniforms[Camera] = "camera";
87 m_uniforms[Color] = "color";
88 m_uniforms[DiffuseLightDir] = "dd";
89 m_uniforms[DiffuseLightColor] = "dc";
90 m_uniforms[FadeValue] = "fade";
91 m_uniforms[MaterialDiffuseCoeff] = "dCoeff";
92 m_uniforms[MaterialSpecularCoeff] = "sCoeff";
93 m_uniforms[MaterialTex0] = "tex0";
94 m_uniforms[MaterialTex1] = "tex1";
95 m_uniforms[MaterialTex2] = "tex2";
96 m_uniforms[Model] = "model";
97 m_uniforms[Scale] = "scale";
98 m_uniforms[SpecularLightDir] = "sd";
99 m_uniforms[SpecularLightColor] = "sc";
100 m_uniforms[UserData1] = "userData1";
101 m_uniforms[UserData2] = "userData2";
102 m_uniforms[ViewProjection] = "vp";
103 m_uniforms[View] = "v";
104 }
105
~MCGLShaderProgram()106 MCGLShaderProgram::~MCGLShaderProgram()
107 {
108 glDeleteProgram(m_program);
109 glDeleteShader(m_vertexShader);
110 glDeleteShader(m_fragmentShader);
111 }
112
getUniformLocation(Uniform uniform)113 int MCGLShaderProgram::getUniformLocation(Uniform uniform)
114 {
115 return m_uniformLocationHash[uniform];
116 }
117
initUniformLocationCache()118 void MCGLShaderProgram::initUniformLocationCache()
119 {
120 assert(isLinked());
121
122 auto iter = m_uniforms.begin();
123 while (iter != m_uniforms.end())
124 {
125 m_uniformLocationHash[iter->first] = glGetUniformLocation(m_program, iter->second.c_str());
126 iter++;
127 }
128 }
129
bind()130 void MCGLShaderProgram::bind()
131 {
132 MCGLShaderProgram::m_activeProgram = this;
133 glUseProgram(m_program);
134
135 setPendingAmbientLight();
136 setPendingDiffuseLight();
137 setPendingSpecularLight();
138 setPendingViewMatrix();
139 setPendingViewProjectionMatrix();
140 }
141
release()142 void MCGLShaderProgram::release()
143 {
144 MCGLShaderProgram::m_activeProgram = nullptr;
145 }
146
isBound() const147 bool MCGLShaderProgram::isBound() const
148 {
149 return MCGLShaderProgram::m_activeProgram == this;
150 }
151
link()152 void MCGLShaderProgram::link()
153 {
154 glLinkProgram(m_program);
155 assert(isLinked());
156
157 initUniformLocationCache();
158 m_scene.addShaderProgram(*this);
159
160 bindTextureUnit(0, MaterialTex0);
161 bindTextureUnit(1, MaterialTex1);
162 bindTextureUnit(2, MaterialTex2);
163 }
164
isLinked()165 bool MCGLShaderProgram::isLinked()
166 {
167 GLint status = GL_FALSE;
168 glGetProgramiv(m_program, GL_LINK_STATUS, &status);
169 return status == GL_TRUE;
170 }
171
getShaderLog(GLuint obj)172 std::string MCGLShaderProgram::getShaderLog(GLuint obj)
173 {
174 int logLength = 0;
175 int charsWritten = 0;
176 char * rawLog;
177
178 glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &logLength);
179
180 if (logLength > 0)
181 {
182 rawLog = (char *)malloc(logLength);
183 glGetShaderInfoLog(obj, logLength, &charsWritten, rawLog);
184 std::string log = rawLog;
185 free(rawLog);
186 return log;
187 }
188
189 return "";
190 }
191
addVertexShaderFromSource(const std::string & source)192 bool MCGLShaderProgram::addVertexShaderFromSource(const std::string & source)
193 {
194 GLint len = static_cast<GLint>(source.length());
195 const GLchar * data = source.c_str();
196 glShaderSource(m_vertexShader, 1, &data, &len);
197 glCompileShader(m_vertexShader);
198
199 GLint compiled = GL_FALSE;
200 glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, &compiled);
201 if (!compiled)
202 {
203 throw std::runtime_error("Compiling a vertex shader failed.\n" + getShaderLog(m_vertexShader) + "\n" + source);
204 }
205
206 glBindAttribLocation(m_program, MCGLShaderProgram::VAL_Vertex, "inVertex");
207 glBindAttribLocation(m_program, MCGLShaderProgram::VAL_Normal, "inNormal");
208 glBindAttribLocation(m_program, MCGLShaderProgram::VAL_TexCoords, "inTexCoord");
209 glBindAttribLocation(m_program, MCGLShaderProgram::VAL_Color, "inColor");
210
211 glAttachShader(m_program, m_vertexShader);
212
213 return true;
214 }
215
addFragmentShaderFromSource(const std::string & source)216 bool MCGLShaderProgram::addFragmentShaderFromSource(const std::string & source)
217 {
218 GLint len = static_cast<GLint>(source.length());
219 const GLchar * data = source.c_str();
220 glShaderSource(m_fragmentShader, 1, &data, &len);
221 glCompileShader(m_fragmentShader);
222
223 GLint compiled = GL_FALSE;
224 glGetShaderiv(m_fragmentShader, GL_COMPILE_STATUS, &compiled);
225 if (!compiled)
226 {
227 throw std::runtime_error("Compiling a fragment shader failed.\n" + getShaderLog(m_fragmentShader) + "\n" + source);
228 }
229
230 glAttachShader(m_program, m_fragmentShader);
231
232 return true;
233 }
234
getDefaultVertexShaderSource()235 const char * MCGLShaderProgram::getDefaultVertexShaderSource()
236 {
237 return MCDefaultVsh;
238 }
239
getDefaultSpecularVertexShaderSource()240 const char * MCGLShaderProgram::getDefaultSpecularVertexShaderSource()
241 {
242 return MCDefaultVshSpecular;
243 }
244
getDefaultFragmentShaderSource()245 const char * MCGLShaderProgram::getDefaultFragmentShaderSource()
246 {
247 return MCDefaultFsh;
248 }
249
getDefaultShadowVertexShaderSource()250 const char * MCGLShaderProgram::getDefaultShadowVertexShaderSource()
251 {
252 return MCDefaultShadowVsh;
253 }
254
getDefaultShadowFragmentShaderSource()255 const char * MCGLShaderProgram::getDefaultShadowFragmentShaderSource()
256 {
257 return MCDefaultShadowFsh;
258 }
259
getDefaultTextVertexShaderSource()260 const char * MCGLShaderProgram::getDefaultTextVertexShaderSource()
261 {
262 return MCDefaultTextVsh;
263 }
264
getDefaultTextFragmentShaderSource()265 const char * MCGLShaderProgram::getDefaultTextFragmentShaderSource()
266 {
267 return MCDefaultFsh;
268 }
269
getDefaultTextShadowFragmentShaderSource()270 const char * MCGLShaderProgram::getDefaultTextShadowFragmentShaderSource()
271 {
272 return MCDefaultTextShadowFsh;
273 }
274
getDefaultFBOVertexShaderSource()275 const char * MCGLShaderProgram::getDefaultFBOVertexShaderSource()
276 {
277 return MCDefaultFBOVsh;
278 }
279
getDefaultFBOFragmentShaderSource()280 const char * MCGLShaderProgram::getDefaultFBOFragmentShaderSource()
281 {
282 return MCDefaultFBOFsh;
283 }
284
addGeometryShaderFromSource(const std::string &)285 bool MCGLShaderProgram::addGeometryShaderFromSource(const std::string &)
286 {
287 return false;
288 }
289
pushProgram()290 void MCGLShaderProgram::pushProgram()
291 {
292 if (MCGLShaderProgram::m_activeProgram)
293 {
294 m_programStack.push_back(MCGLShaderProgram::m_activeProgram);
295 }
296 }
297
popProgram()298 void MCGLShaderProgram::popProgram()
299 {
300 if (m_programStack.size())
301 {
302 m_programStack.back()->bind();
303 m_programStack.pop_back();
304 }
305 }
306
setViewProjectionMatrix(const glm::mat4x4 & viewProjectionMatrix)307 void MCGLShaderProgram::setViewProjectionMatrix(const glm::mat4x4 & viewProjectionMatrix)
308 {
309 m_viewProjectionMatrix = viewProjectionMatrix;
310 m_viewProjectionMatrixPending = true;
311
312 if (isBound())
313 {
314 setPendingViewProjectionMatrix();
315 }
316 }
317
setPendingViewProjectionMatrix()318 void MCGLShaderProgram::setPendingViewProjectionMatrix()
319 {
320 if (m_viewProjectionMatrixPending)
321 {
322 m_viewProjectionMatrixPending = false;
323 glUniformMatrix4fv(getUniformLocation(ViewProjection), 1, GL_FALSE, &m_viewProjectionMatrix[0][0]);
324 }
325 }
326
setViewMatrix(const glm::mat4x4 & viewMatrix)327 void MCGLShaderProgram::setViewMatrix(const glm::mat4x4 & viewMatrix)
328 {
329 m_viewMatrix = viewMatrix;
330 m_viewMatrixPending = true;
331
332 if (isBound())
333 {
334 setPendingViewMatrix();
335 }
336 }
337
setPendingViewMatrix()338 void MCGLShaderProgram::setPendingViewMatrix()
339 {
340 if (m_viewMatrixPending)
341 {
342 m_viewMatrixPending = false;
343 glUniformMatrix4fv(getUniformLocation(View), 1, GL_FALSE, &m_viewMatrix[0][0]);
344 }
345 }
346
setTransform(GLfloat angle,const MCVector3dF & pos)347 void MCGLShaderProgram::setTransform(GLfloat angle, const MCVector3dF & pos)
348 {
349 glm::mat4 translate = glm::translate(glm::mat4(1.0f), glm::vec3(pos.i(), pos.j(), pos.k()));
350 glm::mat4 rotation = glm::rotate(translate, angle, glm::vec3(0.0f, 0.0f, 1.0f));
351 glUniformMatrix4fv(getUniformLocation(Model), 1, GL_FALSE, &rotation[0][0]);
352 }
353
setUserData1(const MCVector2dF & data)354 void MCGLShaderProgram::setUserData1(const MCVector2dF & data)
355 {
356 glUniform2f(getUniformLocation(UserData1), data.i(), data.j());
357 }
358
setUserData2(const MCVector2dF & data)359 void MCGLShaderProgram::setUserData2(const MCVector2dF & data)
360 {
361 glUniform2f(getUniformLocation(UserData2), data.i(), data.j());
362 }
363
setCamera(const MCVector2dF & camera)364 void MCGLShaderProgram::setCamera(const MCVector2dF & camera)
365 {
366 glUniform2f(getUniformLocation(Camera), camera.i(), camera.j());
367 }
368
setColor(const MCGLColor & color)369 void MCGLShaderProgram::setColor(const MCGLColor & color)
370 {
371 glUniform4f(getUniformLocation(Color), color.r(), color.g(), color.b(), color.a());
372 }
373
setScale(GLfloat x,GLfloat y,GLfloat z)374 void MCGLShaderProgram::setScale(GLfloat x, GLfloat y, GLfloat z)
375 {
376 glUniform4f(getUniformLocation(Scale), x, y, z, 1);
377 }
378
setFadeValue(GLfloat value)379 void MCGLShaderProgram::setFadeValue(GLfloat value)
380 {
381 glUniform1f(getUniformLocation(FadeValue), value);
382 }
383
setDiffuseLight(const MCGLDiffuseLight & light)384 void MCGLShaderProgram::setDiffuseLight(const MCGLDiffuseLight & light)
385 {
386 m_diffuseLight = light;
387 m_diffuseLightPending = true;
388
389 if (isBound())
390 {
391 setPendingDiffuseLight();
392 }
393 }
394
setPendingDiffuseLight()395 void MCGLShaderProgram::setPendingDiffuseLight()
396 {
397 if (m_diffuseLightPending)
398 {
399 m_diffuseLightPending = false;
400 glUniform4f(
401 getUniformLocation(DiffuseLightDir),
402 m_diffuseLight.direction().i(), m_diffuseLight.direction().j(), m_diffuseLight.direction().k(), 1);
403 glUniform4f(
404 getUniformLocation(DiffuseLightColor),
405 m_diffuseLight.r(), m_diffuseLight.g(), m_diffuseLight.b(), m_diffuseLight.i());
406 }
407 }
408
setSpecularLight(const MCGLDiffuseLight & light)409 void MCGLShaderProgram::setSpecularLight(const MCGLDiffuseLight & light)
410 {
411 m_specularLight = light;
412 m_specularLightPending = true;
413
414 if (isBound())
415 {
416 setPendingSpecularLight();
417 }
418 }
419
setPendingSpecularLight()420 void MCGLShaderProgram::setPendingSpecularLight()
421 {
422 if (m_specularLightPending)
423 {
424 m_specularLightPending = false;
425 glUniform4f(
426 getUniformLocation(SpecularLightDir),
427 m_specularLight.direction().i(), m_specularLight.direction().j(), m_specularLight.direction().k(), 1);
428 glUniform4f(
429 getUniformLocation(SpecularLightColor),
430 m_specularLight.r(), m_specularLight.g(), m_specularLight.b(), m_specularLight.i());
431 }
432 }
433
setAmbientLight(const MCGLAmbientLight & light)434 void MCGLShaderProgram::setAmbientLight(const MCGLAmbientLight & light)
435 {
436 m_ambientLight = light;
437 m_ambientLightPending = true;
438
439 if (isBound())
440 {
441 setPendingAmbientLight();
442 }
443 }
444
setPendingAmbientLight()445 void MCGLShaderProgram::setPendingAmbientLight()
446 {
447 if (m_ambientLightPending)
448 {
449 m_ambientLightPending = false;
450 glUniform4f(
451 getUniformLocation(AmbientLightColor),
452 m_ambientLight.r(), m_ambientLight.g(), m_ambientLight.b(), m_ambientLight.i());
453 }
454 }
455
bindTextureUnit(GLuint index,Uniform uniform)456 void MCGLShaderProgram::bindTextureUnit(GLuint index, Uniform uniform)
457 {
458 const int location = getUniformLocation(uniform);
459 if (location != -1)
460 {
461 glUniform1i(getUniformLocation(uniform), index);
462 }
463 }
464
bindMaterial(MCGLMaterialPtr material)465 void MCGLShaderProgram::bindMaterial(MCGLMaterialPtr material)
466 {
467 assert(material);
468
469 material->doAlphaBlend();
470
471 const GLuint texture1 = material->texture(0);
472 const GLuint texture2 = material->texture(1);
473 const GLuint texture3 = material->texture(2);
474
475 glActiveTexture(GL_TEXTURE0);
476 glBindTexture(GL_TEXTURE_2D, texture1);
477 glActiveTexture(GL_TEXTURE1);
478 glBindTexture(GL_TEXTURE_2D, texture2);
479 glActiveTexture(GL_TEXTURE2);
480 glBindTexture(GL_TEXTURE_2D, texture3);
481
482 glActiveTexture(GL_TEXTURE0);
483
484 glUniform1f(getUniformLocation(MaterialSpecularCoeff), material->specularCoeff());
485 glUniform1f(getUniformLocation(MaterialDiffuseCoeff), material->diffuseCoeff());
486 }
487