History: TriangleMeshRayListener Demo
Source of version: 8 (current)
Copy to clipboard
{img src="img/wiki_up/ODE_logo2.png" alt="" imalign="right"}
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!
__Edit: Updated 9-13-2012__
{CODE(wrap="1", colors="c++")} #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;
Ogre::SceneNode* charNode;
OgreOde::RayGeometry* charRay;
// radius used to position ray above actual node position (at feet)
Ogre::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( Ogre::RenderWindow* win, Ogre::Camera* cam
, Ogre::SceneManager* sMgr, Ogre::Root* mRoot )
: ExampleFrameListener( win, cam )
{
mSceneMgr = sMgr;
// create OgreOde World - from SimpleScenes demo
world = new OgreOde::World( mSceneMgr );
world->setGravity( Ogre::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::StepHandler(
world
, OgreOde::StepHandler::QuickStep
, Ogre::Real( 0.01 ) // Step size
, Ogre::Real( 1.0 / 4 ) // Max interval
, Ogre::Real( 1.0 )); // Time scale
stepper->setStepListener( this );
//create_ODE_Terrain( "yourTerrainFile.mesh", position, size );
create_ODE_Terrain( "racingcircuit.mesh"
, Ogre::Vector3::ZERO
, Ogre::Vector3( 10, 10, 10 ));
/*Entity* ent = mSceneMgr->createEntity( "yourEntityName"
, "yourEntityFile.mesh" ); */
Ogre::Entity* ent = mSceneMgr->createEntity( "Ninja"
, "ninja.mesh" );
Ogre::SceneNode* entNode = mSceneMgr->getRootSceneNode()->
createChildSceneNode( "NinjaNode"
, Ogre::Vector3( 0, 15, 0 ));
entNode->attachObject( ent );
entNode->setScale( 0.05, 0.05, 0.05 );
create_ODE_character( "Ninja", entNode );
// attach and position the camera
entNode->attachObject( cam );
cam->setPosition( 0, 20, 150 );
}
~MyListener( void )
{
// 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 Ogre::FrameEvent& evt )
{
//mKeyboard->capture();
// quit app
if( mKeyboard->isKeyDown( OIS::KC_ESCAPE )) return false;
// basic movement
Ogre::Vector3 direction;
if( mKeyboard->isKeyDown( OIS::KC_UP ))
{
direction = ode_characters[ 0 ].charNode->
_getDerivedOrientation() *
Ogre::Vector3::NEGATIVE_UNIT_Z;
ode_characters[ 0 ].charNode->translate( direction *
( evt.timeSinceLastFrame * 100 ));
}
if( mKeyboard->isKeyDown( OIS::KC_DOWN ))
{
direction = ode_characters[ 0 ].charNode->
_getDerivedOrientation() * Ogre::Vector3::UNIT_Z;
ode_characters[ 0 ].charNode->translate( direction *
( evt.timeSinceLastFrame * 100 ));
}
if( mKeyboard->isKeyDown( OIS::KC_LEFT ))
{
ode_characters[ 0 ].charNode->rotate(
Ogre::Vector3::UNIT_Y, ( Ogre::Radian( 0.1 )) *
( evt.timeSinceLastFrame * 100 ));
}
if( mKeyboard->isKeyDown( OIS::KC_RIGHT ))
{
ode_characters[ 0 ].charNode->
rotate( Ogre::Vector3::UNIT_Y
, ( Ogre::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() )
{
// Clamp the charNode to the terrain on ray contact.
Ogre::Vector3 pos = it->charNode->getPosition();
it->charNode->setPosition( Ogre::Vector3(
pos.x
, contact->getPosition().y + 1
, pos.z ));
//it->charNode->setPosition( contact->getPosition() );
break;
}
}
return true;
}
// OgreOde::StepListener function
bool preStep( Ogre::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 );
//position.y += ( it->radius );
// fire ray downward
it->charRay->setDefinition( position
, Ogre::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
, Ogre::Vector3 position, Ogre::Vector3 size )
{
Ogre::SceneNode* sn =
mSceneMgr->getRootSceneNode()->createChildSceneNode(
"TerrainNode", position );
Ogre::Entity* ent =
mSceneMgr->createEntity( "Terrain", meshFile.c_str() );
sn->attachObject( ent );
sn->setScale( size );
OgreOde::EntityInformer ei( ent, sn->_getFullTransform() );
terrainTriMeshGeom =
ei.createStaticTriangleMesh( world
, world->getDefaultSpace() );
terrainTriMeshGeom->setRayListener( this );
}
// create the character object
void create_ODE_character( std::string name
, Ogre::SceneNode* baseNode )
{
Ogre::AxisAlignedBox aab =
baseNode->getAttachedObject(
name.c_str() )->getBoundingBox();
Ogre::Vector3 min = aab.getMinimum() * baseNode->getScale();
Ogre::Vector3 max = aab.getMaximum() * baseNode->getScale();
Ogre::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, world
, world->getDefaultSpace() );
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::StepHandler* stepper;
Ogre::SceneManager* mSceneMgr;
OgreOde::TriangleMeshGeometry* terrainTriMeshGeom;
};
// Declare a subclass of the ExampleApplication class
class SampleApp : public ExampleApplication
{
public:
SampleApp( void ) {}
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
{CODE}
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.