From b7dadcfefd24b44283fc96207d7352732eb3dae1 Mon Sep 17 00:00:00 2001 From: Norman Lin Date: Fri, 18 Oct 2002 15:46:57 +0000 Subject: first checkin of ode blender engine files --- .../gameengine/Ketsji/KX_OdePhysicsController.cpp | 236 +++++++++ source/gameengine/Ketsji/KX_OdePhysicsController.h | 100 ++++ source/gameengine/Physics/BlOde/Makefile | 50 ++ .../Physics/BlOde/OdePhysicsController.cpp | 577 +++++++++++++++++++++ .../Physics/BlOde/OdePhysicsController.h | 150 ++++++ .../Physics/BlOde/OdePhysicsEnvironment.cpp | 233 +++++++++ .../Physics/BlOde/OdePhysicsEnvironment.h | 73 +++ 7 files changed, 1419 insertions(+) create mode 100644 source/gameengine/Ketsji/KX_OdePhysicsController.cpp create mode 100644 source/gameengine/Ketsji/KX_OdePhysicsController.h create mode 100644 source/gameengine/Physics/BlOde/Makefile create mode 100644 source/gameengine/Physics/BlOde/OdePhysicsController.cpp create mode 100644 source/gameengine/Physics/BlOde/OdePhysicsController.h create mode 100644 source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp create mode 100644 source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h (limited to 'source') diff --git a/source/gameengine/Ketsji/KX_OdePhysicsController.cpp b/source/gameengine/Ketsji/KX_OdePhysicsController.cpp new file mode 100644 index 00000000000..ece08975f4f --- /dev/null +++ b/source/gameengine/Ketsji/KX_OdePhysicsController.cpp @@ -0,0 +1,236 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * The contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL", see + * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or + * later (the "BL", see http://www.blender.org/BL/ ) which has to be + * bought from the Blender Foundation to become active, in which case the + * above mentioned GPL option does not apply. + * + * The Original Code is Copyright (C) 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 ***** + */ +#include "KX_OdePhysicsController.h" +#include "KX_GameObject.h" +#include "KX_MotionState.h" + + +KX_OdePhysicsController::KX_OdePhysicsController( + bool dyna, + bool fullRigidBody, + bool phantom, + class PHY_IMotionState* motionstate, + struct dxSpace* space, + struct dxWorld* world, + float mass, + float friction, + float restitution, + bool implicitsphere, + float center[3], + float extends[3], + float radius + ) +: KX_IPhysicsController(dyna,(PHY_IPhysicsController*)this), +ODEPhysicsController( +dyna,fullRigidBody,phantom,motionstate, +space,world,mass,friction,restitution, +implicitsphere,center,extends,radius) +{ +}; + + +bool KX_OdePhysicsController::Update(double time) +{ + return SynchronizeMotionStates(time); +} + +void KX_OdePhysicsController::SetObject (SG_IObject* object) +{ + SG_Controller::SetObject(object); + + // cheating here... + KX_GameObject* gameobj = (KX_GameObject*) object->GetSGClientObject(); + gameobj->m_pPhysicsController1 = this; + +} + + + +void KX_OdePhysicsController::applyImpulse(const MT_Point3& attach, const MT_Vector3& impulse) +{ + ODEPhysicsController::applyImpulse(attach[0],attach[1],attach[2],impulse[0],impulse[1],impulse[2]); +} + + + +void KX_OdePhysicsController::RelativeTranslate(const MT_Vector3& dloc,bool local) +{ + ODEPhysicsController::RelativeTranslate(dloc[0],dloc[1],dloc[2],local); + +} +void KX_OdePhysicsController::RelativeRotate(const MT_Matrix3x3& drot,bool local) +{ + double oldmat[12]; + drot.getValue(oldmat); + float newmat[9]; + float *m = &newmat[0]; + double *orgm = &oldmat[0]; + + *m++ = *orgm++;*m++ = *orgm++;*m++ = *orgm++;orgm++; + *m++ = *orgm++;*m++ = *orgm++;*m++ = *orgm++;orgm++; + *m++ = *orgm++;*m++ = *orgm++;*m++ = *orgm++;orgm++; + + ODEPhysicsController::RelativeRotate(newmat,local); + +} + +void KX_OdePhysicsController::ApplyTorque(const MT_Vector3& torque,bool local) +{ + ODEPhysicsController::ApplyTorque(torque[0],torque[1],torque[2],local); + +} +void KX_OdePhysicsController::ApplyForce(const MT_Vector3& force,bool local) +{ + ODEPhysicsController::ApplyForce(force[0],force[1],force[2],local); + +} +MT_Vector3 KX_OdePhysicsController::GetLinearVelocity() +{ + return MT_Vector3(0,0,0); +} + +MT_Vector3 KX_OdePhysicsController::GetVelocity(const MT_Point3& pos) +{ + return MT_Vector3(0,0,0); +} + +void KX_OdePhysicsController::SetAngularVelocity(const MT_Vector3& ang_vel,bool local) +{ + +} +void KX_OdePhysicsController::SetLinearVelocity(const MT_Vector3& lin_vel,bool local) +{ + ODEPhysicsController::SetLinearVelocity(lin_vel[0],lin_vel[1],lin_vel[2],local); +} + +void KX_OdePhysicsController::setOrientation(const MT_Quaternion& orn) +{ + ODEPhysicsController::setOrientation(orn[0],orn[1],orn[2],orn[3]); +} + +void KX_OdePhysicsController::getOrientation(MT_Quaternion& orn) +{ + float florn[4]; + florn[0]=orn[0]; + florn[1]=orn[1]; + florn[2]=orn[2]; + florn[3]=orn[3]; + ODEPhysicsController::getOrientation(florn[0],florn[1],florn[2],florn[3]); + orn[0] = florn[0]; + orn[1] = florn[1]; + orn[2] = florn[2]; + orn[3] = florn[3]; + + +} + +void KX_OdePhysicsController::setPosition(const MT_Point3& pos) +{ + ODEPhysicsController::setPosition(pos[0],pos[1],pos[2]); +} + +void KX_OdePhysicsController::setScaling(const MT_Vector3& scaling) +{ +} + +MT_Scalar KX_OdePhysicsController::GetMass() +{ + return ODEPhysicsController::getMass(); +} + +MT_Vector3 KX_OdePhysicsController::getReactionForce() +{ + return MT_Vector3(0,0,0); +} +void KX_OdePhysicsController::setRigidBody(bool rigid) +{ + +} + +void KX_OdePhysicsController::SuspendDynamics() +{ + ODEPhysicsController::SuspendDynamics(); +} +void KX_OdePhysicsController::RestoreDynamics() +{ + ODEPhysicsController::RestoreDynamics(); +} + + +SG_Controller* KX_OdePhysicsController::GetReplica(class SG_Node* destnode) +{ + PHY_IMotionState* motionstate = new KX_MotionState(destnode); + KX_OdePhysicsController* copyctrl = new KX_OdePhysicsController(*this); + + // nlin: copied from KX_SumoPhysicsController.cpp. Not 100% sure what this does.... + // furthermore, the parentctrl is not used in ODEPhysicsController::PostProcessReplica, but + // maybe it can/should be used in the future... + + // begin copy block ------------------------------------------------------------------ + + //parentcontroller is here be able to avoid collisions between parent/child + + PHY_IPhysicsController* parentctrl = NULL; + + if (destnode != destnode->GetRootSGParent()) + { + KX_GameObject* clientgameobj = (KX_GameObject*) destnode->GetRootSGParent()->GetSGClientObject(); + if (clientgameobj) + { + parentctrl = (KX_OdePhysicsController*)clientgameobj->GetPhysicsController(); + } else + { + // it could be a false node, try the children + NodeList::const_iterator childit; + for ( + childit = destnode->GetSGChildren().begin(); + childit!= destnode->GetSGChildren().end(); + ++childit + ) { + KX_GameObject* clientgameobj = static_cast( (*childit)->GetSGClientObject()); + if (clientgameobj) + { + parentctrl = (KX_OdePhysicsController*)clientgameobj->GetPhysicsController(); + } + } + } + } + // end copy block ------------------------------------------------------------------ + + copyctrl->PostProcessReplica(motionstate, this); + + return copyctrl; + +} + + + +void KX_OdePhysicsController::SetSumoTransform(bool nondynaonly) +{ + +} + // todo: remove next line ! +void KX_OdePhysicsController::SetSimulatedTime(double time) +{ + +} + diff --git a/source/gameengine/Ketsji/KX_OdePhysicsController.h b/source/gameengine/Ketsji/KX_OdePhysicsController.h new file mode 100644 index 00000000000..89141f68488 --- /dev/null +++ b/source/gameengine/Ketsji/KX_OdePhysicsController.h @@ -0,0 +1,100 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * The contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL", see + * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or + * later (the "BL", see http://www.blender.org/BL/ ) which has to be + * bought from the Blender Foundation to become active, in which case the + * above mentioned GPL option does not apply. + * + * The Original Code is Copyright (C) 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 ***** + */ +#ifndef __KX_ODEPHYSICSCONTROLLER_H +#define __KX_ODEPHYSICSCONTROLLER_H + +#include "KX_IPhysicsController.h" +#include "OdePhysicsController.h" + + +/** + Physics Controller, a special kind of Scene Graph Transformation Controller. + It get's callbacks from Physics in case a transformation change took place. + Each time the scene graph get's updated, the controller get's a chance + in the 'Update' method to reflect changed. +*/ + +class KX_OdePhysicsController : public KX_IPhysicsController, public ODEPhysicsController + +{ + +public: + KX_OdePhysicsController( + bool dyna, + bool fullRigidBody, + bool phantom, + class PHY_IMotionState* motionstate, + struct dxSpace* space, + struct dxWorld* world, + float mass, + float friction, + float restitution, + bool implicitsphere, + float center[3], + float extends[3], + float radius); + + virtual ~KX_OdePhysicsController() {}; + + virtual void applyImpulse(const MT_Point3& attach, const MT_Vector3& impulse); + virtual void SetObject (SG_IObject* object); + + virtual void RelativeTranslate(const MT_Vector3& dloc,bool local); + virtual void RelativeRotate(const MT_Matrix3x3& drot,bool local); + virtual void ApplyTorque(const MT_Vector3& torque,bool local); + virtual void ApplyForce(const MT_Vector3& force,bool local); + virtual MT_Vector3 GetLinearVelocity(); + virtual MT_Vector3 GetVelocity(const MT_Point3& pos); + virtual void SetAngularVelocity(const MT_Vector3& ang_vel,bool local); + virtual void SetLinearVelocity(const MT_Vector3& lin_vel,bool local); + virtual void getOrientation(MT_Quaternion& orn); + virtual void setOrientation(const MT_Quaternion& orn); + virtual void setPosition(const MT_Point3& pos); + virtual void setScaling(const MT_Vector3& scaling); + virtual MT_Scalar GetMass(); + virtual MT_Vector3 getReactionForce(); + virtual void setRigidBody(bool rigid); + + virtual void SuspendDynamics(); + virtual void RestoreDynamics(); + + + virtual SG_Controller* GetReplica(class SG_Node* destnode); + + + virtual void SetSumoTransform(bool nondynaonly); + // todo: remove next line ! + virtual void SetSimulatedTime(double time); + + // call from scene graph to update + virtual bool Update(double time); + + void + SetOption( + int option, + int value + ){ + // intentionally empty + }; + +}; +#endif //__KX_ODEPHYSICSCONTROLLER_H diff --git a/source/gameengine/Physics/BlOde/Makefile b/source/gameengine/Physics/BlOde/Makefile new file mode 100644 index 00000000000..be890f660ad --- /dev/null +++ b/source/gameengine/Physics/BlOde/Makefile @@ -0,0 +1,50 @@ +# +# $Id$ +# +# ***** 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 ***** +# +# + +LIBNAME = blode +DIR = $(OCGDIR)/gameengine/blphys/$(LIBNAME) + +include nan_compile.mk + +CCFLAGS += $(LEVEL_1_CPP_WARNINGS) + +CPPFLAGS += -I$(OPENGL_HEADERS) +CPPFLAGS += -I$(NAN_STRING)/include +CPPFLAGS += -I$(NAN_PYTHON)/include/python$(NAN_PYTHON_VERSION) +CPPFLAGS += -I$(NAN_FUZZICS)/include -I$(NAN_SUMO)/include -I$(NAN_MOTO)/include +CPPFLAGS += -I$(NAN_ODE)/include +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include +CPPFLAGS += -I../../Physics/common +CPPFLAGS += -I../../Physics/Dummy +# nlin: fix this, should put in NAN_ODE dir +CPPFLAGS += -I./ode/ode/include diff --git a/source/gameengine/Physics/BlOde/OdePhysicsController.cpp b/source/gameengine/Physics/BlOde/OdePhysicsController.cpp new file mode 100644 index 00000000000..5640e478adb --- /dev/null +++ b/source/gameengine/Physics/BlOde/OdePhysicsController.cpp @@ -0,0 +1,577 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * The contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL", see + * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or + * later (the "BL", see http://www.blender.org/BL/ ) which has to be + * bought from the Blender Foundation to become active, in which case the + * above mentioned GPL option does not apply. + * + * The Original Code is Copyright (C) 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 ***** + */ +#include "OdePhysicsController.h" +#include "PHY_IMotionState.h" + +#include + +/////////////////////////////////////////////////////////////////////////// +// +// general to-do list for ODE physics. This is maintained in doxygen format. +// +/// \todo determine assignment time for bounding spheres. +/// +/// it appears you have to select "sphere" for bounding volume AND "draw bounds" +/// in order for a bounding sphere to be generated. otherwise a box is generated. +/// determine exactly when and how the bounding volumes are generated and make +/// this consistent. +/// } +/// +/// \todo bounding sphere size incorrect +/// +/// it appears NOT to use the size of the shown bounding sphere (button "draw bounds"). +/// it appears instead to use the size of the "size" dynamic parameter in the +/// gamebuttons but this "size" draws an incorrectly-sized circle on screen for the +/// bounding sphere (leftover skewed size calculation from sumo?) so figure out WHERE +/// its getting the radius from. +/// +/// \todo ODE collisions must fire collision actuator +/// +/// See OdePhysicsEnvironment::OdeNearCallback. If a sensor was created to check +/// for the presence of this collision, then in the NearCallback you need to +/// take appropriate action regarding the sensor - something like checking its +/// controller and if needed firing its actuator. Need to find similar code in +/// Fuzzics which fires collision controllers/actuators. +/// +/// \todo Are ghost collisions possible? +/// +/// How do ghost collisions work? Do they require collision detection through ODE +/// and NON-CREATION of contact-joint in OdeNearCallback? Currently OdeNearCallback +/// creates joints ALWAYS for collisions. +/// +/// \todo Why is KX_GameObject::addLinearVelocity commented out? +/// +/// Try putting this code back in. +/// +/// \todo Too many non-dynamic actors bogs down ODE physics +/// +/// Lots of "geoms" (ODE static geometry) probably slows down ode. Try a test file +/// with lots of static geometry - the game performance in Blender says it is +/// spending all its time in physics, and I bet all that time is in collision +/// detection. It's ode's non-hierarchical collision detection. +/// try making a separate ode test program (not within blender) with 1000 geoms and +/// see how fast it is. if it is really slow, there is the culprit. +/// isnt someone working on an improved ODE collision detector? check +/// ode mailing list. +/// +/// +/// \todo support collision of dynas with non-dynamic triangle meshes +/// +/// ODE has trimesh-collision support but only for trimeshes without a transform +/// matrix. update ODE tricollider to support a transform matrix. this will allow +/// moving trimeshes non-dynamically (e.g. through Ipos). then collide trimeshes +/// with dynas. this allows dynamic primitives (spheres, boxes) to collide with +/// non-dynamic or kinematically controlled tri-meshes. full dynamic trimesh to +/// dynamic trimesh support is hard because it requires (a) collision and penetration +/// depth for trimesh to trimesh and (hard to compute) (b) an intertia tensor +/// (easy to compute). +/// +/// a triangle mesh collision geometry should be created when the blender +/// bounding volume (F9, EDITBUTTONS) is set to "polyheder", since this is +/// currently the place where sphere/box selection is made +/// +/// \todo specify ODE ERP+CFM in blender interface +/// +/// when ODE physics selected, have to be able to set global cfm and erp. +/// per-joint erp/cfm could be handled in constraint window. +/// +/// \todo moving infinite mass objects should impart extra impulse to objects they collide with +/// +/// currently ODE's ERP pushes them apart but doesn't account for their motion. +/// +/// \todo allow tweaking bounding volume size +/// +/// the scene converter currently uses the blender bounding volume of the selected +/// object as the geometry for ODE collision purposes. this is good and automatic +/// intuitive - lets you choose between cube, sphere, mesh. but you need to be able +/// to tweak this size for physics. +/// +/// \todo off center meshes totally wrong for ode +/// +/// ode uses x, y, z extents regradless of center. then places geom at center of object. +/// but visual geom is not necessarily at center. need to detect off-center situations. +/// then do what? treat it as an encapsulated off-center mass, or recenter it? +/// +/// i.o.w. recalculate center, or recalculate mass distribution (using encapsulation)? +/// +/// \todo allow off-center mass +/// +/// using ode geometry encapsulators +/// +/// \todo allow entering compound geoms for complex collision shapes specified as a union of simpler shapes +/// +/// The collision shape for arbitrary triangle meshes can probably in general be +///well approximated by a compound ODE geometry object, which is merely a combination +///of many primitives (capsule, sphere, box). I eventually want to add the ability +///to associate compound geometry objects with Blender gameobjects. I think one +///way of doing this would be to add a new button in the GameButtons, "RigidBodyCompound". +///If the object is "Dynamic" + "RigidBody", then the object's bounding volume (sphere, +///box) is created. If an object is "Dynamic" + "RigidBodyCompound", then the object itself +///will merely create a "wrapper" compound object, with the actual geometry objects +///being created from the object's children in Blender. E.g. if I wanted to make a +///compound collision object consisting of a sphere and 2 boxes, I would create a +///parent gameobject with the actual triangle mesh, and set its GameButtons to +///"RigidBodyCompound". I would then create 3 children of this object, 1 sphere and +///2 boxes, and set the GameButtons for the children to be "RigidBody". Then at +///scene conversion time, the scene converter sees "RigidBodyCompound" for the +///top-level object, then appropriately traverses the children and creates the compound +///collision geometry consisting of 2 boxes and a sphere. In this way, arbitrary +///mesh-mesh collision becomes much less necessary - the artist can (or must, +///depending on your point of view!) approximate the collision shape for arbitrary +///meshes with a combination of one or more primitive shapes. I think using the +///parent/child relationship in Blender and a new button "RigidBodyCompound" for the +///parent object of a compound is a feasible way of doing this in Blender. +/// +///See ODE demo test_boxstack and look at the code when you drop a compound object +///with the "X" key. +/// +/// \todo add visual specification of constraints +/// +/// extend the armature constraint system. by using empties and constraining one empty +/// to "copy location" of another, you can get a p2p constraint between the two empties. +/// by making the two empties each a parent of a blender object, you effectively have +/// a p2p constraint between 2 blender bodies. the scene converter can detect these +/// empties, detect the constraint, and generate an ODE constraint. +/// +/// then add a new constraint type "hinge" and "slider" to correspond to ODE joints. +/// e.g. a slider would be a constraint which restricts the axis of its object to lie +/// along the same line as another axis of a different object. e.g. you constrain x-axis +/// of one empty to lie along the same line as the z-axis of another empty; this gives +/// a slider joint. +/// +/// open questions: how to handle powered joints? to what extent should/must constraints +/// be enforced during modeling? use CCD-style algorithm in modeler to enforce constraints? +/// how about ODE powered constraints e.g. motors? +/// +/// \todo enable suspension of bodies +/// ODE offers native support for suspending dynas. but what about suspending non-dynas +/// (e.g. geoms)? suspending geoms is also necessary to ease the load of ODE's (simple?) +/// collision detector. suspending dynas and geoms is important for the activity culling, +/// which apparently works at a simple level. perhaps suspension should actually +/// remove or insert geoms/dynas into the ODE space/world? is this operation (insertion/ +/// removal) fast enough at run-time? test it. if fast enough, then suspension=remove from +/// ODE simulation, awakening=insertion into ODE simulation. +/// +/// \todo python interface for tweaking constraints via python +/// +/// \todo raytesting to support gameengine sensors that need it + +ODEPhysicsController::ODEPhysicsController(bool dyna, bool fullRigidBody, + bool phantom, class PHY_IMotionState* motionstate, struct dxSpace* space, + struct dxWorld* world, float mass,float friction,float restitution, + bool implicitsphere,float center[3],float extents[3],float radius) + : + m_OdeDyna(dyna), + m_firstTime(true), + m_bFullRigidBody(fullRigidBody), + m_bPhantom(phantom), + m_bKinematic(false), + m_bPrevKinematic(false), + m_MotionState(motionstate), + m_OdeSuspendDynamics(false), + m_space(space), + m_world(world), + m_mass(mass), + m_friction(friction), + m_restitution(restitution), + m_bodyId(0), + m_geomId(0), + m_implicitsphere(implicitsphere), + m_radius(radius) +{ + m_center[0] = center[0]; + m_center[1] = center[1]; + m_center[2] = center[2]; + m_extends[0] = extents[0]; + m_extends[1] = extents[1]; + m_extends[2] = extents[2]; +}; + + +ODEPhysicsController::~ODEPhysicsController() +{ + if (m_geomId) + { + dGeomDestroy (m_geomId); + } +} + +float ODEPhysicsController::getMass() +{ + dMass mass; + dBodyGetMass(m_bodyId,&mass); + return mass.mass; +} + +////////////////////////////////////////////////////////////////////// +/// \todo Impart some extra impulse to dynamic objects when they collide with kinematically controlled "static" objects (ODE geoms), by using last 2 frames as 1st order approximation to the linear/angular velocity, and computing an appropriate impulse. Sumo (old physics engine) did this, see for details. +/// \todo handle scaling of static ODE geoms or fail with error message if Ipo tries to change scale of a static geom object + +bool ODEPhysicsController::SynchronizeMotionStates(float time) +{ + /** + 'Late binding' of the rigidbody, because the World Scaling is not available until the scenegraph is traversed + */ + + + if (m_firstTime) + { + m_firstTime=false; + + m_MotionState->calculateWorldTransformations(); + + dQuaternion worldquat; + float worldpos[3]; + + m_MotionState->getWorldOrientation(worldquat[1],worldquat[2],worldquat[3],worldquat[0]); + m_MotionState->getWorldPosition(worldpos[0],worldpos[1],worldpos[2]); + + float scaling[3]; + m_MotionState->getWorldScaling(scaling[0],scaling[1],scaling[2]); + + if (!m_bPhantom) + { + if (m_implicitsphere) + { + m_geomId = dCreateSphere (m_space,m_radius*scaling[0]); + } else + { + m_geomId = dCreateBox (m_space, m_extends[0]*scaling[0],m_extends[1]*scaling[1],m_extends[2]*scaling[2]); + } + } else + { + m_geomId=0; + } + + if (m_geomId) + dGeomSetData(m_geomId,this); + + if (!this->m_OdeDyna) + { + if (!m_bPhantom) + { + dGeomSetPosition (this->m_geomId,worldpos[0],worldpos[1],worldpos[2]); + dMatrix3 R; + dQtoR (worldquat, R); + dGeomSetRotation (this->m_geomId,R); + } + } else + { + //it's dynamic, so create a 'model' + m_bodyId = dBodyCreate(this->m_world); + dBodySetPosition (m_bodyId,worldpos[0],worldpos[1],worldpos[2]); + dBodySetQuaternion (this->m_bodyId,worldquat); + //this contains both scalar mass and inertia tensor + dMass m; + float length=1,width=1,height=1; + dMassSetBox (&m,1,m_extends[0]*scaling[0],m_extends[1]*scaling[1],m_extends[2]*scaling[2]); + dMassAdjust (&m,this->m_mass); + dBodySetMass (m_bodyId,&m); + + if (!m_bPhantom) + { + dGeomSetBody (m_geomId,m_bodyId); + } + + + } + + if (this->m_OdeDyna && !m_bFullRigidBody) + { + // ?? huh? what to do here? + } + } + + + + if (m_OdeDyna) + { + if (this->m_OdeSuspendDynamics) + { + return false; + } + + const float* worldPos = dBodyGetPosition(m_bodyId); + m_MotionState->setWorldPosition(worldPos[0],worldPos[1],worldPos[2]); + + const float* worldquat = dBodyGetQuaternion(m_bodyId); + m_MotionState->setWorldOrientation(worldquat[1],worldquat[2],worldquat[3],worldquat[0]); + } + else { + // not a dyna, so dynamics (i.e. this controller) has not updated + // anything. BUT! an Ipo or something else might have changed the + // position/orientation of this geometry. + // so update the static geom position + + /// \todo impart some extra impulse to colliding objects! + dQuaternion worldquat; + float worldpos[3]; + + m_MotionState->getWorldOrientation(worldquat[1],worldquat[2],worldquat[3],worldquat[0]); + m_MotionState->getWorldPosition(worldpos[0],worldpos[1],worldpos[2]); + + float scaling[3]; + m_MotionState->getWorldScaling(scaling[0],scaling[1],scaling[2]); + + /// \todo handle scaling! what if Ipo changes scale of object? + // Must propagate to geom... is scaling geoms possible with ODE? Also + // what about scaling trimeshes, that is certainly difficult... + dGeomSetPosition (this->m_geomId,worldpos[0],worldpos[1],worldpos[2]); + dMatrix3 R; + dQtoR (worldquat, R); + dGeomSetRotation (this->m_geomId,R); + } + + return false; //it update the worldpos +} + + + + + +// kinematic methods +void ODEPhysicsController::RelativeTranslate(float dlocX,float dlocY,float dlocZ,bool local) +{ + +} +void ODEPhysicsController::RelativeRotate(const float drot[9],bool local) +{ +} +void ODEPhysicsController::setOrientation(float quatImag0,float quatImag1,float quatImag2,float quatReal) +{ + + dQuaternion worldquat; + worldquat[0] = quatReal; + worldquat[1] = quatImag0; + worldquat[2] = quatImag1; + worldquat[3] = quatImag2; + + if (!this->m_OdeDyna) + { + dMatrix3 R; + dQtoR (worldquat, R); + dGeomSetRotation (this->m_geomId,R); + } else + { + dBodySetQuaternion (m_bodyId,worldquat); + this->m_MotionState->setWorldOrientation(quatImag0,quatImag1,quatImag2,quatReal); + } + +} + +void ODEPhysicsController::getOrientation(float &quatImag0,float &quatImag1,float &quatImag2,float &quatReal) +{ + float q[4]; + this->m_MotionState->getWorldOrientation(q[0],q[1],q[2],q[3]); + quatImag0=q[0]; + quatImag1=q[1]; + quatImag2=q[2]; + quatReal=q[3]; +} + +void ODEPhysicsController::setPosition(float posX,float posY,float posZ) +{ + if (!m_bPhantom) + { + if (!this->m_OdeDyna) + { + dGeomSetPosition (m_geomId, posX, posY, posZ); + } else + { + dBodySetPosition (m_bodyId, posX, posY, posZ); + } + } +} +void ODEPhysicsController::setScaling(float scaleX,float scaleY,float scaleZ) +{ +} + +// physics methods +void ODEPhysicsController::ApplyTorque(float torqueX,float torqueY,float torqueZ,bool local) +{ + if (m_OdeDyna) { + if(local) { + dBodyAddRelTorque(m_bodyId, torqueX, torqueY, torqueZ); + } else { + dBodyAddTorque (m_bodyId, torqueX, torqueY, torqueZ); + } + } +} + +void ODEPhysicsController::ApplyForce(float forceX,float forceY,float forceZ,bool local) +{ + if (m_OdeDyna) { + if(local) { + dBodyAddRelForce(m_bodyId, forceX, forceY, forceZ); + } else { + dBodyAddForce (m_bodyId, forceX, forceY, forceZ); + } + } +} + +void ODEPhysicsController::SetAngularVelocity(float ang_velX,float ang_velY,float ang_velZ,bool local) +{ + if (m_OdeDyna) { + if(local) { + // TODO: translate angular vel into local frame, then apply + } else { + dBodySetAngularVel (m_bodyId, ang_velX,ang_velY,ang_velZ); + } + } +} + +void ODEPhysicsController::SetLinearVelocity(float lin_velX,float lin_velY,float lin_velZ,bool local) +{ + if (m_OdeDyna) + { + dVector3 vel = {lin_velX,lin_velY,lin_velZ}; + if (local) + { + dMatrix3 worldmat; + dVector3 localvel; + dQuaternion worldquat; + m_MotionState->getWorldOrientation(worldquat[1],worldquat[2],worldquat[3],worldquat[0]); + dQtoR (worldquat, worldmat); + + dMULTIPLY0_331 (localvel,worldmat,vel); + dBodySetLinearVel (m_bodyId, localvel[0],localvel[1],localvel[2]); + + } else + { + dBodySetLinearVel (m_bodyId, lin_velX,lin_velY,lin_velZ); + } + } +} + +void ODEPhysicsController::applyImpulse(float attachX,float attachY,float attachZ, float impulseX,float impulseY,float impulseZ) +{ + if (m_OdeDyna) + { + //apply linear and angular effect + const dReal* linvel = dBodyGetLinearVel(m_bodyId); + float mass = getMass(); + if (mass >= 0.00001f) + { + float massinv = 1.f/mass; + float newvel[3]; + newvel[0]=linvel[0]+impulseX*massinv; + newvel[1]=linvel[1]+impulseY*massinv; + newvel[2]=linvel[2]+impulseZ*massinv; + dBodySetLinearVel(m_bodyId,newvel[0],newvel[1],newvel[2]); + + const float* worldPos = dBodyGetPosition(m_bodyId); + + const float* angvelc = dBodyGetAngularVel (m_bodyId); + float angvel[3]; + angvel[0]=angvelc[0]; + angvel[1]=angvelc[1]; + angvel[2]=angvelc[2]; + + dVector3 impulse; + impulse[0]=impulseX; + impulse[1]=impulseY; + impulse[2]=impulseZ; + + dVector3 ap; + ap[0]=attachX-worldPos[0]; + ap[1]=attachY-worldPos[1]; + ap[2]=attachZ-worldPos[2]; + + dCROSS(angvel,+=,ap,impulse); + dBodySetAngularVel(m_bodyId,angvel[0],angvel[1],angvel[2]); + + } + + } + +} + +void ODEPhysicsController::SuspendDynamics() +{ + +} + +void ODEPhysicsController::RestoreDynamics() +{ + +} + + +/** + reading out information from physics +*/ +void ODEPhysicsController::GetLinearVelocity(float& linvX,float& linvY,float& linvZ) +{ + if (m_OdeDyna) + { + const float* vel = dBodyGetLinearVel(m_bodyId); + linvX = vel[0]; + linvY = vel[1]; + linvZ = vel[2]; + } else + { + linvX = 0.f; + linvY = 0.f; + linvZ = 0.f; + + } +} +/** + GetVelocity parameters are in geometric coordinates (Origin is not center of mass!). +*/ +void ODEPhysicsController::GetVelocity(const float posX,const float posY,const float posZ,float& linvX,float& linvY,float& linvZ) +{ + +} + + +void ODEPhysicsController::getReactionForce(float& forceX,float& forceY,float& forceZ) +{ + +} +void ODEPhysicsController::setRigidBody(bool rigid) +{ + +} + + +void ODEPhysicsController::PostProcessReplica(class PHY_IMotionState* motionstate,class PHY_IPhysicsController* parentctrl) +{ + m_MotionState = motionstate; + m_bKinematic = false; + m_bPrevKinematic = false; + m_firstTime = true; +} + + +void ODEPhysicsController::SetSimulatedTime(float time) +{ +} + + +void ODEPhysicsController::WriteMotionStateToDynamics(bool nondynaonly) +{ + +} + + + diff --git a/source/gameengine/Physics/BlOde/OdePhysicsController.h b/source/gameengine/Physics/BlOde/OdePhysicsController.h new file mode 100644 index 00000000000..659a951a071 --- /dev/null +++ b/source/gameengine/Physics/BlOde/OdePhysicsController.h @@ -0,0 +1,150 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * The contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL", see + * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or + * later (the "BL", see http://www.blender.org/BL/ ) which has to be + * bought from the Blender Foundation to become active, in which case the + * above mentioned GPL option does not apply. + * + * The Original Code is Copyright (C) 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 ***** + */ +#ifndef __ODE_PHYSICSCONTROLLER_H +#define __ODE_PHYSICSCONTROLLER_H + + +#include "PHY_IPhysicsController.h" + + +/** + ODE Physics Controller, a special kind of a PhysicsController. + A Physics Controller is a special kind of Scene Graph Transformation Controller. + Each time the scene graph get's updated, the controller get's a chance + in the 'Update' method to reflect changes. +*/ + +class ODEPhysicsController : public PHY_IPhysicsController + +{ + + bool m_OdeDyna; + +public: + ODEPhysicsController( + bool dyna, + bool fullRigidBody, + bool phantom, + class PHY_IMotionState* motionstate, + struct dxSpace* space, + struct dxWorld* world, + float mass, + float friction, + float restitution, + bool implicitsphere, + float center[3], + float extends[3], + float radius); + + virtual ~ODEPhysicsController(); + + // kinematic methods + virtual void RelativeTranslate(float dlocX,float dlocY,float dlocZ,bool local); + virtual void RelativeRotate(const float drot[9],bool local); + virtual void getOrientation(float &quatImag0,float &quatImag1,float &quatImag2,float &quatReal); + virtual void setOrientation(float quatImag0,float quatImag1,float quatImag2,float quatReal); + virtual void setPosition(float posX,float posY,float posZ); + virtual void setScaling(float scaleX,float scaleY,float scaleZ); + + // physics methods + virtual void ApplyTorque(float torqueX,float torqueY,float torqueZ,bool local); + virtual void ApplyForce(float forceX,float forceY,float forceZ,bool local); + virtual void SetAngularVelocity(float ang_velX,float ang_velY,float ang_velZ,bool local); + virtual void SetLinearVelocity(float lin_velX,float lin_velY,float lin_velZ,bool local); + virtual void applyImpulse(float attachX,float attachY,float attachZ, float impulseX,float impulseY,float impulseZ); + virtual void SetActive(bool active){}; + virtual void SuspendDynamics(); + virtual void RestoreDynamics(); + + + /** + reading out information from physics + */ + virtual void GetLinearVelocity(float& linvX,float& linvY,float& linvZ); + /** + GetVelocity parameters are in geometric coordinates (Origin is not center of mass!). + */ + virtual void GetVelocity(const float posX,const float posY,const float posZ,float& linvX,float& linvY,float& linvZ); + virtual float getMass(); + virtual void getReactionForce(float& forceX,float& forceY,float& forceZ); + virtual void setRigidBody(bool rigid); + + + virtual void PostProcessReplica(class PHY_IMotionState* motionstate,class PHY_IPhysicsController* parentctrl); + + // \todo remove next line ! + virtual void SetSimulatedTime(float time); + + + virtual void WriteDynamicsToMotionState() {}; + virtual void WriteMotionStateToDynamics(bool nondynaonly); + + /** + call from Scene Graph Node to 'update'. + */ + virtual bool SynchronizeMotionStates(float time); + + // clientinfo for raycasts for example + virtual void* getClientInfo() { return m_clientInfo;} + virtual void setClientInfo(void* clientinfo) {m_clientInfo = clientinfo;}; + void* m_clientInfo; + + struct dxBody* GetOdeBodyId() { return m_bodyId; } + + float getFriction() { return m_friction;} + float getRestitution() { return m_restitution;} + + + +private: + + bool m_firstTime; + bool m_bFullRigidBody; + bool m_bPhantom; // special flag for objects that are not affected by physics 'resolver' + + // data to calculate fake velocities for kinematic objects (non-dynas) + bool m_bKinematic; + bool m_bPrevKinematic; + + + float m_lastTime; + bool m_OdeSuspendDynamics; + class PHY_IMotionState* m_MotionState; + + //Ode specific members + struct dxBody* m_bodyId; + struct dxGeom* m_geomId; + struct dxSpace* m_space; + struct dxWorld* m_world; + float m_mass; + float m_friction; + float m_restitution; + bool m_implicitsphere; + float m_center[3]; + float m_extends[3]; + float m_radius; +}; + +#endif //__ODE_PHYSICSCONTROLLER_H + + + diff --git a/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp new file mode 100644 index 00000000000..8023b791be0 --- /dev/null +++ b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp @@ -0,0 +1,233 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * The contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL", see + * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or + * later (the "BL", see http://www.blender.org/BL/ ) which has to be + * bought from the Blender Foundation to become active, in which case the + * above mentioned GPL option does not apply. + * + * The Original Code is Copyright (C) 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 ***** + */ +#include "OdePhysicsEnvironment.h" +#include "PHY_IMotionState.h" +#include "OdePhysicsController.h" + +// Ode +#include +#include +#include <../ode/src/joint.h> +#include + + +ODEPhysicsEnvironment::ODEPhysicsEnvironment() +{ + m_OdeWorld = dWorldCreate(); + m_OdeSpace = dHashSpaceCreate(); + m_OdeContactGroup = dJointGroupCreate (0); + dWorldSetCFM (m_OdeWorld,1e-5f); + + m_JointGroup = dJointGroupCreate(0); + +} + + + +ODEPhysicsEnvironment::~ODEPhysicsEnvironment() +{ + dJointGroupDestroy (m_OdeContactGroup); + dJointGroupDestroy (m_JointGroup); + + dSpaceDestroy (m_OdeSpace); + dWorldDestroy (m_OdeWorld); +} + +void ODEPhysicsEnvironment::proceed(double timeStep) +{ + // ode collision update + dSpaceCollide (m_OdeSpace,this,&ODEPhysicsEnvironment::OdeNearCallback); + + int m_odeContacts = GetNumOdeContacts(); + + //physics integrator + resolver update + dWorldStep (m_OdeWorld,timeStep); + + //clear collision points + this->ClearOdeContactGroup(); +} + +void ODEPhysicsEnvironment::setGravity(float x,float y,float z) +{ + dWorldSetGravity (m_OdeWorld,x,y,z); +} + + + +int ODEPhysicsEnvironment::createConstraint(class PHY_IPhysicsController* ctrl,class PHY_IPhysicsController* ctrl2,PHY_ConstraintType type, + float pivotX,float pivotY,float pivotZ,float axisX,float axisY,float axisZ) +{ + + int constraintid = 0; + ODEPhysicsController* dynactrl = (ODEPhysicsController*)ctrl; + ODEPhysicsController* dynactrl2 = (ODEPhysicsController*)ctrl2; + + switch (type) + { + case PHY_POINT2POINT_CONSTRAINT: + { + if (dynactrl) + { + dJointID jointid = dJointCreateBall (m_OdeWorld,m_JointGroup); + struct dxBody* bodyid1 = dynactrl->GetOdeBodyId(); + struct dxBody* bodyid2=0; + const float* pos = dBodyGetPosition(bodyid1); + const float* R = dBodyGetRotation(bodyid1); + float offset[3] = {pivotX,pivotY,pivotZ}; + float newoffset[3]; + dMULTIPLY0_331 (newoffset,R,offset); + newoffset[0] += pos[0]; + newoffset[1] += pos[1]; + newoffset[2] += pos[2]; + + + if (dynactrl2) + bodyid2 = dynactrl2->GetOdeBodyId(); + + dJointAttach (jointid, bodyid1, bodyid2); + + dJointSetBallAnchor (jointid, newoffset[0], newoffset[1], newoffset[2]); + + constraintid = (int) jointid; + } + break; + } + case PHY_LINEHINGE_CONSTRAINT: + { + if (dynactrl) + { + dJointID jointid = dJointCreateHinge (m_OdeWorld,m_JointGroup); + struct dxBody* bodyid1 = dynactrl->GetOdeBodyId(); + struct dxBody* bodyid2=0; + const float* pos = dBodyGetPosition(bodyid1); + const float* R = dBodyGetRotation(bodyid1); + float offset[3] = {pivotX,pivotY,pivotZ}; + float axisset[3] = {axisX,axisY,axisZ}; + + float newoffset[3]; + float newaxis[3]; + dMULTIPLY0_331 (newaxis,R,axisset); + + dMULTIPLY0_331 (newoffset,R,offset); + newoffset[0] += pos[0]; + newoffset[1] += pos[1]; + newoffset[2] += pos[2]; + + + if (dynactrl2) + bodyid2 = dynactrl2->GetOdeBodyId(); + + dJointAttach (jointid, bodyid1, bodyid2); + + dJointSetHingeAnchor (jointid, newoffset[0], newoffset[1], newoffset[2]); + dJointSetHingeAxis(jointid,newaxis[0],newaxis[1],newaxis[2]); + + constraintid = (int) jointid; + } + break; + } + default: + { + //not yet + } + } + + return constraintid; + +} + +void ODEPhysicsEnvironment::removeConstraint(int constraintid) +{ + if (constraintid) + { + dJointDestroy((dJointID) constraintid); + } +} + +PHY_IPhysicsController* ODEPhysicsEnvironment::rayTest(void* ignoreClient,float fromX,float fromY,float fromZ, float toX,float toY,float toZ, + float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ) +{ + //collision detection / raytesting + return NULL; +} + + +void ODEPhysicsEnvironment::OdeNearCallback (void *data, dGeomID o1, dGeomID o2) +{ + // \todo if this is a registered collision sensor + // fire the callback + + int i; + // if (o1->body && o2->body) return; + ODEPhysicsEnvironment* env = (ODEPhysicsEnvironment*) data; + dBodyID b1,b2; + + b1 = dGeomGetBody(o1); + b2 = dGeomGetBody(o2); + // exit without doing anything if the two bodies are connected by a joint + if (b1 && b2 && dAreConnected (b1,b2)) return; + + ODEPhysicsController * ctrl1 =(ODEPhysicsController *)dGeomGetData(o1); + ODEPhysicsController * ctrl2 =(ODEPhysicsController *)dGeomGetData(o2); + float friction=ctrl1->getFriction(); + float restitution = ctrl1->getRestitution(); + //for friction, take minimum + + friction=(friction < ctrl2->getFriction() ? + friction :ctrl2->getFriction()); + + //restitution:take minimum + restitution = restitution < ctrl2->getRestitution()? + restitution : ctrl2->getRestitution(); + + dContact contact[3]; // up to 3 contacts per box + for (i=0; i<3; i++) { + contact[i].surface.mode = dContactBounce; //dContactMu2; + contact[i].surface.mu = friction;//dInfinity; + contact[i].surface.mu2 = 0; + contact[i].surface.bounce = restitution;//0.5; + contact[i].surface.bounce_vel = 0.1f; + contact[i].surface.slip1=0.0; + } + + if (int numc = dCollide (o1,o2,3,&contact[0].geom,sizeof(dContact))) { + // dMatrix3 RI; + // dRSetIdentity (RI); + // const dReal ss[3] = {0.02,0.02,0.02}; + for (i=0; im_OdeWorld,env->m_OdeContactGroup,contact+i); + dJointAttach (c,b1,b2); + } + } +} + + +void ODEPhysicsEnvironment::ClearOdeContactGroup() +{ + dJointGroupEmpty (m_OdeContactGroup); +} + +int ODEPhysicsEnvironment::GetNumOdeContacts() +{ + return m_OdeContactGroup->num; +} + diff --git a/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h new file mode 100644 index 00000000000..b9872246afc --- /dev/null +++ b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h @@ -0,0 +1,73 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * The contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL", see + * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or + * later (the "BL", see http://www.blender.org/BL/ ) which has to be + * bought from the Blender Foundation to become active, in which case the + * above mentioned GPL option does not apply. + * + * The Original Code is Copyright (C) 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 ***** + */ +#ifndef _ODEPHYSICSENVIRONMENT +#define _ODEPHYSICSENVIRONMENT + + +#include "PHY_IPhysicsEnvironment.h" + + +/** +* Physics Environment takes care of stepping the simulation and is a container for physics entities (rigidbodies,constraints, materials etc.) +* A derived class may be able to 'construct' entities by loading and/or converting +*/ +class ODEPhysicsEnvironment : public PHY_IPhysicsEnvironment +{ + +public: + ODEPhysicsEnvironment(); + virtual ~ODEPhysicsEnvironment(); +// Perform an integration step of duration 'timeStep'. + virtual void proceed(double timeStep); + virtual void setGravity(float x,float y,float z); + virtual int createConstraint(class PHY_IPhysicsController* ctrl,class PHY_IPhysicsController* ctrl2,PHY_ConstraintType type, + float pivotX,float pivotY,float pivotZ, + float axisX,float axisY,float axisZ); + + virtual void removeConstraint(int constraintid); + virtual PHY_IPhysicsController* rayTest(void* ignoreClient,float fromX,float fromY,float fromZ, float toX,float toY,float toZ, + float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ); + + struct dxWorld* GetOdeWorld() { return m_OdeWorld; }; + struct dxSpace* GetOdeSpace() { return m_OdeSpace;}; + +private: + + + // ODE physics response + struct dxWorld* m_OdeWorld; + // ODE collision detection + struct dxSpace* m_OdeSpace; + void ClearOdeContactGroup(); + struct dxJointGroup* m_OdeContactGroup; + struct dxJointGroup* m_JointGroup; + + static void OdeNearCallback(void *data, struct dxGeom* o1, struct dxGeom* o2); + int GetNumOdeContacts(); + +}; + +#endif //_ODEPHYSICSENVIRONMENT + + + + -- cgit v1.2.3