www.xbdev.net
xbdev - software development
Thursday March 28, 2024
home | contact | Support | 3D File Formats The bits and bytes... | 3D File Formats The bits and bytes...
>>
     
 

3D File Formats

The bits and bytes...

 

MD3 Format - Mesh & Vertice Data...

Author bkenwright@xbdev.net



When you first start with a new file format, I think its best to just rip it to pieces....to dissect it byte by byte so you know exactly what your dealing with. 



My code example to show how it worked:

 

Download Source 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);
}


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


typedef unsigned int        uint32;
typedef int                 int32;
typedef unsigned short int  uint16;
typedef short int           int16;
typedef float               float32;



struct stMD3Header
{
    char        ID[4];          //  ID of the file is always "IDP3"
    int32       Version;        //  Version number, usually 15
    char        Filename[68];   //  Filename, sometimes left blank
    int32       numBoneFrames;  //  Number of BoneFrames
    int32       numTags;        //  Number of 'tags' per BoneFrame
    int32       numMeshes;      //  Number of Meshes/Skins in MaxSkin
    int32       numMaxSkins;    //  Maximum number of unique skins
    int32       ofsFrames;      //  Always equal to the length this header
    int32       ofsTagStart;    //  Starting position of tag structures
    int32       ofMeshSurfaces; //  Ending position of tag structure
    int32       ofEndOfFile;    //  Size of file
};



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

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

struct stSkin
{
    char Name[64];
    int32 index;
};

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

struct stTriangle
{
    int32 Vertex[3];
};

struct stTexCoord
{
    float32 Coord[2];
};

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

struct stMeshHeader
{
    char    ID[4];
    char    Name[64];
    int32   flags;
    int32   numMeshFrames;
    int32   numSkins; 
    int32   numVertexes;
    int32   numTriangles;
    int32   ofsTriangles;
    int32   ofsSkins;
    int32   ofsTexVector;
    int32   ofsVertex;
    int32   ofsEndMeshSize;
};

