XNA中的骨骼动画

来源:互联网 发布:网络聊天的好处 编辑:程序博客网 时间:2024/04/30 03:57

XNA有个定义得很好的素材管道(Content Pipeline),它被分成了不同层次和导入器、处理器、编译器(content writers)和读取器(content readers)。因为XNA的素材管道对骨骼动画并不完成支持,你需要自己扩展素材管道添加对骨骼动画的支持。注意素材管道部分支持骨骼动画,因为它可以导入带有骨骼动画数据的X和FBX文件,但输出时并不处理所有的骨骼动画数据。图11-4显示了用于导入、处理、编译并读取模型文件俄素材管道类的简化图示。

首先,根据相应的素材导入器(content importer)导入模型,每个素材导入器将模型数据转换为XNA document object model (DOM)格式。通过这种方式,模型在导入后成为相同格式并可以被相应的素材处理器(content processor)-ModelProcessor处理。模型导入器的输出是一个root NodeContent对象,这个对象描述了一个有自己坐标和子节点的图形类型。有两个类可以扩展NodeContent类:MeshContent和BoneContent。这样,从模型导入器输出的root NodeContent对象可以拥有一些子NodeContent,MeshContent和BoneContent。 
 

图11-4. XNA ContentPipeline-用来导入、处理、编译和读取模型的类 

ModelProcessor将模型导入器(model importer)输出的root NodeContent 对象作为一个参数,返回一个ModelContent对象。ModelContent对象返回object returned by the ModelProcessor has the processed model data, which needs to be stored into an XNB binary file. 要将ModelContent 对象存储为XNB文件,ModelContent和在它之中的所有对象必须要有自己的ContentTypeWriter。ContentTypeWriter定义了对象的输入以何种方式写入到XNB文件中。最后,在实时方式中,ContentManager使用ContentTypeReader从XNB文件读取对象的数据并返回模型对象。

要在XNA中添加对骨骼动画的支持,你需要扩展默认的模型模型处理器(model processor),创建一个新的处理过程并存储模型的骨骼和动画。除此以外,你还要创建一些新的类存储骨骼动画数据和一些ContentTypeWriter和ContentTypeReader类写入和读取这些数据。

图11-5显示了扩展素材管道所需创建的类,添加了对骨骼动画模型的支持。这些类用红色标注(译者注:图中的灰色)。 
 

图11-5.素材管道的扩展,支持骨骼动画模型。 

因为存储骨骼动画数据的类将由程序实时调用,所以你将在一个独立的库中创建这些类。要存储这些类,要创建一个新的叫做AnimationModelContentWin的Windows Game Library。在Windows 平台上model processor将使用这些类存储骨骼动画数据。如果你的游戏是运行在Windows 平台上的,那么这个库还会实时载入骨骼动画数据。

如果是在Xbox 360平台,你还要多创建一个项目:叫做AnimationModelContentXbox的Xbox 360 Game Library。这个库包含与AnimationModelContentWin相同的文件,但Xbox 360程序通过这个库实时载入数据。即使你只运行在Xbox 360平台,你仍需要AnimationModelContentWin,因为原始模型文件是在Windows平台导入并处理的,需要一个Windows库存储这些模型数据。

你将创建不同的类存储骨骼动画数据:Keyframe,AnimationData和AnimatedModelData。Keyframe类存储骨骼动画的动画帧,每个动画帧存储骨骼中bone的一个新配置。AnimationData类存储一个keyframes的数组,组成完整的动画(比如跑、跳等)。最后,AnimatedModelData存储模型骨骼(bone和hierarchy)和一个AnimatedModelData类型的数组,包含所有的模型动画。 
Keyframe类

Keyframe类用来存储bone的一个动画帧。一个动画帧必须指向一个动画bone的引用,被引用的bone的新配置(位置和朝向)和新配置应用的时间。注意你使用keyframe设置初始bone配置,将当前配置变成一个新配置。你使用XNA的Matrix类一矩阵形式存储bone的配置,使用TimeSpan存储动画时间(关键帧作用的时间)。

在AnimatedModelData类中你以bone数组的形式存储模型骨骼,它是由模型骨骼不同深度的traverse构成的。这样,对bone的引用可以以一个代表AnimatedModelData类中bone数组中的一个bone的索引整数表示。Keyframe类的代码如下: 
public class Keyframe : IComparable
{
  int boneIndex;
  TimeSpan time;
  Matrix transform; 
   
