场景交互与场景漫游-场景漫游器(6)

场景漫游

在浏览整个三维场景时,矩阵变换 是非常关键的,通过适当的矩阵变换可以获得各种移动或者渲染效果。因此,在编写自己的场景漫游操作器时,如何作出符合逻辑的矩阵操作器是非常重要的,但这对初学者来说还是有一定难度的。在 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自定义操作器场景漫游示例截图

相关推荐
_君莫笑2 小时前
【视频】将yuv420p的一帧数据写入文件
c++·音视频·yuv420p
测试盐3 小时前
c++编译过程初识
开发语言·c++
single5945 小时前
【c++笔试强训】(第四十五篇)
java·开发语言·数据结构·c++·算法
yuyanjingtao5 小时前
CCF-GESP 等级考试 2023年9月认证C++五级真题解析
c++·青少年编程·gesp·csp-j/s·编程等级考试
云飞云共享云桌面6 小时前
如何让企业研发设计团队低配电脑流畅做3D大装配设计?
服务器·3d·电脑
魔法工坊6 小时前
只谈C++11新特性 - 删除函数
java·开发语言·c++
海螺姑娘的小魏7 小时前
Effective C++ 条款 26:尽可能延后变量定义式的出现时间
开发语言·c++
w(゚Д゚)w吓洗宝宝了7 小时前
C++ 环境搭建 - 安装编译器、IDE选择
开发语言·c++·ide
jndingxin8 小时前
OpenCV相机标定与3D重建(37)计算两幅图像之间单应性矩阵(Homography Matrix)的函数findHomography()的使用
opencv·3d
王老师青少年编程8 小时前
gesp(二级)(16)洛谷:B4037:[GESP202409 二级] 小杨的 N 字矩阵
数据结构·c++·算法·gesp·csp·信奥赛