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