OSG-OSG中的observer_ptr指针
看array大神的CookBook后一些感想,在代码上添加了一些注释,也对源码做了一些研读,记录下学习的过程。
CookBook中第一个例子就是observer_ptr指针,这个指针和它的名字一样,就是用来观察指针的,可以把它想象成一个观察者,它只观察一个你给他指点的对象,但是不会影响这个对象的创建和销毁,它只是一个旁观者,但是它观察的这个对象要是被销毁了它也会知道并且自己也会被销毁。和osg::ref_ptr不同,osg::ref_ptr是用来管理指针的创建和删除的。如下图:
下面是代码,代码中添加了注释,因为CookBook中的例子很多,所以定义了一个common.h和common.cpp,记录一些通用型的类和函数。common.h:
#pragma once #ifdef _DEBUG #pragma comment(lib,"osgd.lib") #pragma comment(lib,"osgDBd.lib") #pragma comment(lib,"osgViewerd.lib") #pragma comment(lib,"osgGAd.lib") #pragma comment(lib,"osgUtild.lib") #pragma comment(lib,"osgTextd.lib") #else #pragma comment(lib,"osg.lib"); #pragma comment(lib,"osgDB.lib") #pragma comment(lib,"osgViewer.lib") #pragma comment(lib,"osgGA.lib") #pragma comment(lib,"osgUtil.lib") #pragma comment(lib,"osgText.lib") #endif #include <osg/Camera> #include <osg/Geode> #include <osg/Node> #include <osg/ShapeDrawable> #include <osgText/Text> #include <osgUtil/LineSegmentIntersector> #include <osgGA/GUIActionAdapter> #include <osgGA/GUIEventAdapter> #include <osgGA/GUIEventHandler> #include <osgViewer/Viewer> namespace osgCookBook{ extern osg::Camera* createHUDCamera(double left,double right,double bottom,double top); extern osgText::Text* createText(const osg::Vec3 &pos,const std::string &content,float size); class PickHandler : public osgGA::GUIEventHandler { public: /** .h中的成往事 */ virtual void doUserOperation(osgUtil::LineSegmentIntersector::Intersection& )=0; virtual bool handle(const osgGA::GUIEventAdapter &ea,osgGA::GUIActionAdapter & aa); }; }
common.cpp:
#include "common.h" namespace osgCookBook{ /** 创建HUD相机 */ osg::Camera* createHUDCamera(double left,double right,double bottom,double top){ osg::ref_ptr<osg::Camera> camera=new osg::Camera; camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); camera->setClearMask(GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::POST_RENDER); camera->setAllowEventFocus(false); camera->setProjectionMatrix(osg::Matrix::ortho2D(left,right,bottom,top)); camera->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF); return camera.release(); } osg::ref_ptr<osgText::Font> g_font=osgText::readFontFile("fonts/arial.ttf"); /** 创建文字节点 */ osgText::Text* createText(const osg::Vec3 &pos,const std::string &content,float size){ osg::ref_ptr<osgText::Text> text=new osgText::Text; text->setDataVariance(osg::Object::DYNAMIC); text->setFont(g_font.get()); text->setCharacterSize(size); text->setAxisAlignment(osgText::TextBase::XY_PLANE); text->setText(content); return text.release(); } /** 实现处理器中的handle方法*/ bool PickHandler::handle(const osgGA::GUIEventAdapter &ea,osgGA::GUIActionAdapter & aa){ if (ea.getEventType()!=osgGA::GUIEventAdapter::RELEASE ||ea.getButton()!=osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON ||!(ea.getModKeyMask()&osgGA::GUIEventAdapter::MODKEY_CTRL)) return false; osgViewer::View *viewer=dynamic_cast<osgViewer::View *>(&aa); if (viewer) { //定义射线求交类,它的定义方式有3种 //在窗口坐标系osgUtil::Intersector::WINDOW中会创建一个一个起点(x,y,0)到终点(x,y,1)的线段 //在投影坐标系osgUtil::Intersector::PROJECTION中会创建一个一个起点(x,y,-1)到终点(x,y,1)的线段 //在模型视图坐标系osgUtil::Intersector::VIEW或osgUtil::Intersector::MODEL中会创建一个一个起点(x,y,0)到终点(x,y,1)的线段 osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector=new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW,ea.getX(),ea.getY()); //创建求交访问器 osgUtil::IntersectionVisitor iv(intersector.get()); viewer->getCamera()->accept(iv); //判断射线求交是否有交点 if (intersector->containsIntersections()) { //射线求交的交点是经过深度排序的 //intersector->getIntersections()的返回结果是Intersections //这是Intersections的定义:typedef std::multiset<Intersection> Intersections //就是说Intersections是一个multiset,multiset是一个有序的容器,里面的元素都是经过排序的 //它排序的标准是里面元素的<操作符的定义,在这里就是Intersection中<操作符的定义 //而在Intersection类中有如下操作符重载: bool operator < (const Intersection& rhs) const { return ratio < rhs.ratio; } //也就是说ratio是反应交点与射线起点距离的一个成员变量,array也说了ratio是交点与线段起点的距离和线段总长度的比例 osgUtil::LineSegmentIntersector::Intersection &result=*(intersector->getIntersections().begin()); doUserOperation(result); } } return false; } }
然后就是observer_ptr指针的例子:observe_ptr.cpp
#include "../common/common.cpp" /** * 事件处理器类 继承自PickHandler,只需要重写用户自定义方法doUserOperation即可 */ class RemoveShapeHandle : public osgCookBook::PickHandler { virtual void doUserOperation(osgUtil::LineSegmentIntersector::Intersection& result){ //射线求交得到的结果 if (result.nodePath.size()>0) { //得到该路径中最后一个节点 osg::Geode* geode=dynamic_cast<osg::Geode *>(result.nodePath.back()); if (geode) geode->removeDrawable(result.drawable); } } int test; }; /** * 节点回调类,每一帧都会执行 */ class ObserveShapeCallback : public osg::NodeCallback{ public: virtual void operator()(osg::Node * node,osg::NodeVisitor *nv){ std::string content; if(_drawable1.valid()) content+="Drawable 1 ;"; if(_drawable2.valid()) content+="Drawable 2 ;"; if(_text.valid()) _text->setText(content); } osg::ref_ptr<osgText::Text> _text; osg::observer_ptr<osg::Drawable> _drawable1; //这里如果用ref_ptr那么当这个Drawable被移除后,文字依然存在 osg::observer_ptr<osg::Drawable> _drawable2; }; int main() { osgText::Text * text=osgCookBook::createText(osg::Vec3(70.0,70.0,0.0f),"",10.0f); osg::ref_ptr<osg::Geode> textGeode=new osg::Geode; textGeode->addDrawable(text); osg::ref_ptr<osg::Camera> hudCamera=osgCookBook::createHUDCamera(0,800,0,600); hudCamera->addChild(textGeode); osg::ref_ptr<osg::Geode> geode=new osg::Geode; geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(-2.0f,0.0,0.0),1.0))); geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(2.0,0.0,0.0),1.0))); osg::ref_ptr<osg::Group> root=new osg::Group; root->addChild(hudCamera); root->addChild(geode); osg::ref_ptr<ObserveShapeCallback> ob=new ObserveShapeCallback; ob->_text=text; ob->_drawable1=geode->getDrawable(0); ob->_drawable2=geode->getDrawable(1); root->addUpdateCallback(ob); osgViewer::Viewer viewer; viewer.addEventHandler(new RemoveShapeHandle); viewer.setSceneData(root); return viewer.run(); }