www.xbdev.net xbdev - software development
Saturday May 25, 2019
home | about | contact | Donations

     
 

3D File Formats

The bits and bytes...

 

MD3 Format - Divide and Conquer...
Author bkenwright@xbdev.net

When you ..

Download Source Code

 

md3.h
/***************************************************************************/
/*                                                                         */
/* File:   md3.h                                                           */
/* Author: bkenwright@xbdev.net                                            */
/* URL:    www.xbdev.net                                                   */
/* Date:   25-03-2006 (easter)                                             */
/*                                                                         */
/***************************************************************************/
/*
   Understanding the Quake3 MD3 File Format
*/

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



#define SZ_MD3_FILE "media\\model\\sarge\\upper.md3"
#define SZ_MD3_ANIM_FILE "media\\model\\sarge\\animation.cfg"
#define SZ_MD3_SKIN_FILE "media\\model\\sarge\\upper_default.skin"

#define MAX_FILENAME_LENGTH 256


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

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

#pragma comment(lib, "D3d8.lib") //directX 8
#pragma comment(lib, "D3dx8.lib")
#include <d3dx8.h>


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

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


struct stM3DModel
{
	char		m_md3FileName[MAX_FILENAME_LENGTH];
	stMD3Header m_md3Header;

	stBoneFrame* m_pBoneFrame;
	stTag*		 m_pTags;
	stMesh*		 m_pMeshes;

	stAnim		 m_Anim[26];

	int	m_FPS;
	int m_startFrame;
	int m_endFrame;
	int m_nextFrame;
	int m_animLower;
	int m_animUpper;
	
	int m_currentframe;
};

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

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

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

 enum
{
	BOTH_DEATH1 = 0,
	BOTH_DEAD1  = 1,
	BOTH_DEATH2 = 2,
	BOTH_DEAD2  = 3,
	BOTH_DEATH3 = 4,
	BOTH_DEAD3  = 5,

	TORSO_GESTURE = 6,
	TORSO_ATTACK  = 7,
	TORSO_ATTACK2 = 8,
	TORSO_DROP    = 9,
	TORSO_RAISE   = 10,
	TORSO_STAND   = 11,
	TORSO_STAND2  = 12,

	LEGS_WALKCR   = 13,
	LEGS_WALK     = 14,
	LEGS_RUN      = 15,
	LEGS_BACK     = 16,
	LEGS_SWIM     = 17,
	LEGS_JUMP     = 18,
	LEGS_LAND     = 19,
	LEGS_JUMPB    = 20,
	LEGS_LANDB    = 21,
	LEGS_IDLE     = 22,
	LEGS_IDLECR   = 23,
	LEGS_TURN     = 24,

	MAX_ANIMATIONS
};

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

extern LPDIRECT3DDEVICE8 g_pD3DDevice;

inline void DbgTriangle(IDirect3DDevice8  * pDevice,
						D3DXVECTOR3& a, D3DXVECTOR3& b, D3DXVECTOR3& c,
						DWORD colour)
{
		struct TLVERTEX
		{
			float x,y,z;
			DWORD col;
			enum{ FVF_TLVERTEX = D3DFVF_XYZ | D3DFVF_DIFFUSE};
		};

		pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
		pDevice->SetTextureStageState(0,D3DTSS_COLOROP,	D3DTOP_SELECTARG1);
		pDevice->SetTextureStageState(0,D3DTSS_COLORARG1,	D3DTA_DIFFUSE);

		TLVERTEX Vertex[3] = 
		{
			//   x	  y		z	colour
			{    a.x, a.y, a.z, colour  },
			{    b.x, b.y, b.z, colour  },
			{    c.x, c.y, c.z, colour  }
		};
		

		
		#if(DIRECT3D_VERSION >= 0x0900)
		pDevice->SetFVF( TLVERTEX::FVF_TLVERTEX );
		#else
		pDevice->SetVertexShader( TLVERTEX::FVF_TLVERTEX );
		#endif // DIRECT3D_VERSION
		pDevice->SetTexture( 0, NULL );

		pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

		pDevice->SetRenderState(  D3DRS_FILLMODE, 
								   D3DFILL_WIREFRAME );

		HRESULT hr = pDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, 1, Vertex, sizeof( TLVERTEX ) );

		//g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}

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




