OSG(2):OSG 场景树

以下内容转载自
http://www.javashuo.com/article/p-eobpqhtt-er.html

一、概述

       OSG采用包围体层次(Bounding Volume Hierarchy, HVH)来实现场景图形的管理。即是将一组物体完全封闭在一个简单空间形体中,从而提高各种检测的运算速度。OSG综合使用包围球和包围盒。

       包围体层次的场景图的组织结构为树,OSG通过树来绘制模型。在OSG中存在场景树和渲染树两颗树。场景树由Node组成,这些Node可能是矩阵变换、状态切换或真正的可绘制对象,反映了场景的空间结构和对象的状态。渲染树是一颗一StateSet和RenderLeaf为节点的树。

       在有的地方,组节点被称为枝节节点,叶节点被称为叶子节点。

在这里插入图片描述

二、场景树

       场景采用一种自顶向下的、分层的树状数据结构来组织空间数据集,以提升渲染的效率。

       OSG主要包含3大基本类节点——Node、Geode(Geometry Node,为叶节点,即不会再有子节点,但可包含几何体信息)、Group(组节点)。
       Geode:叶节点。管理和绘制多个Drawable对象。这些可绘制对象可能是几何形体、复杂模型、场景中的文字和图片、喷薄而出的大量粒子,这些需要渲染的可绘制体可能是以某种位置和姿态固定在三维空间中、直接绘制到二维图形窗口设备、自动转向用户所在方向的、形同广告牌(Billboard)的一些特殊物体。
       Group:组节点。最基本的功能是作为场景树结构的枝节存在,将子节点成组进行管理和调度。当一个Group节点的性质发生变化,其所有子节点也会随之发生类似的变化。类似于摇晃一棵树的枝条,其子分支、叶子和果实都会随之晃动甚至掉落。主要分类为:
  (1)空间变换节点:改变自身在三维空间中的位置和姿态,进而影响子节点的空间位置变化。
  (2)开关节点:管理类别信息的显示、冻结或者隐藏。如隐藏图像上所有河流、所有公路;再如分别控制国道省道的显示与否。
  (3)细节层次节点:在不影响渲染外观的前提下,根据实际情况选择一种更简单的方式来表达要渲染的物体,减轻系统绘制场景的负担。一般根据距离来决定显示哪些细节。
  (4)相机节点:从多视角观察同一个场景。如正视、俯视、仰视。
  (5)投影节点:类似投影仪,将子场景投影到二维平面上,从而可以方便地将所需要表达的内容呈现给用户。
  (6)渲染属性节点:表达真实场景不可或缺的要素,光照、雾效果、透明效果、纹理贴图等。OSG为每个节点设置“渲染状态集”的方式来管理渲染属性,因此父节点的渲染属性可以有选择地集成到子节点上。
  (7)覆盖节点:以实例说明:电影院的人看电影,电影院是一个三维场景A;电影这个故事发生在另一个三维场景B中,因此可以称为将场景A覆盖到电影院的荧幕对象上。
  (8)遮挡裁剪节点:由于计算机硬件条件限制,大规模场景通常不能直接绘制,只有剔除掉用户看不到的地方,如背面、视锥体、被遮挡等。
  (9)动态调度节点:当将大量模型读入内存时,计算机系统负担较大,可能会因为内存不足而程序出错,甚至系统崩溃,此时需要一种动态调度的机制。对于该节点,当它的某些子节点对场景绘制长期没有助益时可以将子节点自动卸载,释放内存空间;也可以即时加载某些不再内存中的子节点,即动态对其场景子树进行调度控制。
  (10)关节节点:人体动作动画,或者机械臂的旋转移动。
  (11)代理节点:只作为另外一种尚未加载的节点的代理者而存在,被代理的对象会在适当的时候被加载到内存中;在此之前,代理者起到占位和节省系统启动时间的作用。
  具体继承关系如图所示:
在这里插入图片描述

