TriangleMeshRayListener Demo

From Ogre Wiki

Jump to: navigation, search

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.

Personal tools
administration