OpenGL:Tutorials:Tutorial Framework:MD2Animation:SourceCode
From GPWiki
This is the source for the OpenGL Framework MD2 Demo. See OpenGL:Tutorials:Tutorial_Framework for further information.
Render.cpp
#include "Framework.h" #include <iostream> #include <math.h> #include <time.h> #include "tga.h" #include "MD2Loader.h" using namespace std; #define FRAMEDELAY 50 #define TEXTURECOUNT 32 // Function declarations bool LoadTexture(char *TexName, GLuint TexHandle); // Here we go! void Render(void) { MD2Obj Obj; // Our object class GLuint Texture[TEXTURECOUNT]; // Texture store float ViewRotate=0.0f; // A few vars to handle view rotation, animation and time base values long Time1,Time2,Ticks,NextFrame; int Frames,CurFrame=0; char Text[256]; // General purpose string GLfloat Ambient[] = { 0.1f, 0.1f, 0.1f, 1.0f}; // Ambient light value GLfloat Diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f}; // Diffuse light value GLfloat Position[] = {10.0f, 60.0f, 10.0f, 1.0f}; // Light position // Allocate all textures in one go glGenTextures(32,Texture); // Load our Object if(Obj.Load("..\\Obj\\WalkMech.md2")) { RunLevel=0; cout<<"Unable to load Object!\n"; return; } // Find out how many frames we have Frames=Obj.GetFrameCount(); // Load a texture for our object strcpy(Text,"..\\Obj\\"); strcat(Text,Obj.GetTexName()); if(LoadTexture(Text,Texture[0])) Obj.SetTexture(Texture[0]); // Background color glClearColor(0.0f,0.0f,0.0f,1.0f); // Setup our screen glMatrixMode(GL_PROJECTION); glViewport(0,0,800,600); glLoadIdentity(); glFrustum(-.5f,.5f,-.5f*(600.0f/800.0f),.5f*(600.0f/800.0f),1.0f,500.0f); glMatrixMode(GL_MODELVIEW); // Enable z-buffer glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); // Enable Lighting glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_AMBIENT, Ambient); // Set the ambient lighting value for Light0 glLightfv(GL_LIGHT0, GL_DIFFUSE, Diffuse); // Set the diffuse lighting value for Light0 // Set up TBM Time1=Time2=clock(); NextFrame=Time1 + FRAMEDELAY; // Main Loop while(RunLevel) { // Esc quits if(Keys[VK_ESCAPE]) RunLevel=0; // Reset view glLoadIdentity(); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // Get ticks since last frame Time2=clock(); Ticks=Time2-Time1; Time1=Time2; // Set up the view glTranslatef(0.0f,-15.0f,-80.0f); glRotatef(-60.0f,1.0f,0.0f,0.0f); // Set Light Position glLightfv(GL_LIGHT0,GL_POSITION,Position); // Set position for the light // Rotate view glRotatef(ViewRotate,0.0f,0.0f,1.0f); // Draw our Object Obj.Draw(CurFrame); // Advance the frame counter if(Time1>NextFrame) { CurFrame++; NextFrame=Time1 + FRAMEDELAY; if(CurFrame>=Frames) CurFrame=0; } // Show our scene FlipBuffers(); // Rotate view for next frame ViewRotate+=(Ticks/50.0f); } // Clean up textures glDeleteTextures(TEXTURECOUNT,Texture); } // Load a TGA texture bool LoadTexture(char *TexName, GLuint TexHandle) { TGAImg Img; // Image loader // Load our Texture if(Img.Load(TexName)!=IMG_OK) return false; glBindTexture(GL_TEXTURE_2D,TexHandle); // Set our Tex handle as current // Create the texture if(Img.GetBPP()==24) glTexImage2D(GL_TEXTURE_2D,0,3,Img.GetWidth(),Img.GetHeight(),0,GL_RGB,GL_UNSIGNED_BYTE,Img.GetImg()); else if(Img.GetBPP()==32) glTexImage2D(GL_TEXTURE_2D,0,4,Img.GetWidth(),Img.GetHeight(),0,GL_RGBA,GL_UNSIGNED_BYTE,Img.GetImg()); else return false; // Specify filtering and edge actions glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); return true; } </code> == MD2Loader.h== <code type="cpp"> #ifndef _MD2LOADER_H #define _MD2LOADER_H #include <iostream> #include <fstream> #include <time.h> #include <memory.h> #include <math.h> #include "framework.h" #define MD2_OK 0x0 #define MD2_ERR_MEM 0x1 #define MD2_ERR_FILE 0x2 #define MD2_ERR_FORMAT 0x4 struct MD2Header { int ID; // File Type - Normally 'IPD2' int Version; int TexWidth; // Texture width int TexHeight; // Texture height int FrameSize; // Size for frames in bytes int nTextures; // Number of textures int nVertices; // Number of vertices int nTexCoords; // Number of UVs int nTriangles; // Number of polys int nGLCmd; // Number of GL Commmands int nFrames; // Number of frames int TexOffset; // Offset to texture name(s) int UVOffset; // Offset to UV data int FaceOffset; // Offset to poly data int FrameOffset;// Offset to first frame int GLCmdOffset;// Offset to GL Cmds int EOFOffset; // Size of file }; struct MD2FrameInfo { float Scale[3]; float Translate[3]; char Name[16]; }; struct MD2Face { short p1,p2,p3; short uv1,uv2,uv3; }; struct MD2Vtx { unsigned char Vtx[3]; unsigned char lNorm; }; struct Mesh_Vtx { float x,y,z; }; struct Mesh_UV { float u,v; }; struct MD2Frame { Mesh_Vtx *Vtx; Mesh_Vtx *Norm; }; struct MD2TexCoord { short u,v; }; class MD2Obj { public: MD2Obj(); ~MD2Obj(); int Load(char* filename); int GetFrameCount(); char* GetTexName(); void SetTexture(GLuint TexNum); void Draw(int Frame); private: int nFrames,nTri,nVtx,nUV; MD2Face *Face; MD2Frame *frame; Mesh_UV *UV; char TexName[64]; GLuint TexID; void CalcNormal(Mesh_Vtx v1,Mesh_Vtx v2,Mesh_Vtx v3,Mesh_Vtx* Result); }; #endif </code> == MD2Loader.cpp== <code type="cpp"> #include "MD2Loader.h" MD2Obj::MD2Obj() { // Terminate Arrays Face=NULL; frame=NULL; UV=NULL; TexName[0]=NULL; TexID=0; } MD2Obj::~MD2Obj() { // Free Arrays if(Face) { delete [] Face; Face=NULL; } if(frame) { delete [] frame; frame=NULL; } if(UV) { delete [] UV; UV=NULL; } } int MD2Obj::Load(char *filename) { using namespace std; ifstream fIn; unsigned long fSize; unsigned char *data=NULL; MD2Header Head; long FrameLoop,ItemLoop; MD2Vtx *vtx; MD2TexCoord *MD2_UV; MD2FrameInfo FrameInfo; // Clear any existing data if(Face) { delete [] Face; Face=NULL; } if(frame) { delete [] frame; frame=NULL; } if(UV) { delete [] UV; UV=NULL; } // Open the specified file fIn.open(filename,ios::binary); if(fIn==NULL) return MD2_ERR_FILE; // Get file size fIn.seekg(0,ios_base::end); fSize=fIn.tellg(); fIn.seekg(0,ios_base::beg); // Allocate some space data=new unsigned char[fSize]; if(data==NULL) { fIn.close(); return MD2_ERR_MEM; } // Read the file into memory fIn.read((char*)data,fSize); fIn.close(); // Get Header data memcpy(&Head,data,68); // Dump info about object cout<<"ID - "<<data[0]<<data[1]<<data[2]<<data[3]<<"\n"; cout<<"Version - "<<Head.Version<<"\n"; cout<<"Tex Width - "<<Head.TexWidth<<"\n"; cout<<"Tex Height - "<<Head.TexHeight<<"\n"; cout<<"Frame Size - "<<Head.FrameSize<<"\n"; cout<<"Textures - "<<Head.nTextures<<"\n"; cout<<"Vertices - "<<Head.nVertices<<"\n"; cout<<"UVs - "<<Head.nTexCoords<<"\n"; cout<<"Faces - "<<Head.nTriangles<<"\n"; cout<<"GL cmds - "<<Head.nGLCmd<<"\n"; cout<<"Frames - "<<Head.nFrames<<"\n"; cout<<"Skin Offset - "<<Head.TexOffset<<"\n"; cout<<"UV Offset - "<<Head.UVOffset<<"\n"; cout<<"Face Offset - "<<Head.FaceOffset<<"\n"; cout<<"Frame Offset - "<<Head.FrameOffset<<"\n"; cout<<"GL Offset - "<<Head.GLCmdOffset<<"\n"; cout<<"Filesize - "<<Head.EOFOffset<<"\n"; // A few checks to ensure this is an MD2 file if(Head.ID!=844121161) // chars 'IDP2' as a dword return MD2_ERR_FORMAT; if(Head.Version!=8) return MD2_ERR_FORMAT; if(Head.EOFOffset!=fSize) return MD2_ERR_FORMAT; // Grab the info we'll need later nFrames=Head.nFrames; nTri=Head.nTriangles; nVtx=Head.nVertices; nUV=Head.nTexCoords; // Allocate arrays // Frames frame=new MD2Frame[nFrames]; if(!frame) { delete [] data; return MD2_ERR_MEM; } // Frame components for(FrameLoop=0;FrameLoop!=nFrames;++FrameLoop) { frame[FrameLoop].Vtx = new Mesh_Vtx[nVtx]; frame[FrameLoop].Norm= new Mesh_Vtx[nVtx]; if(!frame[FrameLoop].Vtx || !frame[FrameLoop].Norm) { delete [] data; return MD2_ERR_MEM; } } // MD2 vtx buffer vtx = new MD2Vtx[Head.nVertices]; // Faces Face = new MD2Face[Head.nTriangles]; // UVs UV = new Mesh_UV[nUV]; // MD2 UV buffer MD2_UV = new MD2TexCoord[Head.nTexCoords]; // Check we've created the arrays if(!vtx || !Face || !UV || !MD2_UV) { delete [] data; return MD2_ERR_MEM; } // Extract and convert info from file // Read first texture name if(Head.nTextures>0) { memcpy(TexName,&data[Head.TexOffset],64); cout<<"Texture Name - "<<TexName<<"\n"; } // Read face data memcpy(Face,&data[Head.FaceOffset],Head.nTriangles*sizeof(MD2Face)); // Read MD2 UV data memcpy(MD2_UV,&data[Head.UVOffset],Head.nTexCoords*sizeof(MD2TexCoord)); // Convert into regular UVs for(ItemLoop=0;ItemLoop!=nUV;++ItemLoop) { UV[ItemLoop].u=((float)MD2_UV[ItemLoop].u)/Head.TexWidth; UV[ItemLoop].v=((float)MD2_UV[ItemLoop].v)/Head.TexHeight; } // Finished with MD2 style UVs delete [] MD2_UV; // Load frame vertex info for(FrameLoop=0;FrameLoop!=nFrames;++FrameLoop) { // Get frame conversion data memcpy(&FrameInfo,&data[Head.FrameOffset + (Head.FrameSize * FrameLoop)],sizeof(FrameInfo)); // Read MD2 style vertex data memcpy(vtx,&data[Head.FrameOffset + (Head.FrameSize * FrameLoop) + sizeof(FrameInfo)],nVtx * sizeof(MD2Vtx)); // Convert vertices for(ItemLoop=0;ItemLoop!=nVtx;++ItemLoop) { frame[FrameLoop].Vtx[ItemLoop].x=(vtx[ItemLoop].Vtx[0] * FrameInfo.Scale[0])+FrameInfo.Translate[0]; frame[FrameLoop].Vtx[ItemLoop].y=(vtx[ItemLoop].Vtx[1] * FrameInfo.Scale[0])+FrameInfo.Translate[1]; frame[FrameLoop].Vtx[ItemLoop].z=(vtx[ItemLoop].Vtx[2] * FrameInfo.Scale[0])+FrameInfo.Translate[2]; } } // Finished with vtx and filedata delete [] vtx; delete [] data; // Calc normals for each frane for(FrameLoop=0;FrameLoop!=nFrames;FrameLoop++) { // Calc face normal for(ItemLoop=0;ItemLoop!=nTri;ItemLoop++) { CalcNormal(frame[FrameLoop].Vtx[Face[ItemLoop].p1], frame[FrameLoop].Vtx[Face[ItemLoop].p2], frame[FrameLoop].Vtx[Face[ItemLoop].p3], &frame[FrameLoop].Norm[ItemLoop]); } } return MD2_OK; } // A few Get() and Set()s to access the private data members int MD2Obj::GetFrameCount() { return nFrames; } char* MD2Obj::GetTexName() { if(TexName) return TexName; else return NULL; } void MD2Obj::SetTexture(GLuint TexNum) { TexID=TexNum; } // Draw the specified frame void MD2Obj::Draw(int Frame) { int Part; // Limit frame range if(Frame>=nFrames) Frame=0; glBindTexture(GL_TEXTURE_2D,TexID); glBegin(GL_TRIANGLES); for(Part=0;Part<nTri;++Part) { glNormal3f(frame[Frame].Norm[Part].x,frame[Frame].Norm[Part].y,frame[Frame].Norm[Part].z); glTexCoord2f(UV[Face[Part].uv1].u,UV[Face[Part].uv1].v); glVertex3f(frame[Frame].Vtx[Face[Part].p1].x,frame[Frame].Vtx[Face[Part].p1].y,frame[Frame].Vtx[Face[Part].p1].z); glTexCoord2f(UV[Face[Part].uv2].u,UV[Face[Part].uv2].v); glVertex3f(frame[Frame].Vtx[Face[Part].p2].x,frame[Frame].Vtx[Face[Part].p2].y,frame[Frame].Vtx[Face[Part].p2].z); glTexCoord2f(UV[Face[Part].uv3].u,UV[Face[Part].uv3].v); glVertex3f(frame[Frame].Vtx[Face[Part].p3].x,frame[Frame].Vtx[Face[Part].p3].y,frame[Frame].Vtx[Face[Part].p3].z); } glEnd(); } void MD2Obj::CalcNormal(Mesh_Vtx v1,Mesh_Vtx v2,Mesh_Vtx v3,Mesh_Vtx* Result) { double v1x,v1y,v1z,v2x,v2y,v2z; double nx,ny,nz; double vLen; // Calculate vectors v1x = v1.x - v2.x; v1y = v1.y - v2.y; v1z = v1.z - v2.z; v2x = v2.x - v3.x; v2y = v2.y - v3.y; v2z = v2.z - v3.z; // Get cross product of vectors nx = (v1y * v2z) - (v1z * v2y); ny = (v1z * v2x) - (v1x * v2z); nz = (v1x * v2y) - (v1y * v2x); // Normalise final vector vLen = sqrt( (nx * nx) + (ny * ny) + (nz * nz) ); Result->x = (float) (nx / vLen); Result->y = (float) (ny / vLen); Result->z = (float) (nz / vLen); }