OGRE源码分析:场景树模块

引擎名称:OGRE 版本:1.11.5
分析模块:场景树模块(Node,SceneNode,Bone,OctreeNode,TagPoint)

引擎模块介绍

本次分析是场景管理的一部分内容,及场景的组织方式(场景树),该模块的功能是将场景中所需要用到的对象存储起来,以节点、骨骼、八叉树等不同组织方式进行描述场景树。
场景结构图如下:
OGRE源码分析:场景树模块

根节点的位置往往在场景的中心。每个节点对象都用数据成员保存相对于父节点的相对位移和相对旋转量(这一点很重要)。节点类提供节点的移动和旋转函数。这样当节点1发生移动和旋转的时候,其全部子节点及它们挂接的场景元素也将被动地移动和旋转。

引擎模块主要类介绍
OGRE源码分析:场景树模块
这是有关场景树的UML图。作为场景中可以渲染 object 的父类,场景树中的每个节点都是可渲染的,所以都是从 Renderable 中集成而来。同场景树相关的类主要是 Node、SceneNode。
Node类:
一个虚类,定义了大部分操作和接口。Node为场景图的节点基类,定义公共属性
⚫ 有指向多个子节点的指针表
⚫ 有指向多个父节点的指针表
⚫ 增加、删除、修改节点操作
⚫ 包围体和变换计算等操作
⚫ Node派生类为各类场景节点,如可移动对象类、静态场景类、角色类等
场景图的公共基类(Node抽象类),派生两个场景节点类:普通场景节点类(SceneNode)和角色骨骼节点类(Bone)
SceneNode类:
重写了大部分Node的函数,此类主要和场景有关。对Node类进行扩展和补充,例如Node
类只定义了yaw绕y轴旋转,而SceneNode定义了可以绕任何轴旋转的函数。树的普通节点,包含该节点以下子树的变换信息和包围体信息,用attachObject()操作将可移动对象“挂到”该节点中。
OctreeNode类:
定义了八叉树节点的操作。
Bone类:
角色骨骼节点类,定义了以骨骼节点为单位各种变换操作。
TagPoint类:
继承于Bone类,是对悬挂在骨骼上一些素材的操作,例如缩放、平移等。

类的成员函数描述

·Node类:
继承图(关系图):
OGRE源码分析:场景树模块

类型定义:
typedef std::vector<Node*> ChildNodeMap;
typedef VectorIterator ChildNodeIterator;
typedef ConstVectorIterator ConstChildNodeIterator;

Node类功能(成员函数):
Node::Node(const String& name)
构造函数产生一个string类型的节点名称,并且初始化节点变量(定义在OgreNode.h)
以下是Node类里跟子节点有关的成员函数:

Node* Node::createChild(const Vector3& inTranslate, const Quaternion& inRotate)
Node* Node::createChild(const String& name, const Vector3& inTranslate, const Quaternion& inRotate)
void Node::addChild(Node* child)
Node* Node::getChild(unsigned short index) const
Node* Node::removeChild(unsigned short index)
Node* Node::removeChild(Node* child)
void Node::removeAllChildren(void)
Node* Node::getChild(const String& name) const
Node* Node::removeChild(const String& name)
Node::ChildNodeIterator Node::getChildIterator(void)
Node::ConstChildNodeIterator Node::getChildIterator(void) const
其中创建子节点的时候,有两个成员函数,一个是创建未命名的子节点,另一个是创建未命名的子节点。上述函数支撑起了创建子节点,根据索引或节点名字得到一个子节点,删除特定位置或名字的子节点的功能,最后两个函数是返回此节点的迭代器,可用于遍历此节点的子节点,速度比较快。

以下是Node类和位置有关的函数 :
void Node::setPosition(const Vector3& pos)
void Node::setPosition(Real x, Real y, Real z)
Vector3 Node::convertWorldToLocalPosition( const Vector3 &worldPos )
Vector3 Node::convertLocalToWorldPosition( const Vector3 &localPos )
Vector3 Node::convertWorldToLocalDirection( const Vector3 &worldDir, bool useScale )
Vector3 Node::convertLocalToWorldDirection( const Vector3 &localDir, bool useScale )
Quaternion Node::convertWorldToLocalOrientation( const Quaternion &worldOrientation )
Quaternion Node::convertLocalToWorldOrientation( const Quaternion &localOrientation )
设置位置坐标是设置相对于父节点的坐标,可以通过重载函数来接收两种不同的参数,还可以获取世界坐标和方向或本地坐标和方向。还可以通过函数名带有Derived的函数来设置节点的最终坐标和方向。

