If you haven't done so already, be sure to visit the Wiki Portal to read about how the wiki works. Especially the Ogre Wiki Overview page.
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.
Contributors to this page: jacmoe
and
OgreWikiBot
.
Page last modified on Tuesday 29 of December, 2009 03:55:56 GMT by jacmoe
.
The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.