class CMD3
{
public:

	stM3DModel  m_upper;


	float poll;


	CMD3()
	{
		//m_startFrame = 0;   
		//m_endFrame = 0;
		//m_currentframe = 0;
		poll = 0;
		//m_lastUpdate = 0;
	}


	void Create(/* file names */)
	{
		LoadModel(&m_upper, SZ_MD3_FILE);
		LoadAnim (&m_upper, SZ_MD3_ANIM_FILE);
		LoadSkin (&m_upper, SZ_MD3_SKIN_FILE);
		SetAnim  (&m_upper, TORSO_STAND);

	}
	void Release()
	{
		ReleaseModel(&m_upper);
	}
	void Update(float time)
	{
		UpdateFrame(&m_upper, time);
	}
	void Render()
	{
		DrawSkeleton(&m_upper);
	}

protected:

	void UpdateFrame(stM3DModel* pMod, float time)
	{
		/*
		poll = (time - m_lastUpdate);
		if (poll > 1/m_FPS)
		{
			m_frame = m_nextFrame;
			m_nextFrame++;
			if (m_nextFrame > m_endFrame)
			{
				m_nextFrame = m_startFrame;
				m_lastUpdate = time;
			}
		}
		*/
		poll += time;
		if (poll>= 1.0f)
		{
			poll=0.0f;
			pMod->m_currentframe = pMod->m_nextFrame;
			pMod->m_nextFrame++;
			if (pMod->m_nextFrame > pMod->m_endFrame)
			{
				pMod->m_nextFrame = pMod->m_startFrame;
			}
		}
	}

	void DrawSkeleton(stM3DModel* pMod)
	{
		DrawModel(pMod);

		/*
		for (int i=0; i<m_md3Header.numTags; i++)
		{
			float (*Rotation)[3];

			Rotation = (m_pTags[m_currentframe * m_md3Header.numTags + i].Rotation); //3x3 mat
			float* Position = m_pTags[m_currentframe * m_md3Header.numTags + i].Position; //vector3
			char* tagName = m_pTags[m_currentframe * m_md3Header.numTags + i].Name;


			D3DXMATRIX m;
			m(0,0)  = Rotation[0][0];
			m(0,1)  = Rotation[0][1];
			m(0,2)  = Rotation[0][2];
			m(0,3)  = 0;
			m(1,0)  = Rotation[1][0];
			m(1,1)  = Rotation[1][1];
			m(1,2)  = Rotation[1][2];
			m(1,3)  = 0;
			m(2,0)  = Rotation[2][0];
			m(2,1)  = Rotation[2][1];
			m(2,2)  = Rotation[2][2];
			m(2,3)  = 0;
			m(3,0)  = Position[0];
			m(3,1)  = Position[1];
			m(3,2)  = Position[2];
			m(3,3)  = 1;


			//D3DXMATRIX tx;
			//D3DXMatrixTranslation(&tx, 0, 0, 0);
			g_pD3DDevice->SetTransform(D3DTS_WORLD, &m);

			if ( strcmp(tagName, "tag_head")==0 )
			{
				DrawSkeleton()
			}
		}
		*/
	}

	void DrawModel(stM3DModel* pMod)
	{
		DrawModelInt(pMod, pMod->m_currentframe, pMod->m_nextFrame, poll);
	}

