TriangleMeshRayListener Demo
From Ogre Wiki
Note: The implementation below uses OgreOde Rays to position an entity's height over a mesh. If you want the benefits of creating a mesh and using that to make a terrain this code is for you =).
I've written a short demo application, and it tested fine on my machine. If for some reason it doesn't work, just re-use the parts of code that work best for you. Also, I didn't spend much time working on this, so the camera view and comments aren't all that great. That being said..enjoy!
#include "ExampleApplication.h"
// include OgreOde files
#include "OgreOde_Core.h"
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct{
std::string charName;
SceneNode* charNode;
OgreOde::RayGeometry* charRay;
// radius used to position ray above actual node position (at feet)
Real radius;
}ODE_CHAR_INFO;
// Declare a subclass of the ExampleFrameListener class
class MyListener :
public ExampleFrameListener,
public OgreOde::CollisionListener,
public OgreOde::StepListener,
public OgreOde::TriangleMeshRayListener
{
public:
MyListener(RenderWindow* win, Camera* cam, SceneManager* sMgr, Root* mRoot )
: ExampleFrameListener(win, cam)
{
mSceneMgr = sMgr;
// create OgreOde World - from SimpleScenes demo
world = new OgreOde::World(mSceneMgr);
world->setGravity(Vector3(0,-9.80665,0));
world->setCFM(10e-5);
world->setERP(0.8);
world->setAutoSleep(true);
world->setContactCorrectionVelocity(1.0);
world->setCollisionListener(this);
world->setAutoSleepAngularThreshold(OgreOde::Utility::Infinity);
// Create something that will step the world, but don't do it automatically
stepper = new OgreOde::ForwardFixedQuickStepper(0.01);
//stepper = new OgreOde::ExactVariableQuickStepper(_time_step);
stepper->setAutomatic(OgreOde::Stepper::AutoMode_NotAutomatic,mRoot);
stepper->setStepListener(this);
//create_ODE_Terrain("yourTerrainFile.mesh",position,size);
create_ODE_Terrain("HutTown.mesh",Vector3::ZERO,Vector3(10,10,10));
//Entity* ent = mSceneMgr->createEntity("yourEntityName", "yourEntityFile.mesh");
Entity* ent = mSceneMgr->createEntity("yourEntityName", "ninja.mesh");
SceneNode* entNode = mSceneMgr->getRootSceneNode()->createChildSceneNode( "yourEntityNodeName", Vector3(0,20,0) );
entNode->attachObject(ent);
entNode->setScale(0.05,0.05,0.05);
create_ODE_character( "yourEntityName", entNode );
// attach and position the camera
entNode->attachObject(cam);
cam->setPosition(0,20,150);
}
~MyListener()
{
// destroy to remove the characters, terrain and stepper
// before destroying the OgreOde world
destroy_ALL_ODE_characters();
if(terrainTriMeshGeom) delete terrainTriMeshGeom;
delete stepper;
stepper = NULL;
delete world;
world = NULL;
}
bool frameStarted(const FrameEvent& evt)
{
mInputDevice->capture();
// quit app
if( mInputDevice->isKeyDown( Ogre::KC_ESCAPE ) ) return false;
// basic movement
Vector3 direction;
if( mInputDevice->isKeyDown(Ogre::KC_W) )
{
direction = ode_characters[0].charNode->getWorldOrientation() * Ogre::Vector3::NEGATIVE_UNIT_Z;
ode_characters[0].charNode->translate( direction * (evt.timeSinceLastFrame * 100) );
}
if( mInputDevice->isKeyDown(Ogre::KC_S) )
{
direction = ode_characters[0].charNode->getWorldOrientation() * Ogre::Vector3::UNIT_Z;
ode_characters[0].charNode->translate( direction * (evt.timeSinceLastFrame * 100) );
}
if( mInputDevice->isKeyDown(Ogre::KC_A) )
{
ode_characters[0].charNode->rotate( Ogre::Vector3::UNIT_Y, (Radian(0.1)) * (evt.timeSinceLastFrame * 100) );
}
if( mInputDevice->isKeyDown(Ogre::KC_D) )
{
ode_characters[0].charNode->rotate( Ogre::Vector3::UNIT_Y, (Radian(-0.1)) * (evt.timeSinceLastFrame * 100) );
}
simulatePhysics(evt);
return true;
}
bool frameEnded(const FrameEvent& evt)
{
return ExampleFrameListener::frameEnded(evt);
}
// OgreOde::CollisionListener function
bool collision(OgreOde::Contact* contact)
{
// search through ode_characters and adjust each charNode's height
for( std::vector< ODE_CHAR_INFO >::iterator it = ode_characters.begin(); it != ode_characters.end(); it++ )
{
if( contact->getFirstGeometry()->getID() == it->charRay->getID() ||
contact->getSecondGeometry()->getID() == it->charRay->getID() )
{
it->charNode->setPosition(contact->getPosition());
break;
}
}
return true;
}
// OgreOde::StepListener function
bool preStep(Real time)
{
return true;
}
// reposition OgreOde Ray(s)
void simulatePhysics( const FrameEvent &evt )
{
stepper->step(evt.timeSinceLastFrame);
world->synchronise();
Vector3 position;
for( std::vector< ODE_CHAR_INFO >::iterator it = ode_characters.begin(); it != ode_characters.end(); it++ )
{
// raise desired ray position a little above character's scenenode
position = it->charNode->getPosition();
// may need to raise it higher for better accuracy
position.y += (it->radius * 2);
// fire ray downward
it->charRay->setDefinition(position,Vector3::NEGATIVE_UNIT_Y);
// add ray to collisionListener
it->charRay->collide(terrainTriMeshGeom,this);
}
}
// Create the tri-mesh terrain from a mesh
void create_ODE_Terrain( std::string meshFile, Vector3 position, Vector3 size )
{
SceneNode* sn = mSceneMgr->getRootSceneNode()->createChildSceneNode("TerrainNode",position);
Entity* ent = mSceneMgr->createEntity("Terrain",meshFile.c_str());
sn->attachObject(ent);
sn->setScale(size);
OgreOde::EntityInformer ei(ent,sn->_getFullTransform());
terrainTriMeshGeom = ei.createStaticTriangleMesh(OgreOde::World::getSingletonPtr()->getDefaultSpace());
terrainTriMeshGeom->setRayListener(this);
}
// create the character object
void create_ODE_character( std::string name, SceneNode* baseNode )
{
AxisAlignedBox aab = baseNode->getAttachedObject(name.c_str())->getBoundingBox();
Vector3 min = aab.getMinimum()*baseNode->getScale();
Vector3 max = aab.getMaximum()*baseNode->getScale();
Vector3 size(fabs(max.x-min.x),fabs(max.y-min.y),fabs(max.z-min.z));
float radius = (size.x > size.z) ? size.z/2.0 : size.x/2;
// create ray for character
// associate node with array. Store data
ODE_CHAR_INFO c;
c.charName = name;
c.charNode = baseNode;
// may want to adjust the length of the ray for accuracy
c.charRay = new OgreOde::RayGeometry(100);
c.radius = radius;
ode_characters.push_back(c);
}
void destroy_ODE_character( std::string name )
{
for( std::vector< ODE_CHAR_INFO >::iterator it = ode_characters.begin(); it != ode_characters.end(); it++ )
{
if( it->charName == name )
{
it->charNode = NULL;
delete it->charRay;
ode_characters.erase(it);
break;
}
}
}
void destroy_ALL_ODE_characters()
{
for( std::vector< ODE_CHAR_INFO >::iterator it = ode_characters.begin(); it != ode_characters.end(); it++ )
{
it->charNode = NULL;
delete it->charRay;
}
ode_characters.clear();
}
private:
std::vector< ODE_CHAR_INFO > ode_characters;
OgreOde::World* world;
OgreOde::ForwardFixedQuickStepper* stepper;
SceneManager* mSceneMgr;
OgreOde::TriangleMeshGeometry* terrainTriMeshGeom;
};
// Declare a subclass of the ExampleApplication class
class SampleApp : public ExampleApplication
{
public:
SampleApp() {}
protected:
// Define what is in the scene
void createScene(void) {}
// Create new frame listener
void createFrameListener(void)
{
mFrameListener = new MyListener(mWindow, mCamera, mSceneMgr, mRoot);
mRoot->addFrameListener(mFrameListener);
}
};
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
// Create application object
SampleApp app;
try {
app.go();
} catch( Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
std::cerr << "An exception has occured: " << e.getFullDescription();
#endif
}
return 0;
}
#ifdef __cplusplus
}
#endif
Another note to add: The length of the ray and where it starts (how much above the character) may need to be adjusted to work properly. For example, a Ray length of 1000 and position of 200 above the character should work fine.
I have found strange effects when walking over parts of the mesh which overlap itself, such as a terrain with a built in bridge. One thing to try is to make the bridge as a separate trimesh and see if the character correctly walks over it.