下面是变换有关的函数:
void Node::setScale(const Vector3& inScale)
void Node::setScale(Real x, Real y, Real z)
void Node::scale(const Vector3& inScale)
void Node::scale(Real x, Real y, Real z)
Matrix3 Node::getLocalAxes(void) const
void Node::translate(const Vector3& d, TransformSpace relativeTo)
void Node::translate(Real x, Real y, Real z, TransformSpace relativeTo)
void Node::translate(const Matrix3& axes, const Vector3& move, TransformSpace relativeTo)
void Node::translate(const Matrix3& axes, Real x, Real y, Real z, TransformSpace relativeTo)
void Node::roll(const Radian& angle, TransformSpace relativeTo)
void Node::pitch(const Radian& angle, TransformSpace relativeTo)
void Node::yaw(const Radian& angle, TransformSpace relativeTo)
void Node::rotate(const Vector3& axis, const Radian& angle, TransformSpace relativeTo)
void Node::rotate(const Quaternion& q, TransformSpace relativeTo)
void Node::setInitialState(void)
设置节点的缩放因子时,要注意与其他变换不同,缩放因子并不总是由子节点继承。 缩放是否影响子节点的大小取决于子节点的setInheritScale选项。 在某些情况下,您希望父节点的缩放因子应用于子节点(例如,子节点是同一对象的一部分,因此您希望它基于父节点的大小具有相同的相对大小),但是 在其他情况下(例如,子节点仅用于定位另一个对象,您希望它保持自己的大小)。 默认设置是继承与其他转换一样。像旋转一样,缩放围绕节点的原点定向。
缩放有两种,一种是根据缩放比例与缩放因子结合进行缩放,另一种是直接对坐标进行操作。
平移有沿着世界笛卡尔轴,即沿着世界x,y,z,通过所提供的矢量移动节点,还有通过相对于自定义轴组的向量转换节点。Roll旋转是绕着z轴进行旋转,pitch旋转是绕着x轴进行旋转,yaw旋转是绕着y轴进行旋转。

·SceneNode类:
继承(关系)图:
OGRE源码分析:场景树模块
从代码中可以看出 SceneNode 类似于 Scene 中的一个 Cell,想象一下在 Octree
Scene Manager 中的一个划分和 BSP 中的 cell。所以 SceneNode 可以认为是一个容
器,可以存放 Movable Object。而存放在同一个 SceneNode 中的 object 应该有一
定的共性。

首先来看构造函数:
SceneNode::SceneNode(SceneManager* creator)
SceneNode::SceneNode(SceneManager* creator, const String& name)
构造函数,仅由创建者SceneManager调用。创建具有生成名称的节点或者特定名称的节点。

下面是和object有关的函数:
void SceneNode::attachObject(MovableObject* obj)
unsigned short SceneNode::numAttachedObjects(void) const
MovableObject* SceneNode::getAttachedObject(unsigned short index)
MovableObject* SceneNode::getAttachedObject(const String& name)
MovableObject* SceneNode::detachObject(unsigned short index)
MovableObject* SceneNode::detachObject(const String& name)
void SceneNode::detachObject(MovableObject* obj)
void SceneNode::detachAllObjects(void)
AttachObject函数将场景对象的实例添加到此节点。场景对象可以包括实体对象,相机对象,灯光对象,粒子系统对象等。来自MovableObject的子类。还可以通过调用numAttachedObjects函数获取附加到此节点的对象数。可以通过索引或者对象的名称来获取附加的对象的指针。另外可以通过索引或名称或者指向对象的指针来分离一个附加的对象。最后,可以调用detachAllObjects把该节点上的所有对象分离。

