11.4.4 AnimatedModel的Update方法
在CPU中,你可以将任务分成三个部分以改进模型动画的性能。首先,你需要根据当前播放的动画和时间更新骨骼bone。第二,计算每个bone的绝对坐标。第三,计算用于变换顶点的最终矩阵。你通过计算当前动画时间开始第一步的处理过程。这一步只需将动画时间加上上一次更新以来的elapsed time,而elapsed time还要乘以animation speed因子:
activeAnimationTime += new TimeSpan((long)(time.ElapsedGameTime.Ticks * animationSpeed));
然后将当前动画的持续时间与activeAnimationTime比较,检查当前动画是否已经结束,如果已经结束,在重置动画时间后就可以重写开始这个动画:
// Loop the animation if (activeAnimationTime > activeAnimation.Duration && enableAnimationLoop) { long elapsedTicks = activeAnimationTime.Ticks % activeAnimation.Duration.Ticks; activeAnimationTime = new TimeSpan(elapsedTicks); activeAnimationKeyframe = 0; }
然后检查是否是动画的第一次更新。这里你需要将骨骼bone恢复到bind pose:
// Put the bind pose in the bones in the beginning of the animation if (activeAnimationKeyframe == 0) { for (int i= 0; i< bones.Length; i++) bones[i] = animatedModelData.BonesBindPose[i]; }
要重建动画,你需遍历当前模型动画的关键帧,当activeAnimationTime比关键帧时间大时更新模型骨骼bone:
// Read all animation keyframes until the current time is reached // That's possible because you have previously sorted the keyframes int index = 0; Keyframe[] keyframes = activeAnimation.Keyframes; while (index < keyframes.Length && keyframes[index].Time <= activeAnimationTime) { int boneIndex = keyframes[index].Bone; bones[boneIndex] = keyframes[index].Transform * bonesTransform[boneIndex]; index++; } activeAnimationKeyframe = index - 1;
在动画处理的第二部分你需要遍历所有的bone矩阵并计算它们的绝对配置。因为bone数组是根据深度构建的,这个数组中的parent bone不会有一个超过数组索引的索引值。所以你可以按顺序遍历集合中的每个元素,计算它的最终位置(因为它的ancestor的最终位置己经计算好了)。注意数组中的第一个bone已经在绝对坐标系中了(因为它没有parent),但你要使用一个自定义矩阵转换它:
// Fill the bones with their absolute coordinate bonesAbsolute[0] = bones[0] * parent; for (int i= 1; i< bonesAnimation.Length; i++) { int boneParent = animatedModelData.BonesParent[i]; // Here we are transforming a child bone by its parent bonesAbsolute[i] = bones[i] * bonesAbsolute[boneParent]; }
最后你通过乘以bone中的bind pose中的反变换矩阵计算每个bone的最终位置和当前的绝对位置:
// Before we can transform the mesh's vertices using the calculated // bone matrix, we need to put the vertices in the coordinate system // of the bone that is linked to it for (int i= 0; i< bonesAnimation.Length; i++) { bonesAnimation[i] = animatedModelData.BonesInverseBindPose[i] * bonesAbsolute[i]; }
下面是AnimatedModel类的Update方法代码:
private void UpdateAnimation(GameTime time, Matrix parent) { activeAnimationTime += new TimeSpan((long)(time.ElapsedGameTime.Ticks * animationSpeed)); if (activeAnimation != null) { // Loop the animation if (activeAnimationTime >activeAnimation.Duration && enableAnimationLoop) { long elapsedTicks = activeAnimationTime.Ticks %activeAnimation.Duration.Ticks; activeAnimationTime = new TimeSpan(elapsedTicks); activeAnimationKeyframe = 0; } // Every time the animation starts put the local bind pose in // the bones array if (activeAnimationKeyframe == 0) { for (int i= 0; i< bones.Length; i++) bones[i] = animatedModelData.BonesBindPose[i]; } // Play all animation keyframes until the current time // is reached. This is possible because we have sorted the // keyframes by time during the model processing int index = 0; Keyframe[] keyframes = activeAnimation.Keyframes; while (index < keyframes.Length &&keyframes[index].Time <= activeAnimationTime) { int boneIndex = keyframes[index].Bone; bones[boneIndex] = keyframes[index].Transform * bonesTransform[boneIndex]; index++; activeAnimationKeyframe = index - 1; } // Calculate the bones absolute coordinate bonesAbsolute[0] = bones[0] * parent; for (int i= 1; i< bonesAnimation.Length; i++) { int boneParent = animatedModelData.BonesParent[i]; // Transform the bone configuration by its // parent configuration bonesAbsolute[i] = bones[i] * bonesAbsolute[boneParent]; } // Before we can transform the vertices we // need to put the vertices in the coordinate system of the // bone that is linked to it for (int i= 0; i< bonesAnimation.Length; i++) { bonesAnimation[i] = animatedModelData.BonesInverseBindPose[i] * bonesAbsolute[i]; } } }
发布时间:2009/5/12 上午8:36:19 阅读次数:5242