• 场景交互与场景漫游-交运算与对象选取(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类的对象可以指定一个特定的线段来执行相交检测,在构造函数中即可初始化。
    1. // 创建一个线段交集检测对象
    2. osgUtil::LineSegmentIntersector::Intersections intersections:
    3. viewer->computelntersections(x,y,intersections)

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

    1. // 得到相交交集的交点
    2. for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin();hitr!=intersections.end();++hitr)
    3. {
    4.     // 输入流
    5. cout<<”Mouse in world X:”<getWorldIntersectPoint().x()<<” Y:”<getWorldIntersectPoint().y()<<” Z:”<getWorldIntersectPoint().z()<
    6. }
    • 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 中完成,创建的过程如下:

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

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

    显示位置及拾取示例

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

    1. /******************************************* 显示位置及拾取示例 *************************************/
    2. // pick 事件处理器
    3. class CPickHandler:public osgGA::GUIEventHandler
    4. {
    5. public:
    6. // 构造函数
    7. CPickHandler(osg::ref_ptr updateText) :_updateText(updateText)
    8. {
    9. }
    10. // 析构函数
    11. ~CPickHandler()
    12. {
    13. }
    14. // 事件处理
    15. bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa);
    16. // pick
    17. virtual void pick(osg::ref_ptr viewer, const osgGA::GUIEventAdapter &ea);
    18. // 设置显示内容
    19. void setLabel(const std::string &name)
    20. {
    21. _updateText->setText(name);
    22. }
    23. protected:
    24. // 得到当前视图矩阵
    25. osg::Vec3 position;
    26. osg::Vec3 center;
    27. osg::Vec3 up;
    28. // 传递一个文字对象
    29. osg::ref_ptr _updateText;
    30. };
    31. // HUD
    32. class CreateHUD
    33. {
    34. public:
    35. CreateHUD()
    36. {
    37. }
    38. ~CreateHUD()
    39. {
    40. }
    41. // 创建HUD
    42. osg::ref_ptr createHUD(osg::ref_ptr updateText)
    43. {
    44. // 创建一个相机
    45. osg::ref_ptr hudCamera = new osg::Camera;
    46. // 设置绝对帧引用
    47. hudCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
    48. // 设置正投影矩阵2D
    49. hudCamera->setProjectionMatrixAsOrtho2D(0, 1280, 0, 1024);
    50. // 设置视图矩阵
    51. hudCamera->setViewMatrix(osg::Matrix::identity());
    52. // 设置渲染顺序为POST
    53. hudCamera->setRenderOrder(osg::Camera::POST_RENDER);
    54. // 清除深度缓存
    55. hudCamera->setClearMask(GL_DEPTH_BUFFER_BIT);
    56. // 设置字体
    57. string timesFont = "D:\\WorkAndStudy\\SDK\\VS2013\\OSG\\Data\\font\\cour.ttf";
    58. // 设置位置
    59. osg::Vec3 position(700, 900, 0.0);
    60. osg::ref_ptr geode = new osg::Geode();
    61. osg::ref_ptr stateset = geode->getOrCreateStateSet();
    62. // 关闭光照
    63. stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    64. //关闭深度测试
    65. stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
    66. geode->addDrawable(updateText.get());
    67. hudCamera->addChild(geode.get());
    68. updateText->setCharacterSize(20.0f);
    69. updateText->setFont(timesFont);
    70. updateText->setColor(osg::Vec4(1.0f, 1.0, 1.0, 1.0));
    71. updateText->setText("");
    72. updateText->setPosition(position);
    73. // 设置数据变量为DYNAMIC
    74. updateText->setDataVariance(osg::Object::DYNAMIC);
    75. return hudCamera.get();
    76. }
    77. };
    78. /* 显示位置及拾取示例 */
    79. void pickLineSegment_8_10(const string &strDataFolder);
    80. /******************************************* 显示位置及拾取示例 *************************************/
    81. // 事件处理函数
    82. bool CPickHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
    83. {
    84. switch (ea.getEventType())
    85. {
    86. // 每一帧
    87. case(osgGA::GUIEventAdapter::FRAME) :
    88. {
    89. osg::ref_ptr viewer = dynamic_cast(&aa);
    90. // 得到视图矩阵
    91. viewer->getCamera()->getViewMatrixAsLookAt(position, center, up);
    92. if (viewer)
    93. {
    94. // 执行PICK动作
    95. pick(viewer.get(), ea);
    96. }
    97. return false;
    98. }
    99. default:
    100. return false;
    101. }
    102. }
    103. // PICK动作
    104. void CPickHandler::pick(osg::ref_ptr viewer, const osgGA::GUIEventAdapter &ea)
    105. {
    106. // 创建一个线段交集检测对象
    107. osgUtil::LineSegmentIntersector::Intersections intersections;
    108. std::string gdlist = "";
    109. // 申请一个流
    110. std::ostringstream os;
    111. // 得到鼠标的位置
    112. float x = ea.getX();
    113. float y = ea.getY();
    114. // 如果没有发生交集运算及鼠标没有点中物体
    115. if (viewer->computeIntersections(x, y, intersections))
    116. {
    117. // 得到相交交集的交点
    118. for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr)
    119. {
    120. // 输入流
    121. os << "Mouse in World X:" << hitr->getWorldIntersectPoint().x() << " Y:" << hitr->getWorldIntersectPoint().y() << " Z:" << hitr->getWorldIntersectPoint().z() << endl;
    122. }
    123. }
    124. // 输入流
    125. os << "Viewer Position X:" << position[0] << " Y:" << position[1] << " Z:" << position[2] << endl;
    126. gdlist += os.str();
    127. // 设置显示内容
    128. setLabel(gdlist);
    129. }
    130. void pickLineSegment_8_10(const string &strDataFolder)
    131. {
    132. // 创建Viewer对象,场景浏览器
    133. osg::ref_ptr viewer = new osgViewer::Viewer();
    134. osg::ref_ptr root = new osg::Group();
    135. // 读取地形模型
    136. string strDataPath = strDataFolder + "lz.osg";
    137. osg::ref_ptr node = osgDB::readNodeFile(strDataPath);
    138. osg::ref_ptr updateText = new osgText::Text();
    139. CreateHUD *hudText = new CreateHUD();
    140. // 添加到场景
    141. root->addChild(node);
    142. root->addChild(hudText->createHUD(updateText));
    143. // 添加PICK事件处理器
    144. viewer->addEventHandler(new CPickHandler(updateText));
    145. // 优化场景数据
    146. osgUtil::Optimizer optimizer;
    147. optimizer.optimize(root);
    148. viewer->setSceneData(root);
    149. viewer->realize();
    150. viewer->run();
    151. }

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

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

  • 相关阅读:
    信息系统漏洞与风险管理制度
    【C++ 提高编程】- 泛型编程之模板(类型参数化)
    物理学专业英语(词汇整理)--------07
    Linux 基本语句_5_创建静态库|动态库
    如何使用“–format”和输出模板成为 Docker CLI 高级用户
    C与C++之间相互调用的基本方法
    JavaScript系列从入门到精通系列第十五篇:JavaScript中函数的实参介绍返回值介绍以及函数的立即执行
    计算机应用专业,报软考应该选什么?
    操作系统——处理机调度
    一、Rabbit的介绍与安装
  • 原文地址:https://blog.csdn.net/liangfei868/article/details/134496295