XNA中的骨骼动画

来源:互联网 发布:淘宝u站营销导购平台 编辑:程序博客网 时间:2024/04/30 07:44

XNA中的骨骼动画

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

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

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和在它之中的所有对象必须要有自己的ContentTypeWriterContentTypeWriter定义了对象的输入以何种方式写入到XNB文件中。最后,在实时方式中,ContentManager使用ContentTypeReaderXNB文件读取对象的数据并返回模型对象。

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

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

115.素材管道的扩展,支持骨骼动画模型。

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

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

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

Keyframe

Keyframe类用来存储bone的一个动画帧。一个动画帧必须指向一个动画bone的引用,被引用的bone的新配置(位置和朝向)和新配置应用的时间。注意你使用keyframe设置初始bone配置,将当前配置变成一个新配置。你使用XNAMatrix类一矩阵形式存储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

例如,如图116中的depth traverse of the hierarchy返回包含Root BoneNeckLeft ShoulderLeft ForearmLeft HandLeft End BoneRight ShoulderRight ForearmRight HandRight 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存储每个boneparent的索引。最后,animations属性存储模型动画。

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

原创粉丝点击