Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm trying to load MD2 models (stolen from Cube) and my loader seems to be loading the models fine, but I can't say the same for the drawing of the model. Here's my code:

typedef float vec3_t[3];

struct md2_t
{
    int ident;
    int version;

    int skinwidth;
    int skinheigh;
    int framesize;

    int num_skins;
    int num_xyz;
    int num_st;
    int num_tris;
    int num_glcommands;
    int num_frames;

    int ofs_skins;
    int ofs_st;
    int ofs_tris;
    int ofs_frames;
    int ofs_glcommands;
    int ofs_end;
};

struct md2_vertex_t
{
    unsigned char v[3];
    unsigned char index_normal;
};

typedef short md2_textcoord_t[2];

struct md2_frame_t
{
    float scale[3];
    vec3_t translate;
    char name[16];
    md2_vertex_t vertices[1]; // First vertex of this frame.
};


struct md2_triangle_t
{
    short index_xyz[3];
    short index_st[3];
};

struct md2_anim_t
{
    int first_frame, last_frame, fps;
};

struct md2_animstate_t
{
    md2_anim_t* animation;
    float current_time, old_time, interpol;
    int type, current_frame, next_frame;
};

struct md2_glcmd_t
{
    float s;
    float t;
    int index;
};

#define MD2_IDENT (('2'<<24) + ('P'<<16) + ('D'<<8) + 'I')
#define MD2_VERSION 8

class MD2 : public VertexModel
{
    public:
        MD2(const std::string& Path);
        ~MD2();

        void Draw(timestep_t Time);

    private:
        md2_t m_MD2Header;
        md2_animstate_t m_MD2AnimState;
        md2_anim_t* m_MD2Animations;
        vec3_t* m_Vertices;
        vec3_t* m_InterpolatedVertices;
        int* m_Commands;
        int* m_Normals;

        unsigned int m_TextureID;
        float m_Scale;

        void Interpolate(vec3_t* Vertices);
};


#include <helpers/md2.h>
#include <iostream>
#include <fstream>
#include <exception>

MD2::MD2(const std::string& Path) : m_TextureID(0), m_Scale(1.0f)
{
    std::ifstream File(Path.c_str(), std::iostream::in | std::ifstream::binary);
    if(!File.is_open())
    {
        Debugging::Debugf("Unable to open the file: %s", Path.c_str());
        exit(0);
    }

    // Get the filesize.
    File.seekg (0, std::ios::end);
    int Size = File.tellg();
    File.seekg (0, std::ios::beg);

    if(Size < sizeof(md2_t))
    {
        Debugging::Debugf("Incorrect header for the file: %s", Path.c_str());
        exit(0);
    }

    // Get the header.
    memset((void*)&m_MD2Header, 0, sizeof(md2_t));
    File.read((char*)&m_MD2Header, sizeof(md2_t));

    // Check if the file is actually a MD2 model.
    if(m_MD2Header.ident != MD2_IDENT || m_MD2Header.version != MD2_VERSION)
    {
        Debugging::Debugf("The file %s is not a MD2 model.", Path.c_str());
        exit(0);
    }

    // Allocate memory for the data.
    m_Vertices = new vec3_t[m_MD2Header.num_xyz * m_MD2Header.num_frames];
    m_InterpolatedVertices = new vec3_t[m_MD2Header.num_xyz];
    m_Normals = new int[m_MD2Header.num_xyz * m_MD2Header.num_frames];
    m_Commands = new int[m_MD2Header.num_glcommands];
    char* Buffer = new char[m_MD2Header.framesize * m_MD2Header.num_frames];

    // Read the frame data.
    File.seekg(m_MD2Header.ofs_frames, std::ios::beg);
    File.read(Buffer, m_MD2Header.framesize * m_MD2Header.num_frames);

    // Read the command data.
    File.seekg(m_MD2Header.ofs_glcommands, std::ios::beg);
    File.read((char*)m_Commands, m_MD2Header.num_glcommands * sizeof(int));

    // Read all the data.
    for(int i = 0; i < m_MD2Header.num_frames; ++i)
    {
        md2_frame_t* Frame = (md2_frame_t*)&Buffer[m_MD2Header.framesize * i];
        vec3_t* Vertices = &m_Vertices[m_MD2Header.num_xyz * i];
        int* Normals = &m_Normals[m_MD2Header.num_xyz * i];

        for(int vertex = 0; vertex < m_MD2Header.num_xyz; ++vertex)
        {
            Vertices[i][0] = (Frame->vertices[i].v[0] * Frame->scale[0]) + Frame->translate[0];
            Vertices[i][1] = (Frame->vertices[i].v[1] * Frame->scale[1]) + Frame->translate[1];
            Vertices[i][2] = (Frame->vertices[i].v[2] * Frame->scale[2]) + Frame->translate[2];
            Normals[i] = Frame->vertices[i].index_normal;
        }
    }

    // Free unused memory and close the file.
    delete[] Buffer;
    File.close();

    memset((void*)&m_MD2AnimState, 0, sizeof(m_MD2AnimState));
}

