OSG中pick函数的使用

我想用OSG实现点选目标物体,同时得到选中目标的位置,看书上说可以用pick函数来实现这一功能,代码如下:

  1. class CPickHandler :public osgGA::GUIEventHandler  
  2. {  
  3. public:  
  4.     CPickHandler(osgViewer::Viewer* viewer) :mViewer(viewer){}  
  5.     virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)  
  6.     {  
  7.         switch (ea.getEventType())  
  8.         {  
  9.         case(osgGA::GUIEventAdapter::PUSH):  
  10.             if (ea.getButton() == 1)  
  11.             {  
  12.                 Pick(ea.getX(), ea.getY());  
  13.             }  
  14.                 return true;  
  15.         }  
  16.         return false;  
  17.     }  
  18. protected:  
  19.     void Pick(float x, float y)  
  20.     {  
  21.         osgUtil::LineSegmentIntersector::Intersections intersections;  
  22.         if (mViewer->computeIntersections(x, y, intersections))  
  23.         {  
  24.             for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr)  
  25.             {  
  26.                 if (!hitr->nodePath.empty() && !(hitr->nodePath.back()->getName().empty()))  
  27.                 {  
  28.                     const osg::NodePath& np = hitr->nodePath;  
  29.                     for (int i = np.size() - 1; i >= 0; --i)  
  30.                     {  
  31.                         osgFX::Scribe* sc = dynamic_cast(np[i]);  
  32.                         if (sc != NULL)  
  33.                         {  
  34.                             if (sc->getNodeMask() != 0)  
  35.                                 sc->setNodeMask(0);  
  36.                         }  
  37.                     }  
  38.                 }  
  39.             }  
  40.         }  
  41.     }  
  42.     osgViewer::Viewer* mViewer;  
  43. };

这段代码相信大家都很熟悉,其中最重要的是computeIntersections函数,这个函数可以确定当前点击位置是否跟目标物体有交集,从而确定是否点击到了物体。因为我做的是安卓上面的OSG,不知道是不是因为这个原因, computeIntersections这个函数总是false。从源码中找到这个函数,代码如下:
bool View::computeIntersections(float x,float y, osgUtil::LineSegmentIntersector::Intersections& intersections,osg::Node::NodeMask traversalMask)
{
	    if (!_camera.valid()) return false;

	    float local_x, local_y = 0.0;
	    const osg::Camera* camera = getCameraContainingPosition(x, y, local_x, local_y);
	    if (!camera) camera = _camera.get();


	    osgUtil::LineSegmentIntersector::CoordinateFrame cf = camera->getViewport() ? osgUtil::Intersector::WINDOW : osgUtil::Intersector::PROJECTION;
	    osgUtil::LineSegmentIntersector* picker = new osgUtil::LineSegmentIntersector(cf, local_x, local_y);


	    osgUtil::IntersectionVisitor iv(picker);
	    iv.setTraversalMask(traversalMask);
	    const_cast(camera)->accept(iv);

	    if (picker->containsIntersections())
       {
        intersections = picker->getIntersections();
	        return true;
	    }
    else
	    {
	        intersections.clear();
	        return false;
	    }

	    return false;
	}

经过调试,发现是getcameracontainingposition这个函数出现了问题,array大神说这个函数是用来判断给定的xy坐标是否能够被当前viewer的某个摄像机包含,并返回这个摄像机的指针,以及(x,y)在这个摄像机中的局部坐标(local_x,local_y)。下面我们来看一下这个函数的具体代码,因为最近新换了电脑,之前的源码不好找,所以从网上随便找了一份,如下所示:


const osg::Camera* View::getCameraContainingPosition(float x, float y, float& local_x, float& local_y) const
	{
	    const osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState();
	    bool view_invert_y = eventState->getMouseYOrientation()==osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;

	    osg::notify(osg::INFO)<<"View::getCameraContainingPosition("<= (viewport->x()-epsilon) && new_coord.y() >= (viewport->y()-epsilon) &&
	                new_coord.x() < (viewport->x()+viewport->width()-1.0+epsilon) && new_coord.y() <= (viewport->y()+viewport->height()-1.0+epsilon) )
	            {
	                // osg::notify(osg::NOTICE)<<"  in viewport "<x()<<" "<<(viewport->x()+viewport->width())<
看到这份代码的我是崩溃的!为什么呢!因为!这份源码跟我当时用的不一样,我当时用的那份会在程序开始的时候获取一下gw,

const osgViewer::GraphicsWindow*gw =dynamic_cast(eventState->getGraphicsContext());

然后判断gw是否获取成功,如果不成功,则会报错。我之前一直出错就是因为这个原因。

仔细分析一下getcameracontainingposition这个函数具体做了什么,其实很简单,主要是坐标的转换。因为当前场景可能存在不止一个摄像机,所以,要判断一下当前是主摄像机还是从摄像机。这个函数的输入x,y应该是范围(-1,1)的坐标,而输出应该是屏幕坐标。所以理解了这个以后,可以直接从系统中获取屏幕坐标,这样就不需要经过getcameracontainingposition这个函数了。

在computeIntersections这个函数中,getcameracontainingposition之后会 选择cf,cf是来选择输入的模式,可以是WINDOW,也可以PROJECTION,前者是屏幕坐标,后者是世界坐标。

所以最后我的实现过程的代码如下:

osgUtil::LineSegmentIntersector::Intersections intersections;//求交类
                bool view_invert_y =
                        ea.getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;
                osg::ref_ptr picker = new osgUtil::LineSegmentIntersector(
                        osgUtil::Intersector::WINDOW, ea.getX(), ea.getY());//Window参数的意思为输入是屏幕坐标,即ea.getX(), ea.getY()是屏幕坐标(1920,852)
                osgUtil::IntersectionVisitor iv(picker.get());
                iv.setTraversalMask(0xffffffff);
                camera->accept(iv);
                if (picker->containsIntersections()) {
                    intersections = picker->getIntersections();//intersections里面存放了所有触碰到的节点
                } else {
                    intersections.clear();
                }

                    if (intersections.size() != 0) {//如果点击到了物体
                        osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin();//因为我们只需要得到我们点击到离我们最近的那个物体,所以只得到begin就可以
                        eye = (*hitr).drawable->getBound().center();//得到点击到的物体的中心坐标
                     }
 
最后得到的eye就是点击到的物体的中心。

你可能感兴趣的:(OSG中pick函数的使用)