Ogre引擎源码——资源之Skeleton

http://blog.****.net/hunter8777/article/details/6407477

Ogre中Resouce资源有以下几种类型:Texture、Compositor、FontGpuProgramMaterial、Mesh、Skeleton、BspLevel。

本文聚焦于资源Skeleton,这部分资源是Ogre处理动画的环节。Ogre支持三种动画方式:骨骼动画(Skeletal)、变形动画(Morph)以及姿态动画(Pose)。

相关的头文件如下:
OgreNode.h
OgreBone.h
OgreKeyFrame.h
OgreAnimation.h
OgreAnimationTrack.h
OgreSkeleton.h
OgreSkeletonFileFormat.h

1、OgreNode与OgreBone

Ogre引擎源码——资源之Skeleton

Ogre中的骨骼动画,Bone就是相应的骨骼类。在关注这个Bone类之前,我们需要来看下它的父类——Node类。在Ogre场景组织中,Node是一个重要的基类,一个Node对应了场景中的一个组成部分。场景中的所有节点都被组织成一颗结构树,父节点的变换可以影响它相应的子节点。这些基础的变换实现正好符合Bone的需求。

先来看下Node类中重要的数据成员:

  1. ///Pointertoparentnode
  2. Node*mParent;
  3. ///Collectionofpointerstodirectchildren;hashmapforefficiency
  4. typedefHashMap<String,Node*>ChildNodeMap;
  5. ChildNodeMapmChildren;
  6. StringmName;
  7. ///Storestheorientationofthenoderelativetoit'sparent.
  8. QuaternionmOrientation;
  9. ///Storestheposition/translationofthenoderelativetoitsparent.
  10. Vector3mPosition;
  11. ///Storesthescalingfactorappliedtothisnode
  12. Vector3mScale;
  1. ///Pointertoparentnode
  2. Node*mParent;
  3. ///Collectionofpointerstodirectchildren;hashmapforefficiency
  4. typedefHashMap<String,Node*>ChildNodeMap;
  5. ChildNodeMapmChildren;
  6. StringmName;
  7. ///Storestheorientationofthenoderelativetoit'sparent.
  8. QuaternionmOrientation;
  9. ///Storestheposition/translationofthenoderelativetoitsparent.
  10. Vector3mPosition;
  11. ///Storesthescalingfactorappliedtothisnode
  12. Vector3mScale;
/// Pointer to parent node Node* mParent; /// Collection of pointers to direct children; hashmap for efficiency typedef HashMap<String, Node*> ChildNodeMap; ChildNodeMap mChildren; String mName; /// Stores the orientation of the node relative to it's parent. Quaternion mOrientation; /// Stores the position/translation of the node relative to its parent. Vector3 mPosition; /// Stores the scaling factor applied to this node Vector3 mScale;

每个Node节点保存了自己的父节点指针,所有的子节点指针,以及当前节点的基本空间信息:方向、位置、缩放。

Node类的成员函数主要有两类功能:
(a)管理子节点

  1. virtualNode*createChildconstVector3&translate,constQuaternion&rotate);
  2. virtualvoidaddChild(Node*child);
  3. virtualunsignedshortnumChildren(void)const;
  4. virtualNode*removeChild(unsignedshortindex);
  5. virtualNode*removeChild(Node*child);
  1. virtualNode*createChildconstVector3&translate,constQuaternion&rotate);
  2. virtualvoidaddChild(Node*child);
  3. virtualunsignedshortnumChildren(void)const;
  4. virtualNode*removeChild(unsignedshortindex);
  5. virtualNode*removeChild(Node*child);
virtual Node* createChildconst Vector3& translate, const Quaternion& rotate ); virtual void addChild(Node* child); virtual unsigned short numChildren(void) const; virtual Node* removeChild(unsigned short index); virtual Node* removeChild(Node* child);

其中儿子节点可以以名字或者索引两种Key形式。

(b)进行变换

  1. virtualvoidscale(constVector3&scale);
  2. virtualvoidtranslate(constVector3&d,TransformSpacerelativeTo);
  3. virtualvoidroll(constRadian&angle,TransformSpacerelativeTo);
  4. virtualvoidpitch(constRadian&angle,TransformSpacerelativeTo);
  5. virtualvoidyaw(constRadian&angle,TransformSpacerelativeTo);
  6. virtualvoidrotate(constVector3&axis,constRadian&angle,TransformSpacerelativeTo);
  7. virtualMatrix3getLocalAxes(void)const;
  1. virtualvoidscale(constVector3&scale);
  2. virtualvoidtranslate(constVector3&d,TransformSpacerelativeTo);
  3. virtualvoidroll(constRadian&angle,TransformSpacerelativeTo);
  4. virtualvoidpitch(constRadian&angle,TransformSpacerelativeTo);
  5. virtualvoidyaw(constRadian&angle,TransformSpacerelativeTo);
  6. virtualvoidrotate(constVector3&axis,constRadian&angle,TransformSpacerelativeTo);
  7. virtualMatrix3getLocalAxes(void)const;
virtual void scale(const Vector3& scale); virtual void translate(const Vector3& d, TransformSpace relativeTo); virtual void roll(const Radian& angle, TransformSpace relativeTo); virtual void pitch(const Radian& angle, TransformSpace relativeTo); virtual void yaw(const Radian& angle, TransformSpace relativeTo); virtual void rotate(const Vector3& axis, const Radian& angle, TransformSpace relativeTo); virtual Matrix3 getLocalAxes(void) const;

Node类里还有个重要的函数

  1. virtualvoid_update(boolupdateChildren,boolparentHasChanged);
  1. virtualvoid_update(boolupdateChildren,boolparentHasChanged);
virtual void _update(bool updateChildren, bool parentHasChanged);

这个函数从父类获取(如果有父类的话)变换信息,对自身和子类施加变换操作。


主要的代码片段如下:

  1. ChildNodeMap::iteratorit,itend;
  2. itend=mChildren.end();
  3. for(it=mChildren.begin();it!=itend;++it)
  4. {
  5. Node*child=it->second;
  6. child->_update(true,true);
  7. }
  8. mChildrenToUpdate.clear();
  1. ChildNodeMap::iteratorit,itend;
  2. itend=mChildren.end();
  3. for(it=mChildren.begin();it!=itend;++it)
  4. {
  5. Node*child=it->second;
  6. child->_update(true,true);
  7. }
  8. mChildrenToUpdate.clear();
ChildNodeMap::iterator it, itend; itend = mChildren.end(); for (it = mChildren.begin(); it != itend; ++it) { Node* child = it->second; child->_update(true, true); } mChildrenToUpdate.clear();

这里就是一个迭代的过程,对本节点所有的儿子节点同样调用它们的_update函数,使该变换不断传递下去。

可以看出Node类是一个封装了节点变换信息的类,该变换是以树节点的形式进行组织,父类的变换会影响它的所有/部分子节点。

下面就来关注下Bone类。
关于骨骼动画,目前Ogre只支持关键帧形式的正向动力学(FK);也就是说没有提供对逆向动力学(IK)骨骼动画的内建支持。
所谓的正向动力学(FK, forward kinematics,也称为前向动力学)是指完全遵循父子关系的层级,用父层级带动子层级的运动。这也正如我们在_update函数中所看到的那样,变换递推过程从父类不断向子类传递。正向动力学的优点是:计算简单,运算速度快,缺点是:需指定每个关节的角度和位置,而由于骨架的各个节点之间有内在的关联性,直接指定各关节的值很容易产生不自然协调的动作。

