OgreNewt Picking        

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 :-)

-- kallas