	void DrawModelInt(stM3DModel* pMod, const int currentFrame, const int nexFrame, float pol)
	{
		stMD3Header* pHeader = &pMod->m_md3Header;
		stMesh*		 pMeshes = pMod->m_pMeshes;

		for (int k=0; k<pHeader->numMeshes; k++)
		{
			stMesh* currentMesh = &pMeshes[k];
			//char* meshName = currentMesh->MeshHeader.Name;

			int currentOffsetVertex = currentFrame * currentMesh->MeshHeader.numVertexes;
			//interpolation
			int nextCurrentOffsetVertex = nexFrame * currentMesh->MeshHeader.numVertexes;

			int TriangleNum = currentMesh->MeshHeader.numTriangles;

			// get/set texture
			// TO-DO

			for (int i=0; i<TriangleNum; i++)
			{
				D3DXVECTOR3 tri[3];
				for (int j=0; j<3; j++)
				{
					int currentVertex = currentMesh->pTriangle[i].Vertex[j];

					float vA[3];
					vA[0] = currentMesh->pVertex[currentOffsetVertex + currentVertex].Vertex[0] / 64.0f;
					vA[1] = currentMesh->pVertex[currentOffsetVertex + currentVertex].Vertex[1] / 64.0f;
					vA[2] = currentMesh->pVertex[currentOffsetVertex + currentVertex].Vertex[2] / 64.0f;

					float nextVA[3];
					nextVA[0] = currentMesh->pVertex[nextCurrentOffsetVertex + currentVertex].Vertex[0] / 64.0f;
					nextVA[1] = currentMesh->pVertex[nextCurrentOffsetVertex + currentVertex].Vertex[1] / 64.0f;
					nextVA[2] = currentMesh->pVertex[nextCurrentOffsetVertex + currentVertex].Vertex[2] / 64.0f;

					/*
					float normU, normV;
					normU = currentMesh->pVertex[currentOffsetVertex + currentVertex].Normal[0];
					normV = currentMesh->pVertex[currentOffsetVertex + currentVertex].Normal[1];
					*/

					// Interplated value
					float pA0 = vA[0] + pol * (nextVA[0] - vA[0]);
					float pA1 = vA[1] + pol * (nextVA[1] - vA[1]);
					float pA2 = vA[2] + pol * (nextVA[2] - vA[2]);
					
					tri[j].x = pA0;
					tri[j].y = pA1;
					tri[j].z = pA2;
				}

				DbgTriangle(g_pD3DDevice, tri[0], tri[1], tri[2], 0xff00ffff );
			}
		}
	}


	bool SetAnim(stM3DModel* pMod, int ani)
	{
		if ( ani>=0 && ani<=5)
		{
		}
		else if ( ani>=6 && ani<=12)
		{
		}
		else if ( ani>=13 && ani<=24)
		{
		}
		else
		{
			_asm
			{
				int 13;
			}
		}

		pMod->m_FPS        = pMod->m_Anim[ani].FPS;
		pMod->m_startFrame = pMod->m_Anim[ani].FirstFrame;
		pMod->m_nextFrame  = pMod->m_Anim[ani].FirstFrame;
		pMod->m_endFrame   = pMod->m_Anim[ani].FirstFrame + pMod->m_Anim[ani].numFrames;
		pMod->m_animLower  = ani;
		pMod->m_animUpper  = ani;
		pMod->m_currentframe = pMod->m_startFrame;

		return true;
	}


	bool LoadSkin(stM3DModel* pMod, const char* filename)
	{
		char buf[256];

		char  MeshName[256] = {0};
		char  ImageName[256] = {0};

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

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

		//char firstWord[256];

		char * val = NULL;
		int i = 0;

		do
		{
			val = fgets(buf,256,fp);
			if (val==NULL)
			{
				break;
			}
			//sscanf(buf, "%s", firstWord);

			if (buf[ strlen(buf)-1 ] == '\n')
			{
				buf[ strlen(buf)-1 ] = NULL;
			}

			if (buf[ strlen(buf)-1 ] == ',')
			{
				strcpy(MeshName, buf);
				MeshName[ strlen(MeshName)-1 ] = NULL;
			}

			if ( strncmp(buf, "tag_", 4)==0 )  // tags dont have skins
			{
				continue;
			}

			//char* pImageName = strstr(MeshName, ","); // get the full image and path name
			//strcpy(ImageName, pImageName);			  // get name from last / (i.e only filename)
			char* pName = strrchr(buf, '/');
			strcpy(ImageName, pName+1); 

			// lose the starting /
		} while (val);


		return true;
	}