Node类几乎都实现了Bone类最重要的变换信息保存以及变换信息的传递操作。Bone类只是在此基础上添加了一些很简单的内容。

  1. ///Thenumerichandleofthisbone
  2. unsignedshortmHandle;
  3. Skeleton*mCreator;
  4. ///Theinversedderivedscaleoftheboneinthebindingpose
  5. Vector3mBindDerivedInverseScale;
  6. ///Theinversedderivedorientationoftheboneinthebindingpose
  7. QuaternionmBindDerivedInverseOrientation;
  8. ///Theinversedderivedpositionoftheboneinthebindingpose
  9. Vector3mBindDerivedInversePosition;
  1. ///Thenumerichandleofthisbone
  2. unsignedshortmHandle;
  3. Skeleton*mCreator;
  4. ///Theinversedderivedscaleoftheboneinthebindingpose
  5. Vector3mBindDerivedInverseScale;
  6. ///Theinversedderivedorientationoftheboneinthebindingpose
  7. QuaternionmBindDerivedInverseOrientation;
  8. ///Theinversedderivedpositionoftheboneinthebindingpose
  9. Vector3mBindDerivedInversePosition;
/// The numeric handle of this bone unsigned short mHandle; Skeleton* mCreator; /// The inversed derived scale of the bone in the binding pose Vector3 mBindDerivedInverseScale; /// The inversed derived orientation of the bone in the binding pose Quaternion mBindDerivedInverseOrientation; /// The inversed derived position of the bone in the binding pose Vector3 mBindDerivedInversePosition;

Handle是一个用来标示Bone的值。
Skeleton(后面会介绍)是集中管理Bone的类。所以在Bone中保存了一个指向创建自己的Skeleton指针。
最后的3个变量值是子节点反变换回父节点位置的变换信息。
这些信息是在函数void _getOffsetTransform(Matrix4& m)中用来生成变换矩阵:

  1. Vector3scale=_getDerivedScale()*mBindDerivedInverseScale;
  2. Quaternionrotate=_getDerivedOrientation()*mBindDerivedInverseOrientation;
  3. Vector3translate=_getDerivedPosition()+rotate*(scale*mBindDerivedInversePosition);
  4. m.makeTransform(translate,scale,rotate);
  1. Vector3scale=_getDerivedScale()*mBindDerivedInverseScale;
  2. Quaternionrotate=_getDerivedOrientation()*mBindDerivedInverseOrientation;
  3. Vector3translate=_getDerivedPosition()+rotate*(scale*mBindDerivedInversePosition);
  4. m.makeTransform(translate,scale,rotate);
Vector3 scale = _getDerivedScale() * mBindDerivedInverseScale; Quaternion rotate = _getDerivedOrientation() * mBindDerivedInverseOrientation; Vector3 translate = _getDerivedPosition() + rotate * (scale * mBindDerivedInversePosition); m.makeTransform(translate, scale, rotate);

2、OgreKeyFrame、OgreAnimationTrack与OgreAnimation
Bone类定义动画中的骨骼。我们需要接着定义相应的动画信息,然后将每个动画动作的变换信息赋予这些骨骼,这样我们就可以是物体按照我们设定的规则“运动”起来。而这些动画的信息就保存在这一组类中。

Ogre引擎源码——资源之Skeleton

最右侧有个类KeyFrame,它是这组类中最基本的一个。我们就从它开始吧。
类如其名,KeyFrame对应的就是动画中的一帧。它是一个基类,派生出四种不同类型的子类。

  1. class_OgreExportKeyFrame:publicAnimationAlloc
  2. {
  3. public:
  4. KeyFrame(constAnimationTrack*parent,Realtime);
  5. virtual~KeyFrame(){}
  6. RealgetTime(void)const{returnmTime;}
  7. virtualKeyFrame*_clone(AnimationTrack*newParent)const;
  8. protected:
  9. RealmTime;
  10. constAnimationTrack*mParentTrack;
  11. };
  1. class_OgreExportKeyFrame:publicAnimationAlloc
  2. {
  3. public:
  4. KeyFrame(constAnimationTrack*parent,Realtime);
  5. virtual~KeyFrame(){}
  6. RealgetTime(void)const{returnmTime;}
  7. virtualKeyFrame*_clone(AnimationTrack*newParent)const;
  8. protected:
  9. RealmTime;
  10. constAnimationTrack*mParentTrack;
  11. };
class _OgreExport KeyFrame : public AnimationAlloc { public: KeyFrame(const AnimationTrack* parent, Real time); virtual ~KeyFrame() {} Real getTime(void) const { return mTime; } virtual KeyFrame* _clone(AnimationTrack* newParent) const; protected: Real mTime; const AnimationTrack* mParentTrack; };

KeyFrame类的定义就这样简单的几行。它的成员变量只有两个。一个是表示该帧在整个动画轨迹中的时间位置;还有一个就是创建该帧的AnimationTrack类。

KeyFrame类并没有保存每帧需保存的空间变换信息,因为Ogre只是四种不同的关键帧,它们各自有自己需要保存的变换信息,所以在KeyFrame的派生类中可以找到相应的内容。

KeyFrame的四个派生类:
数字关键帧(NumericKeyFrame)
变换关键帧(TransformKeyFrame)
顶点变形关键帧(VertexMorphKeyFrame)
顶点姿态关键帧(VertexPoseKeyFrame)

比如:数字关键帧(NumericKeyFrame)中就保存了相应的数字数据;变换关键帧(TransformKeyFrame)中则保存了变换、缩放和旋转信息等。(这些关键帧会在相应Track里再提到)

在UML图的*,我们可以看到,KeyFrame类是由AnimationTrack的类负责创建和管理的。KeyFrame的四个子类对应了三种动画轨迹类型(最后两种KeyFrame子类对应同一种AnimationTrack),也就定义了Ogre所支持的三种动画轨迹方式。

先来看下这些动画轨迹的父类AnimationTrack
AnimationTrack的主要成员变量如下:

  1. typedefvector<KeyFrame*>::typeKeyFrameList;
  2. KeyFrameListmKeyFrames;
  3. Animation*mParent;
  4. unsignedshortmHandle;
  1. typedefvector<KeyFrame*>::typeKeyFrameList;
  2. KeyFrameListmKeyFrames;
  3. Animation*mParent;
  4. unsignedshortmHandle;
typedef vector<KeyFrame*>::type KeyFrameList; KeyFrameList mKeyFrames; Animation* mParent; unsigned short mHandle;

一个Track表示一个动画序列,也就是一组有序的KeyFrame,用来指示一个可运动物体的某个动作轨迹。
mKeyFrames就是该Track保存的一组关键帧
mParent指向创建和管理Track的Animation类
mHandle则与Bone类中出现的一样,用来标示一个Track

一些重要的成员函数:(虚函数,用以子类接口继承)
virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const = 0;
纯虚函数,根据制定的timeindex,返回插值后的KeyFrame kf。
virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) = 0;
纯虚函数,应用这个动画轨迹。子类根据自己所属的不同关键帧方式来实现相应的动画轨迹。
virtual void optimise(void) {}
虚函数,优化这个动画轨迹,方法为删除重复帧。

AnimationTrack有三个子类
数字动画轨迹(NumericAnimationTrack):对应的是数字关键帧(NumericKeyFrame)
该类比较简单,插值和轨迹应用都是根据数值来进行,就是简单的线性插值。