struct stMesh
{
    stMeshHeader    MeshHeader;
    stSkin*         pSkins;
    stTriangle*     pTriangle;
    stTexCoord*     pTexCoord;
    stVertex*       pVertex;
};

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

 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_pBoneFrame;
    stTag*       m_pTags;
    stMesh*      m_pMeshes;

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

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

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

        // Lets get the size of this md3 file
        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, "ofsFrames: %d", m_md3Header.ofsFrames);
        abc(buf);

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

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

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


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

        // Allocate memory for all or bones/tags/etc
        m_pBoneFrame = new stBoneFrame[m_md3Header.numBoneFrames];
        m_pTags      = new stTag[m_md3Header.numBoneFrames * m_md3Header.numTags];
        m_pMeshes    = new stMesh[m_md3Header.numMeshes];


        // Lets seek to the start of the bone frames & read boneframe
        fseek(fp, m_md3Header.ofsFrames, SEEK_SET);
        fread(m_pBoneFrame, 1, m_md3Header.numBoneFrames*sizeof(stBoneFrame), fp);

        sprintf(buf, "\n~~~~BoneFrames: %d~~~~~~", m_md3Header.numBoneFrames);
        abc(buf);
        for (int i=0; i<m_md3Header.numBoneFrames; i++)
        {
            abc("#");
            sprintf(buf, "mins[%.1f,%.1f,%.1f]", m_pBoneFrame[i].mins[0], m_pBoneFrame[i].mins[1], m_pBoneFrame[i].mins[2]);
            abc(buf);
            sprintf(buf, "maxs[%.1f,%.1f,%.1f]", m_pBoneFrame[i].maxs[0], m_pBoneFrame[i].maxs[1], m_pBoneFrame[i].maxs[2]);
            abc(buf);
            sprintf(buf, "Position[%.1f,%.1f,%.1f]", m_pBoneFrame[i].Position[0], m_pBoneFrame[i].Position[1], m_pBoneFrame[i].Position[2]);
            abc(buf);
            sprintf(buf, "Scale[%.1f]", m_pBoneFrame[i].Scale);
            abc(buf);
            sprintf(buf, "Creator[%s]", m_pBoneFrame[i].Creator);
            abc(buf);
        }


        // Seek to start of tags and read them all in
        fseek(fp, m_md3Header.ofsTagStart, SEEK_SET);
        fread(m_pTags, 1, m_md3Header.numBoneFrames*m_md3Header.numTags*sizeof(stTag), fp);

        sprintf(buf, "\n~~~~Tags: %d~~~~~~", m_md3Header.numTags);
        abc(buf);
        for (int i=0; i<m_md3Header.numTags; i++)
        {
            abc("#");
            sprintf(buf, "Name[%s]", m_pTags[i].Name);
            abc(buf);
            sprintf(buf, "Position[%.1f,%.1f,%.1f]", m_pTags[i].Position[0], m_pTags[i].Position[1], m_pTags[i].Position[2]);
            abc(buf);
            sprintf(buf, "Rotation[%.1f,%.1f,%.1f][...][...]", m_pTags[i].Rotation[0][0], m_pTags[i].Rotation[0][1], m_pTags[i].Rotation[0][2]);
            abc(buf);
        }


        int meshOFS = m_md3Header.ofMeshSurfaces;

        for (int j=0; j<m_md3Header.numMeshes; j++)
        {
            stMesh * pMesh = &m_pMeshes[j];
            stMeshHeader * pMeshHeader = &(pMesh->MeshHeader);

            fseek(fp, meshOFS, SEEK_SET);

            // Seek to the start of the mesh data and read it all in
            fread(pMeshHeader, 1, sizeof(stMeshHeader), fp);

            // Read in all the sub parts of the mesh data
            {
                fseek(fp, meshOFS + pMeshHeader->ofsTriangles, SEEK_SET);
                pMesh->pTriangle = new stTriangle [pMeshHeader->numTriangles];
                fread( pMesh->pTriangle, 1, pMeshHeader->numTriangles * sizeof(stTriangle), fp);

                fseek(fp, meshOFS + pMeshHeader->ofsSkins, SEEK_SET);
                pMesh->pSkins = new stSkin [pMeshHeader->numSkins];
                fread( pMesh->pSkins, 1, pMeshHeader->numSkins * sizeof(stSkin), fp);

                fseek(fp, meshOFS + pMeshHeader->ofsTexVector, SEEK_SET);
                pMesh->pTexCoord = new stTexCoord [pMeshHeader->numVertexes];
                fread( pMesh->pTexCoord, 1, pMeshHeader->numVertexes * sizeof(stTexCoord), fp);
            
                fseek(fp, meshOFS + pMeshHeader->ofsVertex, SEEK_SET);
                pMesh->pVertex = new stVertex [pMeshHeader->numVertexes];
                fread( pMesh->pVertex, 1, pMeshHeader->numVertexes * sizeof(stVertex), fp);
            }

            meshOFS += pMeshHeader->ofsEndMeshSize;

        }//End for meshes


        sprintf(buf, "\n~~~~Mesh Surfaces: %d~~~~~~", m_md3Header.numMeshes);
        abc(buf);
        for (int j=0; j<m_md3Header.numMeshes; j++)
        {
            abc("#");
            stMesh * pMesh = &m_pMeshes[j];
            stMeshHeader * pMeshHeader = &(pMesh->MeshHeader);

            sprintf(buf, "ID [%c%c%c%c]", pMeshHeader->ID[0], pMeshHeader->ID[1], pMeshHeader->ID[2], pMeshHeader->ID[3]);
            abc(buf);
            sprintf(buf, "Name [%s]", pMeshHeader->Name);
            abc(buf);
            sprintf(buf, "flags [0x%.2X]", pMeshHeader->flags);
            abc(buf);
            sprintf(buf, "numMeshFrames [%d]", pMeshHeader->numMeshFrames);
            abc(buf);
            sprintf(buf, "numSkins [%d]", pMeshHeader->numSkins);
            abc(buf);
            sprintf(buf, "numVertexes [%d]", pMeshHeader->numVertexes);
            abc(buf);
            sprintf(buf, "numVertexes [%d]", pMeshHeader->numVertexes);
            abc(buf);
            sprintf(buf, "ofsTriangles [%d]", pMeshHeader->ofsTriangles);
            abc(buf);
            sprintf(buf, "ofsSkins [%d]", pMeshHeader->ofsSkins);
            abc(buf);
            sprintf(buf, "ofsTexVector [%d]", pMeshHeader->ofsTexVector);
            abc(buf);
            sprintf(buf, "ofsVertex [%d]", pMeshHeader->ofsVertex);
            abc(buf);
            sprintf(buf, "ofsEndMeshSize [%d]", pMeshHeader->ofsEndMeshSize);
            abc(buf);


            // Mesh Triangles
            for (int i=0; i<pMeshHeader->numTriangles; i++)
            {
                stTriangle * pTri = &(pMesh->pTriangle[i]);
                sprintf(buf, "Triangle [%d,%d,%d]", pTri->Vertex[0], pTri->Vertex[1], pTri->Vertex[2]);
                abc(buf);
            }

            // Mesh Skins
            for (int i=0; i<pMeshHeader->numSkins; i++)
            {
                stSkin * pSkin = &(pMesh->pSkins[i]);
                sprintf(buf, "Skin:Name [%s]", pSkin->Name);
                abc(buf);
                sprintf(buf, "Skin:Index [%d]", pSkin->index);
                abc(buf);
            }

            for (int i=0; i<pMeshHeader->numVertexes; i++)
            {
                stTexCoord * pTex = &(pMesh->pTexCoord[i]);
                sprintf(buf, "TexCoord:Index [%.1f,%.1f]", pTex->Coord[0], pTex->Coord[1]);
                abc(buf);
            }


            for (int i=0; i<pMeshHeader->numVertexes; i++)
            {
                stVertex* pVert = &(pMesh->pVertex[i]);
                sprintf(buf, "Vertice:Vertex [%d,%d,%d]", pVert->Vertex[0], pVert->Vertex[1], pVert->Vertex[2]);
                abc(buf);

            }
        }


        fclose(fp);

        for (int j=0; j<m_md3Header.numMeshes; j++)
        {
            stMesh * pMesh = &m_pMeshes[j];

            delete[] pMesh->pSkins;
            delete[] pMesh->pTexCoord;
            delete[] pMesh->pTriangle;
            delete[] pMesh->pVertex;

            pMesh->pSkins       = NULL;
            pMesh->pTexCoord    = NULL;
            pMesh->pTriangle    = NULL;
            pMesh->pVertex      = NULL;
        }

        delete[] m_pBoneFrame;
        delete[] m_pTags;
        delete[] m_pMeshes;

        m_pBoneFrame = NULL;
        m_pTags      = NULL;
        m_pMeshes    = NULL;

        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;
}

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

 

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
ofsFrames: 108
ofsTagStart: 8676
ofMeshSurfaces: 60084
ofEndOfFile (Filesize): 468636

