(三)OSG初学者入门基础教程

20.超级指针(难点

关于超级指针的问题由来已久,用的颇广。在 OSG 中问题是这样提出来的,有这样一个场景,有一辆卡车, 卡车上有一个箱子。如果说卡车在运行过程中,箱子突然掉了,这样就知道该把箱子的结点删除。那么如 果说还有一个箱子,在地上放着,从感觉上来讲他们应该用同一块内存。卡车上的箱子虽然掉了,但是地 上的箱子还在,所以箱子所占的资源不能被粗鲁的释放掉。

这样就引用了超级指针的机制,其实就是引用一个计数器,这个计数器会计算这个箱子被引用的次数,被 别人引用一次这个计数器增加一,别人不用一次,即:释放一次,则计数器减一。当减至 0 时,内存放掉 不用。

 

21.超级指针详解 (难点

内容来自  OpenSceneGraph基本渲染理论

Leandro Motta Barros

Start: August 17th 02005

译:FreeSouth

让我们来以一个定义开讲:一个 资源 必须在它被使用之前被申请,而在它使用之后不久被释放,也许我们有很多的应用程

序在使用同一片资源,一个不需要了而另一个却还需要使用这片资源。这两种情况都涉及到页面(在打开前必须处于关闭状态)

和数据的移动(在移动前可能被加锁或做一些包裹处理)。在 OpenGL 当中,也同样有一些关于申请与释放资源的例子(一个示例是纹理函数的名称被命名为 glGenTextures(), 它就必须被函数 glDeleteTextures()释放)。

在事物扩展的事例当中,事物拥有者使用 AllocateTing()来分配该事物, 然后拥有者便有责任来管理他们的分配与释放操作。

在 OSG 当中,有一些稍微有些复杂的细节来实现这些操作,也就是一些事物可能有多个拥有者。比如,在场景图 1.7 所示当中,

类 osg::geode 中含有 box 绑定到两个父结点身上,到底哪个负责他们的分配与释放操作呢。

在这种情况之下,只要还有一个引用指向该资源,该资源就不应该被释放。所以,大多数的 OSG 对象都有一个内部的计数

器用来计算有多少个指针指向它。当没有指针指向该资源(同样,可以称做对象)时,它的计数器会变为 0,当计算器为 0 时就

可以放掉对象。

幸运的是,我们程序员不需要手工的来维护这些引用计数:这就是超级指针为什么存在的原因。在 OSG 当中超级指针被一

个称为 osg::ref_ptr<>的类模版来实现。无论在什么时候,只要该对象被引用,则在 osg::ref_ptr<>当中的计数器会自动增加, 通过这种方式实现了资源的自动管理,当不需要使用它时,它会在不久后释放,在申请它时会自动分配空间。

 

22.超级指针代码解释

下面这个例子会示例如何使用 OSG 的超级指针,例子中有一些代码如下所示:

SmartPointers.cpp

1 #include <cstdlib>

2 #include <iostream>

3 #include <osg/Geode>

4 #include <osg/Group>

5

6 void MayThrow()

7 {

8 if (rand() % 2)

9 throw "Aaaargh!";

10 }

11

12 int main()

13 {

14 try

15 {

16 srand(time(0));

17 osg::ref_ptr<osg::Group> group (new osg::Group());

18

19 // This is OK, albeit a little verbose.

20 osg::ref_ptr<osg::Geode> aGeode (new osg::Geode());

21 MayThrow();

22 group->addChild (aGeode.get());

23

24 // This is quite safe, too.

25 group->addChild (new osg::Geode());

26

27 // This is dangerous! Don’t do this!

28 osg::Geode* anotherGeode = new osg::Geode();

29 MayThrow();

30 group->addChild (anotherGeode);

31

32 // Say goodbye 

33 std::cout << "Oh, fortunate one. No exceptions, no leaks.\n";

34 }

35 catch (...)

36 {

37 std::cerr << "’anotherGeode’ possibly leaked!\n";

38 }

39 }

 

23.代码解释:

       srand(time(0));

srand(time(0)) 就是给这个算法一个启动种子,也就是算法的随机种子数,有这个数以后才可以产生随机数,用1970.1.1至今的秒数,初始化随机数种子。
  Srand是种下随机种子数,你每回种下的种子不一样,用Rand得到的随机数就不一样。为了每回种下一个不一样的种子,所以就选用Time(0),Time(0)是得到当前时时间值(因为每时每刻时间是不一样的了)。

 

20 osg::ref_ptr<osg::Geode> aGeode (new osg::Geode());

21 MayThrow();

22 group->addChild (aGeode.get());

第 20 行到 22 行,示例了一个比较安全的方式来使用超级指针,一个 osg::ref_prt<>(叫做 aGeode)在第 20 行被初始

化为 osg::Geode(资源)。在这一点上,关于 geode 的引用计数器在堆内会被置 1(因为 osg::ref_ptr<>中只分配了一个Geode,故可以命令为 aGeode)。

在稍后的第 22 行,geode 被当做一个孩子加入到一个组当中。这个操作一旦执行结束,组对 geode 的引用计数就会增加 1,变为 2。

现在,有哪些比较烂的事情发生了呢?如果我们在第 21 行调用 MayThrow()之后,会发生干什么事情呢?好的,aGeode将会超出范围,然后被析构掉。然而,在它的计数减至 0 之后,它仍会合适的处理该 geode,这时也没有内存泄漏。

 

28 osg::Geode* anotherGeode = new osg::Geode();

29 MayThrow();

30 group->addChild (anotherGeode);

坏的、错误的、危险的以及应该被责备的方式来管理内存是从第 28 到第 30 行的做法。它看起来和第一种是一样的,但是 geode 是被 new 分配的且存储在一个普通的指针当中。如果 MayThrow()在第 29 行被抛出,没有人会调用 Delete,这时 geode 就发生了泄漏。

 

 

 

 

 

 

 

 

24.移动/旋转/缩放模型

移动/旋转/缩放其实都是对矩阵进行操作,在 OSG 当中,矩阵可以当作一个特殊的结点加入到 root 当中, 而矩阵下也可以另入结点,而加入的结点就会被这个矩阵处理过,比如移动过/旋转过/缩放过。在 OSG 中 控制矩阵的类可以为 osg::MatrixTransform。下面看一个示例,示例对 osgCool.osg 进行了各种操作。

功能:移动/旋转/缩放模型,这里加入了四个 glider,一个是默认加入在最中间,一个向上移 2 单位,一 个是向下移 2 单位且缩放 0.5 倍,另一个是向右 4 单位,缩放 0.5 且平躺 45 度。

 

25.代码及解释如下:这部分代码就用到了超级指针

#include<osgViewer/Viewer>

#include<osgDB/ReadFile>

#include <osg/MatrixTransform>

#include <osg/Switch>

#include <osg/Node>

#include <osgViewer/ViewerEventHandlers>

#include <osgGA/StateSetManipulator>

 

int main(int argc, char **argv)

{

    osgViewer::Viewer viewer;

 

    osg::ref_ptr < osg::Group> root = new osg::Group() ;

    osg::ref_ptr < osg::Node> glider = osgDB::readNodeFile("glider.osg") ;

    osg::ref_ptr < osg::MatrixTransform> trans = new osg::MatrixTransform ;

    trans ->setMatrix(osg::Matrix::translate(0, 0, 2)) ;

    trans ->addChild(glider.get()) ;

    osg::ref_ptr < osg::MatrixTransform> scale = new osg::MatrixTransform ;

    scale ->setMatrix(osg::Matrix::scale(0.5, 0.5, 0.5)*osg::Matrix::translate(0, 0, -2)) ;

    scale ->addChild(glider.get()) ;

    osg::ref_ptr < osg::MatrixTransform > rot = new osg::MatrixTransform ;

    rot ->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(45.0), 1, 0, 0)*osg::Matrix::scale(0.5, 0.5, 0.5)*osg::Matrix::translate(4, 0, -2)) ;

    rot ->addChild(glider.get()) ;

 

    root ->addChild(glider.get()) ;

    root ->addChild(trans.get()) ;

    root ->addChild(scale.get()) ;

    root ->addChild(rot.get()) ;

 

    //添加状态事件

    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );

    //窗口大小变化事件

    viewer.addEventHandler(new osgViewer::WindowSizeHandler);

    //添加一些常用状态设置

    viewer.addEventHandler(new osgViewer::StatsHandler);

   

 

 

    viewer.setSceneData(root.get());

    viewer.realize();

    return viewer.run();

}

 

 