void SceneNode::_findVisibleObjects(Camera* cam, RenderQueue* queue,
VisibleObjectsBoundsInfo* visibleBounds, bool includeChildren,
bool displayNodes, bool onlyShadowCasters)
这是一个内部方法,用于定位附加到此节点的任何可见对象,并将它们添加到传入的队列中。
只应由SceneManager实现调用,并且只有在调用_updat方法后才能确保转换和世界范围是最新的。参数cam是个活动的相机,queue是SceneManager的渲染队列,visibleBounds
动态创建的边界信息,包含摄像机的所有可见对象,includeChildren如果为true,则会自动将调用级联到所有子节点。displayNodes如果为true,则节点本身将呈现为一组3个轴以及正在呈现的对象。 用于调试目的。

子节点有关的函数:
Node* SceneNode::createChildImpl(void)
Node* SceneNode::createChildImpl(const String& name)
void SceneNode::removeAndDestroyChild(const String& name)
void SceneNode::removeAndDestroyChild(unsigned short index)
void SceneNode::removeAndDestroyChild(SceneNode* child)
void SceneNode::removeAndDestroyAllChildren(void)
SceneNode* SceneNode::createChildSceneNode(const Vector3& inTranslate,
const Quaternion& inRotate)
SceneNode* SceneNode::createChildSceneNode(const String& name, const Vector3& inTranslate,
const Quaternion& inRotate)
上述函数实现了创建一个带有名称的子节点,根据索引或名称移除并且销毁一个子节点,删除附加到此节点的所有子节点。 不删除节点,只是将它们与此父节点分离,可能会重新附加到其他位置的功能。

下面是一些操作函数:
void SceneNode::setDirection(Real x, Real y, Real z, TransformSpace relativeTo,
const Vector3& localDirectionVector)
void SceneNode::setDirection(const Vector3& vec, TransformSpace relativeTo,
const Vector3& localDirectionVector)
void SceneNode::lookAt( const Vector3& targetPoint, TransformSpace relativeTo,
const Vector3& localDirectionVector)
void SceneNode::_autoTrack(void)
SceneNode* SceneNode::getParentSceneNode(void) const
void SceneNode::setVisible(bool visible, bool cascade)
void SceneNode::flipVisibility(bool cascade)
SetDirection函数设置了节点的方向向量,参数x,y,z是各自方向的分量,relativeTo表示此方向向量的空间,localDirectionVector通常描述节点自然方向的向量,通常为-Z。
Lookat函数是将该节点的本地-Z方向指向空间中的某个点,targetPoint是看向的点的向量relativeTo点所在的空间(TS_WORLD、TS_PARENT、TS_LOCAL)localDirectionVector通常描述节点自然方向的向量,通常为-Z。_autotrack是用于更新自动跟踪摄像头的内部方法。
setVisible使得悬挂在这个节点上的对象变得可见/不可见,第一个参数是设置可见性,第二个参数是设置是否级联到子节点。FlipVisibility函数是反转所有悬挂对象的可见性,参数是设置是否级联到子节点。

·OctreeNode类:
继承(关系)图:
OGRE源码分析:场景树模块
在三维空间分割,把场景分成很多更小的部分,并用层次树结构组织这些被分割的部分。八叉树就是把空间分成八等份,如果被分的部分填满了场景内容,或被分的部分没有场景内容,则不再分割;否则,继续分割这部分,直到终止条件满足。被分割的空间可以组成八叉树。

首先来看构造函数:
OctreeNode::OctreeNode( SceneManager* creator ) : SceneNode( creator )
OctreeNode::OctreeNode( SceneManager* creator, const String& name )
初始化mOctant,mOctant是被悬挂的节点。

下面是对移除子节点的函数:
void OctreeNode::_removeNodeAndChildren( )
Node * OctreeNode::removeChild( unsigned short index )
Node * OctreeNode::removeChild( Node* child )
void OctreeNode::removeAllChildren()
Node * OctreeNode::removeChild( const String & name )
removeAllChildren函数是从Node中重写,删除对mOctant所有的引用。删除子节点的引用可以通过三种方式,通过索引,通过名称,通过指针。这些删除相关的函数都是从Node里重写而来的。

