Hola a todos..
Bienvenidos a una nueva entrega de como hacer un game engine desde 0 y que nuestros amigos no se avergüencen de nosotros...
El tema de hoy es el model manager, o dicho en un idioma más llano, como hacer para que nuestro game engine pueda cargar y mostrar modelos 3D.
Los modelos 3D no son más ristras de triangulos y coordenadas (iguales que las que usamos en el ejemplo capitulo 3 para el Graphics 3D manager) almancenados con un cierto formato en un archivo. Formatos de modelos 3D los hay cientos pero uno de los más usados (ahora ya un poco anticuado, pero sigue haciendo su cometido) es el formato MD2 usado en el quake 2.
El model manager que implementaremos se basará en el cargador de modelos MD2 que aparece aqui . En el futuro implementaremos otros formatos, pero por ahora, ya nos sirve este para hacer pruebas. Esta vez hay varios archivos añadidos.. por un lado el ModelManager (.cpp y .h) y por otro el md2 (.h y .cpp) junto con el anorms.h y anormtab.h . Se tendrá que incluir al proyecto un .txt de carga adicional para cargar los modelos que queramos. Adicionalmente hay el linkado de este nuevo manager al engine (init y deinit) y el código de prueba.
Realmente hay poca cosa implementada, carga y pintado, pero por ahora ya nos sirve para ir practicando, si queréis jugar con esto e implementar nuevas características recordad que me las podéis enviar para compartirlas con el resto.
Sin más os dejo con el código...
En el render del Graphics3DManager incluid justo antes de la rotación de la piramide:
//Guardamos el estado de las matrices de pintado
glPushMatrix();
glTranslatef(1.0f,-1.0f,1.0f);
glScalef(0.05f,0.05f,0.05f);   //Hacemos que el modelo se vea un “poco” más pequeño.
ModelManager::singleton().Draw("model"); //Pintamos el modelo que hemos cargado previamente.
glPopMatrix();
//Devolvemos el estado de las matrices al punto de inicio
Dentro del mismo Graphics3DManager:
[...]
cout << "Inicializamos el Graphics 3D Manager\n";
ModelManager::singleton().Init(argc,argv);
[…]
void Graphics3DManager::DeInit()
{
 cout << "Desinicializamos el Graphics 3D Manager\n";
 ModelManager::singleton().DeInit();
}
ModelManager.cpp
/**************************************************************************************************/
//  Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
//  para el blog LordPakus (http://lordpakus.blogspot.com/).  
//  Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/
/**************************************************************************************************/
// ModelManager.cpp : Código del gestor de modelos 3D del game engine
//  El código del cargador de modelos MD2 se obtiene de http://tfc.duke.free.fr/coding/md2-specs-en.html
/**************************************************************************************************/
#include "ModelManager.h"
#include <iostream>  //Usada para imprimir por consola
using namespace std;
//Instancia única del sound manager
ModelManager ModelManager::instance;
ModelManager::ModelManager()
{
}
ModelManager::~ModelManager()
{
}
//Devolvemos el puntero al singleton
ModelManager& ModelManager::singleton()
{
 return instance;
}
//Sounds functions
void ModelManager::Init(int* argc, char* argv[])
{
 char cad[256]; //Cadena donde guardaremos cada linea que leamos
 char id[256]; //Cadena donde guardaremos el identificador de cada recurso 
 char ruta[256]; //Cadena donde guardaremos la ruta de cada recurso
 int num_id;  //número que nos asigna la función de carga del recurso.... un -1 significa que no lo ha podido cargar
 cout << "Inicializamos el model manager\n";
 //alutInit(argc, argv) ;
 filemanager.Init("Data\\LoadModel.txt");
 //Todo este código tendrá que ir al filemanager, pero por el momento lo dejamos aquí
 //Leemos cada linea del archivo
 while( filemanager.GetLine(cad) )
 {
  for( int i = 0 ; i < strlen(cad); ++i)
  {
   if ( cad[i] == '=' )
   {
    cad[i]='\0';
    sprintf(id,"%s",cad);
    sprintf(ruta,"%s",&cad[i+1]);
    i = 257;
   }
  }
  num_id = LoadModel(ruta);    //Cargamos el recurso
  cout << "Intentamos cargar modelo con ruta:" << ruta << "\n";
  if (num_id == -1)      //Si el recurso no se ha podido cargar, vamos a cargar el siguiente recurso
   continue;
  cout << "Modelo cargado con identificadores:" << id << " " << num_id << "\n";  
  sprintf( loaded[ num_id ] ,"%s" , id ); //Nos guardamos la referencia al recurso
 }
}
void ModelManager::DeInit()
{
 cout << "Desinicializamos el model manager\n";
 filemanager.DeInit();
}
int ModelManager::LoadModel(char cad[])
{
 static int id = 0;
 model[id++].LoadModel(cad);
 return (id - 1);
}
void ModelManager::Draw( char id[] )
{
 int i; //Iterador
 int m = 0;
 static int t;
 for ( i = 0 ; i < MAX_ELEMENTS_LOADED ; ++i )  //Para cada elemento cargado
 {
  if( !strcmp(loaded[i],id) )     //Hemos encontrado la cadena que nos interesa
  {
   m = i;
   i = MAX_ELEMENTS_LOADED+1;
   continue;  
  }
 }
 if ( i == (MAX_ELEMENTS_LOADED + 1))
  return;
 model[m].DrawModel((t++/10));  //Aqui tendremos que ponerle el tiempo de animacion
}
ModelManager.h
/**************************************************************************************************/
//  Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
//  para el blog LordPakus (http://lordpakus.blogspot.com/).  
//  Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/
/**************************************************************************************************/
// ModelManager.h : Código del gestor de texturas del game engine
//  El código del cargador de modelos MD2 se obtiene de http://tfc.duke.free.fr/coding/md2-specs-en.html
/**************************************************************************************************/
#ifndef __ModelManager__
#define __ModelManager__
#include "MyGL.h"
#include "FileManager.h" //Sirve para cargar el archivo de loading de texturas
#include "md2.h"   //Sirve para cargar modelos MD2
#define MAX_ELEMENTS_LOADED 256 //por ahora hardcodeado, lo arreglaremos en algún momento.
class ModelManager  
{
private:
 // Constructor y destructor de la clase
 static ModelManager instance;
 ModelManager();
 ~ModelManager();
public:
 static ModelManager& singleton();
public:
 //Funcion para inicializar el motor de sonido
 void Init(int* argc, char* argv[]);
 //Funcion para desinicializar el motor de sonido
 void DeInit();
 //Función de pintado de modelo 3D.
 void Draw( char id[] );
private:
 int LoadModel(char cad[]);
 FileManager filemanager;
 char loaded[MAX_ELEMENTS_LOADED][256]; //Matriz donde guardamos los identificadores de modelo y sus indices.
 //Modelos MD2
 CMD2Model model[MAX_ELEMENTS_LOADED];
};
#endif // __ModelManager__
md2.cpp
//
// md2.cpp - source file
//
// David Henry - tfc_duke@hotmail.com
//
#include <GL/glut.h>
#include <fstream>
#include "md2.h"
//#include "texture.h"
//#include <SOIL/SOIL.h>
// precalculated normal vectors
vec3_t CMD2Model::anorms[ NUMVERTEXNORMALS ] = {
#include "anorms.h"
};
// precalculated dot product results
float CMD2Model::anorms_dots[ SHADEDOT_QUANT ][256] = {
#include "anormtab.h"
};
static float *shadedots = CMD2Model::anorms_dots[0];
static vec3_t lcolor;
/////////////////////////////////////////////////
vec3_t   g_lightcolor = { 1.0, 1.0, 1.0 };
int    g_ambientlight = 32;
float   g_shadelight = 128;
float   g_angle   = 0.0;
/////////////////////////////////////////////////
// ----------------------------------------------
// constructor - reset all data.
// ----------------------------------------------
CMD2Model::CMD2Model( void )
{
 m_vertices  = 0;
 m_glcmds  = 0;
 m_lightnormals = 0;
 num_frames  = 0;
 num_xyz   = 0;
 num_glcmds  = 0;
 //m_texid   = 0;
 m_scale   = 1.0;
 SetAnim( 0 );
}
// ----------------------------------------------
// destructeur - free allocated memory.
// ----------------------------------------------
CMD2Model::~CMD2Model( void )
{
 delete [] m_vertices;
 delete [] m_glcmds;
 delete [] m_lightnormals;
}
// ----------------------------------------------
// LoadModel() - load model from file.
// ----------------------------------------------
bool CMD2Model::LoadModel( const char *filename )
{
 std::ifstream file;   // file stream
 md2_t   header;   // md2 header
 char   *buffer;  // buffer storing frame data
 frame_t   *frame;   // temporary variable
 vec3_t   *ptrverts;  // pointer on m_vertices
 int    *ptrnormals; // pointer on m_lightnormals
 // try to open filename
 file.open( filename, std::ios::in | std::ios::binary );
 if( file.fail() )
  return false;
 // read header file
 file.read( (char *)&header, sizeof( md2_t ) );
 /////////////////////////////////////////////
 //  verify that this is a MD2 file
 // check for the ident and the version number
 if( (header.ident != MD2_IDENT) && (header.version != MD2_VERSION) )
 {
  // this is not a MD2 model
  file.close();
  return false;
 }
 /////////////////////////////////////////////
 // initialize member variables
 num_frames = header.num_frames;
 num_xyz  = header.num_xyz;
 num_glcmds = header.num_glcmds;
 // allocate memory
 m_vertices  = new vec3_t[ num_xyz * num_frames ];
 m_glcmds  = new int[ num_glcmds ];
 m_lightnormals = new int[ num_xyz * num_frames ];
 buffer   = new char[ num_frames * header.framesize ];
 /////////////////////////////////////////////
 //   reading file data
 // read frame data...
 file.seekg( header.ofs_frames, std::ios::beg );
 file.read( (char *)buffer, num_frames * header.framesize );
 // read opengl commands...
 file.seekg( header.ofs_glcmds, std::ios::beg );
 file.read( (char *)m_glcmds, num_glcmds * sizeof( int ) );
 /////////////////////////////////////////////
 // vertex array initialization
 for( int j = 0; j < num_frames; j++ )
 {
  // ajust pointers
  frame  = (frame_t *)&buffer[ header.framesize * j ];
  ptrverts = &m_vertices[ num_xyz * j ];
  ptrnormals = &m_lightnormals[ num_xyz * j ];
  for( int i = 0; i < num_xyz; i++ )
  {
   ptrverts[i][0] = (frame->verts[i].v[0] * frame->scale[0]) + frame->translate[0];
   ptrverts[i][1] = (frame->verts[i].v[1] * frame->scale[1]) + frame->translate[1];
   ptrverts[i][2] = (frame->verts[i].v[2] * frame->scale[2]) + frame->translate[2];
   ptrnormals[i] = frame->verts[i].lightnormalindex;
  }
 }
 // free buffer's memory
 delete [] buffer;
 // close the file and return
 file.close();
 return true;
}
// ----------------------------------------------
// LoadSkin() - load model texture.
// ----------------------------------------------
// We need to define this for glTexParameteri()
#define GL_CLAMP_TO_EDGE 0x812F      // This is for our skybox textures
bool CMD2Model::LoadSkin(char *filename )
{
 //m_texid = LoadTexture( filename );
 //m_texid.Load(filename ,GL_RGB,GL_CLAMP_TO_EDGE,GL_CLAMP_TO_EDGE,GL_LINEAR,GL_LINEAR,true); 
 //m_texid.Load(filename ,GL_RGB,GL_REPEAT,GL_REPEAT,GL_LINEAR,GL_LINEAR,true); 
 //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_REPEAT );
 //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
 //return (m_texid != LoadTexture( "default" ));
 //m_texid = SOIL_load_OGL_texture(
 //      filename,
 //      SOIL_LOAD_AUTO,
 //      SOIL_CREATE_NEW_ID,
 //      SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
 return true;
}
// ----------------------------------------------
// DrawModel() - draw the model.
// ----------------------------------------------
void CMD2Model::DrawModel( float time )
{
 // animate. calculate current frame and next frame
 if( time > 0.0 )
  Animate( time );
 glPushMatrix();
  // rotate the model
  glRotatef( -90.0, 1.0, 0.0, 0.0 );
  glRotatef( -90.0, 0.0, 0.0, 1.0 );
  // render it on the screen
  RenderFrame();
 glPopMatrix();
}
void CMD2Model::NoDrawModel( float time )
{
 // animate. calculate current frame and next frame
 if( time > 0.0 )
  Animate( time );
}
void CMD2Model::DrawStaticModel(int anim,int frame)
{
 m_anim.curr_frame = frame;
 m_anim.next_frame = frame;
 SetAnim(anim);
 DrawModel(1.0f);
}
// ----------------------------------------------
// Animate() - calculate the current frame, next
// frame and interpolation percent.
// ----------------------------------------------
void CMD2Model::Animate( float time )
{
 m_anim.curr_time = time;
 // calculate current and next frames
 if( m_anim.curr_time - m_anim.old_time > (1.0 / m_anim.fps) )
 {
  m_anim.curr_frame = m_anim.next_frame;
  m_anim.next_frame++;
  if( m_anim.next_frame > m_anim.endframe )
   m_anim.next_frame = m_anim.startframe;
  m_anim.old_time = m_anim.curr_time;
 }
 // prevent having a current/next frame greater
 // than the total number of frames...
 if( m_anim.curr_frame > (num_frames - 1) )
  m_anim.curr_frame = 0;
 if( m_anim.next_frame > (num_frames - 1) )
  m_anim.next_frame = 0;
 m_anim.interpol = m_anim.fps * (m_anim.curr_time - m_anim.old_time);
}
void   CMD2Model::GetFrame(float time, int *a, int *f)
{
 //Animate(time);
 *a = m_anim.type;
 *f = m_anim.curr_frame;
 printf("tipo:%d frame:%d\n",*a,*f);
}
void CMD2Model::SetFrame(int frame)
{
 m_anim.curr_frame = frame;
}
// ----------------------------------------------
// ProcessLighting() - process all lighting calculus.
// ----------------------------------------------
void CMD2Model::ProcessLighting( void )
{
 float lightvar = (float)((g_shadelight + g_ambientlight)/256.0);
 lcolor[0] = g_lightcolor[0] * lightvar;
 lcolor[1] = g_lightcolor[1] * lightvar;
 lcolor[2] = g_lightcolor[2] * lightvar;
 shadedots = anorms_dots[ ((int)(g_angle * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1) ];
}
// ----------------------------------------------
// Interpolate() - interpolate and scale vertices
// from the current and the next frame.
// ----------------------------------------------
void CMD2Model::Interpolate( vec3_t *vertlist )
{
 vec3_t *curr_v; // pointeur to current frame vertices
 vec3_t *next_v; // pointeur to next frame vertices
 // create current frame and next frame's vertex list
 // from the whole vertex list
 curr_v = &m_vertices[ num_xyz * m_anim.curr_frame ];
 next_v = &m_vertices[ num_xyz * m_anim.next_frame ];
 // interpolate and scale vertices to avoid ugly animation
 for( int i = 0; i < num_xyz ; i++ )
 {
  vertlist[i][0] = (curr_v[i][0] + m_anim.interpol * (next_v[i][0] - curr_v[i][0])) * m_scale;
  vertlist[i][1] = (curr_v[i][1] + m_anim.interpol * (next_v[i][1] - curr_v[i][1])) * m_scale;
  vertlist[i][2] = (curr_v[i][2] + m_anim.interpol * (next_v[i][2] - curr_v[i][2])) * m_scale;
 }
}
// ----------------------------------------------
// RenderFrame() - draw the current model frame
// using OpenGL commands.
// ----------------------------------------------
void CMD2Model::RenderFrame( void )
{
 static vec3_t vertlist[ MAX_MD2_VERTS ]; // interpolated vertices
 int    *ptricmds = m_glcmds;  // pointer on gl commands
 // reverse the orientation of front-facing
 // polygons because gl command list's triangles
 // have clockwise winding
 glPushAttrib( GL_POLYGON_BIT );
 glFrontFace( GL_CW );
 // enable backface culling
 glEnable( GL_CULL_FACE );
 glCullFace( GL_BACK );
 // process lighting
 ProcessLighting();
 // interpolate
 Interpolate( vertlist );
 glEnable( GL_TEXTURE_2D );
 // bind model's texture
 //glBindTexture( GL_TEXTURE_2D, m_texid.GetID());
 glBindTexture( GL_TEXTURE_2D, m_texid);
 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
 // draw each triangle!
 while( int i = *(ptricmds++) )
 {
  if( i < 0 )
  {
   glBegin( GL_TRIANGLE_FAN );
   i = -i;
  }
  else
  {
   glBegin( GL_TRIANGLE_STRIP );
  }
  for( /* nothing */; i > 0; i--, ptricmds += 3 )
  {
   // ptricmds[0] : texture coordinate s
   // ptricmds[1] : texture coordinate t
   // ptricmds[2] : vertex index to render
   float l = shadedots[ m_lightnormals[ ptricmds[2] ] ];
   // set the lighting color
   glColor3f( l * lcolor[0], l * lcolor[1], l * lcolor[2] );
   // parse texture coordinates
   glTexCoord2f( ((float *)ptricmds)[0], ((float *)ptricmds)[1] );
   // parse triangle's normal (for the lighting)
   // >>> only needed if using OpenGL lighting
   glNormal3fv( anorms[ m_lightnormals[ ptricmds[2] ] ] );
   // draw the vertex
   glVertex3fv( vertlist[ ptricmds[2] ] );
  }
  glEnd();
 }
 glDisable( GL_TEXTURE_2D );
 glDisable( GL_CULL_FACE );
 glPopAttrib();
}
// ----------------------------------------------
// RenderFrame() - draw one frame of the model
// using gl commands.
// ----------------------------------------------
void CMD2Model::DrawFrame( int frame )
{
 // set new animation parameters...
 m_anim.startframe = frame;
 m_anim.endframe  = frame;
 m_anim.next_frame = frame;
 m_anim.fps   = 1;
 m_anim.type   = -1;
 // draw the model
 DrawModel( 1.0 );
}
// ----------------------------------------------
// initialize the 21 MD2 model animations.
// ----------------------------------------------
anim_t CMD2Model::animlist[ 21 ] =  
{
 // first, last, fps
 {   0,  39,  9 }, // STAND
 {  40,  45, 10 }, // RUN
 {  46,  53, 10 }, // ATTACK
 {  54,  57,  7 }, // PAIN_A
 {  58,  61,  7 }, // PAIN_B
 {  62,  65,  7 }, // PAIN_C
 {  66,  71,  7 }, // JUMP
 {  72,  83,  7 }, // FLIP
 {  84,  94,  7 }, // SALUTE
 {  95, 111, 10 }, // FALLBACK
 { 112, 122,  7 }, // WAVE
 { 123, 134,  6 }, // POINT
 { 135, 153, 10 }, // CROUCH_STAND
 { 154, 159,  7 }, // CROUCH_WALK
 { 160, 168, 10 }, // CROUCH_ATTACK
 { 196, 172,  7 }, // CROUCH_PAIN
 { 173, 177,  5 }, // CROUCH_DEATH
 { 178, 183,  7 }, // DEATH_FALLBACK
 { 184, 189,  7 }, // DEATH_FALLFORWARD
 { 190, 197,  7 }, // DEATH_FALLBACKSLOW
 { 198, 198,  5 }, // BOOM
};
// ----------------------------------------------
// SetAnim() - initialize m_anim from the specified
// animation.
// ----------------------------------------------
void CMD2Model::SetAnim( int type )
{
 if( (type < 0) || (type > MAX_ANIMATIONS) )
  type = 0;
 m_anim.startframe = animlist[ type ].first_frame;
 m_anim.endframe  = animlist[ type ].last_frame;
 m_anim.next_frame = animlist[ type ].first_frame + 1;
 m_anim.fps   = animlist[ type ].fps;
 m_anim.type   = type;
}
md2.h
//
// md2.h - header file
//
// David Henry - tfc_duke@hotmail.com
//
#ifndef  __MD2_H
#define  __MD2_H
//#include "cTexture.h"
// number of precalculated normals
#define NUMVERTEXNORMALS  162
// precalculated normal vectors
#define SHADEDOT_QUANT   16
// magic number "IDP2" or 844121161
#define MD2_IDENT    (('2'<<24) + ('P'<<16) + ('D'<<8) + 'I')
// model version
#define MD2_VERSION    8
// maximum number of vertices for a MD2 model
#define MAX_MD2_VERTS   2048
typedef float vec3_t[3];
// md2 header
typedef struct
{
 int  ident;    // magic number. must be equal to "IPD2"
 int  version;   // md2 version. must be equal to 8
 int  skinwidth;   // width of the texture
 int  skinheight;   // height of the texture
 int  framesize;   // size of one frame in bytes
 int  num_skins;   // number of textures
 int  num_xyz;   // number of vertices
 int  num_st;    // number of texture coordinates
 int  num_tris;   // number of triangles
 int  num_glcmds;   // number of opengl commands
 int  num_frames;   // total number of frames
 int  ofs_skins;   // offset to skin names (64 bytes each)
 int  ofs_st;    // offset to s-t texture coordinates
 int  ofs_tris;   // offset to triangles
 int  ofs_frames;   // offset to frame data
 int  ofs_glcmds;   // offset to opengl commands
 int  ofs_end;   // offset to the end of file
} md2_t;
// vertex
typedef struct
{
 unsigned char v[3];    // compressed vertex' (x, y, z) coordinates
 unsigned char lightnormalindex; // index to a normal vector for the lighting
} vertex_t;
// frame
typedef struct
{
 float  scale[3];  // scale values
 float  translate[3]; // translation vector
 char  name[16];  // frame name
 vertex_t verts[1];  // first vertex of this frame
} frame_t;
// animation
typedef struct
{
 int  first_frame;   // first frame of the animation
 int  last_frame;    // number of frames
 int  fps;     // number of frames per second
} anim_t;
// status animation
typedef struct
{
 int  startframe;    // first frame
 int  endframe;    // last frame
 int  fps;     // frame per second for this animation
 float curr_time;    // current time
 float old_time;    // old time
 float interpol;    // percent of interpolation
 int  type;     // animation type
 int  curr_frame;    // current frame
 int  next_frame;    // next frame
} animState_t;
// animation list
typedef enum {
 STAND,
 RUN,
 ATTACK,
 PAIN_A,
 PAIN_B,
 PAIN_C,
 JUMP,
 FLIP,
 SALUTE,
 FALLBACK,
 WAVE,
 ANIM_POINT,
 CROUCH_STAND,
 CROUCH_WALK,
 CROUCH_ATTACK,
 CROUCH_PAIN,
 CROUCH_DEATH,  
 DEATH_FALLBACK,
 DEATH_FALLFORWARD,
 DEATH_FALLBACKSLOW,
 BOOM,
 MAX_ANIMATIONS
} animType_t;
// ==============================================
// CMD2Model - MD2 model class object.
// ==============================================
class CMD2Model
{
public:
 // constructor/destructor
 CMD2Model( void );
 ~CMD2Model( void );
 // functions
 bool LoadModel( const char *filename );
 bool LoadSkin( char *filename );
 void DrawModel( float time );
 void NoDrawModel( float time );
 void DrawFrame( int frame );
 void DrawStaticModel(int anim,int frame);
 void SetAnim( int type );
 void ScaleModel( float s ) { m_scale = s; }
 void    GetFrame(float time,int *a, int *f);
 void SetFrame(int frame);
private:
 void Animate( float time );
 void ProcessLighting( void );
 void Interpolate( vec3_t *vertlist );
 void RenderFrame( void );
public:
 // member variables
 static vec3_t anorms[ NUMVERTEXNORMALS ];
 static float anorms_dots[ SHADEDOT_QUANT ][256];
 static anim_t animlist[21];  // animation list
private:
 int    num_frames;   // number of frames
 int    num_xyz;   // number of vertices
 int    num_glcmds;   // number of opengl commands
 vec3_t   *m_vertices;  // vertex array
 int    *m_glcmds;   // opengl command array
 int    *m_lightnormals; // normal index array
 unsigned int m_texid;   // texture id
 //cTexture  m_texid;   // texture
 animState_t  m_anim;    // animation
 float   m_scale;   // scale value
};
#endif // __MD2_H
anorms.h y anormtab.h no los copio aquí por  que son solamente datos en bruto que no nos aportan información y que los podeis conseguir bajandoos mi proyecto o bien yendo al link que os he suminstrado arriba.
Aqui os dejo el video de como deberia quedaros la práctica:
Aqui os dejo el video de como deberia quedaros la práctica:
Sin más, espero que hayais aprendido y que os lo hayais pasado bien. Si os teneis alguna inquietud o duda, no tengais reparos en exponerla en público.
Nos vemos
 

 
 
 
Bueno, de momento estoy bloqueado en este capítulo, ya que obtengo el siguiente error:
ResponderEliminarc:\program files (x86)\microsoft visual studio 10.0\vc\include\stdlib.h(353): error C2381: 'exit' : redefinition; __declspec(noreturn) differs
c:\program files (x86)\microsoft sdks\windows\v7.0a\include\gl\glut.h(146) : see declaration of 'exit'
Problema resuelto. No se muy bien porque pero mirando en internet he descubierto que necesito poner #include delante de #include en el fichero md2.cpp.
ResponderEliminarEn el comentario anterior no aparecen bien los includes. serían:
ResponderEliminar#include stdlib.h
#include GL/glut.h
Nota: no pongo los simbolos menor que y mayor que porque desaparen.