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