在面对大规模的场景管理时,场景图形的交运算和图形对象的拾取变成了一项基本工作。OSG作为一个场景管理系统,自然也实现了场景图形的交运算,交运算主要封装在osgUtil 工具中在OSG中,osgUtil是一个非常强有力的工具,集合了场图形处理、几何体修改工具及高层次的遍历几个功能。
交运算(Intersection)本身是一个非常复杂的立体几何问题。当在阅读这一部分源代码时,读者会发现如果有非常丰富的立体几何思想见解,将能够很快理解源代码,如果没有的话,即使笔者在这里分析源代码也是没有用的。当然,作为一个应用者没有必要去过多关注底层是如何实现的。
关于交运算,OSG本身的实现也是比较局限的,但是对于普通应用已经足够了,可以用一个继承关系图表示出来,如图8-21所示。

图8-21 osgUtil::Intersector 的继承关系及派生图
从继承关系图中可以看出,所有的交运算都共用一个父类osgUtil::Intersector类。下面对这个类的作用逐一说明。
- // 创建一个线段交集检测对象
- 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:”<
getWorldIntersectPoint().x()<<” Y:”<getWorldIntersectPoint().y()<<” Z:”<getWorldIntersectPoint().z()< - }
- 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
lineXY = new osg::LineSegment(newPos, m_vPosition); - 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)); - // 添加两条线段
- 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
updateText) :_updateText(updateText) - {
-
- }
-
- // 析构函数
- ~CPickHandler()
- {
-
- }
-
- // 事件处理
- bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa);
-
- // pick
- virtual void pick(osg::ref_ptr
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
_updateText; - };
-
- // HUD
- class CreateHUD
- {
- public:
- CreateHUD()
- {
-
- }
- ~CreateHUD()
- {
-
- }
-
- // 创建HUD
- osg::ref_ptr
createHUD(osg::ref_ptr updateText) - {
- // 创建一个相机
- osg::ref_ptr
hudCamera = new osg::Camera; -
- // 设置绝对帧引用
- hudCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
-
- // 设置正投影矩阵2D
- hudCamera->setProjectionMatrixAsOrtho2D(0, 1280, 0, 1024);
-
- // 设置视图矩阵
- hudCamera->setViewMatrix(osg::Matrix::identity());
-
- // 设置渲染顺序为POST
- hudCamera->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
geode = new osg::Geode(); - osg::ref_ptr
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);
-
- // 设置数据变量为DYNAMIC
- updateText->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
viewer = dynamic_cast(&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
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
viewer = new osgViewer::Viewer(); - osg::ref_ptr
root = new osg::Group(); -
- // 读取地形模型
- string strDataPath = strDataFolder + "lz.osg";
- osg::ref_ptr
node = osgDB::readNodeFile(strDataPath); - osg::ref_ptr
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显示位置及拾取示例截图