/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ /** \file gameengine/Rasterizer/RAS_2DFilterManager.cpp * \ingroup bgerast */ #define STRINGIFY(A) #A #include "RAS_OpenGLFilters/RAS_Blur2DFilter.h" #include "RAS_OpenGLFilters/RAS_Sharpen2DFilter.h" #include "RAS_OpenGLFilters/RAS_Dilation2DFilter.h" #include "RAS_OpenGLFilters/RAS_Erosion2DFilter.h" #include "RAS_OpenGLFilters/RAS_Laplacian2DFilter.h" #include "RAS_OpenGLFilters/RAS_Sobel2DFilter.h" #include "RAS_OpenGLFilters/RAS_Prewitt2DFilter.h" #include "RAS_OpenGLFilters/RAS_GrayScale2DFilter.h" #include "RAS_OpenGLFilters/RAS_Sepia2DFilter.h" #include "RAS_OpenGLFilters/RAS_Invert2DFilter.h" #include "STR_String.h" #include "RAS_ICanvas.h" #include "RAS_Rect.h" #include "RAS_2DFilterManager.h" #include #include "glew-mx.h" #include #include "EXP_Value.h" RAS_2DFilterManager::RAS_2DFilterManager(): texturewidth(-1), textureheight(-1), /* numberoffilters(0), */ /* UNUSED */ need_tex_update(true) { isshadersupported = GLEW_ARB_shader_objects && GLEW_ARB_fragment_shader && GLEW_ARB_multitexture; /* used to return before 2.49 but need to initialize values so don't */ if (!isshadersupported) std::cout<<"shaders not supported!" << std::endl; int passindex; for (passindex =0; passindex& propNames) { texflag[passindex] = 0; if (glGetUniformLocationARB(m_filters[passindex], "bgl_DepthTexture") != -1) { if (GLEW_ARB_depth_texture) texflag[passindex] |= 0x1; } if (glGetUniformLocationARB(m_filters[passindex], "bgl_LuminanceTexture") != -1) { texflag[passindex] |= 0x2; } if (m_gameObjects[passindex]) { int objProperties = propNames.size(); int i; for (i=0; iGetProperty(m_properties[passindex][i]); if (!property) continue; switch (property->GetValueType()) { case VALUE_INT_TYPE: glUniform1iARB(uniformLoc, property->GetNumber()); break; case VALUE_FLOAT_TYPE: glUniform1fARB(uniformLoc, property->GetNumber()); break; default: break; } } } void RAS_2DFilterManager::EndShaderProgram() { glUseProgramObjectARB(0); } void RAS_2DFilterManager::FreeTextures() { if (texname[0]!=(unsigned int)-1) glDeleteTextures(1, (GLuint*)&texname[0]); if (texname[1]!=(unsigned int)-1) glDeleteTextures(1, (GLuint*)&texname[1]); if (texname[2]!=(unsigned int)-1) glDeleteTextures(1, (GLuint*)&texname[2]); } void RAS_2DFilterManager::SetupTextures(bool depth, bool luminance) { FreeTextures(); glGenTextures(1, (GLuint*)&texname[0]); glBindTexture(GL_TEXTURE_2D, texname[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texturewidth, textureheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); if (depth) { glGenTextures(1, (GLuint*)&texname[1]); glBindTexture(GL_TEXTURE_2D, texname[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, texturewidth,textureheight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } if (luminance) { glGenTextures(1, (GLuint*)&texname[2]); glBindTexture(GL_TEXTURE_2D, texname[2]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE16, texturewidth, textureheight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } } void RAS_2DFilterManager::UpdateOffsetMatrix(RAS_ICanvas* canvas) { /* RAS_Rect canvas_rect = canvas->GetWindowArea(); */ /* UNUSED */ texturewidth = canvas->GetWidth()+1; textureheight = canvas->GetHeight()+1; GLint i,j; if (!GL_ARB_texture_non_power_of_two) { i = 0; while ((1 << i) <= texturewidth) i++; texturewidth = (1 << (i)); // Now for height i = 0; while ((1 << i) <= textureheight) i++; textureheight = (1 << (i)); } GLfloat xInc = 1.0f / (GLfloat)texturewidth; GLfloat yInc = 1.0f / (GLfloat)textureheight; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { textureoffsets[(((i*3)+j)*2)+0] = (-1.0f * xInc) + ((GLfloat)i * xInc); textureoffsets[(((i*3)+j)*2)+1] = (-1.0f * yInc) + ((GLfloat)j * yInc); } } } void RAS_2DFilterManager::UpdateCanvasTextureCoord(const int viewport[4]) { /* * This function update canvascoord[]. * These parameters are used to create texcoord[1] * That way we can access the texcoord relative to the canvas: * (0.0,0.0) bottom left, (1.0,1.0) top right, (0.5,0.5) center */ canvascoord[0] = (GLfloat) viewport[0] / -viewport[2]; canvascoord[1] = (GLfloat) (texturewidth - viewport[0]) / viewport[2]; canvascoord[2] = (GLfloat) viewport[1] / -viewport[3]; canvascoord[3] = (GLfloat)(textureheight - viewport[1]) / viewport[3]; } void RAS_2DFilterManager::RenderFilters(RAS_ICanvas* canvas) { bool need_depth=false; bool need_luminance=false; int num_filters = 0; int passindex; if (!isshadersupported) return; for (passindex =0; passindexGetViewPort(); if (texturewidth != viewport[2] || textureheight != viewport[3]) { UpdateOffsetMatrix(canvas); UpdateCanvasTextureCoord(viewport); need_tex_update = true; } if (need_tex_update) { SetupTextures(need_depth, need_luminance); need_tex_update = false; } if (need_depth) { glActiveTextureARB(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texname[1]); glCopyTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT, viewport[0], viewport[1], viewport[2], viewport[3], 0); } if (need_luminance) { glActiveTextureARB(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, texname[2]); glCopyTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE16, viewport[0], viewport[1], viewport[2], viewport[3], 0); } // reverting to texunit 0, without this we get bug [#28462] glActiveTextureARB(GL_TEXTURE0); // We do this to make side-by-side stereo rendering work correctly with 2D filters. It would probably be nicer to just set the viewport, // but it can be easier for writing shaders to have the coordinates for the whole screen instead of just part of the screen. RAS_Rect scissor_rect = canvas->GetDisplayArea(); glScissor(scissor_rect.GetLeft() + viewport[0], scissor_rect.GetBottom() + viewport[1], scissor_rect.GetWidth() + 1, scissor_rect.GetHeight() + 1); glDisable(GL_DEPTH_TEST); // in case the previous material was wire glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // if the last rendered face had alpha add it would messes with the color of the plane we apply 2DFilter to glDisable(GL_BLEND); // fix for [#34523] alpha buffer is now available for all OSs glDisable(GL_ALPHA_TEST); glPushMatrix(); //GL_MODELVIEW glLoadIdentity(); // GL_MODELVIEW glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); for (passindex =0; passindex& propNames, void* gameObj, RAS_2DFILTER_MODE mode, int pass, STR_String& text) { if (!isshadersupported) return; if (pass<0 || pass>=MAX_RENDER_PASS) return; need_tex_update = true; if (mode == RAS_2DFILTER_DISABLED) { m_enabled[pass] = 0; return; } if (mode == RAS_2DFILTER_ENABLED) { m_enabled[pass] = 1; return; } if (mode == RAS_2DFILTER_NOFILTER) { if (m_filters[pass]) glDeleteObjectARB(m_filters[pass]); m_enabled[pass] = 0; m_filters[pass] = 0; m_gameObjects[pass] = NULL; m_properties[pass].clear(); texflag[pass] = 0; return; } if (mode == RAS_2DFILTER_CUSTOMFILTER) { if (m_filters[pass]) glDeleteObjectARB(m_filters[pass]); m_filters[pass] = CreateShaderProgram(text.Ptr()); m_gameObjects[pass] = gameObj; AnalyseShader(pass, propNames); m_enabled[pass] = 1; return; } // We've checked all other cases, which means we must be dealing with a builtin filter if (m_filters[pass]) glDeleteObjectARB(m_filters[pass]); m_filters[pass] = CreateShaderProgram(mode); m_gameObjects[pass] = NULL; AnalyseShader(pass, propNames); m_enabled[pass] = 1; }