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