在3D世界中,通过鼠标拾取拖拽物体是一个神奇事情,它的具体实现方法如下:
- 将鼠标点击视口的二维坐标转换成3D世界中的三维坐标。
- 视点出发到鼠标点击位置的可形成一条射线,在场景节点树上遍历,查找与射线相交的节点。
- 获得拾取节点与视点之间距离,从而计算得到鼠标释放时拾取节点的位置,平移拾取节点到目标位置。
2D坐标转3D坐标参考
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)); }
|
参考链接
- OSG实现鼠标拖拽物体,by 码农家园.
- OSG拾取对应的实体,by 成魔的羔羊.