The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

MD2 model loading

From Allegro Wiki
Revision as of 03:10, September 19, 2008 by Matt Smith (talk | contribs)
Jump to: navigation, search

MD2 Models

 They're from Quake 2.  They are list of vertexes, with many frames of animation, and a single indexed triangle list with UV which forms the skin.


A MESH structure

 A mesh structure is essentially a list of vertices, and a list of polygons.  It is also sometimes useful to store a list of edges, for fast wireframe drawing and other operations. So the MESH datatype takes a form roughly like 

Example mesh structure

<highlightSyntax language="cpp"> typedef struct MESH {

         vertex_t * verts;
         edge_t * edges;
         polygon_t * polys;
    };

</highlightSyntax>

Vertex Types

There are many variations of vertex data type.  Allegro 4 has V3D and V3D_f built in. 

V3D & V3D_f

VTX_N

That's what I appear to be using. probably in a3dstuff.h (described on Mesh_rendering)


Example

model.h

<highlightSyntax language="cpp">

  1. include <allegro.h>
  2. include "a3dstuff.h"
  1. ifndef _INCLUDE_MODEL_H_
  2. define _INCLUDE_MODEL_H_
   /************************************************************/
       
   typedef struct
   {
      char ident[4];
      int  version;
      int  skin_width;     /* ignored - this is filled later when loading the texmap */
      int  skin_height;    /* ignored - this is filled later when loading the texmap */   
      int  frame_size;
      int  num_skins;      /* ignored */ 

int num_vertices;

      int  num_st;
      int  num_triangles;
      int  num_gl_commands;
      int  num_frames;
      int  offset_skins;   /* ignored */    
      int  offset_st;
      int  offset_triangles;
      int  offset_frames;
      int  offset_gl_commands;
      int  offset_end;
   }
   MD2_HEADER_STRUCT;    
   /************************************************************/
    
   typedef struct
   {
      char unscaled_x;
      char unscaled_y;
      char unscaled_z;
      char normal_list_index; /* using the id surface normals */
   }    
   MD2_VERTEX_STRUCT;
   /************************************************************/
   typedef struct
   {
      float             scale[3];
      float             translate[3];
      char              name[16]; /* just junk */     
      MD2_VERTEX_STRUCT vertices[1]; 
   }
   MD2_ANIM_FRAME_STRUCT;             



   /************************************************************/
    
   typedef struct
   {
      float    tex_coord_x;
      float    tex_coord_y;
      int      vertex_list_index;
   } 
   MD2_TRISTRIP_TRIFAN_ELEMENT;      
  
   /************************************************************/
   typedef struct
   {
       MD2_HEADER_STRUCT				*header;	
       int                             num_anim_frames;		

MD2_ANIM_FRAME_STRUCT *anim_frames; FRAME * frames;

       MD2_TRISTRIP_TRIFAN_ELEMENT     *gl_command_elements;
       int                             *gl_command_list;
       int                             num_gl_commands;

float texture_x_factor; // values to convert pixel x,y into GL s,t float texture_y_factor;

MESH_TUVI * mesh;


}

   MD2_MODEL;
       
   /************************************************************/


  1. define MD2_END_OF_LIST 0


typedef struct { int start; size_t size; void(* callback)(MD2_MODEL *, size_t, PACKFILE *); } queue_block_t;


extern MD2_MODEL * model;


MD2_MODEL * load_MD2_model(const char * filename);


void display_model(const MD2_MODEL *model,

                     double x, double y, double z, 
                     double y_rotation, GLuint texture, int frame);


void render_MD2_model(const MD2_MODEL *model,

                     double x, double y, double z, 
                     double y_rotation, GLuint texture, int frame);


  1. endif


</highlightSyntax>

model.c

<highlightSyntax language="cpp">

  1. include <alleggl.h>
  2. include <GL/glu.h>
  3. include <stdlib.h>
  4. include <stdio.h>
  1. include "a3dstuff.h"
  2. include "model.h"
  3. include "anorms.h"


/* the parts of the file are queued here for reading in order */ /* this will be malloced by the model to make load_model() re-entrant */

queue_block_t queue[20];

int queue_length = 0;


