场景交互与场景漫游-交运算与对象选取(8-1)

交运算与对象选取

        在面对大规模的场景管理时,场景图形的交运算图形对象的拾取变成了一项基本工作。OSG作为一个场景管理系统,自然也实现了场景图形的交运算,交运算主要封装在osgUtil 工具中在OSG中,osgUtil是一个非常强有力的工具,集合了场图形处理、几何体修改工具及高层次的遍历几个功能。

交运算

        交运算(Intersection)本身是一个非常复杂的立体几何问题。当在阅读这一部分源代码时,读者会发现如果有非常丰富的立体几何思想见解,将能够很快理解源代码,如果没有的话,即使笔者在这里分析源代码也是没有用的。当然,作为一个应用者没有必要去过多关注底层是如何实现的。

        关于交运算,OSG本身的实现也是比较局限的,但是对于普通应用已经足够了,可以用一个继承关系图表示出来,如图8-21所示。

图8-21 osgUtil::Intersector 的继承关系及派生图

从继承关系图中可以看出,所有的交运算都共用一个父类osgUtil::Intersector类。下面对这个类的作用逐一说明。

  • osgUtil::Intersector:是一个纯虚类它定义了相交测试的接口osgUtil库从osgUtil::Intersection继承了多个类,适用于各种类型的几何体(如线段、多边形等)。执行相交测试时,应用程序将继承自osgUtil::Intersector的某个类实例化,再将其传递给 osgUtil::IntersectionVisitor 的实例,并请求该实例返回数据以获取交运算的结果。
  • osgUtil::LineSegmentIntersector继承自osgUtil::Intersector 类,用于检测指定线段和场景图形之间的相交情况,并向程序提供查询相交测试结果的函数。该类提供了一种定义射线的方法。它包括两个osg::Vec3实例,一个用于定义线段的起点,另一个用于定义终点。当交集测试被触发时,它将检测射线的相交情况并执行相应的操作。这个在示例显示位置及拾取示例中会用到,可以根据鼠标的位置初始化一个osgUtil::LineSegmentIntersector类的对象可以指定一个特定的线段来执行相交检测,在构造函数中即可初始化。
// 创建一个线段交集检测对象
osgUtil::LineSegmentIntersector::Intersections intersections:
viewer->computelntersections(x,y,intersections)

        通过相交运算,更多的是希望得到相交的点,可以通过申请一个迭代器来实现,代码如下:

// 得到相交交集的交点for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin();hitr!=intersections.end();++hitr)
{// 输入流cout<<”Mouse in world X:”<<hitr->getWorldIntersectPoint().x()<<” Y:”<<hitr->getWorldIntersectPoint().y()<<” Z:”<<hitr->getWorldIntersectPoint().z()<<endl;
}
  • osgUtil::PolytopeIntersector与osgUtil::LineSegmentIntersector类似,不过,该类用于检测由一系列平面构成的多面体的相交运算。当鼠标单击场景图形中某一区域,希望拾取到鼠标位置附近的一个封闭多面体区域时,osgUtil::PolytopeIntersector类最实用。
  • osgUtil::PlaneIntersector,与osgUtil::LineSegmentIntersector类似,用于检测出一系列平面构成的平面的相交运算。

        osgUtil::IntersectionVisitor是一个比较特殊的类,它不直接继承自osgUtil::Intersector,继承关系图如图8-22所示。

图8-22 osgUtil::IntersectionVisitor 的继承关系图

        从继承关系图可以看出,它继承自osg::NodeVisitor,创建和触发机制与osg::NodeVisitor 实例大致相似。访问器需要维护一个进行交集测试的线段列表,而对于其中的每一条线段,访问器都会创建一个交点列表(osgUtil::IntersectVisitor::HitList 实例),它主要用于搜索场景图形中与指定几何体相交的节点。而最后相交测试的工作将在osgUtil::Intersector 的继承类中完成。在前面的自定义漫游操作器中,碰掩检测就是采用该类,最后的检测工作在osgUtil::LineSegmentIntersector 中完成,创建的过程如下:

// 创建一个交集访问器
osgUtil::IntersectVisitor ivXY;
// 根据新的位置得到两条线段检测
osg::ref_ptr<LineSegment> lineXY = new osg::LineSegment(newPos, m_vPosition);
osg::ref_ptr<osg::LineSegment> lineZ = new osg::LineSegment(newPos1 + osg::Vec3(0.0,0.0,10.0), newPos1 - osg::Vec3(0.0,0.0,-10.0));
// 添加两条线段
ivXY.addLineSegment(lineZ.get());
ivXY.addLineSegment(lineXY.get());// 开启交集检测
m_pHowViewer->getSceneData()->accept(ivXY);

        交点列表(osgUtil::IntersectVisitor:HitList)的作用为:一条单一的线段可能与场景中的多个几何体实例(或者多次与同一个几何体)产生交集。对于每一条参与交集测试的线段,系统均会产生一个列表,这个列表包含了所有交集测试产生的 Hit 实例。如果没有监测到任何交集,该列表保持为空。

显示位置及拾取示例

        显示位置及拾取示例的代码如程序清单 8-10所示

/******************************************* 显示位置及拾取示例 *************************************/
// pick 事件处理器
class CPickHandler:public osgGA::GUIEventHandler
{
public:// 构造函数CPickHandler(osg::ref_ptr<osgText::Text> updateText) :_updateText(updateText){}// 析构函数~CPickHandler(){}// 事件处理bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa);// pickvirtual void pick(osg::ref_ptr<osgViewer::Viewer> viewer, const osgGA::GUIEventAdapter &ea);// 设置显示内容void setLabel(const std::string &name){_updateText->setText(name);}
protected:// 得到当前视图矩阵osg::Vec3 position;osg::Vec3 center;osg::Vec3 up;// 传递一个文字对象osg::ref_ptr<osgText::Text> _updateText;
};// HUD
class CreateHUD
{
public:CreateHUD(){}~CreateHUD(){}// 创建HUDosg::ref_ptr<osg::Node> createHUD(osg::ref_ptr<osgText::Text> updateText){// 创建一个相机osg::ref_ptr<osg::Camera> hudCamera = new osg::Camera;// 设置绝对帧引用hudCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);// 设置正投影矩阵2DhudCamera->setProjectionMatrixAsOrtho2D(0, 1280, 0, 1024);// 设置视图矩阵hudCamera->setViewMatrix(osg::Matrix::identity());// 设置渲染顺序为POSThudCamera->setRenderOrder(osg::Camera::POST_RENDER);// 清除深度缓存hudCamera->setClearMask(GL_DEPTH_BUFFER_BIT);// 设置字体string timesFont = "D:\\WorkAndStudy\\SDK\\VS2013\\OSG\\Data\\font\\cour.ttf";// 设置位置osg::Vec3 position(700, 900, 0.0);osg::ref_ptr<osg::Geode> geode = new osg::Geode();osg::ref_ptr<osg::StateSet> stateset = geode->getOrCreateStateSet();// 关闭光照stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);//关闭深度测试stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);geode->addDrawable(updateText.get());hudCamera->addChild(geode.get());updateText->setCharacterSize(20.0f);updateText->setFont(timesFont);updateText->setColor(osg::Vec4(1.0f, 1.0, 1.0, 1.0));updateText->setText("");updateText->setPosition(position);// 设置数据变量为DYNAMICupdateText->setDataVariance(osg::Object::DYNAMIC);return hudCamera.get();}
};/* 显示位置及拾取示例 */
void pickLineSegment_8_10(const string &strDataFolder);/******************************************* 显示位置及拾取示例 *************************************/
// 事件处理函数
bool CPickHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{switch (ea.getEventType()){// 每一帧case(osgGA::GUIEventAdapter::FRAME) :{osg::ref_ptr<osgViewer::Viewer> viewer = dynamic_cast<osgViewer::Viewer*>(&aa);// 得到视图矩阵viewer->getCamera()->getViewMatrixAsLookAt(position, center, up);if (viewer){// 执行PICK动作pick(viewer.get(), ea);}return false;}default:return false;}
}// PICK动作
void CPickHandler::pick(osg::ref_ptr<osgViewer::Viewer> viewer, const osgGA::GUIEventAdapter &ea)
{// 创建一个线段交集检测对象osgUtil::LineSegmentIntersector::Intersections intersections;std::string gdlist = "";// 申请一个流std::ostringstream os;// 得到鼠标的位置float x = ea.getX();float y = ea.getY();// 如果没有发生交集运算及鼠标没有点中物体if (viewer->computeIntersections(x, y, intersections)){// 得到相交交集的交点for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr){// 输入流os << "Mouse in World X:" << hitr->getWorldIntersectPoint().x() << "  Y:" << hitr->getWorldIntersectPoint().y() << "  Z:" << hitr->getWorldIntersectPoint().z() << endl;}}// 输入流os << "Viewer Position X:" << position[0] << " Y:" << position[1] << " Z:" << position[2] << endl;gdlist += os.str();// 设置显示内容setLabel(gdlist);
}void pickLineSegment_8_10(const string &strDataFolder)
{// 创建Viewer对象,场景浏览器osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();osg::ref_ptr<osg::Group> root = new osg::Group();// 读取地形模型string strDataPath = strDataFolder + "lz.osg";osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);osg::ref_ptr<osgText::Text> updateText = new osgText::Text();CreateHUD *hudText = new CreateHUD();// 添加到场景root->addChild(node);root->addChild(hudText->createHUD(updateText));// 添加PICK事件处理器viewer->addEventHandler(new CPickHandler(updateText));// 优化场景数据osgUtil::Optimizer optimizer;optimizer.optimize(root);viewer->setSceneData(root);viewer->realize();viewer->run();
}

        运行程序,截图如图 8-23 所示。

图8-23显示位置及拾取示例截图

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

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

相关文章

@AutoConfigurationPackage的使用

作用 参考&#xff1a;https://blog.csdn.net/yasinawolaopo/article/details/121319977 不过文章最后这个结论是有点问题的&#xff0c;这个注解的作用只导入了一个bean&#xff0c;就是AutoConfigurationPackages.class.getName()。 了解introspectedClass&#xff08;内省…

【Python】给定一个长度为n的数列,将这个数列按从小到大的顺序排列。1<=n<=200

2、问题描述 给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200 样例输入 5 8 3 6 4 9 样例输出 3 4 6 8 9 n int(input()) a list(map(int,input().split())) a.sort() for i in a:print(i,end ) 运行结果&#xff1a;

深度学习基础

深度强化学习 教程链接 DataWhale强化学习课程JoyRL https://johnjim0816.com/joyrl-book/#/ch7/main 深度学习基础 强化学习的问题可以拆分成两类问题&#xff0c;即预测与控制。预测的主要目的是根据环境的状态以及动作来预测状态的价值与动作的价值&#xff0c;而控制的…

毕业设计JSP 2384网上diy蛋糕店管理系统【程序源码+讲解视频+调试运行】

一、摘要 本文将介绍一个功能全面、易于使用的网上DIY蛋糕店管理系统。该系统包括用户和管理员两种用户&#xff0c;每种用户都有相应的功能模块。系统实现了网站首页、用户注册/登录、蛋糕展示、综合排行、购物车、蛋糕DIY和用户中心等功能&#xff0c;同时管理员还可以进行管…

CISP全真模式测试题(二)

免责声明 文章仅做经验分享用途,利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,一旦造成后果请自行承担!!! 1、下列关于信息安全保障的说法错误的是: A.信息安全保障的问题就是安全的效用问题,在解决或预…

使用C语言判断闰年

可以使用以下代码来判断一个年份是否为闰年&#xff1a; #include <stdio.h>int isLeapYear(int year) {if ((year % 4 0 && year % 100 ! 0) || (year % 400 0)) {return 1; // 是闰年} else {return 0; // 不是闰年} }int main() {int year;printf("请…

VAD监测(一)

麦克风的采样率是16000&#xff0c;代表一秒钟采集16000个数据点 我们每次拿1024个采样点作为一个buffer&#xff0c;buffer是一个b类型&#xff0c;也就是字节类型。 这一个buffer的长度不一定是1024&#xff0c;取决于每个采样点的采样点的位深度&#xff0c;如果音频数据是…

每天一道算法题(七)——求一个数组中最多能存储多少雨水(困难)

文章目录 1、问题2、示例3、解决方法&#xff08;1&#xff09;方法1——双指针 总结 1、问题 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 2、示例 示例 1&#xff1a; 输入&#xff1a;height [0,1…

庖丁解牛:NIO核心概念与机制详解 01 _ 入门篇

文章目录 Pre输入/输出Why NIO流与块的比较通道和缓冲区概述什么是缓冲区&#xff1f;缓冲区类型什么是通道&#xff1f;通道类型 NIO 中的读和写概述Demo : 从文件中读取1. 从FileInputStream中获取Channel2. 创建ByteBuffer缓冲区3. 将数据从Channle读取到Buffer中 Demo : 写…

算法-二叉树-简单-二叉树的最大和最小深度

记录一下算法题的学习7 二叉树的最大深度 题目&#xff1a;给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例分析&#xff…

MATLAB 状态空间设计 —— LQG/LQR 和极点配置算法

系列文章目录 文章目录 系列文章目录前言一、相关函数 —— LQG/LQR 和极点配置算法1.1 LQR —— lqr 函数1.1.1 函数用法1.1.2 举例1.1.2.1 倒摆模型的 LQR 控制 1.2 LQG —— lqg() 函数1.2.1 函数用法1.2.2 举例 1.3 极点配置 —— place() 函数1.3.1 函数用法1.3.2 示例1.3…

Selenium安装WebDriver最新Chrome驱动(含116/117/118/119)

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

如何使用ffmpeg将FLAC格式转为MP3格式

以下是使用ffmpeg将FLAC格式转换为MP3的方法&#xff1a; 单个文件转换&#xff1a; ffmpeg -i input.flac -ab 320k -map_metadata 0 -id3v2_version 3 output.mp3 其中&#xff0c;input.flac是要转换的FLAC文件名&#xff0c;output.mp3是转换后的MP3文件名。-ab 320k表示…

如何在虚拟机的Ubuntu22.04中设置静态IP地址

为了让Linux系统的IP地址在重新启动电脑之后IP地址不进行变更&#xff0c;所以将其IP地址设置为静态IP地址。 查看虚拟机中虚拟网络编辑器获取当前的子网IP端 修改文件/etc/netplan/00-installer-config.yaml文件&#xff0c;打开你会看到以下内容 # This is the network conf…

Vue中的组件间通信有哪些方式?

Vue中实现组件间通信的方式非常多样&#xff0c;以下是一些常用的方式&#xff1a; Props / $emit&#xff1a;这是最常用的组件通信方式&#xff0c;父组件通过属性向子组件传递数据&#xff0c;子组件通过事件向父组件传递数据。Ref / $refs&#xff1a;这是一种访问子组件或…

面向开发者的Android

Developerhttps://developer.android.google.cn/?hlzh-cn SDK 平台工具版本说明https://developer.android.google.cn/studio/releases/platform-tools?hlzh-cn#revisions Android SDK Platform-Tools 是 Android SDK 的一个组件。它包含与 Android 平台进行交互的工具…

【Redis】springboot整合redis(模拟短信注册)

要保证redis的服务器处于打开状态 上一篇&#xff1a; 基于session的模拟短信注册 https://blog.csdn.net/m0_67930426/article/details/134420531 整个流程是&#xff0c;前端点击获取验证码这个按钮&#xff0c;后端拿到这个请求&#xff0c;通过RandomUtil 工具类的方法生…

dm 聚合函数和group的总结

-- dm&#xff1a;select中只能为group字段&#xff0c;聚合函数&#xff1b;不能有其他内容 select id,user_id,role_id from assets_dm_test.user_role GROUP by role_id; -- mysql: ok 执行ok&#xff0c;但是语法有问题&#xff0c;多条时只显示了第一条 sel…

nginx得if语句内proxy_pass不允许携带url部分,如何处理

在nginx中&#xff0c;proxy_pass指令不能直接携带URL部分。但是&#xff0c;可以使用rewrite指令结合正则表达式来处理URL部分。 下面是一个示例配置&#xff0c;演示如何使用rewrite指令将URL中的某个部分进行替换后传递给后端服务器&#xff1a; location /v100/{proxy_…