Logo Search packages:      
Sourcecode: blender version File versions

KX_Camera.cpp

/*
 * $Id: KX_Camera.cpp,v 1.15 2004/07/22 00:26:34 kester Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** 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,
                               bool frustum_culling,
                               PyTypeObject *T)
                              :
                              KX_GameObject(sgReplicationInfo,callbacks,T),
                              m_camdata(camdata),
                              m_dirty(true),
                              m_normalised(false),
                              m_frustum_culling(frustum_culling && camdata.m_perspective),
                              m_set_projection_matrix(false),
                              m_set_frustum_centre(false)
{
      // setting a name would be nice...
      m_name = "cam";
      m_projection_matrix.setIdentity();
      m_modelview_matrix.setIdentity();
      SetProperty("camera",new CIntValue(1));
}


KX_Camera::~KX_Camera()
{
}     


      
MT_Transform KX_Camera::GetWorldToCamera() const
{ 
      MT_Transform camtrans;
      camtrans.invert(MT_Transform(NodeGetWorldPosition(), NodeGetWorldOrientation()));
      
      return camtrans;
}


       
MT_Transform KX_Camera::GetCameraToWorld() const
{
      return MT_Transform(NodeGetWorldPosition(), NodeGetWorldOrientation());
}



void KX_Camera::CorrectLookUp(MT_Scalar speed)
{
}



const MT_Point3 KX_Camera::GetCameraLocation() const
{
      /* this is the camera locatio in cam coords... */
      //return m_trans1.getOrigin();
      //return MT_Point3(0,0,0);   <-----
      /* .... I want it in world coords */
      //MT_Transform trans;
      //trans.setBasis(NodeGetWorldOrientation());
      
      return NodeGetWorldPosition();            
}



/* I want the camera orientation as well. */
const MT_Quaternion KX_Camera::GetCameraOrientation() const
{
      return NodeGetWorldOrientation().getRotation();
}



/**
* Sets the projection matrix that is used by the rasterizer.
*/
void KX_Camera::SetProjectionMatrix(const MT_Matrix4x4 & mat)
{
      m_projection_matrix = mat;
      m_dirty = true;
      m_set_projection_matrix = true;
      m_set_frustum_centre = false;
}



/**
* Sets the modelview matrix that is used by the rasterizer.
*/
void KX_Camera::SetModelviewMatrix(const MT_Matrix4x4 & mat)
{
      m_modelview_matrix = mat;
      m_dirty = true;
      m_set_frustum_centre = false;
}



/**
* Gets the projection matrix that is used by the rasterizer.
*/
const MT_Matrix4x4& KX_Camera::GetProjectionMatrix() const
{
      return m_projection_matrix;
}



/**
* Gets the modelview matrix that is used by the rasterizer.
*/
const MT_Matrix4x4& KX_Camera::GetModelviewMatrix() const
{
      return m_modelview_matrix;
}


bool KX_Camera::hasValidProjectionMatrix() const
{
      return m_set_projection_matrix;
}

void KX_Camera::InvalidateProjectionMatrix(bool valid)
{
      m_set_projection_matrix = valid;
}


/*
* These getters retrieve the clip data and the focal length
*/
float KX_Camera::GetLens() const
{
      return m_camdata.m_lens;
}



float KX_Camera::GetCameraNear() const
{
      return m_camdata.m_clipstart;
}



float KX_Camera::GetCameraFar() const
{
      return m_camdata.m_clipend;
}



RAS_CameraData*   KX_Camera::GetCameraData()
{
      return &m_camdata; 
}

void KX_Camera::ExtractClipPlanes()
{
      if (!m_dirty)
            return;

      MT_Matrix4x4 m = m_projection_matrix * m_modelview_matrix;
      // 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;
      m_normalised = false;
}

void KX_Camera::NormaliseClipPlanes()
{
      if (m_normalised)
            return;
      
      for (unsigned int p = 0; p < 6; p++)
      {
            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]);
            if (!MT_fuzzyZero(factor))
                  m_planes[p] /= factor;
      }
      
      m_normalised = true;
}

void KX_Camera::ExtractFrustumSphere()
{
      if (m_set_frustum_centre)
            return;

      // The most extreme points on the near and far plane. (normalised device coords)
      MT_Vector4 hnear(1., 1., 0., 1.), hfar(1., 1., 1., 1.);
      MT_Matrix4x4 clip_camcs_matrix = m_projection_matrix;
      clip_camcs_matrix.invert();
      
      // Transform to hom camera local space
      hnear = clip_camcs_matrix*hnear;
      hfar = clip_camcs_matrix*hfar;
      
      // Tranform to 3d camera local space.
      MT_Point3 nearpoint(hnear[0]/hnear[3], hnear[1]/hnear[3], hnear[2]/hnear[3]);
      MT_Point3 farpoint(hfar[0]/hfar[3], hfar[1]/hfar[3], hfar[2]/hfar[3]);
      
      // Compute centre
      m_frustum_centre = MT_Point3(0., 0.,
            (nearpoint.dot(nearpoint) - farpoint.dot(farpoint))/(2.0*(m_camdata.m_clipend - m_camdata.m_clipstart)));
      m_frustum_radius = m_frustum_centre.distance(farpoint);
      
      // Transform to world space.
      m_frustum_centre = GetCameraToWorld()(m_frustum_centre);
      m_frustum_radius /= fabs(NodeGetWorldScaling()[NodeGetWorldScaling().closestAxis()]);
      
      m_set_frustum_centre = true;
}

