OSG实现鼠标拖拽物体

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

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;
}

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

//参数说明: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
			}
		}
	}
}

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

//我是在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));
			}

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

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
在 OpenSceneGraph 中,可以使用 osgGA::TrackballManipulator 类来实现圆柱拖拽器旋转场景的功能。具体步骤如下: 1. 创建 TrackballManipulator 对象,例如: ``` osg::ref_ptr<osgGA::TrackballManipulator> manipulator = new osgGA::TrackballManipulator(); ``` 2. 将该 Manipulator 对象设置为 Viewer 的事件处理程序,例如: ``` viewer->setCameraManipulator(manipulator.get()); ``` 3. 应用圆柱拖拽器,可以使用 osgGA::GUIEventHandler 类和 osgGA::GUIEventAdapter 类来实现。例如: ``` class CylinderDraggerEventHandler : public osgGA::GUIEventHandler { public: CylinderDraggerEventHandler(osg::ref_ptr<osgGA::TrackballManipulator> manipulator) : _manipulator(manipulator) {} virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { switch (ea.getEventType()) { case osgGA::GUIEventAdapter::PUSH: { osg::Vec3d startWorld, endWorld; if (_manipulator->computeWorldCoord(ea.getX(), ea.getY(), startWorld) && _manipulator->computeWorldCoord(ea.getX() + ea.getXmax(), ea.getY(), endWorld)) { _startLocal = _manipulator->getInverseMatrix() * startWorld; _endLocal = _manipulator->getInverseMatrix() * endWorld; _axisLocal = _endLocal - _startLocal; _axisLocal.normalize(); return true; } break; } case osgGA::GUIEventAdapter::DRAG: { osg::Vec3d startWorld, endWorld; if (_manipulator->computeWorldCoord(ea.getX(), ea.getY(), startWorld) && _manipulator->computeWorldCoord(ea.getX() + ea.getXmax(), ea.getY(), endWorld)) { osg::Vec3d startLocal = _manipulator->getInverseMatrix() * startWorld; osg::Vec3d endLocal = _manipulator->getInverseMatrix() * endWorld; osg::Vec3d axisLocal = endLocal - startLocal; axisLocal.normalize(); osg::Quat rotation; rotation.makeRotate(_axisLocal, axisLocal); _manipulator->setRotation(_manipulator->getRotation() * rotation); _axisLocal = axisLocal; return true; } break; } } return false; } private: osg::ref_ptr<osgGA::TrackballManipulator> _manipulator; osg::Vec3d _startLocal; osg::Vec3d _endLocal; osg::Vec3d _axisLocal; }; osg::ref_ptr<CylinderDraggerEventHandler> eventHandler = new CylinderDraggerEventHandler(manipulator); viewer->addEventHandler(eventHandler.get()); ``` 这里创建了一个 CylinderDraggerEventHandler 类来处理圆柱拖拽器的事件。在 handle() 函数中,根据事件类型分别实现了圆柱拖拽器的 PUSH 和 DRAG 事件。在 PUSH 事件中,记录圆柱拖拽器起始点和终止点的位置,并计算出拖拽的轴向。在 DRAG 事件中,计算出当前轴向,计算出旋转角度,并设置 Manipulator 的旋转矩阵。 注意,这里使用了 Manipulator 的 computeWorldCoord() 函数来计算出 2D 屏幕坐标对应的 3D 世界坐标。 4. 通过 Manipulator 的 getRotation() 函数获取当前旋转矩阵,即可得到当前的旋转值。 ``` osg::Quat rotation = manipulator->getRotation(); ``` 这里获取的 rotation 为 osg::Quat 类型,其中包含了旋转的四元数信息。可以通过 osg::Quat 的 getRotate() 函数获取旋转轴和旋转角度,并将其转换为欧拉角或其他需要的格式。
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值