im_tutorial1.cpp
/* ************************************************************** file: im_tutorial1.cpp description: Intermediate tutorial, modified to manage an arbitrary number of points and robots, and a collision avoidance algorithm ****************************************************************/ #include "ExampleApplication.h" #include "singleton.h" #include "im_tutorial1.h" #include <deque> using namespace std; // RobotAnimator //////////////////////////////////////////////////// // programmer: Andres Vogel // description: controller for single robot being // animated // ///////////////////////////////////// // class for animation of robots // // ///////////////////////////////////// // namepsace IntermediateTutorial1 // programmer: Andres Vogel // description: this is the intermediate tutorial //------------------------------------------------------------------------ namespace IntermediateTutorial1 { //-------------------------------------------------------------------------- // class RobotRayDataManager // Based on the Singleton class // class RobotRayDataManager : public IntermediateTutorial1::Singleton<RobotRayDataManager> { friend class Singleton<RobotRayDataManager>; friend class std::auto_ptr<RobotRayDataManager>; private: Ogre::SceneManager* mSceneMgr; Ogre::RaySceneQuery* mRaySceneQuery; RobotRayDataManager() : mRaySceneQuery(0), mSceneMgr(0) { } ~RobotRayDataManager() { } public: Vector3 getRayIntersection(Ogre::Ray ray) { mRaySceneQuery->setRay( ray ); // Execute query RaySceneQueryResult &result = mRaySceneQuery->execute(); RaySceneQueryResult::iterator itr = result.begin( ); // Get results, create a node/entity on the position if ( itr != result.end() && itr->worldFragment ) { return itr->worldFragment->singleIntersection; } return Vector3::ZERO; } Vector3 getViewportRayIntersection(Camera* cam, Real x, Real y) { Ogre::Ray ray = cam->getCameraToViewportRay(x, y); return getRayIntersection(ray); } void clearResults(void) { mRaySceneQuery->clearResults(); } void setSceneMgr(Ogre::SceneManager* sceneMgr) { mSceneMgr = sceneMgr; mRaySceneQuery = mSceneMgr->createRayQuery( Ogre::Ray() ); } Ogre::SceneManager* getSceneMgr() { return mSceneMgr; } }; // // end RobotRayDataManager //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // ---------------------------------------------------------------------- // class RobotAnimator // class for animating robots across the screen. Do not // use new on this class, simply use the createRobot // factory method on the RobotAnimatorManager class // programmer: Andres Vogel class RobotAnimator : public Ogre::UserDefinedObject { // creator defines the creator object for this // class CREATOR_CLASS(RobotAnimatorManager); CREATOR_CLASS(RobotAnimatorManagerMulti); public: // enums, types, etc. enum RobotTypes { RBT_TYPE_NORMAL, RBT_TYPE_USER }; typedef std::list<RobotAnimator*> RobotAnimatorList; typedef std::list<RobotAnimator*>::iterator RobotAnimatorIterator; private: // class instance variables // the entity that represents the robot Ogre::Entity* mEntity; // the state of the robot at a given moment enum RobotState { RBT_BEGIN_WALK, RBT_WALKING, RBT_IDLE } mRobotState; Ogre::SceneNode* mNode; // robots node std::deque<Vector3> mWalkList; // The list of points we are walking to bool mWalking; // whether the robot is walking Ogre::Real mDistance; // the distance to a destination point Ogre::Vector3 mDirection; // the movement direction Ogre::Vector3 mDestination; // the destination vector Ogre::AnimationState* mAnimationState; // the state of the robot animation RobotTypes mRobotType; // sets the type of robot (not used really) Ogre::RaySceneQuery* mRaySceneQuery; // current scene query data Real mWalkSpeed; // the walking speed of the robot // ///////////////////// // constants // // ///////////////////// static const Real DEFAULT_WALK_SPEED; // the walking speed for the robot static const Real AVOIDANCE_FACTOR; // avoidance factor for robots protected: // // private constructor since we don't want just anyone // creating random RobotAnimators! // RobotAnimator(Entity* ent, SceneNode* node) : // initialize instance variables mEntity(ent), mNode(node), mWalkSpeed(DEFAULT_WALK_SPEED), mWalking(false), mRobotType(RBT_TYPE_NORMAL) { if ( nextLocation() ) { // Set walking animation setAnimationState(true, true, "Walk"); mWalking = true; } // if else { setAnimationState(true, true, "Idle"); mWalking = false; } // else } public: // methods ~RobotAnimator() { } Ogre::String getName(void) { return mEntity->getName(); } inline long getTypeID(void) { return mRobotType; } //----------------------------------------------------- // testGround(): make sure robots feet are on the // the ground void testGround(void) { // get current node position Vector3 nodePos = this->getPosition( ); Ray nodeRay( Vector3(nodePos.x, 5000.0f, nodePos.z), Vector3::NEGATIVE_UNIT_Y ); Vector3 vIntersect = RobotRayDataManager::getSingleton(). getRayIntersection(nodeRay); vIntersect.y += 3.0f; mNode->setPosition( vIntersect ); // clear results of the query RobotRayDataManager::getSingleton().clearResults(); } //------------------------------------------------- // testCollide(): test for a local collide between // robots void testCollide(RobotAnimator* rbt) { if(rbt == this) return; Ogre::Vector3 vRbtPos = rbt->getPosition(); Ogre::Vector3 vMyPos = mNode->getPosition(); Ogre::Vector3 totalMovement = Vector3::ZERO; // get the distance, better to be squared (easier to calc) Real tr = (vRbtPos - vMyPos).squaredLength(); // this is kinda a magic number ;) I multiply the inverse // of the distance by an avoidance factor to create a stronger // avoidance vector as things get close enough to collide Real trFactor = (1.0 / tr) * AVOIDANCE_FACTOR; // if we are closer than about 44-45 units ( sqrt[2000] ) if(tr < 2000) { if(vRbtPos.x < vMyPos.x) totalMovement.x = trFactor; if(vRbtPos.x > vMyPos.x) totalMovement.x = -trFactor; if(vRbtPos.z < vMyPos.z) totalMovement.z = trFactor; if(vRbtPos.z > vMyPos.z) totalMovement.z = -trFactor; // add random noise (makes them wiggle a little) totalMovement.x += Ogre::Math::RangeRandom(-0.1, 0.1); totalMovement.z += Ogre::Math::RangeRandom(-0.1, 0.1); mNode->translate(totalMovement); } } //------------------------------------------------------- // testCollide(RobotAnimatorList): test for multiple // robot collisions with this robot void testCollide(RobotAnimatorList robots) { RobotAnimatorIterator rbIterator; // go through each item in the robot list for ( rbIterator = robots.begin( ); rbIterator != robots.end( ); rbIterator++ ) { ( *rbIterator )->testCollide( this ); } } Ogre::Vector3 getPosition(void) { return mNode->getPosition(); } //----------------------------------------------------- // doFrame(): performs the translation actions on the // robot animation void doFrame(const FrameEvent &evt) { if ( mWalking ) { Real move = mWalkSpeed * evt.timeSinceLastFrame; mDistance -= move; // if we reach a waypoint, check for the next location if (mDistance <= 0.0f) { mNode->setPosition( mDestination ); mDirection = Vector3::ZERO; if (! nextLocation( ) ) { // no other locations found, Idle setAnimationState(true, true, "Idle"); mWalking = false; } // if } // if else // if we are inbetween waypoints, move toward the next one { mNode->translate( mDirection * move ); } // else // make sure we are walking on the ground //testGround(); } // play out animation mAnimationState->addTime(evt.timeSinceLastFrame); } void setAnimationState(bool enable, bool loop, Ogre::String state) { mAnimationState = mEntity->getAnimationState( state ); mAnimationState->setLoop( true ); mAnimationState->setEnabled( true ); } void setWalkSpeed(Ogre::Real walk) { mWalkSpeed = walk; } Real getWalkSpeed(void) { return mWalkSpeed; } bool nextLocation( ) { if ( mWalkList.empty() ) return false; mDestination = mWalkList.front( ); // this gets the front of the deque mWalkList.pop_front( ); // this removes the front of the deque mDirection = mDestination - mNode->getPosition( ); mDistance = mDirection.normalise( ); Vector3 src = mNode->getOrientation( ) * Vector3::UNIT_X; // we must zero out all y components to ensure that // the object stays oriented towards a given point src.y = 0; mDirection.y = 0; if ( (1.0f + src.dotProduct( mDirection )) < 0.0001f ) mNode->yaw( Degree(180) ); else { Ogre::Quaternion quat = src.getRotationTo( mDirection ); mNode->rotate( quat ); } // else return true; } // nextLocation // add a location to the robot walk list void addLocation( Ogre::Vector3 loc ) { mWalkList.push_back(loc); if( !mWalking ) { setAnimationState(true, true, "Walk"); mWalking = true; nextLocation(); } } // addLocation }; /* TODO (dreprog#1#): setup avoidance factor to be controlled by accessor functions */ const Real RobotAnimator::DEFAULT_WALK_SPEED = 35.0f; const Real RobotAnimator::AVOIDANCE_FACTOR = 1500.0f; // end class RobotAnimator //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // RobotAnimatorManager // // Maintains a list of robots that are being managed // programmer: Andres Vogel // description: This class manages all the robots // in the class, I chose not to implement // as a seperate factory and manager // because the manager doesn't have // much responsiblity, and I placed // the responsiblity of animating // in the hands of the RobotAnimator // objects themselves, and the manager // simply brokers the call to the // doFrame() methods of the RobotAnimators // class RobotAnimatorManager : public IntermediateTutorial1::Singleton<RobotAnimatorManager> { friend class Singleton<RobotAnimatorManager>; friend class std::auto_ptr<RobotAnimatorManager>; private: // this is the list of robots being managed RobotAnimator::RobotAnimatorList mRobots; // the scene manager managing the current scene Ogre::SceneManager* mSceneMgr; int mRobotCount; static bool sDestroyed; RobotAnimatorManager() : mRobotCount(0) { } // ~RobotAnimatorManager() { } public: ~RobotAnimatorManager() { // clean up all the robots RobotAnimator::RobotAnimatorIterator rbIterator; // go through each item in the robot list for ( rbIterator = mRobots.begin( ); rbIterator != mRobots.end( ); rbIterator++ ) { // update frame information for each robot delete ( *rbIterator ); // test for a collision between robots // ( *rbIterator )->testCollide(mRobots); } } // attempting to call getSingleton after a destroySingleton // will leave you with a null pointer! RobotAnimator* createRobot(Ogre::Entity* ent, Ogre::SceneNode* sn) { RobotAnimator* rbt = new RobotAnimator(ent, sn); // mRob = rbt; mRobots.push_back( rbt ); mRobotCount++; return rbt; } // doFrame(const FrameEvent &evt) // does a frame event for each void doFrame(const FrameEvent& evt) { RobotAnimator::RobotAnimatorIterator rbIterator; // go through each item in the robot list for ( rbIterator = mRobots.begin( ); rbIterator != mRobots.end( ); rbIterator++ ) { // update frame information for each robot ( *rbIterator )->doFrame(evt); // test for a collision between robots ( *rbIterator )->testCollide(mRobots); } } }; // end RobotAnimationManager //------------------------------------------------------------------- //--------------------------------------------------------------------------- // RobotAnimatorManagerMulti (Non-singleton version) // // Maintains a list of robots that are being managed // programmer: Andres Vogel // description: This class manages all the robots // in the class, I chose not to implement // as a seperate factory and manager // because the manager doesn't have // much responsiblity, and I placed // the responsiblity of animating // in the hands of the RobotAnimator // objects themselves, and the manager // simply brokers the call to the // doFrame() methods of the RobotAnimators // class RobotAnimatorManagerMulti { private: // this is the list of robots being managed RobotAnimator::RobotAnimatorList mRobots; // the scene manager managing the current scene Ogre::SceneManager* mSceneMgr; int mRobotCount; static bool sDestroyed; public: RobotAnimatorManagerMulti() : mRobotCount(0) { } // ~RobotAnimatorManager() { } ~RobotAnimatorManagerMulti() { // clean up all the robots RobotAnimator::RobotAnimatorIterator rbIterator; // go through each item in the robot list for ( rbIterator = mRobots.begin( ); rbIterator != mRobots.end( ); rbIterator++ ) { // update frame information for each robot delete ( *rbIterator ); // test for a collision between robots // ( *rbIterator )->testCollide(mRobots); } } // attempting to call getSingleton after a destroySingleton // will leave you with a null pointer! RobotAnimator* createRobot(Ogre::Entity* ent, Ogre::SceneNode* sn) { RobotAnimator* rbt = new RobotAnimator(ent, sn); // mRob = rbt; mRobots.push_back( rbt ); mRobotCount++; return rbt; } // doFrame(const FrameEvent &evt) // does a frame event for each void doFrame(const FrameEvent& evt) { RobotAnimator::RobotAnimatorIterator rbIterator; // go through each item in the robot list for ( rbIterator = mRobots.begin( ); rbIterator != mRobots.end( ); rbIterator++ ) { // update frame information for each robot ( *rbIterator )->doFrame(evt); // test for a collision between robots ( *rbIterator )->testCollide(mRobots); } } }; // // end RobotAnimatorManagerMulti //------------------------------------------------------------------- //--------------------------------------------------------------- // RobotAnimationListener // class RobotAnimatorListener : public ExampleFrameListener { public: // enums, typedefs, etc // typedef std::list<Ogre::Entity*> EntityList; // typedef std::list<Ogre::Entity*>::iterator EntityListIterator; // typedef std::list<Ogre::Entity*> EntityList; // typedef std::list<Ogre::Entity*>::iterator EntityListIterator; // typedef Ogre::MapIterator<EntityNodeMap> EntityNodeMapIterator public: // methods RobotAnimatorListener(RenderWindow* win, Camera* cam, Ogre::SceneManager* sceneMgr) : ExampleFrameListener(win, cam, false, false) { // EntityListIterator elIterator; // for ( elIterator = entlist.begin( ); elIterator != entlist.end( ); // elIterator++ ) // { // mRobots.push_back( // static_cast<RobotAnimator*> // (( *elIterator )->getUserObject()) ); // } mRobotManager = RobotAnimatorManager::getSingletonPtr(); RobotRayDataManager::getSingleton().setSceneMgr(sceneMgr); } ~RobotAnimatorListener() { // delete mRobotManager; } bool frameStarted(const FrameEvent &evt) { mRobotManager->doFrame(evt); return ExampleFrameListener::frameStarted(evt); } RobotAnimator::RobotAnimatorList mRobots; RobotAnimatorManager* mRobotManager; }; // end RobotAnimatorListener //-------------------------------------------------------------------------- } // // end namespace IntermediateTutorial1 //------------------------------------------------------------------------ using namespace IntermediateTutorial1; class MoveDemoApplication : public ExampleApplication { protected: public: MoveDemoApplication() { } ~MoveDemoApplication() { } protected: Entity *mEntity; // The entity of the object we are animating SceneNode *mNode; // The SceneNode of the object we are moving std::deque<Vector3> mWalkList; // A deque containing the waypoints void createScene(void) { // Set the default lighting. mSceneMgr->setAmbientLight( ColourValue( 1.0f, 1.0f, 1.0f ) ); // Create the entity mEntity = mSceneMgr->createEntity( "Robot", "robot.mesh" ); // Create the scene node mNode = mSceneMgr->getRootSceneNode( )-> createChildSceneNode( "RobotNode", Vector3( 0.0f, 0.0f, 25.0f ) ); mNode->attachObject( mEntity ); // add another robot //////////////////////////////////////// Entity* ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" ); // Create the scene node SceneNode* nd2 = mSceneMgr->getRootSceneNode( )-> createChildSceneNode( "RobotNode2", Vector3( 0.0f, 0.0f, 25.0f ) ); nd2->attachObject( ent2 ); nd2->translate(10,0,100); Entity* ent3 = mSceneMgr->createEntity( "Robot3", "robot.mesh" ); // Create the scene node SceneNode* nd3 = mSceneMgr->getRootSceneNode( )-> createChildSceneNode( "RobotNode3", Vector3( 0.0f, 0.0f, 25.0f ) ); nd3->attachObject( ent3 ); nd3->translate(-20,0,-15); Entity* ent4 = mSceneMgr->createEntity( "Robot4", "robot.mesh" ); // Create the scene node SceneNode* nd4 = mSceneMgr->getRootSceneNode( )-> createChildSceneNode( "RobotNode4", Vector3( 0.0f, 0.0f, 25.0f ) ); nd4->attachObject( ent4 ); nd4->translate(200,0,40); Entity* ent5 = mSceneMgr->createEntity( "Robot5", "robot.mesh" ); // Create the scene node SceneNode* nd5 = mSceneMgr->getRootSceneNode( )-> createChildSceneNode( "RobotNode5", Vector3( 0.0f, 0.0f, 25.0f ) ); nd5->attachObject( ent5 ); nd5->translate(-100,0,-30); Entity* ent6 = mSceneMgr->createEntity( "Robot6", "robot.mesh" ); // Create the scene node SceneNode* nd6 = mSceneMgr->getRootSceneNode( )-> createChildSceneNode( "RobotNode6", Vector3( 0.0f, 0.0f, 25.0f ) ); nd6->attachObject( ent6 ); nd6->translate(-200,0,-40); Entity* ent7 = mSceneMgr->createEntity( "Robot7", "robot.mesh" ); // Create the scene node SceneNode* nd7 = mSceneMgr->getRootSceneNode( )-> createChildSceneNode( "RobotNode7", Vector3( 0.0f, 0.0f, 25.0f ) ); nd7->attachObject( ent7 ); nd7->translate(92,0,12); // add another robot //////////////////////////////////////// // ////////////////////////////////////// robot animator code RobotAnimatorManager* rbtMgr = RobotAnimatorManager::getSingletonPtr(); RobotAnimator* rbtAnimator = rbtMgr->createRobot(mEntity, mNode); rbtAnimator->addLocation( Vector3( 550.0f, 0.0f, 50.0f ) ); rbtAnimator->addLocation( Vector3(-100.0f, 0.0f, -200.0f ) ); mEntity->setUserObject( rbtAnimator ); rbtAnimator = rbtMgr->createRobot(ent2, nd2); rbtAnimator->addLocation( Vector3( 600.0, 0.0f, 20.0f ) ); rbtAnimator->addLocation( Vector3(50.0f, 0.0f, -400.0f ) ); rbtAnimator->setWalkSpeed(35.0f); ent2->setUserObject( rbtAnimator ); rbtAnimator = rbtMgr->createRobot(ent3, nd3); rbtAnimator->addLocation( Vector3( -100.0f, 0.0f, 4.0f ) ); rbtAnimator->addLocation( Vector3(200.0f, 0.0f, 500.0f ) ); rbtAnimator->setWalkSpeed(13.0f); ent3->setUserObject( rbtAnimator ); rbtAnimator = rbtMgr->createRobot(ent4, nd4); rbtAnimator->addLocation( Vector3(1000.0f, 0.0f, -300.0f ) ); rbtAnimator->addLocation( Vector3(-304.0f, 0.0f, 200.0f ) ); rbtAnimator->setWalkSpeed(45.0f); ent4->setUserObject( rbtAnimator ); rbtAnimator = rbtMgr->createRobot(ent5, nd5); rbtAnimator->addLocation( Vector3(90.0f, 0.0f, -12.0f ) ); rbtAnimator->addLocation( Vector3(-491.0f, 0.0f, 392.0f ) ); rbtAnimator->setWalkSpeed(120.0f); ent5->setUserObject( rbtAnimator ); rbtAnimator = rbtMgr->createRobot(ent6, nd6); rbtAnimator->addLocation( Vector3(399.0f, 0.0f, -300.0f ) ); rbtAnimator->addLocation( Vector3(192.0f, 0.0f, 600.0f ) ); rbtAnimator->setWalkSpeed(30.0f); ent6->setUserObject( rbtAnimator ); rbtAnimator = rbtMgr->createRobot(ent7, nd7); rbtAnimator->addLocation( Vector3(522.0f, 0.0f, 100.0f ) ); rbtAnimator->addLocation( Vector3(-900.0f, 0.0f, -200.0f ) ); rbtAnimator->setWalkSpeed(90.0f); ent7->setUserObject( rbtAnimator ); // ////////////////////////////////////// end robot animator code // Create the walking list // ////////////////////////////////////// code commented for animator // mWalkList.push_back( Vector3( 550.0f, 0.0f, 50.0f ) ); // ////////////////////////////////////// robot animator code // mWalkList.push_back( Vector3(-100.0f, 0.0f, -200.0f ) ); // mWalkList.push_back( Vector3(-400.0f, 0.0f, -100.0f ) ); // Create objects so we can see movement Entity *ent; SceneNode *node; ent = mSceneMgr->createEntity( "Knot1", "knot.mesh" ); node = mSceneMgr->getRootSceneNode( )->createChildSceneNode( "Knot1Node", Vector3( 0.0f, -10.0f, 25.0f ) ); node->attachObject( ent ); node->setScale( 0.1f, 0.1f, 0.1f ); ent = mSceneMgr->createEntity( "Knot2", "knot.mesh" ); node = mSceneMgr->getRootSceneNode( )->createChildSceneNode( "Knot2Node", Vector3( 550.0f, -10.0f, 50.0f ) ); node->attachObject( ent ); node->setScale( 0.1f, 0.1f, 0.1f ); ent = mSceneMgr->createEntity( "Knot3", "knot.mesh" ); node = mSceneMgr->getRootSceneNode( )->createChildSceneNode( "Knot3Node", Vector3(-100.0f, -10.0f,-200.0f ) ); node->attachObject( ent ); node->setScale( 0.1f, 0.1f, 0.1f ); // // ent = mSceneMgr->createEntity( "Knot4", "knot.mesh" ); // node = mSceneMgr->getRootSceneNode( )->createChildSceneNode( "Knot4Node", // Vector3(-400.0f, -10.0f,-100.0f ) ); // node->attachObject( ent ); // node->setScale( 0.1f, 0.1f, 0.1f ); // Set the camera to look at our handywork mCamera->setPosition( 90.0f, 280.0f, 535.0f ); mCamera->pitch( Degree(-30.0f) ); mCamera->yaw( Degree(-15.0f) ); } void createFrameListener(void) { // ////////////////////////////////////// code commented for animator // // mFrameListener= new MoveDemoListener(mWindow, mCamera, mNode, // mEntity, mWalkList); // ////////////////////////////////////// end code commented for animator // ////////////////////////////////////// code added for animator mFrameListener = new RobotAnimatorListener(mWindow, mCamera, mSceneMgr); // ////////////////////////////////////// end code added for animator mFrameListener->showDebugOverlay(true); mRoot->addFrameListener(mFrameListener); } }; #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) #else int main(int argc, char **argv) #endif { // Create application object MoveDemoApplication 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 fprintf(stderr, "An exception has occured: %s\n", e.getFullDescription().c_str()); #endif } return 0; }