www.xbdev.net xbdev - software development
Tuesday January 22, 2019
home | about | contact | Donations

     
 

3D File Formats

The bits and bytes...

 

MD3 Format - A Look Inside...
Author bkenwright@xbdev.net

When you first start with a new file format, I think its best to just rip it to pieces....to disect it byte by byte so you know exactly what your dealing with.  So I got a simple quake3 file, its called 'sarge' and is in the media folder with this demo. 

Usually you get the quake3 md3 file zipped up, so if you download one from planetquake or some other website, you'll can just rename it to a .zip and unzip it.  Then inside it you'll find various files.  Usually, as in our sarge demo, you'll get 3 md3 files which contain the actual meshes and animation data, and 3 .skin files which define the graphics and of course animation.cfg, which define which animations are from which frame index to what.

The .md3 files are binary, while the .skin and .cfg files can be opened in notepad and are text based.

<Sarge Files>

---

animation.cfg
head.md3
head_default.skin
lower.md3
lower_default.skin
upper.md3
upper_default.skin

band.tga
cigar.tga

---

For this demo I've just used the upper.md3 file, as once we can load in one .md3 file, we can do a class/structure for each .md3 and load all 3 in, then we link them together to form that full character.

Download Code
/***************************************************************************/
/*                                                                         */
/* File:   main.cpp                                                        */
/* Author: bkenwright@xbdev.net                                            */
/* URL:    www.xbdev.net                                                   */
/* Date:   19-03-2006 (easter)                                             */
/*                                                                         */
/***************************************************************************/
/*
   Understanding the Quake3 MD3 File Format
*/

//---------------------------------------------------------------------------



#define SZ_MD3_FILE "media\\model\\sarge\\upper.md3"

#define MAX_FILENAME_LENGTH 256


//---------------------------------------------------------------------------

#include <windows.h>
#include <stdio.h>                      //sprintf(...)

//---------------------------------------------------------------------------

//Saving debug information to a log file
void abc(char *str)
{
      FILE *fp = fopen("output.txt", "a+");
      fprintf(fp, "%s\n", str);
      fclose(fp);
}


//---------------------------------------------------------------------------


struct stMD3Header
{
    char    ID[4];          //  "IDP3"
    int     Version;        //  15
    char    Filename[68];
    int     numBoneFrames;
    int     numTags;
    int     numMeshes;
    int     numMaxSkins;
    int     headerLength;
    int     TagStart;
    int     TagEnd;
    int     FileSize;
};

struct stBoneFrame
{
    float mins[3];
    float maxs[3];
    float Position[3];
    float Scale;
    float Creator[16];
};

struct stAnim
{
    int FirstFrame; 
    int numFrames;
    int LoopingFrames;
    int FPS; 
};


struct stTag
{
    char            Name[64];
    float           Position[3];
    float           Rotation[3][3];
};

struct stTriangle
{
    int Vertex[3];
};

struct stTexCoord
{
    float Coord[2];
};

struct stVertex // = Record
{
    short int       Vertex[3];
    unsigned char   Normal[2];
}; 

struct stMeshHeader
{
    char    ID[4];
    char    Name[68];
    int     numMeshFrames;
    int     numSkins; 
    int     numVertexes;
    int     numTriangles;
    int     triStart;
    int     headerSize;
    int     TexVectorStart;
    int     VertexStart;
    int     MeshSize;
};

struct stMesh
{
    stMeshHeader    MeshHeader;
    char            Skins[68];
    stTriangle      Triangle;
    stTexCoord      TexCoord;
    stVertex        Vertex;
    unsigned int    Texture;
    bool            SetTexture; 
};

//---------------------------------------------------------------------------

 long filesize(FILE *stream)
 {
    long curpos, length;

    curpos = ftell(stream);
    fseek(stream, 0L, SEEK_END);
    length = ftell(stream);
    fseek(stream, curpos, SEEK_SET);
    return length;
 }

 //---------------------------------------------------------------------------


class CMD3
{
public:
    char        m_md3FileName[MAX_FILENAME_LENGTH];

    stMD3Header m_md3Header;
    stBoneFrame m_boneFrame;
    stTag       m_tags;

    stMesh      m_meshes[100];