OctreeNode类中其他操作函数:
bool OctreeNode::_isIn( AxisAlignedBox &box )
void OctreeNode::_updateBounds( void )
void OctreeNode::_addToRenderQueue( Camera* cam, RenderQueue queue,
bool onlyShadowCasters, VisibleObjectsBoundsInfo
visibleBounds )
void OctreeNode::getRenderOperation( RenderOperation& rend )
isIn函数判断此节点是否在给定的参数box里。_addToRenderQueue函数将所有悬挂的sceneNode添加到渲染队列当中,其中函数内调用processVisibleObject函数需要参数(Camera* cam, RenderQueue queue, bool onlyShadowCasters, VisibleObjectsBoundsInfo visibleBounds )。getRenderOperation函数设置LegacyRenderOperation以将此场景节点渲染为几何体。

·Bone类:
继承(关系)图:
OGRE源码分析:场景树模块
Bone类和SceneNode类都是继承于Node,Bone类主要是存放对象骨骼信息。其中Bone类的大部分成员函数在Mesh类中大量重写,在此主要分析原Bone类中的成员函数。

构造函数:
Bone::Bone(unsigned short handle, Skeleton* creator)
Bone::Bone(const String& name, unsigned short handle, Skeleton* creator)
构造函数,不能直接使用(使用Bone :: createChild或Skeleton :: createBone)createChild函数在下面介绍。

创建有关的函数:
Bone* Bone::createChild(unsigned short handle, const Vector3& inTranslate,
const Quaternion& inRotate)
Node* Bone::createChildImpl(void)
Node* Bone::createChildImpl(const String& name)
createChild函数是创建一个新的骨骼作为此骨骼的子项。要注意的是此方法创建一个新骨骼,该骨骼将继承此骨骼的变换,并指定句柄( Bone* retBone = mCreator->createBone(handle))。参数handle提供新骨骼的数字句柄; 在Skeleton中必须是唯一的。InTranslate是子节点相对于父母的初始平移偏差,inRotate是相对于父级的初始旋转。CreateChild函数可以被两个createChildImpl函数调用,创建一个带或者不带有名称的骨骼。

下面是对骨骼设置有关的函数:
void Bone::setBindingPose(void)
void Bone::reset(void)
void Bone::setManuallyControlled(bool manuallyControlled)
bool Bone::isManuallyControlled() const
unsigned short Bone::getHandle(void) const
setBindingPose函数将当前位置/方向设置为“绑定姿势”,即骨骼最初绑定到网格的布局。
Reset函数是将此骨骼的位置和方向重置为原始绑定位置。调用的是Node类里面的resetToInitialState方法。setManuallyControlled函数设置是否手动控制此骨骼。同样
isManuallyControlled函数是判断此骨骼是不是手动控制的。getHandle函数获取此骨骼的数字句柄(在Skeleton中是唯一的)。

·TagPoint类:
继承(关系)图:
OGRE源码分析:场景树模块

TagPoint::TagPoint(unsigned short handle, Skeleton* creator)
构造函数,初始化句柄和骨架中的构造器。

设置有关的函数:
void TagPoint::setParentEntity(Entity *pEntity)
void TagPoint::setChildObject(MovableObject *pObject)
void TagPoint::setInheritParentEntityOrientation(bool inherit)
void TagPoint::setInheritParentEntityScale(bool inherit)
上述函数实现了设置父节点的实体和设置子节点的对象,分别调用Entity类和MovableObject类中的成员变量。并且通过参数inherit设置了是否继承父节点的方向和缩放因子。

获取信息相关的函数:
Entity TagPoint::getParentEntity(void) const
MovableObject
TagPoint::getChildObject(void) const
bool TagPoint::getInheritParentEntityOrientation(void) const
bool TagPoint::getInheritParentEntityScale(void) const
const Affine3& TagPoint::_getFullLocalTransform(void) const
const Affine3& TagPoint::getParentEntityTransform(void) const
const LightList& TagPoint::getLights(void) const
上述函数实现了获取父级实体和子级对象的功能,同样是返回一个Entity和MovableObject类的参数变量。同样能够判断是否继承了父级的方向、缩放因子、平移量,返回一个bool值。GetLights函数获取灯光列表,列表按照它们与此可渲染器的接近程度排序。