~~~~BoneFrames: 153~~~~~~
#
mins[-3.1,-20.1,-2.4]
maxs[19.7,8.1,22.7]
Position[0.0,0.0,0.0]
Scale[36.1]
Creator[(from ASE)]
#
mins[-3.4,-20.3,-2.3]
maxs[18.8,9.1,21.2]
Position[0.0,0.0,0.0]
Scale[34.9]
Creator[(from ASE)]
#

.....more of the same...

~~~~Tags: 3~~~~~~
#
Name[tag_head]
Position[10.8,-3.0,11.6]
Rotation[0.7,-0.1,-0.7][...][...]
#
Name[tag_weapon]
Position[5.9,-10.3,15.5]
Rotation[-0.9,-0.3,0.3][...][...]
#
Name[tag_torso]
Position[0.0,0.0,0.0]
Rotation[1.0,-0.0,-0.0][...][...]

~~~~Mesh Surfaces: 2~~~~~~
#
ID [IDP3] ----------------MESH 1
Name [u_rshoulder]
flags [0x00]
numMeshFrames [153]
numSkins [1]
numVertexes [27]
numVertexes [27]
ofsTriangles [176]
ofsSkins [108]
ofsTexVector [536]
ofsVertex [752]
ofsEndMeshSize [33800]
Triangle [0,1,2]
Triangle [2,1,3]
Triangle [2,3,8]
Triangle [2,8,5]
Triangle [5,8,6]
Triangle [5,6,7]
Triangle [1,4,3]
Triangle [3,4,9]
Triangle [15,24,14]
Triangle [14,24,23]
Triangle [14,23,12]
Triangle [12,23,10]
Triangle [12,10,11]
Triangle [11,10,17]
Triangle [11,17,20]
Triangle [20,17,25]