节点动画轨迹(NodeAnimationTrack):对应的是变换关键帧(TransformKeyFrame)
每个变换关键帧都包含了两个三元向量和一个四元数,分别用来表现节点在当前帧的位置、缩放以及方向。所以该轨迹中的插值主要针对的是这些空间向量。插值的方式分为线性和样条曲线两种。
当内存空间够用时,可以定义如下样条并设置变量mSplineBuildNeeded为true来获得更好的插值效果。

  1. structSplines
  2. {
  3. SimpleSplinepositionSpline;
  4. SimpleSplinescaleSpline;
  5. RotationalSplinerotationSpline;
  6. };
  1. structSplines
  2. {
  3. SimpleSplinepositionSpline;
  4. SimpleSplinescaleSpline;
  5. RotationalSplinerotationSpline;
  6. };
struct Splines { SimpleSpline positionSpline; SimpleSpline scaleSpline; RotationalSpline rotationSpline; };

顶点动画轨迹(VertexAnimationTrack):对应顶点变形关键帧(VertexMorphKeyFrame)和顶点姿态关键帧(VertexPoseKeyFrame),每个关键帧保存了特定时间的顶点位置数据,在姿态动画(Pose)中还保存了顶点混合权重。

两种不同的顶点动画:
变形动画:记录每个关键帧时刻所有顶点的快照集合。
姿态动画:记录所有顶点的偏移,根据不同的权重来融合不同的姿态,来产生最后的结果。在面部动画中,姿态动画应用比较广泛。

现在再来看Animation类就简单很多了:

  1. ///Nodetracks,indexedbyhandle
  2. NodeTrackListmNodeTrackList;
  3. ///Numerictracks,indexedbyhandle
  4. NumericTrackListmNumericTrackList;
  5. ///Vertextracks,indexedbyhandle
  6. VertexTrackListmVertexTrackList;
  7. StringmName;
  8. RealmLength;
  9. InterpolationModemInterpolationMode;
  10. RotationInterpolationModemRotationInterpolationMode;
  1. ///Nodetracks,indexedbyhandle
  2. NodeTrackListmNodeTrackList;
  3. ///Numerictracks,indexedbyhandle
  4. NumericTrackListmNumericTrackList;
  5. ///Vertextracks,indexedbyhandle
  6. VertexTrackListmVertexTrackList;
  7. StringmName;
  8. RealmLength;
  9. InterpolationModemInterpolationMode;
  10. RotationInterpolationModemRotationInterpolationMode;
/// Node tracks, indexed by handle NodeTrackList mNodeTrackList; /// Numeric tracks, indexed by handle NumericTrackList mNumericTrackList; /// Vertex tracks, indexed by handle VertexTrackList mVertexTrackList; String mName; Real mLength; InterpolationMode mInterpolationMode; RotationInterpolationMode mRotationInterpolationMode;

Animation类各保存有上述三个动画轨迹的一个列表,并记录下当前动画的名字、长度以及插值方式。

该类中最重要的是一组apply函数。该类函数的功能差不多相同,就是迭代相应的AnimationTrack列表,分别调用AnimationTrack的apply函数即可。

值得一提的是在这组apply函数API里,有用到Node(Bone的父类)和Skeleton作为参数类型的。可以猜测到,在这些apply函数中,关键帧的变换信息插值计算出来后,被附加到了骨骼Node上,也即对骨骼变换信息进行了更新。关键帧、动画和骨骼这三个概念在这个函数里汇集到了一起!

3、OgreSkeleton
最后集大成者就是Skeleton类了,先来看下UML图。

Ogre引擎源码——资源之Skeleton

开篇已经提到,Skeleton是Ogre八种资源子类中的一种。Skeleton类中最重要的数据结构就是对Bone(本文的第一部分)和Animation(本文的第二部分)的管理。


如下是两个管理的列表:

  1. ///Storageofbones,indexedbybonehandle
  2. BoneListmBoneList;
  3. ///Storageofanimations,lookupbyname
  4. AnimationListmAnimationsList;
  1. ///Storageofbones,indexedbybonehandle
  2. BoneListmBoneList;
  3. ///Storageofanimations,lookupbyname
  4. AnimationListmAnimationsList;
/// Storage of bones, indexed by bone handle BoneList mBoneList; /// Storage of animations, lookup by name AnimationList mAnimationsList;

Skeleton类中较多的成员函数是对这两个列表成员的具体操作,如创建、添加、删除一个Bone或者AnimationList等。
_getAnimation成员函数获取相应的Animation类,然后在setAnimationState函数中将该动画变换附加所有骨骼上。_updateTransforms成员函数的作用是遍历所有Bone,调用每个Bone的update函数进行变换更新。

还有值得一提的是LinkedSkeletonAnimationSource结构,它允许Skeleton类使用其他的Animation类来实现动画。

本文完。

参考内容
网站:
http://www.ogre3d.cn/wiki/index.php?title=%E9%A6%96%E9%A1%B5
http://blog.****.net/pizi0475/archive/2010/03/11/5370338.aspx
http://hi.baidu.com/bdruiruili/blog/item/e2024135d351861f90ef399f.html
书籍:PRO OGRE 3D PROGRAMMING

http://blog.****.net/hunter8777/article/details/6407477

Ogre中Resouce资源有以下几种类型:Texture、Compositor、FontGpuProgramMaterial、Mesh、Skeleton、BspLevel。

本文聚焦于资源Skeleton,这部分资源是Ogre处理动画的环节。Ogre支持三种动画方式:骨骼动画(Skeletal)、变形动画(Morph)以及姿态动画(Pose)。

相关的头文件如下:
OgreNode.h
OgreBone.h
OgreKeyFrame.h
OgreAnimation.h
OgreAnimationTrack.h
OgreSkeleton.h
OgreSkeletonFileFormat.h

1、OgreNode与OgreBone

Ogre引擎源码——资源之Skeleton

Ogre中的骨骼动画,Bone就是相应的骨骼类。在关注这个Bone类之前,我们需要来看下它的父类——Node类。在Ogre场景组织中,Node是一个重要的基类,一个Node对应了场景中的一个组成部分。场景中的所有节点都被组织成一颗结构树,父节点的变换可以影响它相应的子节点。这些基础的变换实现正好符合Bone的需求。

先来看下Node类中重要的数据成员:

  1. ///Pointertoparentnode
  2. Node*mParent;
  3. ///Collectionofpointerstodirectchildren;hashmapforefficiency
  4. typedefHashMap<String,Node*>ChildNodeMap;
  5. ChildNodeMapmChildren;
  6. StringmName;
  7. ///Storestheorientationofthenoderelativetoit'sparent.
  8. QuaternionmOrientation;
  9. ///Storestheposition/translationofthenoderelativetoitsparent.
  10. Vector3mPosition;
  11. ///Storesthescalingfactorappliedtothisnode
  12. Vector3mScale;
  1. ///Pointertoparentnode
  2. Node*mParent;
  3. ///Collectionofpointerstodirectchildren;hashmapforefficiency
  4. typedefHashMap<String,Node*>ChildNodeMap;
  5. ChildNodeMapmChildren;
  6. StringmName;
  7. ///Storestheorientationofthenoderelativetoit'sparent.
  8. QuaternionmOrientation;
  9. ///Storestheposition/translationofthenoderelativetoitsparent.
  10. Vector3mPosition;
  11. ///Storesthescalingfactorappliedtothisnode
  12. Vector3mScale;