  // Properties... 
   
  public TimeSpan Time { get { return time; } set { time = value; } } 
  public int Bone { get { return boneIndex; } set { boneIndex = value; } } 
  public Matrix Transform { get { return transform; } set { transform = value; } } 
   
  public Keyframe(TimeSpan time, int boneIndex, Matrix transform) 
  {
  this.time = time; 
  this.boneIndex = boneIndex; 
  this.transform = transform; 
  } 
   
  public int CompareTo(object obj) 
  {
  Keyframe keyframe = obj as Keyframe; 
  if (obj == null)
  throw new ArgumentException("Object is not a Keyframe."); 
  return time.CompareTo(keyframe.Time);
  } 
}

在Keyframe中,你实现了一个Icomparable接口用来比较Keyframe对象。Keyframe对象是基于时间来属性比较的。你以后将使用这个比较根据时间帧对关键帧排序。
AnimationData类 

AnimationData类用来存储一个完整的模型动画(比如跑、跳等)。你以一个关键帧数组的形式存储每个动画,除了关键帧,你还有存储其他诸如动画名称和持续时间等数据。代码如下:
public class AnimationData { string name; TimeSpan duration; Keyframe[] keyframes; 
public string Name { get { return name; } set { name = value; } } 
public TimeSpan Duration { get { return duration; } set { duration = value; } } 
public Keyframe[] Keyframes { get { return keyframes; } set { keyframes = value; } } 

public AnimationData(string name, TimeSpan duration, Keyframe[] keyframes)

  this.name = name;
  this.duration = duration;
  this.keyframes = keyframes; 
}
AnimatedModelData类 

AnimatedModelData类用来存储模型骨骼和动画。你以bone数组的形式存储模型骨骼,每个bone都以矩阵表示。你通过模型骨骼不同depth traverse构建bone数组。depth traversal开始于骨骼的root bone终止于最深的bone。当找到最深的bone后,这个traversal将返回并查找另一条可能的路径,再次找到最深的bone。

例如,如图11-6中的depth traverse of the hierarchy返回包含Root Bone,Neck,Left Shoulder,Left Forearm,Left Hand,Left End Bone,Right Shoulder,Right Forearm,Right Hand和Right End Bone的数组。 
 

图11-6. 骨骼层次hierarchy的例子 

你将骨骼bone存储在它的bind pose配置中。bind pose是指哪个bone连接在模型网格上和是否是动画的开始pose。如果模型没有动画或当动画开始时,所有的模型bone都在bind pose中找到。在AnimatedModelData类,你需要创建两个Matrix数组类型用来存储骨骼bone,一个整数数组存储骨骼bone的层次,一个AnimationData类型的数组存储模型动画。代码如下: 
public class AnimatedModelData
{
  Matrix[] bonesBindPose; 
  Matrix[] bonesInverseBindPose;
  int[] bonesParent; 
  AnimationData[] animations; 
   
  // Properties ... 
  public int[] BonesParent { get { return bonesParent; } set { bonesParent = value; } }
  public Matrix[] BonesBindPose { get { return bonesBindPose; } set { bonesBindPose = value; } 
  public Matrix[] BonesInverseBindPose { get { return bonesInverseBindPose; } set { bonesInverseBindPose = value; } } 
  public AnimationData[] Animations { get { return animations; } set { animations = value; } } 
   
  public AnimatedModelData(Matrix[] bonesBindPose, Matrix[] bonesInverseBindPose, int[] bonesParent, AnimationData[] animations) 
  {
  this.bonesParent = bonesParent; 
  this.bonesBindPose = bonesBindPose;
  this.bonesInverseBindPose = bonesInverseBindPose; 
  this.animations = animations;
  } 

在AnimatedModelData类中,bonesBindPose属性存储一个包含本地配置(local configuration)(相对于它的ancestor)的数组中,bonesInverseBindPose属性存储反绝对配置(inverse absolute configuration)(与它的ancestor无关),bonesParent存储每个bone的parent的索引。最后,animations属性存储模型动画。

你使用bone的inverse absolute configuration将bone的顶点的默认坐标系(模型坐标系)转换为这个bone的坐标系,我们会在“Skeletal Animation Equations”部分详细介绍这个过程。

原创粉丝点击