Table of contents
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