/// Pointer to parent node Node* mParent; /// Collection of pointers to direct children; hashmap for efficiency typedef HashMap<String, Node*> ChildNodeMap; ChildNodeMap mChildren; String mName; /// Stores the orientation of the node relative to it's parent. Quaternion mOrientation; /// Stores the position/translation of the node relative to its parent. Vector3 mPosition; /// Stores the scaling factor applied to this node Vector3 mScale;

每个Node节点保存了自己的父节点指针,所有的子节点指针,以及当前节点的基本空间信息:方向、位置、缩放。

Node类的成员函数主要有两类功能:
(a)管理子节点

  1. virtualNode*createChildconstVector3&translate,constQuaternion&rotate);
  2. virtualvoidaddChild(Node*child);
  3. virtualunsignedshortnumChildren(void)const;
  4. virtualNode*removeChild(unsignedshortindex);
  5. virtualNode*removeChild(Node*child);
  1. virtualNode*createChildconstVector3&translate,constQuaternion&rotate);
  2. virtualvoidaddChild(Node*child);
  3. virtualunsignedshortnumChildren(void)const;
  4. virtualNode*removeChild(unsignedshortindex);
  5. virtualNode*removeChild(Node*child);
virtual Node* createChildconst Vector3& translate, const Quaternion& rotate ); virtual void addChild(Node* child); virtual unsigned short numChildren(void) const; virtual Node* removeChild(unsigned short index); virtual Node* removeChild(Node* child);

其中儿子节点可以以名字或者索引两种Key形式。

(b)进行变换

  1. virtualvoidscale(constVector3&scale);
  2. virtualvoidtranslate(constVector3&d,TransformSpacerelativeTo);
  3. virtualvoidroll(constRadian&angle,TransformSpacerelativeTo);
  4. virtualvoidpitch(constRadian&angle,TransformSpacerelativeTo);
  5. virtualvoidyaw(constRadian&angle,TransformSpacerelativeTo);
  6. virtualvoidrotate(constVector3&axis,constRadian&angle,TransformSpacerelativeTo);
  7. virtualMatrix3getLocalAxes(void)const;
  1. virtualvoidscale(constVector3&scale);
  2. virtualvoidtranslate(constVector3&d,TransformSpacerelativeTo);
  3. virtualvoidroll(constRadian&angle,TransformSpacerelativeTo);
  4. virtualvoidpitch(constRadian&angle,TransformSpacerelativeTo);
  5. virtualvoidyaw(constRadian&angle,TransformSpacerelativeTo);
  6. virtualvoidrotate(constVector3&axis,constRadian&angle,TransformSpacerelativeTo);
  7. virtualMatrix3getLocalAxes(void)const;
virtual void scale(const Vector3& scale); virtual void translate(const Vector3& d, TransformSpace relativeTo); virtual void roll(const Radian& angle, TransformSpace relativeTo); virtual void pitch(const Radian& angle, TransformSpace relativeTo); virtual void yaw(const Radian& angle, TransformSpace relativeTo); virtual void rotate(const Vector3& axis, const Radian& angle, TransformSpace relativeTo); virtual Matrix3 getLocalAxes(void) const;

Node类里还有个重要的函数

  1. virtualvoid_update(boolupdateChildren,boolparentHasChanged);
  1. virtualvoid_update(boolupdateChildren,boolparentHasChanged);
virtual void _update(bool updateChildren, bool parentHasChanged);

这个函数从父类获取(如果有父类的话)变换信息,对自身和子类施加变换操作。


主要的代码片段如下:

  1. ChildNodeMap::iteratorit,itend;
  2. itend=mChildren.end();
  3. for(it=mChildren.begin();it!=itend;++it)
  4. {
  5. Node*child=it->second;
  6. child->_update(true,true);
  7. }
  8. mChildrenToUpdate.clear();
  1. ChildNodeMap::iteratorit,itend;
  2. itend=mChildren.end();
  3. for(it=mChildren.begin();it!=itend;++it)
  4. {
  5. Node*child=it->second;
  6. child->_update(true,true);
  7. }
  8. mChildrenToUpdate.clear();
ChildNodeMap::iterator it, itend; itend = mChildren.end(); for (it = mChildren.begin(); it != itend; ++it) { Node* child = it->second; child->_update(true, true); } mChildrenToUpdate.clear();

这里就是一个迭代的过程,对本节点所有的儿子节点同样调用它们的_update函数,使该变换不断传递下去。

可以看出Node类是一个封装了节点变换信息的类,该变换是以树节点的形式进行组织,父类的变换会影响它的所有/部分子节点。

下面就来关注下Bone类。
关于骨骼动画,目前Ogre只支持关键帧形式的正向动力学(FK);也就是说没有提供对逆向动力学(IK)骨骼动画的内建支持。
所谓的正向动力学(FK, forward kinematics,也称为前向动力学)是指完全遵循父子关系的层级,用父层级带动子层级的运动。这也正如我们在_update函数中所看到的那样,变换递推过程从父类不断向子类传递。正向动力学的优点是:计算简单,运算速度快,缺点是:需指定每个关节的角度和位置,而由于骨架的各个节点之间有内在的关联性,直接指定各关节的值很容易产生不自然协调的动作。

Node类几乎都实现了Bone类最重要的变换信息保存以及变换信息的传递操作。Bone类只是在此基础上添加了一些很简单的内容。

  1. ///Thenumerichandleofthisbone
  2. unsignedshortmHandle;
  3. Skeleton*mCreator;
  4. ///Theinversedderivedscaleoftheboneinthebindingpose
  5. Vector3mBindDerivedInverseScale;
  6. ///Theinversedderivedorientationoftheboneinthebindingpose
  7. QuaternionmBindDerivedInverseOrientation;
  8. ///Theinversedderivedpositionoftheboneinthebindingpose
  9. Vector3mBindDerivedInversePosition;
  1. ///Thenumerichandleofthisbone
  2. unsignedshortmHandle;
  3. Skeleton*mCreator;
  4. ///Theinversedderivedscaleoftheboneinthebindingpose
  5. Vector3mBindDerivedInverseScale;
  6. ///Theinversedderivedorientationoftheboneinthebindingpose
  7. QuaternionmBindDerivedInverseOrientation;
  8. ///Theinversedderivedpositionoftheboneinthebindingpose
  9. Vector3mBindDerivedInversePosition;
/// The numeric handle of this bone unsigned short mHandle; Skeleton* mCreator; /// The inversed derived scale of the bone in the binding pose Vector3 mBindDerivedInverseScale; /// The inversed derived orientation of the bone in the binding pose Quaternion mBindDerivedInverseOrientation; /// The inversed derived position of the bone in the binding pose Vector3 mBindDerivedInversePosition;

Handle是一个用来标示Bone的值。
Skeleton(后面会介绍)是集中管理Bone的类。所以在Bone中保存了一个指向创建自己的Skeleton指针。
最后的3个变量值是子节点反变换回父节点位置的变换信息。
这些信息是在函数void _getOffsetTransform(Matrix4& m)中用来生成变换矩阵:

  1. Vector3scale=_getDerivedScale()*mBindDerivedInverseScale;
  2. Quaternionrotate=_getDerivedOrientation()*mBindDerivedInverseOrientation;
  3. Vector3translate=_getDerivedPosition()+rotate*(scale*mBindDerivedInversePosition);
  4. m.makeTransform(translate,scale,rotate);
  1. Vector3scale=_getDerivedScale()*mBindDerivedInverseScale;
  2. Quaternionrotate=_getDerivedOrientation()*mBindDerivedInverseOrientation;
  3. Vector3translate=_getDerivedPosition()+rotate*(scale*mBindDerivedInversePosition);
  4. m.makeTransform(translate,scale,rotate);