MD2::~MD2()
{
    delete[] m_MD2Animations;
    delete[] m_Vertices;
    delete[] m_InterpolatedVertices;
    delete[] m_Commands;
    delete[] m_Normals;
}

void MD2::Draw(timestep_t Time)
{
    glPushMatrix();
        // Transform the model coordinates into OpenGL coordinates.
        glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
        glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);

        // Reverse the orientation of front-facing polygons.
        glPushAttrib(GL_POLYGON_BIT);
        glFrontFace(GL_CW);
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);

        // Bind the texture.
        //glEnable(GL_TEXTURE_2D);
        //glBindTexture(GL_TEXTURE_2D, m_TextureID);

        // Interpolate between frames.
        Interpolate(m_InterpolatedVertices);

        // Draw the model.
        int* Commands = m_Commands;
        while(int i = *(Commands++))
        {
            if(i < 0)
            {
                glBegin(GL_TRIANGLE_FAN);
                i = -i;
            }
            else
            {
                glBegin(GL_TRIANGLE_STRIP);
            }

            for(; i > 0; --i, Commands += 3)
            {
                md2_glcmd_t* Command = (md2_glcmd_t*)Commands;

                glColor3f(1.0f, 1.0f, 1.0f);
                glTexCoord2f(Command->s, Command->t);
                glVertex3fv(m_InterpolatedVertices[Command->index]);
            }

            glEnd();
        }

        // Use the original orientation.
        glDisable(GL_CULL_FACE);
        glPopAttrib();

    glPopMatrix();
}

void MD2::Interpolate(vec3_t* Vertices)
{
    for(int i = 0; i < m_MD2Header.num_xyz; ++i)
    {
        Vertices[i][0] = m_Vertices[i + (m_MD2Header.num_xyz * m_MD2AnimState.current_frame)][0] * m_Scale;
        Vertices[i][1] = m_Vertices[i + (m_MD2Header.num_xyz * m_MD2AnimState.current_frame)][1] * m_Scale;
        Vertices[i][2] = m_Vertices[i + (m_MD2Header.num_xyz * m_MD2AnimState.current_frame)][2] * m_Scale;
    }
}
share|improve this question
add comment

1 Answer

up vote 1 down vote accepted

The drawing code looks fine - are you sure the loading is?

// Read all the data.
for(int i = 0; i < m_MD2Header.num_frames; ++i)
{
    md2_frame_t* Frame = (md2_frame_t*)&Buffer[m_MD2Header.framesize * i];
    vec3_t* Vertices = &m_Vertices[m_MD2Header.num_xyz * i];
    int* Normals = &m_Normals[m_MD2Header.num_xyz * i];

    for(int vertex = 0; vertex < m_MD2Header.num_xyz; ++vertex)
    {
        Vertices[i][0] = (Frame->vertices[i].v[0] * Frame->scale[0]) + Frame->translate[0];
        Vertices[i][1] = (Frame->vertices[i].v[1] * Frame->scale[1]) + Frame->translate[1];
        Vertices[i][2] = (Frame->vertices[i].v[2] * Frame->scale[2]) + Frame->translate[2];
        Normals[i] = Frame->vertices[i].index_normal;
    }
}

To me it looks as if the indexing is wrong - shouldn't you index Vertices[vertex][0] rather than Vertices[i][0]?

Besides: you don't need to take the OpenGL command list from the MD2 file - MD2 files also contain a regular list of triangles with indexes into the texture coordinate and vertex arrays.

I mention it because I took this path when I wrote a MD2 loader. I found this data much more convinient than a pre-optimized OpenGL stream, but it surely depends on the application.

share|improve this answer
    
I can't believe I missed that. –  Jason Dietrich Mar 30 '11 at 0:47
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.