场景漫游
在浏览整个三维场景时,矩阵变换 是非常关键的,通过适当的矩阵变换可以获得各种移动或者渲染效果。因此,在编写自己的场景漫游操作器时,如何作出符合逻辑的矩阵操作器是非常重要的,但这对初学者来说还是有一定难度的。在 OSG 中,已经提供了一个矩阵操作器的康的接口,即为osgGA::MatrixManipulator 。在前面讲到的很多操作器都继承自osgGA:MatrixManipulator 。
编写一个自己的操作器,需要处理的主要问题如下:
- 鼠标或键盘按下时该怎么处理?
- 如何得到当前的矩阵及其逆矩阵?
- 如何控制当前的速度?
- 是否开启碰撞检测?
- 如何设置出生位置?
这些都是做一些简单场景漫游时需要面对的问题。只有充分理解了读者需要解决什么,才会知道解决需要做什么,至于怎么做只是时间问题,只要读者肯花时间研究源代码,也可以解决。编写自定义场景漫游操作器的主要步骤如下:
- 编写一个继承自osgGA:GUIEventHandler 类的新类
- 重载handlel()及相关矩阵变换函数,注意在handle()中添加合适的事件处理函数,并指定执行相关的动作。
- 进行碰撞检测。碰撞检测的方法有很多,如果读者想达到精确的碰撞检测,可以使用一些经典的物理学引擎如牛顿引擎。在第 8.2.5节的示例中只是使用一种非常简单的碰撞检测方法如图8-17所示:
图8-17简单碰撞检测
- 关联该操作器到当前视图场景中,没有这一步,在OSG 的场中是不会自动启动该操作器的,关联很简单,代码如下:
viewer->setCameraManipulator(camera):;
通过学习上面的简单步骤,相信读者也可以完成一个操作器的编写,只要明白原理是如何实现的,结果或许就不那么重要了。下面还是看一下示例,不然可能会不懂其中的一些细节。
自定义操作器场景漫游示例
自定义操作器场景漫游示例的代码如程序清单 8-8 所示
cpp
/******************************************* 自定义漫游器示例 *************************************/
/*
编码时遇到无法打开文件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;
// 鼠标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<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;
}
// 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<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自定义操作器场景漫游示例截图