`

动画框架:Animation

 
阅读更多

在说API之前先了解一下动画的原理:以一个人行走为例,要想使人能够行走,必须在这个人模型上设置一些特真点,然后让这些点在每一帧按照一定的规则进行动,从而改变整个模型的状态。

在ogre里面,Keyframe类描述的是一个特征点(一个结点、一根骨头、一个顶点)的某一个关键帧的状态(位置、缩放、朝向等)和对应的时间点。

一个可驱动点的所有keyframe组合成一个track,每个可驱动点都有一个他的track,这就好比某个点在整段动画中的轨迹,其中keyframe要由track来创建。

多个track组合在一起就成为了一段动画,用Animation类来表示,这就是一个动画,例如对于骨骼动画,他的每一个骨头都有一个track,那么所有的骨头的track的组合在一起也就是整个骨骼动画了。track由Animation来创建。Animation则可以通过skeleton或者scenenmanager等来创建。

AnimationSate类:通常我们操控一段动画的播放等都不是直接操纵animation

 类,而是通过一个类AnimationState,它是animation的一个实例,通过场景管理器创建某个animation的一个animationState,然后就可以利用这个animationstate来播放这个animation了。

 

AnimationState :这个来描述了一些动画的状态,用于控制动画的播放。

 

BoneBlendMask* mBlendMask;   // ???
String mAnimationName; //Animation名字
AnimationStateSet* mParent; // AnimationState 的管理类
Real mTimePos; // 时间点
Real mLength;  // 时间长度
Real mWeight;  // 骨骼的权重
bool mEnabled; // 是否播放
bool mLoop;   // 是否循环播放

 AnimationState只是控制动画的状态,其并不具备播放功能。另外,AnimationStateSet作为一个容器,不仅存储着AnimationState 而且维护着AnimationState 的状态变更,一旦AnimationState 的某个属性值被改变,AnimationStateSet维护的状态就会变脏。

 

 

Animation :

 

1.插值模式:

enum InterpolationMode{ // 插值状态
       IM_LINEAR,  // 线性插值
       IM_SPLINE   // 样条插值(较好)
};
enum RotationInterpolationMode{ //现状插值状态
       RIM_LINEAR,  // 线性插值
       RIM_SPHERICAL // 样条插值
};

2.三种track:

ogre实现了四种动画(1.骨骼动画 2.场景节点动画 3.顶点动画 4.数值动画),在Animation类里面管理了三种track(NumericAnimationTrack,NodeAnimationTrack,VertexAnimationTrack)他们继承于AnimationTrack。

NodeTrackList mNodeTrackList; // 一个<unsigned short,NodeAnimationTrack*>map
NumericTrackList mNumericTrackList; // 一个<unsigned short,NumericAnimationTrack*>
VertexTrackList mVertexTrackList; // 一个<unsigned short,VertexAnimationTrack*>
3.播放动画:
动画播放主要用到的方法是:apply,这个方法有好几个重载的方法,其中一个是播放所有的动画,其它的几个都是播放某个节点的方法。
//播放所有动画
void Animation::apply(Real timePos, Real weight, Real scale)
    {
		_applyBaseKeyFrame();

        // Calculate time index for fast keyframe search
        TimeIndex timeIndex = _getTimeIndex(timePos);

        NodeTrackList::iterator i;
        for (i = mNodeTrackList.begin(); i != mNodeTrackList.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);
		}

    }

//播放当前Skeleton的动画
void Animation::apply(Skeleton* skel, Real timePos, float weight,
      const AnimationState::BoneBlendMask* blendMask, Real scale)
    {
		_applyBaseKeyFrame();

		// Calculate time index for fast keyframe search
      TimeIndex timeIndex = _getTimeIndex(timePos);

      NodeTrackList::iterator i;
      for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
      {
        // get bone to apply to 
        Bone* b = skel->getBone(i->first);
		i->second->applyToNode(b, timeIndex, (*blendMask)[b->getHandle()] * weight, scale);
      }
    }
 

AnimationTrack :动画轨迹

KeyFrameList mKeyFrames;
KeyFrameIndexMap mKeyFrameIndexMap;

AnimationTrack 除了管理KeyFrame以外,还有几个有用的方法,只不过现在不了解,就先不贴出来分析了。

1.NumericAnimationTrack:

void NumericAnimationTrack::applyToAnimable(const AnimableValuePtr& anim, const TimeIndex& timeIndex,
		Real weight, Real scale)
	{
		// 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);

	}

最终动画是由AnimableValue对象播放的。但是,我看了一下AnimableValue的applyDeltaValue方法,都没有被实现,只是抛了一个异常,这是否意味着我们在使用的时候需要自己实现一个AnimableValue类,由于这种动画并不是需要重点关注的,暂时不往下继续了。

2.VertexAnimationTrack :顶点动画:

enum VertexAnimationType  // 顶点动画类型
{
	VAT_NONE = 0,    // 没有动画
	VAT_MORPH = 1,  // 形变动画
	VAT_POSE = 2     // 姿态动画
};

 顶点的轨迹可能有两种产生方式,所以在播放的时候也有两种方式:

void VertexAnimationTrack::applyToVertexData(VertexData* data,
		const TimeIndex& timeIndex, Real weight, const PoseList* 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
				const VertexElement* 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);

			// For each pose reference in key 1, we need to locate the entry in
			// key 2 and interpolate the influence
			const VertexPoseKeyFrame::PoseRefList& poseList1 = vkf1->getPoseReferences();
			const VertexPoseKeyFrame::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 (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 (p2->poseIndex <= poseList->size());
					const Pose* pose = (*poseList)[p2->poseIndex];
					// apply
					applyPoseToVertexData(pose, data, influence);
				}
			} // key 2 iteration
		} // morph or pose animation
	}

3.NodeAnimationTrack :

void NodeAnimationTrack::applyToNode(Node* node, const TimeIndex& timeIndex, Real weight,
		Real scl)
    {
		// 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;
            else if (weight != 1.0f)
                scale = Vector3::UNIT_SCALE + (scale - Vector3::UNIT_SCALE) * weight;
		}
		node->scale(scale);

    }
 

KeyFrame :KeyFrame 的内容比较简单,所有关键的东西都在Track里面处理了

NumericKeyFrame          // KeyFrame
TransformKeyFrame       // KeyFrame
VertexMorphKeyFrame  //  形变动画的KeyFrame
VertexPoseKeyFrame    //  姿态动画的KeyFrame

总结:对于整个ogre的动画体系来说,Animation 是动画的管理类,而AnimationState 是动画状态的控制类。Animation 的动画播放其实是调用其相应的track类来播放的,KeyFrame 更多的是起到数据单元的作用。所以,完全弄清楚四种动画的底层实现,重点需要关注的是track类,这里没有写多少因为我自己也不太清楚,后面在写例子的时候,再从使用者的角度详细描述一下ogre的动画的底部执行过程。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics