code cleanup: bge builds with clang without warnings / errors.
[blender.git] / source / gameengine / Rasterizer / RAS_2DFilterManager.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): none yet.
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file gameengine/Rasterizer/RAS_2DFilterManager.cpp
24  *  \ingroup bgerast
25  */
26
27  
28 #define STRINGIFY(A)  #A
29
30 #include "RAS_OpenGLFilters/RAS_Blur2DFilter.h"
31 #include "RAS_OpenGLFilters/RAS_Sharpen2DFilter.h"
32 #include "RAS_OpenGLFilters/RAS_Dilation2DFilter.h"
33 #include "RAS_OpenGLFilters/RAS_Erosion2DFilter.h"
34 #include "RAS_OpenGLFilters/RAS_Laplacian2DFilter.h"
35 #include "RAS_OpenGLFilters/RAS_Sobel2DFilter.h"
36 #include "RAS_OpenGLFilters/RAS_Prewitt2DFilter.h"
37 #include "RAS_OpenGLFilters/RAS_GrayScale2DFilter.h"
38 #include "RAS_OpenGLFilters/RAS_Sepia2DFilter.h"
39 #include "RAS_OpenGLFilters/RAS_Invert2DFilter.h"
40
41 #include "STR_String.h"
42 #include "RAS_ICanvas.h"
43 #include "RAS_Rect.h"
44 #include "RAS_2DFilterManager.h"
45 #include <iostream>
46
47 #include "GL/glew.h"
48
49 #include <stdio.h>
50
51 #include "Value.h"
52
53 RAS_2DFilterManager::RAS_2DFilterManager():
54 texturewidth(-1), textureheight(-1),
55 canvaswidth(-1), canvasheight(-1),
56 /* numberoffilters(0), */ /* UNUSED */ need_tex_update(true)
57 {
58         isshadersupported = GLEW_ARB_shader_objects &&
59                 GLEW_ARB_fragment_shader && GLEW_ARB_multitexture;
60
61         /* used to return before 2.49 but need to initialize values so don't */
62         if (!isshadersupported)
63                 std::cout<<"shaders not supported!" << std::endl;
64
65         int passindex;
66         for (passindex =0; passindex<MAX_RENDER_PASS; passindex++)
67         {
68                 m_filters[passindex] = 0;
69                 m_enabled[passindex] = 0;
70                 texflag[passindex] = 0;
71                 m_gameObjects[passindex] = NULL;
72         }
73         texname[0] = texname[1] = texname[2] = -1;
74         errorprinted= false;
75 }
76
77 RAS_2DFilterManager::~RAS_2DFilterManager()
78 {
79         FreeTextures();
80 }
81
82 void RAS_2DFilterManager::PrintShaderErrors(unsigned int shader, const char *task, const char *code)
83 {
84         GLcharARB log[5000];
85         GLsizei length = 0;
86         const char *c, *pos, *end;
87         int line = 1;
88
89         if (errorprinted)
90                 return;
91         
92         errorprinted= true;
93
94         glGetInfoLogARB(shader, sizeof(log), &length, log);
95         end = code + strlen(code);
96
97         printf("2D Filter GLSL Shader: %s error:\n", task);
98
99         c = code;
100         while ((c < end) && (pos = strchr(c, '\n'))) {
101                 printf("%2d  ", line);
102                 fwrite(c, (pos+1)-c, 1, stdout);
103                 c = pos+1;
104                 line++;
105         }
106
107         puts(c);
108         puts(log);
109         puts("\n");
110 }
111
112 unsigned int RAS_2DFilterManager::CreateShaderProgram(const char* shadersource)
113 {
114         GLuint program = 0;     
115         GLuint fShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER);
116         GLint success;
117
118         glShaderSourceARB(fShader, 1, (const char**)&shadersource, NULL);
119
120         glCompileShaderARB(fShader);
121
122
123         glGetObjectParameterivARB(fShader, GL_COMPILE_STATUS, &success);
124         if (!success)
125         {
126                 /*Shader Comile Error*/
127                 PrintShaderErrors(fShader, "compile", shadersource);
128                 return 0;
129         }
130                 
131         program = glCreateProgramObjectARB();
132         glAttachObjectARB(program, fShader);
133
134         glLinkProgramARB(program);
135         glGetObjectParameterivARB(program, GL_LINK_STATUS, &success);
136         if (!success)
137         {
138                 /*Program Link Error*/
139                 PrintShaderErrors(fShader, "link", shadersource);
140                 return 0;
141         }
142         
143         glValidateProgramARB(program);
144         glGetObjectParameterivARB(program, GL_VALIDATE_STATUS, &success);
145         if (!success)
146         {
147                 /*Program Validation Error*/
148                 PrintShaderErrors(fShader, "validate", shadersource);
149                 return 0;
150         }
151
152         return program;
153 }
154
155 unsigned int RAS_2DFilterManager::CreateShaderProgram(int filtermode)
156 {
157         switch(filtermode)
158         {
159                 case RAS_2DFILTER_BLUR:
160                         return CreateShaderProgram(BlurFragmentShader);
161                 case RAS_2DFILTER_SHARPEN:
162                         return CreateShaderProgram(SharpenFragmentShader);
163                 case RAS_2DFILTER_DILATION:
164                         return CreateShaderProgram(DilationFragmentShader);
165                 case RAS_2DFILTER_EROSION:
166                         return CreateShaderProgram(ErosionFragmentShader);
167                 case RAS_2DFILTER_LAPLACIAN:
168                         return CreateShaderProgram(LaplacionFragmentShader);
169                 case RAS_2DFILTER_SOBEL:
170                         return CreateShaderProgram(SobelFragmentShader);
171                 case RAS_2DFILTER_PREWITT:
172                         return CreateShaderProgram(PrewittFragmentShader);
173                 case RAS_2DFILTER_GRAYSCALE:
174                         return CreateShaderProgram(GrayScaleFragmentShader);
175                 case RAS_2DFILTER_SEPIA:
176                         return CreateShaderProgram(SepiaFragmentShader);
177                 case RAS_2DFILTER_INVERT:
178                         return CreateShaderProgram(InvertFragmentShader);
179         }
180         return 0;
181 }
182
183 void RAS_2DFilterManager::AnalyseShader(int passindex, vector<STR_String>& propNames)
184 {
185         texflag[passindex] = 0;
186         if (glGetUniformLocationARB(m_filters[passindex], "bgl_DepthTexture") != -1)
187         {
188                 if (GLEW_ARB_depth_texture)
189                         texflag[passindex] |= 0x1;
190         }
191         if (glGetUniformLocationARB(m_filters[passindex], "bgl_LuminanceTexture") != -1)
192         {
193                 texflag[passindex] |= 0x2;
194         }
195
196         if (m_gameObjects[passindex])
197         {
198                 int objProperties = propNames.size();
199                 int i;
200                 for (i=0; i<objProperties; i++)
201                         if (glGetUniformLocationARB(m_filters[passindex], propNames[i]) != -1)
202                                 m_properties[passindex].push_back(propNames[i]);
203         }
204 }
205
206 void RAS_2DFilterManager::StartShaderProgram(int passindex)
207 {
208         GLint uniformLoc;
209         glUseProgramObjectARB(m_filters[passindex]);
210         uniformLoc = glGetUniformLocationARB(m_filters[passindex], "bgl_RenderedTexture");
211         glActiveTextureARB(GL_TEXTURE0);
212         glBindTexture(GL_TEXTURE_2D, texname[0]);
213
214         if (uniformLoc != -1)
215         {
216                 glUniform1iARB(uniformLoc, 0);
217         }
218
219         /* send depth texture to glsl program if it needs */
220         if (texflag[passindex] & 0x1) {
221                 uniformLoc = glGetUniformLocationARB(m_filters[passindex], "bgl_DepthTexture");
222                 glActiveTextureARB(GL_TEXTURE1);
223                 glBindTexture(GL_TEXTURE_2D, texname[1]);
224
225                 if (uniformLoc != -1)
226                 {
227                         glUniform1iARB(uniformLoc, 1);
228                 }
229         }
230
231         /* send luminance texture to glsl program if it needs */
232         if (texflag[passindex] & 0x2) {
233                 uniformLoc = glGetUniformLocationARB(m_filters[passindex], "bgl_LuminanceTexture");
234                 glActiveTextureARB(GL_TEXTURE2);
235                 glBindTexture(GL_TEXTURE_2D, texname[2]);
236
237                 if (uniformLoc != -1)
238                 {
239                         glUniform1iARB(uniformLoc, 2);
240                 }
241         }
242         
243         uniformLoc = glGetUniformLocationARB(m_filters[passindex], "bgl_TextureCoordinateOffset");
244         if (uniformLoc != -1)
245         {
246                 glUniform2fvARB(uniformLoc, 9, textureoffsets);
247         }
248         uniformLoc = glGetUniformLocationARB(m_filters[passindex], "bgl_RenderedTextureWidth");
249         if (uniformLoc != -1)
250         {
251                 glUniform1fARB(uniformLoc,texturewidth);
252         }
253         uniformLoc = glGetUniformLocationARB(m_filters[passindex], "bgl_RenderedTextureHeight");
254         if (uniformLoc != -1)
255         {
256                 glUniform1fARB(uniformLoc,textureheight);
257         }
258
259         int i, objProperties = m_properties[passindex].size();
260         for (i=0; i<objProperties; i++)
261         {
262                 uniformLoc = glGetUniformLocationARB(m_filters[passindex], m_properties[passindex][i]);
263                 if (uniformLoc != -1)
264                 {
265                         float value = ((CValue*)m_gameObjects[passindex])->GetPropertyNumber(m_properties[passindex][i], 0.0);
266                         glUniform1fARB(uniformLoc,value);
267                 }
268         }
269 }
270
271 void RAS_2DFilterManager::EndShaderProgram()
272 {
273         glUseProgramObjectARB(0);
274 }
275
276 void RAS_2DFilterManager::FreeTextures()
277 {
278         if (texname[0]!=(unsigned int)-1)
279                 glDeleteTextures(1, (GLuint*)&texname[0]);
280         if (texname[1]!=(unsigned int)-1)
281                 glDeleteTextures(1, (GLuint*)&texname[1]);
282         if (texname[2]!=(unsigned int)-1)
283                 glDeleteTextures(1, (GLuint*)&texname[2]);
284 }
285
286 void RAS_2DFilterManager::SetupTextures(bool depth, bool luminance)
287 {
288         FreeTextures();
289         
290         glGenTextures(1, (GLuint*)&texname[0]);
291         glBindTexture(GL_TEXTURE_2D, texname[0]);
292         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texturewidth, textureheight, 0, GL_RGBA,
293                         GL_UNSIGNED_BYTE, 0);
294         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
295         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
296         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
297         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
298
299         if (depth) {
300                 glGenTextures(1, (GLuint*)&texname[1]);
301                 glBindTexture(GL_TEXTURE_2D, texname[1]);
302                 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, texturewidth,textureheight,
303                              0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL);
304                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
305                                 GL_NONE);
306                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
307                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
308                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
309                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
310         }
311
312         if (luminance) {
313                 glGenTextures(1, (GLuint*)&texname[2]);
314                 glBindTexture(GL_TEXTURE_2D, texname[2]);
315                 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE16, texturewidth, textureheight,
316                          0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
317                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
318                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
319                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
320                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
321         }
322 }
323
324 void RAS_2DFilterManager::UpdateOffsetMatrix(RAS_ICanvas* canvas)
325 {
326         /* RAS_Rect canvas_rect = canvas->GetWindowArea(); */ /* UNUSED */
327         texturewidth = canvas->GetWidth();
328         textureheight = canvas->GetHeight();
329         GLint i,j;
330
331         if (!GL_ARB_texture_non_power_of_two)
332         {
333                 i = 0;
334                 while ((1 << i) <= texturewidth)
335                         i++;
336                 texturewidth = (1 << (i));
337
338                 // Now for height
339                 i = 0;
340                 while ((1 << i) <= textureheight)
341                         i++;
342                 textureheight = (1 << (i));
343         }
344
345         GLfloat xInc = 1.0f / (GLfloat)texturewidth;
346         GLfloat yInc = 1.0f / (GLfloat)textureheight;
347         
348         for (i = 0; i < 3; i++)
349         {
350                 for (j = 0; j < 3; j++)
351                 {
352                         textureoffsets[(((i*3)+j)*2)+0] = (-1.0f * xInc) + ((GLfloat)i * xInc);
353                         textureoffsets[(((i*3)+j)*2)+1] = (-1.0f * yInc) + ((GLfloat)j * yInc);
354                 }
355         }
356 }
357
358 void RAS_2DFilterManager::UpdateCanvasTextureCoord(unsigned int * viewport)
359 {
360         /*
361          * This function update canvascoord[].
362          * These parameters are used to create texcoord[1]
363          * That way we can access the texcoord relative to the canvas:
364          * (0.0,0.0) bottom left, (1.0,1.0) top right, (0.5,0.5) center
365          */
366         canvascoord[0] = (GLfloat) viewport[0] / viewport[2];
367         canvascoord[0] *= -1;
368         canvascoord[1] = (GLfloat) (texturewidth - viewport[0]) / viewport[2];
369  
370         canvascoord[2] = (GLfloat) viewport[1] / viewport[3];
371         canvascoord[2] *= -1;
372         canvascoord[3] = (GLfloat)(textureheight - viewport[1]) / viewport[3];
373 }
374
375 void RAS_2DFilterManager::RenderFilters(RAS_ICanvas* canvas)
376 {
377         bool need_depth=false;
378         bool need_luminance=false;
379         int num_filters = 0;
380
381         int passindex;
382
383         if (!isshadersupported)
384                 return;
385
386         for (passindex =0; passindex<MAX_RENDER_PASS; passindex++)
387         {
388                 if (m_filters[passindex] && m_enabled[passindex]) {
389                         num_filters ++;
390                         if (texflag[passindex] & 0x1)
391                                 need_depth = true;
392                         if (texflag[passindex] & 0x2)
393                                 need_luminance = true;
394                         if (need_depth && need_luminance)
395                                 break;
396                 }
397         }
398
399         if (num_filters <= 0)
400                 return;
401
402         GLuint  viewport[4]={0};
403         glGetIntegerv(GL_VIEWPORT,(GLint *)viewport);
404         RAS_Rect rect = canvas->GetWindowArea();
405
406         if (canvaswidth != canvas->GetWidth() || canvasheight != canvas->GetHeight())
407         {
408                 UpdateOffsetMatrix(canvas);
409                 UpdateCanvasTextureCoord((unsigned int*)viewport);
410                 need_tex_update = true;
411         }
412         
413         if (need_tex_update)
414         {
415                 SetupTextures(need_depth, need_luminance);
416                 need_tex_update = false;
417         }
418
419         if (need_depth) {
420                 glActiveTextureARB(GL_TEXTURE1);
421                 glBindTexture(GL_TEXTURE_2D, texname[1]);
422                 glCopyTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT, rect.GetLeft(), rect.GetBottom(), rect.GetWidth(), rect.GetHeight(), 0);
423         }
424         
425         if (need_luminance) {
426                 glActiveTextureARB(GL_TEXTURE2);
427                 glBindTexture(GL_TEXTURE_2D, texname[2]);
428                 glCopyTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE16, rect.GetLeft(), rect.GetBottom(), rect.GetWidth(), rect.GetHeight(), 0);
429         }
430
431         // reverting to texunit 0, without this we get bug [#28462]
432         glActiveTextureARB(GL_TEXTURE0);
433
434         glViewport(rect.GetLeft(), rect.GetBottom(), texturewidth, textureheight);
435
436         glDisable(GL_DEPTH_TEST);
437         // in case the previous material was wire
438         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
439         // if the last rendered face had alpha add it would messes with the color of the plane we apply 2DFilter to
440         glDisable(GL_BLEND); 
441         glPushMatrix();         //GL_MODELVIEW
442         glLoadIdentity();       // GL_MODELVIEW
443         glMatrixMode(GL_TEXTURE);
444         glLoadIdentity();
445         glMatrixMode(GL_PROJECTION);
446         glPushMatrix();
447         glLoadIdentity();
448
449         for (passindex =0; passindex<MAX_RENDER_PASS; passindex++)
450         {
451                 if (m_filters[passindex] && m_enabled[passindex])
452                 {
453                         StartShaderProgram(passindex);
454
455                         glActiveTextureARB(GL_TEXTURE0);
456                         glBindTexture(GL_TEXTURE_2D, texname[0]);
457                         glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, rect.GetLeft(), rect.GetBottom(), rect.GetWidth(), rect.GetHeight(), 0); // Don't use texturewidth and textureheight in case we don't have NPOT support
458                         glClear(GL_COLOR_BUFFER_BIT);
459
460                         glBegin(GL_QUADS);
461                                 glColor4f(1.f, 1.f, 1.f, 1.f);
462                                 glTexCoord2f(1.0, 1.0); glMultiTexCoord2fARB(GL_TEXTURE3_ARB, canvascoord[1], canvascoord[3]); glVertex2f(1,1);
463                                 glTexCoord2f(0.0, 1.0); glMultiTexCoord2fARB(GL_TEXTURE3_ARB, canvascoord[0], canvascoord[3]); glVertex2f(-1,1);
464                                 glTexCoord2f(0.0, 0.0); glMultiTexCoord2fARB(GL_TEXTURE3_ARB, canvascoord[0], canvascoord[2]); glVertex2f(-1,-1);
465                                 glTexCoord2f(1.0, 0.0); glMultiTexCoord2fARB(GL_TEXTURE3_ARB, canvascoord[1], canvascoord[2]); glVertex2f(1,-1);
466                         glEnd();
467                 }
468         }
469
470         glEnable(GL_DEPTH_TEST);
471         glViewport(viewport[0],viewport[1],viewport[2],viewport[3]);
472         EndShaderProgram();     
473         glPopMatrix();
474         glMatrixMode(GL_MODELVIEW);
475         glPopMatrix();
476 }
477
478 void RAS_2DFilterManager::EnableFilter(vector<STR_String>& propNames, void* gameObj, RAS_2DFILTER_MODE mode, int pass, STR_String& text)
479 {
480         if (!isshadersupported)
481                 return;
482         if (pass<0 || pass>=MAX_RENDER_PASS)
483                 return;
484         need_tex_update = true;
485         if (mode == RAS_2DFILTER_DISABLED)
486         {
487                 m_enabled[pass] = 0;
488                 return;
489         }
490
491         if (mode == RAS_2DFILTER_ENABLED)
492         {
493                 m_enabled[pass] = 1;
494                 return;
495         }
496
497         if (mode == RAS_2DFILTER_NOFILTER)
498         {
499                 if (m_filters[pass])
500                         glDeleteObjectARB(m_filters[pass]);
501                 m_enabled[pass] = 0;
502                 m_filters[pass] = 0;
503                 m_gameObjects[pass] = NULL;
504                 m_properties[pass].clear();
505                 texflag[pass] = 0;
506                 return;
507         }
508         
509         if (mode == RAS_2DFILTER_CUSTOMFILTER)
510         {
511                 if (m_filters[pass])
512                         glDeleteObjectARB(m_filters[pass]);
513                 m_filters[pass] = CreateShaderProgram(text.Ptr());
514                 m_gameObjects[pass] = gameObj;
515                 AnalyseShader(pass, propNames);
516                 m_enabled[pass] = 1;
517                 return;
518         }
519
520         // We've checked all other cases, which means we must be dealing with a builtin filter
521         if (m_filters[pass])
522                 glDeleteObjectARB(m_filters[pass]);
523         m_filters[pass] = CreateShaderProgram(mode);
524         m_gameObjects[pass] = NULL;
525         AnalyseShader(pass, propNames);
526         m_enabled[pass] = 1;
527 }