Archived

This topic is now archived and is closed to further replies.

xg0blin

MD2 Model Frame Stuff

Recommended Posts

The MD2 Model seems fairly straightforward and simple, up until one point. What is stored after you get to the frame offset? Reading the code supplied from gametutorials.com, it seems like the data there is in the format
struct AliasVertex
{
unsigned char vertex[3];
unsigned char NormalIndex;
};

struct AliasFrame
{
float scale[3];
float translate[3];
char name[16]
AliasVertex *Vertices; 
// It appears to me that this number will be assigned *Vertices = (unsigned char *)malloc(sizeof(unsigned char) * NumVertices))

};
This would make sense to me, as the code given by gametutorials has a loop that looks like this:
	for (int j=0; j < m_Header.numVertices; j++)
		{
			pVertices[j].vertex[0] = pFrame->aliasVertices[j].vertex[0] * pFrame->scale[0] + pFrame->translate[0];
			pVertices[j].vertex[2] = -1 * (pFrame->aliasVertices[j].vertex[1] * pFrame->scale[1] + pFrame->translate[1]);
			pVertices[j].vertex[1] = pFrame->aliasVertices[j].vertex[2] * pFrame->scale[2] + pFrame->translate[2];
		}
Where pFrame is a pointer to an unsigned char buffer, that is casted to an AliasTriangle. Anyways, I mean that scale, transform are never incremented, so it looks like there is just one, with a whole list of vertices. Also, it looks like the name is only specified once because they have
strcpy(m_pFrames[i].strName, pFrame->name);		
If it were specified as I stated above, this might make sense to me, but it''s not. Here is what they do. They have these struct:
struct AliasTriangle
{
unsigned char vertex[3];
unsigned char NormalIndex;
};

struct AliasFrame
{
float scale[3];
float translate[3];
char name[16]
AliasTriangle Vertices[1]; 
// It appears to me that this number will be assigned *Vertices = (unsigned char *)malloc(sizeof(unsigned char) * NumVertices)) so why in the world is there only one space allocated, and why in an unsigned char array, parsed as an AliasFrame *, is there only one of everything but this?

};

What in the hell? How is their loop possible then? Only one vertex is ever allocated. Why aren''t there a series of AliasFrames then, and only one name, one scale, and one translate value, (i.e. no pframe.scale[0]. They just put pframe.scale[0]) (also no pframe[i].scale[0] or pframe[i].translate[0]. It is just pframe.scale[0] and pframe.translate[0]). I''m so lost as to how this little piece of code is working. Anyways, if you know how the data is formatted at this section of the file, please enlighten me, as I''d love to know. If you want to look at the code itself, it is the md2 animation code at www.gametutorials.com.

Share this post


Link to post
Share on other sites
I don''t really advise going to gametutorials, you should rather use the MD2 tutorial from nehe.

Anyways...

The reason why there is a scale and an offset for each frame is because the positions of each vertex are stored in 8 bit integers (to save memory on the hard drive and in the ram). Now, they have put a frame and an offset for each animation frame so that it could be tweaked as finely as possible... If you want this said otherwise: Vertex based animation is primitive, and Quake 2 is full of such "hacks".

The reason this is put this way:

AliasTriangle Vertices[1];

Is because in the file, there is a variable size array of vertices. The number vertices is the same for each frame, but its different for each model.

To work with MD2 in a "clean" way, you need to create your own structures and store it in your own way. You might want to precompute the position of the vertices in floating point vectors and store that in a large vertex buffers directly, getting rid of the scale/offset math in the render loop.



Looking for a serious game project?
www.xgameproject.com

Share this post


Link to post
Share on other sites
quote:
Original post by Max_Payne
The reason why there is a scale and an offset for each frame is because the positions of each vertex are stored in 8 bit integers (to save memory on the hard drive and in the ram). Now, they have put a frame and an offset for each animation frame so that it could be tweaked as finely as possible... If you want this said otherwise: Vertex based animation is primitive, and Quake 2 is full of such "hacks".



I get this part (so far anyways)


quote:
Original post by Max_Payne
AliasTriangle Vertices[1];

Is because in the file, there is a variable size array of vertices. The number vertices is the same for each frame, but its different for each model.

To work with MD2 in a "clean" way, you need to create your own structures and store it in your own way. You might want to precompute the position of the vertices in floating point vectors and store that in a large vertex buffers directly, getting rid of the scale/offset math in the render loop.



This is the part that''s bothering me. It only has memory for one. So does the data have this kind of format


