Search code examples
c++qtopenscenegraph

Object coordinates


I'm working at QT application that have a OSGWidget. How could i get coordinates of picked object in osg viewer and transfer it to QT textEdit in Dialog window. Could you give advice how to extract osg::Vec3d of selected object from viewer convert it to string and show it off? For example. I have these scene: Scene

And when i click on add button it opens a dialog window where i have a textEdit object. Transfer coords in this editText. How could be this done? Forget to add that this cubes are imported from ads file. Probably it can help in any place.


Solution

  • In order to retrieve an object's coordinates from your scene, you'll need to add new event handler to your viewer. Let's call it a PickHandler.

    Here's a basic code that will get you started. You'll need to add the "includes" and modify it to suit your needs. (Please note that I haven't tested it. I wrote it "by memory", but if there are any errors the should be very easy to fix).

    PickHandler.h

    class PickHandler: public QObject, public osgGA::GUIEventHandler {
      Q_OBJECT
    public:
      PickHandler();
      virtual bool handle( const osgGA::GUIEventAdapter& ea,
                         osgGA::GUIActionAdapter& aa );
      signals:
        void query( osg::Vec3f );
    protected:
      virtual ~PickHandler()
      {
      }
    
      bool pick( const double x, const double y, osgViewer::Viewer* viewer );
    private:
      bool getPickedPoint( const double x, const double y, float buffer,
                         osgViewer::Viewer* viewer, osg::Vec3f& point );
    };
    

    PickHandler.cpp

    PickHandler::PickHandler() : osgGA::GUIEventHandler()
    {
    }
    
    bool PickHandler::handle( const osgGA::GUIEventAdapter &ea,
                          osgGA::GUIActionAdapter &aa )
    {
        osgViewer::View* viewer = dynamic_cast<osgViewer::Viewer*>( &aa );
    
        if( !viewer ) {
          return false;
        }
        switch( ea.getEventType() ) {
          default:
            break;
          case osgGA::GUIEventAdapter::RELEASE: {
            if( pick(ea.getXnormalized(), ea.getYnormalized(),
                     viewer ) )
            {
                return true;
            }
            break;
          }
        }
        return false;
    }
    
    bool PickHandler::pick( const double x, const double y,
                        osgViewer::Viewer* viewer )
    {
    
        if( !viewer->getSceneData() ) {
          return false;
        }
    
        osg::Vec3f point;
        float buffer = 0.005f;
    
        if( getPickedPoint( x, y, buffer, viewer, point ) ) {
          emit query( point );
        }
        return false;
    }
    
    bool PickHandler::getPickedPoint( const double x, const double y, float buffer,
                                  osgViewer::Viewer* viewer,
                                  osg::Vec3f& point )
    {
       osg::ref_ptr<osgUtil::PolytopeIntersector> intersector( 0 );
       try {
            intersector = new osgUtil::PolytopeIntersector(
                    osgUtil::Intersector::PROJECTION,
                    x - buffer, y - buffer, x + buffer,
                    y + buffer )
            ;
        } catch( const std::bad_alloc& ) {
          return false;
        }
    
        // DimZero = check only for points
        intersector->setDimensionMask( osgUtil:: PolytopeIntersector::DimZero );
        // 
        intersector->setIntersectionLimit( osgUtil::Intersector::LIMIT_NEAREST );
        osgUtil::IntersectionVisitor iv( intersector );
        viewer->getCamera()->accept( iv );
    
        if( intersector->containsIntersections() ) {
            osgUtil::PolytopeIntersector::Intersection intersection =
                 *( intersector->getIntersections().begin() )
            ;
    
            const osg::Vec3f& P = intersection.intersectionPoints[ 0 ];
    
            if( P.isNaN() ) {
              return false;
            }
    
            point.set( P[ 0 ], P[ 1 ], P[ 2 ] );
            return true;
        }
    
        return false;
    }
    

    I'm using a PolytopeIntersector, since I don't have any solid models, like the cessna in the OSG's example data; only a lot of points and using a LineIntersector (the fastest) is almost impossible to get a hit. The Polytope will build a frustrum volume intersects with anything in the area you have specified (with the parameters when constructing the Polytope).

    Also, you might need to play with the parameters you send to the pick function, like the buffer size. I use ea.getXNormalized() and inside pick() an osgUtil::Intersector::PROJECTION value.

    If you use, say an osgUtil::Intersector::WINDOW value, you don't need to normalize the mouse values. If you don't have any "strange" transformations in your view, most likely the PROJECTION value is what you need.

    Last thing, this code is rather old. With newer osg versions, maybe some of it will be seen as deprecated. I'm not sure as I haven't updated yet my picker code.

    Now, with this code, when an itersection is found, it retrieves the FIRST one and send the point values via an emit. You just have to connect this emit to your SLOT and receive the cliked point coords.

    Lastly, for converting something to a string, you can use the Qt String function, QString::number(...).

    For example:

    const QString x = QString::number( point[0], 'd', 6 );
    

    will stringify the x-coord of the point using fixed-point format with 6 decimal places.