MD2_MODEL * model=NULL; // global model pointer


/* reads the header into the platform's natural structure */

packfread_md2_header( MD2_HEADER_STRUCT * header , PACKFILE * pf) { pack_fread(&(header->ident) , 4, pf); header->version = pack_igetl(pf); header->skin_width = pack_igetl(pf); header->skin_height = pack_igetl(pf); header->frame_size = pack_igetl(pf); header->num_skins = pack_igetl(pf); header->num_vertices = pack_igetl(pf); header->num_st = pack_igetl(pf); header->num_triangles = pack_igetl(pf); header->num_gl_commands = pack_igetl(pf); header->num_frames = pack_igetl(pf); header->offset_skins = pack_igetl(pf); header->offset_st = pack_igetl(pf); header->offset_triangles = pack_igetl(pf); header->offset_frames = pack_igetl(pf); header->offset_gl_commands = pack_igetl(pf); header->offset_end = pack_igetl(pf); }


void read_empty_block( MD2_MODEL * model, size_t size, PACKFILE * pf ) { pack_fseek(pf, size); /* skips size chars */ }



void read_frames_block( MD2_MODEL * model, size_t size, PACKFILE * pf ) { int f, n_frames = model->header->num_frames; int i; float scale_x,scale_y,scale_z,trans_x,trans_y,trans_z;

VTX_N * vtxs = model->mesh->vtxs;

int v, n_vtxs = model->header->num_vertices;

FRAME * frame = model->frames;

for ( f=0; f<n_frames; f++ ) { i = pack_igetl(pf); scale_x = *(float*)&i; i = pack_igetl(pf); scale_y = *(float*)&i; i = pack_igetl(pf); scale_z = *(float*)&i; i = pack_igetl(pf); trans_x = *(float*)&i; i = pack_igetl(pf); trans_y = *(float*)&i; i = pack_igetl(pf); trans_z = *(float*)&i;

pack_fread(frame[f].name, 16, pf);

vtxs = frame[f].vtxs = (VTX_N *) malloc(n_vtxs * sizeof(VTX_N));

for (v=0; v<n_vtxs; v++) { unsigned char c[4]; pack_fread(c, 4, pf); vtxs[v].x = c[0] * scale_x + trans_x; vtxs[v].y = c[1] * scale_y + trans_y; vtxs[v].z = c[2] * scale_z + trans_z;

vtxs[v].nx = normal_list[c[3]].x; vtxs[v].ny = normal_list[c[3]].y; vtxs[v].nz = normal_list[c[3]].z;

} } }


