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