场景漫游
在浏览整个三维场景时,矩阵变换是非常关键的,通过适当的矩阵变换可以获得各种移动或者渲染效果。因此,在编写自己的场景漫游操作器时,如何作出符合逻辑的矩阵操作器是非常重要的,但这对初学者来说还是有一定难度的。在 OSG 中,已经提供了一个矩阵操作器的康的接口,即为osgGA::MatrixManipulator。在前面讲到的很多操作器都继承自osgGA:MatrixManipulator。
编写一个自己的操作器,需要处理的主要问题如下:
这些都是做一些简单场景漫游时需要面对的问题。只有充分理解了读者需要解决什么,才会知道解决需要做什么,至于怎么做只是时间问题,只要读者肯花时间研究源代码,也可以解决。编写自定义场景漫游操作器的主要步骤如下:

图8-17简单碰撞检测
viewer->setCameraManipulator(camera):;
通过学习上面的简单步骤,相信读者也可以完成一个操作器的编写,只要明白原理是如何实现的,结果或许就不那么重要了。下面还是看一下示例,不然可能会不懂其中的一些细节。
自定义操作器场景漫游示例
自定义操作器场景漫游示例的代码如程序清单 8-8 所示
- /******************************************* 自定义漫游器示例 *************************************/
- /*
- 编码时遇到无法打开文件osgGA / MatrixManipulator错误,
- 无法打开包括文件 : “osgGA / MatrixManipulator” : No such file or directory
- 解决办法:
- 新版本中已经改名为CameraManipulator
- 将MatrixManipulator改成CameraManipulator即可
- 并且要#include
- */
- class TravelManipulator : public osgGA::CameraManipulator
- {
- public:
- // 构造函数
- TravelManipulator();
-
- // 析构函数
- ~TravelManipulator(void);
-
- // 把漫游加入到场景中
- static TravelManipulator *TravelToScene(osg::ref_ptr
viewer) ; -
- private:
- osg::ref_ptr
m_pHostViewer; -
- // 移动速度
- float m_fMoveSpeed;
- osg::Vec3 m_vPosition;
- osg::Vec3 m_vRotation;
-
- public:
- // 鼠标左键是否按下
- bool m_bLeftButtonDown;
-
- // 鼠标XY
- float m_fpushY;
- float m_fpushX;
-
- // 设置矩阵
- virtual void setByMatrix(const osg::Matrixd &matrix);
-
- // 设置逆矩阵
- virtual void setByInverseMatrix(const osg::Matrixd &matrix);
-
- // 得到矩阵
- virtual osg::Matrixd getMatrix(void) const;
-
- // 得到逆矩阵
- virtual osg::Matrixd getInverseMatrix(void)const;
-
-
- // 事件处理函数
- virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa);
-
- // 屏幕角度
- float m_fAngle;
-
- // 位置变换函数
- void ChangePosition(osg::Vec3 &delta);
-
- // 碰撞检测是否开启
- bool m_bPeng;
-
- // 设置速度
- float getSpeed();
-
- void setSpeed(float &);
-
- // 设置起始位置
- void SetPosition(osg::Vec3 &position);
-
- osg::Vec3 GetPosition();
- };
-
- void travelManipulator_8_8(const string &strDataFolder);
-
- /******************************************* 自定义漫游器示例 *************************************/
- TravelManipulator::TravelManipulator()
- :m_fMoveSpeed(1.0f)
- , m_bLeftButtonDown(false)
- , m_fpushX(0)
- , m_fAngle(2.5)
- , m_bPeng(true)
- , m_fpushY(0)
- {
- m_vPosition = osg::Vec3(-22.0f, -274.0f, 100.0f);
- m_vRotation = osg::Vec3(osg::PI_2, 0.0f, 0.0f);
- }
-
- TravelManipulator::~TravelManipulator()
- {
- }
-
- // 把漫游器加入到场景中
- TravelManipulator* TravelManipulator::TravelToScene(osg::ref_ptr
viewer) - {
- TravelManipulator *camera = new TravelManipulator;
- viewer->setCameraManipulator(camera);
- camera->m_pHostViewer = viewer;
-
- return camera;
- }
-
- // 设置矩阵
- void TravelManipulator::setByMatrix(const osg::Matrixd& matrix)
- {
-
- }
-
- // 设置逆矩阵
- void TravelManipulator::setByInverseMatrix(const osg::Matrixd& matrix)
- {
-
- }
-
- // 得到矩阵
- osg::Matrixd TravelManipulator::getMatrix()const
- {
- osg::Matrixd mat;
- mat.makeRotate(m_vRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
- m_vRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
- m_vRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
- return mat * osg::Matrixd::translate(m_vPosition);
- }
-
- // 得到逆矩阵
- osg::Matrixd TravelManipulator::getInverseMatrix()const
- {
- osg::Matrixd mat;
- mat.makeRotate(m_vRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
- m_vRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
- m_vRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
- return osg::Matrixd::inverse(mat * osg::Matrixd::translate(m_vPosition));
- }
-
- // 事件处理函数
- bool TravelManipulator::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
- {
- // 得到鼠标的位置
- float mouseX = ea.getX();
- float mouseY = ea.getY();
-
- int iEventType = ea.getEventType();
- switch (iEventType)
- {
- case (osgGA::GUIEventAdapter::KEYDOWN) :
- {
- // 空格键
- if (ea.getKey() == 0x20)
- {
- //us.requestRedraw();
- //us.requestContinuousUpdate(false);
- return true;
- }
- // 上移动
- if (ea.getKey() == 0xFF50)
- {
- ChangePosition(osg::Vec3(0, 0, m_fMoveSpeed));
- return true;
- }
- // 下移动
- if (ea.getKey() == 0xFF57)
- {
- ChangePosition(osg::Vec3(0, 0, -m_fMoveSpeed));
- return true;
- }
- // 增加速度
- if (ea.getKey() == 0x2B)
- {
- m_fMoveSpeed += 1.0f;
- return true;
- }
- // 减少速度
- if (ea.getKey() == 0x2D)
- {
- m_fMoveSpeed -= 1.0f;
- if (m_fMoveSpeed < 1.0f)
- {
- m_fMoveSpeed = 1.0f;
- }
- return true;
- }
- // 前进
- if (ea.getKey() == 0xFF52 || ea.getKey() == 0x57 || ea.getKey() == 0x77)//up
- {
- ChangePosition(osg::Vec3(0, m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0));
- ChangePosition(osg::Vec3(m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0, 0));
-
- return true;
- }
-
- // 后退
- if (ea.getKey() == 0xFF54 || ea.getKey() == 0x53 || ea.getKey() == 0x73)//down
- {
- ChangePosition(osg::Vec3(0, -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0));
- ChangePosition(osg::Vec3(-m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0, 0));
-
- return true;
- }
-
- // 向左
- if (ea.getKey() == 0x41 || ea.getKey() == 0x61)
- {
- ChangePosition(osg::Vec3(0, m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0));
- ChangePosition(osg::Vec3(-m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0, 0));
-
- return true;
- }
-
- // 向右
- if (ea.getKey() == 0x44 || ea.getKey() == 0x64)
- {
- ChangePosition(osg::Vec3(0, m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0));
- ChangePosition(osg::Vec3(m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation._v[2]), 0, 0));
-
- return true;
- }
- // Right
- if (ea.getKey() == 0xFF53)
- {
- m_vRotation._v[2] -= osg::DegreesToRadians(m_fAngle);
- }
- // Left
- if (ea.getKey() == 0xFF51)
- {
- m_vRotation._v[2] += osg::DegreesToRadians(m_fAngle);
- }
- // 改变屏角
- if (ea.getKey() == 0x46 || ea.getKey() == 0x66)//F
- {
- m_fAngle -= 0.2;
-
- return true;
- }
-
- if (ea.getKey() == 0x47 || ea.getKey() == 0x67)//G
- {
- m_fAngle += 0.2;
-
- return true;
- }
-
- return false;
- }
- // 鼠标按下
- case (osgGA::GUIEventAdapter::PUSH):
- {
- if (ea.getButton() == 1)
- {
- m_fpushX = mouseX;
- m_fpushY = mouseY;
-
- m_bLeftButtonDown = true;
- }
- return false;
- }
- // 拖动
- case (osgGA::GUIEventAdapter::DRAG) :
- {
- if (m_bLeftButtonDown)
- {
- m_vRotation._v[2] -= osg::DegreesToRadians(m_fAngle *(mouseX - m_fpushX));
-
- m_vRotation._v[0] += osg::DegreesToRadians(1.1 *(mouseY - m_fpushY));
-
- if (m_vRotation._v[0] >= 3.14)
- {
- m_vRotation._v[0] = 3.14;
- }
-
- if (m_vRotation._v[0] <= 0)
- {
- m_vRotation._v[0] = 0;
- }
- }
-
- return false;
- }
- // 鼠标释放
- case (osgGA::GUIEventAdapter::RELEASE) :
- {
- if (ea.getButton() == 1)
- {
- m_bLeftButtonDown = false;
- }
-
- return false;
- }
- default:
- {
- return false;
- }
- }
- }
-
- // 位置变换函数
- void TravelManipulator::ChangePosition(osg::Vec3 &delta)
- {
- // 碰撞检测
- if (m_bPeng)
- {
- // 得到新的位置
- osg::Vec3 newPos1 = m_vPosition + delta;
- osgUtil::IntersectVisitor ivXY;
-
- // 根据新的位置得到两条线段检测
- osg::ref_ptr
lineXY = new osg::LineSegment(newPos1, m_vPosition); -
- osg::ref_ptr
lineZ = new osg::LineSegment(newPos1 + osg::Vec3(0.0f, 0.0f, 10.0f), newPos1 - osg::Vec3(0.0f, 0.0f, -10.0f)); -
- ivXY.addLineSegment(lineZ.get());
- ivXY.addLineSegment(lineXY.get());
-
- // 结构交集检测
- m_pHostViewer->getSceneData()->accept(ivXY);
-
- // 如果没有碰撞检测
- if (!ivXY.hits())
- {
- m_vPosition += delta;
- }
- }
- else
- {
- m_vPosition += delta;
- }
- }
-
- // 设置速度
- void TravelManipulator::setSpeed(float &sp)
- {
- m_fMoveSpeed = sp;
- }
-
- // 得到当前速度
- float TravelManipulator::getSpeed()
- {
- return m_fMoveSpeed;
- }
-
- // 设置其实的位置
- void TravelManipulator::SetPosition(osg::Vec3 &position)
- {
- m_vPosition = position;
- }
-
- // 得到当前位置
- osg::Vec3 TravelManipulator::GetPosition()
- {
- return m_vPosition;
- }
-
- void travelManipulator_8_8(const string &strDataFolder)
- {
- // 创建Viewer对象,场景浏览器
- osg::ref_ptr
viewer = new osgViewer::Viewer(); -
- // 把漫游器加入到场景中
- TravelManipulator::TravelToScene(viewer.get());
- osg::ref_ptr
root = new osg::Group(); -
- // 读取地形模型
- string strDataPath = strDataFolder + "lz.osg";
- osg::ref_ptr
node = osgDB::readNodeFile(strDataPath); -
- // 添加到场景
- root->addChild(node.get());
-
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
-
- viewer->setSceneData(root.get());
-
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图8-18所示:

图8-18自定义操作器场景漫游示例截图