]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/OpenGL/MaterialRenderer.cpp
20d684c870845a12ff4aaac4f4dd12b0305903d7
[irrlicht.git] / source / Irrlicht / OpenGL / MaterialRenderer.cpp
1 // Copyright (C) 2014 Patryk Nadrowski\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "MaterialRenderer.h"\r
6 \r
7 #include "EVertexAttributes.h"\r
8 #include "IGPUProgrammingServices.h"\r
9 #include "IShaderConstantSetCallBack.h"\r
10 #include "IVideoDriver.h"\r
11 #include "os.h"\r
12 \r
13 #include "Driver.h"\r
14 \r
15 #include "COpenGLCoreTexture.h"\r
16 #include "COpenGLCoreCacheHandler.h"\r
17 \r
18 namespace irr\r
19 {\r
20 namespace video\r
21 {\r
22 \r
23 \r
24 COpenGL3MaterialRenderer::COpenGL3MaterialRenderer(COpenGL3DriverBase* driver,\r
25                 s32& outMaterialTypeNr,\r
26                 const c8* vertexShaderProgram,\r
27                 const c8* pixelShaderProgram,\r
28                 IShaderConstantSetCallBack* callback,\r
29                 E_MATERIAL_TYPE baseMaterial,\r
30                 s32 userData)\r
31         : Driver(driver), CallBack(callback), Alpha(false), Blending(false), FixedBlending(false), Program(0), UserData(userData)\r
32 {\r
33 #ifdef _DEBUG\r
34         setDebugName("MaterialRenderer");\r
35 #endif\r
36 \r
37         switch (baseMaterial)\r
38         {\r
39         case EMT_TRANSPARENT_VERTEX_ALPHA:\r
40         case EMT_TRANSPARENT_ALPHA_CHANNEL:\r
41                 Alpha = true;\r
42                 break;\r
43         case EMT_TRANSPARENT_ADD_COLOR:\r
44                 FixedBlending = true;\r
45                 break;\r
46         case EMT_ONETEXTURE_BLEND:\r
47                 Blending = true;\r
48                 break;\r
49         default:\r
50                 break;\r
51         }\r
52 \r
53         if (CallBack)\r
54                 CallBack->grab();\r
55 \r
56         init(outMaterialTypeNr, vertexShaderProgram, pixelShaderProgram);\r
57 }\r
58 \r
59 \r
60 COpenGL3MaterialRenderer::COpenGL3MaterialRenderer(COpenGL3DriverBase* driver,\r
61                                         IShaderConstantSetCallBack* callback,\r
62                                         E_MATERIAL_TYPE baseMaterial, s32 userData)\r
63 : Driver(driver), CallBack(callback), Alpha(false), Blending(false), FixedBlending(false), Program(0), UserData(userData)\r
64 {\r
65         switch (baseMaterial)\r
66         {\r
67         case EMT_TRANSPARENT_VERTEX_ALPHA:\r
68         case EMT_TRANSPARENT_ALPHA_CHANNEL:\r
69                 Alpha = true;\r
70                 break;\r
71         case EMT_TRANSPARENT_ADD_COLOR:\r
72                 FixedBlending = true;\r
73                 break;\r
74         case EMT_ONETEXTURE_BLEND:\r
75                 Blending = true;\r
76                 break;\r
77         default:\r
78                 break;\r
79         }\r
80 \r
81         if (CallBack)\r
82                 CallBack->grab();\r
83 }\r
84 \r
85 \r
86 COpenGL3MaterialRenderer::~COpenGL3MaterialRenderer()\r
87 {\r
88         if (CallBack)\r
89                 CallBack->drop();\r
90 \r
91         if (Program)\r
92         {\r
93                 GLuint shaders[8];\r
94                 GLint count;\r
95                 glGetAttachedShaders(Program, 8, &count, shaders);\r
96 \r
97                 count=core::min_(count,8);\r
98                 for (GLint i=0; i<count; ++i)\r
99                         glDeleteShader(shaders[i]);\r
100                 glDeleteProgram(Program);\r
101                 Program = 0;\r
102         }\r
103 \r
104         UniformInfo.clear();\r
105 }\r
106 \r
107 GLuint COpenGL3MaterialRenderer::getProgram() const\r
108 {\r
109         return Program;\r
110 }\r
111 \r
112 void COpenGL3MaterialRenderer::init(s32& outMaterialTypeNr,\r
113                 const c8* vertexShaderProgram,\r
114                 const c8* pixelShaderProgram,\r
115                 bool addMaterial)\r
116 {\r
117         outMaterialTypeNr = -1;\r
118 \r
119         Program = glCreateProgram();\r
120 \r
121         if (!Program)\r
122                 return;\r
123 \r
124         if (vertexShaderProgram)\r
125                 if (!createShader(GL_VERTEX_SHADER, vertexShaderProgram))\r
126                         return;\r
127 \r
128         if (pixelShaderProgram)\r
129                 if (!createShader(GL_FRAGMENT_SHADER, pixelShaderProgram))\r
130                         return;\r
131 \r
132         for ( size_t i = 0; i < EVA_COUNT; ++i )\r
133                         glBindAttribLocation( Program, i, sBuiltInVertexAttributeNames[i]);\r
134 \r
135         if (!linkProgram())\r
136                 return;\r
137 \r
138         if (addMaterial)\r
139                 outMaterialTypeNr = Driver->addMaterialRenderer(this);\r
140 }\r
141 \r
142 \r
143 bool COpenGL3MaterialRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype)\r
144 {\r
145         if (CallBack && Program)\r
146                 CallBack->OnSetConstants(this, UserData);\r
147 \r
148         return true;\r
149 }\r
150 \r
151 \r
152 void COpenGL3MaterialRenderer::OnSetMaterial(const video::SMaterial& material,\r
153                                 const video::SMaterial& lastMaterial,\r
154                                 bool resetAllRenderstates,\r
155                                 video::IMaterialRendererServices* services)\r
156 {\r
157         COpenGL3CacheHandler* cacheHandler = Driver->getCacheHandler();\r
158 \r
159         cacheHandler->setProgram(Program);\r
160 \r
161         Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);\r
162 \r
163         if (Alpha)\r
164         {\r
165                 cacheHandler->setBlend(true);\r
166                 cacheHandler->setBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r
167         }\r
168         else if (FixedBlending)\r
169         {\r
170                 cacheHandler->setBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);\r
171                 cacheHandler->setBlend(true);\r
172         }\r
173         else if (Blending)\r
174         {\r
175                 E_BLEND_FACTOR srcRGBFact,dstRGBFact,srcAlphaFact,dstAlphaFact;\r
176                 E_MODULATE_FUNC modulate;\r
177                 u32 alphaSource;\r
178                 unpack_textureBlendFuncSeparate(srcRGBFact, dstRGBFact, srcAlphaFact, dstAlphaFact, modulate, alphaSource, material.MaterialTypeParam);\r
179 \r
180                 cacheHandler->setBlendFuncSeparate(Driver->getGLBlend(srcRGBFact), Driver->getGLBlend(dstRGBFact),\r
181                         Driver->getGLBlend(srcAlphaFact), Driver->getGLBlend(dstAlphaFact));\r
182 \r
183                 cacheHandler->setBlend(true);\r
184         }\r
185 \r
186         if (CallBack)\r
187                 CallBack->OnSetMaterial(material);\r
188 }\r
189 \r
190 \r
191 void COpenGL3MaterialRenderer::OnUnsetMaterial()\r
192 {\r
193 }\r
194 \r
195 \r
196 bool COpenGL3MaterialRenderer::isTransparent() const\r
197 {\r
198         return (Alpha || Blending || FixedBlending);\r
199 }\r
200 \r
201 \r
202 s32 COpenGL3MaterialRenderer::getRenderCapability() const\r
203 {\r
204         return 0;\r
205 }\r
206 \r
207 \r
208 bool COpenGL3MaterialRenderer::createShader(GLenum shaderType, const char* shader)\r
209 {\r
210         if (Program)\r
211         {\r
212                 GLuint shaderHandle = glCreateShader(shaderType);\r
213                 glShaderSource(shaderHandle, 1, &shader, NULL);\r
214                 glCompileShader(shaderHandle);\r
215 \r
216                 GLint status = 0;\r
217 \r
218                 glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &status);\r
219 \r
220                 if (status != GL_TRUE)\r
221                 {\r
222                         os::Printer::log("GLSL shader failed to compile", ELL_ERROR);\r
223 \r
224                         GLint maxLength=0;\r
225                         GLint length;\r
226 \r
227                         glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH,\r
228                                         &maxLength);\r
229 \r
230                         if (maxLength)\r
231                         {\r
232                                 GLchar *infoLog = new GLchar[maxLength];\r
233                                 glGetShaderInfoLog(shaderHandle, maxLength, &length, infoLog);\r
234                                 os::Printer::log(reinterpret_cast<const c8*>(infoLog), ELL_ERROR);\r
235                                 delete [] infoLog;\r
236                         }\r
237 \r
238                         return false;\r
239                 }\r
240 \r
241                 glAttachShader(Program, shaderHandle);\r
242         }\r
243 \r
244         return true;\r
245 }\r
246 \r
247 \r
248 bool COpenGL3MaterialRenderer::linkProgram()\r
249 {\r
250         if (Program)\r
251         {\r
252                 glLinkProgram(Program);\r
253 \r
254                 GLint status = 0;\r
255 \r
256                 glGetProgramiv(Program, GL_LINK_STATUS, &status);\r
257 \r
258                 if (!status)\r
259                 {\r
260                         os::Printer::log("GLSL shader program failed to link", ELL_ERROR);\r
261 \r
262                         GLint maxLength=0;\r
263                         GLsizei length;\r
264 \r
265                         glGetProgramiv(Program, GL_INFO_LOG_LENGTH, &maxLength);\r
266 \r
267                         if (maxLength)\r
268                         {\r
269                                 GLchar *infoLog = new GLchar[maxLength];\r
270                                 glGetProgramInfoLog(Program, maxLength, &length, infoLog);\r
271                                 os::Printer::log(reinterpret_cast<const c8*>(infoLog), ELL_ERROR);\r
272                                 delete [] infoLog;\r
273                         }\r
274 \r
275                         return false;\r
276                 }\r
277 \r
278                 GLint num = 0;\r
279 \r
280                 glGetProgramiv(Program, GL_ACTIVE_UNIFORMS, &num);\r
281 \r
282                 if (num == 0)\r
283                         return true;\r
284 \r
285                 GLint maxlen = 0;\r
286 \r
287                 glGetProgramiv(Program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxlen);\r
288 \r
289                 if (maxlen == 0)\r
290                 {\r
291                         os::Printer::log("GLSL: failed to retrieve uniform information", ELL_ERROR);\r
292                         return false;\r
293                 }\r
294 \r
295                 // seems that some implementations use an extra null terminator.\r
296                 ++maxlen;\r
297                 c8 *buf = new c8[maxlen];\r
298 \r
299                 UniformInfo.clear();\r
300                 UniformInfo.reallocate(num);\r
301 \r
302                 for (GLint i=0; i < num; ++i)\r
303                 {\r
304                         SUniformInfo ui;\r
305                         memset(buf, 0, maxlen);\r
306 \r
307                         GLint size;\r
308                         glGetActiveUniform(Program, i, maxlen, 0, &size, &ui.type, reinterpret_cast<GLchar*>(buf));\r
309 \r
310             core::stringc name = "";\r
311 \r
312                         // array support, workaround for some bugged drivers.\r
313                         for (s32 i = 0; i < maxlen; ++i)\r
314                         {\r
315                                 if (buf[i] == '[' || buf[i] == '\0')\r
316                                         break;\r
317 \r
318                 name += buf[i];\r
319                         }\r
320 \r
321                         ui.name = name;\r
322                         ui.location = glGetUniformLocation(Program, buf);\r
323 \r
324                         UniformInfo.push_back(ui);\r
325                 }\r
326 \r
327                 delete [] buf;\r
328         }\r
329 \r
330         return true;\r
331 }\r
332 \r
333 \r
334 void COpenGL3MaterialRenderer::setBasicRenderStates(const SMaterial& material,\r
335                                                 const SMaterial& lastMaterial,\r
336                                                 bool resetAllRenderstates)\r
337 {\r
338         Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);\r
339 }\r
340 \r
341 s32 COpenGL3MaterialRenderer::getVertexShaderConstantID(const c8* name)\r
342 {\r
343         return getPixelShaderConstantID(name);\r
344 }\r
345 \r
346 s32 COpenGL3MaterialRenderer::getPixelShaderConstantID(const c8* name)\r
347 {\r
348         for (u32 i = 0; i < UniformInfo.size(); ++i)\r
349         {\r
350                 if (UniformInfo[i].name == name)\r
351                         return i;\r
352         }\r
353 \r
354         return -1;\r
355 }\r
356 \r
357 void COpenGL3MaterialRenderer::setVertexShaderConstant(const f32* data, s32 startRegister, s32 constantAmount)\r
358 {\r
359         os::Printer::log("Cannot set constant, please use high level shader call instead.", ELL_WARNING);\r
360 }\r
361 \r
362 void COpenGL3MaterialRenderer::setPixelShaderConstant(const f32* data, s32 startRegister, s32 constantAmount)\r
363 {\r
364         os::Printer::log("Cannot set constant, use high level shader call.", ELL_WARNING);\r
365 }\r
366 \r
367 bool COpenGL3MaterialRenderer::setVertexShaderConstant(s32 index, const f32* floats, int count)\r
368 {\r
369         return setPixelShaderConstant(index, floats, count);\r
370 }\r
371 \r
372 bool COpenGL3MaterialRenderer::setVertexShaderConstant(s32 index, const s32* ints, int count)\r
373 {\r
374         return setPixelShaderConstant(index, ints, count);\r
375 }\r
376 \r
377 bool COpenGL3MaterialRenderer::setVertexShaderConstant(s32 index, const u32* ints, int count)\r
378 {\r
379         return setPixelShaderConstant(index, ints, count);\r
380 }\r
381 \r
382 bool COpenGL3MaterialRenderer::setPixelShaderConstant(s32 index, const f32* floats, int count)\r
383 {\r
384         if(index < 0 || UniformInfo[index].location < 0)\r
385                 return false;\r
386 \r
387         bool status = true;\r
388 \r
389         switch (UniformInfo[index].type)\r
390         {\r
391                 case GL_FLOAT:\r
392                         glUniform1fv(UniformInfo[index].location, count, floats);\r
393                         break;\r
394                 case GL_FLOAT_VEC2:\r
395                         glUniform2fv(UniformInfo[index].location, count/2, floats);\r
396                         break;\r
397                 case GL_FLOAT_VEC3:\r
398                         glUniform3fv(UniformInfo[index].location, count/3, floats);\r
399                         break;\r
400                 case GL_FLOAT_VEC4:\r
401                         glUniform4fv(UniformInfo[index].location, count/4, floats);\r
402                         break;\r
403                 case GL_FLOAT_MAT2:\r
404                         glUniformMatrix2fv(UniformInfo[index].location, count/4, false, floats);\r
405                         break;\r
406                 case GL_FLOAT_MAT3:\r
407                         glUniformMatrix3fv(UniformInfo[index].location, count/9, false, floats);\r
408                         break;\r
409                 case GL_FLOAT_MAT4:\r
410                         glUniformMatrix4fv(UniformInfo[index].location, count/16, false, floats);\r
411                         break;\r
412                 case GL_SAMPLER_2D:\r
413                 case GL_SAMPLER_CUBE:\r
414                         {\r
415                                 if(floats)\r
416                                 {\r
417                                         const GLint id = (GLint)(*floats);\r
418                                         glUniform1iv(UniformInfo[index].location, 1, &id);\r
419                                 }\r
420                                 else\r
421                                         status = false;\r
422                         }\r
423                         break;\r
424                 default:\r
425                         status = false;\r
426                         break;\r
427         }\r
428 \r
429         return status;\r
430 }\r
431 \r
432 bool COpenGL3MaterialRenderer::setPixelShaderConstant(s32 index, const s32* ints, int count)\r
433 {\r
434         if(index < 0 || UniformInfo[index].location < 0)\r
435                 return false;\r
436 \r
437         bool status = true;\r
438 \r
439         switch (UniformInfo[index].type)\r
440         {\r
441                 case GL_INT:\r
442                 case GL_BOOL:\r
443                         glUniform1iv(UniformInfo[index].location, count, ints);\r
444                         break;\r
445                 case GL_INT_VEC2:\r
446                 case GL_BOOL_VEC2:\r
447                         glUniform2iv(UniformInfo[index].location, count/2, ints);\r
448                         break;\r
449                 case GL_INT_VEC3:\r
450                 case GL_BOOL_VEC3:\r
451                         glUniform3iv(UniformInfo[index].location, count/3, ints);\r
452                         break;\r
453                 case GL_INT_VEC4:\r
454                 case GL_BOOL_VEC4:\r
455                         glUniform4iv(UniformInfo[index].location, count/4, ints);\r
456                         break;\r
457                 case GL_SAMPLER_2D:\r
458                 case GL_SAMPLER_CUBE:\r
459                         glUniform1iv(UniformInfo[index].location, 1, ints);\r
460                         break;\r
461                 default:\r
462                         status = false;\r
463                         break;\r
464         }\r
465 \r
466         return status;\r
467 }\r
468 \r
469 bool COpenGL3MaterialRenderer::setPixelShaderConstant(s32 index, const u32* ints, int count)\r
470 {\r
471         os::Printer::log("Unsigned int support needs at least GLES 3.0", ELL_WARNING);\r
472         return false;\r
473 }\r
474 \r
475 IVideoDriver* COpenGL3MaterialRenderer::getVideoDriver()\r
476 {\r
477         return Driver;\r
478 }\r
479 \r
480 }\r
481 }\r