OpenGL:Tutorials:Tutorial Framework:MD2Animation:SourceCode

From GPWiki
Jump to: navigation, search

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