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!

Edit: Updated 9-13-2012

#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

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.