svn merge -r 21041:21301 https://svn.blender.org/svnroot/bf-blender/branches/blender2...
[blender.git] / source / gameengine / VideoTexture / ImageRender.cpp
1 /* $Id$
2 -----------------------------------------------------------------------------
3 This source file is part of VideoTexture library
4
5 Copyright (c) 2007 The Zdeno Ash Miklas
6
7 This program is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the Free Software
9 Foundation; either version 2 of the License, or (at your option) any later
10 version.
11
12 This program is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License along with
17 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
19 http://www.gnu.org/copyleft/lesser.txt.
20 -----------------------------------------------------------------------------
21 */
22
23 // implementation
24
25 #include <PyObjectPlus.h>
26 #include <structmember.h>
27 #include <float.h>
28 #include <math.h>
29
30
31 #include "GL/glew.h"
32
33 #include "KX_PythonInit.h"
34 #include "DNA_scene_types.h"
35 #include "RAS_CameraData.h"
36 #include "RAS_MeshObject.h"
37 #include "BLI_arithb.h"
38
39 #include "ImageRender.h"
40 #include "ImageBase.h"
41 #include "BlendType.h"
42 #include "Exception.h"
43 #include "Texture.h"
44
45 ExceptionID SceneInvalid, CameraInvalid, ObserverInvalid;
46 ExceptionID MirrorInvalid, MirrorSizeInvalid, MirrorNormalInvalid, MirrorHorizontal, MirrorTooSmall;
47 ExpDesc SceneInvalidDesc (SceneInvalid, "Scene object is invalid");
48 ExpDesc CameraInvalidDesc (CameraInvalid, "Camera object is invalid");
49 ExpDesc ObserverInvalidDesc (ObserverInvalid, "Observer object is invalid");
50 ExpDesc MirrorInvalidDesc (MirrorInvalid, "Mirror object is invalid");
51 ExpDesc MirrorSizeInvalidDesc (MirrorSizeInvalid, "Mirror has no vertex or no size");
52 ExpDesc MirrorNormalInvalidDesc (MirrorNormalInvalid, "Cannot determine mirror plane");
53 ExpDesc MirrorHorizontalDesc (MirrorHorizontal, "Mirror is horizontal in local space");
54 ExpDesc MirrorTooSmallDesc (MirrorTooSmall, "Mirror is too small");
55
56 // constructor
57 ImageRender::ImageRender (KX_Scene * scene, KX_Camera * camera) : 
58     ImageViewport(),
59     m_render(true),
60     m_scene(scene),
61     m_camera(camera),
62     m_owncamera(false),
63     m_observer(NULL),
64     m_mirror(NULL),
65         m_clip(100.f)
66 {
67         // initialize background colour
68         setBackground(0, 0, 255, 255);
69     // retrieve rendering objects
70     m_engine = KX_GetActiveEngine();
71     m_rasterizer = m_engine->GetRasterizer();
72     m_canvas = m_engine->GetCanvas();
73     m_rendertools = m_engine->GetRenderTools();
74 }
75
76 // destructor
77 ImageRender::~ImageRender (void)
78 {
79     if (m_owncamera)
80         m_camera->Release();
81 }
82
83
84 // set background color
85 void ImageRender::setBackground (int red, int green, int blue, int alpha)
86 {
87     m_background[0] = (red < 0) ? 0.f : (red > 255) ? 1.f : float(red)/255.f;
88         m_background[1] = (green < 0) ? 0.f : (green > 255) ? 1.f : float(green)/255.f;
89         m_background[2] = (blue < 0) ? 0.f : (blue > 255) ? 1.f : float(blue)/255.f;
90         m_background[3] = (alpha < 0) ? 0.f : (alpha > 255) ? 1.f : float(alpha)/255.f;
91 }
92
93
94 // capture image from viewport
95 void ImageRender::calcImage (unsigned int texId)
96 {
97     if (m_rasterizer->GetDrawingMode() != RAS_IRasterizer::KX_TEXTURED ||   // no need for texture
98         m_camera->GetViewport() ||        // camera must be inactive
99         m_camera == m_scene->GetActiveCamera())
100     {
101         // no need to compute texture in non texture rendering
102         m_avail = false;
103         return;
104     }
105     // render the scene from the camera
106     Render();
107         // get image from viewport
108         ImageViewport::calcImage(texId);
109     // restore OpenGL state
110     m_canvas->EndFrame();
111 }
112
113 void ImageRender::Render()
114 {
115         RAS_FrameFrustum frustrum;
116
117     if (!m_render)
118         return;
119
120     if (m_mirror)
121     {
122         // mirror mode, compute camera frustrum, position and orientation
123         // convert mirror position and normal in world space
124         const MT_Matrix3x3 & mirrorObjWorldOri = m_mirror->GetSGNode()->GetWorldOrientation();
125         const MT_Point3 & mirrorObjWorldPos = m_mirror->GetSGNode()->GetWorldPosition();
126         const MT_Vector3 & mirrorObjWorldScale = m_mirror->GetSGNode()->GetWorldScaling();
127         MT_Point3 mirrorWorldPos = 
128             mirrorObjWorldPos + mirrorObjWorldScale * (mirrorObjWorldOri * m_mirrorPos);
129         MT_Vector3 mirrorWorldZ = mirrorObjWorldOri * m_mirrorZ;
130         // get observer world position
131         const MT_Point3 & observerWorldPos = m_observer->GetSGNode()->GetWorldPosition();
132         // get plane D term = mirrorPos . normal
133         MT_Scalar mirrorPlaneDTerm = mirrorWorldPos.dot(mirrorWorldZ);
134         // compute distance of observer to mirror = D - observerPos . normal
135         MT_Scalar observerDistance = mirrorPlaneDTerm - observerWorldPos.dot(mirrorWorldZ);
136         // if distance < 0.01 => observer is on wrong side of mirror, don't render
137         if (observerDistance < 0.01f)
138             return;
139         // set camera world position = observerPos + normal * 2 * distance
140         MT_Point3 cameraWorldPos = observerWorldPos + (MT_Scalar(2.0)*observerDistance)*mirrorWorldZ;
141         m_camera->GetSGNode()->SetLocalPosition(cameraWorldPos);
142         // set camera orientation: z=normal, y=mirror_up in world space, x= y x z
143         MT_Vector3 mirrorWorldY = mirrorObjWorldOri * m_mirrorY;
144         MT_Vector3 mirrorWorldX = mirrorObjWorldOri * m_mirrorX;
145         MT_Matrix3x3 cameraWorldOri(
146             mirrorWorldX[0], mirrorWorldY[0], mirrorWorldZ[0],
147             mirrorWorldX[1], mirrorWorldY[1], mirrorWorldZ[1], 
148             mirrorWorldX[2], mirrorWorldY[2], mirrorWorldZ[2]);
149         m_camera->GetSGNode()->SetLocalOrientation(cameraWorldOri);
150         m_camera->GetSGNode()->UpdateWorldData(0.0);
151         // compute camera frustrum:
152         //   get position of mirror relative to camera: offset = mirrorPos-cameraPos
153         MT_Vector3 mirrorOffset = mirrorWorldPos - cameraWorldPos;
154         //   convert to camera orientation
155         mirrorOffset = mirrorOffset * cameraWorldOri;
156         //   scale mirror size to world scale: 
157         //     get closest local axis for mirror Y and X axis and scale height and width by local axis scale
158         MT_Scalar x, y;
159         x = fabs(m_mirrorY[0]);
160         y = fabs(m_mirrorY[1]);
161         float height = (x > y) ? 
162             ((x > fabs(m_mirrorY[2])) ? mirrorObjWorldScale[0] : mirrorObjWorldScale[2]):
163             ((y > fabs(m_mirrorY[2])) ? mirrorObjWorldScale[1] : mirrorObjWorldScale[2]);
164         x = fabs(m_mirrorX[0]);
165         y = fabs(m_mirrorX[1]);
166         float width = (x > y) ? 
167             ((x > fabs(m_mirrorX[2])) ? mirrorObjWorldScale[0] : mirrorObjWorldScale[2]):
168             ((y > fabs(m_mirrorX[2])) ? mirrorObjWorldScale[1] : mirrorObjWorldScale[2]);
169         width *= m_mirrorHalfWidth;
170         height *= m_mirrorHalfHeight;
171         //   left = offsetx-width
172         //   right = offsetx+width
173         //   top = offsety+height
174         //   bottom = offsety-height
175         //   near = -offsetz
176         //   far = near+100
177         frustrum.x1 = mirrorOffset[0]-width;
178         frustrum.x2 = mirrorOffset[0]+width;
179         frustrum.y1 = mirrorOffset[1]-height;
180         frustrum.y2 = mirrorOffset[1]+height;
181         frustrum.camnear = -mirrorOffset[2];
182         frustrum.camfar = -mirrorOffset[2]+m_clip;
183     }
184     const RAS_IRasterizer::StereoMode stereomode = m_rasterizer->GetStereoMode();
185
186     // The screen area that ImageViewport will copy is also the rendering zone
187     m_canvas->SetViewPort(m_position[0], m_position[1], m_position[0]+m_capSize[0]-1, m_position[1]+m_capSize[1]-1);
188     m_canvas->ClearColor(m_background[0], m_background[1], m_background[2], m_background[3]);
189     m_canvas->ClearBuffer(RAS_ICanvas::COLOR_BUFFER|RAS_ICanvas::DEPTH_BUFFER);
190     m_rasterizer->BeginFrame(RAS_IRasterizer::KX_TEXTURED,m_engine->GetClockTime());
191     m_rendertools->BeginFrame(m_rasterizer);
192     m_engine->SetWorldSettings(m_scene->GetWorldInfo());
193     m_rendertools->SetAuxilaryClientInfo(m_scene);
194     m_rasterizer->DisplayFog();
195     // matrix calculation, don't apply any of the stereo mode
196     m_rasterizer->SetStereoMode(RAS_IRasterizer::RAS_STEREO_NOSTEREO);
197     if (m_mirror)
198     {
199         // frustrum was computed above
200         // get frustrum matrix and set projection matrix
201                 MT_Matrix4x4 projmat = m_rasterizer->GetFrustumMatrix(
202                         frustrum.x1, frustrum.x2, frustrum.y1, frustrum.y2, frustrum.camnear, frustrum.camfar);
203
204                 m_camera->SetProjectionMatrix(projmat);
205     } else if (m_camera->hasValidProjectionMatrix())
206         {
207                 m_rasterizer->SetProjectionMatrix(m_camera->GetProjectionMatrix());
208     } else 
209     {
210                 float lens = m_camera->GetLens();
211                 bool orthographic = !m_camera->GetCameraData()->m_perspective;
212                 float nearfrust = m_camera->GetCameraNear();
213                 float farfrust = m_camera->GetCameraFar();
214         float aspect_ratio = 1.0f;
215         Scene *blenderScene = m_scene->GetBlenderScene();
216                 MT_Matrix4x4 projmat;
217
218                 // compute the aspect ratio from frame blender scene settings so that render to texture
219         // works the same in Blender and in Blender player
220         if (blenderScene->r.ysch != 0)
221             aspect_ratio = float(blenderScene->r.xsch*blenderScene->r.xasp) / float(blenderScene->r.ysch*blenderScene->r.yasp);
222
223                 if (orthographic) {
224
225                         RAS_FramingManager::ComputeDefaultOrtho(
226                                 nearfrust,
227                                 farfrust,
228                                 m_camera->GetScale(),
229                                 aspect_ratio,
230                                 frustrum
231                         );
232
233                         projmat = m_rasterizer->GetOrthoMatrix(
234                                 frustrum.x1, frustrum.x2, frustrum.y1, frustrum.y2, frustrum.camnear, frustrum.camfar);
235                 } else 
236                 {
237                         RAS_FramingManager::ComputeDefaultFrustum(
238                                 nearfrust,
239                                 farfrust,
240                                 lens,
241                                 aspect_ratio,
242                                 frustrum);
243                         
244                         projmat = m_rasterizer->GetFrustumMatrix(
245                                 frustrum.x1, frustrum.x2, frustrum.y1, frustrum.y2, frustrum.camnear, frustrum.camfar);
246                 }
247                 m_camera->SetProjectionMatrix(projmat);
248         }
249
250         MT_Transform camtrans(m_camera->GetWorldToCamera());
251         MT_Matrix4x4 viewmat(camtrans);
252         
253         m_rasterizer->SetViewMatrix(viewmat, m_camera->NodeGetWorldOrientation(), m_camera->NodeGetWorldPosition(), m_camera->GetCameraData()->m_perspective);
254         m_camera->SetModelviewMatrix(viewmat);
255     // restore the stereo mode now that the matrix is computed
256     m_rasterizer->SetStereoMode(stereomode);
257
258         m_scene->CalculateVisibleMeshes(m_rasterizer,m_camera);
259
260         m_scene->RenderBuckets(camtrans, m_rasterizer, m_rendertools);
261 }
262
263
264 // cast Image pointer to ImageRender
265 inline ImageRender * getImageRender (PyImage * self)
266 { return static_cast<ImageRender*>(self->m_image); }
267
268
269 // python methods
270
271 // Blender Scene type
272 BlendType<KX_Scene> sceneType ("KX_Scene");
273 // Blender Camera type
274 BlendType<KX_Camera> cameraType ("KX_Camera");
275
276
277 // object initialization
278 static int ImageRender_init (PyObject * pySelf, PyObject * args, PyObject * kwds)
279 {
280         // parameters - scene object
281         PyObject * scene;
282         // camera object
283         PyObject * camera;
284         // parameter keywords
285         static char *kwlist[] = {"sceneObj", "cameraObj", NULL};
286         // get parameters
287         if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &scene, &camera))
288                 return -1;
289         try
290         {
291                 // get scene pointer
292                 KX_Scene * scenePtr (NULL);
293                 if (scene != NULL) scenePtr = sceneType.checkType(scene);
294                 // throw exception if scene is not available
295                 if (scenePtr == NULL) THRWEXCP(SceneInvalid, S_OK);
296
297                 // get camera pointer
298                 KX_Camera * cameraPtr (NULL);
299                 if (camera != NULL) cameraPtr = cameraType.checkType(camera);
300                 // throw exception if camera is not available
301                 if (cameraPtr == NULL) THRWEXCP(CameraInvalid, S_OK);
302
303                 // get pointer to image structure
304                 PyImage * self = reinterpret_cast<PyImage*>(pySelf);
305                 // create source object
306                 if (self->m_image != NULL) delete self->m_image;
307                 self->m_image = new ImageRender(scenePtr, cameraPtr);
308         }
309         catch (Exception & exp)
310         {
311                 exp.report();
312                 return -1;
313         }
314         // initialization succeded
315         return 0;
316 }
317
318
319 // get background color
320 PyObject * getBackground (PyImage * self, void * closure)
321 {
322         return Py_BuildValue("[BBBB]", 
323         getImageRender(self)->getBackground(0),
324                 getImageRender(self)->getBackground(1), 
325         getImageRender(self)->getBackground(2),
326         getImageRender(self)->getBackground(3));
327 }
328
329 // set color
330 static int setBackground (PyImage * self, PyObject * value, void * closure)
331 {
332         // check validity of parameter
333         if (value == NULL || !PySequence_Check(value) || PySequence_Length(value) != 4
334                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 0))
335                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 1))
336                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 2))
337                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 3)))
338         {
339                 PyErr_SetString(PyExc_TypeError, "The value must be a sequence of 4 integer between 0 and 255");
340                 return -1;
341         }
342         // set background color
343         getImageRender(self)->setBackground((unsigned char)(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 0))),
344                 (unsigned char)(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 1))),
345                 (unsigned char)(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 2))),
346         (unsigned char)(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 3))));
347         // success
348         return 0;
349 }
350
351
352 // methods structure
353 static PyMethodDef imageRenderMethods[] =
354 { // methods from ImageBase class
355         {"refresh", (PyCFunction)Image_refresh, METH_NOARGS, "Refresh image - invalidate its current content"},
356         {NULL}
357 };
358 // attributes structure
359 static PyGetSetDef imageRenderGetSets[] =
360
361         {(char*)"background", (getter)getBackground, (setter)setBackground, (char*)"background color", NULL},
362     // attribute from ImageViewport
363         {(char*)"capsize", (getter)ImageViewport_getCaptureSize, (setter)ImageViewport_setCaptureSize, (char*)"size of render area", NULL},
364         {(char*)"alpha", (getter)ImageViewport_getAlpha, (setter)ImageViewport_setAlpha, (char*)"use alpha in texture", NULL},
365         {(char*)"whole", (getter)ImageViewport_getWhole, (setter)ImageViewport_setWhole, (char*)"use whole viewport to render", NULL},
366         // attributes from ImageBase class
367         {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
368         {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
369         {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbour)", NULL},
370         {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
371         {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
372         {NULL}
373 };
374
375
376 // define python type
377 PyTypeObject ImageRenderType =
378
379 #if (PY_VERSION_HEX >= 0x02060000)
380         PyVarObject_HEAD_INIT(NULL, 0)
381 #else
382         /* python 2.5 and below */
383         PyObject_HEAD_INIT( NULL )  /* required py macro */
384         0,                         /*ob_size*/
385 #endif
386         "VideoTexture.ImageRender",   /*tp_name*/
387         sizeof(PyImage),          /*tp_basicsize*/
388         0,                         /*tp_itemsize*/
389         (destructor)Image_dealloc, /*tp_dealloc*/
390         0,                         /*tp_print*/
391         0,                         /*tp_getattr*/
392         0,                         /*tp_setattr*/
393         0,                         /*tp_compare*/
394         0,                         /*tp_repr*/
395         0,                         /*tp_as_number*/
396         0,                         /*tp_as_sequence*/
397         0,                         /*tp_as_mapping*/
398         0,                         /*tp_hash */
399         0,                         /*tp_call*/
400         0,                         /*tp_str*/
401         0,                         /*tp_getattro*/
402         0,                         /*tp_setattro*/
403         0,                         /*tp_as_buffer*/
404         Py_TPFLAGS_DEFAULT,        /*tp_flags*/
405         "Image source from render",       /* tp_doc */
406         0,                             /* tp_traverse */
407         0,                             /* tp_clear */
408         0,                             /* tp_richcompare */
409         0,                             /* tp_weaklistoffset */
410         0,                             /* tp_iter */
411         0,                             /* tp_iternext */
412         imageRenderMethods,    /* tp_methods */
413         0,                   /* tp_members */
414         imageRenderGetSets,          /* tp_getset */
415         0,                         /* tp_base */
416         0,                         /* tp_dict */
417         0,                         /* tp_descr_get */
418         0,                         /* tp_descr_set */
419         0,                         /* tp_dictoffset */
420         (initproc)ImageRender_init,     /* tp_init */
421         0,                         /* tp_alloc */
422         Image_allocNew,           /* tp_new */
423 };
424
425 // object initialization
426 static int ImageMirror_init (PyObject * pySelf, PyObject * args, PyObject * kwds)
427 {
428         // parameters - scene object
429         PyObject * scene;
430         // reference object for mirror
431         PyObject * observer;
432     // object holding the mirror
433     PyObject * mirror;
434     // material of the mirror
435     short materialID = 0;
436         // parameter keywords
437         static char *kwlist[] = {"scene", "observer", "mirror", "material", NULL};
438         // get parameters
439         if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO|h", kwlist, &scene, &observer, &mirror, &materialID))
440                 return -1;
441         try
442         {
443                 // get scene pointer
444                 KX_Scene * scenePtr (NULL);
445         if (scene != NULL && PyObject_TypeCheck(scene, &KX_Scene::Type))
446             scenePtr = static_cast<KX_Scene*>BGE_PROXY_REF(scene);
447                 else
448             THRWEXCP(SceneInvalid, S_OK);
449                 
450                 if(scenePtr==NULL) /* incase the python proxy reference is invalid */
451                         THRWEXCP(SceneInvalid, S_OK);
452                 
453                 // get observer pointer
454                 KX_GameObject * observerPtr (NULL);
455                 if (observer != NULL && PyObject_TypeCheck(observer, &KX_GameObject::Type))
456             observerPtr = static_cast<KX_GameObject*>BGE_PROXY_REF(observer);
457         else if (observer != NULL && PyObject_TypeCheck(observer, &KX_Camera::Type))
458             observerPtr = static_cast<KX_Camera*>BGE_PROXY_REF(observer);
459                 else
460             THRWEXCP(ObserverInvalid, S_OK);
461                 
462                 if(observerPtr==NULL) /* incase the python proxy reference is invalid */
463                         THRWEXCP(ObserverInvalid, S_OK);
464
465                 // get mirror pointer
466                 KX_GameObject * mirrorPtr (NULL);
467                 if (mirror != NULL && PyObject_TypeCheck(mirror, &KX_GameObject::Type))
468             mirrorPtr = static_cast<KX_GameObject*>BGE_PROXY_REF(mirror);
469                 else
470             THRWEXCP(MirrorInvalid, S_OK);
471                 
472                 if(mirrorPtr==NULL) /* incase the python proxy reference is invalid */
473                         THRWEXCP(MirrorInvalid, S_OK);
474
475         // locate the material in the mirror
476                 RAS_IPolyMaterial * material = getMaterial(mirror, materialID);
477                 if (material == NULL)
478             THRWEXCP(MaterialNotAvail, S_OK);
479
480                 // get pointer to image structure
481                 PyImage * self = reinterpret_cast<PyImage*>(pySelf);
482
483                 // create source object
484                 if (self->m_image != NULL) 
485         {
486             delete self->m_image;
487             self->m_image = NULL;
488         }
489                 self->m_image = new ImageRender(scenePtr, observerPtr, mirrorPtr, material);
490         }
491         catch (Exception & exp)
492         {
493                 exp.report();
494                 return -1;
495         }
496         // initialization succeded
497         return 0;
498 }
499
500 // get background color
501 PyObject * getClip (PyImage * self, void * closure)
502 {
503         return PyFloat_FromDouble(getImageRender(self)->getClip());
504 }
505
506 // set clip
507 static int setClip (PyImage * self, PyObject * value, void * closure)
508 {
509         // check validity of parameter
510         double clip;
511         if (value == NULL || !PyFloat_Check(value) || (clip = PyFloat_AsDouble(value)) < 0.01 || clip > 5000.0)
512         {
513                 PyErr_SetString(PyExc_TypeError, "The value must be an float between 0.01 and 5000");
514                 return -1;
515         }
516         // set background color
517         getImageRender(self)->setClip(float(clip));
518         // success
519         return 0;
520 }
521
522 // attributes structure
523 static PyGetSetDef imageMirrorGetSets[] =
524
525         {(char*)"clip", (getter)getClip, (setter)setClip, (char*)"clipping distance", NULL},
526         // attribute from ImageRender
527         {(char*)"background", (getter)getBackground, (setter)setBackground, (char*)"background color", NULL},
528     // attribute from ImageViewport
529         {(char*)"capsize", (getter)ImageViewport_getCaptureSize, (setter)ImageViewport_setCaptureSize, (char*)"size of render area", NULL},
530         {(char*)"alpha", (getter)ImageViewport_getAlpha, (setter)ImageViewport_setAlpha, (char*)"use alpha in texture", NULL},
531         {(char*)"whole", (getter)ImageViewport_getWhole, (setter)ImageViewport_setWhole, (char*)"use whole viewport to render", NULL},
532         // attributes from ImageBase class
533         {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
534         {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
535         {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbour)", NULL},
536         {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
537         {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
538         {NULL}
539 };
540
541
542 // constructor
543 ImageRender::ImageRender (KX_Scene * scene, KX_GameObject * observer, KX_GameObject * mirror, RAS_IPolyMaterial * mat) :
544     ImageViewport(),
545     m_render(false),
546     m_scene(scene),
547     m_observer(observer),
548     m_mirror(mirror),
549         m_clip(100.f)
550 {
551     // this constructor is used for automatic planar mirror
552     // create a camera, take all data by default, in any case we will recompute the frustrum on each frame
553         RAS_CameraData camdata;
554     vector<RAS_TexVert*> mirrorVerts;
555     vector<RAS_TexVert*>::iterator it;
556     float mirrorArea = 0.f;
557     float mirrorNormal[3] = {0.f, 0.f, 0.f};
558     float mirrorUp[3];
559     float dist, vec[3], axis[3];
560     float zaxis[3] = {0.f, 0.f, 1.f};
561     float yaxis[3] = {0.f, 1.f, 0.f};
562     float mirrorMat[3][3];
563     float left, right, top, bottom, back;
564         // make sure this camera will delete its node
565         m_camera= new KX_Camera(scene, KX_Scene::m_callbacks, camdata, true, true);
566         m_camera->SetName("__mirror__cam__");
567     // don't add the camera to the scene object list, it doesn't need to be accessible
568     m_owncamera = true;
569     // retrieve rendering objects
570     m_engine = KX_GetActiveEngine();
571     m_rasterizer = m_engine->GetRasterizer();
572     m_canvas = m_engine->GetCanvas();
573     m_rendertools = m_engine->GetRenderTools();
574     // locate the vertex assigned to mat and do following calculation in mesh coordinates
575     for (int meshIndex = 0; meshIndex < mirror->GetMeshCount(); meshIndex++)
576     {
577         RAS_MeshObject* mesh = mirror->GetMesh(meshIndex);
578         int numPolygons = mesh->NumPolygons();
579         for (int polygonIndex=0; polygonIndex < numPolygons; polygonIndex++)
580         {
581             RAS_Polygon* polygon = mesh->GetPolygon(polygonIndex);
582             if (polygon->GetMaterial()->GetPolyMaterial() == mat)
583             {
584                 RAS_TexVert *v1, *v2, *v3, *v4;
585                 float normal[3];
586                 float area;
587                 // this polygon is part of the mirror,
588                 v1 = polygon->GetVertex(0);
589                 v2 = polygon->GetVertex(1);
590                 v3 = polygon->GetVertex(2);
591                 mirrorVerts.push_back(v1);
592                 mirrorVerts.push_back(v2);
593                 mirrorVerts.push_back(v3);
594                 if (polygon->VertexCount() == 4) 
595                 {
596                     v4 = polygon->GetVertex(3);
597                     mirrorVerts.push_back(v4);
598                     area = CalcNormFloat4((float*)v1->getXYZ(), (float*)v2->getXYZ(), (float*)v3->getXYZ(), (float*)v4->getXYZ(), normal);
599                 } else
600                 {
601                     area = CalcNormFloat((float*)v1->getXYZ(), (float*)v2->getXYZ(), (float*)v3->getXYZ(), normal);
602                 }
603                 area = fabs(area);
604                 mirrorArea += area;
605                 VecMulf(normal, area);
606                 VecAddf(mirrorNormal, mirrorNormal, normal);
607             }
608         }
609     }
610     if (mirrorVerts.size() == 0 || mirrorArea < FLT_EPSILON)
611     {
612         // no vertex or zero size mirror
613        THRWEXCP(MirrorSizeInvalid, S_OK);
614     }
615     // compute average normal of mirror faces
616     VecMulf(mirrorNormal, 1.0f/mirrorArea);
617     if (Normalize(mirrorNormal) == 0.f)
618     {
619         // no normal
620         THRWEXCP(MirrorNormalInvalid, S_OK);
621     }
622     // the mirror plane has an equation of the type ax+by+cz = d where (a,b,c) is the normal vector
623         // if the mirror is more vertical then horizontal, the Z axis is the up direction.
624         // otherwise the Y axis is the up direction.
625         // If the mirror is not perfectly vertical(horizontal), the Z(Y) axis projection on the mirror
626         // plan by the normal will be the up direction.
627         if (fabs(mirrorNormal[2]) > fabs(mirrorNormal[1]) &&
628                 fabs(mirrorNormal[2]) > fabs(mirrorNormal[0]))
629         {
630                 // the mirror is more horizontal than vertical
631         VecCopyf(axis, yaxis);
632         }
633         else
634         {
635                 // the mirror is more vertical than horizontal
636         VecCopyf(axis, zaxis);
637         }
638     dist = Inpf(mirrorNormal, axis);
639     if (fabs(dist) < FLT_EPSILON)
640     {
641         // the mirror is already fully aligned with up axis
642         VecCopyf(mirrorUp, axis);
643     }
644     else
645     {
646         // projection of axis to mirror plane through normal
647         VecCopyf(vec, mirrorNormal);
648         VecMulf(vec, dist);
649         VecSubf(mirrorUp, axis, vec);
650         if (Normalize(mirrorUp) == 0.f)
651         {
652             // should not happen
653             THRWEXCP(MirrorHorizontal, S_OK);
654             return;
655         }
656     }
657     // compute rotation matrix between local coord and mirror coord
658     // to match camera orientation, we select mirror z = -normal, y = up, x = y x z
659     VecCopyf(mirrorMat[2], mirrorNormal);
660     VecMulf(mirrorMat[2], -1.0f);
661     VecCopyf(mirrorMat[1], mirrorUp);
662     Crossf(mirrorMat[0], mirrorMat[1], mirrorMat[2]);
663     // transpose to make it a orientation matrix from local space to mirror space
664     Mat3Transp(mirrorMat);
665     // transform all vertex to plane coordinates and determine mirror position
666     left = FLT_MAX; 
667     right = -FLT_MAX;
668     bottom = FLT_MAX;
669     top = -FLT_MAX;
670     back = -FLT_MAX; // most backward vertex (=highest Z coord in mirror space)
671     for (it = mirrorVerts.begin(); it != mirrorVerts.end(); it++)
672     {   
673         VecCopyf(vec, (float*)(*it)->getXYZ());
674         Mat3MulVecfl(mirrorMat, vec);
675         if (vec[0] < left)
676             left = vec[0];
677         if (vec[0] > right)
678             right = vec[0];
679         if (vec[1] < bottom)
680             bottom = vec[1];
681         if (vec[1] > top)
682             top = vec[1];
683         if (vec[2] > back)
684             back = vec[2];
685     }
686     // now store this information in the object for later rendering
687     m_mirrorHalfWidth = (right-left)*0.5f;
688     m_mirrorHalfHeight = (top-bottom)*0.5f;
689     if (m_mirrorHalfWidth < 0.01f || m_mirrorHalfHeight < 0.01f)
690     {
691         // mirror too small
692         THRWEXCP(MirrorTooSmall, S_OK);
693     }
694     // mirror position in mirror coord
695     vec[0] = (left+right)*0.5f;
696     vec[1] = (top+bottom)*0.5f;
697     vec[2] = back;
698     // convert it in local space: transpose again the matrix to get back to mirror to local transform
699     Mat3Transp(mirrorMat);
700     Mat3MulVecfl(mirrorMat, vec);
701     // mirror position in local space
702     m_mirrorPos.setValue(vec[0], vec[1], vec[2]);
703     // mirror normal vector (pointed towards the back of the mirror) in local space
704     m_mirrorZ.setValue(-mirrorNormal[0], -mirrorNormal[1], -mirrorNormal[2]);
705     m_mirrorY.setValue(mirrorUp[0], mirrorUp[1], mirrorUp[2]);
706     m_mirrorX = m_mirrorY.cross(m_mirrorZ);
707     m_render = true;
708
709         setBackground(0, 0, 255, 255);
710 }
711
712
713
714
715 // define python type
716 PyTypeObject ImageMirrorType =
717
718 #if (PY_VERSION_HEX >= 0x02060000)
719         PyVarObject_HEAD_INIT(NULL, 0)
720 #else
721         /* python 2.5 and below */
722         PyObject_HEAD_INIT( NULL )  /* required py macro */
723         0,                         /*ob_size*/
724 #endif
725         "VideoTexture.ImageMirror",   /*tp_name*/
726         sizeof(PyImage),          /*tp_basicsize*/
727         0,                         /*tp_itemsize*/
728         (destructor)Image_dealloc, /*tp_dealloc*/
729         0,                         /*tp_print*/
730         0,                         /*tp_getattr*/
731         0,                         /*tp_setattr*/
732         0,                         /*tp_compare*/
733         0,                         /*tp_repr*/
734         0,                         /*tp_as_number*/
735         0,                         /*tp_as_sequence*/
736         0,                         /*tp_as_mapping*/
737         0,                         /*tp_hash */
738         0,                         /*tp_call*/
739         0,                         /*tp_str*/
740         0,                         /*tp_getattro*/
741         0,                         /*tp_setattro*/
742         0,                         /*tp_as_buffer*/
743         Py_TPFLAGS_DEFAULT,        /*tp_flags*/
744         "Image source from mirror",       /* tp_doc */
745         0,                             /* tp_traverse */
746         0,                             /* tp_clear */
747         0,                             /* tp_richcompare */
748         0,                             /* tp_weaklistoffset */
749         0,                             /* tp_iter */
750         0,                             /* tp_iternext */
751         imageRenderMethods,    /* tp_methods */
752         0,                   /* tp_members */
753         imageMirrorGetSets,          /* tp_getset */
754         0,                         /* tp_base */
755         0,                         /* tp_dict */
756         0,                         /* tp_descr_get */
757         0,                         /* tp_descr_set */
758         0,                         /* tp_dictoffset */
759         (initproc)ImageMirror_init,     /* tp_init */
760         0,                         /* tp_alloc */
761         Image_allocNew,           /* tp_new */
762 };
763
764