MD2文件格式分析及显示

本文介绍MD2文件的格式,并介绍使用OpenGL显示MD2文件的方法。
首先,我们必须要搞清几个问题:
1、动画的实现原理
2、MD2文件的数据存储格式
3、OpenGL显示动画的方法
一、动画的原理
动画就是连续出现的画面,在3D动画中,在一个在两个差别很大的动作之间进行插值,使得3D模型的各个部分连续运动而得到动画的效果。比如:将手臂在左边时的3D模型和手臂在右边时的3D模型进行保留,然后根据时间在这两个模型之间进行插值,让其在某个时刻显示其在中间的模型,如此连续的显示便构成了动画的效果。
因此,MD2文件中便存储了动画的各个关键帧,只不过可能某些动作的完成需要多个关键帧,另外,我们了解了动画的原理,我们便知道,在动画的运动过程中,模型的顶点个数和纹理是相同的,只是在某个时刻模型的顶点坐标有差异。
二、MD2文件数据的格式
要搞清楚MD2文件的格式必须要知道其中都存储了那些数据,MD2动画由两个文件组成,一个是以.MD2为后缀的文件,其中保留了动画模型的各个点的信息,包括:顶点坐标、纹理坐标、纹理名称、三角形索引等信息。另一个是一个图片文件,可以是多种格式的图片,本文中使用的是BMP文件。
1、文件头
要搞清楚MD2文件中各种数据的大小和存储位置就必须要先分析文件头,我们使用下面的结构体来描述文件头:
Code
下面对各个变量进行解释:
magic:是表明该文件是MD2文件的标志,它必须等于"IPD2",不然就不是一个MD2文件。
version:表明该文件的版本,本文中,它的值为8。
skinWidth:纹理的宽度,我们用这个参数来对纹理坐标进行解压。当然,因为纹理是与MD2文件分离的,你也可以到文件中去获取。
skinHeight:纹理的高度,它的用途同上。
frameSize:表明每个关键帧的大小,它决定了我们每次读取关键帧时的数据读取量。
numSkins:表明纹理的个数,本文中只有一个纹理。
numVertices:每帧中顶点的个数,我们用这个参数决定读取顶点信息时的数据读取量。
numTexCoords:纹理坐标的个数,我们用这个参数决定读取纹理坐标时的数据读取量。
numTriangles:三角形个数,在动画模型中,使用三角形索引来绘制一个面。
numGlCommands:OpenGL命令的条数,本文中未使用这个参数。
numFrames:总帧数,它决定了我们需要读取的帧信息量。
offsetSkins:纹理名称在文件中的偏移量,读取纹理名称从它指定的地方开始。
offsetTexCoords:纹理坐标在文件中的偏移量,读取纹理坐标从它指定的地方开始。
offsetTriangles:面顶点索引在文件中的偏移量,读取面顶点索引从它指定的地方开始。
offsetFrames:第一帧的位置,读取帧信息时从它指定的地方开始。
offsetGlCommands:OpenGL命令在文件中的偏移量,文中未使用这个参数。
offsetEnd:文件结束的位置,这个参数可以用来检查该文件的完整性。
2、顶点结构
MD2文件中的顶点是经过压缩的,它包含的这样的一个结构:
Code
每一帧都是由帧大小(frameSize)个顶点组成,因此,每个帧占用的空间为:sizeof(tMd2AliasFrame)*frameSize。
2、纹理名称
MD2文件中纹理名称是长度为64的字符序列,我们这样表示:
/* * 纹理名字  */
typedef 
char  tMd2Skin[ 64 ];
3、纹理坐标
MD2文件中的纹理坐标也是经过压缩的,它的结构如下:
/* * 纹理坐标结构  */
struct  tMd2TexCoord
{
   
short  u, v;
};
在读取纹理坐标后需要对其进行解压,公式为:U = u / skinWidth; V = v / skinHeight。
4、面结构
我们说过了,MD2文件中的使用面结构组成一个三角形,面结构保存了该三角形的三个顶点在帧顶点中的索引,和三个顶点所对应的纹理坐标在纹理坐标序列中的索引。
/* * 面结构  */
struct  tMd2Face
{
   
short  vertexIndices[ 3 ]; // 顶点索引
    short  textureIndices[ 3 ]; // 纹理索引
};
三、辅助结构
因为MD2文件数据本身是压缩过的,因此为了得到真正能有的信息,我们必须要定义一些辅助结构来存储转换后的数据。
1、顶点结构
顶点结构用来存储解压后的顶点信息。
/* * 解压后的顶点结构  */   
struct  tMd2Triangle
{
   
float  vertex[ 3 ]; // 顶点坐标
    float  normal[ 3 ]; // 法向量
};
2、面结构
面结构用来存储每个三角形面的三个点的顶点坐标索引和纹理坐标索引。
/* * 面信息  */
struct  tFace
{
    
int  vertIndex[ 3 ];             /* *< 顶点索引  */
    
int  coordIndex[ 3 ];             /* *< 纹理坐标索引  */
};
3、关键帧结构
关键帧结构用来存储关键帧的名称和它包含的所有的顶点信息。
/* * 关键帧结构  */
struct  tMd2Frame
{
   
char  strName[ 16 ]; // 关键帧名称
   tMd2Triangle  * pVertices; // 帧中顶点信息
};
4、动作信息结构
动作信息结构用来存放该动作的名称和该动作包含的起始关键帧索引和结束关键帧索引。
/* * 动作信息结构体  */
struct  tAnimationInfo
{
    
char  strName[ 255 ];             /* *< 帧的名称  */
    
int  startFrame;                 /* *< 开始帧  */
    
int  endFrame;                 /* *< 结束帧  */
};
5、关键帧结构
关键帧结构用来存储当前帧中顶点、面、纹理坐标信息。
Code
6、模型信息结构
模型信息结构用来存放动画的全部信息,包括:关键帧链表,材质链表和动作信息链表等。
Code
四、实现过程
我们构建好了用于存储数据的结构,下面介绍实现动画的过程,我们将整个过程分为三个部分:读取原始数据,将数据转换成模型结构和动画显示。
1、数据读取
Code
2、数据结构转换
未完待续...

你可能感兴趣的:(文件)