Trouble loading .md2 files

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

Some image loaders load images upside-down. Try flipping the source image.

I tried flipping it all possible ways but none of them worked correctly.

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.

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