02f23a8431b7a0cc3327a0a502f943b68a02829c
[blender.git] / source / gameengine / GamePlayer / common / GPC_RenderTools.cpp
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include "GL/glew.h"
31
32 #include "BMF_Api.h"
33
34 #include "DNA_scene_types.h"
35
36 #include "RAS_IRenderTools.h"
37 #include "RAS_IRasterizer.h"
38 #include "RAS_LightObject.h"
39 #include "RAS_ICanvas.h"
40 #include "RAS_GLExtensionManager.h"
41
42 #include "KX_GameObject.h"
43 #include "KX_PolygonMaterial.h"
44 #include "KX_BlenderMaterial.h"
45 #include "KX_RayCast.h"
46 #include "KX_IPhysicsController.h"
47
48 #include "PHY_IPhysicsEnvironment.h"
49
50 #include "STR_String.h"
51
52 #include "GPU_draw.h"
53
54 #include "BKE_bmfont.h" // for text printing
55 #include "BKE_bmfont_types.h"
56
57 #include "GPC_RenderTools.h"
58
59
60 unsigned int GPC_RenderTools::m_numgllights;
61
62 GPC_RenderTools::GPC_RenderTools()
63 {
64         m_font = BMF_GetFont(BMF_kHelvetica10);
65
66         glGetIntegerv(GL_MAX_LIGHTS, (GLint*) &m_numgllights);
67         if (m_numgllights < 8)
68                 m_numgllights = 8;
69 }
70
71 GPC_RenderTools::~GPC_RenderTools()
72 {
73 }
74
75 void GPC_RenderTools::BeginFrame(RAS_IRasterizer* rasty)
76 {
77         m_clientobject = NULL;
78         m_lastlightlayer = -1;
79         m_lastauxinfo = NULL;
80         m_lastlighting = true; /* force disable in DisableOpenGLLights() */
81         DisableOpenGLLights();
82 }
83
84 void GPC_RenderTools::EndFrame(RAS_IRasterizer* rasty)
85 {
86 }
87
88 /* ProcessLighting performs lighting on objects. the layer is a bitfield that
89  * contains layer information. There are 20 'official' layers in blender. A
90  * light is applied on an object only when they are in the same layer. OpenGL
91  * has a maximum of 8 lights (simultaneous), so 20 * 8 lights are possible in
92  * a scene. */
93
94 void GPC_RenderTools::ProcessLighting(RAS_IRasterizer *rasty, bool uselights, const MT_Transform& viewmat)
95 {
96         bool enable = false;
97         int layer= -1;
98
99         /* find the layer */
100         if(uselights) {
101                 if(m_clientobject)
102                         layer = static_cast<KX_GameObject*>(m_clientobject)->GetLayer();
103         }
104
105         /* avoid state switching */
106         if(m_lastlightlayer == layer && m_lastauxinfo == m_auxilaryClientInfo)
107                 return;
108
109         m_lastlightlayer = layer;
110         m_lastauxinfo = m_auxilaryClientInfo;
111
112         /* enable/disable lights as needed */
113         if(layer >= 0)
114                 enable = applyLights(layer, viewmat);
115
116         if(enable)
117                 EnableOpenGLLights(rasty);
118         else
119                 DisableOpenGLLights();
120 }
121
122 void GPC_RenderTools::EnableOpenGLLights(RAS_IRasterizer *rasty)
123 {
124         if(m_lastlighting == true)
125                 return;
126
127         glEnable(GL_LIGHTING);
128         glEnable(GL_COLOR_MATERIAL);
129
130         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
131         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
132         glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, (rasty->GetCameraOrtho())? GL_FALSE: GL_TRUE);
133         if (GLEW_EXT_separate_specular_color || GLEW_VERSION_1_2)
134                 glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
135         
136         m_lastlighting = true;
137 }
138
139 void GPC_RenderTools::DisableOpenGLLights()
140 {
141         if(m_lastlighting == false)
142                 return;
143
144         glDisable(GL_LIGHTING);
145         glDisable(GL_COLOR_MATERIAL);
146
147         m_lastlighting = false;
148 }
149
150
151 void GPC_RenderTools::SetClientObject(RAS_IRasterizer *rasty, void* obj)
152 {
153         if (m_clientobject != obj)
154         {
155                 bool ccw = (obj == NULL || !((KX_GameObject*)obj)->IsNegativeScaling());
156                 rasty->SetFrontFace(ccw);
157
158                 m_clientobject = obj;
159         }
160 }
161
162 bool GPC_RenderTools::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
163 {
164         double* const oglmatrix = (double* const) data;
165         MT_Point3 resultpoint(result->m_hitPoint);
166         MT_Vector3 resultnormal(result->m_hitNormal);
167         MT_Vector3 left(oglmatrix[0],oglmatrix[1],oglmatrix[2]);
168         MT_Vector3 dir = -(left.cross(resultnormal)).safe_normalized();
169         left = (dir.cross(resultnormal)).safe_normalized();
170         // for the up vector, we take the 'resultnormal' returned by the physics
171         
172         double maat[16]={
173                         left[0],        left[1],        left[2], 0,
174                                 dir[0],         dir[1],         dir[2], 0,
175                 resultnormal[0],resultnormal[1],resultnormal[2], 0,
176                                 0,              0,              0, 1};
177         glTranslated(resultpoint[0],resultpoint[1],resultpoint[2]);
178         //glMultMatrixd(oglmatrix);
179         glMultMatrixd(maat);
180         return true;
181 }
182
183 void GPC_RenderTools::applyTransform(RAS_IRasterizer* rasty,double* oglmatrix,int objectdrawmode )
184 {
185         /* FIXME:
186         blender: intern/moto/include/MT_Vector3.inl:42: MT_Vector3 operator/(const
187         MT_Vector3&, double): Assertion `!MT_fuzzyZero(s)' failed. 
188         
189         Program received signal SIGABRT, Aborted. 
190         [Switching to Thread 16384 (LWP 1519)] 
191         0x40477571 in kill () from /lib/libc.so.6 
192         (gdb) bt 
193         #7  0x08334368 in MT_Vector3::normalized() const () 
194         #8  0x0833e6ec in GPC_RenderTools::applyTransform(RAS_IRasterizer*, double*, int) () 
195         */
196
197         if (objectdrawmode & RAS_IPolyMaterial::BILLBOARD_SCREENALIGNED ||
198                 objectdrawmode & RAS_IPolyMaterial::BILLBOARD_AXISALIGNED)
199         {
200                 // rotate the billboard/halo
201                 //page 360/361 3D Game Engine Design, David Eberly for a discussion
202                 // on screen aligned and axis aligned billboards
203                 // assumed is that the preprocessor transformed all billboard polygons
204                 // so that their normal points into the positive x direction (1.0 , 0.0 , 0.0)
205                 // when new parenting for objects is done, this rotation
206                 // will be moved into the object
207                 
208                 MT_Point3 objpos (oglmatrix[12],oglmatrix[13],oglmatrix[14]);
209                 MT_Point3 campos = rasty->GetCameraPosition();
210                 MT_Vector3 dir = (campos - objpos).safe_normalized();
211                 MT_Vector3 up(0,0,1.0);
212
213                 KX_GameObject* gameobj = (KX_GameObject*) this->m_clientobject;
214                 // get scaling of halo object
215                 MT_Vector3  size = gameobj->GetSGNode()->GetLocalScale();
216                 
217                 bool screenaligned = (objectdrawmode & RAS_IPolyMaterial::BILLBOARD_SCREENALIGNED)!=0;//false; //either screen or axisaligned
218                 if (screenaligned)
219                 {
220                         up = (up - up.dot(dir) * dir).safe_normalized();
221                 } else
222                 {
223                         dir = (dir - up.dot(dir)*up).safe_normalized();
224                 }
225
226                 MT_Vector3 left = dir.normalized();
227                 dir = (left.cross(up)).normalized();
228
229                 // we have calculated the row vectors, now we keep
230                 // local scaling into account:
231
232                 left *= size[0];
233                 dir  *= size[1];
234                 up   *= size[2];
235                 double maat[16]={
236                         left[0], left[1],left[2], 0,
237                                 dir[0], dir[1],dir[2],0,
238                                 up[0],up[1],up[2],0,
239                                 0,0,0,1};
240                         glTranslated(objpos[0],objpos[1],objpos[2]);
241                         glMultMatrixd(maat);
242                         
243         } else
244         {
245                 if (objectdrawmode & RAS_IPolyMaterial::SHADOW)
246                 {
247                         // shadow must be cast to the ground, physics system needed here!
248                         MT_Point3 frompoint(oglmatrix[12],oglmatrix[13],oglmatrix[14]);
249                         KX_GameObject *gameobj = (KX_GameObject*) this->m_clientobject;
250                         MT_Vector3 direction = MT_Vector3(0,0,-1);
251
252                         direction.normalize();
253                         direction *= 100000;
254
255                         MT_Point3 topoint = frompoint + direction;
256
257                         KX_Scene* kxscene = (KX_Scene*) m_auxilaryClientInfo;
258                         PHY_IPhysicsEnvironment* physics_environment = kxscene->GetPhysicsEnvironment();
259                         KX_IPhysicsController* physics_controller = gameobj->GetPhysicsController();
260                         
261                         KX_GameObject *parent = gameobj->GetParent();
262                         if (!physics_controller && parent)
263                                 physics_controller = parent->GetPhysicsController();
264                         if (parent)
265                                 parent->Release();
266                                 
267                         KX_RayCast::Callback<GPC_RenderTools> callback(this, physics_controller, oglmatrix);
268                         if (!KX_RayCast::RayTest(physics_environment, frompoint, topoint, callback))
269                         {
270                                 // couldn't find something to cast the shadow on...
271                                 glMultMatrixd(oglmatrix);
272                         }
273                 } else
274                 {
275
276                         // 'normal' object
277                         glMultMatrixd(oglmatrix);
278                 }
279         }
280 }
281
282
283 void GPC_RenderTools::RenderText2D(RAS_TEXT_RENDER_MODE mode,
284                                                                                  const char* text,
285                                                                                  int xco,
286                                                                                  int yco,                                                                        
287                                                                                  int width,
288                                                                                  int height)
289 {
290         STR_String tmpstr(text);
291         char* s = tmpstr.Ptr();
292
293         // Save and change OpenGL settings
294         int texture2D;
295         glGetIntegerv(GL_TEXTURE_2D, (GLint*)&texture2D);
296         glDisable(GL_TEXTURE_2D);
297         int fog;
298         glGetIntegerv(GL_FOG, (GLint*)&fog);
299         glDisable(GL_FOG);
300         
301         int light;
302         glGetIntegerv(GL_LIGHTING, (GLint*)&light);
303         glDisable(GL_LIGHTING);
304
305         
306         // Set up viewing settings
307         glMatrixMode(GL_PROJECTION);
308         glPushMatrix();
309         glLoadIdentity();
310         glOrtho(0, width, 0, height, -1, 1);
311         glMatrixMode(GL_MODELVIEW);
312         glPushMatrix();
313         glLoadIdentity();
314
315         // Actual drawing (draw black first if padded)
316         if (mode == RAS_IRenderTools::RAS_TEXT_PADDED)
317         {
318                 glColor3ub(0, 0, 0);
319                 glRasterPos2s(xco+1, height-yco-1);
320                 BMF_DrawString(m_font, s);
321         }
322
323         glColor3ub(255, 255, 255);
324         glRasterPos2s(xco, height-yco);
325         BMF_DrawString(m_font, s);
326
327         // Restore view settings
328         glMatrixMode(GL_PROJECTION);
329         glPopMatrix();
330         glMatrixMode(GL_MODELVIEW);
331         glPopMatrix();
332
333         // Restore OpenGL Settings
334         if (fog)
335                 glEnable(GL_FOG);
336         else
337                 glDisable(GL_FOG);
338         
339         if (texture2D)
340                 glEnable(GL_TEXTURE_2D);
341         else
342                 glDisable(GL_TEXTURE_2D);
343         if (light)
344                 glEnable(GL_LIGHTING);
345         else
346                 glDisable(GL_LIGHTING);
347 }
348
349 /* Render Text renders text into a (series of) polygon, using a texture font,
350  * Each character consists of one polygon (one quad or two triangles) */
351
352 void GPC_RenderTools::RenderText(
353         int mode,
354         RAS_IPolyMaterial* polymat,
355         float v1[3], float v2[3], float v3[3], float v4[3], int glattrib)
356 {
357         STR_String mytext = ((CValue*)m_clientobject)->GetPropertyText("Text");
358         
359         const unsigned int flag = polymat->GetFlag();
360         struct MTFace* tface = 0;
361         unsigned int *col = 0;
362
363         if(flag & RAS_BLENDERMAT) {
364                 KX_BlenderMaterial *bl_mat = static_cast<KX_BlenderMaterial*>(polymat);
365                 tface = bl_mat->GetMTFace();
366                 col = bl_mat->GetMCol();
367         } else {
368                 KX_PolygonMaterial* blenderpoly = static_cast<KX_PolygonMaterial*>(polymat);
369                 tface = blenderpoly->GetMTFace();
370                 col = blenderpoly->GetMCol();
371         }
372         
373         GPU_render_text(tface, mode, mytext, mytext.Length(), col, v1, v2, v3, v4, glattrib);
374 }
375
376
377 void GPC_RenderTools::PushMatrix()
378 {
379         glPushMatrix();
380 }
381
382 void GPC_RenderTools::PopMatrix()
383 {
384         glPopMatrix();
385 }
386
387
388 int GPC_RenderTools::applyLights(int objectlayer, const MT_Transform& viewmat)
389 {
390         // taken from blender source, incompatibility between Blender Object / GameObject       
391         KX_Scene* kxscene = (KX_Scene*)m_auxilaryClientInfo;
392         int scenelayer = ~0;
393         float glviewmat[16];
394         unsigned int count;
395         float vec[4];
396
397         vec[3]= 1.0;
398
399         if(kxscene && kxscene->GetBlenderScene())
400                 scenelayer = kxscene->GetBlenderScene()->lay;
401         
402         for(count=0; count<m_numgllights; count++)
403                 glDisable((GLenum)(GL_LIGHT0+count));
404         
405         //std::vector<struct    RAS_LightObject*> m_lights;
406         std::vector<struct      RAS_LightObject*>::iterator lit = m_lights.begin();
407
408         viewmat.getValue(glviewmat);
409         
410         glPushMatrix();
411         glLoadMatrixf(glviewmat);
412         for (lit = m_lights.begin(), count = 0; !(lit==m_lights.end()) && count < m_numgllights; ++lit)
413         {
414                 RAS_LightObject* lightdata = (*lit);
415                 KX_Scene* lightscene = (KX_Scene*)lightdata->m_scene;
416
417                 /* only use lights in the same layer as the object */
418                 if(!(lightdata->m_layer & objectlayer))
419                         continue;
420                 /* only use lights in the same scene, and in a visible layer */
421                 if(kxscene != lightscene || !(lightdata->m_layer & scenelayer))
422                         continue;
423
424                 vec[0] = (*(lightdata->m_worldmatrix))(0,3);
425                 vec[1] = (*(lightdata->m_worldmatrix))(1,3);
426                 vec[2] = (*(lightdata->m_worldmatrix))(2,3);
427                 vec[3] = 1;
428
429                 if(lightdata->m_type==RAS_LightObject::LIGHT_SUN) {
430                         
431                         vec[0] = (*(lightdata->m_worldmatrix))(0,2);
432                         vec[1] = (*(lightdata->m_worldmatrix))(1,2);
433                         vec[2] = (*(lightdata->m_worldmatrix))(2,2);
434                         //vec[0]= base->object->obmat[2][0];
435                         //vec[1]= base->object->obmat[2][1];
436                         //vec[2]= base->object->obmat[2][2];
437                         vec[3]= 0.0;
438                         glLightfv((GLenum)(GL_LIGHT0+count), GL_POSITION, vec); 
439                 }
440                 else {
441                         //vec[3]= 1.0;
442                         glLightfv((GLenum)(GL_LIGHT0+count), GL_POSITION, vec); 
443                         glLightf((GLenum)(GL_LIGHT0+count), GL_CONSTANT_ATTENUATION, 1.0);
444                         glLightf((GLenum)(GL_LIGHT0+count), GL_LINEAR_ATTENUATION, lightdata->m_att1/lightdata->m_distance);
445                         // without this next line it looks backward compatible.
446                         //attennuation still is acceptable 
447                         glLightf((GLenum)(GL_LIGHT0+count), GL_QUADRATIC_ATTENUATION, lightdata->m_att2/(lightdata->m_distance*lightdata->m_distance)); 
448                         
449                         if(lightdata->m_type==RAS_LightObject::LIGHT_SPOT) {
450                                 vec[0] = -(*(lightdata->m_worldmatrix))(0,2);
451                                 vec[1] = -(*(lightdata->m_worldmatrix))(1,2);
452                                 vec[2] = -(*(lightdata->m_worldmatrix))(2,2);
453                                 //vec[0]= -base->object->obmat[2][0];
454                                 //vec[1]= -base->object->obmat[2][1];
455                                 //vec[2]= -base->object->obmat[2][2];
456                                 glLightfv((GLenum)(GL_LIGHT0+count), GL_SPOT_DIRECTION, vec);
457                                 glLightf((GLenum)(GL_LIGHT0+count), GL_SPOT_CUTOFF, lightdata->m_spotsize/2.0);
458                                 glLightf((GLenum)(GL_LIGHT0+count), GL_SPOT_EXPONENT, 128.0*lightdata->m_spotblend);
459                         }
460                         else glLightf((GLenum)(GL_LIGHT0+count), GL_SPOT_CUTOFF, 180.0);
461                 }
462                 
463                 if (lightdata->m_nodiffuse)
464                 {
465                         vec[0] = vec[1] = vec[2] = vec[3] = 0.0;
466                 } else {
467                         vec[0]= lightdata->m_energy*lightdata->m_red;
468                         vec[1]= lightdata->m_energy*lightdata->m_green;
469                         vec[2]= lightdata->m_energy*lightdata->m_blue;
470                         vec[3]= 1.0;
471                 }
472                 glLightfv((GLenum)(GL_LIGHT0+count), GL_DIFFUSE, vec);
473                 if (lightdata->m_nospecular)
474                 {
475                         vec[0] = vec[1] = vec[2] = vec[3] = 0.0;
476                 } else if (lightdata->m_nodiffuse) {
477                         vec[0]= lightdata->m_energy*lightdata->m_red;
478                         vec[1]= lightdata->m_energy*lightdata->m_green;
479                         vec[2]= lightdata->m_energy*lightdata->m_blue;
480                         vec[3]= 1.0;
481                 }
482                 glLightfv((GLenum)(GL_LIGHT0+count), GL_SPECULAR, vec);
483                 glEnable((GLenum)(GL_LIGHT0+count));
484
485                 count++;
486         }
487         glPopMatrix();
488
489         return count;
490
491 }
492
493 void GPC_RenderTools::MotionBlur(RAS_IRasterizer* rasterizer)
494 {
495         int state = rasterizer->GetMotionBlurState();
496         float motionblurvalue;
497         if(state)
498         {
499                 motionblurvalue = rasterizer->GetMotionBlurValue();
500                 if(state==1)
501                 {
502                         //bugfix:load color buffer into accum buffer for the first time(state=1)
503                         glAccum(GL_LOAD, 1.0);
504                         rasterizer->SetMotionBlurState(2);
505                 }
506                 else if(motionblurvalue>=0.0 && motionblurvalue<=1.0)
507                 {
508                         glAccum(GL_MULT, motionblurvalue);
509                         glAccum(GL_ACCUM, 1-motionblurvalue);
510                         glAccum(GL_RETURN, 1.0);
511                         glFlush();
512                 }
513         }
514 }
515
516 void GPC_RenderTools::Update2DFilter(vector<STR_String>& propNames, void* gameObj, RAS_2DFilterManager::RAS_2DFILTER_MODE filtermode, int pass, STR_String& text)
517 {
518         m_filtermanager.EnableFilter(propNames, gameObj, filtermode, pass, text);
519 }
520
521 void GPC_RenderTools::Render2DFilters(RAS_ICanvas* canvas)
522 {
523         m_filtermanager.RenderFilters(canvas);
524 }
525