Print

Image
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: jacmoe133512 points  and OgreWikiBot .
Page last modified on Tuesday 29 of December, 2009 03:55:56 UTC by jacmoe133512 points .


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.