Frame 1:
AliasFrame[1]
{
scale[0] scale[1] scale[2]
tran[0] tran[1] tran[2]
name[0 - 16]
AliasTriangle
vertex[0] vertex[1] vertex[2]
lightindex
}
AliasFrame[2]
{

scale[0] scale[1] scale[2]
tran[0] tran[1] tran[2]
name[0 - 16]
AliasTriangle
vertex[0] vertex[1] vertex[2]
lightindex
}
... up to AliasFrame[n]



That would make things possible like Frame[12].AliasFrame[11].scale[0]

That doesn''t seem like what is there. The reason I''m confused is because it seems the data would be like this in the file


frame 1:
scale[0] scale[1] scale[2]
tran[0] tran[1] tran[2]
name[0 - 16]
AliasTriangle[0]
vertex[0] vertex[1] vertex[2]
lightindex
AliasTriangle[1]
vertex[0] vertex[1] vertex[2]
lightindex
AliasTriangle[2]
vertex[0] vertex[1] vertex[2]
lightindex
...
up to AliasTriangle[n]


That''s what it seems like it is doing (judging by the code) to me, but then how does that work if you only have AliasTraingle[1] allocated?

Share this post


Link to post
Share on other sites
quote:
Original post by Max_Payne
The reason this is put this way:

AliasTriangle Vertices[1];

Is because in the file, there is a variable size array of vertices.


Ok, I just reread your post and pinpointed what I''m trying to say is my problem. There is only one set of scale, translate and name values, but a variable number of vertices. How then does it work to have Vertices[1], when there are plainly more?

And then when you''re going about the indicies, you have

unsigned char BUFFER[A BIG VALUE];

AliasFrame *pFrame = (AliasFrame *)buffer

AliasFrame is a 44 byte structure, so everytime you moved the index, the compiler would move the pointer 44 bytes.

So, could this exist: pFrame[24].scale[3]? I see nowhere in the code where they have pFrame followed by an index. They only have the vertices part with the variable number of indicies. I don''t understand why this works.

Share this post


Link to post
Share on other sites
MD2 Model Header file


#ifndef MD2_H
#define MD2_H

#define MD2_IDENTITY ((''2''<<24) + (''P''<<16) + (''D''<<8) + ''I'') // MD2 "magic" number - 844121161

#define MD2_VERSION 8 // MD2 version number - 8


/* MD2 Header */
struct MD2Header {
int identity; // magic number

int version; // md2 version number


int texWidth; // texture width

int texHeight; // texture height


int frameSize; // frame size in bytes


int numTex; // number of textures

int numVert; // number of vertices

int numTexCoord; // number of texture coordinates

int numTri; // number of triangles

int numCmd; // number of openGL commands

int numFrame; // number of frames


int ofsName; // offset to skin names

int ofsTexCoord; // offset to texture coordinates

int ofsTri; // offset to triangles

int ofsFrame; // offset to frame data

int ofsCmd; // offset to OpenGL commands

int ofsEnd; // offset to EOF

};

/* MD2 Frame Header */
struct MD2FrameHeader {
Vector3 scale;
Vector3 translate;
char name[16];
};

/* MD2 Vertex */
struct MD2Vertex {
byte x;
byte y;
byte z;
byte normalIndex;
};

/* MD2 Triangle */
struct MD2Triangle {
unsigned short vertIndex[3];
unsigned short texIndex[3];
};

/* MD2 Texture Coordinate */
struct MD2TexCoord {
unsigned short u;
unsigned short v;
};

#endif



MD2 Model Loader


void ModelLoader::LoadMD2(FILE* file, Model* model) {
// allocate memory for header

MD2Header* md2Header = new MD2Header();

// load MD2 File

LoadMD2Header(file, md2Header, model);
LoadMD2Data(file, md2Header, model);

// clean up

delete md2Header;
}

/////////////

// PRIVATE //

/////////////