void read_tris_block( MD2_MODEL * model, size_t size, PACKFILE * pf ) { int n_tris = model->header->num_triangles; int i,t = 0 ;

TRI_UVI * tris = model->mesh->tris;

for (t=0; t< n_tris; t++ ) { tris[t].p1 = pack_igetw(pf); tris[t].p2 = pack_igetw(pf); tris[t].p3 = pack_igetw(pf);

tris[t].uv1 = pack_igetw(pf); // uv index tris[t].uv2 = pack_igetw(pf); tris[t].uv3 = pack_igetw(pf); }

}


void read_skins_block( MD2_MODEL * model, size_t size, PACKFILE * pf ) { int s; char buf[64];

for ( s=0 ; s< model->header->num_skins; s++) { pack_fread( buf, 64, pf);

} }


void read_st_block( MD2_MODEL * model, size_t size , PACKFILE * pf) {

short * st = (short *)malloc(size); UV_f * uv_list = model->mesh->uvs = (UV_f *) malloc(sizeof(UV_f) * model->header->num_st);

pack_fread(st, size, pf);

while(size) { uv_list->u = (float)(*st++) * model->texture_x_factor; uv_list->v = (float)(*st++) * model->texture_y_factor;

uv_list++; size -= 4; }

}


void read_glcmds_block( MD2_MODEL * model, size_t size, PACKFILE * pf) { int element_index = 0; int command_index = 0; int dest = 0; int counter; char buffer2[sizeof(MD2_TRISTRIP_TRIFAN_ELEMENT)];


model->gl_command_list = (int *) malloc(model->header->num_gl_commands * 4); /* not 100% efficient, but safe enough */ model->gl_command_elements = (MD2_TRISTRIP_TRIFAN_ELEMENT *) malloc(model->header->num_gl_commands * 4);


//pack_fseek(pf, model->header->offset_gl_commands);

do { /** get the next gl command from the file **/

model->gl_command_list[command_index] = pack_igetl(pf);

/** if it's a trifan (negative), we need it's inverse **/ dest = abs(model->gl_command_list[command_index]);

/** now read the tri-strip/tri-fan elements **/ for(counter = 0; counter < dest; counter++) { pack_fread(buffer2, sizeof(MD2_TRISTRIP_TRIFAN_ELEMENT), pf); memcpy(&model->gl_command_elements[element_index], buffer2, sizeof(MD2_TRISTRIP_TRIFAN_ELEMENT)); element_index++; }

command_index++; } while (dest != 0);

model->num_gl_commands = command_index - 1; /* because header->num_gl_commands is the number of dwords the cmd list takes */

}


/* this queues block requests so they can be read in order by read_blocks() */

void read_block(int start, size_t size , void(* callback)(MD2_MODEL *, size_t, PACKFILE *)) { queue_block_t * slot = &queue[queue_length];

//while (slot->start) slot++;

slot->start = start; slot->size = size; slot->callback = callback;

queue_length++; slot[1].start = 0; }

/* for qsort */ int compare_queue(const void * q1, const void * q2) { return ( ((queue_block_t *)q1)->start - ((queue_block_t *)q2)->start); }


/* this sorts the block requests queue and then reads them in order.

  This overcomes the forward-seeking-only limitation in pack_fseek() */

void read_blocks( MD2_MODEL * model, int end, PACKFILE * pf) { queue_block_t * slot = NULL; int slot_end = 0; int s,ns;

read_block(end,0,NULL);

qsort(queue, queue_length, sizeof(queue_block_t), &compare_queue);


slot = queue; ns = queue_length; //queue_length may change in loop

for (s=0 ; s<ns ; s++) { /* see if there is an unused block between this one and the next */ slot_end = slot->start + slot->size; if (slot_end < slot[1].start) read_block(slot_end, slot[1].start - slot_end, &read_empty_block); slot ++; }

/* sort again, for empty blocks interleaved */ qsort(queue, queue_length, sizeof(queue_block_t), &compare_queue);

slot = queue;

/* perform callbacks, which performs actual file reading */ for (s=0; s<queue_length ; s++ ) { //pack_fread(slot->size , pf); if (slot->callback) slot->callback( model , slot->size , pf); slot++; } }


MD2_MODEL * load_MD2_model(const char * filename) {

   PACKFILE            *pf;
   MD2_MODEL           *model = NULL;
   MD2_HEADER_STRUCT   *header;

int frames_data_size, skins_data_size, st_data_size, tri_data_size,gl_data_size;


   char                 buffer[sizeof(MD2_HEADER_STRUCT)];


header = (MD2_HEADER_STRUCT *)buffer;

   pf    =    pack_fopen(filename,"rb");
   
   if (pf)

{

if (!packfread_md2_header(header,pf)) return NULL; /* read header or return if failed */


model = (MD2_MODEL *)malloc(sizeof(MD2_MODEL));

if (model) {

model->header = header;

model->num_anim_frames = header->num_frames;

model->gl_command_list = (int *)malloc(header->num_gl_commands * 4); /* not 100% efficient, but safe enough */ model->gl_command_elements = (MD2_TRISTRIP_TRIFAN_ELEMENT *)malloc(header->num_gl_commands * 4); model->anim_frames = (MD2_ANIM_FRAME_STRUCT *)malloc(header->frame_size * header->num_frames);

model->frames = (FRAME *) malloc( sizeof(FRAME) * header->num_frames);

model->mesh = (MESH_TUVI *)malloc(sizeof(MESH_TUVI)); model->mesh->n_tris = header->num_triangles; model->mesh->tris = (TRI_UVI *)malloc(header->num_triangles * sizeof(TRI_UVI)); model->mesh->n_vtxs = header->num_vertices; model->mesh->vtxs = (VTX_N *)malloc(header->num_vertices * sizeof(VTX_N));


model->texture_x_factor = 1.0 / header->skin_width; model->texture_y_factor = 1.0 / header->skin_height;


frames_data_size = header->frame_size * header->num_frames; skins_data_size = 64 * header->num_skins; st_data_size = 4 * header->num_st; tri_data_size = header->num_triangles * 12; gl_data_size = header->num_gl_commands * 4;

queue_length = 0;

read_block( header->offset_frames, frames_data_size , &read_frames_block ); read_block( header->offset_skins, skins_data_size , &read_skins_block ); read_block( header->offset_st, st_data_size , &read_st_block ); read_block( header->offset_triangles, tri_data_size , &read_tris_block ); read_block( header->offset_gl_commands, gl_data_size, &read_glcmds_block );

read_blocks( model , header->offset_end , pf); }

pack_fclose(pf);

   }
   return model;

}


void draw_gl_model_tex(V3D_f * vtx , float u, float v) { glTexCoord2f ( u/512.0, 1.0 - v/512.0); glVertex3f(vtx->x, vtx->y, vtx->z);


}


void draw_gl_model_uv(V3D_f * vtx) { float r,g,b; int c = vtx->c; r= getr(c); g=getg(c); b =getb(c); glColor3f(r/255.0, g/255.0, b/255.0); glVertex3f(vtx->x, vtx->y, vtx->z);

}


void display_model(const MD2_MODEL * model, double x, double y, double z,

             double y_rotation, GLuint texture, int frame)

{ int t,n_tris = model->mesh->n_tris; TRI_UVI * tris = model->mesh->tris; int v,n_vtxs = model->mesh->n_vtxs; VTX_N * vtxs = model->mesh->vtxs; UV_f * uv_list = model->mesh->uvs; FRAME * frame_list = model->frames;

vtxs = frame_list[frame].vtxs;

   glPushMatrix();
   /* move to where the model is supposed to go in the scene */
   glTranslatef(x,y,z);
   glRotatef(y_rotation,0,0,1);

glScalef(0.02,0.02,0.02);

   glBindTexture(GL_TEXTURE_2D, texture)                              ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP)          ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP)          ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)     ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)     ;