	bool LoadAnim(stM3DModel* pMod, const char* filename)
	{
		char buf[256];

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

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

		char firstWord[256];

		char * val = NULL;
		int i = 0;

		do
		{

			val = fgets(buf,256,fp);
			if (val==NULL)
			{
				break;
			}
			sscanf(buf, "%s", firstWord);


			if (strcmp("sex", firstWord)==NULL)
			{
			}
			else if (strcmp("headoffset", firstWord)==NULL)
			{
			}
			else if (strcmp("footsteps", firstWord)==NULL)
			{
			}
			else if (strcmp("", firstWord)==NULL)
			{
			}
			else if (strcmp("//", firstWord)==NULL)
			{
			}
			else
			{
				char words[4][256];

				sscanf(buf, "%s %s %s %s", &words[0], &words[1], &words[2], &words[3]);
				// Extract the values of FirstFrame, numFrames, LoopingFrames, FPS from the String
				int FirstFrame		= atoi(words[0]);
				int numFrames		= atoi(words[1]);
				int loopingFrames	= atoi(words[2]);
				int FPS				= atoi(words[3]);

				pMod->m_Anim[i].FirstFrame    = FirstFrame;
				pMod->m_Anim[i].numFrames     = numFrames;
				pMod->m_Anim[i].LoopingFrames = loopingFrames;
				pMod->m_Anim[i].FPS           = FPS;

				i++;
			}

		} while (val);

		int skip = pMod->m_Anim[LEGS_WALKCR].FirstFrame - pMod->m_Anim[TORSO_GESTURE].FirstFrame;
  
		for (int i=LEGS_WALKCR; i<MAX_ANIMATIONS; i++)
		{
			pMod->m_Anim[i].FirstFrame = pMod->m_Anim[i].FirstFrame - skip;
		}

		for (int i=0; i<MAX_ANIMATIONS; i++)
		{
			if (pMod->m_Anim[i].numFrames > 0)
			{
				pMod->m_Anim[i].numFrames = pMod->m_Anim[i].numFrames - 1;
			}
		}

		fclose(fp);

		return true;
	}

