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
5 #include "COpenGLShaderMaterialRenderer.h"
\r
7 #ifdef _IRR_COMPILE_WITH_OPENGL_
\r
9 #include "IGPUProgrammingServices.h"
\r
10 #include "IShaderConstantSetCallBack.h"
\r
11 #include "IVideoDriver.h"
\r
14 #include "COpenGLDriver.h"
\r
15 #include "COpenGLCacheHandler.h"
\r
16 #include "COpenGLMaterialRenderer.h"
\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
32 setDebugName("COpenGLShaderMaterialRenderer");
\r
35 PixelShader.set_used(4);
\r
36 for (u32 i=0; i<4; ++i)
\r
41 switch (baseMaterial)
\r
43 case EMT_TRANSPARENT_VERTEX_ALPHA:
\r
44 case EMT_TRANSPARENT_ALPHA_CHANNEL:
\r
47 case EMT_TRANSPARENT_ADD_COLOR:
\r
48 FixedBlending = true;
\r
50 case EMT_ONETEXTURE_BLEND:
\r
53 case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
\r
63 init(outMaterialTypeNr, vertexShaderProgram, pixelShaderProgram, EVT_STANDARD);
\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
75 PixelShader.set_used(4);
\r
76 for (u32 i=0; i<4; ++i)
\r
81 switch (baseMaterial)
\r
83 case EMT_TRANSPARENT_VERTEX_ALPHA:
\r
84 case EMT_TRANSPARENT_ALPHA_CHANNEL:
\r
87 case EMT_TRANSPARENT_ADD_COLOR:
\r
88 FixedBlending = true;
\r
90 case EMT_ONETEXTURE_BLEND:
\r
93 case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
\r
106 COpenGLShaderMaterialRenderer::~COpenGLShaderMaterialRenderer()
\r
112 Driver->extGlDeletePrograms(1, &VertexShader);
\r
114 for (u32 i=0; i<PixelShader.size(); ++i)
\r
115 if (PixelShader[i])
\r
116 Driver->extGlDeletePrograms(1, &PixelShader[i]);
\r
120 void COpenGLShaderMaterialRenderer::init(s32& outMaterialTypeNr,
\r
121 const c8* vertexShaderProgram, const c8* pixelShaderProgram,
\r
122 E_VERTEX_TYPE type)
\r
124 outMaterialTypeNr = -1;
\r
128 // create vertex shader
\r
129 success=createVertexShader(vertexShaderProgram);
\r
131 // create pixel shader
\r
132 if (!createPixelShader(pixelShaderProgram) || !success)
\r
135 // register as a new material
\r
136 outMaterialTypeNr = Driver->addMaterialRenderer(this);
\r
140 bool COpenGLShaderMaterialRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype)
\r
142 // call callback to set shader constants
\r
143 if (CallBack && (VertexShader || PixelShader[0]))
\r
144 CallBack->OnSetConstants(service, UserData);
\r
150 void COpenGLShaderMaterialRenderer::OnSetMaterial(const video::SMaterial& material, const video::SMaterial& lastMaterial,
\r
151 bool resetAllRenderstates, video::IMaterialRendererServices* services)
\r
153 if (Driver->getFixedPipelineState() == COpenGLDriver::EOFPS_ENABLE)
\r
154 Driver->setFixedPipelineState(COpenGLDriver::EOFPS_ENABLE_TO_DISABLE);
\r
156 Driver->setFixedPipelineState(COpenGLDriver::EOFPS_DISABLE);
\r
158 COpenGLCacheHandler* cacheHandler = Driver->getCacheHandler();
\r
160 if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
\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
174 // set new pixel shader
\r
175 if (PixelShader[0])
\r
177 GLuint nextShader=PixelShader[0];
\r
178 if (material.FogEnable)
\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
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
201 Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
\r
205 cacheHandler->setBlend(true);
\r
206 cacheHandler->setBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
\r
208 else if (FixedBlending)
\r
210 cacheHandler->setBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
\r
211 cacheHandler->setBlend(true);
\r
215 E_BLEND_FACTOR srcRGBFact,dstRGBFact,srcAlphaFact,dstAlphaFact;
\r
216 E_MODULATE_FUNC modulate;
\r
218 unpack_textureBlendFuncSeparate(srcRGBFact, dstRGBFact, srcAlphaFact, dstAlphaFact, modulate, alphaSource, material.MaterialTypeParam);
\r
220 if (Driver->queryFeature(EVDF_BLEND_SEPARATE))
\r
222 cacheHandler->setBlendFuncSeparate(Driver->getGLBlend(srcRGBFact), Driver->getGLBlend(dstRGBFact),
\r
223 Driver->getGLBlend(srcAlphaFact), Driver->getGLBlend(dstAlphaFact));
\r
227 cacheHandler->setBlendFunc(Driver->getGLBlend(srcRGBFact), Driver->getGLBlend(dstRGBFact));
\r
230 cacheHandler->setBlend(true);
\r
232 else if (AlphaTest)
\r
234 cacheHandler->setAlphaTest(true);
\r
235 cacheHandler->setAlphaFunc(GL_GREATER, 0.5f);
\r
239 CallBack->OnSetMaterial(material);
\r
243 void COpenGLShaderMaterialRenderer::OnUnsetMaterial()
\r
245 // disable vertex shader
\r
246 #ifdef GL_ARB_vertex_program
\r
248 glDisable(GL_VERTEX_PROGRAM_ARB);
\r
249 #elif defined(GL_NV_vertex_program)
\r
251 glDisable(GL_VERTEX_PROGRAM_NV);
\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
262 COpenGLCacheHandler* cacheHandler = Driver->getCacheHandler();
\r
263 if (Alpha || FixedBlending || Blending)
\r
265 cacheHandler->setBlend(false);
\r
267 else if (AlphaTest)
\r
269 cacheHandler->setAlphaTest(false);
\r
274 //! Returns if the material is transparent.
\r
275 bool COpenGLShaderMaterialRenderer::isTransparent() const
\r
277 return (Alpha || Blending || FixedBlending);
\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
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
289 core::stringc errString = type;
\r
290 errString += " compilation failed";
\r
292 errString += " at position ";
\r
294 #if defined(GL_ARB_vertex_program) || defined(GL_ARB_fragment_program)
\r
295 glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errPos );
\r
297 glGetIntegerv( GL_PROGRAM_ERROR_POSITION_NV, &errPos );
\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
304 errString += reinterpret_cast<const char*>(glGetString(GL_PROGRAM_ERROR_STRING_NV));
\r
307 core::stringc errString("Shaders not supported.");
\r
309 os::Printer::log(errString.c_str(), ELL_ERROR);
\r
314 bool COpenGLShaderMaterialRenderer::createPixelShader(const c8* pxsh)
\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
324 for (u32 i=0; i<numShaders; ++i)
\r
332 shdr = inshdr.subString(0, pos);
\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
338 shdr += inshdr.subString(pos+16, inshdr.size()-pos-16);
\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
347 // clear error buffer
\r
348 while(glGetError() != GL_NO_ERROR)
\r
351 #ifdef GL_ARB_fragment_program
\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
360 if (checkError("Pixel shader"))
\r
362 Driver->extGlDeletePrograms(1, &PixelShader[i]);
\r
373 bool COpenGLShaderMaterialRenderer::createVertexShader(const c8* vtxsh)
\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
385 // clear error buffer
\r
386 while(glGetError() != GL_NO_ERROR)
\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
398 if (checkError("Vertex shader"))
\r
400 Driver->extGlDeletePrograms(1, &VertexShader);
\r
410 } // end namespace video
\r
411 } // end namespace irr
\r