场景漫游
在浏览整个三维场景时,矩阵变换是非常关键的,通过适当的矩阵变换可以获得各种移动或者渲染效果。因此,在编写自己的场景漫游操作器时,如何作出符合逻辑的矩阵操作器是非常重要的,但这对初学者来说还是有一定难度的。在 OSG 中,已经提供了一个矩阵操作器的康的接口,即为osgGA::MatrixManipulator。在前面讲到的很多操作器都继承自osgGA:MatrixManipulator。
编写一个自己的操作器,需要处理的主要问题如下:
- 鼠标或键盘按下时该怎么处理?
- 如何得到当前的矩阵及其逆矩阵?
- 如何控制当前的速度?
- 是否开启碰撞检测?
- 如何设置出生位置?
这些都是做一些简单场景漫游时需要面对的问题。只有充分理解了读者需要解决什么,才会知道解决需要做什么,至于怎么做只是时间问题,只要读者肯花时间研究源代码,也可以解决。编写自定义场景漫游操作器的主要步骤如下:
- 编写一个继承自osgGA:GUIEventHandler 类的新类
- 重载handlel()及相关矩阵变换函数,注意在handle()中添加合适的事件处理函数,并指定执行相关的动作。
- 进行碰撞检测。碰撞检测的方法有很多,如果读者想达到精确的碰撞检测,可以使用一些经典的物理学引擎如牛顿引擎。在第 8.2.5节的示例中只是使用一种非常简单的碰撞检测方法如图8-17所示:
图8-17简单碰撞检测
- 关联该操作器到当前视图场景中,没有这一步,在OSG 的场中是不会自动启动该操作器的,关联很简单,代码如下:
viewer->setCameraManipulator(camera):;
通过学习上面的简单步骤,相信读者也可以完成一个操作器的编写,只要明白原理是如何实现的,结果或许就不那么重要了。下面还是看一下示例,不然可能会不懂其中的一些细节。
自定义操作器场景漫游示例
自定义操作器场景漫游示例的代码如程序清单 8-8 所示
/******************************************* 自定义漫游器示例 *************************************/
/*编码时遇到无法打开文件osgGA / MatrixManipulator错误,无法打开包括文件 : “osgGA / MatrixManipulator” : No such file or directory解决办法:新版本中已经改名为CameraManipulator将MatrixManipulator改成CameraManipulator即可并且要#include <osgGA/CameraManipulator>
*/
class TravelManipulator : public osgGA::CameraManipulator
{
public:// 构造函数TravelManipulator();// 析构函数~TravelManipulator(void);// 把漫游加入到场景中static TravelManipulator *TravelToScene(osg::ref_ptr<osgViewer::Viewer> viewer);private:osg::ref_ptr<osgViewer::Viewer> m_pHostViewer;// 移动速度float m_fMoveSpeed;osg::Vec3 m_vPosition;osg::Vec3 m_vRotation;public:// 鼠标左键是否按下bool m_bLeftButtonDown;// 鼠标XYfloat 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<osgViewer::Viewer> 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;}// Rightif (ea.getKey() == 0xFF53){m_vRotation._v[2] -= osg::DegreesToRadians(m_fAngle);}// Leftif (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<osg::LineSegment> lineXY = new osg::LineSegment(newPos1, m_vPosition);osg::ref_ptr<osg::LineSegment> 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<osgViewer::Viewer> viewer = new osgViewer::Viewer();// 把漫游器加入到场景中TravelManipulator::TravelToScene(viewer.get());osg::ref_ptr<osg::Group> root = new osg::Group();// 读取地形模型string strDataPath = strDataFolder + "lz.osg";osg::ref_ptr<osg::Node> 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自定义操作器场景漫游示例截图