2002-10-12 11:37:38 +00:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
2008-04-16 22:40:48 +00:00
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
|
|
|
* 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
|
2008-04-16 22:40:48 +00:00
|
|
|
* of the License, or (at your option) any later version.
|
2002-10-12 11:37:38 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2008-04-16 22:40:48 +00:00
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
2002-10-12 11:37:38 +00:00
|
|
|
* Camera in the gameengine. Cameras are also used for views.
|
|
|
|
*/
|
2004-05-16 12:53:22 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#include "KX_Camera.h"
|
2008-02-15 23:12:03 +00:00
|
|
|
#include "KX_Scene.h"
|
|
|
|
#include "KX_PythonInit.h"
|
2004-05-16 12:53:22 +00:00
|
|
|
#include "KX_Python.h"
|
|
|
|
#include "KX_PyMath.h"
|
2002-11-25 15:29:57 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
KX_Camera::KX_Camera(void* sgReplicationInfo,
|
|
|
|
SG_Callbacks callbacks,
|
2004-05-16 12:53:22 +00:00
|
|
|
const RAS_CameraData& camdata,
|
2004-05-21 09:18:42 +00:00
|
|
|
bool frustum_culling,
|
|
|
|
PyTypeObject *T)
|
2002-10-12 11:37:38 +00:00
|
|
|
:
|
2004-05-21 09:18:42 +00:00
|
|
|
KX_GameObject(sgReplicationInfo,callbacks,T),
|
2004-05-16 12:53:22 +00:00
|
|
|
m_camdata(camdata),
|
|
|
|
m_dirty(true),
|
2007-04-04 13:18:41 +00:00
|
|
|
m_normalized(false),
|
2004-07-20 12:07:06 +00:00
|
|
|
m_frustum_culling(frustum_culling && camdata.m_perspective),
|
2004-05-21 09:18:42 +00:00
|
|
|
m_set_projection_matrix(false),
|
2007-04-04 13:18:41 +00:00
|
|
|
m_set_frustum_center(false)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
|
|
|
// setting a name would be nice...
|
|
|
|
m_name = "cam";
|
2004-04-26 07:19:18 +00:00
|
|
|
m_projection_matrix.setIdentity();
|
|
|
|
m_modelview_matrix.setIdentity();
|
2008-03-01 19:46:50 +00:00
|
|
|
CValue* val = new CIntValue(1);
|
|
|
|
SetProperty("camera",val);
|
|
|
|
val->Release();
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KX_Camera::~KX_Camera()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-14 20:54:20 +00:00
|
|
|
CValue* KX_Camera::GetReplica()
|
|
|
|
{
|
|
|
|
KX_Camera* replica = new KX_Camera(*this);
|
|
|
|
|
|
|
|
// this will copy properties and so on...
|
|
|
|
CValue::AddDataToReplica(replica);
|
|
|
|
ProcessReplica(replica);
|
|
|
|
|
|
|
|
return replica;
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2008-04-14 20:54:20 +00:00
|
|
|
void KX_Camera::ProcessReplica(KX_Camera* replica)
|
|
|
|
{
|
|
|
|
KX_GameObject::ProcessReplica(replica);
|
|
|
|
}
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
MT_Transform KX_Camera::GetWorldToCamera() const
|
|
|
|
{
|
|
|
|
MT_Transform camtrans;
|
2004-07-20 12:07:06 +00:00
|
|
|
camtrans.invert(MT_Transform(NodeGetWorldPosition(), NodeGetWorldOrientation()));
|
2002-10-12 11:37:38 +00:00
|
|
|
|
|
|
|
return camtrans;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MT_Transform KX_Camera::GetCameraToWorld() const
|
|
|
|
{
|
2004-07-20 12:07:06 +00:00
|
|
|
return MT_Transform(NodeGetWorldPosition(), NodeGetWorldOrientation());
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void KX_Camera::CorrectLookUp(MT_Scalar speed)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-05-16 12:53:22 +00:00
|
|
|
const MT_Point3 KX_Camera::GetCameraLocation() const
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
|
|
|
/* this is the camera locatio in cam coords... */
|
|
|
|
//return m_trans1.getOrigin();
|
|
|
|
//return MT_Point3(0,0,0); <-----
|
|
|
|
/* .... I want it in world coords */
|
2004-03-22 22:02:18 +00:00
|
|
|
//MT_Transform trans;
|
|
|
|
//trans.setBasis(NodeGetWorldOrientation());
|
2002-10-12 11:37:38 +00:00
|
|
|
|
|
|
|
return NodeGetWorldPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* I want the camera orientation as well. */
|
2004-05-16 12:53:22 +00:00
|
|
|
const MT_Quaternion KX_Camera::GetCameraOrientation() const
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2004-07-20 12:07:06 +00:00
|
|
|
return NodeGetWorldOrientation().getRotation();
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the projection matrix that is used by the rasterizer.
|
|
|
|
*/
|
|
|
|
void KX_Camera::SetProjectionMatrix(const MT_Matrix4x4 & mat)
|
|
|
|
{
|
|
|
|
m_projection_matrix = mat;
|
2004-05-16 12:53:22 +00:00
|
|
|
m_dirty = true;
|
|
|
|
m_set_projection_matrix = true;
|
2007-04-04 13:18:41 +00:00
|
|
|
m_set_frustum_center = false;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the modelview matrix that is used by the rasterizer.
|
|
|
|
*/
|
|
|
|
void KX_Camera::SetModelviewMatrix(const MT_Matrix4x4 & mat)
|
|
|
|
{
|
|
|
|
m_modelview_matrix = mat;
|
2004-05-16 12:53:22 +00:00
|
|
|
m_dirty = true;
|
2007-04-04 13:18:41 +00:00
|
|
|
m_set_frustum_center = false;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the projection matrix that is used by the rasterizer.
|
|
|
|
*/
|
2004-05-16 12:53:22 +00:00
|
|
|
const MT_Matrix4x4& KX_Camera::GetProjectionMatrix() const
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2004-04-26 07:19:18 +00:00
|
|
|
return m_projection_matrix;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the modelview matrix that is used by the rasterizer.
|
|
|
|
*/
|
2004-05-16 12:53:22 +00:00
|
|
|
const MT_Matrix4x4& KX_Camera::GetModelviewMatrix() const
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2004-04-26 07:19:18 +00:00
|
|
|
return m_modelview_matrix;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-16 12:53:22 +00:00
|
|
|
bool KX_Camera::hasValidProjectionMatrix() const
|
|
|
|
{
|
|
|
|
return m_set_projection_matrix;
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2004-07-22 00:26:34 +00:00
|
|
|
void KX_Camera::InvalidateProjectionMatrix(bool valid)
|
|
|
|
{
|
|
|
|
m_set_projection_matrix = valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/*
|
|
|
|
* These getters retrieve the clip data and the focal length
|
|
|
|
*/
|
2004-05-16 12:53:22 +00:00
|
|
|
float KX_Camera::GetLens() const
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
|
|
|
return m_camdata.m_lens;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-05-16 12:53:22 +00:00
|
|
|
float KX_Camera::GetCameraNear() const
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
|
|
|
return m_camdata.m_clipstart;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-05-16 12:53:22 +00:00
|
|
|
float KX_Camera::GetCameraFar() const
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
|
|
|
return m_camdata.m_clipend;
|
|
|
|
}
|
|
|
|
|
2008-05-24 08:34:04 +00:00
|
|
|
float KX_Camera::GetFocalLength() const
|
|
|
|
{
|
|
|
|
return m_camdata.m_focallength;
|
|
|
|
}
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
RAS_CameraData* KX_Camera::GetCameraData()
|
|
|
|
{
|
|
|
|
return &m_camdata;
|
|
|
|
}
|
2004-05-16 12:53:22 +00:00
|
|
|
|
|
|
|
void KX_Camera::ExtractClipPlanes()
|
|
|
|
{
|
2004-05-21 09:18:42 +00:00
|
|
|
if (!m_dirty)
|
|
|
|
return;
|
|
|
|
|
2004-07-20 12:07:06 +00:00
|
|
|
MT_Matrix4x4 m = m_projection_matrix * m_modelview_matrix;
|
2004-05-16 12:53:22 +00:00
|
|
|
// 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;
|
2007-04-04 13:18:41 +00:00
|
|
|
m_normalized = false;
|
2004-05-21 09:18:42 +00:00
|
|
|
}
|
|
|
|
|
2007-04-04 13:18:41 +00:00
|
|
|
void KX_Camera::NormalizeClipPlanes()
|
2004-05-21 09:18:42 +00:00
|
|
|
{
|
2007-04-04 13:18:41 +00:00
|
|
|
if (m_normalized)
|
2004-05-21 09:18:42 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (unsigned int p = 0; p < 6; p++)
|
2004-05-30 11:04:26 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2004-05-21 09:18:42 +00:00
|
|
|
|
2007-04-04 13:18:41 +00:00
|
|
|
m_normalized = true;
|
2004-05-21 09:18:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void KX_Camera::ExtractFrustumSphere()
|
|
|
|
{
|
2007-04-04 13:18:41 +00:00
|
|
|
if (m_set_frustum_center)
|
2004-05-21 09:18:42 +00:00
|
|
|
return;
|
|
|
|
|
2007-04-04 13:18:41 +00:00
|
|
|
// The most extreme points on the near and far plane. (normalized device coords)
|
2004-05-21 09:18:42 +00:00
|
|
|
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.
|
2004-05-21 14:09:18 +00:00
|
|
|
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]);
|
2004-05-21 09:18:42 +00:00
|
|
|
|
2007-04-04 13:18:41 +00:00
|
|
|
// Compute center
|
|
|
|
m_frustum_center = MT_Point3(0., 0.,
|
2004-05-30 11:04:26 +00:00
|
|
|
(nearpoint.dot(nearpoint) - farpoint.dot(farpoint))/(2.0*(m_camdata.m_clipend - m_camdata.m_clipstart)));
|
2007-04-04 13:18:41 +00:00
|
|
|
m_frustum_radius = m_frustum_center.distance(farpoint);
|
2004-05-21 09:18:42 +00:00
|
|
|
|
|
|
|
// Transform to world space.
|
2007-04-04 13:18:41 +00:00
|
|
|
m_frustum_center = GetCameraToWorld()(m_frustum_center);
|
2004-05-30 11:04:26 +00:00
|
|
|
m_frustum_radius /= fabs(NodeGetWorldScaling()[NodeGetWorldScaling().closestAxis()]);
|
2004-05-21 09:18:42 +00:00
|
|
|
|
2007-04-04 13:18:41 +00:00
|
|
|
m_set_frustum_center = true;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool KX_Camera::PointInsideFrustum(const MT_Point3& x)
|
|
|
|
{
|
2004-05-21 09:18:42 +00:00
|
|
|
ExtractClipPlanes();
|
2004-05-16 12:53:22 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2004-05-21 09:18:42 +00:00
|
|
|
ExtractClipPlanes();
|
2004-05-16 12:53:22 +00:00
|
|
|
|
|
|
|
unsigned int insideCount = 0;
|
2004-05-21 09:18:42 +00:00
|
|
|
// 6 view frustum planes
|
2004-05-16 12:53:22 +00:00
|
|
|
for( unsigned int p = 0; p < 6 ; p++ )
|
|
|
|
{
|
|
|
|
unsigned int behindCount = 0;
|
2004-05-21 09:18:42 +00:00
|
|
|
// 8 box verticies.
|
2004-05-16 12:53:22 +00:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2004-05-21 09:18:42 +00:00
|
|
|
// 8 points behind this plane
|
2004-05-16 12:53:22 +00:00
|
|
|
if (behindCount == 8)
|
|
|
|
return OUTSIDE;
|
|
|
|
|
2004-05-21 09:18:42 +00:00
|
|
|
// Every box vertex is on the front side of this plane
|
2004-05-16 12:53:22 +00:00
|
|
|
if (!behindCount)
|
|
|
|
insideCount++;
|
|
|
|
}
|
|
|
|
|
2004-05-21 09:18:42 +00:00
|
|
|
// All box verticies are on the front side of all frustum planes.
|
2004-05-16 12:53:22 +00:00
|
|
|
if (insideCount == 6)
|
|
|
|
return INSIDE;
|
|
|
|
|
|
|
|
return INTERSECT;
|
|
|
|
}
|
|
|
|
|
2007-04-04 13:18:41 +00:00
|
|
|
int KX_Camera::SphereInsideFrustum(const MT_Point3& center, const MT_Scalar &radius)
|
2004-05-16 12:53:22 +00:00
|
|
|
{
|
2004-05-21 09:18:42 +00:00
|
|
|
ExtractFrustumSphere();
|
2007-04-04 13:18:41 +00:00
|
|
|
if (center.distance2(m_frustum_center) > (radius + m_frustum_radius)*(radius + m_frustum_radius))
|
2004-05-21 09:18:42 +00:00
|
|
|
return OUTSIDE;
|
|
|
|
|
|
|
|
unsigned int p;
|
|
|
|
ExtractClipPlanes();
|
2007-04-04 13:18:41 +00:00
|
|
|
NormalizeClipPlanes();
|
2004-05-21 09:18:42 +00:00
|
|
|
|
2004-05-16 12:53:22 +00:00
|
|
|
MT_Scalar distance;
|
2004-05-21 09:18:42 +00:00
|
|
|
int intersect = INSIDE;
|
|
|
|
// distance: <-------- OUTSIDE -----|----- INTERSECT -----0----- INTERSECT -----|----- INSIDE -------->
|
|
|
|
// -radius radius
|
|
|
|
for (p = 0; p < 6; p++)
|
2004-05-16 12:53:22 +00:00
|
|
|
{
|
2007-04-04 13:18:41 +00:00
|
|
|
distance = m_planes[p][0]*center[0] + m_planes[p][1]*center[1] + m_planes[p][2]*center[2] + m_planes[p][3];
|
2004-05-21 09:18:42 +00:00
|
|
|
if (fabs(distance) <= radius)
|
|
|
|
intersect = INTERSECT;
|
|
|
|
else if (distance < -radius)
|
2004-05-16 12:53:22 +00:00
|
|
|
return OUTSIDE;
|
|
|
|
}
|
2004-05-21 09:18:42 +00:00
|
|
|
|
|
|
|
return intersect;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool KX_Camera::GetFrustumCulling() const
|
|
|
|
{
|
|
|
|
return m_frustum_culling;
|
|
|
|
}
|
2006-01-06 03:46:54 +00:00
|
|
|
|
|
|
|
void KX_Camera::EnableViewport(bool viewport)
|
|
|
|
{
|
|
|
|
m_camdata.m_viewport = viewport;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KX_Camera::SetViewport(int left, int bottom, int right, int top)
|
|
|
|
{
|
|
|
|
m_camdata.m_viewportleft = left;
|
|
|
|
m_camdata.m_viewportbottom = bottom;
|
|
|
|
m_camdata.m_viewportright = right;
|
|
|
|
m_camdata.m_viewporttop = top;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KX_Camera::GetViewport() const
|
|
|
|
{
|
|
|
|
return m_camdata.m_viewport;
|
|
|
|
}
|
|
|
|
|
|
|
|
int KX_Camera::GetViewportLeft() const
|
|
|
|
{
|
|
|
|
return m_camdata.m_viewportleft;
|
|
|
|
}
|
|
|
|
|
|
|
|
int KX_Camera::GetViewportBottom() const
|
|
|
|
{
|
|
|
|
return m_camdata.m_viewportbottom;
|
|
|
|
}
|
|
|
|
|
|
|
|
int KX_Camera::GetViewportRight() const
|
|
|
|
{
|
|
|
|
return m_camdata.m_viewportright;
|
|
|
|
}
|
|
|
|
|
|
|
|
int KX_Camera::GetViewportTop() const
|
|
|
|
{
|
|
|
|
return m_camdata.m_viewporttop;
|
|
|
|
}
|
2004-05-16 12:53:22 +00:00
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//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),
|
2006-01-06 03:46:54 +00:00
|
|
|
KX_PYMETHODTABLE(KX_Camera, enableViewport),
|
|
|
|
KX_PYMETHODTABLE(KX_Camera, setViewport),
|
2008-02-15 23:12:03 +00:00
|
|
|
KX_PYMETHODTABLE(KX_Camera, setOnTop),
|
2004-05-16 12:53:22 +00:00
|
|
|
|
|
|
|
{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")
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyInt_FromLong(INSIDE); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "OUTSIDE")
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyInt_FromLong(OUTSIDE); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "INTERSECT")
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyInt_FromLong(INTERSECT); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
|
|
|
|
if (attr == "lens")
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyFloat_FromDouble(GetLens()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "near")
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyFloat_FromDouble(GetCameraNear()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "far")
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyFloat_FromDouble(GetCameraFar()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "frustum_culling")
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyInt_FromLong(m_frustum_culling); /* new ref */
|
2004-07-20 12:07:06 +00:00
|
|
|
if (attr == "perspective")
|
|
|
|
return PyInt_FromLong(m_camdata.m_perspective); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "projection_matrix")
|
2004-07-17 05:28:23 +00:00
|
|
|
return PyObjectFrom(GetProjectionMatrix()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "modelview_matrix")
|
2004-07-17 05:28:23 +00:00
|
|
|
return PyObjectFrom(GetModelviewMatrix()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "camera_to_world")
|
2004-07-17 05:28:23 +00:00
|
|
|
return PyObjectFrom(GetCameraToWorld()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
if (attr == "world_to_camera")
|
2004-07-17 05:28:23 +00:00
|
|
|
return PyObjectFrom(GetWorldToCamera()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
|
|
|
|
_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;
|
|
|
|
}
|
2004-07-20 12:07:06 +00:00
|
|
|
|
|
|
|
if (attr == "perspective")
|
|
|
|
{
|
|
|
|
m_camdata.m_perspective = PyInt_AsLong(pyvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-26 12:09:17 +00:00
|
|
|
if (PyObject_IsMT_Matrix(pyvalue, 4))
|
2004-05-16 12:53:22 +00:00
|
|
|
{
|
2004-05-26 12:09:17 +00:00
|
|
|
if (attr == "projection_matrix")
|
|
|
|
{
|
2004-07-17 05:28:23 +00:00
|
|
|
MT_Matrix4x4 mat;
|
|
|
|
if (PyMatTo(pyvalue, mat))
|
|
|
|
{
|
|
|
|
SetProjectionMatrix(mat);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
2004-05-26 12:09:17 +00:00
|
|
|
}
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
return KX_GameObject::_setattr(attr, pyvalue);
|
|
|
|
}
|
|
|
|
|
|
|
|
KX_PYMETHODDEF_DOC(KX_Camera, sphereInsideFrustum,
|
2007-04-04 13:18:41 +00:00
|
|
|
"sphereInsideFrustum(center, radius) -> Integer\n"
|
2004-05-16 12:53:22 +00:00
|
|
|
"\treturns INSIDE, OUTSIDE or INTERSECT if the given sphere is\n"
|
|
|
|
"\tinside/outside/intersects this camera's viewing frustum.\n\n"
|
2007-04-04 13:18:41 +00:00
|
|
|
"\tcenter = the center of the sphere (in world coordinates.)\n"
|
2004-05-16 12:53:22 +00:00
|
|
|
"\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"
|
|
|
|
)
|
|
|
|
{
|
2007-04-04 13:18:41 +00:00
|
|
|
PyObject *pycenter;
|
2004-05-16 12:53:22 +00:00
|
|
|
float radius;
|
2007-04-04 13:18:41 +00:00
|
|
|
if (PyArg_ParseTuple(args, "Of", &pycenter, &radius))
|
2004-05-16 12:53:22 +00:00
|
|
|
{
|
2007-04-04 13:18:41 +00:00
|
|
|
MT_Point3 center;
|
|
|
|
if (PyVecTo(pycenter, center))
|
2004-05-16 12:53:22 +00:00
|
|
|
{
|
2007-04-04 13:18:41 +00:00
|
|
|
return PyInt_FromLong(SphereInsideFrustum(center, radius)); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-04 13:18:41 +00:00
|
|
|
PyErr_SetString(PyExc_TypeError, "sphereInsideFrustum: Expected arguments: (center, radius)");
|
2004-05-16 12:53:22 +00:00
|
|
|
|
2008-07-01 16:43:46 +00:00
|
|
|
return NULL;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2004-06-07 11:03:12 +00:00
|
|
|
PyErr_Format(PyExc_TypeError, "boxInsideFrustum: Expected eight (8) points, got %d", num_points);
|
|
|
|
return NULL;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MT_Point3 box[8];
|
|
|
|
for (unsigned int p = 0; p < 8 ; p++)
|
|
|
|
{
|
2004-05-21 09:18:42 +00:00
|
|
|
PyObject *item = PySequence_GetItem(pybox, p); /* new ref */
|
2004-07-17 05:28:23 +00:00
|
|
|
bool error = !PyVecTo(item, box[p]);
|
2004-05-21 09:18:42 +00:00
|
|
|
Py_DECREF(item);
|
2004-07-17 05:28:23 +00:00
|
|
|
if (error)
|
2004-06-07 11:03:12 +00:00
|
|
|
return NULL;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyInt_FromLong(BoxInsideFrustum(box)); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
2004-06-07 11:03:12 +00:00
|
|
|
PyErr_SetString(PyExc_TypeError, "boxInsideFrustum: Expected argument: list of points.");
|
|
|
|
return NULL;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"
|
|
|
|
)
|
|
|
|
{
|
2004-07-17 05:28:23 +00:00
|
|
|
MT_Point3 point;
|
|
|
|
if (PyVecArgTo(args, point))
|
2004-05-16 12:53:22 +00:00
|
|
|
{
|
2004-05-21 09:18:42 +00:00
|
|
|
return PyInt_FromLong(PointInsideFrustum(point)); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
2004-06-07 11:03:12 +00:00
|
|
|
PyErr_SetString(PyExc_TypeError, "pointInsideFrustum: Expected point argument.");
|
|
|
|
return NULL;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"
|
|
|
|
)
|
|
|
|
{
|
2004-07-17 05:28:23 +00:00
|
|
|
return PyObjectFrom(GetCameraToWorld()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"
|
|
|
|
)
|
|
|
|
{
|
2004-07-17 05:28:23 +00:00
|
|
|
return PyObjectFrom(GetWorldToCamera()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"
|
|
|
|
)
|
|
|
|
{
|
2004-07-17 05:28:23 +00:00
|
|
|
return PyObjectFrom(GetProjectionMatrix()); /* new ref */
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
{
|
2004-07-17 05:28:23 +00:00
|
|
|
MT_Matrix4x4 mat;
|
|
|
|
if (PyMatTo(pymat, mat))
|
|
|
|
{
|
|
|
|
SetProjectionMatrix(mat);
|
|
|
|
Py_Return;
|
|
|
|
}
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
|
|
|
|
2004-06-07 11:03:12 +00:00
|
|
|
PyErr_SetString(PyExc_TypeError, "setProjectionMatrix: Expected 4x4 list as matrix argument.");
|
|
|
|
return NULL;
|
2004-05-16 12:53:22 +00:00
|
|
|
}
|
2006-01-06 03:46:54 +00:00
|
|
|
|
|
|
|
KX_PYMETHODDEF_DOC(KX_Camera, enableViewport,
|
|
|
|
"enableViewport(viewport)\n"
|
|
|
|
"Sets this camera's viewport status\n"
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int viewport;
|
|
|
|
if (PyArg_ParseTuple(args,"i",&viewport))
|
|
|
|
{
|
|
|
|
if(viewport)
|
|
|
|
EnableViewport(true);
|
|
|
|
else
|
|
|
|
EnableViewport(false);
|
|
|
|
}
|
2008-07-01 16:43:46 +00:00
|
|
|
else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-01-06 03:46:54 +00:00
|
|
|
Py_Return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KX_PYMETHODDEF_DOC(KX_Camera, setViewport,
|
|
|
|
"setViewport(left, bottom, right, top)\n"
|
|
|
|
"Sets this camera's viewport\n")
|
|
|
|
{
|
|
|
|
int left, bottom, right, top;
|
|
|
|
if (PyArg_ParseTuple(args,"iiii",&left, &bottom, &right, &top))
|
|
|
|
{
|
|
|
|
SetViewport(left, bottom, right, top);
|
2008-07-01 16:43:46 +00:00
|
|
|
} else {
|
|
|
|
return NULL;
|
2006-01-06 03:46:54 +00:00
|
|
|
}
|
|
|
|
Py_Return;
|
|
|
|
}
|
2008-02-15 23:12:03 +00:00
|
|
|
|
|
|
|
KX_PYMETHODDEF_DOC(KX_Camera, setOnTop,
|
|
|
|
"setOnTop()\n"
|
|
|
|
"Sets this camera's viewport on top\n")
|
|
|
|
{
|
|
|
|
class KX_Scene* scene;
|
|
|
|
|
2008-10-31 21:06:48 +00:00
|
|
|
scene = KX_GetActiveScene();
|
2008-02-15 23:12:03 +00:00
|
|
|
MT_assert(scene);
|
|
|
|
scene->SetCameraOnTop(this);
|
|
|
|
Py_Return;
|
|
|
|
}
|