三、节点访问

       获取某个节点对象并读取它的属性,以及对其执行指定用户操作的过程。

       向不同的节点元素施加用户自定义的操作,将这些操作整合到一个对象中,同时避免这些操作可能造成的数据结构本身的变化,从而实现了一种更为自由的编码方式。

       对于节点的访问是从节点接受一个访问器开始的,用户执行某个节点的accept()函数,将一个具体的访问器对象传递给节点。然后节点反过来执行访问器的apply()函数,并将自身传入访问器。最后,在具体访问器对象中重写的apply()函数中,执行节点的具体访问操作。

       OSG中节点访问器对于场景树的访问采用的是深度优先遍历的形式:访问节点直至末端的叶节点,再逐步返回到上一级尚未访问的节点。

       OSG中的节点主要使用回调来完成用户临时定义的、需要每帧执行的工作。包括更新回调和人机交互事件回调。前者在每帧系统遍历到当前节点时都会被自动调用;后者为操作键盘、鼠标、关闭窗口、改变窗口尺寸等人机交互事件触发。

四、节点方法

1、osg::Node类

Node() //默认构造函数
void dirtyBound() //提示更新节点的包围体
const BoundingSphere &getBound() const //获取节点的包围体
BoundingSphere computeBound() const //虚函数,计算节点包围体
const ParentList getParents() const //获取节点的父节点列表
const Group *getParent(unsigned int i) const //获取制定的父节点
unsigned int getNumParents() const //获取父节点的数目
void addParent(Group *node) //为当前节点追加一个父节点
void removeParent(Group *node) //删除当前节点的某个父节点
 
void accept(NodeVisitor &nv) //接受一个访问器
 
void ascend(NodeVisitor &nv) //虚函数。向上一级节点推进访问器
void traverse(NodeVisitor &nv) //虚函数。向下一级节点推进访问器
 
//例:获取和操作节点的每一个父节点的方法
for (unsigned int i = 0; i<getNumParents(); ++i)
{
	const osg::Group *parent = node.getParent(i);
	/*执行parent对象的操作*/
}
 
void setUpdateCallback(NodeCallback *) //设置节点的更新回调
NodeCallback *getUpdateCallback() //获取节点的更新回调
void setEventCallback(NodeCallback *) //设置节点的交互事件回调
NodeCallback *getEventCallback() //获取节点的交互事件回调

2、osg::Geode类

Geode() //默认构造函数
bool addDrawable(Drawable *) //从叶节点追加一个可绘制体对象
boolremoveDrawable(Drawable *) //从叶节点删除一个可绘制体对象
virtual bool removeDrawables(unsigned int i, unsigned int numToRemove)//从制定索引位置开始,删除制定数目的可绘制体
bool replaceDrawable(Drawable *origDraw, Drawable *newDraw) //将当前节点中包含的一个可绘制体替换为新的可绘制体
unsigned int getNumDrawables() const //获取可绘制体的数目
Drawable *getDrawables(unsigned int i) //获取一个指定位置的可绘制体
unsigned int getDrawableIndex(const Drawable *) const //获取一个可绘制体在叶节点的索引位置
const DrawableList &getDrawableList() const //获取可绘制体的列表
 
//例:在可绘制体drawable1和drawable2中加入一个叶节点,然后从叶节点中获取并操作索引位置为i的可绘制体
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(drawable1);
geode->addDrawable(drawable2);
osg::Drawable *drawable = geode->getDrawable(i);

3、osg::Group类

Group() //默认构造函数
bool addChild(Node *child) //追加一个子节点
bool removeChild(Node *child) //删除一个子节点
bool removeChildren(unsigned int pos, unsigned int numToRemove) //从指引索引位置开始,删除制定数目的子节点
bool insertChild(unsigned int index, Node *child) //向指定索引位置插入一个子节点
bool replaceChild(Node *origChild, Node *newChild) //将当前节点中包含一个子节点替换为新的子节点
unsigned int getNumChildren() const //获取子节点的数目
Node *getChild(unsigned int i) //获取一个指定位置的子节点
unsigned int getChildIndex(const Node *node) const //获取一个子节点的索引位置

4、osg::NodeVisitor类