osg::ref_ptr < osg::Node> glider = osgDB::readNodeFile("glider.osg") ;

读入一个 glider.osg 模型

trans ->setMatrix(osg::Matrix::translate(0, 0, 2)) ;

申请一个矩阵使加入矩阵的东西沿Z 轴平移 2 个单位,在 OSG 中坐标轴是可以设置的,默认 X 轴是 X 轴,向里的是 Y 轴,而向上是 Z 轴,这与传统的 OPENGL 坐标轴多有不同。

scale ->setMatrix(osg::Matrix::scale(0.5, 0.5, 0.5)*osg::Matrix::translate(0, 0, -2)) ;

 

申请一个矩阵使加入该矩阵的模型向下移动两个单位,值得一提的是,移动和缩放以及旋转都 是对矩阵进行操作,这些操作如果要叠加直接矩阵相乘就可以了,可以查阅一下矩阵的基本知识,最 好看图形学的相关书籍中关于矩阵操作一节。

 

rot ->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(45.0), 1, 0, 0)*osg::Matrix::scale(0.5, 0.5, 0.5)*osg::Matrix::translate(4, 0, -2)) ;

申请一个矩阵使加入该矩阵的模型向下移动两个单位,向右移动四个单位,且绕 X 轴转 45 度。 这里的几个矩阵也是叠加的,函数 DegreesToRadians 可以把角度转为弧度。

 

root ->addChild(glider.get()) ;

root ->addChild(trans.get()) ;

root ->addChild(scale.get()) ;

root ->addChild(rot.get()) ;

把这些矩阵都加入到 root 当中。

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页