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