PDA

View Full Version : Trouble loading .md2 files



Niven07
08-03-2013, 02:59 PM
I have a class I created for loading .md2 files which works almost perfectly. The one problem that I can't seem to fix is that the displayed model has its texture messed up. It's as if I'm using the wrong texture indices or just plain storing the wrong texture coordinates. Any help with this problem would be appreciated.

My Structs:


struct MD2_Header
{
char ident[4]; ///identifer - IDP2
int version; ///version number - 8
int skinwidth; ///width of texture
int skinheight; ///height of texture
int framesize; ///size of a frame in bytes
int num_skins; ///number of textures
int num_xyz; ///number of vertices
int num_st; ///number of texture coordinates
int num_tris; ///number of triangles
int num_glcmds; ///number of opengl commands
int num_frames; ///number of frames
int ofs_skins; ///offset to texture names
int ofs_st; ///offset to texture coordinates
int ofs_tris; ///offset to triangles
int ofs_frames; ///offset to frame data
int ofs_glcmds; ///offset to gl commands
int ofs_end; ///offset to end of file
};

struct MD2_TextureCoord
{
short s;
short t;
float gl_X;
float gl_Y;
};

struct MD2_Triangle
{
short VertexIndex[3];
short TextureIndex[3];
};

struct MD2_Vertex
{
vec3 Pos;
vec3 Norm;
};

struct MD2_Frame
{
char Name[16];
vector<MD2_Vertex> Vertices;
};

struct MD2_glCommand
{
float s, t;
int VertexIndex;
};

struct MD2_CommandGroup
{
int Size; /// positive - triangle strip/negative - triangle fan
vector<MD2_glCommand> Commands;
};


My loading and displaying functions:


void MD2_Model::Load(string File, int Which)
{
cout<<"Loading .md2 file..."<<endl;
WritetoLog("Loading Model: ",false);
WritetoLog(File, false);
WritetoLog("...");

ifstream Model(File.c_str(), istream::binary);
char Buffer[64];

///Check to see if it's a valid MD2 file
Model.read( Header.ident, 4 );
if ( Header.ident[0] != 'I' || Header.ident[1] != 'D' || Header.ident[2] != 'P' || Header.ident[3] != '2' )
{
cout<<"Not a valid MD2 file";
WritetoLog("Attempted to load an invalid MD2 file: ", false);
WritetoLog(File, false);
WritetoLog(" .Wrong Identifer.");
exit(0xBAD);
}
Header.version = ReadInt(Model);
if ( Header.version != 8 )
{
cout<<"Not a valid MD2 file";
WritetoLog("Attempted to load an invalid MD2 file: ", false);
WritetoLog(File, false);
WritetoLog(" .Wrong Version Number.");
exit(0xBAD);
}
///Get the header information
Header.skinwidth = ReadInt(Model);
Header.skinheight = ReadInt(Model);
Header.framesize = ReadInt(Model);
Header.num_skins = ReadInt(Model);
Header.num_xyz = ReadInt(Model);
Header.num_st = ReadInt(Model);
Header.num_tris = ReadInt(Model);
Header.num_glcmds = ReadInt(Model);
Header.num_frames = ReadInt(Model);
Header.ofs_skins = ReadInt(Model);
Header.ofs_st = ReadInt(Model);
Header.ofs_tris = ReadInt(Model);
Header.ofs_frames = ReadInt(Model);
Header.ofs_glcmds = ReadInt(Model);
Header.ofs_end = ReadInt(Model);

if ( Header.num_skins != 1 )
{
cout<<"Invalid MD2 file";
WritetoLog("Attempted to load MD2 value with incorrect amount of textures: ", false);
WritetoLog(File);
exit(0xBAD);
}

///Load Texture
Model.seekg( Header.ofs_skins, ios_base::beg );
Model.read( Buffer, 64 );
int Length = strlen(Buffer);
wchar_t tempBuffer[Length];
for ( int k = 0; k < Length; k++ )
tempBuffer[k] = (wchar_t)Buffer[k];
/// I only want to load .png files as the texture
tempBuffer[ Length - 3 ] = L'p';
tempBuffer[ Length - 2 ] = L'n';
tempBuffer[ Length - 1 ] = L'g';
Skin.LoadTexture( tempBuffer, Which );

///Load Texture Coordinates
Model.seekg( Header.ofs_st, ios_base::beg );
for ( int i = 0; i < Header.num_st; i++ )
{
MD2_TextureCoord Temp;
Temp.s = ReadShort(Model);
Temp.t = ReadShort(Model);
Temp.gl_X = (float)Temp.s/Header.skinwidth;
Temp.gl_Y = (float)Temp.t/Header.skinheight;

TexCoords.push_back( Temp );
}

///Load Triangles
Model.seekg( Header.ofs_tris, ios_base::beg );
for ( int i = 0; i < Header.num_tris; i++ )
{
MD2_Triangle Temp;
for ( int j = 0; j < 3; j++ )
Temp.VertexIndex[j] = ReadShort(Model);
for ( int j = 0; j < 3; j++ )
Temp.TextureIndex[j] = ReadShort(Model);

Triangles.push_back(Temp);
}

///Load Frame Data
Model.seekg( Header.ofs_frames, ios_base::beg );
for ( int i = 0; i < Header.num_frames; i++ )
{
MD2_Frame Temp;

vec3 Scale = ReadVec3(Model);
vec3 Translate = ReadVec3(Model);
Model.read( Temp.Name, 16 );

for ( int j = 0; j < Header.num_xyz; j++ )
{
MD2_Vertex Temp2;

Model.read( Buffer, 3 );
Temp2.Pos = vec3( (unsigned char)Buffer[0], (unsigned char)Buffer[1], (unsigned char)Buffer[2] );
Temp2.Pos = Scale*Temp2.Pos + Translate;

Model.read( Buffer, 1 );
int Index = (int)((unsigned char)Buffer[0]);
Temp2.Norm = vec3( MD2_Normals[3*Index], MD2_Normals[3*Index+1], MD2_Normals[3*Index+2] );

Temp.Vertices.push_back( Temp2 );
}

Frames.push_back(Temp);
}

///Load OpenGL Commands
Model.seekg( Header.ofs_glcmds, ios_base::beg );
for ( int num = ReadInt(Model); num != 0; num = ReadInt(Model) )
{
MD2_CommandGroup Temp;
Temp.Size = num;
for ( int i = 0; i < fabs(Temp.Size); i++ )
{
MD2_glCommand Temp2;

Temp2.s = ReadFloat(Model);
Temp2.t = ReadFloat(Model);
Temp2.VertexIndex = ReadInt(Model);

Temp.Commands.push_back(Temp2);
}
Groups.push_back(Temp);
}

StartFrame = 0;
EndFrame = Header.num_frames - 1;
}

