OgreNewt Picking

From Newton Wiki
Jump to: navigation, search

Picking objects using OgreNewt

For debugging and playing-around, it's handy to be able to pick things up and throwing them around using your mouse. It has been implemented in OgreNewt tutorials but not in a centralised, reusable way. I created a small class, based on the original sample code, that is easy to use and plug in where needed.

Usage

Just setup your graphics (we need scenemanager) and physics and call this once:

   Picker::getSingletonPtr()->init(sceneManager);

As you can see from the header, you can optionally set maximum distance and force multiplier too.


After you finish, call the following before destroying the scenemanager and physics:

   Picker::getSingletonPtr()->deInit();


And every frame you wish picking would be possible, call:

   Picker::getSingletonPtr()->update(world, camera);


Thats it!

Code

Picker.h

   #ifndef PICKER_H_INCLUDED
   #define PICKER_H_INCLUDED
   
   #include <Ogre.h>
   #include <OgreNewt.h>
   
   class Picker
   {
       public:
       void init(Ogre::SceneManager* sceneMgr, int maxDistance = 100, int pickForce = 10);
       void deInit();
       void update(OgreNewt::World* world, Ogre::Camera* camera);
       
       void dragCallback(OgreNewt::Body* body, Ogre::Real timeStep, int threadIndex);
       
       static Picker* getSingletonPtr();
       
       private:
       bool active;
       bool dragging;
       int maxDist;
       int forceMultiplier;
       
       void removeLine();
       
       Ogre::SceneManager* sceneManager;
       Ogre::Camera* camera;
       Ogre::Vector3 dragPoint;
       Ogre::Real dragDist;
       OgreNewt::Body* dragBody;
       Ogre::SceneNode* dragLineNode;
       Ogre::ManualObject* dragLineObject;
       
       Picker();
       ~Picker();
       Picker(const Picker&);
       Picker& operator = (const Picker&);
   };
   
   #endif // PICKER_H_INCLUDED