	//-----------------------------------------------------------------------
	//
	//  Loads model from a .md3 file
	//
	//-----------------------------------------------------------------------
	bool LoadModel(stM3DModel* pMod, 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(pMod->m_md3FileName, filename);
		sprintf(buf, "MD3 FileName: %s", pMod->m_md3FileName);
		abc(buf);

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

		abc("\n~~MD3 Header~~\n");
	
		stMD3Header* pHeader = &pMod->m_md3Header;

		// read header
		fread(pHeader, 1, sizeof(stMD3Header), fp);

		/*
		// log debug information to file
		sprintf(buf, "ID %c%c%c%c", pHeader->ID[0], pHeader->ID[1], pHeader->ID[2], pHeader->ID[3]);
		abc(buf);

		sprintf(buf, "Version: %d", pHeader->Version);
		abc(buf);

		sprintf(buf, "FileName: %s", pHeader->Filename);
		abc(buf);

		sprintf(buf, "numBoneFrames: %d", pHeader->numBoneFrames);
		abc(buf);

		sprintf(buf, "numTags: %d", pHeader->numTags);
		abc(buf);

		sprintf(buf, "numMeshes: %d", pHeader->numMeshes);
		abc(buf);

		sprintf(buf, "numMaxSkins: %d", pHeader->numMaxSkins);
		abc(buf);

		sprintf(buf, "ofsFrames: %d", pHeader->ofsFrames);
		abc(buf);

		sprintf(buf, "ofsTagStart: %d", pHeader->ofsTagStart);
		abc(buf);

		sprintf(buf, "ofMeshSurfaces: %d", pHeader->ofMeshSurfaces);
		abc(buf);

		sprintf(buf, "ofEndOfFile (Filesize): %d", pHeader->ofEndOfFile);
		abc(buf);
		*/

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


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

		stBoneFrame* pBoneFrame = pMod->m_pBoneFrame;
		stTag* pTags			= pMod->m_pTags;
		stMesh* pMeshes			= pMod->m_pMeshes;

		// Lets seek to the start of the bone frames & read boneframe
		fseek(fp, pHeader->ofsFrames, SEEK_SET);
		fread(pBoneFrame, 1, pHeader->numBoneFrames*sizeof(stBoneFrame), fp);

		/*
		sprintf(buf, "\n~~~~BoneFrames: %d~~~~~~", pHeader->numBoneFrames);
		abc(buf);
		for (int i=0; i<pHeader->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, pHeader->ofsTagStart, SEEK_SET);
		fread(pTags, 1, pHeader->numBoneFrames * pHeader->numTags*sizeof(stTag), fp);

		/*
		sprintf(buf, "\n~~~~Tags: %d~~~~~~", pHeader->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 = pHeader->ofMeshSurfaces;

		for (int j=0; j<pHeader->numMeshes; j++)
		{
			stMesh * pMesh = &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 * pMeshHeader->numMeshFrames];
				fread( pMesh->pVertex, 1, pMeshHeader->numMeshFrames * 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(..)


	bool ReleaseModel(stM3DModel* pMod)
	{
		stMD3Header * pHeader = &pMod->m_md3Header;
		stMesh * pMeshes = pMod->m_pMeshes;

		for (int j=0; j<pHeader->numMeshes; j++)
		{
			stMesh * pMesh = &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;
		}

		stBoneFrame* pBoneFrame = pMod->m_pBoneFrame;
		stTag* pTags			= pMod->m_pTags;
		stMesh *pMshes			= pMod->m_pMeshes;

		delete[] pBoneFrame;
		delete[] pTags;
		delete[] pMshes;

		pBoneFrame = NULL;
		pTags		 = NULL;
		pMshes	 = NULL;

		return true;
	}
};


 

 

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

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



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

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

// These few lines are compiler directives that include the DirectX library's
// during linking.  You could instead inside visual studio goto Project->
// settings menu and under the "Link" tabe add the necessary librarys instead.
#pragma comment(lib, "D3d8.lib") //directX 8
#pragma comment(lib, "D3dx8.lib")
#include <d3dx8.h>


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

#include "md3.h"

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



LPDIRECT3D8 g_pD3D = NULL;
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;

static CMD3 md3;

void init(HWND hWnd)
{
    //First of all, create the main D3D object. If it is created successfully we 
    //should get a pointer to an IDirect3D8 interface.
    g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

    //Get the current display mode
    D3DDISPLAYMODE d3ddm;
    g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

    //Create a structure to hold the settings for our device
    D3DPRESENT_PARAMETERS d3dpp; 
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    //Fill the structure. 
    //We want our program to be windowed, and set the back buffer to a format
    //that matches our current display mode
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = d3ddm.Format;

	//For depth buffering (e.g.) the z-buffer
	d3dpp.BackBufferCount=1;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.EnableAutoDepthStencil = TRUE;

    //Create a Direct3D device.
    g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESS SING, &d3dpp, &g_pD3DDevice);
 
	/*
	g_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
	g_pD3DDevice->SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_TEXTURE);
	g_pD3DDevice->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	g_pD3DDevice->SetTextureStageState(0,D3DTSS_MAGFILTER,D3DTEXF_LINEAR);
	g_pD3DDevice->SetTextureStageState(0,D3DTSS_MAGFILTER,D3DTEXF_LINEAR);

	//Turn on back face culling. This is becuase we want to hide the back of our polygons
    //g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);

	//Turn off lighting becuase we are specifying that our vertices have colour
    g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

	//Turn on z-buffering
	g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
	*/
	
	md3.Create();

}

void de_init()
{
	md3.Release();

	g_pD3DDevice->Release();
	g_pD3DDevice = NULL;

	g_pD3D->Release();
	g_pD3D = NULL;
}


void set_camera()
{
    // [1] D3DTS_VIEW
    D3DXMATRIX v;
    g_pD3DDevice->SetTransform(D3DTS_VIEW, D3DXMatrixLookAtLH(&v, &D3DXVECTOR3(0,0,-50), 
                                                                  &D3DXVECTOR3(0,0,0), 
                                                                 &D3DXVECTOR3(0,1,0)));  
    // [2] D3DTS_PROJECTION
    D3DXMATRIX p;
    g_pD3DDevice->SetTransform( D3DTS_PROJECTION, D3DXMatrixPerspectiveFovLH( &p,  
		                        D3DX_PI/4,  1.0f, 1.0f, 500.0f));
};



// Well if we need to do any drawing or rendering triangles we can do it in here!
void Render()
{
	if(!g_pD3DDevice)return;

	Sleep(100);

	// Some stuff to setup or graphics card!
	//Turn off lighting becuase we are specifying that our vertices have colour
    g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

	g_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
	g_pD3DDevice->SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_DIFFUSE);

	