void MD2_Model::Draw( int Attributes[], bool Which_Way )
{
///Get Frame indicies
int FrameIndex1 = StartFrame, FrameIndex2 = StartFrame;
int AnimationLength = EndFrame - StartFrame + 1;

vector<float> FramePos;
for ( int i = 0; i < AnimationLength; i++ )
FramePos.push_back( i * 1.f/AnimationLength );

for ( int i = 1; i < FramePos.size(); i++ )
if ( Time >= FramePos[i-1] and Time <= FramePos[i] )
{
FrameIndex1 = i - 1 + StartFrame;
FrameIndex2 = i + StartFrame;
}
if ( FrameIndex1 == FrameIndex2 )
FrameIndex1 = EndFrame;

MD2_Frame Frame1 = Frames[FrameIndex1], Frame2 = Frames[FrameIndex2];

///Get Fraction between the two frames
float Fraction = 0;
if ( AnimationLength > 0 )
Fraction = float(Time - FramePos[FrameIndex1 - StartFrame] )*AnimationLength;

///Draw
Skin.Bind();

/// Usual Way
if ( Which_Way )
{
glBegin(GL_TRIANGLES);
for ( int i = 0; i < Header.num_tris; i++ )
{
MD2_Triangle Triangle = Triangles[i];
for ( int j = 0; j < 3; j++ )
{
MD2_Vertex V1 = Frame1.Vertices[ Triangle.VertexIndex[j] ];
MD2_Vertex V2 = Frame2.Vertices[ Triangle.VertexIndex[j] ];

vec3 Pos = V1.Pos * (1.f-Fraction) + V2.Pos * Fraction;
vec3 Norm = V1.Norm * (1.f-Fraction) + V2.Norm * Fraction;

MD2_TextureCoord Coord = TexCoords[ Triangle.TextureIndex[j] ];

glVertexAttrib3f( Attributes[0], Pos[0], Pos[1], Pos[2] ); ///Position
glVertexAttrib4f( Attributes[1], 1, 1, 1, 1 ); ///Color
glVertexAttrib2f( Attributes[2], Coord.gl_X, Coord.gl_Y ); ///TexCoord
glVertexAttrib3f( Attributes[3], Norm[0], Norm[1], Norm[2] ); ///Normal
}
}
glEnd();
}
///Using file's commands
else
{
for ( int i = 0; i < Groups.size(); i++ )
{
MD2_CommandGroup Group = Groups[i];
if ( Group.Size > 0 )
glBegin( GL_TRIANGLE_STRIP );
else
glBegin( GL_TRIANGLE_FAN );

for ( int j = 0; j < fabs(Group.Size); j++ )
{
MD2_glCommand Command = Group.Commands[j];

MD2_Vertex V1 = Frame1.Vertices[ Command.VertexIndex ];
MD2_Vertex V2 = Frame2.Vertices[ Command.VertexIndex ];

vec3 Pos = V1.Pos * (1.f-Fraction) + V2.Pos * Fraction;
vec3 Norm = V1.Norm * (1.f-Fraction) + V2.Norm * Fraction;

glVertexAttrib3f( Attributes[0], Pos[0], Pos[1], Pos[2] ); ///Position
glVertexAttrib4f( Attributes[1], 1, 1, 1, 1 ); ///Color
glVertexAttrib2f( Attributes[2], Command.s, Command.t ); ///TexCoord
glVertexAttrib3f( Attributes[3], Norm[0], Norm[1], Norm[2] ); ///Normal
}
glEnd();
}
}

Skin.UnBind();
}


Any help is appreciated. If there are any functions that I need to post to help you see what's going on in the program, just say so

*the readshort/readint/whatever functions have obvious purposes and I'm certain the problem is not with them which is why I didn't post them
**I've tested to make sure the problem is not with the file I'm loading

cireneikual
08-03-2013, 03:07 PM
Some image loaders load images upside-down. Try flipping the source image.

Niven07
08-03-2013, 03:11 PM
I tried flipping it all possible ways but none of them worked correctly.

Dan Bartlett
08-03-2013, 06:42 PM
You're using glBegin/glEnd + glVertexAttrib{2/3/4}f to issue vertices, this generally isn't the best way to use OpenGL and glBegin/glEnd aren't available in more recent OpenGL versions.

When using glBegin/glEnd, the only calls that actually issue a new vertex are glVertex(...) and glVertexAttrib(0, ...). All the rest, such as glColor/glNormal/glVertexAttrib(N, ...){where N > 0}/etc. only set the current value for that attribute to be used when the vertex is issued.

Depending on what OpenGL version you're using, you should read http://www.opengl.org/sdk/docs/man2/xhtml/glVertexAttrib.xml or http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml

For your code to work, the last attribute would have have to be Position (assuming Position location = 0), otherwise you will be issuing vertices with the current attribute values from the previous iteration of the loop.

Niven07
08-03-2013, 07:08 PM
I changed the ordering of the calls to glVertexAttrib so that the position one is called last and it worked perfectly. Thanks for the advice