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

news2024/12/30 3:02:04

 场景漫游

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

	// 鼠标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自定义操作器场景漫游示例截图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1222374.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马React18: 基础Part 1

黑马React: 基础1 Date: November 15, 2023 Sum: React介绍、JSX、事件绑定、组件、useState、B站评论 React介绍 概念: React由Meta公司研发&#xff0c;是一个用于 构建Web和原生交互界面的库 优势: 1-组件化的开发方式 2-优秀的性能 3-丰富的生态 4-跨平台开发 开发环境搭…

鸿蒙ToastDialog内嵌一个xml页面会弹跳到一个新页面《解决》

ToastDialog 土司组件 1.问题展示2.代码展示3.问题分析 1.问题展示 0.理想效果 错误效果: 1.首页展示页面 (未点击按钮前) 2.点击按钮之后&#xff0c;弹窗不在同一个位置 2.代码展示 1.点击按钮的 <?xml version"1.0" encoding"utf-8"?> <…

Jmeter 如何监控目标服务的系统资源

下载Jmeter插件管理下载 perfmon 将这个插件管理放到Jmeter的\lib\ext目录下 然后重启Jmeter jmeter-plugins-manager-1.10.jar 下载 perfmon插件 添加 io 内存 磁盘的监听 并且添加监听 在宿主机中安装代理监听程序 并启动 ServerAgent.tar.gz

Linux常用命令——bzcat命令

在线Linux命令查询工具 bzcat 解压缩指定的.bz2文件 补充说明 bzcat命令解压缩指定的.bz2文件&#xff0c;并显示解压缩后的文件内容。保留原压缩文件&#xff0c;并且不生成解压缩后的文件。 语法 bzcat(参数)参数 .bz2压缩文件&#xff1a;指定要显示内容的.bz2压缩文…

任正非说:公司要逐步实行分灶吃饭,我们在管理上不能过于整齐划一,否则缺少战斗力。

你好&#xff01;这是华研荟【任正非说】系列的第42篇文章&#xff0c;让我们聆听任正非先生的真知灼见&#xff0c;学习华为的管理思想和管理理念。 一、我们必须在混沌中寻找战略方向。规划就是要抓住机会点&#xff0c;委员会是火花荟萃的地方&#xff0c;它预研的方向是可做…

贝加莱MQTT功能

贝加莱实现MQTT Client端的功能库和例程 导入库和例程&#xff0c;AS Logical View中分别通过Add Object—Library&#xff0c;Add—Program插入MQTT库和例程。 将例程Sample放置于CPU循环周期中 定义证书存放路径&#xff0c;在AS Physical View 中&#xff0c;右击PLC—Con…

聚观早报 |零跑C10亮相广州车展;小鹏X9亮相广州车展

【聚观365】11月18日消息 零跑C10亮相广州车展 小鹏X9亮相广州车展 坦克700 Hi4-T开启预售 超A级家轿五菱星光正式预售 哪吒汽车发布山海平台2.0 零跑C10亮相广州车展 零跑汽车首款全球车型C10在广州车展首次亮相&#xff0c;同时该车也是零跑LEAP 3.0技术架构下的首款全…

C++菜鸟日记2

关于getline()函数&#xff0c;在char和string输入的区别 参考博客 1.在char中的使用&#xff1a; 2.在string中的使用&#xff1a; 关于char字符数组拼接和string字符串拼接方法 参考博客 字符串拼接方法&#xff1a; 1.直接用 号 2.利用append&#xff08;&#xff0…

【草料】uni-app ts vue 小程序 如何如何通过草料生成对应的模块化二维码

一、查看uni-app项目 1、找到路径 可以看到项目从 src-race-pages-group 这个使我们目标的查询页面 下面我们将这个路径copy到草料内 2、找到进入页面入参 一般我们都会选择 onload() 函数下的入参 这里我们参数的是 id 二、草料 建议看完这里的教程文档 十分清晰&#xff01…

详解自动化测试之 Selenium