	// Clear the back buffer to a blue color
	g_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
	g_pD3DDevice->BeginScene();

	// Just to rotate our shape!  See it rotate.
	//static float angle = 0.0f;
    //angle += 0.001f;
	//~~~*~~~~ Create a matrix
	//D3DXMATRIX mx;
	//~~~*~~~~ Do somthing to our empty matrix.
	//D3DXMatrixRotationY(&mx, angle ); // angle in radians...eg. 1 degree = PI/180
	//~~~*~~~~ Use the matrix! No use having a matrix if we don't use it!
	//g_pD3DDevice->SetTransform(D3DTS_WORLD, &mx);

	D3DXMATRIX tx;
	D3DXMatrixTranslation(&tx, 0, 0, 0);
	g_pD3DDevice->SetTransform(D3DTS_WORLD, &tx);

	set_camera();


	md3.Update(0.1f);
	md3.Render();


	g_pD3DDevice->EndScene();
	// After rendering the scene we display it.
	g_pD3DDevice->Present( NULL, NULL, NULL, NULL );


}

void mainloop()
{
	Render();
}


/***************************************************************************/
/*                                                                         */
/* Handle all messages for the main window here                            */
/*                                                                         */
/***************************************************************************/
long _stdcall MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if(uMsg == WM_DESTROY)
    {
		// inside init.cpp we destroy any directx memory we allocated etc, 
		// tidy up before leaving.
		// TIDY UP DIRECTX HERE BEFORE EXITING HERE!!!!!!!!!!!!!!!!!!!!!!!!!
		de_init();
        PostQuitMessage(0);
    }
    return (long)DefWindowProc(hWnd, uMsg, wParam, lParam);
}

/***************************************************************************/
/*                                                                         */
/* Program entry point.                                                    */
/*                                                                         */
/***************************************************************************/
int _stdcall WinMain(HINSTANCE i, HINSTANCE, char* k, int) 
{
    MSG msg;
	char szname[] = "DirectX3D - www.xbdev.net - Quake3 MD3";
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      szname, NULL };
    RegisterClassEx( &wc );
    HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW,
					szname, szname, 
					WS_OVERLAPPEDWINDOW,//for fullscreen make into WS_POPUP
					50, 50, 500,500, //for full screen GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
					GetDesktopWindow(), NULL, wc.hInstance, NULL);
    
    // Initilise or directX code here!
	// INIT OUR DIRECTX ONCE HERE AT THE START HERE!!!!!!!!!!!!!!!!!!!!!!!!!
    init(hWnd);

	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);	


    // Message loop. Note that this has been modified to allow
    // us to execute if no messages are being processed.
    while(1)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            if (!GetMessage(&msg, NULL, 0, 0))
                break;
            DispatchMessage(&msg);
        }
        
        // Idle-time processing - call our loop function in game.cpp
		// CALLING OUR DIRECTX CODE IN GAME.CPP FROM HERE!!!!!!!!!!!!!!!!!
        mainloop();
    }
    return 0;
}
//---------------------------------------------------------------------------



 
 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.