www.xbdev.net xbdev - software development
Sunday November 17, 2019
home | about | contact | Donations

     
 

3D File Formats

The bits and bytes...

 

MD3 Format - More bits & bytes...
Author bkenwright@xbdev.net

When you get the header, you find that you have to look further, using the header information and a few stuctures to define the layout of the various parts we can explore things further.  With the offsets to the data, in the file, we can now dump even more data!

 

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


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


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


        //fseek(fp, m_md3Header.ofMeshSurfaces, SEEK_SET);
        int meshOFS = m_md3Header.ofMeshSurfaces;


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

            fseek(fp, meshOFS, SEEK_SET);

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


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


            meshOFS += pMeshHeader->ofsEndMeshSize;

        }//End for meshes


        fclose(fp);

        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)]
#

.... lots more 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]
Name [u_rshoulder]
flags [0x00]
numMeshFrames [153]
numSkins [1]
numVertexes [27]
numVertexes [27]
ofsTriangles [176]
ofsSkins [108]
ofsTexVector [536]
ofsVertex [752]
ofsEndMeshSize [33800]
#
ID [IDP3]
Name [u_torso]
flags [0x00]
numMeshFrames [153]
numSkins [1]
numVertexes [301]
numVertexes [301]
ofsTriangles [176]
ofsSkins [108]
ofsTexVector [3920]
ofsVertex [6328]
ofsEndMeshSize [374752]

 

 
 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.