- added GCC warning -Wstrict-prototypes
[blender.git] / source / gameengine / Ketsji / KX_Light.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #if defined(WIN32) && !defined(FREE_WINDOWS)
30 #pragma warning (disable : 4786)
31 #endif
32
33 #include "GL/glew.h"
34
35 #include "KX_Light.h"
36 #include "KX_Camera.h"
37 #include "RAS_IRasterizer.h"
38 #include "RAS_IRenderTools.h"
39
40 #include "KX_PyMath.h"
41
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44 #include "GPU_material.h"
45  
46 KX_LightObject::KX_LightObject(void* sgReplicationInfo,SG_Callbacks callbacks,
47                                                            class RAS_IRenderTools* rendertools,
48                                                            const RAS_LightObject&       lightobj,
49                                                            bool glsl)
50         : KX_GameObject(sgReplicationInfo,callbacks),
51           m_rendertools(rendertools)
52 {
53         m_lightobj = lightobj;
54         m_lightobj.m_scene = sgReplicationInfo;
55         m_lightobj.m_light = this;
56         m_rendertools->AddLight(&m_lightobj);
57         m_glsl = glsl;
58         m_blenderscene = ((KX_Scene*)sgReplicationInfo)->GetBlenderScene();
59 };
60
61
62 KX_LightObject::~KX_LightObject()
63 {
64         GPULamp *lamp;
65
66         if((lamp = GetGPULamp())) {
67                 float obmat[4][4] = {{0}};
68                 GPU_lamp_update(lamp, 0, 0, obmat);
69         }
70
71         m_rendertools->RemoveLight(&m_lightobj);
72 }
73
74
75 CValue*         KX_LightObject::GetReplica()
76 {
77
78         KX_LightObject* replica = new KX_LightObject(*this);
79
80         replica->ProcessReplica();
81         
82         replica->m_lightobj.m_light = replica;
83         m_rendertools->AddLight(&replica->m_lightobj);
84
85         return replica;
86 }
87
88 bool KX_LightObject::ApplyLight(KX_Scene *kxscene, int oblayer, int slot)
89 {
90         KX_Scene* lightscene = (KX_Scene*)m_lightobj.m_scene;
91         float vec[4];
92         int scenelayer = ~0;
93
94         if(kxscene && kxscene->GetBlenderScene())
95                 scenelayer = kxscene->GetBlenderScene()->lay;
96         
97         /* only use lights in the same layer as the object */
98         if(!(m_lightobj.m_layer & oblayer))
99                 return false;
100         /* only use lights in the same scene, and in a visible layer */
101         if(kxscene != lightscene || !(m_lightobj.m_layer & scenelayer))
102                 return false;
103
104         // lights don't get their openGL matrix updated, do it now
105         if(GetSGNode()->IsDirty())
106                 GetOpenGLMatrix();
107
108         MT_CmMatrix4x4& worldmatrix= *GetOpenGLMatrixPtr();
109
110         vec[0] = worldmatrix(0,3);
111         vec[1] = worldmatrix(1,3);
112         vec[2] = worldmatrix(2,3);
113         vec[3] = 1.0f;
114
115         if(m_lightobj.m_type==RAS_LightObject::LIGHT_SUN) {
116                 
117                 vec[0] = worldmatrix(0,2);
118                 vec[1] = worldmatrix(1,2);
119                 vec[2] = worldmatrix(2,2);
120                 //vec[0]= base->object->obmat[2][0];
121                 //vec[1]= base->object->obmat[2][1];
122                 //vec[2]= base->object->obmat[2][2];
123                 vec[3]= 0.0;
124                 glLightfv((GLenum)(GL_LIGHT0+slot), GL_POSITION, vec); 
125         }
126         else {
127                 //vec[3]= 1.0;
128                 glLightfv((GLenum)(GL_LIGHT0+slot), GL_POSITION, vec); 
129                 glLightf((GLenum)(GL_LIGHT0+slot), GL_CONSTANT_ATTENUATION, 1.0);
130                 glLightf((GLenum)(GL_LIGHT0+slot), GL_LINEAR_ATTENUATION, m_lightobj.m_att1/m_lightobj.m_distance);
131                 // without this next line it looks backward compatible.
132                 //attennuation still is acceptable 
133                 glLightf((GLenum)(GL_LIGHT0+slot), GL_QUADRATIC_ATTENUATION, m_lightobj.m_att2/(m_lightobj.m_distance*m_lightobj.m_distance)); 
134                 
135                 if(m_lightobj.m_type==RAS_LightObject::LIGHT_SPOT) {
136                         vec[0] = -worldmatrix(0,2);
137                         vec[1] = -worldmatrix(1,2);
138                         vec[2] = -worldmatrix(2,2);
139                         //vec[0]= -base->object->obmat[2][0];
140                         //vec[1]= -base->object->obmat[2][1];
141                         //vec[2]= -base->object->obmat[2][2];
142                         glLightfv((GLenum)(GL_LIGHT0+slot), GL_SPOT_DIRECTION, vec);
143                         glLightf((GLenum)(GL_LIGHT0+slot), GL_SPOT_CUTOFF, m_lightobj.m_spotsize/2.0);
144                         glLightf((GLenum)(GL_LIGHT0+slot), GL_SPOT_EXPONENT, 128.0*m_lightobj.m_spotblend);
145                 }
146                 else
147                         glLightf((GLenum)(GL_LIGHT0+slot), GL_SPOT_CUTOFF, 180.0);
148         }
149         
150         if (m_lightobj.m_nodiffuse) {
151                 vec[0] = vec[1] = vec[2] = vec[3] = 0.0;
152         }
153         else {
154                 vec[0]= m_lightobj.m_energy*m_lightobj.m_red;
155                 vec[1]= m_lightobj.m_energy*m_lightobj.m_green;
156                 vec[2]= m_lightobj.m_energy*m_lightobj.m_blue;
157                 vec[3]= 1.0;
158         }
159
160         glLightfv((GLenum)(GL_LIGHT0+slot), GL_DIFFUSE, vec);
161         if(m_lightobj.m_nospecular)
162         {
163                 vec[0] = vec[1] = vec[2] = vec[3] = 0.0;
164         }
165         else if (m_lightobj.m_nodiffuse) {
166                 vec[0]= m_lightobj.m_energy*m_lightobj.m_red;
167                 vec[1]= m_lightobj.m_energy*m_lightobj.m_green;
168                 vec[2]= m_lightobj.m_energy*m_lightobj.m_blue;
169                 vec[3]= 1.0;
170         }
171
172         glLightfv((GLenum)(GL_LIGHT0+slot), GL_SPECULAR, vec);
173         glEnable((GLenum)(GL_LIGHT0+slot));
174
175         return true;
176 }
177
178 GPULamp *KX_LightObject::GetGPULamp()
179 {
180         if(m_glsl)
181                 return GPU_lamp_from_blender(m_blenderscene, GetBlenderObject(), GetBlenderGroupObject());
182         else
183                 return NULL;
184 }
185
186 void KX_LightObject::Update()
187 {
188         GPULamp *lamp;
189
190         if((lamp = GetGPULamp()) != NULL && GetSGNode()) {
191                 float obmat[4][4];
192                 // lights don't get their openGL matrix updated, do it now
193                 if (GetSGNode()->IsDirty())
194                         GetOpenGLMatrix();
195                 double *dobmat = GetOpenGLMatrixPtr()->getPointer();
196
197                 for(int i=0; i<4; i++)
198                         for(int j=0; j<4; j++, dobmat++)
199                                 obmat[i][j] = (float)*dobmat;
200
201                 GPU_lamp_update(lamp, m_lightobj.m_layer, 0, obmat);
202                 GPU_lamp_update_colors(lamp, m_lightobj.m_red, m_lightobj.m_green, 
203                         m_lightobj.m_blue, m_lightobj.m_energy);
204         }
205 }
206
207 bool KX_LightObject::HasShadowBuffer()
208 {
209         GPULamp *lamp;
210
211         if((lamp = GetGPULamp()))
212                 return GPU_lamp_has_shadow_buffer(lamp);
213         else
214                 return false;
215 }
216
217 int KX_LightObject::GetShadowLayer()
218 {
219         GPULamp *lamp;
220
221         if((lamp = GetGPULamp()))
222                 return GPU_lamp_shadow_layer(lamp);
223         else
224                 return 0;
225 }
226
227 void KX_LightObject::BindShadowBuffer(RAS_IRasterizer *ras, KX_Camera *cam, MT_Transform& camtrans)
228 {
229         GPULamp *lamp;
230         float viewmat[4][4], winmat[4][4];
231         int winsize;
232
233         /* bind framebuffer */
234         lamp = GetGPULamp();
235         GPU_lamp_shadow_buffer_bind(lamp, viewmat, &winsize, winmat);
236
237         /* setup camera transformation */
238         MT_Matrix4x4 modelviewmat((float*)viewmat);
239         MT_Matrix4x4 projectionmat((float*)winmat);
240
241         MT_Transform trans = MT_Transform((float*)viewmat);
242         camtrans.invert(trans);
243
244         cam->SetModelviewMatrix(modelviewmat);
245         cam->SetProjectionMatrix(projectionmat);
246         
247         cam->NodeSetLocalPosition(camtrans.getOrigin());
248         cam->NodeSetLocalOrientation(camtrans.getBasis());
249         cam->NodeUpdateGS(0);
250
251         /* setup rasterizer transformations */
252         ras->SetProjectionMatrix(projectionmat);
253         ras->SetViewMatrix(modelviewmat, cam->NodeGetWorldOrientation(), cam->NodeGetWorldPosition(), cam->GetCameraData()->m_perspective);
254 }
255
256 void KX_LightObject::UnbindShadowBuffer(RAS_IRasterizer *ras)
257 {
258         GPULamp *lamp = GetGPULamp();
259         GPU_lamp_shadow_buffer_unbind(lamp);
260 }
261
262 #ifdef WITH_PYTHON
263 /* ------------------------------------------------------------------------- */
264 /* Python Integration Hooks                                                                      */
265 /* ------------------------------------------------------------------------- */
266
267 PyTypeObject KX_LightObject::Type = {
268         PyVarObject_HEAD_INIT(NULL, 0)
269         "KX_LightObject",
270         sizeof(PyObjectPlus_Proxy),
271         0,
272         py_base_dealloc,
273         0,
274         0,
275         0,
276         0,
277         py_base_repr,
278         0,
279         &KX_GameObject::Sequence,
280         &KX_GameObject::Mapping,
281         0,0,0,
282         NULL,
283         NULL,
284         0,
285         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
286         0,0,0,0,0,0,0,
287         Methods,
288         0,
289         0,
290         &KX_GameObject::Type,
291         0,0,0,0,0,0,
292         py_base_new
293 };
294
295 PyMethodDef KX_LightObject::Methods[] = {
296         {NULL,NULL} //Sentinel
297 };
298
299 PyAttributeDef KX_LightObject::Attributes[] = {
300         KX_PYATTRIBUTE_INT_RW("layer", 1, 20, true, KX_LightObject, m_lightobj.m_layer),
301         KX_PYATTRIBUTE_FLOAT_RW("energy", 0, 10, KX_LightObject, m_lightobj.m_energy),
302         KX_PYATTRIBUTE_FLOAT_RW("distance", 0.01, 5000, KX_LightObject, m_lightobj.m_distance),
303         KX_PYATTRIBUTE_RW_FUNCTION("color", KX_LightObject, pyattr_get_color, pyattr_set_color),
304         KX_PYATTRIBUTE_FLOAT_RW("lin_attenuation", 0, 1, KX_LightObject, m_lightobj.m_att1),
305         KX_PYATTRIBUTE_FLOAT_RW("quad_attenuation", 0, 1, KX_LightObject, m_lightobj.m_att2),
306         KX_PYATTRIBUTE_FLOAT_RW("spotsize", 1, 180, KX_LightObject, m_lightobj.m_spotsize),
307         KX_PYATTRIBUTE_FLOAT_RW("spotblend", 0, 1, KX_LightObject, m_lightobj.m_spotblend),
308         KX_PYATTRIBUTE_RO_FUNCTION("SPOT", KX_LightObject, pyattr_get_typeconst),
309         KX_PYATTRIBUTE_RO_FUNCTION("SUN", KX_LightObject, pyattr_get_typeconst),
310         KX_PYATTRIBUTE_RO_FUNCTION("NORMAL", KX_LightObject, pyattr_get_typeconst),
311         KX_PYATTRIBUTE_RW_FUNCTION("type", KX_LightObject, pyattr_get_type, pyattr_set_type),
312         { NULL }        //Sentinel
313 };
314
315 PyObject* KX_LightObject::pyattr_get_color(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
316 {
317         KX_LightObject* self = static_cast<KX_LightObject*>(self_v);
318         return Py_BuildValue("[fff]", self->m_lightobj.m_red, self->m_lightobj.m_green, self->m_lightobj.m_blue);
319 }
320
321 int KX_LightObject::pyattr_set_color(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
322 {
323         KX_LightObject* self = static_cast<KX_LightObject*>(self_v);
324
325         MT_Vector3 color;
326         if (PyVecTo(value, color))
327         {
328                 self->m_lightobj.m_red = color[0];
329                 self->m_lightobj.m_green = color[1];
330                 self->m_lightobj.m_blue = color[2];
331                 return PY_SET_ATTR_SUCCESS;
332         }
333         return PY_SET_ATTR_FAIL;
334 }
335
336 PyObject* KX_LightObject::pyattr_get_typeconst(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
337 {
338         PyObject* retvalue;
339
340         const char* type = attrdef->m_name;
341
342         if(!strcmp(type, "SPOT")) {
343                 retvalue = PyLong_FromSsize_t(RAS_LightObject::LIGHT_SPOT);
344         } else if (!strcmp(type, "SUN")) {
345                 retvalue = PyLong_FromSsize_t(RAS_LightObject::LIGHT_SUN);
346         } else if (!strcmp(type, "NORMAL")) {
347                 retvalue = PyLong_FromSsize_t(RAS_LightObject::LIGHT_NORMAL);
348         }
349     else {
350         /* should never happen */
351         PyErr_SetString(PyExc_TypeError, "light.type: internal error, invalid light type");
352         retvalue = NULL;
353     }
354
355         return retvalue;
356 }
357
358 PyObject* KX_LightObject::pyattr_get_type(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef)
359 {
360         KX_LightObject* self = static_cast<KX_LightObject*>(self_v);
361         return PyLong_FromSsize_t(self->m_lightobj.m_type);
362 }
363
364 int KX_LightObject::pyattr_set_type(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject* value)
365 {
366         KX_LightObject* self = static_cast<KX_LightObject*>(self_v);
367         int val = PyLong_AsSsize_t(value);
368         if((val==-1 && PyErr_Occurred()) || val<0 || val>2) {
369                 PyErr_SetString(PyExc_ValueError, "light.type= val: KX_LightObject, expected an int between 0 and 2");
370                 return PY_SET_ATTR_FAIL;
371         }
372         
373         switch(val) {
374                 case 0:
375                         self->m_lightobj.m_type = self->m_lightobj.LIGHT_SPOT;
376                         break;
377                 case 1:
378                         self->m_lightobj.m_type = self->m_lightobj.LIGHT_SUN;
379                         break;
380                 case 2:
381                         self->m_lightobj.m_type = self->m_lightobj.LIGHT_NORMAL;
382                         break;
383         }
384
385         return PY_SET_ATTR_SUCCESS;
386 }
387 #endif // WITH_PYTHON