OGRE Wiki
Support and community documentation for Ogre3D
Ogre Forums
ogre3d.org
Log in
Username:
Password:
CapsLock is on.
Remember me (for 1 year)
Log in
Home
Tutorials
Tutorials Home
Basic Tutorials
Intermediate Tutorials
Mad Marx Tutorials
In Depth Tutorials
Older Tutorials
External Tutorials
Cookbook
Cookbook Home
CodeBank
Snippets
Experiences
Ogre Articles
Libraries
Libraries Home
Alternative Languages
Assembling A Toolset
Development Tools
OGRE Libraries
List of Libraries
Tools
Tools Home
DCC Tools
DCC Tutorials
DCC Articles
DCC Resources
Assembling a production pipeline
Development
Development Home
Roadmap
Building Ogre
Installing the Ogre SDK
Setting Up An Application
Ogre Wiki Tutorial Framework
Frequently Asked Questions
Google Summer Of Code
Help Requested
Ogre Core Articles
Community
Community Home
Projects Using Ogre
Recommended Reading
Contractors
Wiki
Immediate Wiki Tasklist
Wiki Ideas
Wiki Guidelines
Article Writing Guidelines
Wiki Styles
Wiki Page Tracker
Ogre Wiki Help
Ogre Wiki Help Overview
Help - Basic Syntax
Help - Images
Help - Pages and Structures
Help - Wiki Plugins
Toolbox
Freetags
Categories
List Pages
Structures
Trackers
Statistics
Rankings
List Galleries
Ogre Lexicon
Comments
History: Pick Drag Drop
View page
Source of version: 6
(current)
__How to make drag&drop objects__ __Note:__ This tutorial includes code and algorithms based on the author's knowledge and may be incorrect or less accurate, so please correct all the problems you find. !!Introduction This is a common question at the Ogre forums, and a very useful tool for those who want to make some kind of editor for their applications. The code snippets that follow are parts of my own application and may not work correctly without small tweakings as I did not tested it outside of my application code. !!Moving widgets First of all, I made an arrow mesh in 3D Studio Max, here is a screenshot of it: {img src="img/wiki_up/move_arrow.png" alt="move_arrow.png"} We have to use this mesh to move the objects, because we need something to intersect with the ray cast we will be using. !!!Creating the move arrow axis In order to move a mesh in all 3 axes, we need 3 arrows like the one on the screenshot. The next code is part of my init method: {CODE(wrap="1", colors="c++")} //move widgets try { //sceneNodes SceneNode* node = mSceneMgr->createSceneNode("move_widget"); SceneNode* node_x = node->createChildSceneNode("move_widget_x");node_x->showBoundingBox(false); SceneNode* node_y = node->createChildSceneNode("move_widget_y");node_y->showBoundingBox(false); SceneNode* node_z = node->createChildSceneNode("move_widget_z");node_z->showBoundingBox(false); node_x->yaw(Degree(90)); node_y->pitch(Degree(-90)); //Materials MaterialPtr blue = MaterialManager::getSingleton().create("blue_widget",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); blue->setSelfIllumination(0,0,1); blue->setAmbient(0,0,0); blue->setSpecular(0,0,0,1); blue->setDiffuse(0.5,0.5,0.5,1); MaterialPtr red = MaterialManager::getSingleton().create("red_widget",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); red->setSelfIllumination(1,0,0); red->setAmbient(0,0,0); red->setSpecular(0,0,0,1); red->setDiffuse(0.5,0.5,0.5,1); MaterialPtr green = MaterialManager::getSingleton().create("green_widget",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); green->setSelfIllumination(0,1,0); green->setAmbient(0,0,0); green->setSpecular(0,0,0,1); green->setDiffuse(0.5,0.5,0.5,1); //Entities Entity* entityZ = mSceneMgr->createEntity("move_widget_z", "arrow.mesh"); Entity* entityX = mSceneMgr->createEntity("move_widget_x", "arrow.mesh"); Entity* entityY = mSceneMgr->createEntity("move_widget_y", "arrow.mesh"); MovablePlane *mPlane; mPlane = new MovablePlane("dummy_plane_x"); mPlane->normal = Vector3::UNIT_Y; MeshManager::getSingleton().createPlane("dummy_plane_x",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,*mPlane, 800, 800, 1, 1, true, 1, 1, 1, Vector3::UNIT_X); mSceneMgr->createEntity( "dummy_plane_x", "dummy_plane_x" ); mSceneMgr->getEntity("dummy_plane_x")->setVisible(false); mPlane = new MovablePlane("dummy_plane_z"); mPlane->normal = Vector3::UNIT_X; MeshManager::getSingleton().createPlane("dummy_plane_z",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,*mPlane, 800, 800, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y); mSceneMgr->createEntity( "dummy_plane_z", "dummy_plane_z" ); mSceneMgr->getEntity("dummy_plane_z")->setVisible(false); mPlane = new MovablePlane("dummy_plane_y"); mPlane->normal = Vector3::UNIT_Z; MeshManager::getSingleton().createPlane("dummy_plane_y",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,*mPlane, 800, 800, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y); mSceneMgr->createEntity( "dummy_plane_y", "dummy_plane_y" ); mSceneMgr->getEntity("dummy_plane_y")->setVisible(false); //ZZ arrows entityZ->setNormaliseNormals(true); entityZ->setCastShadows(false); entityZ->setMaterialName("blue_widget"); node_z->attachObject(entityZ); //XX arrows entityX->setNormaliseNormals(true); entityX->setCastShadows(false); entityX->setMaterialName("red_widget"); node_x->attachObject(entityX); //YY arrows entityY->setNormaliseNormals(true); entityY->setCastShadows(false); entityY->setMaterialName("green_widget"); node_y->attachObject(entityY); } catch(Ogre::Exception e){std::cout << "An exception has occured while creating widgets: " << e.getFullDescription().c_str() << std::endl;} {CODE} {img src="img/wiki_up/axis_arrow.png" alt="axis_arrow.png"} Now we have a {LEX()}SceneNode{LEX} named "move_widget" with 3 other nodes attached, one to each axis. You probably noticed that I also create 3 dummy planes. So the base concept is, you click on an object, the "move_widget" SceneNode appears, and you click on any of the arrows to drag&drop the mesh in the direction you want. But, this approach has a small flaw: As we are using a raycast, we need something to intersect with the ray. In this case we have the arrows, but what happens when you press the mouse button over the arrow, and when you drag it, the mouse leaves the arrow? Well, you lost it, and you cannot continue dragging the object. So I came out with this hack: Attach 2 huge and invisible planes to the arows, so that - when you loose those - you can intersect this planes instead. !!!Raycast function Now, in order to select an object or to move it, I've created a raycast function, that also converts the 2D coordinates to 3D. Using this function you can only select objects by its BoundingBox, maybe later I implement an {LEX()}ODE{LEX} version of this raycast. {CODE(wrap="1", colors="c++")} Ogre::MovableObject* MyListener::getNode(float mouseScreenX, float mouseScreenY) { Ray mouseRay = ogreSystem->getCamera()->getCameraToViewportRay(mouseScreenX,mouseScreenY); mRaySceneQuery->setRay(mouseRay); mRaySceneQuery->setSortByDistance(true); RaySceneQueryResult &result = mRaySceneQuery->execute(); Ogre::MovableObject *closestObject = NULL; Real closestDistance = 100000; Ogre::RaySceneQueryResult::iterator rayIterator; for(rayIterator = result.begin(); rayIterator != result.end(); rayIterator++ ) { if ((*rayIterator).movable !=NULL && closestDistance>(*rayIterator).distance && (*rayIterator).movable->getMovableType() != "TerrainMipMap") { closestObject = ( *rayIterator ).movable; closestDistance = ( *rayIterator ).distance; oldpos = mouseRay.getPoint((*rayIterator).distance); originalPos = oldpos; } } mRaySceneQuery->clearResults(); return closestObject; } {CODE} !!!Selecting an object The next code will be used to select an object. I tried to create a function that adapts the size of the arrow, based on the size of the object selected. {CODE(wrap="1", colors="c++")} void MySystem::selectObjectForEdit(char* idObject, char* type) { try { SceneNode* widget = mSceneMgr->getSceneNode(type); SceneNode* node = mSceneMgr->getSceneNode(idObject); Ogre::Vector3 scale = node->getScale(); if (node->getParent()->getScale()!=Ogre::Vector3::UNIT_SCALE) { scale *= node->getParent()->getScale(); cout << "\033[34m WARNING: ParentNode scaled! \033[0m" << endl; } //size of the editable object Ogre::AxisAlignedBox ax = node->getAttachedObject(idObject)->getBoundingBox(); Ogre::Vector3 min = ax.getMinimum()*scale; Ogre::Vector3 max = ax.getMaximum()*scale; Ogre::Vector3 size(fabs(max.x-min.x),fabs(max.y-min.y),fabs(max.z-min.z)); Ogre::Vector3 center = Vector3((max.x+min.x)/2.0f,(max.y+min.y)/2.0f,(max.z+min.z)/2.0f); float big = (size.x>size.y)?size.x:size.y; big = (size.z>big)?size.z:big; if (big < 1) big = 1; //size of the widgets 60 float size_w = big/60.0f; widget->setScale(2*size_w, 2*size_w, 2*size_w); mSceneMgr->getRootSceneNode()->addChild(widget); widget->setPosition(node->getWorldPosition()+center); widget->setVisible(true); } catch(...) { cout << "\033[31m ERROR! selectObjectForEdit \033[0m" << endl; } } {CODE} Well, when we press the mouse over an object, we will select this object and reposition the "move_widget" to the same position, using the mouse listener: !!!!Adding dummy planes {CODE(wrap="1", colors="c++")} void MyListener::mousePressed(Ogre::MouseEvent* e) { //IF the left mouse button is pressed if(e->getButtonID() == Ogre::MouseEvent::BUTTON0_MASK) { Ogre::MovableObject* nodeM = getNode(e->getX(),e->getY()); if(nodeM != NULL) { name = nodeM->getParentSceneNode()->getName(); if (name=="move_widget_x") { nodeM->getParentSceneNode()->attachObject(mySystem->getSceneManager()->getEntity("dummy_plane_x")); nodeM->getParentSceneNode()->attachObject(mySystem->getSceneManager()->getEntity("dummy_plane_z")); selectedGizmo=1; } else if (name=="move_widget_y") { nodeM->getParentSceneNode()->attachObject(mySystem->getSceneManager()->getEntity("dummy_plane_x")); nodeM->getParentSceneNode()->attachObject(mySystem->getSceneManager()->getEntity("dummy_plane_z")); selectedGizmo=2; } else if (name=="move_widget_z") { nodeM->getParentSceneNode()->attachObject(mySystem->getSceneManager()->getEntity("dummy_plane_x")); nodeM->getParentSceneNode()->attachObject(mySystem->getSceneManager()->getEntity("dummy_plane_z")); selectedGizmo=3; } else mySystem->selectObjectForEdit(name,"move_widget"); } } } {CODE} !!!!Removing dummy planes {CODE(wrap="1", colors="c++")} void MyListener::mouseReleased(Ogre::MouseEvent* e) { try { mySystem->getSceneManager()->getSceneNode("move_widget_x")->detachObject("dummy_plane_x"); }catch(...){} try { mySystem->getSceneManager()->getSceneNode("move_widget_x")->detachObject("dummy_plane_z"); }catch(...){} try { mySystem->getSceneManager()->getSceneNode("move_widget_y")->detachObject("dummy_plane_x"); }catch(...){} try { mySystem->getSceneManager()->getSceneNode("move_widget_y")->detachObject("dummy_plane_z"); }catch(...){} try { mySystem->getSceneManager()->getSceneNode("move_widget_z")->detachObject("dummy_plane_x"); }catch(...){} try { mySystem->getSceneManager()->getSceneNode("move_widget_z")->detachObject("dummy_plane_z"); }catch(...){} } {CODE} !!!Moving the objects And now, we only need to drag the arrows and the object as well. This is done at the mouseDragged() function: {img src="img/wiki_up/objectselected.png" alt="objectselected.png"} {CODE(wrap="1", colors="c++")} void MyListener::mouseDragged (Ogre::MouseEvent *e) { Ray mouseRay = ogreSystem->getCamera()->getCameraToViewportRay(e->getX(),e->getY()); mRaySceneQuery->setRay(mouseRay); mRaySceneQuery->setSortByDistance(true); RaySceneQueryResult &result = mRaySceneQuery->execute(); Ogre::MovableObject *closestObject = NULL; Real closestDistance = 100000; std::list< Ogre::RaySceneQueryResultEntry >::iterator rayIterator; for(rayIterator = result.begin(); rayIterator != result.end(); rayIterator++ ) { if ((*rayIterator).movable !=NULL && closestDistance>(*rayIterator).distance && (*rayIterator).movable->getMovableType() != "TerrainMipMap") { closestObject = ( *rayIterator ).movable; Ogre::String name = closestObject->getName(); switch (selectedGizmo) { case 1: case 2: case 3: if (name!="move_widget_x" && name!="move_widget_y" && name!="move_widget_z" && name!="dummy_plane_x" && name!="dummy_plane_z") return; break; } if (selectedGizmo==1) { SceneNode* widget = closestObject->getParentSceneNode()->getParentSceneNode(); SceneNode* entity = mySystem->getSceneManager()->getSceneNode(selectedObject); Ogre::Vector3 newpos = mouseRay.getPoint((*rayIterator).distance); widget->translate(newpos.x-oldpos.x,0,0); entity->translate(newpos.x-oldpos.x,0,0); oldpos=newpos; } else if (selectedGizmo==2) { SceneNode* widget = closestObject->getParentSceneNode()->getParentSceneNode(); SceneNode* entity = mySystem->getSceneManager()->getSceneNode(selectedObject); Ogre::Vector3 newpos = mouseRay.getPoint((*rayIterator).distance); widget->translate(0,newpos.y-oldpos.y,0); entity->translate(0,newpos.y-oldpos.y,0); oldpos=newpos; } else if (selectedGizmo==3) { SceneNode* widget = closestObject->getParentSceneNode()->getParentSceneNode(); SceneNode* entity = mySystem->getSceneManager()->getSceneNode(selectedObject); Ogre::Vector3 newpos = mouseRay.getPoint((*rayIterator).distance); widget->translate(0,0,newpos.z-oldpos.z); entity->translate(0,0,newpos.z-oldpos.z); oldpos=newpos; } mRaySceneQuery->clearResults(); e->consume(); } {CODE} It is kind of difficult to explain this last snippet, just try to understand it by reading carefully. The most important line may be this one: {CODE(wrap="1", colors="c++")} Ogre::Vector3 newpos = mouseRay.getPoint((*rayIterator).distance); {CODE} You can attach the widget node to your object's node. This way you just have to move the widget, and the object would be dragged too, but because of the rotation method, I needed to make it this way. !!Final Notes * I already have code for scale and rotate widgets, but just because I'm working on something else, and I believe those widgets do not work too well, I decided to post them some other time (shortly). * As I mentioned earlier, this may not work as pasted above, but may need some corrections. --- Alias: (alias(Pick_Drag_Drop))
Search by Tags
Search Wiki by Freetags
Latest Changes
IDE Eclipse
FMOD SoundManager
HDRlib
Building Ogre V2 with CMake
Ogre 2.1 FAQ
Minimal Ogre Collision
Artifex Terra
OpenMB
Advanced Mogre Framework
MogreSocks
...more
Search
Find
Advanced
Search Help
Online Users
131 online users