38.蒙皮动画模型内容处理器
我们已经可以在X文件的导出系列5蒙皮动画模型中导出包含动画数据的模型,现在就可以自己编写内容处理器将动画数据包含在二进制的xnb文件中,然后在XNA程序中加以播放。但在进入到内容处理器的编写之前,首先需要理解XNA框架中Model类的组织方式。以下代码使用的模型都是X文件的导出系列5蒙皮动画模型导出的那个简陋的人体manSkinningX.X,它包含提右腿和提左腿两个动画。
ModelMesh和ModelBone
这个知识可参见什么是模型Bone和4.8 可视化模型骨骼结构。
简而言之:XNA框架的Model类表示整个模型。而Model中每个可独立运动的网格都对应一个ModelMesh,每个ModelMesh包含顶点、索引、纹理坐标等几何信息(即X文件中的Mesh、MeshNormals、MeshMaterialList、Material、MeshTextureCoords大括号中的数据),而它的位置信息(即X文件中FrameTransformMatrix大括号中的数据,其实就是一个表示相对于父节点偏移的4*4矩阵)存储在ModelBone对象中。两者通过ModelMeshBone中的ParentBone的Index属性联系在一起。
以manSkinningX.X为例,你可以使用在4.8 可视化模型骨骼结构中介绍的方法看到模型的结构:1.在XNA代码的合适地方设置中断,2.插入System.Diagnostics.Debugger.Break();代码,3.编程输出文本,我使用了第3个方法在一个文本文件中获得了这个模型的结构:
Model Bone Information ---------------------- - Name : Index: 0 - Name : Body Index: 1 - Name : null Index: 2 - Name : Spine Index: 3 - Name : Spine_Nub Index: 4 - Name : L_Leg Index: 5 - Name : null Index: 6 - Name : L_Nub Index: 7 - Name : R_Leg Index: 8 - Name : R_Nub Index: 9 Model Mesh Information ---------------------- - ID : 0 Name: Bone: (2)
由这个文件可以看出,manSkinningX.X只有一个ModelMesh,有10个ModelBone,而ModelMesh链接到索引为2的bone上。这个结构都是由框架默认的Model - XNA Framework处理器导出的,事实上在3DSMAX制作这个模型时,只有6个bone,索引为0,1,2,6的bone都是处理器自行添加的,至于为什么这样我不是很清楚,最不能理解的是为什么L_Leg下要添加一个索引为6的额外的bone,而R_Leg下却不添加。
但如果导出manSkinningKW.X文件的结构,却发现有点不同,只有9个bone。:
Model Bone Information ---------------------- - Name : Index: 0 - Name : Body Index: 1 - Name : mesh_Body Index: 2 - Name : Spine Index: 3 - Name : Spine_Nub Index: 4 - Name : L_Leg Index: 5 - Name : L_Nub Index: 6 - Name : R_Leg Index: 7 - Name : R_Nub Index: 8 Model Mesh Information ---------------------- - ID : 0 Name: mesh_Body Bone: mesh_Body (2)
如果导出manSkinningFbx.FBX文件的结构,只有8个bone,好像fbx文件的冗余数据最少:
Model Bone Information ---------------------- - Name : RootNode Index: 0 - Name : Body Index: 1 - Name : Spine Index: 2 - Name : Spine_Nub Index: 3 - Name : L_Leg Index: 4 - Name : L_Nub Index: 5 - Name : R_Leg Index: 6 - Name : R_Nub Index: 7 Model Mesh Information ---------------------- - ID : 0 Name: Body Bone: Body (1)
不过接下去的代码中我还是使用manSkinningX.X进行分析。
Content Pipeline类库中的类层次
ModelMesh和ModelBone位于XNA Framework Class Library中,但要理解内容管道中到底进行了什么处理,你首先需要理解Content Pipeline Class Library中的几个类的层次,如下图所示:
以下内容来自于XNA帮助文件:
1.ContentItem:提供定义内容(content)时所需的属性,这些内容是使用XNA框架中介文件格式定义的,是所有内容的基类。
2.NodeContent:它定义了本地坐标系统的图形对象的基类。
3.MeshContent:提供定义一个mesh的各种属性。一个mesh具有以下特征:
- Mesh是没有关节的。例如,车轮的mesh是和车身的mesh分离的。
- 它可以包含多个材质。例如,车身可以是一个mesh,它的风挡可以使用一个与车的皮质座椅不同的纹理和shader。
- 包含不同channel的顶点可以混合成一个mesh。例如,座椅的几何体包含切线向量,而风挡不需要。但是使用一个单独的mesh可以在材质和顶点格式边界保留拓扑连续性(译者?原文如下:However, using a single mesh preserves topological continuity (shared vertex identities) across material and vertex format boundaries)。
4.BoneContent:表示一个动画骨骼(Animation skeleton)。动画骨骼(Animation skeleton)是以一个BoneContent对象树表示的,这个树在Transform属性中保存了bind pose。整个骨骼的动画数据存储在根bone的Animations属性中(根bone也是一个BoneContent 对象)。
5.GeometryContent:定义一个geometry batch的各种属性。一个geometry batch是一个mesh的子组件,表示可以提交给GPU进行一次绘制调用的一组齐次几何数据。它包含了索引化的三角形列表(使用一个材质),其中所有顶点都共享相同的数据通道。 如果顶点在其数据通道中有所不同,则会被设置成唯一的(unique)。Coordinates that require unique vertices on either side of a join create unique vertices(译者:?)。
6.AnimationContent :提供表示动画所需的属性。一个动画包含一个数据channel的集合,这个集合表示一组完整的运动,这个运动可以施加在任意数量的bone或刚体上。数据channel存储在Animations dictionary中。对character skinning来说,动画数据通常链接在根bone。但是,它也可能属于任何节点。例如我们使用刚体动画的时候。
ContentItem的子类还有EffectContent,MaterialContent,BitmapContent,TextureContent,FontDescription,因为与本文无关,所以没有列出。
内容管道的调试
为了更好地理解内容管道的处理过程,你还想单步调试项目,但是因为内容管道不是在编译时运行的,所以在SkinnedModelPipeline内容管道项目中设置中断不会起任何作用,需要进行额外的操作,这个方法参考自外的操作,这个方法参考自http://www.cse.msu.edu/~cse473/step5.html。
你需要右击SkinnedModelPipeline,在弹出菜单中选择“属性”,在打开的页面中选择“调试”,点击“启动外部程序”右侧的按钮,找到你的电脑上的MSBuild.exe文件,它通常位于C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe,然后找到项目中的Content处理项目文件,我的电脑上位于F:\StunEngine0.4\TestGame\TestGame\Content\Content.contentproj(这个位置通常不一样,是由你的游戏项目位置决定的)。
最后在SkinnedModelPipeline项目中的SkinnedModelProcessor.cs文件合适位置设置中断,右击SkinnedModelPipeline项目,在弹出菜单中选择调试→启动新项目,程序就会在你中断处停止,你可以单步调试看看到底发生了什么事。
SkinnedModelWindows类库
好了,现在终于可以来研究一下XNA官网上的处理蒙皮动画模型的示例了,原文地址http://creators.xna.com/en-US/sample/skinnedmodel,经过翻译后的文章为蒙皮动画模型(Skinned Model)示例。我同时还参考了《Beginning XNA 3 0 Game Programming FromN ovice to Professional》(以下简称《BeninningXNA》)的第12章:骨骼动画,它的过程写得更加详尽,你应该先看一看11.1 动画类型对动画的概念有个大致的了解。
我想做的事情就是将manSkinningX.X文件中的动画数据经过一定的处理放置在Model类的Tag属性中,这样就可以被XNA程序调用了,此数据如下:
AnimationSet RaiseRight { Animation Anim-Body { { Body } AnimationKey { 4; 10; 0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 160;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 320;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 480;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 640;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 800;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 960;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 1120;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 1280;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 1440;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;; } } Animation Anim-Spine { { Spine } AnimationKey { 4; 10; 0;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 160;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 320;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 480;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 640;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 800;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 960;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 1120;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 1280;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 1440;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;; } } Animation Anim-Spine_Nub { { Spine_Nub } AnimationKey { 4; 10; 0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 160;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 320;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 480;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 640;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 800;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 960;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 1120;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 1280;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 1440;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;; } } Animation Anim-L_Leg { { L_Leg } AnimationKey { 4; 10; 0;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 160;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 320;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 480;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 640;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 800;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 960;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 1120;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 1280;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 1440;16;-1.000000,0.000648,0.000000,0.000000,0.000648,1.000000,0.000005,0.000000,-0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;; } } Animation Anim-L_Nub { { L_Nub } AnimationKey { 4; 10; 0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 160;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 320;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 480;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 640;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 800;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 960;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 1120;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 1280;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 1440;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;; } } Animation Anim-R_Leg { { R_Leg } AnimationKey { 4; 10; 0;16;-1.000000,-0.000073,-0.000000,0.000000,-0.000073,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 160;16;-0.999639,0.026860,-0.000000,0.000000,0.026860,0.999639,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 320;16;-0.995098,0.098892,-0.000000,0.000000,0.098892,0.995098,0.000005,0.000000,0.000001,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 480;16;-0.979352,0.202164,-0.000000,0.000000,0.202164,0.979352,0.000005,0.000000,0.000001,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 640;16;-0.946857,0.321654,-0.000000,0.000000,0.321654,0.946857,0.000005,0.000000,0.000002,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 800;16;-0.897005,0.442020,-0.000000,0.000000,0.442020,0.897005,0.000005,0.000000,0.000003,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 960;16;-0.835498,0.549493,-0.000000,0.000000,0.549493,0.835498,0.000005,0.000000,0.000003,0.000004,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 1120;16;-0.773614,0.633657,-0.000000,0.000000,0.633657,0.773614,0.000005,0.000000,0.000004,0.000004,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 1280;16;-0.725895,0.687805,-0.000000,0.000000,0.687805,0.725895,0.000005,0.000000,0.000004,0.000004,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 1440;16;-0.707107,0.707107,-0.000000,0.000000,0.707107,0.707107,0.000006,0.000000,0.000004,0.000004,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;; } } Animation Anim-R_Nub { { R_Nub } AnimationKey { 4; 10; 0;16;1.000000,-0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714412,-0.000000,0.000002,1.000000;;, 160;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,9.714414,-0.000000,0.000002,1.000000;;, 320;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714414,-0.000000,0.000002,1.000000;;, 480;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714414,-0.000000,0.000003,1.000000;;, 640;16;1.000000,-0.000000,0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714414,-0.000000,0.000003,1.000000;;, 800;16;1.000000,-0.000000,0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,0.000000,0.000003,1.000000;;, 960;16;1.000000,-0.000000,0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714413,0.000000,0.000003,1.000000;;, 1120;16;1.000000,-0.000000,-0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000003,1.000000;;, 1280;16;1.000000,-0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714414,-0.000000,0.000002,1.000000;;, 1440;16;1.000000,-0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714414,0.000000,0.000002,1.000000;;; } } } AnimationSet RaiseLeft { Animation Anim-Body { { Body } AnimationKey { 4; 10; 0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 160;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 320;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 480;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 640;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 800;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 960;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 1120;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 1280;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;, 1440;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,3.000000,0.000000,0.000000,1.000000;;; } } Animation Anim-Spine { { Spine } AnimationKey { 4; 10; 0;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 160;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 320;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 480;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 640;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 800;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 960;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 1120;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 1280;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;, 1440;16;0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,10.000000,-0.000000,1.000000;;; } } Animation Anim-Spine_Nub { { Spine_Nub } AnimationKey { 4; 10; 0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 160;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 320;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 480;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 640;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 800;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 960;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 1120;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 1280;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;, 1440;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,8.358768,0.000000,0.000000,1.000000;;; } } Animation Anim-L_Leg { { L_Leg } AnimationKey { 4; 10; 0;16;-0.999744,0.022619,0.000000,0.000000,0.022619,0.999744,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 160;16;-0.996618,0.082170,0.000000,0.000000,0.082170,0.996618,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 320;16;-0.985559,0.169334,0.000000,0.000000,0.169334,0.985559,0.000005,0.000000,0.000001,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 480;16;-0.961913,0.273356,0.000000,0.000000,0.273356,0.961913,0.000005,0.000000,0.000001,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 640;16;-0.923755,0.382983,0.000000,0.000000,0.382983,0.923755,0.000005,0.000000,0.000002,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 800;16;-0.873151,0.487449,0.000000,0.000000,0.487449,0.873151,0.000005,0.000000,0.000003,0.000005,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 960;16;-0.816258,0.577687,0.000000,0.000000,0.577687,0.816258,0.000005,0.000000,0.000003,0.000004,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 1120;16;-0.762399,0.647107,0.000000,0.000000,0.647107,0.762399,0.000005,0.000000,0.000003,0.000004,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 1280;16;-0.722472,0.691400,0.000000,0.000000,0.691400,0.722472,0.000005,0.000000,0.000004,0.000004,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;, 1440;16;-0.707107,0.707107,0.000000,0.000000,0.707107,0.707107,0.000005,0.000000,0.000004,0.000004,-1.000000,0.000000,0.000000,0.000000,-3.000000,1.000000;;; } } Animation Anim-L_Nub { { L_Nub } AnimationKey { 4; 10; 0;16;1.000000,0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,9.714414,-0.000000,0.000002,1.000000;;, 160;16;1.000000,-0.000000,-0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 320;16;1.000000,0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,9.714413,0.000000,0.000002,1.000000;;, 480;16;1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714413,0.000000,0.000002,1.000000;;, 640;16;1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714414,0.000000,0.000002,1.000000;;, 800;16;1.000000,-0.000000,-0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,9.714414,-0.000000,0.000002,1.000000;;, 960;16;1.000000,-0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714414,0.000000,0.000002,1.000000;;, 1120;16;1.000000,-0.000000,-0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,9.714415,-0.000000,0.000002,1.000000;;, 1280;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,0.000000,0.000002,1.000000;;, 1440;16;1.000000,0.000000,-0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,9.714414,0.000000,0.000002,1.000000;;; } } Animation Anim-R_Leg { { R_Leg } AnimationKey { 4; 10; 0;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 160;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 320;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 480;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 640;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 800;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 960;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 1120;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 1280;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;, 1440;16;-1.000000,0.000916,-0.000000,0.000000,0.000916,1.000000,0.000005,0.000000,0.000000,0.000005,-1.000000,0.000000,0.000000,-0.000001,3.000000,1.000000;;; } } Animation Anim-R_Nub { { R_Nub } AnimationKey { 4; 10; 0;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 160;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 320;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 480;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 640;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 800;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 960;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 1120;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 1280;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;, 1440;16;1.000000,-0.000000,-0.000000,0.000000,-0.000000,1.000000,-0.000000,0.000000,-0.000000,0.000000,1.000000,0.000000,9.714413,-0.000000,0.000002,1.000000;;; } } }
我们需要创建三个类Keyframe,AnimationClip,SkinningData存储这些数据,具体解释可参见11.2 XNA中的骨骼动画,因为这些数据在XNA程序和内容管道中都要用到,所以把这三个类放置在一个新的类库项目中,《Beginning XNA》和官网例子区别之一:《Beginning XNA》中这个类库名为AnimatedModelContentWin,官网中名为SkinnedModelWindows。
关键帧Keyframe类
关键帧是构成一个动画的最小单位,其中最关键的数据就是一个矩阵,X文件中AnimationKey中的一组16个数据即对于一个关键帧变换矩阵。因此建立以下Keyframe.cs类:
namespace SkinnedModel { ////// 关键帧,表示一个bone在一个时刻的位置。 /// public class Keyframe { ////// 创建一个Keyframe对象。 /// public Keyframe(int bone, TimeSpan time, Matrix transform) { Bone = bone; Time = time; Transform = transform; } ////// 用于XNB deserializer的私用构造函数 /// private Keyframe() { } ////// 获取此关键帧的bone索引 /// [ContentSerializer] public int Bone { get; private set; } ////// 获取此关键帧离开所属动画片段开始时刻的时间 /// [ContentSerializer] public TimeSpan Time { get; private set; } ////// 获取此关键帧的bone变换矩阵 /// [ContentSerializer] public Matrix Transform { get; private set; } } }
区别之二:《Beginning XNA》中关键帧按时间排序的代码就在这个类中实现,官网中在内容管道的模型处理器中实现。
动画片段AnimationClip类
而一组关键帧就构成了一个动画片段,对应X文件中AnimationSet大括号中的所有内容,因此建立以下AnimationClip.cs类:
namespace SkinnedModel { ////// 一个动画片段(AnimationClip)对应Microsoft.Xna.Framework.Content.Pipeline.Graphics.AnimationContent类型。 /// 它保存了一个动画的所有关键帧。 /// public class AnimationClip { ////// 创建一个AnimationClip对象 /// public AnimationClip(TimeSpan duration, Listkeyframes) { Duration = duration; Keyframes = keyframes; } /// /// 用于XNB deserializer的私有构造函数 /// private AnimationClip() { } ////// 获取动画的时间长度 /// [ContentSerializer] public TimeSpan Duration { get; private set; } ////// 获取包含所有关键帧的集合,关键帧需要根据时间排序 /// [ContentSerializer] public ListKeyframes { get; private set; } } }
区别之三:《Beginning XNA》中这个类叫做AnimationData,并多了个表示此动画片段名称的Name属性,Keyframes使用的是数组形式。官网中在这个名称属性在SkinningData类中,放在dictionary的key中,Keyframes使用的是泛型集合。
动画数据SkinningData类
最后是保存绘制一个蒙皮动画对象所需的所有数据的SkinningData.cs类,我们自定义处理器的最终目的就是得到这个类,并将它存储在模型的Tag属性中,它对应X文件中所有AnimationSet之下的内容(当然还需经过一些数据的组织)。代码如下:
namespace SkinnedModel { ////// 保存绘制一个蒙皮动画对象所需的所有数据,它存储在Model的Tag属性中。 /// public class SkinningData { ////// 创建一个SkinningData对象 /// public SkinningData(DictionaryanimationClips,List bindPose, List inverseBindPose,List skeletonHierarchy) { AnimationClips = animationClips; BindPose = bindPose; InverseBindPose = inverseBindPose; SkeletonHierarchy = skeletonHierarchy; } /// /// 用于XNB deserializer的私有构造函数 /// private SkinningData() { } ////// 获取动画片段的集合。这些动画片段存储在一个dictionary中,动画片段的名称,如"Walk", "Run","JumpReallyHigh"等作为dictionary的键。 /// [ContentSerializer] public DictionaryAnimationClips { get; private set; } /// /// 骨骼中每个bone的Bindpose矩阵,与父bone相联系 /// [ContentSerializer] public ListBindPose { get; private set; } /// /// 骨骼中每个bone的顶点至bone空间的转换矩阵 /// [ContentSerializer] public ListInverseBindPose { get; private set; } /// /// 对骨骼中的每个bone的父bone索引 /// [ContentSerializer] public ListSkeletonHierarchy { get; private set; } } }
区别之四:《Beginning XNA》中这个类叫做AnimatedModelData,动画片段的集合使用了数组,父bone索引的名称为bonesParent。官网中在动画片段的集合作为一个dictionary的vlaue,它的key为对应动画片段的名称,父bone索引的名称为SkeletonHierarchy。
在SkinnedModelWindows项目中还包含一个AnimationPlayer.cs类,但是这个类与本教程无关,将在下一个教程中讨论。
准备好数据类后,下面就可以讨论内容管道处理器了。
SkinnedModelPipeline处理器
《Beginning XNA》和XNA官网的例子本质上是相同的,只不过官网的例子多做了几个检查工作,更加严密点而已,所以以下代码以官网的例子为基础,但你也可以参见11.3 Animated Model Processor获取详尽的解释。以下是代码:
////// 重写Process方法,此方法将内容管道的NodeContent树中间格式转换为包含动画数据的Content对象 /// public override ModelContent Process(NodeContent input,ContentProcessorContext context) { // 确认这个mesh包含我们知道如何处理的数据 ValidateMesh(input, context, null); // 获取骨骼的根bone BoneContent skeleton = MeshHelper.FindSkeleton(input); if (skeleton == null) throw new InvalidContentException("未找到输入的skeleton。"); // We don't want to have to worry about different parts of the model being // in different local coordinate systems, so let's just bake everything. // 原文使用了Bake一词,译为烘焙,为3D程序中的一个专有名词,这里的操作就是计算每个bone的绝对变换 FlattenTransforms(input, skeleton); // 将skeleton的层次结构转换为一个BoneContent集合 IListbones = MeshHelper.FlattenSkeleton(skeleton); if (bones.Count > MaxBones) { throw new InvalidContentException(string.Format("骨骼包含{0}个bone,但最大只支持{1}个。",bones.Count, MaxBones)); } List bindPose = new List (); List inverseBindPose = new List (); List skeletonHierarchy = new List (); // 读取bind pose和骨骼层次数据 foreach (BoneContent bone in bones) { bindPose.Add(bone.Transform); inverseBindPose.Add(Matrix.Invert(bone.AbsoluteTransform)); skeletonHierarchy.Add(bones.IndexOf(bone.Parent as BoneContent)); } // 将动画数据转换为可实时运行的AnimationClip格式 Dictionary animationClips; animationClips = ProcessAnimations(skeleton.Animations, bones); // 调用基类的Process方法转换模型数据 ModelContent model = base.Process(input, context); // 将自定义动画数据存储在模型的Tag属性中 model.Tag = new SkinningData(animationClips, bindPose,inverseBindPose, skeletonHierarchy); return model; }
在重写的Process方法一开始调用ValidateMesh方法检查模型是否符合需要,在ValidateMesh方法中还调用了MeshHasSkinning方法,这两个方法主要检查模型制作时是否有几何体链接到骨骼的情况,如果有就会被删除,在X文件的导出系列5蒙皮动画模型中怪物的眼球就属于这种情况,被这个处理器处理后就不会被显示。第二是模型必须包含蒙皮数据。
////// 确认这个mesh包含我们知道如何处理的数据 /// static void ValidateMesh(NodeContent node, ContentProcessorContext context, string parentBoneName) { MeshContent mesh = node as MeshContent; if (mesh != null) { // 确认mesh. if (parentBoneName != null) { context.Logger.LogWarning(null, null, "Mesh {0}是bone {1}的一个子节点,而SkinnedModelProcessor " + "无法正确处理作为bone子节点的mesh。", mesh.Name, parentBoneName); } if (!MeshHasSkinning(mesh)) { context.Logger.LogWarning(null, null,"Mesh {0} 不包含skinning信息,所以会被删除",mesh.Name); mesh.Parent.Children.Remove(mesh); return; } } else if (node is BoneContent) { // 如果此节点是一个BoneContent,则我们需要记下它的名称用于进一步检查。 parentBoneName = node.Name; } // 递归检测子节点 (需要遍历子节点集合的副本,因为有可能在ValidateMesh过程中会删除某些子节点) foreach (NodeContent child in new List(node.Children)) ValidateMesh(child, context, parentBoneName); } /// /// 检测mesh是否包含skininng信息 /// static bool MeshHasSkinning(MeshContent mesh) { foreach (GeometryContent geometry in mesh.Geometry) { if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Weights())) return false; } return true; }
然后使用内容管道自带的MeshHelper.FindSkeleton方法获取根bone,它是一个BoneContent对象。
接着调用FlattenTransforms方法重新计算模型每个bone的绝对变换,在FlattenTransforms方法中调用了XNA自带的MeshHelper.TransformScene 方法,FlattenTransforms方法代码如下:
////// 烘焙模型几何体的变换矩阵,这样所有对象都在同一个坐标系中。 /// static void FlattenTransforms(NodeContent node, BoneContent skeleton) { foreach (NodeContent child in node.Children) { // 不要处理根节点,因为它是特殊的 if (child == skeleton) continue; // 烘焙几何体的本地变换 MeshHelper.TransformScene(child, child.Transform); // 烘焙完成后,我们就可以将本地坐标系设置为单位矩阵 child.Transform = Matrix.Identity; // 递归处理子节点 FlattenTransforms(child, skeleton); } }
区别之五:《Beginning XNA》中并不包含ValidateMesh和FlattenTransforms方法。
然后调用XNA自带的MeshHelper.FlattenSkeleton方法将Skeleton中层次结构的BoneContent转换为一个BoneContent集合。
还记得我们最终要获取的SkinningData有4个属性吗?前三个属性很容易获取,由以下代码实现:
ListbindPose = new List (); List inverseBindPose = new List (); List skeletonHierarchy = new List (); // 读取bind pose和骨骼层次数据 foreach (BoneContent bone in bones) { bindPose.Add(bone.Transform); inverseBindPose.Add(Matrix.Invert(bone.AbsoluteTransform)); skeletonHierarchy.Add(bones.IndexOf(bone.Parent as BoneContent)); }
难的数据是第4个,调用了ProcessAnimations处理所有动画,而ProcessAnimations又调用了ProcessAnimation处理每一个动画,代码如下:
////// 将内容管道的AnimationContentDictionary对象转换为可实时运行的AnimationClip格式 /// static DictionaryProcessAnimations(AnimationContentDictionary animations, IList bones) { // 创建一个dictionary将bone名称对应bone索引 Dictionary boneMap = new Dictionary (); // 填充这个dictionary for (int i = 0; i < bones.Count; i++) { string boneName = bones[i].Name; if (!string.IsNullOrEmpty(boneName)) boneMap.Add(boneName, i); } // 依次转换每个动画片段 Dictionary animationClips; animationClips = new Dictionary (); foreach (KeyValuePair animation in animations) { AnimationClip processed = ProcessAnimation(animation.Value, boneMap); animationClips.Add(animation.Key, processed); } if (animationClips.Count == 0) { throw new InvalidContentException("输入文件不包含任何动画。"); } return animationClips; } /// /// 将内容管道的AnimationContentDictionary对象转换为可实时运行的AnimationClip格式 /// static AnimationClip ProcessAnimation(AnimationContent animation,DictionaryboneMap) { List keyframes = new List (); // 遍历每个动画通道 foreach (KeyValuePair channel in animation.Channels) { // 获取当前通道控制的bone索引 int boneIndex; if (!boneMap.TryGetValue(channel.Key, out boneIndex)) { throw new InvalidContentException(string.Format( "未在bone'{0}'中找到动画数据, " + "它不是骨骼的一部分。", channel.Key)); } // 转换关键帧数据 foreach (AnimationKeyframe keyframe in channel.Value) { keyframes.Add(new Keyframe(boneIndex, keyframe.Time,keyframe.Transform)); } } // 根据时间对关键帧排序 keyframes.Sort(CompareKeyframeTimes); if (keyframes.Count == 0) throw new InvalidContentException("动画不包含关键帧"); if (animation.Duration <= TimeSpan.Zero) throw new InvalidContentException("动画持续时间为0"); return new AnimationClip(animation.Duration, keyframes); } /// /// 将关键帧根据时间升序排序 /// static int CompareKeyframeTimes(Keyframe a, Keyframe b) { return a.Time.CompareTo(b.Time); }
要理解上述过程,你一定要对动画数据的组织结构有所了解,强烈建议设置中断看看动画数据到底在哪里,它们是如何组织的。
由上述截图可以看出,动画数据位于skeleton下的Animations(http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.graphics.nodecontent.animations(v=XNAGameStudio.31).aspx)中,Animations的类型是AnimationContentDictionary(http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.graphics.animationcontentdictionary.aspx),而AnimationContentDictionary的Key为动画片段名称,在本例中有两个动画片段:RaiseLeft和RaiseRight,AnimationContentDictionary的Value即动画片段,对应内容管道中的AnimationContent(http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.graphics.animationcontent(v=XNAGameStudio.31).aspx)类。
在AnimationContent类之下有一个Channels属性,对应AnimationChannelDictionary(http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.graphics.animationchanneldictionary(v=XNAGameStudio.31).aspx)类,AnimationChannelDictionary类的Key对应bone,本例中为6个:Spine,Spine_Nub,L_Leg,L_Nub,R_Leg,R_Nub,AnimationChannelDictionary类的Value是一个AnimationChannel(http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.graphics.animationchannel(v=XNAGameStudio.31).aspx)对象,此对象包含了类型为AnimationKeyframe(http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.graphics.animationkeyframe(v=XNAGameStudio.31).aspx)的关键帧集合。
在AnimationContent类之下还有一个Duration(http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.graphics.animationcontent.duration(v=XNAGameStudio.31).aspx)的属性表示动画片段的时间长度。
只有理解了以上知识,你才会真正了解提取动画进行的操作。
提取完动画后,使用以下代码将处理好的SkinningData数据存储在模型的Tag属性中:
// 将自定义动画数据存储在模型的Tag属性中 model.Tag = new SkinningData(animationClips, bindPose, inverseBindPose, skeletonHierarchy);
区别之六:《Beginning XNA》中还要编写Content Type Writers和Content Type Readers,而在XNA3.1中可以通过反射实现,无需此代码。
最后,在官网的例子中还编写代码实现了自定义Effect,这个代码在我的引擎中并不需要,因为使用自定义Effect我是在引擎中的实现的。只要不使用BasicEffect而使用自定义effect,XNA官网的很多例子都用到了类似的代码:
////// 改变材质使之可以使用蒙皮模型的effect. /// protected override MaterialContent ConvertMaterial(MaterialContent material,ContentProcessorContext context) { BasicMaterialContent basicMaterial = material as BasicMaterialContent; if (basicMaterial == null) { throw new InvalidContentException(string.Format( "SkinnedModelProcessor只支持BasicMaterialContent," + "但输入的网格使用了{0}.", material.GetType())); } EffectMaterialContent effectMaterial = new EffectMaterialContent(); // 存储effect的引用 string effectPath = Path.GetFullPath("SkinnedModel.fx"); effectMaterial.Effect = new ExternalReference(effectPath); // 将纹理设置从输入的BasicMaterialContent对象复制到新材质中 if (basicMaterial.Texture != null) effectMaterial.Textures.Add("Texture", basicMaterial.Texture); // 调用基类的ConvertMaterial方法 return base.ConvertMaterial(effectMaterial, context); }
好了,至此已经使用自定义模型处理器完成了动画数据的处理,但工作只完成了一半,下一个教程将讨论如何在XNA代码中使用这些动画数据。
文件下载(已下载 1740 次)发布时间:2010/6/7 下午1:11:16 阅读次数:11140