ThreeDeeObjectPicking         3D object picking and dragging
Print

Example 1

This could be used for 3D object picking from a Win32 aplication, I use it from a OnMouseMove callback :

RECT rc;
 this->GetClientRect(&rc);
 int width    = rc.right - rc.left;
 int height    = rc.bottom - rc.top;
 float tx = (float)(1.0f / width) * cpt.x;
 float ty = (float)(1.0f / height) * cpt.y;
 Camera *pCam = ogreEngine::getSingletonPtr()->getCurrentCamera();
 // calc the ray using normalised screen coordinates [0-1]
 Ray ray = pCam->getCameraToViewportRay(tx - 0.5, 0.5 - ty);
 // Set up the ray query
 if (!mpRayQuery)
     mpRayQuery = ogreEngine::getSingletonPtr()->getSceneManager()->createRayQuery(ray);
 else
     mpRayQuery->setRay(ray); // update ray
 // Sort by distance, and say we're only interested in the first hit
 mpRayQuery->setSortByDistance(true, 1);
 // Execute
 RaySceneQueryResult res = mpRayQuery->execute();
 RaySceneQueryResult::iterator it = res.begin();
 if (it != res.end())
 {
     mCurrMO = it->movable;
     this->updateProgressListeners("mouse : " + mCurrMO->getName());
 }
 else
 {
     this->updateProgressListeners("mouse : " + StringConverter::toString(tx) + "x" + StringConverter::toString(ty));
     mCurrMO = NULL;
 }
 mpRayQuery->clearResults();

 

Example 2

Quick background: I was trying to do some simple 3D object-picking-and-dragging, and the code sample above helped me a lot; also, this forum thread(external link) helped me a lot. But there were a few hitches that I had to overcome even with those code samples, so I offer my version of 3D object picking & dragging, with an explanation for my rationale.

Note: my code is fairly sloppy, so don't expect the best design decisions to be reflected in it. :-)

Picking

void MyClassName::MouseClicky(unsigned int absoluteX, unsigned int absoluteY) {
  float width = (float) this->mCam->getViewport()->getActualWidth(); // viewport width
  float height = (float) this->mCam->getViewport()->getActualHeight(); // viewport height
 
  Ray ray = this->mCam->getCameraToViewportRay((float) absoluteX / width, (float) absoluteY / height);
 
  // Set up the ray query - you will probably not want to create this every time
  RaySceneQuery * rq = this->mSceneMan->createRayQuery(ray);
 
  // Sort by distance, and say we're only interested in the first hit; also, only pick entities
  rq->setSortByDistance(true, 1);
  rq->setQueryTypeMask(Ogre::SceneManager::ENTITY_TYPE_MASK);
 
  // Execute
  RaySceneQueryResult res = rq->execute();
  RaySceneQueryResult::iterator it = res.begin();
 
  // these two things should probably be encapsulated in their own class/struct
  this->mSelectedEntity = NULL;
  this->mSelectedEntityDist = 0.0f; 
 
  if (it != res.end())
  {
    this->mSelectedEntity = it->movable;
    this->mSelectedEntityDist = it->distance;
    printf("clicked: %s\n", this->mSelectedEntity ->getName().c_str());
  }
  else 
  {
    printf("cleared selection.\n");
  }
 
  this->mSceneMan->destroyQuery(rq);
}

 
It's pretty much the same as Example 1, but I made a few changes:

  1. When using the getCameraToViewportRay function, I left out the 0.5 constant arithmetic (it wasn't applicable in my case and was giving me strange results.)
  2. I added a line to make it so that the ray will only register hits with entities. This is because I had a lot of other junk in my scene, like particles and a skybox, and I wanted to make sure those were not being selected.
  3. I saved the distance from the viewport to the selected entity; this will be useful for dragging, which is next!

 

Dragging

void MyClassName::Mousedraggy(unsigned int x, unsigned int y) // these are absolute screen coords, like in picking
{
  if(this->mSelectedEntity) 
  {
    SceneNode * p = this->mSelectedEntity->getParentSceneNode();
    if(p){
      // Should make this a separate function that returns a Ray instead of copying and pasting code
      int width = this->mCam->getViewport()->getActualWidth();
      int height = this->mCam->getViewport()->getActualHeight();
 
      Ray ray = this->mCam->getCameraToViewportRay((float) x / width, (float) y / height);
      Vector3 pos = ray.getPoint(this->mSelectedEntityDist);
      pos.y = 0;
 
      p->setPosition(pos);
    }
  }
}

 
This is pretty much the same code as the example I linked to in the forum thread above (this one(external link)). Some differences:

  1. I use the entity distance in the ray.getPoint function instead of the camera's near clip plane distance. The getPoint function gives you the point on the ray at the specified length down the ray. This will give you a better 1-to-1 mouse-movement-to-entity-movement (it did for me anyway).
  2. I zeroed out the Y component of the final position, because I didn't want my entities to move up or down, but you may not want to do that.

 

Conclusion

I hope this clarified some things and comes in handy for some people! :-D


Contributors to this page: gr1ever1 points  , Spacegaier4386 points  and jacmoe133512 points  .
Page last modified on Thursday 22 of March, 2012 14:30:23 UTC by gr1ever1 points .


The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.