Picker.cpp

   #include "Picker.h"
   #include <CEGUI/CEGUI.h>
   #include "InputManager.h"
   
   Picker::Picker()
   {
       active = false;
   }
   
   Picker::~Picker()
   {
       deInit();
   }
   
   void Picker::init(Ogre::SceneManager* sceneMgr, int maxDistance, int pickForce)
   {
       sceneManager = sceneMgr;
       maxDist = maxDistance;
       forceMultiplier = pickForce;
       
       dragLineNode = sceneManager->getRootSceneNode()->createChildSceneNode();
       dragLineObject = new Ogre::ManualObject( "__DRAG_LINES__" );
       
       active = true;
   }
   
   void Picker::deInit()
   {
       if(active)
       {
           if(dragLineNode != NULL)
           {
               dragLineNode->detachAllObjects();
           
               if(dragLineObject != NULL)
               {
                   sceneManager->destroyManualObject(dragLineObject);
                   dragLineObject = NULL;
               }
                  
               dragLineNode->getParentSceneNode()->removeAndDestroyChild(dragLineNode->getName());
               dragLineNode = NULL;
       }
       
       active = false;
       }
   }
   
   void Picker::update(OgreNewt::World* world, Ogre::Camera* cam)
   {
       if(!active || sceneManager == NULL)
       {
           return;
       }
   
       if(!dragging)
       {
           if(InputManager::getSingletonPtr()->getMouse()->getMouseState().buttonDown(OIS::MB_Left))
           {
               Ogre::Vector3 start, end;
               
               // Get absolute mouse position on screen [0 - resulution] and renderer for window size
               CEGUI::Point mouse = CEGUI::MouseCursor::getSingleton().getPosition();
               CEGUI::Renderer* guiRenderer = CEGUI::System::getSingleton().getRenderer();
               
               // Calculate normalised mouse position [0..1]
               Ogre::Real mx = mouse.d_x / guiRenderer->getWidth();
               Ogre::Real my = mouse.d_y / guiRenderer->getHeight();
               
               // Get a world space ray as cast from the camera through a viewport position
               Ogre::Ray camray = cam->getCameraToViewportRay(mx, my);
               
               // Get ray origing and end coordinates
               start = camray.getOrigin();
               end = camray.getPoint(maxDist);
               
               // Cast a ray between these points and check for first hit
               OgreNewt::BasicRaycast* ray = new OgreNewt::BasicRaycast(world, start, end);
               OgreNewt::BasicRaycast::BasicRaycastInfo info = ray->getFirstHit();
               
               // Found a body in the ray's path
               if(info.mBody)
               {
                   Ogre::Vector3 bodyPos;
                   Ogre::Quaternion bodyOrientation;
                   
                   // Store the body position and orientation
                   info.mBody->getPositionOrientation(bodyPos, bodyOrientation);
                   
                   // Calculate hit global and local position (info.mDistance is in the range [0,1])
                   Ogre::Vector3 globalPos = camray.getPoint(maxDist * info.mDistance);
                   Ogre::Vector3 localPos = bodyOrientation.Inverse() * (globalPos - bodyPos);
                   
                   // Change the force callback from the standard one to the one that applies the spring picking force
                   info.mBody->setCustomForceAndTorqueCallback<Picker>(&Picker::dragCallback, this);
                   
                   // Store the relevant picking information
                   dragBody = info.mBody;
                   dragDist = (maxDist * info.mDistance);
                   dragPoint = localPos;
                   
                   // We need the camera too, mark picking active
                   camera = cam;
                   dragging = true;
               }
               
               delete ray;
           }
           
           if(dragLineNode)
           {
               removeLine();
           }
       }
       else
       {
           // Check whether user has released the mouse button
           if(!InputManager::getSingletonPtr()->getMouse()->getMouseState().buttonDown(OIS::MB_Left))
           {
               // Restore body force callback (FIXME: doesnt work it it was set to anything else but the default one)
               dragBody->setStandardForceCallback();
               
               // Reinitialise dragging information
               dragBody = NULL;
               dragPoint = Ogre::Vector3::ZERO;
               dragDist = 0.0;
               dragging = false;
           }
       }
   }
   
   void Picker::dragCallback(OgreNewt::Body* body, Ogre::Real timeStep, int threadIndex)
   {
       // Deinitialise if scenemanager has disappeared
       if(sceneManager == NULL)
       {
           deInit();
           
           return;
       }
       
       // Again, get absolute mouse position on screen [0 - resulution] and renderer for window size
       CEGUI::Point mouse = CEGUI::MouseCursor::getSingleton().getPosition();
       CEGUI::Renderer* guiRenderer = CEGUI::System::getSingleton().getRenderer();
       
       // Calculate normalised mouse position [0..1]
       Ogre::Real mx = mouse.d_x / guiRenderer->getWidth();
       Ogre::Real my = mouse.d_y / guiRenderer->getHeight();
       
       // Get a world space ray as cast from the camera through a viewport position
       Ogre::Ray camray = camera->getCameraToViewportRay(mx, my);
       
       // Get the global position our cursor is at
       Ogre::Vector3 cursorPos = camray.getPoint(dragDist);
       
       Ogre::Quaternion bodyOrientation;
       Ogre::Vector3 bodyPos;
       
       // Now find the global point on the body
       body->getPositionOrientation(bodyPos, bodyOrientation);
       
       // Fint the handle position we are holding the body from
       Ogre::Vector3 dragPos = (bodyOrientation * dragPoint) + bodyPos;
       
       Ogre::Vector3 inertia;
       Ogre::Real mass;
       
       body->getMassMatrix(mass, inertia);
       
       // Calculate picking spring force
       Ogre::Vector3 dragForce = ((cursorPos - dragPos) * mass * forceMultiplier) - body->getVelocity();
       
       // Draw a 3D line between these points for visual effect
       removeLine();
       dragLineObject->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
       dragLineObject->position(cursorPos);
       dragLineObject->position(dragPos);
       dragLineObject->end();
       dragLineNode->attachObject(dragLineObject);
       
       // Add the picking spring force at the handle
       body->addGlobalForce(dragForce, dragPos);
       
       // Add gravity too
       Ogre::Vector3 gravity = Ogre::Vector3(0,-9.8,0) * mass;
       body->addForce(gravity);
   }
       
   void Picker::removeLine()
   {
       // Clear the line between handle and cursor
       dragLineNode->detachAllObjects();
       dragLineObject->clear();
   }
   
   Picker* Picker::getSingletonPtr()
   {
       static Picker instance;
       
       return &instance;
   }

Hope it helps anyone :)