OSG实现鼠标拖拽物体


首先要具备射线拾取的基本知识和向量运算的基础知识,屏幕点转换为三维点怎么取转换。
现在先看一下屏幕的点如何转换为三维点,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
osg::Vec3 screenToWorld(osgViewer::Viewer* viewer,double dx,double dy)
{
    osg::Camera *camera = viewer->getCamera();
    osg::Matrix viewMat = camera->getViewMatrix(); //获取当前视图矩阵
    osg::Matrix projMat = camera->getProjectionMatrix();//获取投影矩阵
    osg::Matrix windMat = camera->getViewport()->computeWindowMatrix();//获取窗口矩阵
    osg::Matrix MVPW = viewMat * projMat *windMat;  //视图-》投影-》窗口变换

    osg::Matrix inverseMVPW = osg::Matrix::inverse(MVPW);  
    osg::Vec3 mouseWorld = osg::Vec3(dx, dy, 0) * inverseMVPW;  
    return mouseWorld;
}

好了,上述代码完成从屏幕的二维点变换到世界的三维点,接下来实现以下鼠标点拾取物体,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//参数说明:firstPos:是当前摄像机的位置。endPos:为偏移坐标值,eye + curRayLineDir*100
//curRayLineDir = mouseWorldPos(屏幕点转三维点使用上面的函数) - eye;
//curRayLineDir.normallize();
void CIntersectEventHandler::rayLinePick(const osg::Vec3& firstPos,const osg::Vec3& endPos)
{
    osg::ref_ptr<osgUtil::LineSegmentIntersector> lineSegmentIntesector = \
        new osgUtil::LineSegmentIntersector(firstPos,endPos);
    osgUtil::IntersectionVisitor intersectionVisitor(lineSegmentIntesector);

    m_matNode->accept(intersectionVisitor);//m_matNode为你拾取的物体

    osgUtil::LineSegmentIntersector::Intersections intersections;
    if (lineSegmentIntesector->containsIntersections())
    {
        intersections = lineSegmentIntesector->getIntersections();
        for(auto iter = intersections.begin(); iter != intersections.end(); ++iter)
        {
            osg::NodePath nodePath = iter->nodePath;
            m_pickPoint = iter->getWorldIntersectPoint();
            for(int i=0; i<nodePath.size(); ++i)
            {
                m_pickObj = dynamic_cast<osg::MatrixTransform*>(nodePath[i]);//拾取到的node
            }
        }
    }
}

最后我要计算当前的鼠标拾取到物体之后移动的位置。代码走你:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//我是在osgGA::GUIEventAdapter::DRAG进行拖拽的功能
            if(m_pickObj && m_bLeftMouseBtn)//这个布尔值就是晓得鼠标的左键是否按下了。
            {
                //获取当前的摄像机的位置
                osg::Vec3 eye = viewer->getCamera()->getInverseViewMatrix().getTrans();
                //计算当前摄像机与pick到的模型之间的距离是多少
                osg::Vec3 offset = m_pickPoint - eye;
                int dist = offset.length();
                //计算当前的鼠标屏幕点映射到三维中的值
                osg::Vec3 mouseWorldPos = screenToWorld(viewer,ea.getX(),ea.getY());
                //计算当前鼠标三维点与摄像机的方向
                osg::Vec3 rayDir = mouseWorldPos - eye;
                rayDir.normalize();
                //最后计算物体拖拽时最终的世界位置
                osg::Vec3 curPos = eye + rayDir*dist;
                m_pickObj->setMatrix(osg::Matrix::translate(curPos));
            }

好了,拖拽功能完成,若有不足之处还请指出更正!
下面是演示效果:
在这里插入图片描述