domingo, 12 de junio de 2011

GameEngine: Capitulo 8. Model Manager

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:

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.

LordPakusBlog
Nos vemos

3 comentarios :

  1. Bueno, de momento estoy bloqueado en este capítulo, ya que obtengo el siguiente error:
    c:\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'

    ResponderEliminar
  2. 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.

    ResponderEliminar
  3. En el comentario anterior no aparecen bien los includes. serían:
    #include stdlib.h
    #include GL/glut.h
    Nota: no pongo los simbolos menor que y mayor que porque desaparen.

    ResponderEliminar

Entradas populares