glColor3f(1.0, 1.0, 0.8); glShadeModel(GL_SMOOTH); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_MODULATE); //draw_mesh_GL

for( t=0 ; t < n_tris ; t++) {

//glBindTexture (GL_TEXTURE_2D, 4); //if (t==200) display_skybox();

//glBindTexture (GL_TEXTURE_2D, tris[t].gltex); //glBindTexture (GL_TEXTURE_2D, 10);


glBegin(GL_TRIANGLES); glTexCoord2f( uv_list[tris[t].uv1].u , uv_list[tris[t].uv1].v);

               glNormal3f(vtxs[tris[t].p1].nx, vtxs[tris[t].p1].ny, vtxs[tris[t].p1].nz );

glVertex3f( vtxs[tris[t].p1].x, vtxs[tris[t].p1].y, vtxs[tris[t].p1].z ); glTexCoord2f( uv_list[tris[t].uv2].u , uv_list[tris[t].uv2].v); glNormal3f(vtxs[tris[t].p2].nx, vtxs[tris[t].p2].ny, vtxs[tris[t].p2].nz ); glVertex3f( vtxs[tris[t].p2].x, vtxs[tris[t].p2].y, vtxs[tris[t].p2].z ); glTexCoord2f( uv_list[tris[t].uv3].u , uv_list[tris[t].uv3].v); glNormal3f(vtxs[tris[t].p3].nx, vtxs[tris[t].p3].ny, vtxs[tris[t].p3].nz ); glVertex3f( vtxs[tris[t].p3].x, vtxs[tris[t].p3].y, vtxs[tris[t].p3].z ); glEnd(); }

   /* draw normals */

/* for ( v=0; v<n_vtxs; v++ ) { glBegin(GL_LINES);

          glVertex3f( vtxs[v].x + vtxs[v].nx,

vtxs[v].y + vtxs[v].ny, vtxs[v].z + vtxs[v].nz ); glVertex3f( vtxs[v].x, vtxs[v].y, vtxs[v].z ); glEnd(); }

  • /
   glPopMatrix();

}


/******************************************************************************/

