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