Vector3 scale = _getDerivedScale() * mBindDerivedInverseScale; Quaternion rotate = _getDerivedOrientation() * mBindDerivedInverseOrientation; Vector3 translate = _getDerivedPosition() + rotate * (scale * mBindDerivedInversePosition); m.makeTransform(translate, scale, rotate);

2、OgreKeyFrame、OgreAnimationTrack与OgreAnimation
Bone类定义动画中的骨骼。我们需要接着定义相应的动画信息,然后将每个动画动作的变换信息赋予这些骨骼,这样我们就可以是物体按照我们设定的规则“运动”起来。而这些动画的信息就保存在这一组类中。

Ogre引擎源码——资源之Skeleton

最右侧有个类KeyFrame,它是这组类中最基本的一个。我们就从它开始吧。
类如其名,KeyFrame对应的就是动画中的一帧。它是一个基类,派生出四种不同类型的子类。

  1. class_OgreExportKeyFrame:publicAnimationAlloc
  2. {
  3. public:
  4. KeyFrame(constAnimationTrack*parent,Realtime);
  5. virtual~KeyFrame(){}
  6. RealgetTime(void)const{returnmTime;}
  7. virtualKeyFrame*_clone(AnimationTrack*newParent)const;
  8. protected:
  9. RealmTime;
  10. constAnimationTrack*mParentTrack;
  11. };
  1. class_OgreExportKeyFrame:publicAnimationAlloc
  2. {
  3. public:
  4. KeyFrame(constAnimationTrack*parent,Realtime);
  5. virtual~KeyFrame(){}
  6. RealgetTime(void)const{returnmTime;}
  7. virtualKeyFrame*_clone(AnimationTrack*newParent)const;
  8. protected:
  9. RealmTime;
  10. constAnimationTrack*mParentTrack;
  11. };
class _OgreExport KeyFrame : public AnimationAlloc { public: KeyFrame(const AnimationTrack* parent, Real time); virtual ~KeyFrame() {} Real getTime(void) const { return mTime; } virtual KeyFrame* _clone(AnimationTrack* newParent) const; protected: Real mTime; const AnimationTrack* mParentTrack; };

KeyFrame类的定义就这样简单的几行。它的成员变量只有两个。一个是表示该帧在整个动画轨迹中的时间位置;还有一个就是创建该帧的AnimationTrack类。

KeyFrame类并没有保存每帧需保存的空间变换信息,因为Ogre只是四种不同的关键帧,它们各自有自己需要保存的变换信息,所以在KeyFrame的派生类中可以找到相应的内容。

KeyFrame的四个派生类:
数字关键帧(NumericKeyFrame)
变换关键帧(TransformKeyFrame)
顶点变形关键帧(VertexMorphKeyFrame)
顶点姿态关键帧(VertexPoseKeyFrame)

比如:数字关键帧(NumericKeyFrame)中就保存了相应的数字数据;变换关键帧(TransformKeyFrame)中则保存了变换、缩放和旋转信息等。(这些关键帧会在相应Track里再提到)

在UML图的*,我们可以看到,KeyFrame类是由AnimationTrack的类负责创建和管理的。KeyFrame的四个子类对应了三种动画轨迹类型(最后两种KeyFrame子类对应同一种AnimationTrack),也就定义了Ogre所支持的三种动画轨迹方式。

先来看下这些动画轨迹的父类AnimationTrack
AnimationTrack的主要成员变量如下:

  1. typedefvector<KeyFrame*>::typeKeyFrameList;
  2. KeyFrameListmKeyFrames;
  3. Animation*mParent;
  4. unsignedshortmHandle;
  1. typedefvector<KeyFrame*>::typeKeyFrameList;
  2. KeyFrameListmKeyFrames;
  3. Animation*mParent;
  4. unsignedshortmHandle;
typedef vector<KeyFrame*>::type KeyFrameList; KeyFrameList mKeyFrames; Animation* mParent; unsigned short mHandle;

一个Track表示一个动画序列,也就是一组有序的KeyFrame,用来指示一个可运动物体的某个动作轨迹。
mKeyFrames就是该Track保存的一组关键帧
mParent指向创建和管理Track的Animation类
mHandle则与Bone类中出现的一样,用来标示一个Track

一些重要的成员函数:(虚函数,用以子类接口继承)
virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const = 0;
纯虚函数,根据制定的timeindex,返回插值后的KeyFrame kf。
virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) = 0;
纯虚函数,应用这个动画轨迹。子类根据自己所属的不同关键帧方式来实现相应的动画轨迹。
virtual void optimise(void) {}
虚函数,优化这个动画轨迹,方法为删除重复帧。

AnimationTrack有三个子类
数字动画轨迹(NumericAnimationTrack):对应的是数字关键帧(NumericKeyFrame)
该类比较简单,插值和轨迹应用都是根据数值来进行,就是简单的线性插值。

节点动画轨迹(NodeAnimationTrack):对应的是变换关键帧(TransformKeyFrame)
每个变换关键帧都包含了两个三元向量和一个四元数,分别用来表现节点在当前帧的位置、缩放以及方向。所以该轨迹中的插值主要针对的是这些空间向量。插值的方式分为线性和样条曲线两种。
当内存空间够用时,可以定义如下样条并设置变量mSplineBuildNeeded为true来获得更好的插值效果。

  1. structSplines
  2. {
  3. SimpleSplinepositionSpline;
  4. SimpleSplinescaleSpline;
  5. RotationalSplinerotationSpline;
  6. };
  1. structSplines
  2. {
  3. SimpleSplinepositionSpline;
  4. SimpleSplinescaleSpline;
  5. RotationalSplinerotationSpline;
  6. };
struct Splines { SimpleSpline positionSpline; SimpleSpline scaleSpline; RotationalSpline rotationSpline; };

顶点动画轨迹(VertexAnimationTrack):对应顶点变形关键帧(VertexMorphKeyFrame)和顶点姿态关键帧(VertexPoseKeyFrame),每个关键帧保存了特定时间的顶点位置数据,在姿态动画(Pose)中还保存了顶点混合权重。

两种不同的顶点动画:
变形动画:记录每个关键帧时刻所有顶点的快照集合。
姿态动画:记录所有顶点的偏移,根据不同的权重来融合不同的姿态,来产生最后的结果。在面部动画中,姿态动画应用比较广泛。

现在再来看Animation类就简单很多了:

  1. ///Nodetracks,indexedbyhandle
  2. NodeTrackListmNodeTrackList;
  3. ///Numerictracks,indexedbyhandle
  4. NumericTrackListmNumericTrackList;
  5. ///Vertextracks,indexedbyhandle
  6. VertexTrackListmVertexTrackList;
  7. StringmName;
  8. RealmLength;
  9. InterpolationModemInterpolationMode;
  10. RotationInterpolationModemRotationInterpolationMode;
  1. ///Nodetracks,indexedbyhandle
  2. NodeTrackListmNodeTrackList;
  3. ///Numerictracks,indexedbyhandle
  4. NumericTrackListmNumericTrackList;
  5. ///Vertextracks,indexedbyhandle
  6. VertexTrackListmVertexTrackList;
  7. StringmName;
  8. RealmLength;
  9. InterpolationModemInterpolationMode;
  10. RotationInterpolationModemRotationInterpolationMode;
