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

 场景漫游

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

37 关于 undo 日志

前言 undo 和 redo 是在 mysql 中 事务, 或者 异常恢复 的场景下面 经常会看到的两个概念 这里 来看一下 undo, undo 主要是用于 事务回滚 的场景下面 测试表结构如下 CREATE TABLE tz_test (id int(11) unsigned NOT NULL AUTO_INCREMENT,field1 varchar(128) DEFAULT NUL…

YB4019是一款完整的单电池锂离子恒流/恒压线性充电器电池

YB4019 耐压18V 1A线性双节8.4V 锂电充电芯片 概述&#xff1a; YB4019是一款完整的单电池锂离子恒流/恒压线性充电器电池。底部采用热增强ESOP8封装&#xff0c;外部组件数量低使YB4019成为便携式应用的理想选择。此外&#xff0c;YB4019设计用于在USB电源规格范围内工作。Y…

Docker Swarm: 容器编排的力量和优势深度解析

文章目录 Docker Swarm的核心概念1. 节点&#xff08;Node&#xff09;2. 服务&#xff08;Service&#xff09;3. 栈&#xff08;Stack&#xff09; 使用Docker Swarm1. 初始化Swarm2. 加入节点3. 创建服务4. 扩展和缩减服务5. 管理栈6. 管理服务更新 Docker Swarm的优势深度解…

vivado产生报告阅读分析7-时序报告3

1、“ Timing Summary Report ”详情 “ Timing Summary Report ” &#xff08; 时序汇总报告 &#xff09; 包含下列部分 &#xff1a; • “ General Information ”部分 • “ Timer Settings ”部分 • “ Design Timing Summary ”部分 • “ Clock Summary ”部…

C语言每日一题(32)环形链表

力扣网 141.环形链表 题目描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾…

Hangfire.Pro 3.0 Crack

Hangfire.Pro 有限的存储支持 Hangfire Pro 是一组扩展包&#xff0c;允许使用批处理创建复杂的后台作业工作流程&#xff0c;并提供对超快速Redis作为作业存储的支持 请注意&#xff0c;仅在使用Hangfire.SqlServer、Hangfire.Pro.Redis或Hangfire.InMemory包作为作业存储时才…

Android并发编程与多线程

一、Android线程基础 1.线程和进程 一个进程最少一个线程&#xff0c;进程可以包含多个线程进程在执行过程中拥有独立的内存空间&#xff0c;而线程运行在进程内 2.线程的创建方式 new Thread&#xff1a; 缺点&#xff1a;缺乏统一管理&#xff0c;可能无限制创建线程&…

excel怎么能锁住行 和/或 列的自增长,保证粘贴公式的时候不自增长或者只有部分自增长

例如在C4单元格中输入了公式&#xff1a; 现在如果把C4拷贝到C5&#xff0c;D3会自增长为D4&#xff1a; 现在如果想拷贝的时候不自增长&#xff0c;可以先把光标放到C4单元格&#xff0c;然后按F4键&#xff0c;行和列的前面加上了$符号&#xff0c;锁定了&#xff1a; …

单稳态中间继电器\UEG/A-2H/220V 8A导轨安装 JOSEF约瑟

UEG系列中间继电器 UEG/A-2H2D中间继电器UEG/A-4H4D中间继电器UEG/A-2D中间继电器 UEG/A-2H中间继电器UEG/A-4H中间继电器UEG/A-4D中间继电器 UEG/A-6H中间继电器UEG/A-6D中间继电器UEG/A-8H中间继电器 UEG/A-10D中间继电器UEG/A-10H中间继电器UEG/A-2DPDT中间继电器 UEG/A-4DP…

C++多态原理揭秘

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;强烈推荐优质专栏: &#x1f354;&#x1f35f;&#x1f32f;C的世界(持续更新中) &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;…

值得你一生收藏的BMW宝马汽车底盘代号各个版本说明,方便今后查阅使用!

很少有汽车品牌像宝马一样&#xff0c;本属于内部交流使用的底盘代号&#xff08;Development Code&#xff09;&#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…