diff options
Diffstat (limited to 'source/gameengine/Ketsji')
-rw-r--r-- | source/gameengine/Ketsji/KX_Camera.cpp | 460 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_Camera.h | 102 |
2 files changed, 541 insertions, 21 deletions
diff --git a/source/gameengine/Ketsji/KX_Camera.cpp b/source/gameengine/Ketsji/KX_Camera.cpp index cc970d90339..cad6501bd13 100644 --- a/source/gameengine/Ketsji/KX_Camera.cpp +++ b/source/gameengine/Ketsji/KX_Camera.cpp @@ -30,19 +30,25 @@ * ***** END GPL/BL DUAL LICENSE BLOCK ***** * Camera in the gameengine. Cameras are also used for views. */ - + #include "KX_Camera.h" +#include "KX_Python.h" +#include "KX_PyMath.h" #ifdef HAVE_CONFIG_H #include <config.h> #endif KX_Camera::KX_Camera(void* sgReplicationInfo, SG_Callbacks callbacks, - const RAS_CameraData& camdata) + const RAS_CameraData& camdata, + bool frustum_culling) : KX_GameObject(sgReplicationInfo,callbacks), - m_camdata(camdata) + m_camdata(camdata), + m_dirty(true), + m_frustum_culling(frustum_culling), + m_set_projection_matrix(false) { // setting a name would be nice... m_name = "cam"; @@ -91,7 +97,7 @@ void KX_Camera::CorrectLookUp(MT_Scalar speed) -const MT_Point3 KX_Camera::GetCameraLocation() +const MT_Point3 KX_Camera::GetCameraLocation() const { /* this is the camera locatio in cam coords... */ //return m_trans1.getOrigin(); @@ -106,7 +112,7 @@ const MT_Point3 KX_Camera::GetCameraLocation() /* I want the camera orientation as well. */ -const MT_Quaternion KX_Camera::GetCameraOrientation() +const MT_Quaternion KX_Camera::GetCameraOrientation() const { MT_Transform trans; trans.setBasis(NodeGetWorldOrientation()); @@ -123,6 +129,8 @@ const MT_Quaternion KX_Camera::GetCameraOrientation() void KX_Camera::SetProjectionMatrix(const MT_Matrix4x4 & mat) { m_projection_matrix = mat; + m_dirty = true; + m_set_projection_matrix = true; } @@ -133,6 +141,7 @@ void KX_Camera::SetProjectionMatrix(const MT_Matrix4x4 & mat) void KX_Camera::SetModelviewMatrix(const MT_Matrix4x4 & mat) { m_modelview_matrix = mat; + m_dirty = true; } @@ -140,7 +149,7 @@ void KX_Camera::SetModelviewMatrix(const MT_Matrix4x4 & mat) /** * Gets the projection matrix that is used by the rasterizer. */ -const MT_Matrix4x4& KX_Camera::GetProjectionMatrix() +const MT_Matrix4x4& KX_Camera::GetProjectionMatrix() const { return m_projection_matrix; } @@ -150,31 +159,35 @@ const MT_Matrix4x4& KX_Camera::GetProjectionMatrix() /** * Gets the modelview matrix that is used by the rasterizer. */ -const MT_Matrix4x4& KX_Camera::GetModelviewMatrix() +const MT_Matrix4x4& KX_Camera::GetModelviewMatrix() const { return m_modelview_matrix; } +bool KX_Camera::hasValidProjectionMatrix() const +{ + return m_set_projection_matrix; +} /* * These getters retrieve the clip data and the focal length */ -float KX_Camera::GetLens() +float KX_Camera::GetLens() const { return m_camdata.m_lens; } -float KX_Camera::GetCameraNear() +float KX_Camera::GetCameraNear() const { return m_camdata.m_clipstart; } -float KX_Camera::GetCameraFar() +float KX_Camera::GetCameraFar() const { return m_camdata.m_clipend; } @@ -185,3 +198,430 @@ RAS_CameraData* KX_Camera::GetCameraData() { return &m_camdata; } + +void KX_Camera::ExtractClipPlanes() +{ + MT_Matrix4x4 m = m_projection_matrix * GetWorldToCamera(); + // Left clip plane + m_planes[0] = m[3] + m[0]; + // Right clip plane + m_planes[1] = m[3] - m[0]; + // Top clip plane + m_planes[2] = m[3] - m[1]; + // Bottom clip plane + m_planes[3] = m[3] + m[1]; + // Near clip plane + m_planes[4] = m[3] + m[2]; + // Far clip plane + m_planes[5] = m[3] - m[2]; + + m_dirty = false; +} + +bool KX_Camera::PointInsideFrustum(const MT_Point3& x) +{ + if (m_dirty) + ExtractClipPlanes(); + + for( unsigned int i = 0; i < 6 ; i++ ) + { + if (m_planes[i][0]*x[0] + m_planes[i][1]*x[1] + m_planes[i][2]*x[2] + m_planes[i][3] < 0.) + return false; + } + return true; +} + +int KX_Camera::BoxInsideFrustum(const MT_Point3 *box) +{ + if (m_dirty) + ExtractClipPlanes(); + + unsigned int insideCount = 0; + for( unsigned int p = 0; p < 6 ; p++ ) + { + unsigned int behindCount = 0; + for (unsigned int v = 0; v < 8 ; v++) + { + 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.) + behindCount++; + } + + if (behindCount == 8) + return OUTSIDE; + + if (!behindCount) + insideCount++; + } + + if (insideCount == 6) + return INSIDE; + + return INTERSECT; +} + +int KX_Camera::SphereInsideFrustum(const MT_Point3& centre, const MT_Scalar &radius) +{ + MT_Scalar distance; + for (unsigned int p = 0; p < 6; p++) + { + distance = m_planes[p][0]*centre[0] + m_planes[p][1]*centre[1] + m_planes[p][2]*centre[2] + m_planes[p][3]; + if (distance < -radius) + return OUTSIDE; + if (fabs(distance) < radius) + return INTERSECT; + } + return INSIDE; +} + +bool KX_Camera::GetFrustumCulling() const +{ + return m_frustum_culling; +} + +//---------------------------------------------------------------------------- +//Python + + +PyMethodDef KX_Camera::Methods[] = { + KX_PYMETHODTABLE(KX_Camera, sphereInsideFrustum), + KX_PYMETHODTABLE(KX_Camera, boxInsideFrustum), + KX_PYMETHODTABLE(KX_Camera, pointInsideFrustum), + KX_PYMETHODTABLE(KX_Camera, getCameraToWorld), + KX_PYMETHODTABLE(KX_Camera, getWorldToCamera), + KX_PYMETHODTABLE(KX_Camera, getProjectionMatrix), + KX_PYMETHODTABLE(KX_Camera, setProjectionMatrix), + + {NULL,NULL} //Sentinel +}; + +char KX_Camera::doc[] = "Module KX_Camera\n\n" +"Constants:\n" +"\tINSIDE\n" +"\tINTERSECT\n" +"\tOUTSIDE\n" +"Attributes:\n" +"\tlens -> float\n" +"\t\tThe camera's lens value\n" +"\tnear -> float\n" +"\t\tThe camera's near clip distance\n" +"\tfar -> float\n" +"\t\tThe camera's far clip distance\n" +"\tfrustum_culling -> bool\n" +"\t\tNon zero if this camera is frustum culling.\n" +"\tprojection_matrix -> [[float]]\n" +"\t\tThis camera's projection matrix.\n" +"\tmodelview_matrix -> [[float]] (read only)\n" +"\t\tThis camera's model view matrix.\n" +"\t\tRegenerated every frame from the camera's position and orientation.\n" +"\tcamera_to_world -> [[float]] (read only)\n" +"\t\tThis camera's camera to world transform.\n" +"\t\tRegenerated every frame from the camera's position and orientation.\n" +"\tworld_to_camera -> [[float]] (read only)\n" +"\t\tThis camera's world to camera transform.\n" +"\t\tRegenerated every frame from the camera's position and orientation.\n" +"\t\tThis is camera_to_world inverted.\n"; + +PyTypeObject KX_Camera::Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "KX_Camera", + sizeof(KX_Camera), + 0, + PyDestructor, + 0, + __getattr, + __setattr, + 0, //&MyPyCompare, + __repr, + 0, //&cvalue_as_number, + 0, + 0, + 0, + 0, 0, 0, 0, 0, 0, + doc +}; + +PyParentObject KX_Camera::Parents[] = { + &KX_Camera::Type, + &KX_GameObject::Type, + &SCA_IObject::Type, + &CValue::Type, + NULL +}; + +PyObject* KX_Camera::_getattr(const STR_String& attr) +{ + if (attr == "INSIDE") + return PyInt_FromLong(INSIDE); + if (attr == "OUTSIDE") + return PyInt_FromLong(OUTSIDE); + if (attr == "INTERSECT") + return PyInt_FromLong(INTERSECT); + + if (attr == "lens") + return PyFloat_FromDouble(GetLens()); + if (attr == "near") + return PyFloat_FromDouble(GetCameraNear()); + if (attr == "far") + return PyFloat_FromDouble(GetCameraFar()); + if (attr == "frustum_culling") + return PyInt_FromLong(m_frustum_culling); + if (attr == "projection_matrix") + return PyObjectFromMT_Matrix4x4(GetProjectionMatrix()); + if (attr == "modelview_matrix") + return PyObjectFromMT_Matrix4x4(GetModelviewMatrix()); + if (attr == "camera_to_world") + return PyObjectFromMT_Matrix4x4(GetCameraToWorld()); + if (attr == "world_to_camera") + return PyObjectFromMT_Matrix4x4(GetWorldToCamera()); + + _getattr_up(KX_GameObject); +} + +int KX_Camera::_setattr(const STR_String &attr, PyObject *pyvalue) +{ + if (PyInt_Check(pyvalue)) + { + if (attr == "frustum_culling") + { + m_frustum_culling = PyInt_AsLong(pyvalue); + return 0; + } + } + + if (PyFloat_Check(pyvalue)) + { + if (attr == "lens") + { + m_camdata.m_lens = PyFloat_AsDouble(pyvalue); + m_set_projection_matrix = false; + return 0; + } + if (attr == "near") + { + m_camdata.m_clipstart = PyFloat_AsDouble(pyvalue); + m_set_projection_matrix = false; + return 0; + } + if (attr == "far") + { + m_camdata.m_clipend = PyFloat_AsDouble(pyvalue); + m_set_projection_matrix = false; + return 0; + } + } + + if (attr == "projection_matrix") + { + PysetProjectionMatrix((PyObject*) this, pyvalue, NULL); + return 0; + } + + return KX_GameObject::_setattr(attr, pyvalue); +} + +KX_PYMETHODDEF_DOC(KX_Camera, sphereInsideFrustum, +"sphereInsideFrustum(centre, radius) -> Integer\n" +"\treturns INSIDE, OUTSIDE or INTERSECT if the given sphere is\n" +"\tinside/outside/intersects this camera's viewing frustum.\n\n" +"\tcentre = the centre of the sphere (in world coordinates.)\n" +"\tradius = the radius of the sphere\n\n" +"\tExample:\n" +"\timport GameLogic\n\n" +"\tco = GameLogic.getCurrentController()\n" +"\tcam = co.GetOwner()\n\n" +"\t# A sphere of radius 4.0 located at [x, y, z] = [1.0, 1.0, 1.0]\n" +"\tif (cam.sphereInsideFrustum([1.0, 1.0, 1.0], 4) != cam.OUTSIDE):\n" +"\t\t# Sphere is inside frustum !\n" +"\t\t# Do something useful !\n" +"\telse:\n" +"\t\t# Sphere is outside frustum\n" +) +{ + PyObject *pycentre; + float radius; + if (PyArg_ParseTuple(args, "Of", &pycentre, &radius)) + { + MT_Point3 centre = MT_Point3FromPyList(pycentre); + if (PyErr_Occurred()) + { + PyErr_SetString(PyExc_TypeError, "Expected list for argument centre."); + return Py_None; + } + + return PyInt_FromLong(SphereInsideFrustum(centre, radius)); + } + + PyErr_SetString(PyExc_TypeError, "Expected arguments: (centre, radius)"); + + return Py_None; +} + +KX_PYMETHODDEF_DOC(KX_Camera, boxInsideFrustum, +"boxInsideFrustum(box) -> Integer\n" +"\treturns INSIDE, OUTSIDE or INTERSECT if the given box is\n" +"\tinside/outside/intersects this camera's viewing frustum.\n\n" +"\tbox = a list of the eight (8) corners of the box (in world coordinates.)\n\n" +"\tExample:\n" +"\timport GameLogic\n\n" +"\tco = GameLogic.getCurrentController()\n" +"\tcam = co.GetOwner()\n\n" +"\tbox = []\n" +"\tbox.append([-1.0, -1.0, -1.0])\n" +"\tbox.append([-1.0, -1.0, 1.0])\n" +"\tbox.append([-1.0, 1.0, -1.0])\n" +"\tbox.append([-1.0, 1.0, 1.0])\n" +"\tbox.append([ 1.0, -1.0, -1.0])\n" +"\tbox.append([ 1.0, -1.0, 1.0])\n" +"\tbox.append([ 1.0, 1.0, -1.0])\n" +"\tbox.append([ 1.0, 1.0, 1.0])\n\n" +"\tif (cam.boxInsideFrustum(box) != cam.OUTSIDE):\n" +"\t\t# Box is inside/intersects frustum !\n" +"\t\t# Do something useful !\n" +"\telse:\n" +"\t\t# Box is outside the frustum !\n" +) +{ + PyObject *pybox; + if (PyArg_ParseTuple(args, "O", &pybox)) + { + unsigned int num_points = PySequence_Size(pybox); + if (num_points != 8) + { + PyErr_Format(PyExc_TypeError, "Expected eight (8) points, got %d", num_points); + return Py_None; + } + + MT_Point3 box[8]; + for (unsigned int p = 0; p < 8 ; p++) + { + box[p] = MT_Point3FromPyList(PySequence_GetItem(pybox, p)); + if (PyErr_Occurred()) + { + return Py_None; + } + } + + return PyInt_FromLong(BoxInsideFrustum(box)); + } + + PyErr_SetString(PyExc_TypeError, "Expected argument: list of points."); + + return Py_None; +} + +KX_PYMETHODDEF_DOC(KX_Camera, pointInsideFrustum, +"pointInsideFrustum(point) -> Bool\n" +"\treturns 1 if the given point is inside this camera's viewing frustum.\n\n" +"\tpoint = The point to test (in world coordinates.)\n\n" +"\tExample:\n" +"\timport GameLogic\n\n" +"\tco = GameLogic.getCurrentController()\n" +"\tcam = co.GetOwner()\n\n" +"\t# Test point [0.0, 0.0, 0.0]" +"\tif (cam.pointInsideFrustum([0.0, 0.0, 0.0])):\n" +"\t\t# Point is inside frustum !\n" +"\t\t# Do something useful !\n" +"\telse:\n" +"\t\t# Box is outside the frustum !\n" +) +{ + PyObject *pypoint; + if (PyArg_ParseTuple(args, "O", &pypoint)) + { + MT_Point3 point = MT_Point3FromPyList(pypoint); + if (PyErr_Occurred()) + return Py_None; + + return PyInt_FromLong(PointInsideFrustum(point)); + } + + PyErr_SetString(PyExc_TypeError, "Expected point argument."); + return Py_None; +} + +KX_PYMETHODDEF_DOC(KX_Camera, getCameraToWorld, +"getCameraToWorld() -> Matrix4x4\n" +"\treturns the camera to world transformation matrix, as a list of four lists of four values.\n\n" +"\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" +) +{ + return PyObjectFromMT_Matrix4x4(GetCameraToWorld()); +} + +KX_PYMETHODDEF_DOC(KX_Camera, getWorldToCamera, +"getWorldToCamera() -> Matrix4x4\n" +"\treturns the world to camera transformation matrix, as a list of four lists of four values.\n\n" +"\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" +) +{ + return PyObjectFromMT_Matrix4x4(GetWorldToCamera()); +} + +KX_PYMETHODDEF_DOC(KX_Camera, getProjectionMatrix, +"getProjectionMatrix() -> Matrix4x4\n" +"\treturns this camera's projection matrix, as a list of four lists of four values.\n\n" +"\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" +) +{ + return PyObjectFromMT_Matrix4x4(GetProjectionMatrix()); +} + +KX_PYMETHODDEF_DOC(KX_Camera, setProjectionMatrix, +"setProjectionMatrix(MT_Matrix4x4 m) -> None\n" +"\tSets this camera's projection matrix\n" +"\n" +"\tExample:\n" +"\timport GameLogic\n" +"\t# Set a perspective projection matrix\n" +"\tdef Perspective(left, right, bottom, top, near, far):\n" +"\t\tm = MT_Matrix4x4()\n" +"\t\tm[0][0] = m[0][2] = right - left\n" +"\t\tm[1][1] = m[1][2] = top - bottom\n" +"\t\tm[2][2] = m[2][3] = -far - near\n" +"\t\tm[3][2] = -1\n" +"\t\tm[3][3] = 0\n" +"\t\treturn m\n" +"\n" +"\t# Set an orthographic projection matrix\n" +"\tdef Orthographic(left, right, bottom, top, near, far):\n" +"\t\tm = MT_Matrix4x4()\n" +"\t\tm[0][0] = right - left\n" +"\t\tm[0][3] = -right - left\n" +"\t\tm[1][1] = top - bottom\n" +"\t\tm[1][3] = -top - bottom\n" +"\t\tm[2][2] = far - near\n" +"\t\tm[2][3] = -far - near\n" +"\t\tm[3][3] = 1\n" +"\t\treturn m\n" +"\n" +"\t# Set an isometric projection matrix\n" +"\tdef Isometric(left, right, bottom, top, near, far):\n" +"\t\tm = MT_Matrix4x4()\n" +"\t\tm[0][0] = m[0][2] = m[1][1] = 0.8660254037844386\n" +"\t\tm[1][0] = 0.25\n" +"\t\tm[1][2] = -0.25\n" +"\t\tm[3][3] = 1\n" +"\t\treturn m\n" +"\n" +"\t" +"\tco = GameLogic.getCurrentController()\n" +"\tcam = co.getOwner()\n" +"\tcam.setProjectionMatrix(Perspective(-1.0, 1.0, -1.0, 1.0, 0.1, 1))\n") +{ + PyObject *pymat; + if (PyArg_ParseTuple(args, "O", &pymat)) + { + MT_Matrix4x4 mat = MT_Matrix4x4FromPyObject(pymat); + if (PyErr_Occurred()) + { + return Py_None; + } + + SetProjectionMatrix(mat); + return Py_None; + } + + PyErr_SetString(PyExc_TypeError, "Expected 4x4 list as matrix argument."); + return Py_None; +} diff --git a/source/gameengine/Ketsji/KX_Camera.h b/source/gameengine/Ketsji/KX_Camera.h index 0185a0ba9d5..29bb35183f0 100644 --- a/source/gameengine/Ketsji/KX_Camera.h +++ b/source/gameengine/Ketsji/KX_Camera.h @@ -46,9 +46,10 @@ class KX_Camera : public KX_GameObject { - + Py_Header; +protected: /** Camera parameters (clips distances, focal lenght). These - * params are closely tied to BLender. In the gameengine, only the + * params are closely tied to Blender. In the gameengine, only the * projection and modelview matrices are relevant. There's a * conversion being done in the engine class. Why is it stored * here? It doesn't really have a function here. */ @@ -74,20 +75,57 @@ class KX_Camera : public KX_GameObject * Storage for the modelview matrix that is passed to the * rasterizer. */ MT_Matrix4x4 m_modelview_matrix; + + /** + * true if the view frustum (modelview/projection matrix) + * has changed - the clip planes (m_planes) will have to be + * regenerated. + */ + bool m_dirty; + + /** + * View Frustum clip planes. + */ + MT_Vector4 m_planes[6]; + + /** + * This camera is frustum culling. + * Some cameras (ie if the game was started from a non camera view should not cull.) + */ + bool m_frustum_culling; + + /** + * true if this camera has a valid projection matrix. + */ + bool m_set_projection_matrix; + /** + * Python module doc string. + */ + static char doc[]; + + /** + * Extracts the camera clip frames from the projection and world-to-camera matrices. + */ + void ExtractClipPlanes(); public: - KX_Camera(void* sgReplicationInfo,SG_Callbacks callbacks,const RAS_CameraData& camdata); + typedef enum { INSIDE, INTERSECT, OUTSIDE } ; + + KX_Camera(void* sgReplicationInfo,SG_Callbacks callbacks,const RAS_CameraData& camdata, bool frustum_culling = true); virtual ~KX_Camera(); MT_Transform GetWorldToCamera() const; MT_Transform GetCameraToWorld() const; + /** + * Not implemented. + */ void CorrectLookUp(MT_Scalar speed); - const MT_Point3 GetCameraLocation(); + const MT_Point3 GetCameraLocation() const; /* I want the camera orientation as well. */ - const MT_Quaternion GetCameraOrientation(); + const MT_Quaternion GetCameraOrientation() const; /** Sets the projection matrix that is used by the rasterizer. */ void SetProjectionMatrix(const MT_Matrix4x4 & mat); @@ -96,21 +134,63 @@ public: void SetModelviewMatrix(const MT_Matrix4x4 & mat); /** Gets the projection matrix that is used by the rasterizer. */ - const MT_Matrix4x4& GetProjectionMatrix(); + const MT_Matrix4x4& GetProjectionMatrix() const; + + /** returns true if this camera has been set a projection matrix. */ + bool hasValidProjectionMatrix() const; /** Gets the modelview matrix that is used by the rasterizer. * @warning If the Camera is a dynamic object then this method may return garbage. Use GetCameraToWorld() instead. */ - const MT_Matrix4x4& GetModelviewMatrix(); + const MT_Matrix4x4& GetModelviewMatrix() const; - /** Gets the focal lenght. */ - float GetLens(); + /** Gets the focal length. */ + float GetLens() const; /** Gets the near clip distance. */ - float GetCameraNear(); + float GetCameraNear() const; /** Gets the far clip distance. */ - float GetCameraFar(); + float GetCameraFar() const; /** Gets all camera data. */ RAS_CameraData* GetCameraData(); + + /** + * Tests if the given sphere is inside this camera's view frustum. + * + * @param centre The centre of the sphere, in world coordinates. + * @param radius The radius of the sphere. + * @return INSIDE, INTERSECT, or OUTSIDE depending on the sphere's relation to the frustum. + */ + int SphereInsideFrustum(const MT_Point3& centre, const MT_Scalar &radius); + /** + * Tests the given eight corners of a box with the view frustum. + * + * @param box a pointer to eight MT_Point3 representing the world coordinates of the corners of the box. + * @return INSIDE, INTERSECT, or OUTSIDE depending on the box's relation to the frustum. + */ + int BoxInsideFrustum(const MT_Point3 *box); + /** + * Tests the given point against the view frustum. + * @return true if the given point is inside or on the view frustum; false if it is outside. + */ + bool PointInsideFrustum(const MT_Point3& x); + + /** + * Gets this camera's culling status. + */ + bool GetFrustumCulling() const; + + KX_PYMETHOD_DOC(KX_Camera, sphereInsideFrustum); + KX_PYMETHOD_DOC(KX_Camera, boxInsideFrustum); + KX_PYMETHOD_DOC(KX_Camera, pointInsideFrustum); + + KX_PYMETHOD_DOC(KX_Camera, getCameraToWorld); + KX_PYMETHOD_DOC(KX_Camera, getWorldToCamera); + KX_PYMETHOD_DOC(KX_Camera, getProjectionMatrix); + KX_PYMETHOD_DOC(KX_Camera, setProjectionMatrix); + + virtual PyObject* _getattr(const STR_String& attr); /* lens, near, far, projection_matrix */ + virtual int _setattr(const STR_String& attr, PyObject *pyvalue); + }; #endif //__KX_CAMERA |