X文件解析.docx
《X文件解析.docx》由会员分享,可在线阅读,更多相关《X文件解析.docx(23页珍藏版)》请在冰点文库上搜索。
X文件解析
在三维图形程序设计中,网格模型占有非常重要的地位,而且也是比较复杂的部分,特别是包含动画和蒙皮信息的网格模型。
•X文件格式分析
•X文件格式最初是为传统的Direct3D保留模式而设计的,在DirectX6.0问世后,针对立即模式对它作过一次扩展。
要想在Direct3D程序中灵活自如地使用网格模型,应当深入理解.x文件格式。
•X文件是由模板(template)驱动的,模板定义了如何存储一个数据对象,这样用户便可以自己定义具体的格式。
Direct3D预定义了许多模板,这些预定义
的模板位于rmxftmpl.h中,模板标识符都在rmxfguid.h中,通用文件DXUTMesh.cpp包含了这两个头文件。
模板所允许的数据类型称为可选成员
(optionalmember),这些可选成员作为数据对象的子对象来保存,子对象可以是另一种数据类型或对先前数据对象的引用,或是一个二进制对象。
来看一个.x文件(cube_1.x)的完整内容:
xof0302txt0064
Header{
1;
0;
1;
}
MeshCube{//网格
8;〃8个顶点,以下为8个顶点的坐标
1.000000;1.000000;-1.000000;,
-1.000000;1.000000;-1.000000;,
-1.000000;1.000000;1.000000;,
1.000000;1.000000;1.000000;,
1.000000;-1.000000;-1.000000;,
-1.000000;-1.000000;-1.000000;,
-1.000000;-1.000000;1.000000;,
1.000000;-1.000000;1.000000;;
12;//12个面,以下为每个面三个顶点的索引3;0,1,2;,
3;0,2,3;,
3;0,4,5;,
3;0,5,1;,
3;1,5,6;,
3;1,6,2;,
3;2,6,7;,
3;2,7,3;,
3;3,7,4;,
3;3,4,0;,
3;4,7,6;,
3;4,6,5;;
}
该模型的效果图如下:
第一行文件头中的"xof"告诉程序,它加载的文件是一个.x文件,"0302"表示它使用的是DirectX3.2版本的模板,"txt"表示这些数据是以文本形式存储的,"0064"定义了浮点数的位数是64位。
文件头后面的一个数据块涉及了模板和数据对象等内容。
一个模板和一个数据对
象之间的差异在于所有的模板都是以一个template单词开始,模板看起来很像
一个C语言的结构定义,数据对象就是那些模板的实例。
使用模板定义包含在.x文件里的数据对象(一个模板定义了数据对象的布局),每个模板都可以通过一个数据类型的集合去定义并容纳任何类型的数据。
同时,任何数据类型的组合都
可以在一个模板里使用。
模板header的定义如下:
templateHeader{
<3D82AB43-62DA-11cf-AB39-0020AF71E433>
WORDmajor;
WORDminor;
DWORDflags;
}
各成员分别表示.x文件最大版本,最小版本,以及定义方式。
•X文件中涉及模板的概念,它由四部分组成:
第一部分是模板的名字,可是由数字、字符、下划线构成,但不能以数字开头,第二部分是GUID(GlobalUniqueIdentifier,全局唯一标识符),第三部分由各个数据项构成,最后一部分用于控制模板的限制程度,一个模板可以是开放的、闭合的或受限的。
开放模板的定义在结束部位有一个待展开的方括号[…]表示它可以包含其他数据类型,封闭模板不能包含其他任何数据类型,受限模板只能包含特定的数据类型。
模板的使用
与结构体有相似之处。
一般情况下,.x文件都至少包含一个Mesh模板,其定义如下:
templateMesh
{
<3D82AB44-62DA-11CF-AB39-0020AF71E433>
DWORDnVertices;
arrayVectorvertices[nVertices];
DWORDnFaces;
arrayMeshFacefaces[nFaces];
[•••]
}
Where:
nVertices-Numberofvertices.
arrayVectorvertices[nVertices]-Arrayofvertices,eachoftype
Vector.
nFaces-Numberoffaces.
arrayMeshFacefaces[nFaces]-Arrayoffaces,eachoftype
MeshFace.
[...]-Any.xfiletemplatecanbeusedhere.Thismakesthe
architectureextensible.MaterialandTextureFilenametemplatesaretypicallyused.
Mesh模板是一个开放的模板,同时它还用到了Vector模板和MeshFace模板,它们的定义如下:
Definesavector.
templateVector
{
<3D82AB5E-62DA-11cf-AB39-0020AF71E433>
floatx;
floaty;
float乙
}
templateMeshFace
{
<3D82AB5F-62DA-11cf-AB39-0020AF71E433>
DWORDnFaceVertexIndices;
arrayDWORDfaceVertexIndices[nFaceVertexIndices];
}
Where:
nFaceVertexIndices-Numberofindices.
arrayDWORDfaceVertexIndices[nFaceVertexIndices]-Arrayof
indices.
因为没有为网格模型设置材质和纹理等信息,所以该文件显示效果是一个黑色立方体,接下来将为该立方体添加材质、法线和纹理信息。
我们在cube_1.x的基础上添加材质、法线和纹理,构成cube_2.x:
效果图如下:
可以看到在Mesh模板中嵌套着一个子模板MeshMaterialList,它是Mesh模
板的一部分,用来将每个面与材质相关联,其定义如下:
templateMeshMaterialList
{
DWORDnMaterials;
DWORDnFaceIndexes;
arrayDWORDfaceIndexes[nFaceIndexes];
[Material<3D82AB4D-62DA-11CF-AB39-0020AF71E433>]
}
Where:
nMaterials-ADWORD.Thenumberofmaterials.
nFaceIndexes-ADWORD.Thenumberofindices.
faceIndexes[nFaceIndexes]-AnarrrayofDWORDscontainingthe
faceindices.
MeshMaterialList是一个受限的模板,它只能包含Material模板,其定义如下:
templateMaterial
{
<3D82AB4D-62DA-11CF-AB39-0020AF71E433>
ColorRGBAfaceColor;
FLOATpower;
ColorRGBspecularColor;
ColorRGBemissiveColor;
[…]
}
Where:
faceColor-Facecolor.AColorRGBAtemplate.
power-Materialspecularcolorexponent.
specularColor-Materialspecularcolor.AColorRGBtemplate.emissiveColor-Materialemissivecolor.AColorRGBtemplate.
templateColorRGBA
{
<35FF44E0-6C7C-11cf-8F52-0040333594A3>
floatred;
floatgreen;
floatblue;
floatalpha;
}
templateColorRGB
{
floatred;
floatgreen;
floatblue;
}
在cube_2.x中,首先定义了两个材质RedMaterial和GreenMaterial:
MaterialRedMaterial{//第一块材料
1.000000;0.000000;0.000000;1.000000;;//R=1.0,G=0.0,B
=0.0
0.000000;
0.000000;0.000000;0.000000;;
0.000000;0.000000;0.000000;;
TextureFilename{
"Tex1.jpg";//纹理文件名
}
}
MaterialGreenMaterial{//第二块材料
0.000000;1.000000;0.000000;1.000000;;//R=0.0,G=1.0,B
=0.0
0.000000;
0.000000;0.000000;0.000000;;
0.000000;0.000000;0.000000;;
TextureFilename{
"Tex2.jpg";//纹理文件名
}
}
在模板MeshMaterialList中则给出了各个面与材质的关联信息:
//网格材质列表
//使用材质的数量:
2块材质
//为12个面指定材质
//为前6个面使用第一块材质
MeshMaterialList{
2;
12;
0,
0,
0,0,
0,
0,
1,//为后面的6个面使用第二块材质1,
1,
1,
1,1;;
1JJ
{RedMaterial}//第一块材质,引用前面定义的RedMaterial材质{GreenMaterial}//第二块材质,引用前面定义的GreenMaterial材质
}
其中,{RedMaterial}和{GreenMaterial}是对上面定义的材质模板对象的引用。
在光照模型运算时需要用到法向量,法向量分为面法向量和顶点法向量。
在基于
逐顶点计算的光照模型中,需要使用顶点法向量。
通常顶点法向量的计算过程是:
先将共享该顶点的几个面的面法向量相加并除以共享该顶点的面的个数,接着归
一化这个结果。
模板MeshNormals用来指定法向量:
templateMeshNormals
{
DWORDnNormals;
arrayVectornormals[nNormals];
DWORDnFaceNormals;
arrayMeshFacefaceNormals[nFaceNormals];
}
Where:
nNormals-Numberofnormals.
arrayVectornormals[nNormals]-Arrayofnormals.
nFaceNormals-Numberoffacenormals.
arrayMeshFacefaceNormals[nFaceNormals]-Arrayofmeshface
normals.
在文件cube_2.x中,法向量的定义以及为面指定法向量的内容如下:
//顶点法线
MeshNormals{
8;//定义8个法线向量
0.333333;0.666667;-0.666667;,
-0.816497;0.408248;-0.408248;,
-0.333333;0.666667;0.666667;,
0.816497;0.408248;0.408248;,
0.666667;-0.666667;-0.333333;,
-0.408248;-0.408248;-0.816497;,
-0.666667;-0.666667;0.333333;,
0.408248;-0.408248;0.816497;;
12;//为12个面的每个顶点指定法线
3;0,1,2;,
3;0,2,3;,
3;0,4,5;,
3;0,5,1;,
3;1,5,6;,
3;1,6,2;,
3;2,6,7;,
3;2,7,3;,
3;3,7,4;,
3;3,4,0;,
3;4,7,6;,
3;4,6,5;;
}
模板TextureFilename用于引用纹理,它通常作为Material模板对象的子对象
出现,其定义如下:
templateTextureFilename
{
stringfilename;
}
在使用TextureFilename模板时,只需要使用字符串filename指定一个纹理文
件名即可,但要将这幅纹理映射到网格模型中,还需要指定纹理坐标:
templateMeshTextureCoords
{
DWORDnTextureCoords;
arrayCoords2dtextureCoords[nTextureCoords];
}
Where:
nTextureCoords-Numberoftexturecoordinates.
arrayCoords2dtextureCoords[nTextureCoords]-Arrayof2Dtexturecoordinates.
templateCoords2d
{
floatu;
floatv;
}
u-ucoordinatevalue.
v-vcoordinatevalue.
在cube_2.x中,定义纹理坐标的代码如下:
//纹理坐标
MeshTextureCoords{
8;//定义8对纹理坐标
0.000000;1.000000;
1.000000;1.000000;
0.000000;1.000000;
1.000000;1.000000;
0.000000;0.000000;
1.000000;0.000000;
0.000000;0.000000;
1.000000;0.000000;;
通过前面的介绍,可以建立起一个网格模型,但这个网格模型是浑然一体的,而
在现实生活中,为了能独立对一个物体的不同部分进行变换,必须将模型分割成不同的模块,在.X文件中使用框架(frame)对一个网格模型的不同部分进行组织和管理。
框架仅仅是一个外壳,在框架中通常需要包含具体的网格和一个框架变换矩阵,其中框架变换矩阵用于指定该框架包含的部分在整个模型中的初始位置。
模板Frame和FrameTransformMatrix的定义如下:
templateFrame
{
<3D82AB46-62DA-11CF-AB39-0020AF71E433>
[…]
}
templateFrameTransformMatrix
{
Matrix4x4frameMatrix;
}
templateMatrix4x4
{
arrayfloatmatrix[16];
}
框架可以嵌套,即一个框架可以由许多子框架构成。
例如为了模拟一个人的网格
模型,整体可以由头部、胸部、左臂、右臂、左腿、右腿等框架组成,而左、右臂又可以由上臂、下臂和手三个框架组成,而手又可以由五指和手掌6个框架组成,甚至每个手指还可以继续细分。
我们在cube2.x的基础上添加框架构成cube3.x:
为了使一个.X文件产生动画,必须至少提供一个动画集,每个动画集都应具有一个对某个框架的引用。
模板AnimationSet用来定义动画集:
templateAnimationSet
{
<3D82AB50-62DA-11cf-AB39-0020AF71E433>
[Animation<3D82AB4F-62DA-11cf-AB39-0020AF71E433>]
}
模板Animation的定义如下:
templateAnimation
{
<3D82AB4F-62DA-11cf-AB39-0020AF71E433>
[…]
}
很显然,Animation是一个完全开放的模板,一般情况下,用模板AnimationKey来填充它,模板AnimationKey的定义如下:
templateAnimationKey
{
<10DD46A8-775B-11CF-8F52-0040333594A3>
DWORDkeyType;
DWORDnKeys;
arrayTimedFloatKeyskeys[nKeys];
}
Where:
keyType-Specifieswhetherthekeysarerotation,scale,position,
ormatrixkeys(usingtheintegers0,1,2,or4,respectively).
nKeys-Numberofkeys.
keys-Anarrayofkeys.
在模板AnimationKey中,keyType可取的值是0、1、2、4。
0表示旋转键,在.x文件中,用一个四元数来实现模型的旋转,旋转值使用4个分量w、x、y、z来存储,也就是说,此时变换数组的大小是4,它依次存储四元数的4个分量。
1表示缩放键,可以使用这种类型的关键帧实现模型的缩放,此时变换数组的大小是3,它们分别对应x、y、z轴的缩放值。
2表示平移键,使用3个分量实现模型的平移,此时变换数组的大小是3,它们
分别对应沿x、y、z轴的平移值。
4表示变换矩阵键,此时关键帧的变换数组使用16个浮点数来实现该模型的各
种变换。
因为矩阵可以实现模型的平移、旋转、缩放以及它们的组合变换。
模板AnimationKey用来定义一组动画关键帧,而模板TimeFloatKeys用来定
义每个动画关键帧:
templateTimedFloatKeys
{
DWORDtime;
FloatKeystfkeys;
}
Where:
tfkeys-SeeFloatKeys.
templateFloatKeys
{
<10DD46A9-775B-11cf-8F52-0040333594A3>
DWORDnValues;
arrayfloatvalues]nValues];
nValues-Numberoffloats.
values]nValues]-Arrayoffloatvalues.
我们在cube_3.x的基础上添加动画部分形成cube_4.x,该动画集定义了立方体绕y轴旋转,增加部分如下:
//动画集
//动画
//引用上面的立方体框架,表示下面
AnimationSetAnimationSet0{
AnimationAnimation0{
{CubeFrame}
AnimationKey{
0;
//Rotationkeys
//动画键沿丫轴旋转网格
//9keys
的动画是针对立方体框架的
10;
1000;4;0.000000,0.15643448,0.000000,0.98768836;;,2000;4;0.000000,0.30901700,0.000000,0.95105654;;,3000;4;0.000000,0.45399046,0.000000,0.89100653;;,4000;4;0.000000,0.58778530,0.000000,0.80901694;;,
5000;4;0.000000,0.70710671,0.000000,0.70710683;;,
6000;4;0.000000,0.80901694,0.000000,
7000;4;0.000000,0.89100653,
8000;4;0.000000,0.95105654,
9000;4;0.000000,0.98768836,
10000;4;0.000000,1.0000000,}
}
}
这里一共定义了10个关键帧,在第10个关键帧时回到初始位置开始新一轮的
动画。
{CubeFrame}表示该动画集是对框架CubeFrame进行的操作。
我们在cube_3.x的基础上添加缩放动画形成cube_5.x:
AnimationSetAnimationSet0{//动画集
AnimationAnimation0{//动画
{CubeFrame}//Usetheframecontainingthecube.
1000;3;
2000;3;
3000;3;
4000;3;
5000;3;
6000;3;
7000;3;
8000;3;
9000;3;
}
}
}
1.000000,1.000000,1.000000;;,
0.800000,0.800000,0.800000;;,
0.600000,0.600000,0.600000;;,
0.400000,0.400000,0.400000;;,
0.200000,0.200000,0.200000;;,
0.400000,0.4000