/* LoadMD2Header() */
void ModelLoader::LoadMD2Header(FILE* file, MD2Header* md2Header, Model* model) {
// read header data

if(fread(md2Header, sizeof(MD2Header), 1, file) < 1)
return;

// validate header

if((md2Header->identity != MD2_IDENTITY) && (md2Header->version != MD2_VERSION))
return;

// copy header data into model

model->numFrame = md2Header->numFrame;
model->numVert = md2Header->numVert;
model->numTri = md2Header->numTri;
}
/* Load MD2Data() */
void ModelLoader::LoadMD2Data(FILE* file, MD2Header* md2Header, Model* model) {
// load vertex data into model

LoadMD2VertexData(file, md2Header, model);

// load triangle and texture indices into model

LoadMD2TriangleAndTextureData(file, md2Header, model);

// load texture coordinates

LoadMD2TextureCoordinates(file, md2Header, model);
}
/* Load MD2VertexData() */
void ModelLoader::LoadMD2VertexData(FILE* file, MD2Header* md2Header, Model* model) {
// frame and vertex info

int numVert = md2Header->numVert;
int numFrame = md2Header->numFrame;
int frameSize = md2Header->frameSize;
int frameOffset = md2Header->ofsFrame;

// allocate memory in model for frames

model->frame = new Frame[numFrame];

// store vertex data in model, one frame at at time

for(int frameIndex = 0; frameIndex < numFrame; frameIndex++) {
// allocate memory for header

MD2FrameHeader* frameHeader = new MD2FrameHeader();

// read in frame header

int seekOffset = frameOffset + (frameIndex*frameSize) + sizeof(MD2FrameHeader);
fseek(file, frameOffset + (frameIndex * frameSize), SEEK_SET);
fread(frameHeader, sizeof(MD2FrameHeader), 1, file);

cout << frameHeader->name << endl;

// allocate memory for md2 frame vertex data

MD2Vertex* frameVertexData = new MD2Vertex[numVert];

// read in frame vertex data

fseek(file, seekOffset, SEEK_SET);
fread(frameVertexData, sizeof(MD2Vertex), numVert, file);

// allocate memory for model vertices of this frame

model->frame[frameIndex].vert = new Vertex[numVert];

// decompress MD2 vertex data and store it in model

for(int i = 0; i < numVert; i++) {
// setup pointers to vertex data in md2 and model

Vertex* modelVertexPtr = &model->frame[frameIndex].vert[i];
MD2Vertex* md2VertexPtr = &frameVertexData[i];

// decompress and store in model

modelVertexPtr->x = (md2VertexPtr->x * frameHeader->scale.x) + frameHeader->translate.x;
modelVertexPtr->z = (md2VertexPtr->y * frameHeader->scale.y) + frameHeader->translate.y;
modelVertexPtr->y = (md2VertexPtr->z * frameHeader->scale.z) + frameHeader->translate.z;
modelVertexPtr->norm = md2VertexPtr->normalIndex;
}

// clean up

delete frameHeader;
delete [] frameVertexData;
}
}
/* LoadMD2Triangle() */
void ModelLoader::LoadMD2TriangleAndTextureData(FILE* file, MD2Header* md2Header, Model* model) {
// allocate memory for md2 triangles

MD2Triangle* md2Tri = new MD2Triangle[md2Header->numTri];

// read in Triangle data

fseek(file, md2Header->ofsTri, SEEK_SET);
fread(md2Tri, sizeof(MD2Triangle), md2Header->numTri, file);

// allocate memory in model for triangle and texture indices

model->tri = new Triangle[md2Header->numTri];
model->tex = new Triangle[md2Header->numTri];

// copy triangle data into model

for(int index = 0; index < md2Header->numTri; index++) {
model->tri[index].i = md2Tri[index].vertIndex[0];
model->tri[index].j = md2Tri[index].vertIndex[1];
model->tri[index].k = md2Tri[index].vertIndex[2];

model->tex[index].i = md2Tri[index].texIndex[0];
model->tex[index].j = md2Tri[index].texIndex[1];
model->tex[index].k = md2Tri[index].texIndex[2];
}

// clean up

delete [] md2Tri;
}
/* LoadMD2TextureCoordinates() */
void ModelLoader::LoadMD2TextureCoordinates(FILE* file, MD2Header* md2Header, Model* model) {
// allocate memory for md2 texture coordinates

MD2TexCoord* md2TexCoord = new MD2TexCoord[md2Header->numTexCoord];

// read in texture coordinates

fseek(file, md2Header->ofsTexCoord, SEEK_SET);
fread(md2TexCoord, sizeof(MD2TexCoord), md2Header->numTexCoord, file);

// allocate memory in model for texture coordinates

model->texCoord = new TextureCoordinate[md2Header->numTexCoord];

// convert and copy texture coordinates to model

for(int index = 0; index < md2Header->numTexCoord; index++) {
model->texCoord[index].u = (float)md2TexCoord[index].u / (float)md2Header->texWidth;
model->texCoord[index].v = (float)md2TexCoord[index].v / (float)md2Header->texHeight;
}

// clean up

delete [] md2TexCoord;
}




enjoy

Share this post


Link to post
Share on other sites