/// Node tracks, indexed by handle NodeTrackList mNodeTrackList; /// Numeric tracks, indexed by handle NumericTrackList mNumericTrackList; /// Vertex tracks, indexed by handle VertexTrackList mVertexTrackList; String mName; Real mLength; InterpolationMode mInterpolationMode; RotationInterpolationMode mRotationInterpolationMode;

Animation类各保存有上述三个动画轨迹的一个列表,并记录下当前动画的名字、长度以及插值方式。

该类中最重要的是一组apply函数。该类函数的功能差不多相同,就是迭代相应的AnimationTrack列表,分别调用AnimationTrack的apply函数即可。

值得一提的是在这组apply函数API里,有用到Node(Bone的父类)和Skeleton作为参数类型的。可以猜测到,在这些apply函数中,关键帧的变换信息插值计算出来后,被附加到了骨骼Node上,也即对骨骼变换信息进行了更新。关键帧、动画和骨骼这三个概念在这个函数里汇集到了一起!

3、OgreSkeleton
最后集大成者就是Skeleton类了,先来看下UML图。

Ogre引擎源码——资源之Skeleton

开篇已经提到,Skeleton是Ogre八种资源子类中的一种。Skeleton类中最重要的数据结构就是对Bone(本文的第一部分)和Animation(本文的第二部分)的管理。


如下是两个管理的列表:

  1. ///Storageofbones,indexedbybonehandle
  2. BoneListmBoneList;
  3. ///Storageofanimations,lookupbyname
  4. AnimationListmAnimationsList;
  1. ///Storageofbones,indexedbybonehandle
  2. BoneListmBoneList;
  3. ///Storageofanimations,lookupbyname
  4. AnimationListmAnimationsList;
/// Storage of bones, indexed by bone handle BoneList mBoneList; /// Storage of animations, lookup by name AnimationList mAnimationsList;

Skeleton类中较多的成员函数是对这两个列表成员的具体操作,如创建、添加、删除一个Bone或者AnimationList等。
_getAnimation成员函数获取相应的Animation类,然后在setAnimationState函数中将该动画变换附加所有骨骼上。_updateTransforms成员函数的作用是遍历所有Bone,调用每个Bone的update函数进行变换更新。

还有值得一提的是LinkedSkeletonAnimationSource结构,它允许Skeleton类使用其他的Animation类来实现动画。

本文完。

参考内容
网站:
http://www.ogre3d.cn/wiki/index.php?title=%E9%A6%96%E9%A1%B5
http://blog.****.net/pizi0475/archive/2010/03/11/5370338.aspx
http://hi.baidu.com/bdruiruili/blog/item/e2024135d351861f90ef399f.html
书籍:PRO OGRE 3D PROGRAMMING

Ogre中Resouce资源有以下几种类型:Texture、Compositor、FontGpuProgramMaterial、Mesh、Skeleton、BspLevel。

本文聚焦于资源Skeleton,这部分资源是Ogre处理动画的环节。Ogre支持三种动画方式:骨骼动画(Skeletal)、变形动画(Morph)以及姿态动画(Pose)。

相关的头文件如下:
OgreNode.h
OgreBone.h
OgreKeyFrame.h
OgreAnimation.h
OgreAnimationTrack.h
OgreSkeleton.h
OgreSkeletonFileFormat.h

1、OgreNode与OgreBone

Ogre引擎源码——资源之Skeleton

Ogre中的骨骼动画,Bone就是相应的骨骼类。在关注这个Bone类之前,我们需要来看下它的父类——Node类。在Ogre场景组织中,Node是一个重要的基类,一个Node对应了场景中的一个组成部分。场景中的所有节点都被组织成一颗结构树,父节点的变换可以影响它相应的子节点。这些基础的变换实现正好符合Bone的需求。

先来看下Node类中重要的数据成员:

  1. ///Pointertoparentnode
  2. Node*mParent;
  3. ///Collectionofpointerstodirectchildren;hashmapforefficiency
  4. typedefHashMap<String,Node*>ChildNodeMap;
  5. ChildNodeMapmChildren;
  6. StringmName;
  7. ///Storestheorientationofthenoderelativetoit'sparent.
  8. QuaternionmOrientation;
  9. ///Storestheposition/translationofthenoderelativetoitsparent.
  10. Vector3mPosition;
  11. ///Storesthescalingfactorappliedtothisnode
  12. Vector3mScale;
  1. ///Pointertoparentnode
  2. Node*mParent;
  3. ///Collectionofpointerstodirectchildren;hashmapforefficiency
  4. typedefHashMap<String,Node*>ChildNodeMap;
  5. ChildNodeMapmChildren;
  6. StringmName;
  7. ///Storestheorientationofthenoderelativetoit'sparent.
  8. QuaternionmOrientation;
  9. ///Storestheposition/translationofthenoderelativetoitsparent.
  10. Vector3mPosition;
  11. ///Storesthescalingfactorappliedtothisnode
  12. Vector3mScale;
/// Pointer to parent node Node* mParent; /// Collection of pointers to direct children; hashmap for efficiency typedef HashMap<String, Node*> ChildNodeMap; ChildNodeMap mChildren; String mName; /// Stores the orientation of the node relative to it's parent. Quaternion mOrientation; /// Stores the position/translation of the node relative to its parent. Vector3 mPosition; /// Stores the scaling factor applied to this node Vector3 mScale;

每个Node节点保存了自己的父节点指针,所有的子节点指针,以及当前节点的基本空间信息:方向、位置、缩放。

Node类的成员函数主要有两类功能:
(a)管理子节点

  1. virtualNode*createChildconstVector3&translate,constQuaternion&rotate);
  2. virtualvoidaddChild(Node*child);
  3. virtualunsignedshortnumChildren(void)const;
  4. virtualNode*removeChild(unsignedshortindex);
  5. virtualNode*removeChild(Node*child);
  1. virtualNode*createChildconstVector3&translate,constQuaternion&rotate);
  2. virtualvoidaddChild(Node*child);
  3. virtualunsignedshortnumChildren(void)const;
  4. virtualNode*removeChild(unsignedshortindex);
  5. virtualNode*removeChild(Node*child);
virtual Node* createChildconst Vector3& translate, const Quaternion& rotate ); virtual void addChild(Node* child); virtual unsigned short numChildren(void) const; virtual Node* removeChild(unsigned short index); virtual Node* removeChild(Node* child);

其中儿子节点可以以名字或者索引两种Key形式。

(b)进行变换

  1. virtualvoidscale(constVector3&scale);
  2. virtualvoidtranslate(constVector3&d,TransformSpacerelativeTo);
  3. virtualvoidroll(constRadian&angle,TransformSpacerelativeTo);
  4. virtualvoidpitch(constRadian&angle,TransformSpacerelativeTo);
  5. virtualvoidyaw(constRadian&angle,TransformSpacerelativeTo);
  6. virtualvoidrotate(constVector3&axis,constRadian&angle,TransformSpacerelativeTo);
  7. virtualMatrix3getLocalAxes(void)const;
  1. virtualvoidscale(constVector3&scale);
  2. virtualvoidtranslate(constVector3&d,TransformSpacerelativeTo);
  3. virtualvoidroll(constRadian&angle,TransformSpacerelativeTo);
  4. virtualvoidpitch(constRadian&angle,TransformSpacerelativeTo);
  5. virtualvoidyaw(constRadian&angle,TransformSpacerelativeTo);
  6. virtualvoidrotate(constVector3&axis,constRadian&angle,TransformSpacerelativeTo);
  7. virtualMatrix3getLocalAxes(void)const;