void render_MD2_model(const MD2_MODEL *model,

                     double x, double y, double z, 
                     double y_rotation, GLuint texture, int frame)

{

   double scaled_x, scaled_y, scaled_z;
   int    command_list_index = 0;
   int    command_element_index = 0;
   int    vertex_list_index;
   int    normal_list_index;
   int    temp_index;
   
   /** feeble attempt to defend ourselves from our own stupidity **/
   if(model == NULL)
   {    
        TRACE("\nmd2_model.c: render_MD2_model(): first param pointed nowhere (nice try, smart guy)");
        return;    
   }
   
   glPushMatrix();
   /* move to where the model is supposed to go in the scene */
   glTranslatef(x,y,z);
   glRotatef(y_rotation,0,1,0);
   glBindTexture(GL_TEXTURE_2D, texture)                              ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP)          ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP)          ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)     ;
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)     ;
   
   /** the end of the list is marked by a 0, so... **/
   if(model->gl_command_list == NULL)
   while(model->gl_command_list[command_list_index] != MD2_END_OF_LIST)
   {
       /** should we be using tri-strips or tri-fans? **/
       /** we should use fan ***/
       if(model->gl_command_list[command_list_index] < 0)
       {
            glBegin(GL_TRIANGLE_FAN);
            temp_index = command_element_index - model->gl_command_list[command_list_index];
            
            while(command_element_index < temp_index)
            {
                glTexCoord2f(model->gl_command_elements[command_element_index].tex_coord_x,    
                             model->gl_command_elements[command_element_index].tex_coord_y);
              
                vertex_list_index =  model->gl_command_elements[command_element_index].vertex_list_index;  
                normal_list_index =  model->anim_frames[frame].vertices[vertex_list_index].normal_list_index;            
                          
                glNormal3f(normal_list[normal_list_index].x,
                           normal_list[normal_list_index].y,
                           normal_list[normal_list_index].z);
                            
                /*** un-byte-ify the vertex position ***/                                                  
                

scaled_x = model->anim_frames[frame].vertices[vertex_list_index].unscaled_x;

                scaled_y = model->anim_frames[frame].vertices[vertex_list_index].unscaled_y;
                scaled_z = model->anim_frames[frame].vertices[vertex_list_index].unscaled_z;
                
                glVertex3f(scaled_x,scaled_y,scaled_z);
                
                command_element_index++;
            }
            
            glEnd();
       }
       /*** we should use strip ***/
       else        
       {
            glBegin(GL_TRIANGLE_STRIP);
            
            temp_index = command_element_index + model->gl_command_list[command_list_index];
            
            while(command_element_index < temp_index)
            {
                glTexCoord2f(model->gl_command_elements[command_element_index].tex_coord_x,    
                             model->gl_command_elements[command_element_index].tex_coord_y);
                                                       
                vertex_list_index =  model->gl_command_elements[command_element_index].vertex_list_index; 
                normal_list_index =  model->anim_frames[frame].vertices[vertex_list_index].normal_list_index;            
                          
                glNormal3f(normal_list[normal_list_index].x,
                           normal_list[normal_list_index].y,
                           normal_list[normal_list_index].z);
                                               
                /*** un-byte-ify the vertex position ***/                                                  
                scaled_x = model->anim_frames[frame].vertices[vertex_list_index].unscaled_x * model->anim_frames[frame].scale[0] + model->anim_frames[frame].translate[0];
                scaled_y = model->anim_frames[frame].vertices[vertex_list_index].unscaled_y * model->anim_frames[frame].scale[1] + model->anim_frames[frame].translate[1];
                scaled_z = model->anim_frames[frame].vertices[vertex_list_index].unscaled_z * model->anim_frames[frame].scale[2] + model->anim_frames[frame].translate[2];
             
                glVertex3f(scaled_x,scaled_y,scaled_z);
                command_element_index++;
            }
            
            glEnd();
       } /*** end else **/
   
       command_list_index++;    
   } /*** end while ***/    
   glPopMatrix();
   return;

}

/******************************************************************************/ </highlightSyntax>