Minimalistic threading for OgreODE.

Anonymous

10-03-2007 13:43:41

Some months back, I promised to share my tests of threading OgreODE - this is the original thread: http://www.ogre3d.org/phpBB2addons/viewtopic.php?t=2269&start=44
Seems I will never get around to doing this properly, but in case luis or someone is interested, here are the important parts of my threaded ogreODE implementation. It was written as a "proof of concept" demo, and stuff will have to be added in order to make it usable in a game. In particular, communication is currently one-way (ODE moves Ogre nodes, but there is no way to apply forces to ODE bodies). Two-way communication would not be too hard to add, using a thread-safe queue or something.

ah, yes. It uses Dagon and a kindof old OgreOde. Just see it as some ideas on how to implement threaded physics with OgreOde in a minimalistic way...

first, there is the WorldState object, which is basically a list of movable entities in the physics world:
WorldState.h:
#ifndef _WORLDSTATE_H_
#define _WORLDSTATE_H_

#include "Ogre.h"
#include "PhysicsManagerObject.h"
#include <Boost/thread/mutex.hpp>

class WorldState : public Singleton<WorldState>
{
public:
typedef std::list< PhysicsManagerObject *> worldObjectList;
void addPhysicsManagerObject(PhysicsManagerObject *obj);
bool removePhysicsManagerObject(Ogre::SceneNode *n); //true if found
void updateOgreWorld();
inline boost::mutex * getWorldStateMutex() {return &worldStateMutex;}

protected:
boost::mutex worldStateMutex;
worldObjectList worldState;
};
#endif //_WORLDSTATE_H_

and WorldState.cpp:

#include "WorldState.h"

template<> WorldState* Ogre::Singleton<WorldState>::ms_Singleton = 0;

void WorldState::addPhysicsManagerObject(PhysicsManagerObject *obj)
{
worldState.push_back(obj);
}

bool WorldState::removePhysicsManagerObject(Ogre::SceneNode *n)
{
for(worldObjectList::iterator it = worldState.begin(); it != worldState.end(); ++it)
if((*it)->getParentNode() == n)
{
worldState.erase(it);
return true;
}
return false;
}

void WorldState::updateOgreWorld()
{
for(worldObjectList::iterator it = worldState.begin(); it != worldState.end(); ++it)
{
(*it)->syncParentNode();
}
}


Every frame the main app will call updateOgreWorld(), which calls syncParentNode() on all PhysicsManagerObjects in its list.

The physicsManagerObject is basically a compound of a sceneNode and body:
PhysicsManagerBody.h

#ifndef _PHYSICSMANAGEROBJECT_H_
#define _PHYSICSMANAGEROBJECT_H_

#include "Ogre.h"

using namespace Ogre;

class PhysicsManagerBody;

class PhysicsManagerObject
{
public:
PhysicsManagerObject(SceneNode *parentNode, PhysicsManagerBody *body);
SceneNode * getParentNode() {return parentNode;}
PhysicsManagerBody * getBody() {return body;}
void setPosition(const Vector3 &p);
void setOrientation(const Quaternion &q);
void syncParentNode()
{
parentNode->setOrientation(orientation);
parentNode->setPosition(position);
}
void syncBody() {}; //unimplemented.

protected:
Vector3 position;
Quaternion orientation;
SceneNode *parentNode;
PhysicsManagerBody *body;
};

#endif //_PHYSICSMANAGEROBJECT_H_

and PhysicsManagerObject.cpp:

#include "PhysicsManagerObject.h"

PhysicsManagerObject::PhysicsManagerObject(SceneNode *parentNode, PhysicsManagerBody *body)
: parentNode(parentNode), body(body),
position(parentNode->getPosition()), orientation(parentNode->getOrientation())
{
}

void PhysicsManagerObject::setPosition(const Vector3 &p)
{
position = p;
}

void PhysicsManagerObject::setOrientation(const Quaternion &q)
{
orientation = q;
}


so... from ODEs side of things, I subclassed the OgreOde::QuickStepper into my own PhysicsManagerStepper, which first steps, then locks the worldstatemutex and updates positins of worldState.
PhysicsManagerStepper.h

#ifndef _PHYSICSMANAGERSTEPPER_H_
#define _PHYSICSMANAGERSTEPPER_H_

#include "OgreOdeStepper.h"
#include "OgreOdeWorld.h"
#include "SimpleTime.h"
#include <time.h>
#include <Boost/thread/mutex.hpp>

//Do a step, lock worldstate mutex, synchronize.
//(and sleep until next timestep.)
// step(evt.timeSinceLastFrame);
// lock_mutex...
// _world->synchronise();
//This is done in framestarted in current implementation.
class PhysicsManagerStepper : public OgreOde::QuickStepper
{
public:
PhysicsManagerStepper(Real timeStep, boost::mutex *worldStateMutex);
virtual void step(Real time);
void run();

protected:
Real mTimeStep;
long int oldTime;
boost::mutex *mWorldStateMutex;

};

#endif //_PHYSICSMANAGERSTEPPER_H_


PhysicsManagerStepper.cpp

#include "PhysicsManagerStepper.h"
#include "Windows.h" //for Sleep() on windows.
#include "PhysicsManager.h"
#include "OgreOdeSpace.h"
#include "OgreOdeWorld.h"


PhysicsManagerStepper::PhysicsManagerStepper(Real timeStep, boost::mutex *worldStateMutex)
: QuickStepper(timeStep), mTimeStep(timeStep), mWorldStateMutex(worldStateMutex)
{
Time::initialise();
}


void PhysicsManagerStepper::step(Real time)
{
oldTime = Time::getMilliseconds();
if ((!_listener)||(_listener->preStep(time)))
{
OgreOde::Space * sp = _world->getDefaultSpace();
sp->collide();
_world->quickStep(time);
_world->clearContacts();
}
boost::mutex::scoped_lock lock(*mWorldStateMutex);
_world->synchronise();

long newTime = Time::getMilliseconds();
long sleepTime = mTimeStep * 1000.0 - newTime + oldTime;
if(sleepTime > 0)
Sleep(sleepTime);
// else we´re in trouble
oldTime = Time::getMilliseconds();
}

void PhysicsManagerStepper::run()
{
while(true)
step(mTimeStep);
}


Then, there is the PhysicsManagerBody which reimplemets body::sync() to update WorldState instead of a SceneNode:
PhysicsManagerBody.h

#ifndef _PHYSICSMANAGERBODY_H_
#define _PHYSICSMANAGERBODY_H_

#include "OgreOdeBody.h"
#include "PhysicsManagerObject.h"

class PhysicsManagerObject;

class PhysicsManagerBody : public OgreOde::Body
{
public:
PhysicsManagerBody(const String& name = StringUtil::BLANK)
: Body(name) {};
virtual ~PhysicsManagerBody() {};
virtual void sync();
void updateWorldState(); //USE ONLY ON MUTEXED WORLDSTATE!!!
void setPhysObject(PhysicsManagerObject *pmo) {mPhysObj = pmo;}
protected:
PhysicsManagerObject *mPhysObj;

};

#endif //_PHYSICSMANAGERBODY_H_


PhysicsManagerBody.cpp

#include "PhysicsManagerBody.h"

void PhysicsManagerBody::sync()
{
applyDamping();
updateWorldState();
}

void PhysicsManagerBody::updateWorldState()
{
mPhysObj->setPosition(getPosition());
mPhysObj->setOrientation(getOrientation());
}


To tie it all together, and to integrate plsm2 I have a physicsManager. It is even hackier than what I have posted, and quite a lot bigger, so better not post it here (this post is ridiculously long as it is) but I can post it later.