目录 1. 什么是自动化 2.自动化测试的分类 3. selenium&#xff08;web 自动化测试工具&#xff09; 1&#xff09;选择 selenium 的原因 2&#xff09;环境部署 3&#xff09;什么是驱动&#xff1f; 4. 一个简单的自动化例子 5.selenium 常用方法 5.1 查找页面元素&…

【STM32】RTC(实时时钟)

1.RTC简介 本质&#xff1a;计数器 RTC中断是外部中断&#xff08;EXTI&#xff09; 当VDD掉电的时候&#xff0c;Vbat可以通过电源--->实时计时 STM32的RTC外设&#xff08;Real Time Clock&#xff09;&#xff0c;实质是一个 掉电 后还继续运行的定时器。从定时器的角度…

三十分钟学会zookeeper

zookeeper 一、前提知识 集群与分布式 ​ 集群&#xff1a;将一个任务部署在多个服务器&#xff0c;每个服务器都能独立完成该任务。 ​ 分布式&#xff1a;将一个任务拆分成若干个子任务&#xff0c;由若干个服务器分别完成这些子任务&#xff0c;每个服务器只能完成某个特…

Vite -静态资源处理 - SVG格式的图片

特点 Vite 对静态资源是开箱即用的。 无需做特殊的配置。项目案例 项目结构 study-vite| -- src| -- assets| -- bbb.svg # 静态的svg图片资源| -- index.html # 主页面| -- main.js # 引入静态资源| -- package.json # 脚本配置| -- vite.co…

探索Scrapy中间件:自定义Selenium中间件实例解析

简介 Scrapy是一个强大的Python爬虫框架&#xff0c;可用于从网站上抓取数据。本教程将指导你创建自己的Scrapy爬虫。其中&#xff0c;中间件是其重要特性之一&#xff0c;允许开发者在爬取过程中拦截和处理请求与响应&#xff0c;实现个性化的爬虫行为。 本篇博客将深入探讨…

Pycharm之配置python虚拟环境

最近给身边的人写了脚本&#xff0c;在自己电脑可以正常运行。分享给我身边的人&#xff0c;却运行不起来&#xff0c;然后把报错的截图给我看了&#xff0c;所以难道不会利用pycharm搭建虚拟的环境&#xff1f;记录一下配置的过程。 第一步&#xff1a;右键要打开的python的代…

什么是单域名SSL安全证书?

单域名证书是什么&#xff1f; 单域名证书是指只包含一个具体域名的SSL/TLS证书&#xff0c;它可以用于保护单个主机名的HTTPS通信。例如&#xff0c;如果您有一个网站http://www.example.com&#xff0c;则单域名证书将仅为该域名颁发。 这种证书在保护单个域的安全方面很有…

hash 哈希表

哈希表是一种期望算法。 一般情况下&#xff0c;哈希表的时间复杂度是 O(1)。 作用 将一个复杂数据结构映射到一个较小的空间 0~N&#xff08;10^5~10^6&#xff09;&#xff0c;最常用的情景&#xff1a;将 0~10^9 映射到 0~10^5。 离散化是一种及其特殊的哈希方式。离散化…

【978.最长湍流子数组】

目录 一、题目描述二、算法原理三、代码实现 一、题目描述 二、算法原理 三、代码实现 class Solution { public:int maxTurbulenceSize(vector<int>& arr) {int narr.size();vector<int> f(n),g(n);f[0]g[0]1;if(n1) return 1;int retmax(f[0],g[0]);for(int…

鸿蒙4.0开发笔记之DevEco Studio启动时不直接打开原项目

1、想要在DevEco Studio启动时不直接打开关闭前的那个项目&#xff0c;可以在设置中进行。 有两个位置可以进入“设置”&#xff0c;一个是左上角的File>Settings&#xff0c;二是右上方的设置图标。 2、进入Settings界面以后&#xff0c;选择Appearance&Behavior下面…

redis + celery

首先&#xff0c;部署Redis数据库&#xff1a; 先下载包&#xff1a; wget http://download.redis.io/releases/redis-5.0.7.tar.gz 解压redis包&#xff1a; tar -xvf redis-5.0.7.tar.gz 编译&#xff1a; make sudo make install &#xff08;这样没有指定安装目录&#…