KeyFrameKeyFrame是基类,有四个子类,NumericKeyFrame,TransformKeyFrame,VertexMorphKeyFrame, VertexPoseKeyFrame. KeyFrame:关键帧,基类 class_OgreExportKeyFrame : publicAnimationAlloc { public: KeyFrame(constAnimationTrack* parent, Realtime); 构造函数由AnimationTrack调用 virtual ~KeyFrame() {} 获得关键帧在动画序列里的时间 Real getTime(void) const { return mTime; } 复制关键帧的函数,因为是virtual,每个子类都有各自的复制函数 virtualKeyFrame* _clone(AnimationTrack* newParent) const; protected: Real mTime; 储存该关键帧在动画序列的时间 constAnimationTrack* mParentTrack; 父亲Track };
NumericKeyFrame:数值关键帧,储存数值 class_OgreExportNumericKeyFrame : publicKeyFrame { public: NumericKeyFrame(constAnimationTrack* parent, Realtime); ~NumericKeyFrame() {}
获得关键帧存的任何值 virtualconstAnyNumeric& getValue(void) const; 设置值 virtualvoid setValue(constAnyNumeric& val); NumericKeyFrame的复制函数 KeyFrame* _clone(AnimationTrack* newParent) const; protected: AnyNumeric mValue; };
TransformKeyFrame:变换关键帧,储存了各种变换,包括平移,旋转,缩放 class_OgreExportTransformKeyFrame : publicKeyFrame { public: TransformKeyFrame(constAnimationTrack* parent, Realtime); ~TransformKeyFrame() {} 设置这个帧中动画物体的平移 virtualvoid setTranslate(constVector3& trans);
constVector3& getTranslate(void) const;
设置这个帧中动画物体的缩放 virtualvoid setScale(constVector3& scale); virtualconstVector3& getScale(void) const; 设置这个帧中动画物体的旋转,用四元数来旋转 virtualvoid setRotation(constQuaternion& rot); virtualconstQuaternion& getRotation(void) const;
复制函数 KeyFrame* _clone(AnimationTrack* newParent) const; protected: Vector3 mTranslate; Vector3 mScale; Quaternion mRotate;
}; VertexMorphKeyFrame:顶点Morph关键帧 储存在缓存中的顶点位置,是为了和其他在同一个track的关键帧做内插值 class_OgreExportVertexMorphKeyFrame : publicKeyFrame { public: VertexMorphKeyFrame(constAnimationTrack* parent, Realtime); ~VertexMorphKeyFrame() {} 用在缓存中的前三个float元素来当作关键帧中的位置 void setVertexBuffer(constHardwareVertexBufferSharedPtr& buf); constHardwareVertexBufferSharedPtr& getVertexBuffer(void) const; 复制函数 KeyFrame* _clone(AnimationTrack* newParent) const;
protected: HardwareVertexBufferSharedPtr mBuffer;
}; VertexPoseKeyFrame:动作点关键帧 该关键帧参考了Mesh::Pose以一个确定的影响程度 class_OgreExportVertexPoseKeyFrame : publicKeyFrame { public: VertexPoseKeyFrame(constAnimationTrack* parent, Realtime); ~VertexPoseKeyFrame() {}
structPoseRef 动作参考 { (typedef unsigned short ushort)Mesh包含所有pose的所有顶点信息在一个list 这个poseIndex存的就是参考pose在Mesh里的list的索引 ushort poseIndex; Real influence; 影响程度在0.0-1.0
PoseRef(ushortp, Reali) : poseIndex(p), influence(i) {} }; typedefvector<PoseRef>::typePoseRefList;
下面4个函数都是对PoseRef参考动作的操作 void addPoseReference(ushortposeIndex, Realinfluence); void updatePoseReference(ushortposeIndex, Realinfluence); void removePoseReference(ushortposeIndex); void removeAllPoseReferences(void);
对mPoseRefs(就是存了PoseRef的list)的操作 constPoseRefList& getPoseReferences(void) const; typedefVectorIterator<PoseRefList> PoseRefIterator; typedefConstVectorIterator<PoseRefList> ConstPoseRefIterator; PoseRefIterator getPoseReferenceIterator(void); ConstPoseRefIterator getPoseReferenceIterator(void) const;
KeyFrame* _clone(AnimationTrack* newParent) const; void _applyBaseKeyFrame(constVertexPoseKeyFrame* base);
protected: PoseRefList mPoseRefs;
}; 下面是对mPoseRefs操作的具体代码,可以看出就是许多对list的操作 voidVertexPoseKeyFrame::addPoseReference(ushortposeIndex, Realinfluence) { mPoseRefs.push_back(PoseRef(poseIndex, influence)); } //--------------------------------------------------------------------- voidVertexPoseKeyFrame::updatePoseReference(ushortposeIndex, Realinfluence) { for (PoseRefList::iterator i = mPoseRefs.begin(); i != mPoseRefs.end(); ++i) { if (i->poseIndex == poseIndex) { i->influence = influence; return; } } // if we got here, we didn't find it addPoseReference(poseIndex, influence);
} //--------------------------------------------------------------------- voidVertexPoseKeyFrame::removePoseReference(ushortposeIndex) { for (PoseRefList::iterator i = mPoseRefs.begin(); i != mPoseRefs.end(); ++i) { if (i->poseIndex == poseIndex) { mPoseRefs.erase(i); return; } } } //--------------------------------------------------------------------- voidVertexPoseKeyFrame::removeAllPoseReferences(void) { mPoseRefs.clear(); }
AnimationTrack(动画轨迹)动画轨迹由多个关键帧组成,负责做两个关键帧之间的插值运算。 同样是一个基类,有3个子类,NodeAnimationTrack,NumericAnimationTrack,VertexAnimationTrack. 一个track实际上就是一系列Keyframes影响一个动画物体。 Since the most common animable object is a Node, there are options in this class for associating the track with a Node which will receive keyframe updates automatically when the 'apply' method is called.
TimeIndex通常用于寻找keyframe class_OgreExportTimeIndex { protected: Real mTimePos; uint mKeyIndex; staticconstuint INVALID_KEY_INDEX = (uint)-1; public:
TimeIndex(RealtimePos) : mTimePos(timePos) , mKeyIndex(INVALID_KEY_INDEX) { } TimeIndex(RealtimePos, uintkeyIndex) : mTimePos(timePos) , mKeyIndex(keyIndex) { } bool hasKeyIndex(void) const { return mKeyIndex != INVALID_KEY_INDEX; } Real getTimePos(void) const { return mTimePos; } uint getKeyIndex(void) const { return mKeyIndex; } };
AniamtionTrack代码主要函数 class_OgreExportAnimationTrack : publicAnimationAlloc { public:
Listener允许你重写一个确定的AniamtionTrack class_OgreExportListener { public: virtual ~Listener() {} virtualbool getInterpolatedKeyFrame(constAnimationTrack* t, constTimeIndex& timeIndex, KeyFrame* kf) = 0; }; AnimationTrack(Animation* parent, unsignedshorthandle); virtual ~AnimationTrack(); unsignedshort getHandle(void) const { return mHandle; } virtualunsignedshort getNumKeyFrames(void) const; virtualKeyFrame* getKeyFrame(unsignedshortindex) const; 返回一个值,介于0-1,如果值为0则代表keyFrame1,如果值为0.25,则为keyFrame1和keyFrame2的四分之一处。timeIndex是用于两帧之间时间标记,keyFrame1和KeyFrame2分别是用来构造新帧的帧模板,最后一个参数指向了形成的帧 virtualReal getKeyFramesAtTime(constTimeIndex& timeIndex, KeyFrame** keyFrame1, KeyFrame** keyFrame2, unsignedshort* firstKeyIndex = 0) const; 创造一个KeyFrame,在指定的timePos,创建最好按时间顺序 virtualKeyFrame* createKeyFrame(RealtimePos); 获得指定的插值帧 virtualvoid getInterpolatedKeyFrame(constTimeIndex& timeIndex, KeyFrame* kf) const = 0;
在指定的timeIndex的目标运用该AnimationTrack virtualvoid apply(constTimeIndex& timeIndex, Realweight = 1.0, Realscale = 1.0f) = 0;
NumericAnimationTrack 数值动作轨迹,用来处理一般的动作数值, 由NumeraicKeyFrame来构成 class_OgreExportNumericAnimationTrack : publicAnimationTrack { public: /// Constructor NumericAnimationTrack(Animation* parent, unsignedshorthandle); /// Constructor, associates with an AnimableValue NumericAnimationTrack(Animation* parent, unsignedshorthandle, AnimableValuePtr& target); virtualNumericKeyFrame* createNumericKeyFrame(RealtimePos); virtualvoid getInterpolatedKeyFrame(constTimeIndex& timeIndex, KeyFrame* kf) const; virtualvoid apply(constTimeIndex& timeIndex, Realweight = 1.0, Realscale = 1.0f); 运用一个animation track到一个给定的Animation void applyToAnimable(constAnimableValuePtr& anim, constTimeIndex& timeIndex, Realweight = 1.0, Realscale = 1.0f); virtualconstAnimableValuePtr& getAssociatedAnimable(void) const; virtualvoid setAssociatedAnimable(constAnimableValuePtr& val); NumericKeyFrame* getNumericKeyFrame(unsignedshortindex) const; NumericAnimationTrack* _clone(Animation* newParent) const; protected:
AnimableValuePtr mTargetAnim; KeyFrame* createKeyFrameImpl(Realtime); }; 最重要的是getInterpolatedKeyFrame和applyToAnimable voidNumericAnimationTrack::getInterpolatedKeyFrame(constTimeIndex& timeIndex, KeyFrame* kf) const { if (mListener) { if (mListener->getInterpolatedKeyFrame(this, timeIndex, kf)) return; }
NumericKeyFrame* kret = static_cast<NumericKeyFrame*>(kf);
// Keyframe pointers KeyFrame *kBase1, *kBase2; NumericKeyFrame *k1, *k2; unsignedshort firstKeyIndex;
Real t = this->getKeyFramesAtTime(timeIndex, &kBase1, &kBase2, &firstKeyIndex); k1 = static_cast<NumericKeyFrame*>(kBase1); k2 = static_cast<NumericKeyFrame*>(kBase2);
if (t == 0.0) { // Just use k1 kret->setValue(k1->getValue()); } else { // Interpolate by t AnyNumeric diff = k2->getValue() - k1->getValue(); kret->setValue(k1->getValue() + diff * t); } } voidNumericAnimationTrack::applyToAnimable(constAnimableValuePtr& anim, constTimeIndex& timeIndex, Realweight, Realscale) { // Nothing to do if no keyframes or zero weight, scale if (mKeyFrames.empty() || !weight || !scale) return;
NumericKeyFrame kf(0, timeIndex.getTimePos()); getInterpolatedKeyFrame(timeIndex, &kf); // add to existing. Weights are not relative, but treated as // absolute multipliers for the animation AnyNumeric val = kf.getValue() * (weight * scale);
anim->applyDeltaValue(val);
}
NodeAnimationTrack 节点动作轨迹,用来处理节点的动画变换, 由TransformKeyFrame来构成
class_OgreExport NodeAnimationTrack : public AnimationTrack { //---------------------结点动作轨迹 public: /// Constructor NodeAnimationTrack(Animation* parent, unsigned short handle); /// Constructor, associates with a Node NodeAnimationTrack(Animation* parent, unsigned short handle, Node* targetNode); /// Destructor virtual ~NodeAnimationTrack(); virtual TransformKeyFrame* createNodeKeyFrame(Real timePos); virtual void setAssociatedNode(Node* node); 运动到节点上 virtual void applyToNode(Node* node, const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f); virtual void setUseShortestRotationPath(bool useShortestPath); virtual bool getUseShortestRotationPath() const; virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const;
virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f); void _keyFrameDataChanged(void) const; virtual TransformKeyFrame* getNodeKeyFrame(unsigned short index) const; virtual bool hasNonZeroKeyFrames(void) const; virtual void optimise(void); NodeAnimationTrack* _clone(Animation* newParent) const; protected: KeyFrame* createKeyFrameImpl(Real time); virtual void buildInterpolationSplines(void) const;
struct Splines //里面保存了修改器 { SimpleSpline positionSpline; SimpleSpline scaleSpline; RotationalSpline rotationSpline; };
Node* mTargetNode; mutable Splines* mSplines; mutable bool mSplineBuildNeeded; mutable bool mUseShortestRotationPath ; };
其中重要的是 voidNodeAnimationTrack::applyToNode(Node* node, constTimeIndex& timeIndex, Realweight, Realscl) { // Nothing to do if no keyframes or zero weight or no node if (mKeyFrames.empty() || !weight || !node) return;
TransformKeyFrame kf(0, timeIndex.getTimePos()); getInterpolatedKeyFrame(timeIndex, &kf);
// add to existing. Weights are not relative, but treated as absolute multipliers for the animation Vector3 translate = kf.getTranslate() *weight*scl; node->translate(translate);
// interpolate between no-rotation and full rotation, to point 'weight', so 0 = no rotate, 1 = full Quaternion rotate; Animation::RotationInterpolationMode rim = mParent->getRotationInterpolationMode(); if (rim == Animation::RIM_LINEAR) { rotate =Quaternion::nlerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath); } else//if (rim == Animation::RIM_SPHERICAL) { rotate =Quaternion::Slerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath); } node->rotate(rotate);
Vector3 scale = kf.getScale(); // Not sure how to modify scale for cumulative anims... leave it alone //scale = ((Vector3::UNIT_SCALE - kf.getScale()) * weight) + Vector3::UNIT_SCALE; if (scale !=Vector3::UNIT_SCALE) { if (scl != 1.0f) scale =Vector3::UNIT_SCALE + (scale -Vector3::UNIT_SCALE) *scl; elseif (weight != 1.0f) scale =Vector3::UNIT_SCALE + (scale -Vector3::UNIT_SCALE) *weight; } node->scale(scale);
}
VertexAnimationTrack 顶点动作轨迹,用来处理顶点的位置变化, 由VertexMorphKeyFrame和VertexPoseKeyFrame来构成 class_OgreExportVertexAnimationTrack : publicAnimationTrack { public: /** The target animation mode */ enumTargetMode { /// Interpolate vertex positions in software TM_SOFTWARE, /** Bind keyframe 1 to position, and keyframe 2 to a texture coordinate for interpolation in hardware */ TM_HARDWARE };
VertexAnimationTrack(Animation* parent, unsignedshorthandle, VertexAnimationTypeanimType); VertexAnimationTrack(Animation* parent, unsignedshorthandle, VertexAnimationTypeanimType, VertexData* targetData, TargetModetarget = TM_SOFTWARE); VertexAnimationType getAnimationType(void) const { return mAnimationType; } bool getVertexAnimationIncludesNormals() const; virtualVertexMorphKeyFrame* createVertexMorphKeyFrame(RealtimePos); virtualVertexPoseKeyFrame* createVertexPoseKeyFrame(RealtimePos); virtualvoid getInterpolatedKeyFrame(constTimeIndex& timeIndex, KeyFrame* kf) const; virtualvoid apply(constTimeIndex& timeIndex, Realweight = 1.0, Realscale = 1.0f); virtualvoid applyToVertexData(VertexData* data, constTimeIndex& timeIndex, Realweight = 1.0, constPoseList* poseList = 0); void _applyBaseKeyFrame(constKeyFrame* base); protected: VertexAnimationType mAnimationType; VertexData* mTargetVertexData; TargetMode mTargetMode; KeyFrame* createKeyFrameImpl(Realtime); void applyPoseToVertexData(constPose* pose, VertexData* data, Realinfluence); };
其中重要的还是applyToVertexData voidVertexAnimationTrack::applyToVertexData(VertexData* data, constTimeIndex& timeIndex, Realweight, constPoseList* poseList) { // Nothing to do if no keyframes or no vertex data if (mKeyFrames.empty() || !data) return;
// Get keyframes KeyFrame *kf1, *kf2; Real t = getKeyFramesAtTime(timeIndex, &kf1, &kf2);
if (mAnimationType == VAT_MORPH) { VertexMorphKeyFrame* vkf1 = static_cast<VertexMorphKeyFrame*>(kf1); VertexMorphKeyFrame* vkf2 = static_cast<VertexMorphKeyFrame*>(kf2);
if (mTargetMode == TM_HARDWARE) { // If target mode is hardware, need to bind our 2 keyframe buffers, // one to main pos, one to morph target texcoord assert(!data->hwAnimationDataList.empty() && "Haven't set up hardware vertex animation elements!");
// no use for TempBlendedBufferInfo here btw // NB we assume that position buffer is unshared, except for normals // VertexDeclaration::getAutoOrganisedDeclaration should see to that constVertexElement* posElem = data->vertexDeclaration->findElementBySemantic(VES_POSITION); // Set keyframe1 data as original position data->vertexBufferBinding->setBinding( posElem->getSource(), vkf1->getVertexBuffer()); // Set keyframe2 data as derived data->vertexBufferBinding->setBinding( data->hwAnimationDataList[0].targetBufferIndex, vkf2->getVertexBuffer()); // save T for use later data->hwAnimationDataList[0].parametric = t;
} else { // If target mode is software, need to software interpolate each vertex
Mesh::softwareVertexMorph( t, vkf1->getVertexBuffer(), vkf2->getVertexBuffer(), data); } } else { // Pose
VertexPoseKeyFrame* vkf1 = static_cast<VertexPoseKeyFrame*>(kf1); VertexPoseKeyFrame* vkf2 = static_cast<VertexPoseKeyFrame*>(kf2); constVertexPoseKeyFrame::PoseRefList& poseList1 = vkf1->getPoseReferences(); constVertexPoseKeyFrame::PoseRefList& poseList2 = vkf2->getPoseReferences(); for (VertexPoseKeyFrame::PoseRefList::const_iterator p1 = poseList1.begin(); p1 != poseList1.end(); ++p1) { Real startInfluence = p1->influence; Real endInfluence = 0; // Search for entry in keyframe 2 list (if not there, will be 0) for (VertexPoseKeyFrame::PoseRefList::const_iterator p2 = poseList2.begin(); p2 != poseList2.end(); ++p2) { if (p1->poseIndex == p2->poseIndex) { endInfluence = p2->influence; break; } } // Interpolate influence Real influence = startInfluence + t*(endInfluence - startInfluence); // Scale by animation weight influence = weight * influence; // Get pose assert (poseList && p1->poseIndex < poseList->size()); Pose* pose = (*poseList)[p1->poseIndex]; // apply applyPoseToVertexData(pose, data, influence); } // Now deal with any poses in key 2 which are not in key 1 for (VertexPoseKeyFrame::PoseRefList::const_iterator p2 = poseList2.begin(); p2 != poseList2.end(); ++p2) { bool found = false; for (VertexPoseKeyFrame::PoseRefList::const_iterator p1 = poseList1.begin(); p1 != poseList1.end(); ++p1) { if (p1->poseIndex == p2->poseIndex) { found = true; break; } } if (!found) { // Need to apply this pose too, scaled from 0 start Real influence = t * p2->influence; // Scale by animation weight influence = weight * influence; // Get pose assert (poseList && p2->poseIndex <= poseList->size()); constPose* pose = (*poseList)[p2->poseIndex]; // apply applyPoseToVertexData(pose, data, influence); } } // key 2 iteration } // morph or pose animation }
Animation(动画)一个动画由多个动画轨迹(AnimationTrack)组成,而一个动画轨迹可以控制一个节点,这样一个动画可以使得多个节点沿着自己的轨迹运动。 Animatiion不能自己直接创建,应该通过拥有Animation的父物体来调用,如Skeleton 关键函数和成员变量如下: class_OgreExportAnimation : publicAnimationAlloc {
public: /** The types of animation interpolation available. */ enumInterpolationMode { /** Values are interpolated along straight lines. */ IM_LINEAR, 线性插值 /** Values are interpolated along a spline, resulting in smoother changes in direction. */ IM_SPLINE 样条插值 };
/** The types of rotational interpolation available. */ enumRotationInterpolationMode { /** Values are interpolated linearly. This is faster but does not necessarily give a completely accurate result. */ RIM_LINEAR, 线性插值 /** Values are interpolated spherically. This is more accurate but has a higher cost. */ RIM_SPHERICAL 样条插值
}; Animation管理三个Track,所以有一系列的函数都是跟这三种Track相关的 NodeAnimationTrack* createNodeTrack(void); NumericAnimationTrack* createNumericTrack(unsignedshorthandle); VertexAnimationTrack* createVertexTrack(unsignedshorthandle, VertexAnimationTypeanimType);
apply相关函数 void apply(RealtimePos, Realweight = 1.0, Realscale = 1.0f); void applyToNode(OldNode* node, RealtimePos, Realweight = 1.0, Realscale = 1.0f); void apply(Skeleton* skeleton, RealtimePos, Realweight = 1.0, Realscale = 1.0f); void apply(Skeleton* skeleton, RealtimePos, floatweight, constAnimationState::BoneBlendMask* blendMask, Realscale); void apply(Entity* entity, RealtimePos, Realweight, boolsoftware, boolhardware); void applyToAnimable(constAnimableValuePtr& anim, RealtimePos, Realweight = 1.0, Realscale = 1.0f); void applyToVertexData(VertexData* data, RealtimePos, Realweight = 1.0);
apply具体代码:应用到Animation里的所有Track voidAnimation::apply(RealtimePos, Realweight, Realscale) { _applyBaseKeyFrame();
// Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos);
{ NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { (*i)->apply(timeIndex, weight, scale); } } { OldNodeTrackList::iterator i; for (i = mOldNodeTrackList.begin(); i != mOldNodeTrackList.end(); ++i) { i->second->apply(timeIndex, weight, scale); } } NumericTrackList::iterator j; for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j) { j->second->apply(timeIndex, weight, scale); } VertexTrackList::iterator k; for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k) { k->second->apply(timeIndex, weight, scale); }
} AnimationState(动画状态)用来控制动画播放 成员变量以下: String mAnimationName; AnimationStateSet* mParent; Real mTimePos; Real mLength; Real mWeight; bool mEnabled; bool mLoop;
构造函数:参数意义: animName动画状态的名字,parent属于哪个AnimationStateSet,timepos在时间线的那个位置,length动画时间长度,weight动画状态的权重,enabled是否可以播放,true则可以播放 AnimationState(constString& animName, AnimationStateSet *parent, RealtimePos, Reallength, Realweight = 1.0, boolenabled = false); 其他函数大多都是对成本变量的set,get操作
以一个人行走为例,要想使人能够行走,必须在这个人模型上设置一些特真点,然后让这些点在每一帧按照一定的规则进行动,从而改变整个模型的状态。 在ogre里面,Keyframe类描述的是一个特征点(一个结点、一根骨头、一个顶点)的某一个关键帧的状态(位置、缩放、朝向等)和对应的时间点。 一个可驱动点的所有keyframe组合成一个track,每个可驱动点都有一个他的track,这就好比某个点在整段动画中的轨迹,其中keyframe要由track来创建。 多个track组合在一起就成为了一段动画,用Animation类来表示,这就是一个动画,例如对于骨骼动画,他的每一个骨头都有一个track,那么所有的骨头的track的组合在一起也就是整个骨骼动画了。track由Animation来创建。Animation则可以通过skeleton或者scenenmanager等来创建。 AnimationSate类:通常我们操控一段动画的播放等都不是直接操纵animation 类,而是通过一个类AnimationState,它是animation的一个实例,通过场景管理器创建某个animation的一个animationState,然后就可以利用这个animationstate来播放这个animation了。 下面是一个例子 void CreateAnimation() ...{ /**//*首先我们考虑怎样可以把动画应用到当前摄像机上。 因为一个动画可以应用到一个节点上,所以可以创建 一个节点并将当前摄像机attach到这个节点上去*/ SceneNode* camNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); camNode->attachObject(mCamera);
//下面定义动画、动画轨迹以及关键帧: // 定义动画,指定动画的名称及长度(这里为10秒) Animation* anim = mSceneMgr->createAnimation("CameraTrack", 10); // 指定动画关键帧之间的插值方式(包括线性插值和样条插值) anim->setInterpolationMode(Animation::IM_SPLINE); // 定义动画的一个动画轨迹,并指定这个轨迹是作用到camNode节点上的 NodeAnimationTrack* track = anim->createNodeTrack(0, camNode); // 定义动画轨迹包含的关键帧,下面定义了四个关键帧,加上起始帧 // 五个关健帧形成了一个翻转的动画。 TransformKeyFrame* key = track->createNodeKeyFrame(0); // startposition key = track->createNodeKeyFrame(2.5); key->setTranslate(Vector3(500,500,-1000)); key = track->createNodeKeyFrame(5); key->setTranslate(Vector3(-1500,1000,-600)); key = track->createNodeKeyFrame(7.5); key->setTranslate(Vector3(0,-100,0)); key = track->createNodeKeyFrame(10); key->setTranslate(Vector3(0,0,0));
//然后定义AnimationState类的对象,它和刚才定义的动画类相对应。设置动画的状态为启用: mAnimState = mSceneMgr->createAnimationState("CameraTrack"); mAnimState->setEnabled(true); // 启用该动画 //到此,初始化工作就做完了。 } |