virtual void scale(const Vector3& scale); virtual void translate(const Vector3& d, TransformSpace relativeTo); virtual void roll(const Radian& angle, TransformSpace relativeTo); virtual void pitch(const Radian& angle, TransformSpace relativeTo); virtual void yaw(const Radian& angle, TransformSpace relativeTo); virtual void rotate(const Vector3& axis, const Radian& angle, TransformSpace relativeTo); virtual Matrix3 getLocalAxes(void) const;

Node类里还有个重要的函数

  1. virtualvoid_update(boolupdateChildren,boolparentHasChanged);
  1. virtualvoid_update(boolupdateChildren,boolparentHasChanged);
virtual void _update(bool updateChildren, bool parentHasChanged);

这个函数从父类获取(如果有父类的话)变换信息,对自身和子类施加变换操作。


主要的代码片段如下:

  1. ChildNodeMap::iteratorit,itend;
  2. itend=mChildren.end();
  3. for(it=mChildren.begin();it!=itend;++it)
  4. {
  5. Node*child=it->second;
  6. child->_update(true,true);
  7. }
  8. mChildrenToUpdate.clear();
  1. ChildNodeMap::iteratorit,itend;
  2. itend=mChildren.end();
  3. for(it=mChildren.begin();it!=itend;++it)
  4. {
  5. Node*child=it->second;
  6. child->_update(true,true);
  7. }
  8. mChildrenToUpdate.clear();
ChildNodeMap::iterator it, itend; itend = mChildren.end(); for (it = mChildren.begin(); it != itend; ++it) { Node* child = it->second; child->_update(true, true); } mChildrenToUpdate.clear();

这里就是一个迭代的过程,对本节点所有的儿子节点同样调用它们的_update函数,使该变换不断传递下去。

可以看出Node类是一个封装了节点变换信息的类,该变换是以树节点的形式进行组织,父类的变换会影响它的所有/部分子节点。

下面就来关注下Bone类。
关于骨骼动画,目前Ogre只支持关键帧形式的正向动力学(FK);也就是说没有提供对逆向动力学(IK)骨骼动画的内建支持。
所谓的正向动力学(FK, forward kinematics,也称为前向动力学)是指完全遵循父子关系的层级,用父层级带动子层级的运动。这也正如我们在_update函数中所看到的那样,变换递推过程从父类不断向子类传递。正向动力学的优点是:计算简单,运算速度快,缺点是:需指定每个关节的角度和位置,而由于骨架的各个节点之间有内在的关联性,直接指定各关节的值很容易产生不自然协调的动作。

Node类几乎都实现了Bone类最重要的变换信息保存以及变换信息的传递操作。Bone类只是在此基础上添加了一些很简单的内容。

  1. ///Thenumerichandleofthisbone
  2. unsignedshortmHandle;
  3. Skeleton*mCreator;
  4. ///Theinversedderivedscaleoftheboneinthebindingpose
  5. Vector3mBindDerivedInverseScale;
  6. ///Theinversedderivedorientationoftheboneinthebindingpose
  7. QuaternionmBindDerivedInverseOrientation;
  8. ///Theinversedderivedpositionoftheboneinthebindingpose
  9. Vector3mBindDerivedInversePosition;
  1. ///Thenumerichandleofthisbone
  2. unsignedshortmHandle;
  3. Skeleton*mCreator;
  4. ///Theinversedderivedscaleoftheboneinthebindingpose
  5. Vector3mBindDerivedInverseScale;
  6. ///Theinversedderivedorientationoftheboneinthebindingpose
  7. QuaternionmBindDerivedInverseOrientation;
  8. ///Theinversedderivedpositionoftheboneinthebindingpose
  9. Vector3mBindDerivedInversePosition;
/// The numeric handle of this bone unsigned short mHandle; Skeleton* mCreator; /// The inversed derived scale of the bone in the binding pose Vector3 mBindDerivedInverseScale; /// The inversed derived orientation of the bone in the binding pose Quaternion mBindDerivedInverseOrientation; /// The inversed derived position of the bone in the binding pose Vector3 mBindDerivedInversePosition;

Handle是一个用来标示Bone的值。
Skeleton(后面会介绍)是集中管理Bone的类。所以在Bone中保存了一个指向创建自己的Skeleton指针。
最后的3个变量值是子节点反变换回父节点位置的变换信息。
这些信息是在函数void _getOffsetTransform(Matrix4& m)中用来生成变换矩阵:

  1. Vector3scale=_getDerivedScale()*mBindDerivedInverseScale;
  2. Quaternionrotate=_getDerivedOrientation()*mBindDerivedInverseOrientation;
  3. Vector3translate=_getDerivedPosition()+rotate*(scale*mBindDerivedInversePosition);
  4. m.makeTransform(translate,scale,rotate);
  1. Vector3scale=_getDerivedScale()*mBindDerivedInverseScale;
  2. Quaternionrotate=_getDerivedOrientation()*mBindDerivedInverseOrientation;
  3. Vector3translate=_getDerivedPosition()+rotate*(scale*mBindDerivedInversePosition);
  4. m.makeTransform(translate,scale,rotate);
Vector3 scale = _getDerivedScale() * mBindDerivedInverseScale; Quaternion rotate = _getDerivedOrientation() * mBindDerivedInverseOrientation; Vector3 translate = _getDerivedPosition() + rotate * (scale * mBindDerivedInversePosition); m.makeTransform(translate, scale, rotate);

2、OgreKeyFrame、OgreAnimationTrack与OgreAnimation
Bone类定义动画中的骨骼。我们需要接着定义相应的动画信息,然后将每个动画动作的变换信息赋予这些骨骼,这样我们就可以是物体按照我们设定的规则“运动”起来。而这些动画的信息就保存在这一组类中。

Ogre引擎源码——资源之Skeleton

最右侧有个类KeyFrame,它是这组类中最基本的一个。我们就从它开始吧。
类如其名,KeyFrame对应的就是动画中的一帧。它是一个基类,派生出四种不同类型的子类。

  1. class_OgreExportKeyFrame:publicAnimationAlloc
  2. {
  3. public:
  4. KeyFrame(constAnimationTrack*parent,Realtime);
  5. virtual~KeyFrame(){}
  6. RealgetTime(void)const{returnmTime;}
  7. virtualKeyFrame*_clone(AnimationTrack*newParent)const;
  8. protected:
  9. RealmTime;
  10. constAnimationTrack*mParentTrack;
  11. };
  1. class_OgreExportKeyFrame:publicAnimationAlloc
  2. {
  3. public:
  4. KeyFrame(constAnimationTrack*parent,Realtime);
  5. virtual~KeyFrame(){}
  6. RealgetTime(void)const{returnmTime;}
  7. virtualKeyFrame*_clone(AnimationTrack*newParent)const;
  8. protected:
  9. RealmTime;
  10. constAnimationTrack*mParentTrack;
  11. };
class _OgreExport KeyFrame : public AnimationAlloc { public: KeyFrame(const AnimationTrack* parent, Real time); virtual ~KeyFrame() {} Real getTime(void) const { return mTime; } virtual KeyFrame* _clone(AnimationTrack* newParent) const; protected: Real mTime; const AnimationTrack* mParentTrack; };

KeyFrame类的定义就这样简单的几行。它的成员变量只有两个。一个是表示该帧在整个动画轨迹中的时间位置;还有一个就是创建该帧的AnimationTrack类。

KeyFrame类并没有保存每帧需保存的空间变换信息,因为Ogre只是四种不同的关键帧,它们各自有自己需要保存的变换信息,所以在KeyFrame的派生类中可以找到相应的内容。

