OpenGL:Tutorials:Tutorial Framework:MD2Animation:SourceCode
From GPWiki
The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server. However, the GPWiki forums are still active! Come say hello. This is the source for the OpenGL Framework MD2 Demo. See OpenGL:Tutorials:Tutorial_Framework for further information. [edit] 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; } [edit] MD2Loader.h#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 [edit] MD2Loader.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); } |


