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