载入动画模型
本节你将创建一个叫做AnimatedModel的用于实时处理骨骼动画模型的类,这个类包含载入模型,播放和更新动画,绘制模型的方法,AnimatedModel类中开始时先声明变量。
动画模型以XNA Model的形式被载入,它有一个dictionary,此dictionary包含一个可以通过Tag属性访问的AnimatedModelData对象。通过这种方式,Model类包含了模型的网格和effect,而AnimatedModelData包含模型的骨骼和动画。你声明了类型为Model的model变量和类型为AnimatedModelData的animatedModel变量,你将模型的变换信息存储在一个类型为Transformation的变量中。
Model model;
AnimatedModelData animatedModelData;
Transformation transformation;
你还需要声明一些变量处理如何重建动画。你需要存储当前播放的动画、当前帧和动画的时间。你声明activeAnimation变量存储当前播放的动画,activeAnimationKeyframeIndex和activeAnimationTime存储当前动画帧和时间:
AnimationData activeAnimation;
int activeAnimationKeyframe;
TimeSpan activeAnimationTime;
你还需要声明两个变量用来设置动画速度和开启动画循环——enableAnimationLoop animationSpeed:
bool enableAnimationLoop;
float animationSpeed;
当模型正在进行动画时,你使用一些临时矩阵数组用来计算骨骼bone的最后配置。因为bone的配置是作为一个动画改变的,所以你要声明bone变量存储每个bone的本地配置。你还要声明bonesAbsolute变量存储每个bone的绝对配置,这个配置由bone数组计算得出,用在实时动画中。最后你应声明bonesAnimation变量存储每个bone的最后变换,这个变换用于将顶点放置到bone的坐标系统内,并使用每个bone的绝对配置使它们动起来。我们会在“骨骼动画公式”一节中详细解释细节。
Matrix[] bones;
Matrix[] bonesAbsolute;
Matrix[] bonesAnimation;
要能把自定义变换作用到bone上,你还要声明一个矩阵数组。你可以独立于动画使用自定义变换改变骨骼的bone。例如,你可以对角色骨骼的脖子bone作用一个自定义的旋转:
Matrix[] bonesTransform;
最后,你需要声明两个变量存储动画模型的effect和材质:
AnimatedModelEffect animatedModelEffect;
LightMaterial lightMaterial;
你创建了AnimatedModelEffect类封装了模型的effect,可以使用第8章中创建的LightMaterial类设置它。
载入动画模型
动画模型是作为一个XNA Model存储的,所以第一步是使用素材管理器载入一个XNA Model,然后你需要检查这个模型是否是动画模型-是否在模型的Tag属性中包含一个拥有AnimatedModelData对象的dictionary。
model = Game.Content.Load<Model>( GameAssetsPath.MODELS_PATH + modelFileName);
// Get the dictionary
Dictionary<string, object> modelTag =(Dictionary<string, object>)model.Tag;
if (modelTag == null)
throw new InvalidOperationException("This is not a valid animated model.");
// Get the AnimatedModelData from the dictionary
if (modelTag.ContainsKey("AnimatedModelData"))
animatedModelData = (AnimatedModelData)modelTag["AnimatedModelData"];
else
throw new InvalidOperationException("This is not a valid animated model.");
载入模型后,你需要初始化一些用来设置和重新生成动画的变量。默认模型动画被设置为AnimatedModelData对象中的动画序列的第一个,它被存储在activeAnimation变量中:
if (animatedModelData.Animations.Length > 0)
activeAnimation = animatedModelData.Animations[0];
当前动画关键帧和时间被存储在对应的activeAnimationKeyframe和activeAnimationTime变量中,最后你通过animationSpeed变量设置动画的速度。
// Default animation configuration
animationSpeed = 1.0f;
activeAnimationKeyframe = 0;
activeAnimationTime = TimeSpan.Zero;
当模型正在进行动画时,你使用一些临时矩阵数组用来计算每个bone的最后配置。这里你需要创建一个矩阵数组,数组长度应该等于bone的数量。你应该使用存储在AnimatedModelData中的bone配置初始化bone数组,用单位矩阵初始化bonesTransform数组。
// Temporary matrices used to animate the bones
bones = new Matrix[animatedModelData.BonesBindPose.Length];
bonesAbsolute = new Matrix[animatedModelData.BonesBindPose.Length];
bonesAnimation = new Matrix[animatedModelData.BonesBindPose.Length];
// Used to apply custom transformation over the bones
bonesTransform = new Matrix[animatedModelData.BonesBindPose.Length];
for (int i= 0; i< bones.Length; i++)
{
bones[i] = animatedModelData.BonesBindPose[i];
bonesTransform[i] = Matrix.Identity;
}
最后,你获取模型的effect,并把它封装在AnimatedModelEffect中:
// Get the animated model effect - Shared by all meshes
animatedModelEffect = new AnimatedModelEffect(model.Meshes[0].Effects[0]);
// Create a default material
lightMaterial = new LightMaterial();
注意绘制模型的effect也被所有的模型网格共享,下面是AnimatedModel类的Load方法的代码:
public void Load(string modelFileName)
{
if (!isInitialized)
Initialize();
model = Game.Content.Load<Model>( GameAssetsPath.MODELS_PATH + modelFileName);
// Get the dictionary
Dictionary<string, object> modelTag =(Dictionary<string, object>)model.Tag;
if (modelTag == null)
throw new InvalidOperationException( "This is not a valid animated model.");
// Get the AnimatedModelData from the dictionary
if (modelTag.ContainsKey("AnimatedModelData"))
animatedModelData = (AnimatedModelData)modelTag["AnimatedModelData"];
else
throw new InvalidOperationException("This is not a valid animated model.");
// Default animation
animationSpeed = 1.0f;
activeAnimationKeyframe = 0;
activeAnimationTime = TimeSpan.Zero;
if (animatedModelData.Animations.Length > 0)
activeAnimation = animatedModelData.Animations[0];
// Temporary matrices used to animate the bones
bones = new Matrix[animatedModelData.BonesBindPose.Length];
bonesAbsolute = new Matrix[animatedModelData.BonesBindPose.Length];
bonesAnimation = new Matrix[animatedModelData.BonesBindPose.Length];
// Used to apply custom transformation over the bones
bonesTransform = new Matrix[animatedModelData.BonesBindPose.Length];
for (int i= 0; i< bones.Length; i++)
{
bones[i] = animatedModelData.BonesBindPose[i];
bonesTransform[i] = Matrix.Identity;
}
// Get the animated model effect - Shared by all meshes
animatedModelEffect = new AnimatedModelEffect(model.Meshes[0].Effects[0]);
// Create a default material
lightMaterial = new LightMaterial();
}