bool KX_Camera::PointInsideFrustum(const MT_Point3& x)
{
      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)
{
      ExtractClipPlanes();
      
      unsigned int insideCount = 0;
      // 6 view frustum planes
      for( unsigned int p = 0; p < 6 ; p++ )
      {
            unsigned int behindCount = 0;
            // 8 box verticies.
            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++;
            }
            
            // 8 points behind this plane
            if (behindCount == 8)
                  return OUTSIDE;

            // Every box vertex is on the front side of this plane
            if (!behindCount)
                  insideCount++;
      }
      
      // All box verticies are on the front side of all frustum planes.
      if (insideCount == 6)
            return INSIDE;
      
      return INTERSECT;
}

int KX_Camera::SphereInsideFrustum(const MT_Point3& centre, const MT_Scalar &radius)
{
      ExtractFrustumSphere();
      if (centre.distance2(m_frustum_centre) > (radius + m_frustum_radius)*(radius + m_frustum_radius))
            return OUTSIDE;

      unsigned int p;
      ExtractClipPlanes();
      NormaliseClipPlanes();
            
      MT_Scalar distance;
      int intersect = INSIDE;
      // distance:  <-------- OUTSIDE -----|----- INTERSECT -----0----- INTERSECT -----|----- INSIDE -------->
      //                                -radius                                      radius
      for (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 (fabs(distance) <= radius)
                  intersect = INTERSECT;
            else if (distance < -radius)
                  return OUTSIDE;
      }
      
      return intersect;
}

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); /* new ref */
      if (attr == "OUTSIDE")
            return PyInt_FromLong(OUTSIDE); /* new ref */
      if (attr == "INTERSECT")
            return PyInt_FromLong(INTERSECT); /* new ref */
      
      if (attr == "lens")
            return PyFloat_FromDouble(GetLens()); /* new ref */
      if (attr == "near")
            return PyFloat_FromDouble(GetCameraNear()); /* new ref */
      if (attr == "far")
            return PyFloat_FromDouble(GetCameraFar()); /* new ref */
      if (attr == "frustum_culling")
            return PyInt_FromLong(m_frustum_culling); /* new ref */
      if (attr == "perspective")
            return PyInt_FromLong(m_camdata.m_perspective); /* new ref */
      if (attr == "projection_matrix")
            return PyObjectFrom(GetProjectionMatrix()); /* new ref */
      if (attr == "modelview_matrix")
            return PyObjectFrom(GetModelviewMatrix()); /* new ref */
      if (attr == "camera_to_world")
            return PyObjectFrom(GetCameraToWorld()); /* new ref */
      if (attr == "world_to_camera")
            return PyObjectFrom(GetWorldToCamera()); /* new ref */
      
      _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 (attr == "perspective")
            {
                  m_camdata.m_perspective = 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 (PyObject_IsMT_Matrix(pyvalue, 4))
      {
            if (attr == "projection_matrix")
            {
                  MT_Matrix4x4 mat;
                  if (PyMatTo(pyvalue, mat))
                  {
                        SetProjectionMatrix(mat);
                        return 0;
                  }
                  return 1;
            }
      }
      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;
            if (PyVecTo(pycentre, centre))
            {
                  return PyInt_FromLong(SphereInsideFrustum(centre, radius)); /* new ref */
            }
      }

      PyErr_SetString(PyExc_TypeError, "sphereInsideFrustum: Expected arguments: (centre, radius)");
      
      Py_Return;
}

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, "boxInsideFrustum: Expected eight (8) points, got %d", num_points);
                  return NULL;
            }
            
            MT_Point3 box[8];
            for (unsigned int p = 0; p < 8 ; p++)
            {
                  PyObject *item = PySequence_GetItem(pybox, p); /* new ref */
                  bool error = !PyVecTo(item, box[p]);
                  Py_DECREF(item);
                  if (error)
                        return NULL;
            }
            
            return PyInt_FromLong(BoxInsideFrustum(box)); /* new ref */
      }
      
      PyErr_SetString(PyExc_TypeError, "boxInsideFrustum: Expected argument: list of points.");
      return NULL;
}

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"
)
{
      MT_Point3 point;
      if (PyVecArgTo(args, point))
      {
            return PyInt_FromLong(PointInsideFrustum(point)); /* new ref */
      }
      
      PyErr_SetString(PyExc_TypeError, "pointInsideFrustum: Expected point argument.");
      return NULL;
}

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 PyObjectFrom(GetCameraToWorld()); /* new ref */
}

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 PyObjectFrom(GetWorldToCamera()); /* new ref */
}

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 PyObjectFrom(GetProjectionMatrix()); /* new ref */
}

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;
            if (PyMatTo(pymat, mat))
            {
                  SetProjectionMatrix(mat);
                  Py_Return;
            }
      }

      PyErr_SetString(PyExc_TypeError, "setProjectionMatrix: Expected 4x4 list as matrix argument.");
      return NULL;
}

Generated by  Doxygen 1.6.0   Back to index