我想用OSG实现点选目标物体,同时得到选中目标的位置,看书上说可以用pick函数来实现这一功能,代码如下:
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
然后判断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就是点击到的物体的中心。