Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/gameengine/Ketsji/KX_GameObject.cpp')
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp1414
1 files changed, 1146 insertions, 268 deletions
diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp
index 1cb64585ff8..a168beb9a70 100644
--- a/source/gameengine/Ketsji/KX_GameObject.cpp
+++ b/source/gameengine/Ketsji/KX_GameObject.cpp
@@ -51,6 +51,7 @@ typedef unsigned long uint_ptr;
#include "KX_GameObject.h"
#include "RAS_MeshObject.h"
#include "KX_MeshProxy.h"
+#include "KX_PolyProxy.h"
#include <stdio.h> // printf
#include "SG_Controller.h"
#include "KX_IPhysicsController.h"
@@ -58,8 +59,13 @@ typedef unsigned long uint_ptr;
#include "SG_Controller.h"
#include "KX_ClientObjectInfo.h"
#include "RAS_BucketManager.h"
-
+#include "KX_RayCast.h"
+#include "KX_PythonInit.h"
#include "KX_PyMath.h"
+#include "SCA_IActuator.h"
+#include "SCA_ISensor.h"
+
+#include "PyObjectPlus.h" /* python stuff */
// This file defines relationships between parents and children
// in the game engine.
@@ -73,36 +79,56 @@ KX_GameObject::KX_GameObject(
) :
SCA_IObject(T),
m_bDyna(false),
+ m_layer(0),
+ m_pBlenderObject(NULL),
+ m_pBlenderGroupObject(NULL),
m_bSuspendDynamics(false),
m_bUseObjectColor(false),
+ m_bIsNegativeScaling(false),
m_bVisible(true),
+ m_bCulled(true),
m_pPhysicsController1(NULL),
+ m_pPhysicsEnvironment(NULL),
+ m_xray(false),
+ m_pHitObject(NULL),
m_isDeformable(false)
{
m_ignore_activity_culling = false;
m_pClient_info = new KX_ClientObjectInfo(this, KX_ClientObjectInfo::ACTOR);
m_pSGNode = new SG_Node(this,sgReplicationInfo,callbacks);
-
+
// define the relationship between this node and it's parent.
KX_NormalParentRelation * parent_relation =
KX_NormalParentRelation::New();
m_pSGNode->SetParentRelation(parent_relation);
-
-
};
KX_GameObject::~KX_GameObject()
{
+ RemoveMeshes();
+
// is this delete somewhere ?
//if (m_sumoObj)
// delete m_sumoObj;
delete m_pClient_info;
//if (m_pSGNode)
// delete m_pSGNode;
-
+ if (m_pSGNode)
+ {
+ // must go through controllers and make sure they will not use us anymore
+ // This is important for KX_BulletPhysicsControllers that unregister themselves
+ // from the object when they are deleted.
+ SGControllerList::iterator contit;
+ SGControllerList& controllers = m_pSGNode->GetSGControllerList();
+ for (contit = controllers.begin();contit!=controllers.end();++contit)
+ {
+ (*contit)->ClearObject();
+ }
+ m_pSGNode->SetSGClientObject(NULL);
+ }
}
@@ -186,7 +212,73 @@ KX_GameObject* KX_GameObject::GetParent()
}
+void KX_GameObject::SetParent(KX_Scene *scene, KX_GameObject* obj)
+{
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (obj && GetSGNode() && obj->GetSGNode() && GetSGNode()->GetSGParent() != obj->GetSGNode())
+ {
+ // Make sure the objects have some scale
+ MT_Vector3 scale1 = NodeGetWorldScaling();
+ MT_Vector3 scale2 = obj->NodeGetWorldScaling();
+ if (fabs(scale2[0]) < FLT_EPSILON ||
+ fabs(scale2[1]) < FLT_EPSILON ||
+ fabs(scale2[2]) < FLT_EPSILON ||
+ fabs(scale1[0]) < FLT_EPSILON ||
+ fabs(scale1[1]) < FLT_EPSILON ||
+ fabs(scale1[2]) < FLT_EPSILON) { return; }
+
+ // Remove us from our old parent and set our new parent
+ RemoveParent(scene);
+ obj->GetSGNode()->AddChild(GetSGNode());
+
+ if (m_pPhysicsController1)
+ {
+ m_pPhysicsController1->SuspendDynamics(true);
+ }
+ // Set us to our new scale, position, and orientation
+ scale2[0] = 1.0/scale2[0];
+ scale2[1] = 1.0/scale2[1];
+ scale2[2] = 1.0/scale2[2];
+ scale1 = scale1 * scale2;
+ MT_Matrix3x3 invori = obj->NodeGetWorldOrientation().inverse();
+ MT_Vector3 newpos = invori*(NodeGetWorldPosition()-obj->NodeGetWorldPosition())*scale2;
+
+ NodeSetLocalScale(scale1);
+ NodeSetLocalPosition(MT_Point3(newpos[0],newpos[1],newpos[2]));
+ NodeSetLocalOrientation(invori*NodeGetWorldOrientation());
+ NodeUpdateGS(0.f,true);
+ // object will now be a child, it must be removed from the parent list
+ CListValue* rootlist = scene->GetRootParentList();
+ if (rootlist->RemoveValue(this))
+ // the object was in parent list, decrement ref count as it's now removed
+ Release();
+ }
+}
+
+void KX_GameObject::RemoveParent(KX_Scene *scene)
+{
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (GetSGNode() && GetSGNode()->GetSGParent())
+ {
+ // Set us to the right spot
+ GetSGNode()->SetLocalScale(GetSGNode()->GetWorldScaling());
+ GetSGNode()->SetLocalOrientation(GetSGNode()->GetWorldOrientation());
+ GetSGNode()->SetLocalPosition(GetSGNode()->GetWorldPosition());
+ // Remove us from our parent
+ GetSGNode()->DisconnectFromParent();
+ NodeUpdateGS(0.f,true);
+ // the object is now a root object, add it to the parentlist
+ CListValue* rootlist = scene->GetRootParentList();
+ if (!rootlist->SearchValue(this))
+ // object was not in root list, add it now and increment ref count
+ rootlist->Add(AddRef());
+ if (m_pPhysicsController1)
+ {
+ m_pPhysicsController1->RestoreDynamics();
+ }
+ }
+}
void KX_GameObject::ProcessReplica(KX_GameObject* replica)
{
@@ -194,6 +286,7 @@ void KX_GameObject::ProcessReplica(KX_GameObject* replica)
replica->m_pSGNode = NULL;
replica->m_pClient_info = new KX_ClientObjectInfo(*m_pClient_info);
replica->m_pClient_info->m_gameobject = replica;
+ replica->m_state = 0;
}
@@ -201,11 +294,11 @@ void KX_GameObject::ProcessReplica(KX_GameObject* replica)
CValue* KX_GameObject::GetReplica()
{
KX_GameObject* replica = new KX_GameObject(*this);
-
+
// this will copy properties and so on...
CValue::AddDataToReplica(replica);
ProcessReplica(replica);
-
+
return replica;
}
@@ -241,12 +334,12 @@ void KX_GameObject::ApplyMovement(const MT_Vector3& dloc,bool local)
void KX_GameObject::ApplyRotation(const MT_Vector3& drot,bool local)
{
MT_Matrix3x3 rotmat(drot);
- rotmat.transpose();
-
- if (m_pPhysicsController1) // (IsDynamic())
- m_pPhysicsController1->RelativeRotate(rotmat,local);
- // in worldspace
+
GetSGNode()->RelativeRotate(rotmat,local);
+
+ if (m_pPhysicsController1) { // (IsDynamic())
+ m_pPhysicsController1->RelativeRotate(rotmat,local);
+ }
}
@@ -264,31 +357,54 @@ double* KX_GameObject::GetOpenGLMatrix()
trans.setBasis(GetSGNode()->GetWorldOrientation());
MT_Vector3 scaling = GetSGNode()->GetWorldScaling();
-
+ m_bIsNegativeScaling = ((scaling[0] < 0.0) ^ (scaling[1] < 0.0) ^ (scaling[2] < 0.0)) ? true : false;
trans.scale(scaling[0], scaling[1], scaling[2]);
trans.getValue(fl);
return fl;
}
+void KX_GameObject::AddMeshUser()
+{
+ for (size_t i=0;i<m_meshes.size();i++)
+ m_meshes[i]->AddMeshUser(this);
+
+ UpdateBuckets(false);
+}
+
+static void UpdateBuckets_recursive(SG_Node* node)
+{
+ NodeList& children = node->GetSGChildren();
+ for (NodeList::iterator childit = children.begin();!(childit==children.end());++childit)
+ {
+ SG_Node* childnode = (*childit);
+ KX_GameObject *clientgameobj = static_cast<KX_GameObject*>( (*childit)->GetSGClientObject());
+ if (clientgameobj != NULL) // This is a GameObject
+ clientgameobj->UpdateBuckets(0);
+
+ // if the childobj is NULL then this may be an inverse parent link
+ // so a non recursive search should still look down this node.
+ UpdateBuckets_recursive(childnode);
+ }
+}
-void KX_GameObject::Bucketize()
+void KX_GameObject::UpdateBuckets( bool recursive )
{
double* fl = GetOpenGLMatrix();
for (size_t i=0;i<m_meshes.size();i++)
- m_meshes[i]->Bucketize(fl, this, m_bUseObjectColor, m_objectColor);
+ m_meshes[i]->UpdateBuckets(this, fl, m_bUseObjectColor, m_objectColor, m_bVisible, m_bCulled);
+
+ if (recursive) {
+ UpdateBuckets_recursive(m_pSGNode);
+ }
}
-
-
void KX_GameObject::RemoveMeshes()
{
- double* fl = GetOpenGLMatrix();
-
for (size_t i=0;i<m_meshes.size();i++)
- m_meshes[i]->RemoveFromBuckets(fl, this);
+ m_meshes[i]->RemoveFromBuckets(this);
//note: meshes can be shared, and are deleted by KX_BlenderSceneConverter
@@ -332,34 +448,25 @@ void KX_GameObject::ResetDebugColor()
SetDebugColor(0xff000000);
}
+void KX_GameObject::InitIPO(bool ipo_as_force,
+ bool ipo_add,
+ bool ipo_local)
+{
+ SGControllerList::iterator it = GetSGNode()->GetSGControllerList().begin();
+ while (it != GetSGNode()->GetSGControllerList().end()) {
+ (*it)->SetOption(SG_Controller::SG_CONTR_IPO_RESET, true);
+ (*it)->SetOption(SG_Controller::SG_CONTR_IPO_IPO_AS_FORCE, ipo_as_force);
+ (*it)->SetOption(SG_Controller::SG_CONTR_IPO_IPO_ADD, ipo_add);
+ (*it)->SetOption(SG_Controller::SG_CONTR_IPO_LOCAL, ipo_local);
+ it++;
+ }
+}
void KX_GameObject::UpdateIPO(float curframetime,
- bool recurse,
- bool ipo_as_force,
- bool force_local)
+ bool recurse)
{
-
- // The ipo-actuator needs a sumo reference... this is retrieved (unfortunately)
- // by the iposgcontr itself...
-// ipocontr->SetSumoReference(gameobj->GetSumoScene(),
-// gameobj->GetSumoObject());
-
-
- // The ipo has to be treated as a force, and not a displacement!
- // For this case, we send some settings to the controller. This
- // may need some caching...
- if (ipo_as_force) {
- SGControllerList::iterator it = GetSGNode()->GetSGControllerList().begin();
-
- while (it != GetSGNode()->GetSGControllerList().end()) {
- (*it)->SetOption(SG_Controller::SG_CONTR_IPO_IPO_AS_FORCE, ipo_as_force);
- (*it)->SetOption(SG_Controller::SG_CONTR_IPO_FORCES_ACT_LOCAL, force_local);
- it++;
- }
- }
-
- // The rest is the 'normal' update procedure.
+ // just the 'normal' update procedure.
GetSGNode()->SetSimulatedTime(curframetime,recurse);
GetSGNode()->UpdateWorldData(curframetime);
UpdateTransform();
@@ -368,6 +475,7 @@ void KX_GameObject::UpdateIPO(float curframetime,
// IPO update
void
KX_GameObject::UpdateMaterialData(
+ dword matname_hash,
MT_Vector4 rgba,
MT_Vector3 specrgb,
MT_Scalar hard,
@@ -380,15 +488,34 @@ KX_GameObject::UpdateMaterialData(
{
int mesh = 0;
if (((unsigned int)mesh < m_meshes.size()) && mesh >= 0) {
- RAS_MaterialBucket::Set::iterator mit = m_meshes[mesh]->GetFirstMaterial();
+ list<RAS_MeshMaterial>::iterator mit = m_meshes[mesh]->GetFirstMaterial();
+
for(; mit != m_meshes[mesh]->GetLastMaterial(); ++mit)
{
- RAS_IPolyMaterial* poly = (*mit)->GetPolyMaterial();
+ RAS_IPolyMaterial* poly = mit->m_bucket->GetPolyMaterial();
+
if(poly->GetFlag() & RAS_BLENDERMAT )
{
- SetObjectColor(rgba);
KX_BlenderMaterial *m = static_cast<KX_BlenderMaterial*>(poly);
- m->UpdateIPO(rgba, specrgb,hard,spec,ref,emit, alpha);
+
+ if (matname_hash == NULL)
+ {
+ m->UpdateIPO(rgba, specrgb,hard,spec,ref,emit, alpha);
+ // if mesh has only one material attached to it then use original hack with no need to edit vertices (better performance)
+ SetObjectColor(rgba);
+ }
+ else
+ {
+ if (matname_hash == poly->GetMaterialNameHash())
+ {
+ m->UpdateIPO(rgba, specrgb,hard,spec,ref,emit, alpha);
+ m_meshes[mesh]->SetVertexColor(poly,rgba);
+
+ // no break here, because one blender material can be split into several game engine materials
+ // (e.g. one uvsphere material is split into one material at poles with ras_mode TRIANGLE and one material for the body
+ // if here was a break then would miss some vertices if material was split
+ }
+ }
}
}
}
@@ -401,56 +528,75 @@ KX_GameObject::GetVisible(
return m_bVisible;
}
+static void setVisible_recursive(SG_Node* node, bool v)
+{
+ NodeList& children = node->GetSGChildren();
+
+ for (NodeList::iterator childit = children.begin();!(childit==children.end());++childit)
+ {
+ SG_Node* childnode = (*childit);
+ KX_GameObject *clientgameobj = static_cast<KX_GameObject*>( (*childit)->GetSGClientObject());
+ if (clientgameobj != NULL) // This is a GameObject
+ clientgameobj->SetVisible(v, 0);
+
+ // if the childobj is NULL then this may be an inverse parent link
+ // so a non recursive search should still look down this node.
+ setVisible_recursive(childnode, v);
+ }
+}
+
+
void
KX_GameObject::SetVisible(
- bool v
+ bool v,
+ bool recursive
)
{
m_bVisible = v;
+ if (recursive)
+ setVisible_recursive(m_pSGNode, v);
}
-// used by Python, and the actuatorshould _not_ be misused by the
-// scene!
-void
-KX_GameObject::MarkVisible(
- bool visible
+bool
+KX_GameObject::GetCulled(
+ void
)
{
- /* If explicit visibility settings are used, this is
- * determined on this level. Maybe change this to mesh level
- * later on? */
-
- double* fl = GetOpenGLMatrixPtr()->getPointer();
- for (size_t i=0;i<m_meshes.size();i++)
- {
- m_meshes[i]->MarkVisible(fl,this,visible,m_bUseObjectColor,m_objectColor);
- }
+ return m_bCulled;
}
+void
+KX_GameObject::SetCulled(
+ bool c
+ )
+{
+ m_bCulled = c;
+}
-// Always use the flag?
-void
-KX_GameObject::MarkVisible(
- void
+
+void
+KX_GameObject::SetLayer(
+ int l
)
{
- double* fl = GetOpenGLMatrixPtr()->getPointer();
- for (size_t i=0;i<m_meshes.size();i++)
- {
- m_meshes[i]->MarkVisible(fl,
- this,
- m_bVisible,
- m_bUseObjectColor,
- m_objectColor
- );
- }
+ m_layer = l;
}
+int
+KX_GameObject::GetLayer(
+ void
+ )
+{
+ return m_layer;
+}
void KX_GameObject::addLinearVelocity(const MT_Vector3& lin_vel,bool local)
{
- if (m_pPhysicsController1)
- m_pPhysicsController1->SetLinearVelocity(lin_vel + m_pPhysicsController1->GetLinearVelocity(),local);
+ if (m_pPhysicsController1)
+ {
+ MT_Vector3 lv = local ? NodeGetWorldOrientation() * lin_vel : lin_vel;
+ m_pPhysicsController1->SetLinearVelocity(lv + m_pPhysicsController1->GetLinearVelocity(), 0);
+ }
}
@@ -492,62 +638,205 @@ void KX_GameObject::SetObjectColor(const MT_Vector4& rgbavec)
m_objectColor = rgbavec;
}
+void KX_GameObject::AlignAxisToVect(const MT_Vector3& dir, int axis, float fac)
+{
+ MT_Matrix3x3 orimat;
+ MT_Vector3 vect,ori,z,x,y;
+ MT_Scalar len;
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (!GetSGNode())
+ return;
-MT_Vector3 KX_GameObject::GetLinearVelocity()
-{
- MT_Vector3 velocity(0.0,0.0,0.0);
+ vect = dir;
+ len = vect.length();
+ if (MT_fuzzyZero(len))
+ {
+ cout << "alignAxisToVect() Error: Null vector!\n";
+ return;
+ }
+
+ if (fac<=0.0) {
+ return;
+ }
+ // normalize
+ vect /= len;
+ orimat = GetSGNode()->GetWorldOrientation();
+ switch (axis)
+ {
+ case 0: //x axis
+ ori = MT_Vector3(orimat[0][2], orimat[1][2], orimat[2][2]); //pivot axis
+ if (MT_abs(vect.dot(ori)) > 1.0-3.0*MT_EPSILON) //is the vector paralell to the pivot?
+ ori = MT_Vector3(orimat[0][1], orimat[1][1], orimat[2][1]); //change the pivot!
+ if (fac == 1.0) {
+ x = vect;
+ } else {
+ x = (vect * fac) + ((orimat * MT_Vector3(1.0, 0.0, 0.0)) * (1-fac));
+ len = x.length();
+ if (MT_fuzzyZero(len)) x = vect;
+ else x /= len;
+ }
+ y = ori.cross(x);
+ z = x.cross(y);
+ break;
+ case 1: //y axis
+ ori = MT_Vector3(orimat[0][0], orimat[1][0], orimat[2][0]);
+ if (MT_abs(vect.dot(ori)) > 1.0-3.0*MT_EPSILON)
+ ori = MT_Vector3(orimat[0][2], orimat[1][2], orimat[2][2]);
+ if (fac == 1.0) {
+ y = vect;
+ } else {
+ y = (vect * fac) + ((orimat * MT_Vector3(0.0, 1.0, 0.0)) * (1-fac));
+ len = y.length();
+ if (MT_fuzzyZero(len)) y = vect;
+ else y /= len;
+ }
+ z = ori.cross(y);
+ x = y.cross(z);
+ break;
+ case 2: //z axis
+ ori = MT_Vector3(orimat[0][1], orimat[1][1], orimat[2][1]);
+ if (MT_abs(vect.dot(ori)) > 1.0-3.0*MT_EPSILON)
+ ori = MT_Vector3(orimat[0][0], orimat[1][0], orimat[2][0]);
+ if (fac == 1.0) {
+ z = vect;
+ } else {
+ z = (vect * fac) + ((orimat * MT_Vector3(0.0, 0.0, 1.0)) * (1-fac));
+ len = z.length();
+ if (MT_fuzzyZero(len)) z = vect;
+ else z /= len;
+ }
+ x = ori.cross(z);
+ y = z.cross(x);
+ break;
+ default: //wrong input?
+ cout << "alignAxisToVect(): Wrong axis '" << axis <<"'\n";
+ return;
+ }
+ x.normalize(); //normalize the vectors
+ y.normalize();
+ z.normalize();
+ orimat = MT_Matrix3x3( x[0],y[0],z[0],
+ x[1],y[1],z[1],
+ x[2],y[2],z[2]);
+ if (GetSGNode()->GetSGParent() != NULL)
+ {
+ // the object is a child, adapt its local orientation so that
+ // the global orientation is aligned as we want.
+ MT_Matrix3x3 invori = GetSGNode()->GetSGParent()->GetWorldOrientation().inverse();
+ NodeSetLocalOrientation(invori*orimat);
+ }
+ else
+ NodeSetLocalOrientation(orimat);
+}
+
+MT_Scalar KX_GameObject::GetMass()
+{
+ if (m_pPhysicsController1)
+ {
+ return m_pPhysicsController1->GetMass();
+ }
+ return 0.0;
+}
+
+MT_Vector3 KX_GameObject::GetLinearVelocity(bool local)
+{
+ MT_Vector3 velocity(0.0,0.0,0.0), locvel;
+ MT_Matrix3x3 ori;
if (m_pPhysicsController1)
{
velocity = m_pPhysicsController1->GetLinearVelocity();
+
+ if (local)
+ {
+ ori = GetSGNode()->GetWorldOrientation();
+
+ locvel = velocity * ori;
+ return locvel;
+ }
}
- return velocity;
-
+ return velocity;
+}
+
+MT_Vector3 KX_GameObject::GetAngularVelocity(bool local)
+{
+ MT_Vector3 velocity(0.0,0.0,0.0), locvel;
+ MT_Matrix3x3 ori;
+ if (m_pPhysicsController1)
+ {
+ velocity = m_pPhysicsController1->GetAngularVelocity();
+
+ if (local)
+ {
+ ori = GetSGNode()->GetWorldOrientation();
+
+ locvel = velocity * ori;
+ return locvel;
+ }
+ }
+ return velocity;
}
+MT_Vector3 KX_GameObject::GetVelocity(const MT_Point3& point)
+{
+ if (m_pPhysicsController1)
+ {
+ return m_pPhysicsController1->GetVelocity(point);
+ }
+ return MT_Vector3(0.0,0.0,0.0);
+}
// scenegraph node stuff
void KX_GameObject::NodeSetLocalPosition(const MT_Point3& trans)
{
- if (m_pPhysicsController1)
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (!GetSGNode())
+ return;
+
+ if (m_pPhysicsController1 && !GetSGNode()->GetSGParent())
{
+ // don't update physic controller if the object is a child:
+ // 1) the transformation will not be right
+ // 2) in this case, the physic controller is necessarily a static object
+ // that is updated from the normal kinematic synchronization
m_pPhysicsController1->setPosition(trans);
}
- if (GetSGNode())
- GetSGNode()->SetLocalPosition(trans);
+ GetSGNode()->SetLocalPosition(trans);
}
void KX_GameObject::NodeSetLocalOrientation(const MT_Matrix3x3& rot)
{
- if (m_pPhysicsController1)
- {
- m_pPhysicsController1->setOrientation(rot.getRotation());
- }
- if (GetSGNode())
- GetSGNode()->SetLocalOrientation(rot);
- else
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (!GetSGNode())
+ return;
+
+ if (m_pPhysicsController1 && !GetSGNode()->GetSGParent())
{
- int i;
- i=0;
+ // see note above
+ m_pPhysicsController1->setOrientation(rot);
}
+ GetSGNode()->SetLocalOrientation(rot);
}
void KX_GameObject::NodeSetLocalScale(const MT_Vector3& scale)
{
- if (m_pPhysicsController1)
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (!GetSGNode())
+ return;
+
+ if (m_pPhysicsController1 && !GetSGNode()->GetSGParent())
{
+ // see note above
m_pPhysicsController1->setScaling(scale);
}
-
- if (GetSGNode())
- GetSGNode()->SetLocalScale(scale);
+ GetSGNode()->SetLocalScale(scale);
}
@@ -555,9 +844,44 @@ void KX_GameObject::NodeSetLocalScale(const MT_Vector3& scale)
void KX_GameObject::NodeSetRelativeScale(const MT_Vector3& scale)
{
if (GetSGNode())
+ {
GetSGNode()->RelativeScale(scale);
+ if (m_pPhysicsController1 && (!GetSGNode()->GetSGParent()))
+ {
+ // see note above
+ // we can use the local scale: it's the same thing for a root object
+ // and the world scale is not yet updated
+ MT_Vector3 newscale = GetSGNode()->GetLocalScale();
+ m_pPhysicsController1->setScaling(newscale);
+ }
+ }
}
+void KX_GameObject::NodeSetWorldPosition(const MT_Point3& trans)
+{
+ SG_Node* parent = m_pSGNode->GetSGParent();
+ if (parent != NULL)
+ {
+ // Make sure the objects have some scale
+ MT_Vector3 scale = parent->GetWorldScaling();
+ if (fabs(scale[0]) < FLT_EPSILON ||
+ fabs(scale[1]) < FLT_EPSILON ||
+ fabs(scale[2]) < FLT_EPSILON)
+ {
+ return;
+ }
+ scale[0] = 1.0/scale[0];
+ scale[1] = 1.0/scale[1];
+ scale[2] = 1.0/scale[2];
+ MT_Matrix3x3 invori = parent->GetWorldOrientation().inverse();
+ MT_Vector3 newpos = invori*(trans-parent->GetWorldPosition())*scale;
+ NodeSetLocalPosition(MT_Point3(newpos[0],newpos[1],newpos[2]));
+ }
+ else
+ {
+ NodeSetLocalPosition(trans);
+ }
+}
void KX_GameObject::NodeUpdateGS(double time,bool bInitiator)
@@ -570,6 +894,13 @@ void KX_GameObject::NodeUpdateGS(double time,bool bInitiator)
const MT_Matrix3x3& KX_GameObject::NodeGetWorldOrientation() const
{
+ static MT_Matrix3x3 defaultOrientation = MT_Matrix3x3( 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 0.0, 1.0);
+
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (!GetSGNode())
+ return defaultOrientation;
return GetSGNode()->GetWorldOrientation();
}
@@ -577,6 +908,12 @@ const MT_Matrix3x3& KX_GameObject::NodeGetWorldOrientation() const
const MT_Vector3& KX_GameObject::NodeGetWorldScaling() const
{
+ static MT_Vector3 defaultScaling = MT_Vector3(1.0, 1.0, 1.0);
+
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (!GetSGNode())
+ return defaultScaling;
+
return GetSGNode()->GetWorldScaling();
}
@@ -584,6 +921,12 @@ const MT_Vector3& KX_GameObject::NodeGetWorldScaling() const
const MT_Point3& KX_GameObject::NodeGetWorldPosition() const
{
+ static MT_Point3 defaultPosition = MT_Point3(0.0, 0.0, 0.0);
+
+ // check on valid node in case a python controller holds a reference to a deleted object
+ if (!GetSGNode())
+ return defaultPosition;
+
return GetSGNode()->GetWorldPosition();
}
@@ -604,7 +947,7 @@ void KX_GameObject::Resume(void)
}
}
-void KX_GameObject::Suspend(void)
+void KX_GameObject::Suspend()
{
if ((!m_ignore_activity_culling)
&& (!m_suspended)) {
@@ -623,25 +966,44 @@ void KX_GameObject::Suspend(void)
PyMethodDef KX_GameObject::Methods[] = {
- {"setVisible",(PyCFunction) KX_GameObject::sPySetVisible, METH_VARARGS},
- {"setPosition", (PyCFunction) KX_GameObject::sPySetPosition, METH_VARARGS},
- {"getPosition", (PyCFunction) KX_GameObject::sPyGetPosition, METH_VARARGS},
- {"getOrientation", (PyCFunction) KX_GameObject::sPyGetOrientation, METH_VARARGS},
- {"setOrientation", (PyCFunction) KX_GameObject::sPySetOrientation, METH_VARARGS},
+ {"getPosition", (PyCFunction) KX_GameObject::sPyGetPosition, METH_NOARGS},
+ {"setPosition", (PyCFunction) KX_GameObject::sPySetPosition, METH_O},
+ {"setWorldPosition", (PyCFunction) KX_GameObject::sPySetWorldPosition, METH_O},
{"getLinearVelocity", (PyCFunction) KX_GameObject::sPyGetLinearVelocity, METH_VARARGS},
+ {"setLinearVelocity", (PyCFunction) KX_GameObject::sPySetLinearVelocity, METH_VARARGS},
+ {"getAngularVelocity", (PyCFunction) KX_GameObject::sPyGetAngularVelocity, METH_VARARGS},
+ {"setAngularVelocity", (PyCFunction) KX_GameObject::sPySetAngularVelocity, METH_VARARGS},
{"getVelocity", (PyCFunction) KX_GameObject::sPyGetVelocity, METH_VARARGS},
- {"getMass", (PyCFunction) KX_GameObject::sPyGetMass, METH_VARARGS},
- {"getReactionForce", (PyCFunction) KX_GameObject::sPyGetReactionForce, METH_VARARGS},
+ {"getMass", (PyCFunction) KX_GameObject::sPyGetMass, METH_NOARGS},
+ {"getReactionForce", (PyCFunction) KX_GameObject::sPyGetReactionForce, METH_NOARGS},
+ {"getOrientation", (PyCFunction) KX_GameObject::sPyGetOrientation, METH_NOARGS},
+ {"setOrientation", (PyCFunction) KX_GameObject::sPySetOrientation, METH_O},
+ {"getVisible",(PyCFunction) KX_GameObject::sPyGetVisible, METH_NOARGS},
+ {"setVisible",(PyCFunction) KX_GameObject::sPySetVisible, METH_VARARGS},
+ {"getState",(PyCFunction) KX_GameObject::sPyGetState, METH_NOARGS},
+ {"setState",(PyCFunction) KX_GameObject::sPySetState, METH_O},
+ {"alignAxisToVect",(PyCFunction) KX_GameObject::sPyAlignAxisToVect, METH_VARARGS},
+ {"getAxisVect",(PyCFunction) KX_GameObject::sPyGetAxisVect, METH_O},
+ {"suspendDynamics", (PyCFunction)KX_GameObject::sPySuspendDynamics,METH_NOARGS},
+ {"restoreDynamics", (PyCFunction)KX_GameObject::sPyRestoreDynamics,METH_NOARGS},
+ {"enableRigidBody", (PyCFunction)KX_GameObject::sPyEnableRigidBody,METH_NOARGS},
+ {"disableRigidBody", (PyCFunction)KX_GameObject::sPyDisableRigidBody,METH_NOARGS},
{"applyImpulse", (PyCFunction) KX_GameObject::sPyApplyImpulse, METH_VARARGS},
- {"setCollisionMargin", (PyCFunction) KX_GameObject::sPySetCollisionMargin, METH_VARARGS},
- {"suspendDynamics", (PyCFunction)KX_GameObject::sPySuspendDynamics,METH_VARARGS},
- {"restoreDynamics", (PyCFunction)KX_GameObject::sPyRestoreDynamics,METH_VARARGS},
- {"enableRigidBody", (PyCFunction)KX_GameObject::sPyEnableRigidBody,METH_VARARGS},
- {"disableRigidBody", (PyCFunction)KX_GameObject::sPyDisableRigidBody,METH_VARARGS},
- {"getParent", (PyCFunction)KX_GameObject::sPyGetParent,METH_VARARGS},
+ {"setCollisionMargin", (PyCFunction) KX_GameObject::sPySetCollisionMargin, METH_O},
+ {"getParent", (PyCFunction)KX_GameObject::sPyGetParent,METH_NOARGS},
+ {"setParent", (PyCFunction)KX_GameObject::sPySetParent,METH_O},
+ {"removeParent", (PyCFunction)KX_GameObject::sPyRemoveParent,METH_NOARGS},
+ {"getChildren", (PyCFunction)KX_GameObject::sPyGetChildren,METH_NOARGS},
+ {"getChildrenRecursive", (PyCFunction)KX_GameObject::sPyGetChildrenRecursive,METH_NOARGS},
{"getMesh", (PyCFunction)KX_GameObject::sPyGetMesh,METH_VARARGS},
- {"getPhysicsId", (PyCFunction)KX_GameObject::sPyGetPhysicsId,METH_VARARGS},
+ {"getPhysicsId", (PyCFunction)KX_GameObject::sPyGetPhysicsId,METH_NOARGS},
+ {"getPropertyNames", (PyCFunction)KX_GameObject::sPyGetPropertyNames,METH_NOARGS},
+ {"replaceMesh",(PyCFunction) KX_GameObject::sPyReplaceMesh, METH_O},
+ {"endObject",(PyCFunction) KX_GameObject::sPyEndObject, METH_NOARGS},
+ KX_PYMETHODTABLE(KX_GameObject, rayCastTo),
+ KX_PYMETHODTABLE(KX_GameObject, rayCast),
KX_PYMETHODTABLE(KX_GameObject, getDistanceTo),
+ KX_PYMETHODTABLE(KX_GameObject, getVectTo),
{NULL,NULL} //Sentinel
};
@@ -663,19 +1025,40 @@ bool KX_GameObject::ConvertPythonVectorArgs(PyObject* args,
}
*/
-
-PyObject* KX_GameObject::sPySetPosition(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyReplaceMesh(PyObject* self, PyObject* value)
{
- return ((KX_GameObject*) self)->PySetPosition(self, args, kwds);
+ KX_Scene *scene = KX_GetActiveScene();
+ char* meshname;
+ void* mesh_pt;
+
+ meshname = PyString_AsString(value);
+ if (meshname==NULL) {
+ PyErr_SetString(PyExc_ValueError, "Expected a mesh name");
+ return NULL;
+ }
+ mesh_pt = SCA_ILogicBrick::m_sCurrentLogicManager->GetMeshByName(STR_String(meshname));
+
+ if (mesh_pt==NULL) {
+ PyErr_SetString(PyExc_ValueError, "The mesh name given does not exist");
+ return NULL;
+ }
+ scene->ReplaceMesh(this, (class RAS_MeshObject*)mesh_pt);
+
+ Py_RETURN_NONE;
}
+
+PyObject* KX_GameObject::PyEndObject(PyObject* self)
+{
+
+ KX_Scene *scene = KX_GetActiveScene();
+ scene->DelayedRemoveObject(this);
+ Py_RETURN_NONE;
+
+}
-PyObject* KX_GameObject::PyGetPosition(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyGetPosition(PyObject* self)
{
return PyObjectFrom(NodeGetWorldPosition());
}
@@ -729,7 +1112,7 @@ PyObject* KX_GameObject::_getattr(const STR_String& attr)
parent->AddRef();
return parent;
}
- Py_Return;
+ Py_RETURN_NONE;
}
if (attr == "visible")
@@ -746,27 +1129,53 @@ PyObject* KX_GameObject::_getattr(const STR_String& attr)
if (attr == "name")
return PyString_FromString(m_name.ReadPtr());
+ if (attr == "timeOffset") {
+ if (m_pSGNode->GetSGParent()->IsSlowParent()) {
+ return PyFloat_FromDouble(static_cast<KX_SlowParentRelation *>(m_pSGNode->GetSGParent()->GetParentRelation())->GetTimeOffset());
+ } else {
+ return PyFloat_FromDouble(0.0);
+ }
+ }
+
_getattr_up(SCA_IObject);
}
int KX_GameObject::_setattr(const STR_String& attr, PyObject *value) // _setattr method
{
- if (attr == "mass")
+ if (attr == "mass") {
+ PyErr_SetString(PyExc_AttributeError, "attribute \"mass\" is read only");
return 1;
+ }
- if (attr == "parent")
+ if (attr == "parent") {
+ PyErr_SetString(PyExc_AttributeError, "attribute \"mass\" is read only\nUse setParent()");
return 1;
+ }
if (PyInt_Check(value))
{
int val = PyInt_AsLong(value);
if (attr == "visible")
{
- SetVisible(val != 0);
+ SetVisible(val != 0, false);
+ UpdateBuckets(false);
return 0;
}
}
+
+ if (PyFloat_Check(value))
+ {
+ MT_Scalar val = PyFloat_AsDouble(value);
+ if (attr == "timeOffset") {
+ if (m_pSGNode->GetSGParent() && m_pSGNode->GetSGParent()->IsSlowParent()) {
+ static_cast<KX_SlowParentRelation *>(m_pSGNode->GetSGParent()->GetParentRelation())->SetTimeOffset(val);
+ return 0;
+ } else {
+ return 0;
+ }
+ }
+ }
if (PySequence_Check(value))
{
@@ -778,6 +1187,7 @@ int KX_GameObject::_setattr(const STR_String& attr, PyObject *value) // _setattr
if (PyMatTo(value, rot))
{
NodeSetLocalOrientation(rot);
+ NodeUpdateGS(0.f,true);
return 0;
}
return 1;
@@ -790,6 +1200,7 @@ int KX_GameObject::_setattr(const STR_String& attr, PyObject *value) // _setattr
{
rot.setRotation(qrot);
NodeSetLocalOrientation(rot);
+ NodeUpdateGS(0.f,true);
return 0;
}
return 1;
@@ -802,11 +1213,12 @@ int KX_GameObject::_setattr(const STR_String& attr, PyObject *value) // _setattr
{
rot.setEuler(erot);
NodeSetLocalOrientation(rot);
+ NodeUpdateGS(0.f,true);
return 0;
}
return 1;
}
-
+ PyErr_SetString(PyExc_AttributeError, "could not set the orientation from a 3x3 matrix, quaternion or euler sequence");
return 1;
}
@@ -816,6 +1228,7 @@ int KX_GameObject::_setattr(const STR_String& attr, PyObject *value) // _setattr
if (PyVecTo(value, pos))
{
NodeSetLocalPosition(pos);
+ NodeUpdateGS(0.f,true);
return 0;
}
return 1;
@@ -827,6 +1240,7 @@ int KX_GameObject::_setattr(const STR_String& attr, PyObject *value) // _setattr
if (PyVecTo(value, scale))
{
NodeSetLocalScale(scale);
+ NodeUpdateGS(0.f,true);
return 0;
}
return 1;
@@ -842,44 +1256,117 @@ int KX_GameObject::_setattr(const STR_String& attr, PyObject *value) // _setattr
}
}
+ /* Need to have parent settable here too */
+
return SCA_IObject::_setattr(attr, value);
}
-PyObject* KX_GameObject::PyGetLinearVelocity(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyGetLinearVelocity(PyObject* self, PyObject* args)
{
// only can get the velocity if we have a physics object connected to us...
- return PyObjectFrom(GetLinearVelocity());
+ int local = 0;
+ if (PyArg_ParseTuple(args,"|i",&local))
+ {
+ return PyObjectFrom(GetLinearVelocity((local!=0)));
+ }
+ else
+ {
+ return NULL;
+ }
}
-
-
-PyObject* KX_GameObject::PySetVisible(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PySetLinearVelocity(PyObject* self, PyObject* args)
{
- int visible = 1;
+ int local = 0;
+ PyObject* pyvect;
- if (PyArg_ParseTuple(args,"i",&visible))
+ if (PyArg_ParseTuple(args,"O|i",&pyvect,&local)) {
+ MT_Vector3 velocity;
+ if (PyVecTo(pyvect, velocity)) {
+ setLinearVelocity(velocity, (local!=0));
+ Py_RETURN_NONE;
+ }
+ }
+ return NULL;
+}
+
+PyObject* KX_GameObject::PyGetAngularVelocity(PyObject* self, PyObject* args)
+{
+ // only can get the velocity if we have a physics object connected to us...
+ int local = 0;
+ if (PyArg_ParseTuple(args,"|i",&local))
{
- MarkVisible(visible!=0);
- m_bVisible = (visible!=0);
+ return PyObjectFrom(GetAngularVelocity((local!=0)));
}
else
{
- return NULL;
+ return NULL;
}
- Py_Return;
+}
+
+PyObject* KX_GameObject::PySetAngularVelocity(PyObject* self, PyObject* args)
+{
+ int local = 0;
+ PyObject* pyvect;
+ if (PyArg_ParseTuple(args,"O|i",&pyvect,&local)) {
+ MT_Vector3 velocity;
+ if (PyVecTo(pyvect, velocity)) {
+ setAngularVelocity(velocity, (local!=0));
+ Py_RETURN_NONE;
+ }
+ }
+ return NULL;
+}
+
+PyObject* KX_GameObject::PySetVisible(PyObject* self, PyObject* args)
+{
+ int visible, recursive = 0;
+ if (!PyArg_ParseTuple(args,"i|i",&visible, &recursive))
+ return NULL;
+
+ SetVisible(visible ? true:false, recursive ? true:false);
+ UpdateBuckets(recursive ? true:false);
+ Py_RETURN_NONE;
+
+}
+
+PyObject* KX_GameObject::PyGetVisible(PyObject* self)
+{
+ return PyInt_FromLong(m_bVisible);
+}
+
+PyObject* KX_GameObject::PyGetState(PyObject* self)
+{
+ int state = 0;
+ state |= GetState();
+ return PyInt_FromLong(state);
+}
+
+PyObject* KX_GameObject::PySetState(PyObject* self, PyObject* value)
+{
+ int state_i = PyInt_AsLong(value);
+ unsigned int state = 0;
+
+ if (state_i == -1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError, "expected an int bit field");
+ return NULL;
+ }
+
+ state |= state_i;
+ if ((state & ((1<<30)-1)) == 0) {
+ PyErr_SetString(PyExc_AttributeError, "The state bitfield was not between 0 and 30 (1<<0 and 1<<29)");
+ return NULL;
+ }
+ SetState(state);
+
+ Py_RETURN_NONE;
}
-PyObject* KX_GameObject::PyGetVelocity(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyGetVelocity(PyObject* self, PyObject* args)
{
// only can get the velocity if we have a physics object connected to us...
MT_Vector3 velocity(0.0,0.0,0.0);
@@ -892,6 +1379,9 @@ PyObject* KX_GameObject::PyGetVelocity(PyObject* self,
if (pypos)
PyVecTo(pypos, point);
}
+ else {
+ return NULL;
+ }
if (m_pPhysicsController1)
{
@@ -903,26 +1393,14 @@ PyObject* KX_GameObject::PyGetVelocity(PyObject* self,
-PyObject* KX_GameObject::PyGetMass(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyGetMass(PyObject* self)
{
- PyObject* pymass = NULL;
-
- float mass = GetPhysicsController()->GetMass();
- pymass = PyFloat_FromDouble(mass);
-
- if (pymass)
- return pymass;
-
- Py_Return;
+ return PyFloat_FromDouble(GetPhysicsController()->GetMass());
}
-PyObject* KX_GameObject::PyGetReactionForce(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyGetReactionForce(PyObject* self)
{
// only can get the velocity if we have a physics object connected to us...
return PyObjectFrom(GetPhysicsController()->getReactionForce());
@@ -930,32 +1408,25 @@ PyObject* KX_GameObject::PyGetReactionForce(PyObject* self,
-PyObject* KX_GameObject::PyEnableRigidBody(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyEnableRigidBody(PyObject* self)
{
-
GetPhysicsController()->setRigidBody(true);
- Py_Return;
+ Py_RETURN_NONE;
}
-PyObject* KX_GameObject::PyDisableRigidBody(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyDisableRigidBody(PyObject* self)
{
GetPhysicsController()->setRigidBody(false);
- Py_Return;
+ Py_RETURN_NONE;
}
-PyObject* KX_GameObject::PyGetParent(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyGetParent(PyObject* self)
{
KX_GameObject* parent = this->GetParent();
if (parent)
@@ -963,69 +1434,128 @@ PyObject* KX_GameObject::PyGetParent(PyObject* self,
parent->AddRef();
return parent;
}
- Py_Return;
+ Py_RETURN_NONE;
+}
+
+PyObject* KX_GameObject::PySetParent(PyObject* self, PyObject* value)
+{
+ if (!PyObject_TypeCheck(value, &KX_GameObject::Type)) {
+ PyErr_SetString(PyExc_TypeError, "expected a KX_GameObject type");
+ return NULL;
+ }
+
+ // The object we want to set as parent
+ CValue *m_ob = (CValue*)value;
+ KX_GameObject *obj = ((KX_GameObject*)m_ob);
+ KX_Scene *scene = KX_GetActiveScene();
+
+ this->SetParent(scene, obj);
+
+ Py_RETURN_NONE;
}
+PyObject* KX_GameObject::PyRemoveParent(PyObject* self)
+{
+ KX_Scene *scene = KX_GetActiveScene();
+ this->RemoveParent(scene);
+ Py_RETURN_NONE;
+}
-PyObject* KX_GameObject::PyGetMesh(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+static void walk_children(SG_Node* node, CListValue* list, bool recursive)
{
- int mesh = 0;
+ NodeList& children = node->GetSGChildren();
- if (PyArg_ParseTuple(args, "|i", &mesh))
+ for (NodeList::iterator childit = children.begin();!(childit==children.end());++childit)
{
- if (((unsigned int)mesh < m_meshes.size()) && mesh >= 0)
+ SG_Node* childnode = (*childit);
+ CValue* childobj = (CValue*)childnode->GetSGClientObject();
+ if (childobj != NULL) // This is a GameObject
{
- KX_MeshProxy* meshproxy = new KX_MeshProxy(m_meshes[mesh]);
- return meshproxy;
+ // add to the list
+ list->Add(childobj->AddRef());
+ }
+
+ // if the childobj is NULL then this may be an inverse parent link
+ // so a non recursive search should still look down this node.
+ if (recursive || childobj==NULL) {
+ walk_children(childnode, list, recursive);
}
}
- Py_Return;
}
+PyObject* KX_GameObject::PyGetChildren(PyObject* self)
+{
+ CListValue* list = new CListValue();
+ walk_children(m_pSGNode, list, 0);
+ return list;
+}
+PyObject* KX_GameObject::PyGetChildrenRecursive(PyObject* self)
+{
+ CListValue* list = new CListValue();
+ walk_children(m_pSGNode, list, 1);
+ return list;
+}
-
-
-PyObject* KX_GameObject::PySetCollisionMargin(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyGetMesh(PyObject* self, PyObject* args)
{
- float collisionMargin;
- if (PyArg_ParseTuple(args, "f", &collisionMargin))
+ int mesh = 0;
+
+ if (!PyArg_ParseTuple(args, "|i", &mesh))
+ return NULL; // python sets a simple error
+
+ if (((unsigned int)mesh < m_meshes.size()) && mesh >= 0)
{
- if (m_pPhysicsController1)
- {
- m_pPhysicsController1->setMargin(collisionMargin);
- Py_Return;
- }
+ KX_MeshProxy* meshproxy = new KX_MeshProxy(m_meshes[mesh]);
+ return meshproxy;
+ }
+
+ Py_RETURN_NONE;
+}
+
+
+
+
+PyObject* KX_GameObject::PySetCollisionMargin(PyObject* self, PyObject* value)
+{
+ float collisionMargin = PyFloat_AsDouble(value);
+
+ if (collisionMargin==-1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError, "expected a float");
+ return NULL;
}
+ if (m_pPhysicsController1)
+ {
+ m_pPhysicsController1->setMargin(collisionMargin);
+ Py_RETURN_NONE;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "This object has no physics controller");
return NULL;
}
-PyObject* KX_GameObject::PyApplyImpulse(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyApplyImpulse(PyObject* self, PyObject* args)
{
PyObject* pyattach;
PyObject* pyimpulse;
+
+ if (!m_pPhysicsController1) {
+ PyErr_SetString(PyExc_RuntimeError, "This object has no physics controller");
+ return NULL;
+ }
+
if (PyArg_ParseTuple(args, "OO", &pyattach, &pyimpulse))
{
MT_Point3 attach;
MT_Vector3 impulse;
- if (m_pPhysicsController1)
+ if (PyVecTo(pyattach, attach) && PyVecTo(pyimpulse, impulse))
{
- if (PyVecTo(pyattach, attach) && PyVecTo(pyimpulse, impulse))
- {
- m_pPhysicsController1->applyImpulse(attach, impulse);
- Py_Return;
- }
+ m_pPhysicsController1->applyImpulse(attach, impulse);
+ Py_RETURN_NONE;
}
}
@@ -1035,102 +1565,109 @@ PyObject* KX_GameObject::PyApplyImpulse(PyObject* self,
-PyObject* KX_GameObject::PySuspendDynamics(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PySuspendDynamics(PyObject* self)
{
- if (m_bSuspendDynamics)
- {
- Py_Return;
- }
-
- if (m_pPhysicsController1)
- {
- m_pPhysicsController1->SuspendDynamics();
- }
- m_bSuspendDynamics = true;
-
- Py_Return;
+ SuspendDynamics();
+ Py_RETURN_NONE;
}
-PyObject* KX_GameObject::PyRestoreDynamics(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PyRestoreDynamics(PyObject* self)
{
-
- if (!m_bSuspendDynamics)
- {
- Py_Return;
- }
-
- if (m_pPhysicsController1)
- {
- m_pPhysicsController1->RestoreDynamics();
- }
- m_bSuspendDynamics = false;
-
- Py_Return;
+ RestoreDynamics();
+ Py_RETURN_NONE;
}
-PyObject* KX_GameObject::PyGetOrientation(PyObject* self,
- PyObject* args,
- PyObject* kwds) //keywords
+PyObject* KX_GameObject::PyGetOrientation(PyObject* self) //keywords
{
return PyObjectFrom(NodeGetWorldOrientation());
}
-PyObject* KX_GameObject::PySetOrientation(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PySetOrientation(PyObject* self, PyObject* value)
{
- PyObject* pylist;
-
- if (PyArg_ParseTuple(args,"O",&pylist))
+ MT_Matrix3x3 matrix;
+ if (PyObject_IsMT_Matrix(value, 3) && PyMatTo(value, matrix))
{
- MT_Matrix3x3 matrix;
- if (PyObject_IsMT_Matrix(pylist, 3) && PyMatTo(pylist, matrix))
- {
- NodeSetLocalOrientation(matrix);
- Py_Return;
- }
+ NodeSetLocalOrientation(matrix);
+ NodeUpdateGS(0.f,true);
+ Py_RETURN_NONE;
+ }
+
+ MT_Quaternion quat;
+ if (PyVecTo(value, quat))
+ {
+ matrix.setRotation(quat);
+ NodeSetLocalOrientation(matrix);
+ NodeUpdateGS(0.f,true);
+ Py_RETURN_NONE;
+ }
+ return NULL;
+}
+
+PyObject* KX_GameObject::PyAlignAxisToVect(PyObject* self, PyObject* args)
+{
+ PyObject* pyvect;
+ int axis = 2; //z axis is the default
+ float fac = 1.0;
- MT_Quaternion quat;
- if (PyVecTo(pylist, quat))
+ if (PyArg_ParseTuple(args,"O|if",&pyvect,&axis, &fac))
+ {
+ MT_Vector3 vect;
+ if (PyVecTo(pyvect, vect))
{
- matrix.setRotation(quat);
- NodeSetLocalOrientation(matrix);
- Py_Return;
+ if (fac<=0.0) Py_RETURN_NONE; // Nothing to do.
+ if (fac> 1.0) fac= 1.0;
+
+ AlignAxisToVect(vect,axis,fac);
+ NodeUpdateGS(0.f,true);
+ Py_RETURN_NONE;
}
}
return NULL;
}
+PyObject* KX_GameObject::PyGetAxisVect(PyObject* self, PyObject* value)
+{
+ MT_Vector3 vect;
+ if (PyVecTo(value, vect))
+ {
+ return PyObjectFrom(NodeGetWorldOrientation() * vect);
+ }
+ return NULL;
+}
-
-PyObject* KX_GameObject::PySetPosition(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PySetPosition(PyObject* self, PyObject* value)
{
MT_Point3 pos;
- if (PyVecArgTo(args, pos))
+ if (PyVecTo(value, pos))
{
NodeSetLocalPosition(pos);
NodeUpdateGS(0.f,true);
- Py_Return;
+ Py_RETURN_NONE;
}
-
+
return NULL;
}
-PyObject* KX_GameObject::PyGetPhysicsId(PyObject* self,
- PyObject* args,
- PyObject* kwds)
+PyObject* KX_GameObject::PySetWorldPosition(PyObject* self, PyObject* value)
+{
+ MT_Point3 pos;
+ if (PyVecTo(value, pos))
+ {
+ NodeSetWorldPosition(pos);
+ NodeUpdateGS(0.f,true);
+ Py_RETURN_NONE;
+ }
+
+ return NULL;
+}
+
+PyObject* KX_GameObject::PyGetPhysicsId(PyObject* self)
{
KX_IPhysicsController* ctrl = GetPhysicsController();
uint_ptr physid=0;
@@ -1141,6 +1678,11 @@ PyObject* KX_GameObject::PyGetPhysicsId(PyObject* self,
return PyInt_FromLong((long)physid);
}
+PyObject* KX_GameObject::PyGetPropertyNames(PyObject* self)
+{
+ return ConvertKeysToPython();
+}
+
KX_PYMETHODDEF_DOC(KX_GameObject, getDistanceTo,
"getDistanceTo(other): get distance to another point/KX_GameObject")
{
@@ -1152,20 +1694,356 @@ KX_PYMETHODDEF_DOC(KX_GameObject, getDistanceTo,
PyErr_Clear();
PyObject *pyother;
- if (PyArg_ParseTuple(args, "O!", &KX_GameObject::Type, &pyother))
+ KX_GameObject *other;
+ if (PyArg_ParseTuple(args, "O", &pyother) && ConvertPythonToGameObject(pyother, &other, false))
{
- KX_GameObject *other = static_cast<KX_GameObject*>(pyother);
return PyFloat_FromDouble(NodeGetWorldPosition().distance(other->NodeGetWorldPosition()));
}
return NULL;
}
+KX_PYMETHODDEF_DOC(KX_GameObject, getVectTo,
+"getVectTo(other): get vector and the distance to another point/KX_GameObject\n"
+"Returns a 3-tuple with (distance,worldVector,localVector)\n")
+{
+ MT_Point3 toPoint, fromPoint;
+ MT_Vector3 toDir, locToDir;
+ MT_Scalar distance;
+
+ PyObject *returnValue;
+ PyObject *pyother;
+
+ if (!PyVecArgTo(args, toPoint))
+ {
+ PyErr_Clear();
+
+ KX_GameObject *other;
+ if (PyArg_ParseTuple(args, "O", &pyother) && ConvertPythonToGameObject(pyother, &other, false))
+ {
+ toPoint = other->NodeGetWorldPosition();
+ } else
+ {
+ PyErr_SetString(PyExc_TypeError, "Expected a 3D Vector or GameObject type");
+ return NULL;
+ }
+ }
+
+ fromPoint = NodeGetWorldPosition();
+ toDir = toPoint-fromPoint;
+ distance = toDir.length();
+
+ if (MT_fuzzyZero(distance))
+ {
+ //cout << "getVectTo() Error: Null vector!\n";
+ locToDir = toDir = MT_Vector3(0.0,0.0,0.0);
+ distance = 0.0;
+ } else {
+ toDir.normalize();
+ locToDir = toDir * NodeGetWorldOrientation();
+ }
+
+ returnValue = PyTuple_New(3);
+ if (returnValue) { // very unlikely to fail, python sets a memory error here.
+ PyTuple_SET_ITEM(returnValue, 0, PyFloat_FromDouble(distance));
+ PyTuple_SET_ITEM(returnValue, 1, PyObjectFrom(toDir));
+ PyTuple_SET_ITEM(returnValue, 2, PyObjectFrom(locToDir));
+ }
+ return returnValue;
+}
+
+bool KX_GameObject::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
+{
+ KX_GameObject* hitKXObj = client->m_gameobject;
+
+ // if X-ray option is selected, the unwnted objects were not tested, so get here only with true hit
+ // if not, all objects were tested and the front one may not be the correct one.
+ if (m_xray || m_testPropName.Length() == 0 || hitKXObj->GetProperty(m_testPropName) != NULL)
+ {
+ m_pHitObject = hitKXObj;
+ return true;
+ }
+ // return true to stop RayCast::RayTest from looping, the above test was decisive
+ // We would want to loop only if we want to get more than one hit point
+ return true;
+}
+
+/* this function is used to pre-filter the object before casting the ray on them.
+ This is useful for "X-Ray" option when we want to see "through" unwanted object.
+ */
+bool KX_GameObject::NeedRayCast(KX_ClientObjectInfo* client)
+{
+ KX_GameObject* hitKXObj = client->m_gameobject;
+
+ if (client->m_type > KX_ClientObjectInfo::ACTOR)
+ {
+ // Unknown type of object, skip it.
+ // Should not occur as the sensor objects are filtered in RayTest()
+ printf("Invalid client type %d found in ray casting\n", client->m_type);
+ return false;
+ }
+
+ // if X-Ray option is selected, skip object that don't match the criteria as we see through them
+ // if not, test all objects because we don't know yet which one will be on front
+ if (!m_xray || m_testPropName.Length() == 0 || hitKXObj->GetProperty(m_testPropName) != NULL)
+ {
+ return true;
+ }
+ // skip the object
+ return false;
+}
+
+KX_PYMETHODDEF_DOC(KX_GameObject, rayCastTo,
+"rayCastTo(other,dist,prop): look towards another point/KX_GameObject and return first object hit within dist that matches prop\n"
+" prop = property name that object must have; can be omitted => detect any object\n"
+" dist = max distance to look (can be negative => look behind); 0 or omitted => detect up to other\n"
+" other = 3-tuple or object reference")
+{
+ MT_Point3 toPoint;
+ PyObject* pyarg;
+ float dist = 0.0f;
+ char *propName = NULL;
+
+ if (!PyArg_ParseTuple(args,"O|fs", &pyarg, &dist, &propName)) {
+ return NULL; // python sets simple error
+ }
+
+ if (!PyVecTo(pyarg, toPoint))
+ {
+ KX_GameObject *other;
+ PyErr_Clear();
+
+ if (ConvertPythonToGameObject(pyarg, &other, false))
+ {
+ toPoint = other->NodeGetWorldPosition();
+ } else
+ {
+ PyErr_SetString(PyExc_TypeError, "the first argument to rayCastTo must be a vector or a KX_GameObject");
+ return NULL;
+ }
+ }
+ MT_Point3 fromPoint = NodeGetWorldPosition();
+ if (dist != 0.0f)
+ {
+ MT_Vector3 toDir = toPoint-fromPoint;
+ toDir.normalize();
+ toPoint = fromPoint + (dist) * toDir;
+ }
+
+ PHY_IPhysicsEnvironment* pe = GetPhysicsEnvironment();
+ KX_IPhysicsController *spc = GetPhysicsController();
+ KX_GameObject *parent = GetParent();
+ if (!spc && parent)
+ spc = parent->GetPhysicsController();
+ if (parent)
+ parent->Release();
+
+ m_pHitObject = NULL;
+ if (propName)
+ m_testPropName = propName;
+ else
+ m_testPropName.SetLength(0);
+ KX_RayCast::Callback<KX_GameObject> callback(this,spc);
+ KX_RayCast::RayTest(pe, fromPoint, toPoint, callback);
+
+ if (m_pHitObject)
+ {
+ m_pHitObject->AddRef();
+ return m_pHitObject;
+ }
+ Py_RETURN_NONE;
+}
+
+KX_PYMETHODDEF_DOC(KX_GameObject, rayCast,
+ "rayCast(to,from,dist,prop,face,xray,poly): cast a ray and return 3-tuple (object,hit,normal) or 4-tuple (object,hit,normal,polygon) of contact point with object within dist that matches prop.\n"
+ " If no hit, return (None,None,None) or (None,None,None,None).\n"
+" to = 3-tuple or object reference for destination of ray (if object, use center of object)\n"
+" from = 3-tuple or object reference for origin of ray (if object, use center of object)\n"
+" Can be None or omitted => start from self object center\n"
+" dist = max distance to look (can be negative => look behind); 0 or omitted => detect up to to\n"
+" prop = property name that object must have; can be omitted => detect any object\n"
+" face = normal option: 1=>return face normal; 0 or omitted => normal is oriented towards origin\n"
+" xray = X-ray option: 1=>skip objects that don't match prop; 0 or omitted => stop on first object\n"
+" poly = polygon option: 1=>return value is a 4-tuple and the 4th element is a KX_PolyProxy object\n"
+" which can be None if hit object has no mesh or if there is no hit\n"
+" If 0 or omitted, return value is a 3-tuple\n"
+"Note: The object on which you call this method matters: the ray will ignore it.\n"
+" prop and xray option interact as follow:\n"
+" prop off, xray off: return closest hit or no hit if there is no object on the full extend of the ray\n"
+" prop off, xray on : idem\n"
+" prop on, xray off: return closest hit if it matches prop, no hit otherwise\n"
+" prop on, xray on : return closest hit matching prop or no hit if there is no object matching prop on the full extend of the ray\n")
+{
+ MT_Point3 toPoint;
+ MT_Point3 fromPoint;
+ PyObject* pyto;
+ PyObject* pyfrom = NULL;
+ float dist = 0.0f;
+ char *propName = NULL;
+ KX_GameObject *other;
+ int face=0, xray=0, poly=0;
+
+ if (!PyArg_ParseTuple(args,"O|Ofsiii", &pyto, &pyfrom, &dist, &propName, &face, &xray, &poly)) {
+ return NULL; // Python sets a simple error
+ }
+
+ if (!PyVecTo(pyto, toPoint))
+ {
+ PyErr_Clear();
+
+ if (ConvertPythonToGameObject(pyto, &other, false))
+ {
+ toPoint = other->NodeGetWorldPosition();
+ } else
+ {
+ PyErr_SetString(PyExc_TypeError, "the first argument to rayCast must be a vector or a KX_GameObject");
+ return NULL;
+ }
+ }
+ if (!pyfrom || pyfrom == Py_None)
+ {
+ fromPoint = NodeGetWorldPosition();
+ }
+ else if (!PyVecTo(pyfrom, fromPoint))
+ {
+ PyErr_Clear();
+
+ if (ConvertPythonToGameObject(pyfrom, &other, false))
+ {
+ fromPoint = other->NodeGetWorldPosition();
+ } else
+ {
+ PyErr_SetString(PyExc_TypeError, "the second optional argument to rayCast must be a vector or a KX_GameObject");
+ return NULL;
+ }
+ }
+
+ if (dist != 0.0f) {
+ MT_Vector3 toDir = toPoint-fromPoint;
+ if (MT_fuzzyZero(toDir.length2())) {
+ return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
+ }
+ toDir.normalize();
+ toPoint = fromPoint + (dist) * toDir;
+ } else if (MT_fuzzyZero((toPoint-fromPoint).length2())) {
+ return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
+ }
+
+ PHY_IPhysicsEnvironment* pe = GetPhysicsEnvironment();
+ KX_IPhysicsController *spc = GetPhysicsController();
+ KX_GameObject *parent = GetParent();
+ if (!spc && parent)
+ spc = parent->GetPhysicsController();
+ if (parent)
+ parent->Release();
+
+ m_pHitObject = NULL;
+ if (propName)
+ m_testPropName = propName;
+ else
+ m_testPropName.SetLength(0);
+ m_xray = xray;
+ // to get the hit results
+ KX_RayCast::Callback<KX_GameObject> callback(this,spc,NULL,face);
+ KX_RayCast::RayTest(pe, fromPoint, toPoint, callback);
+
+ if (m_pHitObject)
+ {
+ PyObject* returnValue = (poly) ? PyTuple_New(4) : PyTuple_New(3);
+ if (returnValue) { // unlikely this would ever fail, if it does python sets an error
+ PyTuple_SET_ITEM(returnValue, 0, m_pHitObject->AddRef());
+ PyTuple_SET_ITEM(returnValue, 1, PyObjectFrom(callback.m_hitPoint));
+ PyTuple_SET_ITEM(returnValue, 2, PyObjectFrom(callback.m_hitNormal));
+ if (poly)
+ {
+ if (callback.m_hitMesh)
+ {
+ // if this field is set, then we can trust that m_hitPolygon is a valid polygon
+ RAS_Polygon* poly = callback.m_hitMesh->GetPolygon(callback.m_hitPolygon);
+ KX_PolyProxy* polyproxy = new KX_PolyProxy(callback.m_hitMesh, poly);
+ PyTuple_SET_ITEM(returnValue, 3, polyproxy);
+ }
+ else
+ {
+ Py_INCREF(Py_None);
+ PyTuple_SET_ITEM(returnValue, 3, Py_None);
+ }
+ }
+ }
+ return returnValue;
+ }
+ // no hit
+ if (poly)
+ return Py_BuildValue("OOOO", Py_None, Py_None, Py_None, Py_None);
+ else
+ return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
+}
+
/* ---------------------------------------------------------------------
* Some stuff taken from the header
* --------------------------------------------------------------------- */
void KX_GameObject::Relink(GEN_Map<GEN_HashedPtr, void*> *map_parameter)
{
- /* intentionally empty ? */
+ // we will relink the sensors and actuators that use object references
+ // if the object is part of the replicated hierarchy, use the new
+ // object reference instead
+ SCA_SensorList& sensorlist = GetSensors();
+ SCA_SensorList::iterator sit;
+ for (sit=sensorlist.begin(); sit != sensorlist.end(); sit++)
+ {
+ (*sit)->Relink(map_parameter);
+ }
+ SCA_ActuatorList& actuatorlist = GetActuators();
+ SCA_ActuatorList::iterator ait;
+ for (ait=actuatorlist.begin(); ait != actuatorlist.end(); ait++)
+ {
+ (*ait)->Relink(map_parameter);
+ }
}
+bool ConvertPythonToGameObject(PyObject * value, KX_GameObject **object, bool py_none_ok)
+{
+ if (value==NULL) {
+ PyErr_SetString(PyExc_TypeError, "Error in ConvertPythonToGameObject, python pointer NULL, should never happen");
+ *object = NULL;
+ return false;
+ }
+
+ if (value==Py_None) {
+ *object = NULL;
+
+ if (py_none_ok) {
+ return true;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "Expected KX_GameObject or a string for a name of a KX_GameObject, None is invalid");
+ return false;
+ }
+ return (py_none_ok ? true : false);
+ }
+
+ if (PyString_Check(value)) {
+ *object = (KX_GameObject *)SCA_ILogicBrick::m_sCurrentLogicManager->GetGameObjectByName(STR_String( PyString_AsString(value) ));
+
+ if (*object) {
+ return true;
+ } else {
+ PyErr_SetString(PyExc_ValueError, "Requested name did not match any KX_GameObject");
+ return false;
+ }
+ }
+
+ if (PyObject_TypeCheck(value, &KX_GameObject::Type)) {
+ *object = static_cast<KX_GameObject*>(value);
+ return true;
+ }
+
+ *object = NULL;
+
+ if (py_none_ok) {
+ PyErr_SetString(PyExc_TypeError, "Expect a KX_GameObject, a string or None");
+ } else {
+ PyErr_SetString(PyExc_TypeError, "Expect a KX_GameObject or a string");
+ }
+
+ return false;
+}