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