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