NodeVisitor(TraversalMode tm) //构造函数。传入参数为节点树的遍历方式:TRAVERSE_NODE仅当前节点;TRAVERSE_PARENTS向当前节点的父节点遍历;TRAVERSE_ALL_CHILDREN向子节点遍历
void traverse(Node &node) //向下一个需要访问的节点推进
//虚函数。访问各种类型的节点,并执行访问器中自定义的节点操作
void apply(Node &node)
void apply(Node &node)
void apply(Node &node)

5、osg::NodeCallback类

NodeCallback() //默认构造函数
void operator()(Node *node, NodeVisitor *nv) //虚函数。当回调动作发生时,将会执行这一操作符的内容,并将节点和访问器对象作为参数传入
void addNestedCallback(NodeCallback *) //添加一个临近回调,其内容将在节点回调的执行过程中被依次调用
void removeNestedCallback(NodeCallback *) //移除一个临近回调
void setNestedCallback(NodeCallback *) //直接设置一个临近回调
NodeCallback *getNestedCallback(NodeCallback *) //直接获取一个临近回调

6、osg::Transform类

Transform() //默认构造函数
void setReferenceFrame(ReferenceFrame rf) //设置节点所用的参考坐标系
ReferenceFrame getReferenceFrame() const //获取节点所用的参考坐标系
bool computerLocalToWorldMatrix(Matrix &matrix, NodeVisitor *nv) const //虚函数。计算从局部坐标系到世界坐标系的级联矩阵,保存到matrix变量中
bool computerWorldToLocalMatrix(Matrix &matrix, NodeVisitor *nv) const //虚函数。计算从世界坐标系到局部坐标系的级联矩阵,保存到matrix变量

7、osg::MatrixTransform类

MatrixTransform() //默认构造函数
void setMatrix(const Matrix &mat) //设置空间变换矩阵的内容
const Matrix &getMatrix() const //获取空间变换矩阵的内容
void preMult(const Matrix &mat) //递乘,比较类似于++a
void postMult(const Matrix &mat) //递乘,比较类似于a++
const Matrix &getInverseMatrix() const //得到逆矩阵

8、osg::PositionAttitudeTransform类

PositionAttitudeTransform() //默认构造函数
void setPosition(const VEC3D &) //设置空间中平移的距离
const Vec3d &getPosition() const //获取空间中平移的距离
void setAttitude(const Quat &) //设置空间中旋转的四元数
const Quat &getAttitude() const //获取空间中旋转的四元数
void setScale(const Vec3d &) //设置空间缩放的倍数
const Vec3d& getScale() const //获取空间缩放的倍数
void setPivotPoint(const Vec3d&) //设置空间旋转与缩放的轴心位置
const Vec3d& getPivotPoint() const //获取空间旋转与缩放的轴心位置

9、osg::Switch类

Switch() //默认构造函数
bool addChild(Node *child, bool) //添加一个子节点,同时设置其开关值
void setValue(unsigned int pos, bool) //设置指定索引pos位置子节点的开关值
bool getValue(unsigned int pos) const //获取指定索引pos位置子节点的开关值
void setNewChildDefaultValue(bool value) //设置新加节点的初始值
bool getNewChildDefaultValue() const //得到新加节点的初始值
void setChildValue(const Node *child, bool value) //设置child的值
bool getChildValue(const Node *child) const //得到child的值
bool setAllChildrenOff() //设置所有子节点不显示
bool setAllChildrenOn() //设置所有子节点显示
bool setSingleChildOn(unsigned int pos) //设置索引pos单个节点显示

10、osg::LOD类

LOD() //默认构造函数
bool addChild(Node *child, float min, float max) //添加一个子节点,并设置其对应的观察范围
void setRange(unsigned int childNo, float min, float max) //设置指定位置的子节点对应的观察范围
float getMinRange(unsigned int childNo) const //获取某个子节点对应的观察最小值
float getMaxRange(unsigned int childNo) const //获取某个子节点对应的观察最大值
const RangeList &getRangeList() const //获取所有子节点观察范围的列表