KeyFrame的四个派生类:
数字关键帧(NumericKeyFrame)
变换关键帧(TransformKeyFrame)
顶点变形关键帧(VertexMorphKeyFrame)
顶点姿态关键帧(VertexPoseKeyFrame)

比如:数字关键帧(NumericKeyFrame)中就保存了相应的数字数据;变换关键帧(TransformKeyFrame)中则保存了变换、缩放和旋转信息等。(这些关键帧会在相应Track里再提到)

在UML图的*,我们可以看到,KeyFrame类是由AnimationTrack的类负责创建和管理的。KeyFrame的四个子类对应了三种动画轨迹类型(最后两种KeyFrame子类对应同一种AnimationTrack),也就定义了Ogre所支持的三种动画轨迹方式。

先来看下这些动画轨迹的父类AnimationTrack
AnimationTrack的主要成员变量如下:

  1. typedefvector<KeyFrame*>::typeKeyFrameList;
  2. KeyFrameListmKeyFrames;
  3. Animation*mParent;
  4. unsignedshortmHandle;
  1. typedefvector<KeyFrame*>::typeKeyFrameList;
  2. KeyFrameListmKeyFrames;
  3. Animation*mParent;
  4. unsignedshortmHandle;
typedef vector<KeyFrame*>::type KeyFrameList; KeyFrameList mKeyFrames; Animation* mParent; unsigned short mHandle;

一个Track表示一个动画序列,也就是一组有序的KeyFrame,用来指示一个可运动物体的某个动作轨迹。
mKeyFrames就是该Track保存的一组关键帧
mParent指向创建和管理Track的Animation类
mHandle则与Bone类中出现的一样,用来标示一个Track

一些重要的成员函数:(虚函数,用以子类接口继承)
virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const = 0;
纯虚函数,根据制定的timeindex,返回插值后的KeyFrame kf。
virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) = 0;
纯虚函数,应用这个动画轨迹。子类根据自己所属的不同关键帧方式来实现相应的动画轨迹。
virtual void optimise(void) {}
虚函数,优化这个动画轨迹,方法为删除重复帧。

AnimationTrack有三个子类
数字动画轨迹(NumericAnimationTrack):对应的是数字关键帧(NumericKeyFrame)
该类比较简单,插值和轨迹应用都是根据数值来进行,就是简单的线性插值。

节点动画轨迹(NodeAnimationTrack):对应的是变换关键帧(TransformKeyFrame)
每个变换关键帧都包含了两个三元向量和一个四元数,分别用来表现节点在当前帧的位置、缩放以及方向。所以该轨迹中的插值主要针对的是这些空间向量。插值的方式分为线性和样条曲线两种。
当内存空间够用时,可以定义如下样条并设置变量mSplineBuildNeeded为true来获得更好的插值效果。

  1. structSplines
  2. {
  3. SimpleSplinepositionSpline;
  4. SimpleSplinescaleSpline;
  5. RotationalSplinerotationSpline;
  6. };
  1. structSplines
  2. {
  3. SimpleSplinepositionSpline;
  4. SimpleSplinescaleSpline;
  5. RotationalSplinerotationSpline;
  6. };
struct Splines { SimpleSpline positionSpline; SimpleSpline scaleSpline; RotationalSpline rotationSpline; };

顶点动画轨迹(VertexAnimationTrack):对应顶点变形关键帧(VertexMorphKeyFrame)和顶点姿态关键帧(VertexPoseKeyFrame),每个关键帧保存了特定时间的顶点位置数据,在姿态动画(Pose)中还保存了顶点混合权重。

两种不同的顶点动画:
变形动画:记录每个关键帧时刻所有顶点的快照集合。
姿态动画:记录所有顶点的偏移,根据不同的权重来融合不同的姿态,来产生最后的结果。在面部动画中,姿态动画应用比较广泛。

现在再来看Animation类就简单很多了:

  1. ///Nodetracks,indexedbyhandle
  2. NodeTrackListmNodeTrackList;
  3. ///Numerictracks,indexedbyhandle
  4. NumericTrackListmNumericTrackList;
  5. ///Vertextracks,indexedbyhandle
  6. VertexTrackListmVertexTrackList;
  7. StringmName;
  8. RealmLength;
  9. InterpolationModemInterpolationMode;
  10. RotationInterpolationModemRotationInterpolationMode;
  1. ///Nodetracks,indexedbyhandle
  2. NodeTrackListmNodeTrackList;
  3. ///Numerictracks,indexedbyhandle
  4. NumericTrackListmNumericTrackList;
  5. ///Vertextracks,indexedbyhandle
  6. VertexTrackListmVertexTrackList;
  7. StringmName;
  8. RealmLength;
  9. InterpolationModemInterpolationMode;
  10. RotationInterpolationModemRotationInterpolationMode;
/// Node tracks, indexed by handle NodeTrackList mNodeTrackList; /// Numeric tracks, indexed by handle NumericTrackList mNumericTrackList; /// Vertex tracks, indexed by handle VertexTrackList mVertexTrackList; String mName; Real mLength; InterpolationMode mInterpolationMode; RotationInterpolationMode mRotationInterpolationMode;

Animation类各保存有上述三个动画轨迹的一个列表,并记录下当前动画的名字、长度以及插值方式。

该类中最重要的是一组apply函数。该类函数的功能差不多相同,就是迭代相应的AnimationTrack列表,分别调用AnimationTrack的apply函数即可。

值得一提的是在这组apply函数API里,有用到Node(Bone的父类)和Skeleton作为参数类型的。可以猜测到,在这些apply函数中,关键帧的变换信息插值计算出来后,被附加到了骨骼Node上,也即对骨骼变换信息进行了更新。关键帧、动画和骨骼这三个概念在这个函数里汇集到了一起!

3、OgreSkeleton
最后集大成者就是Skeleton类了,先来看下UML图。

Ogre引擎源码——资源之Skeleton

开篇已经提到,Skeleton是Ogre八种资源子类中的一种。Skeleton类中最重要的数据结构就是对Bone(本文的第一部分)和Animation(本文的第二部分)的管理。


如下是两个管理的列表:

  1. ///Storageofbones,indexedbybonehandle
  2. BoneListmBoneList;
  3. ///Storageofanimations,lookupbyname
  4. AnimationListmAnimationsList;
  1. ///Storageofbones,indexedbybonehandle
  2. BoneListmBoneList;
  3. ///Storageofanimations,lookupbyname
  4. AnimationListmAnimationsList;
/// Storage of bones, indexed by bone handle BoneList mBoneList; /// Storage of animations, lookup by name AnimationList mAnimationsList;

Skeleton类中较多的成员函数是对这两个列表成员的具体操作,如创建、添加、删除一个Bone或者AnimationList等。
_getAnimation成员函数获取相应的Animation类,然后在setAnimationState函数中将该动画变换附加所有骨骼上。_updateTransforms成员函数的作用是遍历所有Bone,调用每个Bone的update函数进行变换更新。

还有值得一提的是LinkedSkeletonAnimationSource结构,它允许Skeleton类使用其他的Animation类来实现动画。

本文完。

参考内容
网站:
http://www.ogre3d.cn/wiki/index.php?title=%E9%A6%96%E9%A1%B5
http://blog.****.net/pizi0475/archive/2010/03/11/5370338.aspx
http://hi.baidu.com/bdruiruili/blog/item/e2024135d351861f90ef399f.html
书籍:PRO OGRE 3D PROGRAMMING