    //-----------------------------------------------------------------------
    //
    //  Loads model from a .md3 file
    //
    //-----------------------------------------------------------------------
    bool LoadModel(char* filename)
    {
        char buf[256];

        FILE* fp = fopen(filename, "rb");

        if (fp==NULL)
        {
            abc("unable to open file");
            return false;
        }

        int md3filesize = filesize(fp);
        fseek(fp, 0L, SEEK_SET);

        if (strlen(filename)>255)
        {
            sprintf(buf, "filename is longer than %d",  MAX_FILENAME_LENGTH);
            abc(buf);
            return false;
        }
        // copy name
        strcpy(m_md3FileName, filename);
        sprintf(buf, "MD3 FileName: %s", m_md3FileName);
        abc(buf);

        sprintf(buf, "FileSize: %d", md3filesize);
        abc(buf);

        abc("\n~~MD3 Header~~\n");
    
        // read header
        fread(&m_md3Header, 1, sizeof(stMD3Header), fp);

        // log debug information to file
        sprintf(buf, "ID %c%c%c%c", m_md3Header.ID[0], m_md3Header.ID[1], m_md3Header.ID[2], m_md3Header.ID[3]);
        abc(buf);

        sprintf(buf, "Version: %d", m_md3Header.Version);
        abc(buf);

        sprintf(buf, "FileName: %s", m_md3Header.Filename);
        abc(buf);

        sprintf(buf, "numBoneFrames: %d", m_md3Header.numBoneFrames);
        abc(buf);

        sprintf(buf, "numTags: %d", m_md3Header.numTags);
        abc(buf);

        sprintf(buf, "numMeshes: %d", m_md3Header.numMeshes);
        abc(buf);

        sprintf(buf, "numMaxSkins: %d", m_md3Header.numMaxSkins);
        abc(buf);

        sprintf(buf, "headerLength: %d", m_md3Header.headerLength);
        abc(buf);

        sprintf(buf, "TagStart: %d", m_md3Header.TagStart);
        abc(buf);

        sprintf(buf, "TagEnd: %d", m_md3Header.TagEnd);
        abc(buf);

        sprintf(buf, "FileSize: %d", m_md3Header.FileSize);
        abc(buf);



        if (strcmp("IDP3", m_md3Header.ID)==NULL)
        {
            sprintf(buf, "Incorrect File Format 'Incorrect ID' ie. ('IDP3')");
            abc(buf);
        }

        /*
        // read boneframes
        fread(&m_boneFrame[0], 1, m_md3Header.numBoneFrames*sizeof(stBoneFrame), fp);

        // read tags
        fread(&m_tags[0], m_md3Header.numBoneFrames*m_md3Header.numTags*sizeof(stTag));


        int MeshOffset = ftell(fp);

        //For I :=0 to Header.numMeshes-1 do
        //  begin
        for (int i=0; i<MD3Header.numMeshes; i++)
        {
            fseek(fp, MeshOffset, SEEK_SET);

            // Load the Skins
            fread(Meshes[i].Skins[0], 68 * Meshes[i].MeshHeader.numSkins, fp);

            // Triangles
            fseek(fp, MeshOffset + Meshes[i].MeshHeader.triStart, SEEK_SET);
            fread(Meshes[i].Triangle[0], sizeof(stTriangle)*Meshes[i].MeshHeader.numTriangles, fp);

            // Texture Coordiantes
            fseek(fp, MeshOffset + Meshes[i].MeshHeader.TexVectorStart);
            fread(Meshes[i].TexCoord[0], sizeof(stTexCoord)*Meshes[i].MeshHeader.numVertexes, fp);

            // Vertices
            fseek(fp, MeshOffset + Meshes[i].MeshHeader.VertexStart);
            fread(Meshes[i].Vertex[0], sizeof(stVertex)*Meshes[i].MeshHeader.numVertexes * Meshes[i].MeshHeader.numMeshFrames);

            MeshOffset = MeshOffset + Meshes[i].MeshHeader.MeshSize;
        }
    */

        fclose(fp);

        return true;
    }// End LoadModel(..)

};



//---------------------------------------------------------------------------

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    
    static CMD3 md3;

    md3.LoadModel(SZ_MD3_FILE);

    // If something was not done, let it go
    return 0;
}

//---------------------------------------------------------------------------

Below shows what your likely to see if you run the above code, we use the standard c library functions to read in various information from the header.  This header information tells us that its a quake3 file, how many bytes long it is, and the offsets to the various other information that we need, such as vertices, bones etc.

Output.txt
MD3 FileName: media\model\sarge\upper.md3
FileSize: 468636

~~MD3 Header~~

ID IDP3
Version: 15
FileName: models/players/sarge/sarge.md3
numBoneFrames: 153
numTags: 3
numMeshes: 2
numMaxSkins: 0
headerLength: 108
TagStart: 8676
TagEnd: 60084
FileSize: 468636

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.