fd289a0b55ec200c079e7d3712cb309e3fa18424
[blender.git] / source / gameengine / Ketsji / KX_Camera.cpp
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  * Camera in the gameengine. Cameras are also used for views.
29  */
30  
31 #include "GL/glew.h"
32 #include "KX_Camera.h"
33 #include "KX_Scene.h"
34 #include "KX_PythonInit.h"
35 #include "KX_Python.h"
36 #include "KX_PyMath.h"
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 KX_Camera::KX_Camera(void* sgReplicationInfo,
42                                          SG_Callbacks callbacks,
43                                          const RAS_CameraData& camdata,
44                                          bool frustum_culling,
45                                          bool delete_node)
46                                         :
47                                         KX_GameObject(sgReplicationInfo,callbacks),
48                                         m_camdata(camdata),
49                                         m_dirty(true),
50                                         m_normalized(false),
51                                         m_frustum_culling(frustum_culling),
52                                         m_set_projection_matrix(false),
53                                         m_set_frustum_center(false),
54                                         m_delete_node(delete_node)
55 {
56         // setting a name would be nice...
57         m_name = "cam";
58         m_projection_matrix.setIdentity();
59         m_modelview_matrix.setIdentity();
60         CValue* val = new CIntValue(1);
61         SetProperty("camera",val);
62         val->Release();
63 }
64
65
66 KX_Camera::~KX_Camera()
67 {
68         if (m_delete_node && m_pSGNode)
69         {
70                 // for shadow camera, avoids memleak
71                 delete m_pSGNode;
72                 m_pSGNode = NULL;
73         }
74 }       
75
76
77 CValue* KX_Camera::GetReplica()
78 {
79         KX_Camera* replica = new KX_Camera(*this);
80         
81         // this will copy properties and so on...
82         replica->ProcessReplica();
83         
84         return replica;
85 }
86
87 void KX_Camera::ProcessReplica()
88 {
89         KX_GameObject::ProcessReplica();
90         // replicated camera are always registered in the scene
91         m_delete_node = false;
92 }
93
94 MT_Transform KX_Camera::GetWorldToCamera() const
95
96         MT_Transform camtrans;
97         camtrans.invert(MT_Transform(NodeGetWorldPosition(), NodeGetWorldOrientation()));
98         
99         return camtrans;
100 }
101
102
103          
104 MT_Transform KX_Camera::GetCameraToWorld() const
105 {
106         return MT_Transform(NodeGetWorldPosition(), NodeGetWorldOrientation());
107 }
108
109
110
111 void KX_Camera::CorrectLookUp(MT_Scalar speed)
112 {
113 }
114
115
116
117 const MT_Point3 KX_Camera::GetCameraLocation() const
118 {
119         /* this is the camera locatio in cam coords... */
120         //return m_trans1.getOrigin();
121         //return MT_Point3(0,0,0);   <-----
122         /* .... I want it in world coords */
123         //MT_Transform trans;
124         //trans.setBasis(NodeGetWorldOrientation());
125         
126         return NodeGetWorldPosition();          
127 }
128
129
130
131 /* I want the camera orientation as well. */
132 const MT_Quaternion KX_Camera::GetCameraOrientation() const
133 {
134         return NodeGetWorldOrientation().getRotation();
135 }
136
137
138
139 /**
140 * Sets the projection matrix that is used by the rasterizer.
141 */
142 void KX_Camera::SetProjectionMatrix(const MT_Matrix4x4 & mat)
143 {
144         m_projection_matrix = mat;
145         m_dirty = true;
146         m_set_projection_matrix = true;
147         m_set_frustum_center = false;
148 }
149
150
151
152 /**
153 * Sets the modelview matrix that is used by the rasterizer.
154 */
155 void KX_Camera::SetModelviewMatrix(const MT_Matrix4x4 & mat)
156 {
157         m_modelview_matrix = mat;
158         m_dirty = true;
159         m_set_frustum_center = false;
160 }
161
162
163
164 /**
165 * Gets the projection matrix that is used by the rasterizer.
166 */
167 const MT_Matrix4x4& KX_Camera::GetProjectionMatrix() const
168 {
169         return m_projection_matrix;
170 }
171
172
173
174 /**
175 * Gets the modelview matrix that is used by the rasterizer.
176 */
177 const MT_Matrix4x4& KX_Camera::GetModelviewMatrix() const
178 {
179         return m_modelview_matrix;
180 }
181
182
183 bool KX_Camera::hasValidProjectionMatrix() const
184 {
185         return m_set_projection_matrix;
186 }
187
188 void KX_Camera::InvalidateProjectionMatrix(bool valid)
189 {
190         m_set_projection_matrix = valid;
191 }
192
193
194 /*
195 * These getters retrieve the clip data and the focal length
196 */
197 float KX_Camera::GetLens() const
198 {
199         return m_camdata.m_lens;
200 }
201
202 float KX_Camera::GetScale() const
203 {
204         return m_camdata.m_scale;
205 }
206
207
208
209 float KX_Camera::GetCameraNear() const
210 {
211         return m_camdata.m_clipstart;
212 }
213
214
215
216 float KX_Camera::GetCameraFar() const
217 {
218         return m_camdata.m_clipend;
219 }
220
221 float KX_Camera::GetFocalLength() const
222 {
223         return m_camdata.m_focallength;
224 }
225
226
227
228 RAS_CameraData* KX_Camera::GetCameraData()
229 {
230         return &m_camdata; 
231 }
232
233 void KX_Camera::ExtractClipPlanes()
234 {
235         if (!m_dirty)
236                 return;
237
238         MT_Matrix4x4 m = m_projection_matrix * m_modelview_matrix;
239         // Left clip plane
240         m_planes[0] = m[3] + m[0];
241         // Right clip plane
242         m_planes[1] = m[3] - m[0];
243         // Top clip plane
244         m_planes[2] = m[3] - m[1];
245         // Bottom clip plane
246         m_planes[3] = m[3] + m[1];
247         // Near clip plane
248         m_planes[4] = m[3] + m[2];
249         // Far clip plane
250         m_planes[5] = m[3] - m[2];
251         
252         m_dirty = false;
253         m_normalized = false;
254 }
255
256 void KX_Camera::NormalizeClipPlanes()
257 {
258         if (m_normalized)
259                 return;
260         
261         for (unsigned int p = 0; p < 6; p++)
262         {
263                 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]);
264                 if (!MT_fuzzyZero(factor))
265                         m_planes[p] /= factor;
266         }
267         
268         m_normalized = true;
269 }
270
271 void KX_Camera::ExtractFrustumSphere()
272 {
273         if (m_set_frustum_center)
274                 return;
275
276     // compute sphere for the general case and not only symmetric frustum:
277     // the mirror code in ImageRender can use very asymmetric frustum.
278     // We will put the sphere center on the line that goes from origin to the center of the far clipping plane
279     // This is the optimal position if the frustum is symmetric or very asymmetric and probably close
280     // to optimal for the general case. The sphere center position is computed so that the distance to 
281     // the near and far extreme frustum points are equal.
282
283     // get the transformation matrix from device coordinate to camera coordinate
284         MT_Matrix4x4 clip_camcs_matrix = m_projection_matrix;
285         clip_camcs_matrix.invert();
286
287         if (m_projection_matrix[3][3] == MT_Scalar(0.0)) 
288         {
289                 // frustrum projection
290                 // detect which of the corner of the far clipping plane is the farthest to the origin
291                 MT_Vector4 nfar;    // far point in device normalized coordinate
292                 MT_Point3 farpoint; // most extreme far point in camera coordinate
293                 MT_Point3 nearpoint;// most extreme near point in camera coordinate
294                 MT_Point3 farcenter(0.,0.,0.);// center of far cliping plane in camera coordinate
295                 MT_Scalar F=-1.0, N; // square distance of far and near point to origin
296                 MT_Scalar f, n;     // distance of far and near point to z axis. f is always > 0 but n can be < 0
297                 MT_Scalar e, s;     // far and near clipping distance (<0)
298                 MT_Scalar c;        // slope of center line = distance of far clipping center to z axis / far clipping distance
299                 MT_Scalar z;        // projection of sphere center on z axis (<0)
300                 // tmp value
301                 MT_Vector4 npoint(1., 1., 1., 1.);
302                 MT_Vector4 hpoint;
303                 MT_Point3 point;
304                 MT_Scalar len;
305                 for (int i=0; i<4; i++)
306                 {
307                 hpoint = clip_camcs_matrix*npoint;
308                         point.setValue(hpoint[0]/hpoint[3], hpoint[1]/hpoint[3], hpoint[2]/hpoint[3]);
309                         len = point.dot(point);
310                         if (len > F)
311                         {
312                                 nfar = npoint;
313                                 farpoint = point;
314                                 F = len;
315                         }
316                         // rotate by 90 degree along the z axis to walk through the 4 extreme points of the far clipping plane
317                         len = npoint[0];
318                         npoint[0] = -npoint[1];
319                         npoint[1] = len;
320                         farcenter += point;
321                 }
322                 // the far center is the average of the far clipping points
323                 farcenter *= 0.25;
324                 // the extreme near point is the opposite point on the near clipping plane
325                 nfar.setValue(-nfar[0], -nfar[1], -1., 1.);
326                 nfar = clip_camcs_matrix*nfar;
327                 nearpoint.setValue(nfar[0]/nfar[3], nfar[1]/nfar[3], nfar[2]/nfar[3]);
328                 // this is a frustrum projection
329                 N = nearpoint.dot(nearpoint);
330                 e = farpoint[2];
331                 s = nearpoint[2];
332                 // projection on XY plane for distance to axis computation
333                 MT_Point2 farxy(farpoint[0], farpoint[1]);
334                 // f is forced positive by construction
335                 f = farxy.length();
336                 // get corresponding point on the near plane
337                 farxy *= s/e;
338                 // this formula preserve the sign of n
339                 n = f*s/e - MT_Point2(nearpoint[0]-farxy[0], nearpoint[1]-farxy[1]).length();
340                 c = MT_Point2(farcenter[0], farcenter[1]).length()/e;
341                 // the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case
342                 z = (F-N)/(2.0*(e-s+c*(f-n)));
343                 m_frustum_center = MT_Point3(farcenter[0]*z/e, farcenter[1]*z/e, z);
344                 m_frustum_radius = m_frustum_center.distance(farpoint);
345         } 
346         else
347         {
348                 // orthographic projection
349                 // The most extreme points on the near and far plane. (normalized device coords)
350                 MT_Vector4 hnear(1., 1., 1., 1.), hfar(-1., -1., -1., 1.);
351                 
352                 // Transform to hom camera local space
353                 hnear = clip_camcs_matrix*hnear;
354                 hfar = clip_camcs_matrix*hfar;
355                 
356                 // Tranform to 3d camera local space.
357                 MT_Point3 nearpoint(hnear[0]/hnear[3], hnear[1]/hnear[3], hnear[2]/hnear[3]);
358                 MT_Point3 farpoint(hfar[0]/hfar[3], hfar[1]/hfar[3], hfar[2]/hfar[3]);
359                 
360                 // just use mediant point
361                 m_frustum_center = (farpoint + nearpoint)*0.5;
362                 m_frustum_radius = m_frustum_center.distance(farpoint);
363         }
364         // Transform to world space.
365         m_frustum_center = GetCameraToWorld()(m_frustum_center);
366         m_frustum_radius /= fabs(NodeGetWorldScaling()[NodeGetWorldScaling().closestAxis()]);
367         
368         m_set_frustum_center = true;
369 }
370
371 bool KX_Camera::PointInsideFrustum(const MT_Point3& x)
372 {
373         ExtractClipPlanes();
374         
375         for( unsigned int i = 0; i < 6 ; i++ )
376         {
377                 if (m_planes[i][0]*x[0] + m_planes[i][1]*x[1] + m_planes[i][2]*x[2] + m_planes[i][3] < 0.)
378                         return false;
379         }
380         return true;
381 }
382
383 int KX_Camera::BoxInsideFrustum(const MT_Point3 *box)
384 {
385         ExtractClipPlanes();
386         
387         unsigned int insideCount = 0;
388         // 6 view frustum planes
389         for( unsigned int p = 0; p < 6 ; p++ )
390         {
391                 unsigned int behindCount = 0;
392                 // 8 box verticies.
393                 for (unsigned int v = 0; v < 8 ; v++)
394                 {
395                         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.)
396                                 behindCount++;
397                 }
398                 
399                 // 8 points behind this plane
400                 if (behindCount == 8)
401                         return OUTSIDE;
402
403                 // Every box vertex is on the front side of this plane
404                 if (!behindCount)
405                         insideCount++;
406         }
407         
408         // All box verticies are on the front side of all frustum planes.
409         if (insideCount == 6)
410                 return INSIDE;
411         
412         return INTERSECT;
413 }
414
415 int KX_Camera::SphereInsideFrustum(const MT_Point3& center, const MT_Scalar &radius)
416 {
417         ExtractFrustumSphere();
418         if (center.distance2(m_frustum_center) > (radius + m_frustum_radius)*(radius + m_frustum_radius))
419                 return OUTSIDE;
420
421         unsigned int p;
422         ExtractClipPlanes();
423         NormalizeClipPlanes();
424                 
425         MT_Scalar distance;
426         int intersect = INSIDE;
427         // distance:  <-------- OUTSIDE -----|----- INTERSECT -----0----- INTERSECT -----|----- INSIDE -------->
428         //                                -radius                                      radius
429         for (p = 0; p < 6; p++)
430         {
431                 distance = m_planes[p][0]*center[0] + m_planes[p][1]*center[1] + m_planes[p][2]*center[2] + m_planes[p][3];
432                 if (fabs(distance) <= radius)
433                         intersect = INTERSECT;
434                 else if (distance < -radius)
435                         return OUTSIDE;
436         }
437         
438         return intersect;
439 }
440
441 bool KX_Camera::GetFrustumCulling() const
442 {
443         return m_frustum_culling;
444 }
445  
446 void KX_Camera::EnableViewport(bool viewport)
447 {
448         m_camdata.m_viewport = viewport;
449 }
450
451 void KX_Camera::SetViewport(int left, int bottom, int right, int top)
452 {
453         m_camdata.m_viewportleft = left;
454         m_camdata.m_viewportbottom = bottom;
455         m_camdata.m_viewportright = right;
456         m_camdata.m_viewporttop = top;
457 }
458
459 bool KX_Camera::GetViewport() const
460 {
461         return m_camdata.m_viewport;
462 }
463
464 int KX_Camera::GetViewportLeft() const
465 {
466         return m_camdata.m_viewportleft;
467 }
468
469 int KX_Camera::GetViewportBottom() const
470 {
471         return m_camdata.m_viewportbottom;
472 }
473
474 int KX_Camera::GetViewportRight() const
475 {
476         return m_camdata.m_viewportright;
477 }
478
479 int KX_Camera::GetViewportTop() const
480 {
481         return m_camdata.m_viewporttop;
482 }
483
484 //----------------------------------------------------------------------------
485 //Python
486
487
488 PyMethodDef KX_Camera::Methods[] = {
489         KX_PYMETHODTABLE(KX_Camera, sphereInsideFrustum),
490         KX_PYMETHODTABLE_O(KX_Camera, boxInsideFrustum),
491         KX_PYMETHODTABLE_O(KX_Camera, pointInsideFrustum),
492         KX_PYMETHODTABLE_NOARGS(KX_Camera, getCameraToWorld),
493         KX_PYMETHODTABLE_NOARGS(KX_Camera, getWorldToCamera),
494         KX_PYMETHODTABLE(KX_Camera, setViewport),
495         KX_PYMETHODTABLE_NOARGS(KX_Camera, setOnTop),
496         KX_PYMETHODTABLE_O(KX_Camera, getScreenPosition),
497         KX_PYMETHODTABLE(KX_Camera, getScreenVect),
498         KX_PYMETHODTABLE(KX_Camera, getScreenRay),
499
500         // DEPRECATED
501         KX_PYMETHODTABLE_O(KX_Camera, enableViewport),
502         KX_PYMETHODTABLE_NOARGS(KX_Camera, getProjectionMatrix),
503         KX_PYMETHODTABLE_O(KX_Camera, setProjectionMatrix),
504         
505         {NULL,NULL} //Sentinel
506 };
507
508 PyAttributeDef KX_Camera::Attributes[] = {
509         
510         KX_PYATTRIBUTE_BOOL_RW("frustum_culling", KX_Camera, m_frustum_culling),
511         KX_PYATTRIBUTE_RW_FUNCTION("perspective", KX_Camera, pyattr_get_perspective, pyattr_set_perspective),
512         
513         KX_PYATTRIBUTE_RW_FUNCTION("lens",      KX_Camera,      pyattr_get_lens, pyattr_set_lens),
514         KX_PYATTRIBUTE_RW_FUNCTION("near",      KX_Camera,      pyattr_get_near, pyattr_set_near),
515         KX_PYATTRIBUTE_RW_FUNCTION("far",       KX_Camera,      pyattr_get_far,  pyattr_set_far),
516         
517         KX_PYATTRIBUTE_RW_FUNCTION("useViewport",       KX_Camera,      pyattr_get_use_viewport,  pyattr_set_use_viewport),
518         
519         KX_PYATTRIBUTE_RW_FUNCTION("projection_matrix", KX_Camera,      pyattr_get_projection_matrix, pyattr_set_projection_matrix),
520         KX_PYATTRIBUTE_RO_FUNCTION("modelview_matrix",  KX_Camera,      pyattr_get_modelview_matrix),
521         KX_PYATTRIBUTE_RO_FUNCTION("camera_to_world",   KX_Camera,      pyattr_get_camera_to_world),
522         KX_PYATTRIBUTE_RO_FUNCTION("world_to_camera",   KX_Camera,      pyattr_get_world_to_camera),
523         
524         /* Grrr, functions for constants? */
525         KX_PYATTRIBUTE_RO_FUNCTION("INSIDE",    KX_Camera, pyattr_get_INSIDE),
526         KX_PYATTRIBUTE_RO_FUNCTION("OUTSIDE",   KX_Camera, pyattr_get_OUTSIDE),
527         KX_PYATTRIBUTE_RO_FUNCTION("INTERSECT", KX_Camera, pyattr_get_INTERSECT),
528         
529         { NULL }        //Sentinel
530 };
531
532 PyTypeObject KX_Camera::Type = {
533         PyVarObject_HEAD_INIT(NULL, 0)
534         "KX_Camera",
535         sizeof(PyObjectPlus_Proxy),
536         0,
537         py_base_dealloc,
538         0,
539         0,
540         0,
541         0,
542         py_base_repr,
543         0,
544         &KX_GameObject::Sequence,
545         &KX_GameObject::Mapping,
546         0,0,0,
547         NULL,
548         NULL,
549         0,
550         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
551         0,0,0,0,0,0,0,
552         Methods,
553         0,
554         0,
555         &KX_GameObject::Type,
556         0,0,0,0,0,0,
557         py_base_new
558 };
559
560 KX_PYMETHODDEF_DOC_VARARGS(KX_Camera, sphereInsideFrustum,
561 "sphereInsideFrustum(center, radius) -> Integer\n"
562 "\treturns INSIDE, OUTSIDE or INTERSECT if the given sphere is\n"
563 "\tinside/outside/intersects this camera's viewing frustum.\n\n"
564 "\tcenter = the center of the sphere (in world coordinates.)\n"
565 "\tradius = the radius of the sphere\n\n"
566 "\tExample:\n"
567 "\timport GameLogic\n\n"
568 "\tco = GameLogic.getCurrentController()\n"
569 "\tcam = co.GetOwner()\n\n"
570 "\t# A sphere of radius 4.0 located at [x, y, z] = [1.0, 1.0, 1.0]\n"
571 "\tif (cam.sphereInsideFrustum([1.0, 1.0, 1.0], 4) != cam.OUTSIDE):\n"
572 "\t\t# Sphere is inside frustum !\n"
573 "\t\t# Do something useful !\n"
574 "\telse:\n"
575 "\t\t# Sphere is outside frustum\n"
576 )
577 {
578         PyObject *pycenter;
579         float radius;
580         if (PyArg_ParseTuple(args, "Of:sphereInsideFrustum", &pycenter, &radius))
581         {
582                 MT_Point3 center;
583                 if (PyVecTo(pycenter, center))
584                 {
585                         return PyLong_FromSsize_t(SphereInsideFrustum(center, radius)); /* new ref */
586                 }
587         }
588
589         PyErr_SetString(PyExc_TypeError, "camera.sphereInsideFrustum(center, radius): KX_Camera, expected arguments: (center, radius)");
590         
591         return NULL;
592 }
593
594 KX_PYMETHODDEF_DOC_O(KX_Camera, boxInsideFrustum,
595 "boxInsideFrustum(box) -> Integer\n"
596 "\treturns INSIDE, OUTSIDE or INTERSECT if the given box is\n"
597 "\tinside/outside/intersects this camera's viewing frustum.\n\n"
598 "\tbox = a list of the eight (8) corners of the box (in world coordinates.)\n\n"
599 "\tExample:\n"
600 "\timport GameLogic\n\n"
601 "\tco = GameLogic.getCurrentController()\n"
602 "\tcam = co.GetOwner()\n\n"
603 "\tbox = []\n"
604 "\tbox.append([-1.0, -1.0, -1.0])\n"
605 "\tbox.append([-1.0, -1.0,  1.0])\n"
606 "\tbox.append([-1.0,  1.0, -1.0])\n"
607 "\tbox.append([-1.0,  1.0,  1.0])\n"
608 "\tbox.append([ 1.0, -1.0, -1.0])\n"
609 "\tbox.append([ 1.0, -1.0,  1.0])\n"
610 "\tbox.append([ 1.0,  1.0, -1.0])\n"
611 "\tbox.append([ 1.0,  1.0,  1.0])\n\n"
612 "\tif (cam.boxInsideFrustum(box) != cam.OUTSIDE):\n"
613 "\t\t# Box is inside/intersects frustum !\n"
614 "\t\t# Do something useful !\n"
615 "\telse:\n"
616 "\t\t# Box is outside the frustum !\n"
617 )
618 {
619         unsigned int num_points = PySequence_Size(value);
620         if (num_points != 8)
621         {
622                 PyErr_Format(PyExc_TypeError, "camera.boxInsideFrustum(box): KX_Camera, expected eight (8) points, got %d", num_points);
623                 return NULL;
624         }
625         
626         MT_Point3 box[8];
627         for (unsigned int p = 0; p < 8 ; p++)
628         {
629                 PyObject *item = PySequence_GetItem(value, p); /* new ref */
630                 bool error = !PyVecTo(item, box[p]);
631                 Py_DECREF(item);
632                 if (error)
633                         return NULL;
634         }
635         
636         return PyLong_FromSsize_t(BoxInsideFrustum(box)); /* new ref */
637 }
638
639 KX_PYMETHODDEF_DOC_O(KX_Camera, pointInsideFrustum,
640 "pointInsideFrustum(point) -> Bool\n"
641 "\treturns 1 if the given point is inside this camera's viewing frustum.\n\n"
642 "\tpoint = The point to test (in world coordinates.)\n\n"
643 "\tExample:\n"
644 "\timport GameLogic\n\n"
645 "\tco = GameLogic.getCurrentController()\n"
646 "\tcam = co.GetOwner()\n\n"
647 "\t# Test point [0.0, 0.0, 0.0]"
648 "\tif (cam.pointInsideFrustum([0.0, 0.0, 0.0])):\n"
649 "\t\t# Point is inside frustum !\n"
650 "\t\t# Do something useful !\n"
651 "\telse:\n"
652 "\t\t# Box is outside the frustum !\n"
653 )
654 {
655         MT_Point3 point;
656         if (PyVecTo(value, point))
657         {
658                 return PyLong_FromSsize_t(PointInsideFrustum(point)); /* new ref */
659         }
660         
661         PyErr_SetString(PyExc_TypeError, "camera.pointInsideFrustum(point): KX_Camera, expected point argument.");
662         return NULL;
663 }
664
665 KX_PYMETHODDEF_DOC_NOARGS(KX_Camera, getCameraToWorld,
666 "getCameraToWorld() -> Matrix4x4\n"
667 "\treturns the camera to world transformation matrix, as a list of four lists of four values.\n\n"
668 "\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"
669 )
670 {
671         return PyObjectFrom(GetCameraToWorld()); /* new ref */
672 }
673
674 KX_PYMETHODDEF_DOC_NOARGS(KX_Camera, getWorldToCamera,
675 "getWorldToCamera() -> Matrix4x4\n"
676 "\treturns the world to camera transformation matrix, as a list of four lists of four values.\n\n"
677 "\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"
678 )
679 {
680         return PyObjectFrom(GetWorldToCamera()); /* new ref */
681 }
682
683 KX_PYMETHODDEF_DOC_NOARGS(KX_Camera, getProjectionMatrix,
684 "getProjectionMatrix() -> Matrix4x4\n"
685 "\treturns this camera's projection matrix, as a list of four lists of four values.\n\n"
686 "\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"
687 )
688 {
689         ShowDeprecationWarning("getProjectionMatrix()", "the projection_matrix property");
690         return PyObjectFrom(GetProjectionMatrix()); /* new ref */
691 }
692
693 KX_PYMETHODDEF_DOC_O(KX_Camera, setProjectionMatrix,
694 "setProjectionMatrix(MT_Matrix4x4 m) -> None\n"
695 "\tSets this camera's projection matrix\n"
696 "\n"
697 "\tExample:\n"
698 "\timport GameLogic\n"
699 "\t# Set a perspective projection matrix\n"
700 "\tdef Perspective(left, right, bottom, top, near, far):\n"
701 "\t\tm = MT_Matrix4x4()\n"
702 "\t\tm[0][0] = m[0][2] = right - left\n"
703 "\t\tm[1][1] = m[1][2] = top - bottom\n"
704 "\t\tm[2][2] = m[2][3] = -far - near\n"
705 "\t\tm[3][2] = -1\n"
706 "\t\tm[3][3] = 0\n"
707 "\t\treturn m\n"
708 "\n"
709 "\t# Set an orthographic projection matrix\n"
710 "\tdef Orthographic(left, right, bottom, top, near, far):\n"
711 "\t\tm = MT_Matrix4x4()\n"
712 "\t\tm[0][0] = right - left\n"
713 "\t\tm[0][3] = -right - left\n"
714 "\t\tm[1][1] = top - bottom\n"
715 "\t\tm[1][3] = -top - bottom\n"
716 "\t\tm[2][2] = far - near\n"
717 "\t\tm[2][3] = -far - near\n"
718 "\t\tm[3][3] = 1\n"
719 "\t\treturn m\n"
720 "\n"
721 "\t# Set an isometric projection matrix\n"
722 "\tdef Isometric(left, right, bottom, top, near, far):\n"
723 "\t\tm = MT_Matrix4x4()\n"
724 "\t\tm[0][0] = m[0][2] = m[1][1] = 0.8660254037844386\n"
725 "\t\tm[1][0] = 0.25\n"
726 "\t\tm[1][2] = -0.25\n"
727 "\t\tm[3][3] = 1\n"
728 "\t\treturn m\n"
729 "\n"
730 "\t"
731 "\tco = GameLogic.getCurrentController()\n"
732 "\tcam = co.getOwner()\n"
733 "\tcam.setProjectionMatrix(Perspective(-1.0, 1.0, -1.0, 1.0, 0.1, 1))\n")
734 {
735         ShowDeprecationWarning("setProjectionMatrix(mat)", "the projection_matrix property");
736         
737         MT_Matrix4x4 mat;
738         if (!PyMatTo(value, mat))
739         {
740                 PyErr_SetString(PyExc_TypeError, "camera.setProjectionMatrix(matrix): KX_Camera, expected 4x4 list as matrix argument.");
741                 return NULL;
742         }
743         
744         SetProjectionMatrix(mat);
745         Py_RETURN_NONE;
746 }
747
748 KX_PYMETHODDEF_DOC_O(KX_Camera, enableViewport,
749 "enableViewport(viewport)\n"
750 "Sets this camera's viewport status\n"
751 )
752 {
753         ShowDeprecationWarning("enableViewport(bool)", "the useViewport property");
754         
755         int viewport = PyObject_IsTrue(value);
756         if (viewport == -1) {
757                 PyErr_SetString(PyExc_ValueError, "camera.enableViewport(bool): KX_Camera, expected True/False or 0/1");
758                 return NULL;
759         }
760         
761         if(viewport)
762                 EnableViewport(true);
763         else
764                 EnableViewport(false);
765         
766         Py_RETURN_NONE;
767 }
768
769 KX_PYMETHODDEF_DOC_VARARGS(KX_Camera, setViewport,
770 "setViewport(left, bottom, right, top)\n"
771 "Sets this camera's viewport\n")
772 {
773         int left, bottom, right, top;
774         if (!PyArg_ParseTuple(args,"iiii:setViewport",&left, &bottom, &right, &top))
775                 return NULL;
776         
777         SetViewport(left, bottom, right, top);
778         Py_RETURN_NONE;
779 }
780
781 KX_PYMETHODDEF_DOC_NOARGS(KX_Camera, setOnTop,
782 "setOnTop()\n"
783 "Sets this camera's viewport on top\n")
784 {
785         class KX_Scene* scene = KX_GetActiveScene();
786         scene->SetCameraOnTop(this);
787         Py_RETURN_NONE;
788 }
789
790 PyObject* KX_Camera::pyattr_get_perspective(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
791 {
792         KX_Camera* self= static_cast<KX_Camera*>(self_v);
793         return PyBool_FromLong(self->m_camdata.m_perspective);
794 }
795
796 int KX_Camera::pyattr_set_perspective(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
797 {
798         KX_Camera* self= static_cast<KX_Camera*>(self_v);
799         int param = PyObject_IsTrue( value );
800         if (param == -1) {
801                 PyErr_SetString(PyExc_AttributeError, "camera.perspective = bool: KX_Camera, expected True/False or 0/1");
802                 return PY_SET_ATTR_FAIL;
803         }
804         
805         self->m_camdata.m_perspective= param;
806         return PY_SET_ATTR_SUCCESS;
807 }
808
809 PyObject* KX_Camera::pyattr_get_lens(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
810 {
811         KX_Camera* self= static_cast<KX_Camera*>(self_v);
812         return PyFloat_FromDouble(self->m_camdata.m_lens);
813 }
814
815 int KX_Camera::pyattr_set_lens(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
816 {
817         KX_Camera* self= static_cast<KX_Camera*>(self_v);
818         float param = PyFloat_AsDouble(value);
819         if (param == -1) {
820                 PyErr_SetString(PyExc_AttributeError, "camera.lens = float: KX_Camera, expected a float greater then zero");
821                 return PY_SET_ATTR_FAIL;
822         }
823         
824         self->m_camdata.m_lens= param;
825         self->m_set_projection_matrix = false;
826         return PY_SET_ATTR_SUCCESS;
827 }
828
829 PyObject* KX_Camera::pyattr_get_near(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
830 {
831         KX_Camera* self= static_cast<KX_Camera*>(self_v);
832         return PyFloat_FromDouble(self->m_camdata.m_clipstart);
833 }
834
835 int KX_Camera::pyattr_set_near(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
836 {
837         KX_Camera* self= static_cast<KX_Camera*>(self_v);
838         float param = PyFloat_AsDouble(value);
839         if (param == -1) {
840                 PyErr_SetString(PyExc_AttributeError, "camera.near = float: KX_Camera, expected a float greater then zero");
841                 return PY_SET_ATTR_FAIL;
842         }
843         
844         self->m_camdata.m_clipstart= param;
845         self->m_set_projection_matrix = false;
846         return PY_SET_ATTR_SUCCESS;
847 }
848
849 PyObject* KX_Camera::pyattr_get_far(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
850 {
851         KX_Camera* self= static_cast<KX_Camera*>(self_v);
852         return PyFloat_FromDouble(self->m_camdata.m_clipend);
853 }
854
855 int KX_Camera::pyattr_set_far(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
856 {
857         KX_Camera* self= static_cast<KX_Camera*>(self_v);
858         float param = PyFloat_AsDouble(value);
859         if (param == -1) {
860                 PyErr_SetString(PyExc_AttributeError, "camera.far = float: KX_Camera, expected a float greater then zero");
861                 return PY_SET_ATTR_FAIL;
862         }
863         
864         self->m_camdata.m_clipend= param;
865         self->m_set_projection_matrix = false;
866         return PY_SET_ATTR_SUCCESS;
867 }
868
869
870 PyObject* KX_Camera::pyattr_get_use_viewport(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
871 {
872         KX_Camera* self= static_cast<KX_Camera*>(self_v);
873         return PyBool_FromLong(self->GetViewport());
874 }
875
876 int KX_Camera::pyattr_set_use_viewport(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
877 {
878         KX_Camera* self= static_cast<KX_Camera*>(self_v);
879         int param = PyObject_IsTrue( value );
880         if (param == -1) {
881                 PyErr_SetString(PyExc_AttributeError, "camera.useViewport = bool: KX_Camera, expected True or False");
882                 return PY_SET_ATTR_FAIL;
883         }
884         self->EnableViewport((bool)param);
885         return PY_SET_ATTR_SUCCESS;
886 }
887
888
889 PyObject* KX_Camera::pyattr_get_projection_matrix(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
890 {
891         KX_Camera* self= static_cast<KX_Camera*>(self_v);
892         return PyObjectFrom(self->GetProjectionMatrix()); 
893 }
894
895 int KX_Camera::pyattr_set_projection_matrix(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
896 {
897         KX_Camera* self= static_cast<KX_Camera*>(self_v);
898         MT_Matrix4x4 mat;
899         if (!PyMatTo(value, mat)) 
900                 return PY_SET_ATTR_FAIL;
901         
902         self->SetProjectionMatrix(mat);
903         return PY_SET_ATTR_SUCCESS;
904 }
905
906 PyObject* KX_Camera::pyattr_get_modelview_matrix(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
907 {
908         KX_Camera* self= static_cast<KX_Camera*>(self_v);
909         return PyObjectFrom(self->GetModelviewMatrix()); 
910 }
911
912 PyObject* KX_Camera::pyattr_get_camera_to_world(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
913 {
914         KX_Camera* self= static_cast<KX_Camera*>(self_v);
915         return PyObjectFrom(self->GetCameraToWorld());
916 }
917
918 PyObject* KX_Camera::pyattr_get_world_to_camera(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
919 {
920         KX_Camera* self= static_cast<KX_Camera*>(self_v);
921         return PyObjectFrom(self->GetWorldToCamera()); 
922 }
923
924
925 PyObject* KX_Camera::pyattr_get_INSIDE(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
926 {       return PyLong_FromSsize_t(INSIDE); }
927 PyObject* KX_Camera::pyattr_get_OUTSIDE(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
928 {       return PyLong_FromSsize_t(OUTSIDE); }
929 PyObject* KX_Camera::pyattr_get_INTERSECT(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
930 {       return PyLong_FromSsize_t(INTERSECT); }
931
932
933 bool ConvertPythonToCamera(PyObject * value, KX_Camera **object, bool py_none_ok, const char *error_prefix)
934 {
935         if (value==NULL) {
936                 PyErr_Format(PyExc_TypeError, "%s, python pointer NULL, should never happen", error_prefix);
937                 *object = NULL;
938                 return false;
939         }
940                 
941         if (value==Py_None) {
942                 *object = NULL;
943                 
944                 if (py_none_ok) {
945                         return true;
946                 } else {
947                         PyErr_Format(PyExc_TypeError, "%s, expected KX_Camera or a KX_Camera name, None is invalid", error_prefix);
948                         return false;
949                 }
950         }
951         
952         if (PyUnicode_Check(value)) {
953                 STR_String value_str = _PyUnicode_AsString(value);
954                 *object = KX_GetActiveScene()->FindCamera(value_str);
955                 
956                 if (*object) {
957                         return true;
958                 } else {
959                         PyErr_Format(PyExc_ValueError, "%s, requested name \"%s\" did not match any KX_Camera in this scene", error_prefix, _PyUnicode_AsString(value));
960                         return false;
961                 }
962         }
963         
964         if (PyObject_TypeCheck(value, &KX_Camera::Type)) {
965                 *object = static_cast<KX_Camera*>BGE_PROXY_REF(value);
966                 
967                 /* sets the error */
968                 if (*object==NULL) {
969                         PyErr_Format(PyExc_SystemError, "%s, " BGE_PROXY_ERROR_MSG, error_prefix);
970                         return false;
971                 }
972                 
973                 return true;
974         }
975         
976         *object = NULL;
977         
978         if (py_none_ok) {
979                 PyErr_Format(PyExc_TypeError, "%s, expect a KX_Camera, a string or None", error_prefix);
980         } else {
981                 PyErr_Format(PyExc_TypeError, "%s, expect a KX_Camera or a string", error_prefix);
982         }
983         
984         return false;
985 }
986
987 KX_PYMETHODDEF_DOC_O(KX_Camera, getScreenPosition,
988 "getScreenPosition()\n"
989 )
990
991 {
992         MT_Vector3 vect;
993         KX_GameObject *obj = NULL;
994
995         if (!PyVecTo(value, vect))
996         {
997                 if(ConvertPythonToGameObject(value, &obj, true, ""))
998                 {
999                         PyErr_Clear();
1000                         vect = MT_Vector3(obj->NodeGetWorldPosition());
1001                 }
1002                 else
1003                 {
1004                         PyErr_SetString(PyExc_TypeError, "Error in getScreenPosition. Expected a Vector3 or a KX_GameObject or a string for a name of a KX_GameObject");
1005                         return NULL;
1006                 }
1007         }
1008
1009         GLint viewport[4];
1010         GLdouble win[3];
1011         GLdouble modelmatrix[16];
1012         GLdouble projmatrix[16];
1013
1014         MT_Matrix4x4 m_modelmatrix = this->GetModelviewMatrix();
1015         MT_Matrix4x4 m_projmatrix = this->GetProjectionMatrix();
1016
1017         m_modelmatrix.getValue(modelmatrix);
1018         m_projmatrix.getValue(projmatrix);
1019
1020         glGetIntegerv(GL_VIEWPORT, viewport);
1021
1022         gluProject(vect[0], vect[1], vect[2], modelmatrix, projmatrix, viewport, &win[0], &win[1], &win[2]);
1023
1024         vect[0] =  (win[0] - viewport[0]) / viewport[2];
1025         vect[1] =  (win[1] - viewport[1]) / viewport[3];
1026
1027         vect[1] = 1.0 - vect[1]; //to follow Blender window coordinate system (Top-Down)
1028
1029         PyObject* ret = PyTuple_New(2);
1030         if(ret){
1031                 PyTuple_SET_ITEM(ret, 0, PyFloat_FromDouble(vect[0]));
1032                 PyTuple_SET_ITEM(ret, 1, PyFloat_FromDouble(vect[1]));
1033                 return ret;
1034         }
1035
1036         return NULL;
1037 }
1038
1039 KX_PYMETHODDEF_DOC_VARARGS(KX_Camera, getScreenVect,
1040 "getScreenVect()\n"
1041 )
1042 {
1043         double x,y;
1044         if (!PyArg_ParseTuple(args,"dd:getScreenVect",&x,&y))
1045                 return NULL;
1046
1047         y = 1.0 - y; //to follow Blender window coordinate system (Top-Down)
1048
1049         MT_Vector3 vect;
1050         MT_Point3 campos, screenpos;
1051
1052         GLint viewport[4];
1053         GLdouble win[3];
1054         GLdouble modelmatrix[16];
1055         GLdouble projmatrix[16];
1056
1057         MT_Matrix4x4 m_modelmatrix = this->GetModelviewMatrix();
1058         MT_Matrix4x4 m_projmatrix = this->GetProjectionMatrix();
1059
1060         m_modelmatrix.getValue(modelmatrix);
1061         m_projmatrix.getValue(projmatrix);
1062
1063         glGetIntegerv(GL_VIEWPORT, viewport);
1064
1065         vect[0] = x * viewport[2];
1066         vect[1] = y * viewport[3];
1067
1068         vect[0] += viewport[0];
1069         vect[1] += viewport[1];
1070
1071         glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &vect[2]);
1072         gluUnProject(vect[0], vect[1], vect[2], modelmatrix, projmatrix, viewport, &win[0], &win[1], &win[2]);
1073
1074         campos = this->GetCameraLocation();
1075         screenpos = MT_Point3(win[0], win[1], win[2]);
1076         vect = campos-screenpos;
1077
1078         vect.normalize();
1079         return PyObjectFrom(vect);
1080 }
1081
1082 KX_PYMETHODDEF_DOC_VARARGS(KX_Camera, getScreenRay,
1083 "getScreenRay()\n"
1084 )
1085 {
1086         MT_Vector3 vect;
1087         double x,y,dist;
1088         char *propName = NULL;
1089
1090         if (!PyArg_ParseTuple(args,"ddd|s:getScreenRay",&x,&y,&dist,&propName))
1091                 return NULL;
1092
1093         PyObject* argValue = PyTuple_New(2);
1094         if (argValue) {
1095                 PyTuple_SET_ITEM(argValue, 0, PyFloat_FromDouble(x));
1096                 PyTuple_SET_ITEM(argValue, 1, PyFloat_FromDouble(y));
1097         }
1098
1099         if(!PyVecTo(PygetScreenVect(argValue), vect))
1100         {
1101                 Py_DECREF(argValue);
1102                 PyErr_SetString(PyExc_TypeError,
1103                         "Error in getScreenRay. Invalid 2D coordinate. Expected a normalized 2D screen coordinate, a distance and an optional property argument");
1104                 return NULL;
1105         }
1106         Py_DECREF(argValue);
1107
1108         dist = -dist;
1109         vect += this->GetCameraLocation();
1110
1111         argValue = (propName?PyTuple_New(3):PyTuple_New(2));
1112         if (argValue) {
1113                 PyTuple_SET_ITEM(argValue, 0, PyObjectFrom(vect));
1114                 PyTuple_SET_ITEM(argValue, 1, PyFloat_FromDouble(dist));
1115                 if (propName)
1116                         PyTuple_SET_ITEM(argValue, 2, PyUnicode_FromString(propName));
1117
1118                 PyObject* ret= this->PyrayCastTo(argValue,NULL);
1119                 Py_DECREF(argValue);
1120                 return ret;
1121         }
1122
1123         return NULL;
1124 }
1125