5d455a70ed18d9f937381bc50f6059e77ac1f1c4
[blender.git] / source / gameengine / Ketsji / KX_Camera.cpp
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  * Camera in the gameengine. Cameras are also used for views.
32  */
33  
34 #include "KX_Camera.h"
35
36 #include "KX_Python.h"
37 #include "KX_PyMath.h"
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 KX_Camera::KX_Camera(void* sgReplicationInfo,
43                                          SG_Callbacks callbacks,
44                                          const RAS_CameraData& camdata,
45                                          bool frustum_culling,
46                                          PyTypeObject *T)
47                                         :
48                                         KX_GameObject(sgReplicationInfo,callbacks,T),
49                                         m_camdata(camdata),
50                                         m_dirty(true),
51                                         m_normalised(false),
52                                         m_frustum_culling(frustum_culling),
53                                         m_set_projection_matrix(false),
54                                         m_set_frustum_centre(false)
55 {
56         // setting a name would be nice...
57         m_name = "cam";
58         m_projection_matrix.setIdentity();
59         m_modelview_matrix.setIdentity();
60         SetProperty("camera",new CIntValue(1));
61 }
62
63
64 KX_Camera::~KX_Camera()
65 {
66 }       
67
68
69         
70 MT_Transform KX_Camera::GetWorldToCamera() const
71
72         MT_Transform camtrans;
73         MT_Transform trans;
74         
75         trans.setBasis(NodeGetWorldOrientation());
76         trans.setOrigin(NodeGetWorldPosition());
77         
78         camtrans.invert(trans);
79         
80         return camtrans;
81 }
82
83
84          
85 MT_Transform KX_Camera::GetCameraToWorld() const
86 {
87         MT_Transform trans;
88         trans.setBasis(NodeGetWorldOrientation());
89         trans.setOrigin(NodeGetWorldPosition());
90         
91         return trans;
92 }
93
94
95
96 void KX_Camera::CorrectLookUp(MT_Scalar speed)
97 {
98 }
99
100
101
102 const MT_Point3 KX_Camera::GetCameraLocation() const
103 {
104         /* this is the camera locatio in cam coords... */
105         //return m_trans1.getOrigin();
106         //return MT_Point3(0,0,0);   <-----
107         /* .... I want it in world coords */
108         //MT_Transform trans;
109         //trans.setBasis(NodeGetWorldOrientation());
110         
111         return NodeGetWorldPosition();          
112 }
113
114
115
116 /* I want the camera orientation as well. */
117 const MT_Quaternion KX_Camera::GetCameraOrientation() const
118 {
119         MT_Transform trans;
120         trans.setBasis(NodeGetWorldOrientation());
121         trans.setOrigin(NodeGetWorldPosition());
122
123         return trans.getRotation();
124 }
125
126
127
128 /**
129 * Sets the projection matrix that is used by the rasterizer.
130 */
131 void KX_Camera::SetProjectionMatrix(const MT_Matrix4x4 & mat)
132 {
133         m_projection_matrix = mat;
134         m_dirty = true;
135         m_set_projection_matrix = true;
136         m_set_frustum_centre = false;
137 }
138
139
140
141 /**
142 * Sets the modelview matrix that is used by the rasterizer.
143 */
144 void KX_Camera::SetModelviewMatrix(const MT_Matrix4x4 & mat)
145 {
146         m_modelview_matrix = mat;
147         m_dirty = true;
148         m_set_frustum_centre = false;
149 }
150
151
152
153 /**
154 * Gets the projection matrix that is used by the rasterizer.
155 */
156 const MT_Matrix4x4& KX_Camera::GetProjectionMatrix() const
157 {
158         return m_projection_matrix;
159 }
160
161
162
163 /**
164 * Gets the modelview matrix that is used by the rasterizer.
165 */
166 const MT_Matrix4x4& KX_Camera::GetModelviewMatrix() const
167 {
168         return m_modelview_matrix;
169 }
170
171
172 bool KX_Camera::hasValidProjectionMatrix() const
173 {
174         return m_set_projection_matrix;
175 }
176
177 /*
178 * These getters retrieve the clip data and the focal length
179 */
180 float KX_Camera::GetLens() const
181 {
182         return m_camdata.m_lens;
183 }
184
185
186
187 float KX_Camera::GetCameraNear() const
188 {
189         return m_camdata.m_clipstart;
190 }
191
192
193
194 float KX_Camera::GetCameraFar() const
195 {
196         return m_camdata.m_clipend;
197 }
198
199
200
201 RAS_CameraData* KX_Camera::GetCameraData()
202 {
203         return &m_camdata; 
204 }
205
206 void KX_Camera::ExtractClipPlanes()
207 {
208         if (!m_dirty)
209                 return;
210
211         MT_Matrix4x4 m = m_projection_matrix * GetWorldToCamera();
212         // Left clip plane
213         m_planes[0] = m[3] + m[0];
214         // Right clip plane
215         m_planes[1] = m[3] - m[0];
216         // Top clip plane
217         m_planes[2] = m[3] - m[1];
218         // Bottom clip plane
219         m_planes[3] = m[3] + m[1];
220         // Near clip plane
221         m_planes[4] = m[3] + m[2];
222         // Far clip plane
223         m_planes[5] = m[3] - m[2];
224         
225         m_dirty = false;
226         m_normalised = false;
227 }
228
229 void KX_Camera::NormaliseClipPlanes()
230 {
231         if (m_normalised)
232                 return;
233         
234         for (unsigned int p = 0; p < 6; p++)
235         {
236                 MT_Scalar factor = sqrt(m_planes[p][0]*m_planes[p][0] + m_planes[p][1]*m_planes[p][1] + m_planes[p][2]*m_planes[p][2]);
237                 if (!MT_fuzzyZero(factor))
238                         m_planes[p] /= factor;
239         }
240         
241         m_normalised = true;
242 }
243
244 void KX_Camera::ExtractFrustumSphere()
245 {
246         if (m_set_frustum_centre)
247                 return;
248
249         // The most extreme points on the near and far plane. (normalised device coords)
250         MT_Vector4 hnear(1., 1., 0., 1.), hfar(1., 1., 1., 1.);
251         MT_Matrix4x4 clip_camcs_matrix = m_projection_matrix;
252         clip_camcs_matrix.invert();
253         
254         // Transform to hom camera local space
255         hnear = clip_camcs_matrix*hnear;
256         hfar = clip_camcs_matrix*hfar;
257         
258         // Tranform to 3d camera local space.
259         MT_Point3 nearpoint(hnear[0]/hnear[3], hnear[1]/hnear[3], hnear[2]/hnear[3]);
260         MT_Point3 farpoint(hfar[0]/hfar[3], hfar[1]/hfar[3], hfar[2]/hfar[3]);
261         
262         // Compute centre
263         m_frustum_centre = MT_Point3(0., 0.,
264                 (nearpoint.dot(nearpoint) - farpoint.dot(farpoint))/(2.0*(m_camdata.m_clipend - m_camdata.m_clipstart)));
265         m_frustum_radius = m_frustum_centre.distance(farpoint);
266         
267         // Transform to world space.
268         m_frustum_centre = GetCameraToWorld()(m_frustum_centre);
269         m_frustum_radius /= fabs(NodeGetWorldScaling()[NodeGetWorldScaling().closestAxis()]);
270         
271         m_set_frustum_centre = true;
272 }
273
274 bool KX_Camera::PointInsideFrustum(const MT_Point3& x)
275 {
276         ExtractClipPlanes();
277         
278         for( unsigned int i = 0; i < 6 ; i++ )
279         {
280                 if (m_planes[i][0]*x[0] + m_planes[i][1]*x[1] + m_planes[i][2]*x[2] + m_planes[i][3] < 0.)
281                         return false;
282         }
283         return true;
284 }
285
286 int KX_Camera::BoxInsideFrustum(const MT_Point3 *box)
287 {
288         ExtractClipPlanes();
289         
290         unsigned int insideCount = 0;
291         // 6 view frustum planes
292         for( unsigned int p = 0; p < 6 ; p++ )
293         {
294                 unsigned int behindCount = 0;
295                 // 8 box verticies.
296                 for (unsigned int v = 0; v < 8 ; v++)
297                 {
298                         if (m_planes[p][0]*box[v][0] + m_planes[p][1]*box[v][1] + m_planes[p][2]*box[v][2] + m_planes[p][3] < 0.)
299                                 behindCount++;
300                 }
301                 
302                 // 8 points behind this plane
303                 if (behindCount == 8)
304                         return OUTSIDE;
305
306                 // Every box vertex is on the front side of this plane
307                 if (!behindCount)
308                         insideCount++;
309         }
310         
311         // All box verticies are on the front side of all frustum planes.
312         if (insideCount == 6)
313                 return INSIDE;
314         
315         return INTERSECT;
316 }
317
318 int KX_Camera::SphereInsideFrustum(const MT_Point3& centre, const MT_Scalar &radius)
319 {
320         ExtractFrustumSphere();
321         if (centre.distance2(m_frustum_centre) > (radius + m_frustum_radius)*(radius + m_frustum_radius))
322                 return OUTSIDE;
323
324         unsigned int p;
325         ExtractClipPlanes();
326         NormaliseClipPlanes();
327                 
328         MT_Scalar distance;
329         int intersect = INSIDE;
330         // distance:  <-------- OUTSIDE -----|----- INTERSECT -----0----- INTERSECT -----|----- INSIDE -------->
331         //                                -radius                                      radius
332         for (p = 0; p < 6; p++)
333         {
334                 distance = m_planes[p][0]*centre[0] + m_planes[p][1]*centre[1] + m_planes[p][2]*centre[2] + m_planes[p][3];
335                 if (fabs(distance) <= radius)
336                         intersect = INTERSECT;
337                 else if (distance < -radius)
338                         return OUTSIDE;
339         }
340         
341         return intersect;
342 }
343
344 bool KX_Camera::GetFrustumCulling() const
345 {
346         return m_frustum_culling;
347 }
348
349 //----------------------------------------------------------------------------
350 //Python
351
352
353 PyMethodDef KX_Camera::Methods[] = {
354         KX_PYMETHODTABLE(KX_Camera, sphereInsideFrustum),
355         KX_PYMETHODTABLE(KX_Camera, boxInsideFrustum),
356         KX_PYMETHODTABLE(KX_Camera, pointInsideFrustum),
357         KX_PYMETHODTABLE(KX_Camera, getCameraToWorld),
358         KX_PYMETHODTABLE(KX_Camera, getWorldToCamera),
359         KX_PYMETHODTABLE(KX_Camera, getProjectionMatrix),
360         KX_PYMETHODTABLE(KX_Camera, setProjectionMatrix),
361         
362         {NULL,NULL} //Sentinel
363 };
364
365 char KX_Camera::doc[] = "Module KX_Camera\n\n"
366 "Constants:\n"
367 "\tINSIDE\n"
368 "\tINTERSECT\n"
369 "\tOUTSIDE\n"
370 "Attributes:\n"
371 "\tlens -> float\n"
372 "\t\tThe camera's lens value\n"
373 "\tnear -> float\n"
374 "\t\tThe camera's near clip distance\n"
375 "\tfar -> float\n"
376 "\t\tThe camera's far clip distance\n"
377 "\tfrustum_culling -> bool\n"
378 "\t\tNon zero if this camera is frustum culling.\n"
379 "\tprojection_matrix -> [[float]]\n"
380 "\t\tThis camera's projection matrix.\n"
381 "\tmodelview_matrix -> [[float]] (read only)\n"
382 "\t\tThis camera's model view matrix.\n"
383 "\t\tRegenerated every frame from the camera's position and orientation.\n"
384 "\tcamera_to_world -> [[float]] (read only)\n"
385 "\t\tThis camera's camera to world transform.\n"
386 "\t\tRegenerated every frame from the camera's position and orientation.\n"
387 "\tworld_to_camera -> [[float]] (read only)\n"
388 "\t\tThis camera's world to camera transform.\n"
389 "\t\tRegenerated every frame from the camera's position and orientation.\n"
390 "\t\tThis is camera_to_world inverted.\n";
391
392 PyTypeObject KX_Camera::Type = {
393         PyObject_HEAD_INIT(&PyType_Type)
394                 0,
395                 "KX_Camera",
396                 sizeof(KX_Camera),
397                 0,
398                 PyDestructor,
399                 0,
400                 __getattr,
401                 __setattr,
402                 0, //&MyPyCompare,
403                 __repr,
404                 0, //&cvalue_as_number,
405                 0,
406                 0,
407                 0,
408                 0, 0, 0, 0, 0, 0,
409                 doc
410 };
411
412 PyParentObject KX_Camera::Parents[] = {
413         &KX_Camera::Type,
414         &KX_GameObject::Type,
415                 &SCA_IObject::Type,
416                 &CValue::Type,
417                 NULL
418 };
419
420 PyObject* KX_Camera::_getattr(const STR_String& attr)
421 {
422         if (attr == "INSIDE")
423                 return PyInt_FromLong(INSIDE); /* new ref */
424         if (attr == "OUTSIDE")
425                 return PyInt_FromLong(OUTSIDE); /* new ref */
426         if (attr == "INTERSECT")
427                 return PyInt_FromLong(INTERSECT); /* new ref */
428         
429         if (attr == "lens")
430                 return PyFloat_FromDouble(GetLens()); /* new ref */
431         if (attr == "near")
432                 return PyFloat_FromDouble(GetCameraNear()); /* new ref */
433         if (attr == "far")
434                 return PyFloat_FromDouble(GetCameraFar()); /* new ref */
435         if (attr == "frustum_culling")
436                 return PyInt_FromLong(m_frustum_culling); /* new ref */
437         if (attr == "projection_matrix")
438                 return PyObjectFromMT_Matrix4x4(GetProjectionMatrix()); /* new ref */
439         if (attr == "modelview_matrix")
440                 return PyObjectFromMT_Matrix4x4(GetModelviewMatrix()); /* new ref */
441         if (attr == "camera_to_world")
442                 return PyObjectFromMT_Matrix4x4(GetCameraToWorld()); /* new ref */
443         if (attr == "world_to_camera")
444                 return PyObjectFromMT_Matrix4x4(GetWorldToCamera()); /* new ref */
445         
446         _getattr_up(KX_GameObject);
447 }
448
449 int KX_Camera::_setattr(const STR_String &attr, PyObject *pyvalue)
450 {
451         if (PyInt_Check(pyvalue))
452         {
453                 if (attr == "frustum_culling")
454                 {
455                         m_frustum_culling = PyInt_AsLong(pyvalue);
456                         return 0;
457                 }
458         }
459         
460         if (PyFloat_Check(pyvalue))
461         {
462                 if (attr == "lens")
463                 {
464                         m_camdata.m_lens = PyFloat_AsDouble(pyvalue);
465                         m_set_projection_matrix = false;
466                         return 0;
467                 }
468                 if (attr == "near")
469                 {
470                         m_camdata.m_clipstart = PyFloat_AsDouble(pyvalue);
471                         m_set_projection_matrix = false;
472                         return 0;
473                 }
474                 if (attr == "far")
475                 {
476                         m_camdata.m_clipend = PyFloat_AsDouble(pyvalue);
477                         m_set_projection_matrix = false;
478                         return 0;
479                 }
480         }
481         
482         if (PyObject_IsMT_Matrix(pyvalue, 4))
483         {
484                 if (attr == "projection_matrix")
485                 {
486                         SetProjectionMatrix(MT_Matrix4x4FromPyObject(pyvalue));
487                         return 0;
488                 }
489         }
490         return KX_GameObject::_setattr(attr, pyvalue);
491 }
492
493 KX_PYMETHODDEF_DOC(KX_Camera, sphereInsideFrustum,
494 "sphereInsideFrustum(centre, radius) -> Integer\n"
495 "\treturns INSIDE, OUTSIDE or INTERSECT if the given sphere is\n"
496 "\tinside/outside/intersects this camera's viewing frustum.\n\n"
497 "\tcentre = the centre of the sphere (in world coordinates.)\n"
498 "\tradius = the radius of the sphere\n\n"
499 "\tExample:\n"
500 "\timport GameLogic\n\n"
501 "\tco = GameLogic.getCurrentController()\n"
502 "\tcam = co.GetOwner()\n\n"
503 "\t# A sphere of radius 4.0 located at [x, y, z] = [1.0, 1.0, 1.0]\n"
504 "\tif (cam.sphereInsideFrustum([1.0, 1.0, 1.0], 4) != cam.OUTSIDE):\n"
505 "\t\t# Sphere is inside frustum !\n"
506 "\t\t# Do something useful !\n"
507 "\telse:\n"
508 "\t\t# Sphere is outside frustum\n"
509 )
510 {
511         PyObject *pycentre;
512         float radius;
513         if (PyArg_ParseTuple(args, "Of", &pycentre, &radius))
514         {
515                 MT_Point3 centre = MT_Point3FromPyList(pycentre);
516                 if (PyErr_Occurred())
517                 {
518                         PyErr_SetString(PyExc_TypeError, "sphereInsideFrustum: Expected list for argument centre.");
519                         Py_Return;
520                 }
521                 
522                 return PyInt_FromLong(SphereInsideFrustum(centre, radius)); /* new ref */
523         }
524
525         PyErr_SetString(PyExc_TypeError, "sphereInsideFrustum: Expected arguments: (centre, radius)");
526         
527         Py_Return;
528 }
529
530 KX_PYMETHODDEF_DOC(KX_Camera, boxInsideFrustum,
531 "boxInsideFrustum(box) -> Integer\n"
532 "\treturns INSIDE, OUTSIDE or INTERSECT if the given box is\n"
533 "\tinside/outside/intersects this camera's viewing frustum.\n\n"
534 "\tbox = a list of the eight (8) corners of the box (in world coordinates.)\n\n"
535 "\tExample:\n"
536 "\timport GameLogic\n\n"
537 "\tco = GameLogic.getCurrentController()\n"
538 "\tcam = co.GetOwner()\n\n"
539 "\tbox = []\n"
540 "\tbox.append([-1.0, -1.0, -1.0])\n"
541 "\tbox.append([-1.0, -1.0,  1.0])\n"
542 "\tbox.append([-1.0,  1.0, -1.0])\n"
543 "\tbox.append([-1.0,  1.0,  1.0])\n"
544 "\tbox.append([ 1.0, -1.0, -1.0])\n"
545 "\tbox.append([ 1.0, -1.0,  1.0])\n"
546 "\tbox.append([ 1.0,  1.0, -1.0])\n"
547 "\tbox.append([ 1.0,  1.0,  1.0])\n\n"
548 "\tif (cam.boxInsideFrustum(box) != cam.OUTSIDE):\n"
549 "\t\t# Box is inside/intersects frustum !\n"
550 "\t\t# Do something useful !\n"
551 "\telse:\n"
552 "\t\t# Box is outside the frustum !\n"
553 )
554 {
555         PyObject *pybox;
556         if (PyArg_ParseTuple(args, "O", &pybox))
557         {
558                 unsigned int num_points = PySequence_Size(pybox);
559                 if (num_points != 8)
560                 {
561                         PyErr_Format(PyExc_TypeError, "boxInsideFrustum: Expected eight (8) points, got %d", num_points);
562                         return NULL;
563                 }
564                 
565                 MT_Point3 box[8];
566                 for (unsigned int p = 0; p < 8 ; p++)
567                 {
568                         PyObject *item = PySequence_GetItem(pybox, p); /* new ref */
569                         box[p] = MT_Point3FromPyList(item);
570                         Py_DECREF(item);
571                         if (PyErr_Occurred())
572                                 return NULL;
573                 }
574                 
575                 return PyInt_FromLong(BoxInsideFrustum(box)); /* new ref */
576         }
577         
578         PyErr_SetString(PyExc_TypeError, "boxInsideFrustum: Expected argument: list of points.");
579         return NULL;
580 }
581
582 KX_PYMETHODDEF_DOC(KX_Camera, pointInsideFrustum,
583 "pointInsideFrustum(point) -> Bool\n"
584 "\treturns 1 if the given point is inside this camera's viewing frustum.\n\n"
585 "\tpoint = The point to test (in world coordinates.)\n\n"
586 "\tExample:\n"
587 "\timport GameLogic\n\n"
588 "\tco = GameLogic.getCurrentController()\n"
589 "\tcam = co.GetOwner()\n\n"
590 "\t# Test point [0.0, 0.0, 0.0]"
591 "\tif (cam.pointInsideFrustum([0.0, 0.0, 0.0])):\n"
592 "\t\t# Point is inside frustum !\n"
593 "\t\t# Do something useful !\n"
594 "\telse:\n"
595 "\t\t# Box is outside the frustum !\n"
596 )
597 {
598         PyObject *pypoint;
599         if (PyArg_ParseTuple(args, "O", &pypoint))
600         {
601                 MT_Point3 point = MT_Point3FromPyList(pypoint);
602                 if (PyErr_Occurred())
603                         return NULL;
604                         
605                 return PyInt_FromLong(PointInsideFrustum(point)); /* new ref */
606         }
607         
608         PyErr_SetString(PyExc_TypeError, "pointInsideFrustum: Expected point argument.");
609         return NULL;
610 }
611
612 KX_PYMETHODDEF_DOC(KX_Camera, getCameraToWorld,
613 "getCameraToWorld() -> Matrix4x4\n"
614 "\treturns the camera to world transformation matrix, as a list of four lists of four values.\n\n"
615 "\tie: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]])\n"
616 )
617 {
618         return PyObjectFromMT_Matrix4x4(GetCameraToWorld()); /* new ref */
619 }
620
621 KX_PYMETHODDEF_DOC(KX_Camera, getWorldToCamera,
622 "getWorldToCamera() -> Matrix4x4\n"
623 "\treturns the world to camera transformation matrix, as a list of four lists of four values.\n\n"
624 "\tie: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]])\n"
625 )
626 {
627         return PyObjectFromMT_Matrix4x4(GetWorldToCamera()); /* new ref */
628 }
629
630 KX_PYMETHODDEF_DOC(KX_Camera, getProjectionMatrix,
631 "getProjectionMatrix() -> Matrix4x4\n"
632 "\treturns this camera's projection matrix, as a list of four lists of four values.\n\n"
633 "\tie: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]])\n"
634 )
635 {
636         return PyObjectFromMT_Matrix4x4(GetProjectionMatrix()); /* new ref */
637 }
638
639 KX_PYMETHODDEF_DOC(KX_Camera, setProjectionMatrix,
640 "setProjectionMatrix(MT_Matrix4x4 m) -> None\n"
641 "\tSets this camera's projection matrix\n"
642 "\n"
643 "\tExample:\n"
644 "\timport GameLogic\n"
645 "\t# Set a perspective projection matrix\n"
646 "\tdef Perspective(left, right, bottom, top, near, far):\n"
647 "\t\tm = MT_Matrix4x4()\n"
648 "\t\tm[0][0] = m[0][2] = right - left\n"
649 "\t\tm[1][1] = m[1][2] = top - bottom\n"
650 "\t\tm[2][2] = m[2][3] = -far - near\n"
651 "\t\tm[3][2] = -1\n"
652 "\t\tm[3][3] = 0\n"
653 "\t\treturn m\n"
654 "\n"
655 "\t# Set an orthographic projection matrix\n"
656 "\tdef Orthographic(left, right, bottom, top, near, far):\n"
657 "\t\tm = MT_Matrix4x4()\n"
658 "\t\tm[0][0] = right - left\n"
659 "\t\tm[0][3] = -right - left\n"
660 "\t\tm[1][1] = top - bottom\n"
661 "\t\tm[1][3] = -top - bottom\n"
662 "\t\tm[2][2] = far - near\n"
663 "\t\tm[2][3] = -far - near\n"
664 "\t\tm[3][3] = 1\n"
665 "\t\treturn m\n"
666 "\n"
667 "\t# Set an isometric projection matrix\n"
668 "\tdef Isometric(left, right, bottom, top, near, far):\n"
669 "\t\tm = MT_Matrix4x4()\n"
670 "\t\tm[0][0] = m[0][2] = m[1][1] = 0.8660254037844386\n"
671 "\t\tm[1][0] = 0.25\n"
672 "\t\tm[1][2] = -0.25\n"
673 "\t\tm[3][3] = 1\n"
674 "\t\treturn m\n"
675 "\n"
676 "\t"
677 "\tco = GameLogic.getCurrentController()\n"
678 "\tcam = co.getOwner()\n"
679 "\tcam.setProjectionMatrix(Perspective(-1.0, 1.0, -1.0, 1.0, 0.1, 1))\n")
680 {
681         PyObject *pymat;
682         if (PyArg_ParseTuple(args, "O", &pymat))
683         {
684                 MT_Matrix4x4 mat = MT_Matrix4x4FromPyObject(pymat);
685                 if (PyErr_Occurred())
686                         return NULL;
687                 
688                 SetProjectionMatrix(mat);
689                 Py_Return;
690         }
691
692         PyErr_SetString(PyExc_TypeError, "setProjectionMatrix: Expected 4x4 list as matrix argument.");
693         return NULL;
694 }