MD2 文件是3D关键帧动画文件。
任何文件都有自己的格式,MD2也有自己组织数据的格式,要想正确读取MD2中大量的数据,首先弄清楚文件的格式,MD2文件分为两部分,①文件头 ②文件数据。
文件头部分描述了MD2 文件的基本信息
文件数据部分才是真正数据源。
格式如下(做了部分注释):
文件头:
Offset Data type Name Description
0 int ident Magic number. Must be equal to “IDP2”
4 int version MD2 version. Must be equal to 8 版本号
8 int skinwidth Width of the texture 纹理宽度
12 int skinheight Height of the texture纹理高度
16 int framesize Size of one frame in bytes每帧大小
20 int num_skins Number of textures 纹理图片数量
24 int num_xyz Number of vertices 顶点数量
28 int num_st Number of texture coordinates 纹理坐标数量
32 int num_tris Number of triangles 三角面片数量
36 int num_glcmds Number of OpenGL commands
40 int num_frames Total number of frames 关键帧数量
44 int ofs_skins Offset to skin names (each skin name is an unsigned char[64] and are null terminated)
48 int ofs_st Offset to s-t texture coordinates纹理坐标偏移量
52 int ofs_tris Offset to triangles三角面片偏移量
56 int ofs_frames Offset to frame data帧偏移量
60 int ofs_glcmds Offset to OpenGL commands
64 int ofs_end Offset to end of file
这里偏移量是指改纹理坐标、关键帧等数据是从哪里开始的,相对于文件头。
文件数据部分:
其实就是按照文件头的结构来组织数据,里面存放了纹理、纹理坐标、顶点坐标、帧顶点索引、三角面片的顶点索引等等。
MD2Model* MD2Model::load(const char* filename) {
ifstream input;
input.open(filename, istream::binary);
char buffer[64];
input.read(buffer, 4); //Should be "IPD2", if this is an MD2 file
if (buffer[0] != 'I' || buffer[1] != 'D' ||
buffer[2] != 'P' || buffer[3] != '2') {
return NULL;
}
if (readInt(input) != 8) { //版本号The version number
return NULL;
}
int textureWidth = readInt(input); //纹理宽度The width of the textures
int textureHeight = readInt(input); //纹理高度The height of the textures
readInt(input); //每帧字节数The number of bytes per frame
int numTextures = readInt(input); //纹理数The number of textures
if (numTextures != 1) {
return NULL;
}
int numVertices = readInt(input); //顶点数The number of vertices
int numTexCoords = readInt(input); //纹理坐标数The number of texture coordinates
int numTriangles = readInt(input); //三角面片数The number of triangles
readInt(input); //GL命令数The number of OpenGL commands
int numFrames = readInt(input); //关键帧数The number of frames
//Offsets (number of bytes after the beginning of the file to the beginning
//of where certain data appear)
int textureOffset = readInt(input); //纹理(即bmp图片名字)偏移量The offset to the textures
int texCoordOffset = readInt(input); //纹理坐标偏移量The offset to the texture coordinates
int triangleOffset = readInt(input); //三角面片偏移量The offset to the triangles
int frameOffset = readInt(input); //关键帧偏移量The offset to the frames
readInt(input); //The offset to the OpenGL commands
readInt(input); //The offset to the end of the file
//Load the texture
input.seekg(textureOffset, ios_base::beg);
input.read(buffer, 64);
//纹理图片是否是bmp格式
if (strlen(buffer) < 5 ||
strcmp(buffer + strlen(buffer) - 4, ".bmp") != 0) {
return NULL;
}
Image* image = loadBMP(buffer);
GLuint textureId = loadTexture(image);
delete image;
MD2Model* model = new MD2Model();
model->textureId = textureId;
//Load the texture coordinates
input.seekg(texCoordOffset, ios_base::beg);
model->texCoords = new MD2TexCoord[numTexCoords];
for(int i = 0; i < numTexCoords; i++) {
MD2TexCoord* texCoord = model->texCoords + i;
texCoord->texCoordX = (float)readShort(input) / textureWidth;
texCoord->texCoordY = 1 - (float)readShort(input) / textureHeight;// opengl coordinate different from texture coordinate
}
//Load the triangles
input.seekg(triangleOffset, ios_base::beg);
model->triangles = new MD2Triangle[numTriangles];
model->numTriangles = numTriangles;
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = model->triangles + i;
for(int j = 0; j < 3; j++) {
triangle->vertices[j] = readUShort(input);
}
for(int j = 0; j < 3; j++) {
triangle->texCoords[j] = readUShort(input);
}
}
//Load the frames
input.seekg(frameOffset, ios_base::beg);
model->frames = new MD2Frame[numFrames];
model->numFrames = numFrames;
for(int i = 0; i < numFrames; i++) {
MD2Frame* frame = model->frames + i;
frame->vertices = new MD2Vertex[numVertices];
Vec3f scale = readVec3f(input);
Vec3f translation = readVec3f(input);
input.read(frame->name, 16);
for(int j = 0; j < numVertices; j++) {
MD2Vertex* vertex = frame->vertices + j;
input.read(buffer, 3);
Vec3f v((unsigned char)buffer[0],
(unsigned char)buffer[1],
(unsigned char)buffer[2]);
vertex->pos = translation + Vec3f(scale[0] * v[0],
scale[1] * v[1],
scale[2] * v[2]);
input.read(buffer, 1);
int normalIndex = (int)((unsigned char)buffer[0]);
vertex->normal = Vec3f(NORMALS[3 * normalIndex],
NORMALS[3 * normalIndex + 1],
NORMALS[3 * normalIndex + 2]);
}
}
model->startFrame = 0;
model->endFrame = numFrames - 1;
return model;
}