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

Difference between revisions of "MD2 model loading"

From Allegro Wiki
Jump to: navigation, search
m (Vertex Types)
(MD2 Models)
 
Line 1: Line 1:
 
== MD2 Models ==
 
== MD2 Models ==
 
This format was invented by iD Software for Quake 2. They are list of vertices, with many frames of animation, and a single indexed triangle list with UV which forms the skin.
 
This format was invented by iD Software for Quake 2. They are list of vertices, with many frames of animation, and a single indexed triangle list with UV which forms the skin.
 +
 +
* A good tutorial, where I got most of my info from  [http://tfc.duke.free.fr/old/models/md2.htm]
  
 
== A MESH structure ==
 
== A MESH structure ==

Latest revision as of 06:04, May 23, 2009

MD2 Models

This format was invented by iD Software for Quake 2. They are list of vertices, with many frames of animation, and a single indexed triangle list with UV which forms the skin.

  • A good tutorial, where I got most of my info from [1]

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>