...... more of the same...

Skin:Name []
Skin:Index [0]
TexCoord:Index [0.3,0.4]
TexCoord:Index [0.3,0.4]
TexCoord:Index [0.3,0.3]
TexCoord:Index [0.2,0.3]
TexCoord:Index [0.3,0.4]
TexCoord:Index [0.3,0.4]
TexCoord:Index [0.3,0.4]
TexCoord:Index [0.3,0.4]
TexCoord:Index [0.2,0.4]
TexCoord:Index [0.2,0.4]
TexCoord:Index [0.0,0.6]
TexCoord:Index [0.0,0.6]
TexCoord:Index [0.1,0.6]
TexCoord:Index [0.1,0.6]
TexCoord:Index [0.1,0.5]
TexCoord:Index [0.2,0.5]
TexCoord:Index [0.2,0.6]
TexCoord:Index [0.0,0.7]
TexCoord:Index [0.1,0.6]
TexCoord:Index [0.2,0.6]
TexCoord:Index [0.1,0.7]
TexCoord:Index [0.1,0.7]
TexCoord:Index [0.2,0.7]
TexCoord:Index [0.0,0.5]
TexCoord:Index [0.1,0.5]
TexCoord:Index [0.0,0.7]
TexCoord:Index [0.1,0.7]
Vertice:Vertex [375,-971,397]
Vertice:Vertex [321,-1131,568]
Vertice:Vertex [476,-765,420]
Vertice:Vertex [614,-913,1037]
Vertice:Vertex [370,-1186,836]
Vertice:Vertex [392,-643,536]
Vertice:Vertex [378,-594,957]
Vertice:Vertex [327,-539,729]
Vertice:Vertex [505,-762,1035]
Vertice:Vertex [514,-1122,1021]
Vertice:Vertex [327,-539,729]
Vertice:Vertex [168,-784,844]
Vertice:Vertex [223,-719,636]
Vertice:Vertex [167,-955,680]
Vertice:Vertex [223,-831,524]
Vertice:Vertex [375,-971,397]
Vertice:Vertex [321,-1131,568]
Vertice:Vertex [378,-594,957]
Vertice:Vertex [246,-995,903]
Vertice:Vertex [370,-1186,836]
Vertice:Vertex [306,-809,1009]
Vertice:Vertex [341,-959,1052]
Vertice:Vertex [514,-1122,1021]
Vertice:Vertex [392,-643,536]
Vertice:Vertex [476,-765,420]
Vertice:Vertex [505,-762,1035]
Vertice:Vertex [614,-913,1037]
#
ID [IDP3] ------------------------MESH 2
Name [u_torso]
flags [0x00]
numMeshFrames [153]
numSkins [1]
numVertexes [301]
numVertexes [301]
ofsTriangles [176]
ofsSkins [108]
ofsTexVector [3920]
ofsVertex [6328]
ofsEndMeshSize [374752]
Triangle [27,28,29]
Triangle [259,260,261]
Triangle [23,21,22]
Triangle [22,21,24]
Triangle [22,24,26]
Triangle [26,24,25]
Triangle [26,25,56]
Triangle [56,25,59]
Triangle [56,59,20]
Triangle [20,59,19]
Triangle [20,19,18]
Triangle [18,19,17]
Triangle [18,17,63]
Triangle [55,45,52]

..etc

Skin:Index [0]
TexCoord:Index [0.0,0.0]
TexCoord:Index [-0.0,0.0]
TexCoord:Index [0.0,0.0]
TexCoord:Index [0.0,0.0]
TexCoord:Index [0.0,0.0]
TexCoord:Index [0.1,0.0]

...etc

Vertice:Vertex [767,-197,857]
Vertice:Vertex [683,-210,922]
Vertice:Vertex [683,-139,931]
Vertice:Vertex [682,-279,914]
Vertice:Vertex [767,-197,857]

 

 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2024 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.