]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/COpenGLShaderMaterialRenderer.cpp
Merge branch 'opengl3' of https://github.com/numberZero/irrlicht
[irrlicht.git] / source / Irrlicht / COpenGLShaderMaterialRenderer.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\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 "COpenGLShaderMaterialRenderer.h"\r
6 \r
7 #ifdef _IRR_COMPILE_WITH_OPENGL_\r
8 \r
9 #include "IGPUProgrammingServices.h"\r
10 #include "IShaderConstantSetCallBack.h"\r
11 #include "IVideoDriver.h"\r
12 #include "os.h"\r
13 \r
14 #include "COpenGLDriver.h"\r
15 #include "COpenGLCacheHandler.h"\r
16 #include "COpenGLMaterialRenderer.h"\r
17 \r
18 namespace irr\r
19 {\r
20 namespace video\r
21 {\r
22 \r
23 \r
24 //! Constructor\r
25 COpenGLShaderMaterialRenderer::COpenGLShaderMaterialRenderer(video::COpenGLDriver* driver,\r
26         s32& outMaterialTypeNr, const c8* vertexShaderProgram, const c8* pixelShaderProgram,\r
27         IShaderConstantSetCallBack* callback, E_MATERIAL_TYPE baseMaterial, s32 userData)\r
28         : Driver(driver), CallBack(callback), Alpha(false), Blending(false), FixedBlending(false),\r
29                 AlphaTest(false), VertexShader(0), UserData(userData)\r
30 {\r
31         #ifdef _DEBUG\r
32         setDebugName("COpenGLShaderMaterialRenderer");\r
33         #endif\r
34 \r
35         PixelShader.set_used(4);\r
36         for (u32 i=0; i<4; ++i)\r
37         {\r
38                 PixelShader[i]=0;\r
39         }\r
40 \r
41         switch (baseMaterial)\r
42         {\r
43         case EMT_TRANSPARENT_VERTEX_ALPHA:\r
44         case EMT_TRANSPARENT_ALPHA_CHANNEL:\r
45                 Alpha = true;\r
46                 break;\r
47         case EMT_TRANSPARENT_ADD_COLOR:\r
48                 FixedBlending = true;\r
49                 break;\r
50         case EMT_ONETEXTURE_BLEND:\r
51                 Blending = true;\r
52                 break;\r
53         case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:\r
54                 AlphaTest = true;\r
55                 break;\r
56         default:\r
57                 break;\r
58         }\r
59 \r
60         if (CallBack)\r
61                 CallBack->grab();\r
62 \r
63         init(outMaterialTypeNr, vertexShaderProgram, pixelShaderProgram, EVT_STANDARD);\r
64 }\r
65 \r
66 \r
67 //! constructor only for use by derived classes who want to\r
68 //! create a fall back material for example.\r
69 COpenGLShaderMaterialRenderer::COpenGLShaderMaterialRenderer(COpenGLDriver* driver,\r
70                                 IShaderConstantSetCallBack* callback,\r
71                                 E_MATERIAL_TYPE baseMaterial, s32 userData)\r
72 : Driver(driver), CallBack(callback), Alpha(false), Blending(false), FixedBlending(false),\r
73         AlphaTest(false), VertexShader(0), UserData(userData)\r
74 {\r
75         PixelShader.set_used(4);\r
76         for (u32 i=0; i<4; ++i)\r
77         {\r
78                 PixelShader[i]=0;\r
79         }\r
80 \r
81         switch (baseMaterial)\r
82         {\r
83         case EMT_TRANSPARENT_VERTEX_ALPHA:\r
84         case EMT_TRANSPARENT_ALPHA_CHANNEL:\r
85                 Alpha = true;\r
86                 break;\r
87         case EMT_TRANSPARENT_ADD_COLOR:\r
88                 FixedBlending = true;\r
89                 break;\r
90         case EMT_ONETEXTURE_BLEND:\r
91                 Blending = true;\r
92                 break;\r
93         case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:\r
94                 AlphaTest = true;\r
95                 break;\r
96         default:\r
97                 break;\r
98         }\r
99 \r
100         if (CallBack)\r
101                 CallBack->grab();\r
102 }\r
103 \r
104 \r
105 //! Destructor\r
106 COpenGLShaderMaterialRenderer::~COpenGLShaderMaterialRenderer()\r
107 {\r
108         if (CallBack)\r
109                 CallBack->drop();\r
110 \r
111         if (VertexShader)\r
112                 Driver->extGlDeletePrograms(1, &VertexShader);\r
113 \r
114         for (u32 i=0; i<PixelShader.size(); ++i)\r
115                 if (PixelShader[i])\r
116                         Driver->extGlDeletePrograms(1, &PixelShader[i]);\r
117 }\r
118 \r
119 \r
120 void COpenGLShaderMaterialRenderer::init(s32& outMaterialTypeNr,\r
121                 const c8* vertexShaderProgram, const c8* pixelShaderProgram,\r
122                 E_VERTEX_TYPE type)\r
123 {\r
124         outMaterialTypeNr = -1;\r
125 \r
126         bool success;\r
127 \r
128         // create vertex shader\r
129         success=createVertexShader(vertexShaderProgram);\r
130 \r
131         // create pixel shader\r
132         if (!createPixelShader(pixelShaderProgram) || !success)\r
133                 return;\r
134 \r
135         // register as a new material\r
136         outMaterialTypeNr = Driver->addMaterialRenderer(this);\r
137 }\r
138 \r
139 \r
140 bool COpenGLShaderMaterialRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype)\r
141 {\r
142         // call callback to set shader constants\r
143         if (CallBack && (VertexShader || PixelShader[0]))\r
144                 CallBack->OnSetConstants(service, UserData);\r
145 \r
146         return true;\r
147 }\r
148 \r
149 \r
150 void COpenGLShaderMaterialRenderer::OnSetMaterial(const video::SMaterial& material, const video::SMaterial& lastMaterial,\r
151         bool resetAllRenderstates, video::IMaterialRendererServices* services)\r
152 {\r
153         if (Driver->getFixedPipelineState() == COpenGLDriver::EOFPS_ENABLE)\r
154                 Driver->setFixedPipelineState(COpenGLDriver::EOFPS_ENABLE_TO_DISABLE);\r
155         else\r
156                 Driver->setFixedPipelineState(COpenGLDriver::EOFPS_DISABLE);\r
157 \r
158         COpenGLCacheHandler* cacheHandler = Driver->getCacheHandler();\r
159 \r
160         if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)\r
161         {\r
162                 if (VertexShader)\r
163                 {\r
164                         // set new vertex shader\r
165 #ifdef GL_ARB_vertex_program\r
166                         Driver->extGlBindProgram(GL_VERTEX_PROGRAM_ARB, VertexShader);\r
167                         glEnable(GL_VERTEX_PROGRAM_ARB);\r
168 #elif defined(GL_NV_vertex_program)\r
169                         Driver->extGlBindProgram(GL_VERTEX_PROGRAM_NV, VertexShader);\r
170                         glEnable(GL_VERTEX_PROGRAM_NV);\r
171 #endif\r
172                 }\r
173 \r
174                 // set new pixel shader\r
175                 if (PixelShader[0])\r
176                 {\r
177                         GLuint nextShader=PixelShader[0];\r
178                         if (material.FogEnable)\r
179                         {\r
180                                 GLint curFogMode;\r
181                                 glGetIntegerv(GL_FOG_MODE, &curFogMode);\r
182 //                              if (Driver->LinearFog && PixelShader[1])\r
183                                 if (curFogMode==GL_LINEAR && PixelShader[1])\r
184                                         nextShader=PixelShader[1];\r
185 //                              else if (!Driver->LinearFog && PixelShader[2])\r
186                                 else if (curFogMode==GL_EXP && PixelShader[2])\r
187                                         nextShader=PixelShader[2];\r
188                                 else if (curFogMode==GL_EXP2 && PixelShader[3])\r
189                                         nextShader=PixelShader[3];\r
190                         }\r
191 #ifdef GL_ARB_fragment_program\r
192                         Driver->extGlBindProgram(GL_FRAGMENT_PROGRAM_ARB, nextShader);\r
193                         glEnable(GL_FRAGMENT_PROGRAM_ARB);\r
194 #elif defined(GL_NV_fragment_program)\r
195                         Driver->extGlBindProgram(GL_FRAGMENT_PROGRAM_NV, nextShader);\r
196                         glEnable(GL_FRAGMENT_PROGRAM_NV);\r
197 #endif\r
198                 }\r
199         }\r
200 \r
201         Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);\r
202 \r
203         if (Alpha)\r
204         {\r
205                 cacheHandler->setBlend(true);\r
206                 cacheHandler->setBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r
207         }\r
208         else if (FixedBlending)\r
209         {\r
210                 cacheHandler->setBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);\r
211                 cacheHandler->setBlend(true);\r
212         }\r
213         else if (Blending)\r
214         {\r
215                 E_BLEND_FACTOR srcRGBFact,dstRGBFact,srcAlphaFact,dstAlphaFact;\r
216                 E_MODULATE_FUNC modulate;\r
217                 u32 alphaSource;\r
218                 unpack_textureBlendFuncSeparate(srcRGBFact, dstRGBFact, srcAlphaFact, dstAlphaFact, modulate, alphaSource, material.MaterialTypeParam);\r
219 \r
220                 if (Driver->queryFeature(EVDF_BLEND_SEPARATE))\r
221         {\r
222                         cacheHandler->setBlendFuncSeparate(Driver->getGLBlend(srcRGBFact), Driver->getGLBlend(dstRGBFact),\r
223                 Driver->getGLBlend(srcAlphaFact), Driver->getGLBlend(dstAlphaFact));\r
224         }\r
225         else\r
226         {\r
227                         cacheHandler->setBlendFunc(Driver->getGLBlend(srcRGBFact), Driver->getGLBlend(dstRGBFact));\r
228         }\r
229 \r
230                 cacheHandler->setBlend(true);\r
231         }\r
232         else if (AlphaTest)\r
233         {\r
234                 cacheHandler->setAlphaTest(true);\r
235                 cacheHandler->setAlphaFunc(GL_GREATER, 0.5f);\r
236         }\r
237 \r
238         if (CallBack)\r
239                 CallBack->OnSetMaterial(material);\r
240 }\r
241 \r
242 \r
243 void COpenGLShaderMaterialRenderer::OnUnsetMaterial()\r
244 {\r
245         // disable vertex shader\r
246 #ifdef GL_ARB_vertex_program\r
247         if (VertexShader)\r
248                 glDisable(GL_VERTEX_PROGRAM_ARB);\r
249 #elif defined(GL_NV_vertex_program)\r
250         if (VertexShader)\r
251                 glDisable(GL_VERTEX_PROGRAM_NV);\r
252 #endif\r
253 \r
254 #ifdef GL_ARB_fragment_program\r
255         if (PixelShader[0])\r
256                 glDisable(GL_FRAGMENT_PROGRAM_ARB);\r
257 #elif defined(GL_NV_fragment_program)\r
258         if (PixelShader[0])\r
259                 glDisable(GL_FRAGMENT_PROGRAM_NV);\r
260 #endif\r
261 \r
262         COpenGLCacheHandler* cacheHandler = Driver->getCacheHandler();\r
263         if (Alpha || FixedBlending || Blending)\r
264         {\r
265                 cacheHandler->setBlend(false);\r
266         }\r
267         else if (AlphaTest)\r
268         {\r
269                 cacheHandler->setAlphaTest(false);\r
270         }\r
271 }\r
272 \r
273 \r
274 //! Returns if the material is transparent.\r
275 bool COpenGLShaderMaterialRenderer::isTransparent() const\r
276 {\r
277         return (Alpha || Blending || FixedBlending);\r
278 }\r
279 \r
280 \r
281 // This method needs a properly cleaned error state before the checked instruction is called\r
282 bool COpenGLShaderMaterialRenderer::checkError(const irr::c8* type)\r
283 {\r
284 #if defined(GL_ARB_vertex_program) || defined(GL_NV_vertex_program) || defined(GL_ARB_fragment_program) || defined(GL_NV_fragment_program)\r
285         GLenum g = glGetError();\r
286         if (g == GL_NO_ERROR)\r
287                 return false;\r
288 \r
289         core::stringc errString = type;\r
290         errString += " compilation failed";\r
291 \r
292         errString += " at position ";\r
293         GLint errPos=-1;\r
294 #if defined(GL_ARB_vertex_program) || defined(GL_ARB_fragment_program)\r
295         glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errPos );\r
296 #else\r
297         glGetIntegerv( GL_PROGRAM_ERROR_POSITION_NV, &errPos );\r
298 #endif\r
299         errString += core::stringc(s32(errPos));\r
300         errString += ":\n";\r
301 #if defined(GL_ARB_vertex_program) || defined(GL_ARB_fragment_program)\r
302         errString += reinterpret_cast<const char*>(glGetString(GL_PROGRAM_ERROR_STRING_ARB));\r
303 #else\r
304         errString += reinterpret_cast<const char*>(glGetString(GL_PROGRAM_ERROR_STRING_NV));\r
305 #endif\r
306 #else\r
307         core::stringc errString("Shaders not supported.");\r
308 #endif\r
309         os::Printer::log(errString.c_str(), ELL_ERROR);\r
310         return true;\r
311 }\r
312 \r
313 \r
314 bool COpenGLShaderMaterialRenderer::createPixelShader(const c8* pxsh)\r
315 {\r
316         if (!pxsh)\r
317                 return true;\r
318 \r
319         const core::stringc inshdr(pxsh);\r
320         core::stringc shdr;\r
321         const s32 pos = inshdr.find("#_IRR_FOG_MODE_");\r
322         const u32 numShaders = (-1 != pos)?4:1;\r
323 \r
324         for (u32 i=0; i<numShaders; ++i)\r
325         {\r
326                 if (i==0)\r
327                 {\r
328                         shdr=inshdr;\r
329                 }\r
330                 else\r
331                 {\r
332                         shdr = inshdr.subString(0, pos);\r
333                         switch (i) {\r
334                                 case 1: shdr += "OPTION ARB_fog_linear;"; break;\r
335                                 case 2: shdr += "OPTION ARB_fog_exp;"; break;\r
336                                 case 3: shdr += "OPTION ARB_fog_exp2;"; break;\r
337                         }\r
338                         shdr += inshdr.subString(pos+16, inshdr.size()-pos-16);\r
339                 }\r
340                 Driver->extGlGenPrograms(1, &PixelShader[i]);\r
341 #ifdef GL_ARB_fragment_program\r
342                 Driver->extGlBindProgram(GL_FRAGMENT_PROGRAM_ARB, PixelShader[i]);\r
343 #elif defined GL_NV_fragment_program\r
344                 Driver->extGlBindProgram(GL_FRAGMENT_PROGRAM_NV, PixelShader[i]);\r
345 #endif\r
346 \r
347                 // clear error buffer\r
348                 while(glGetError() != GL_NO_ERROR)\r
349                         {}\r
350 \r
351 #ifdef GL_ARB_fragment_program\r
352                 // compile\r
353                 Driver->extGlProgramString(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,\r
354                                 shdr.size(), shdr.c_str());\r
355 #elif defined GL_NV_fragment_program\r
356                 Driver->extGlLoadProgram(GL_FRAGMENT_PROGRAM_NV, PixelShader[i],\r
357                                 shdr.size(), shdr.c_str());\r
358 #endif\r
359 \r
360                 if (checkError("Pixel shader"))\r
361                 {\r
362                         Driver->extGlDeletePrograms(1, &PixelShader[i]);\r
363                         PixelShader[i]=0;\r
364 \r
365                         return false;\r
366                 }\r
367         }\r
368 \r
369         return true;\r
370 }\r
371 \r
372 \r
373 bool COpenGLShaderMaterialRenderer::createVertexShader(const c8* vtxsh)\r
374 {\r
375         if (!vtxsh)\r
376                 return true;\r
377 \r
378         Driver->extGlGenPrograms(1, &VertexShader);\r
379 #ifdef GL_ARB_vertex_program\r
380         Driver->extGlBindProgram(GL_VERTEX_PROGRAM_ARB, VertexShader);\r
381 #elif defined GL_NV_vertex_program\r
382         Driver->extGlBindProgram(GL_VERTEX_PROGRAM_NV, VertexShader);\r
383 #endif\r
384 \r
385         // clear error buffer\r
386         while(glGetError() != GL_NO_ERROR)\r
387         {}\r
388 \r
389         // compile\r
390 #ifdef GL_ARB_vertex_program\r
391         Driver->extGlProgramString(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,\r
392                         (GLsizei)strlen(vtxsh), vtxsh);\r
393 #elif defined GL_NV_vertex_program\r
394         Driver->extGlLoadProgram(GL_VERTEX_PROGRAM_NV, VertexShader,\r
395                         (GLsizei)strlen(vtxsh), vtxsh);\r
396 #endif\r
397 \r
398         if (checkError("Vertex shader"))\r
399         {\r
400                 Driver->extGlDeletePrograms(1, &VertexShader);\r
401                 VertexShader=0;\r
402 \r
403                 return false;\r
404         }\r
405 \r
406         return true;\r
407 }\r
408 \r
409 \r
410 } // end namespace video\r
411 } // end namespace irr\r
412 \r
413 #endif\r
414 \r