#include "quakedef.h" 

qboolean   r_md3fullbright_draw; 

// precalculated dot products for quantized angles 
float   r_avertexnormal_dots_md3[16][256] = 
#include "anorm_dots.h" 
; 

float   *shadedots_md3   = r_avertexnormal_dots_md3[0]; 
float   *shadedots2_md3   = r_avertexnormal_dots_md3[0]; 

// make these seperate "works" 
// only lightcolor is shared but thats k 
float   shadelightmd3, ambientlightmd3; 
float   apitchmd3, ayawmd3; 
float   lightlerpoffsetmd3; 

/* 
============= 
GL_CalcQ3LightLerp 

Calculates lightning between two angles 
============= 
*/ 
float GL_CalcQ3LightLerp (float l1, float l2) 
{ 
   float l, diff; 

   if (l1 != l2) { 
      if (l1 > l2) { 
         diff = (l1 - l2) * -1; 
      } else { 
         diff = l2 - l1; 
      } 
      diff *= lightlerpoffsetmd3; 
      l = l1 + diff; 
   } else { 
      l = l1; 
   } 
   // add contrast - this is a nasty hack from Pox's tut which simulates specular lighting 
   // mainly introduced cos at the time he didn't know the norms.  however, my engine does 
   // calculate the norms at model load time (although it doesn't really use them) so i 
   // should be able to do proper specular lighting... if only i knew how :-( 
   l *= l; 

   return l; 
} 

/* 
================= 
GL_DrawQ3AliasFrame 

Please fix this ANYONE!!! 
================= 
*/ 
extern cvar_t	gl_ringalpha;

void GL_DrawQ3AliasFrame (md3header_mem_t *header, int lastpose, int pose, float blend, qboolean mtex) 
{ 
   int               i, j, k; 
   int               frame; 
   int               lastframe; 
   int               vertframeoffset; 
   int               lastvertframeoffset; 
   vec3_t            l_v; 
   float            alpha, iblend; 
   float            d1, d2, l, l1, l2; 
   md3frame_mem_t      *light, *lastlight; 
   md3surface_mem_t   *surf; 
   md3st_mem_t         *tc; 
   md3tri_mem_t      *tris; 
   md3vert_mem_t      *verts, *vertslast; 
   md3trivertx_t      *lightverts, *lastlightverts; 

   
   	alpha = (currententity == &cl.viewent) ? 
		(
			(cl.items & IT_INVISIBILITY) ? 
				gl_ringalpha.value 
			: 
				bound(0, r_drawviewmodel.value, 1)
		) 
		: 
			r_modelalpha;

   iblend = 1.0 - blend; 

   surf = (md3surface_mem_t *)((byte *)header + header->offs_surfaces); 

   if (alpha < 1.0) { 
      glEnable (GL_BLEND); 
   } 

   for (i = 0; i < header->num_surfaces; i++) { 
      if (*(long *)surf->id != MD3IDHEADER) { 
         Con_Printf("MD3 bad surface for: %s\n", header->filename); 
      } 

      if (surf->num_surf_frames == 0){ 
         surf = (md3surface_mem_t *)((byte *)surf + surf->offs_end); 
         continue;   //shouldn't ever do this, each surface should have at least one frame 
      } 
      frame = pose%surf->num_surf_frames;   //cap the frame inside the list of frames in the model 
      vertframeoffset = frame*surf->num_surf_verts * sizeof(md3vert_mem_t); 
      lastframe = lastpose%surf->num_surf_frames; 
      lastvertframeoffset = lastframe*surf->num_surf_verts * sizeof(md3vert_mem_t); 

      tc = (md3st_mem_t *)((byte *)surf + surf->offs_tc); 
      tris = (md3tri_mem_t *)((byte *)surf + surf->offs_tris); 
      verts = (md3vert_mem_t *)((byte *)surf + surf->offs_verts + vertframeoffset); 
      vertslast = (md3vert_mem_t *)((byte *)surf + surf->offs_verts + lastvertframeoffset); 

      // now this gets hackish 
      light = (md3frame_mem_t *)((int) header + header->offs_frames + (header->num_frames * lastpose)); 
      lastlight = (md3frame_mem_t *)((int) header + header->offs_frames + (header->num_frames * pose)); 

      // damn this was a bitch to get going !! 
      lightverts = &light->verts[0]; 
      lastlightverts = &lastlight->verts[0]; 

      // normals and vertexes come from the frame list 
      // blend the light intensity from the two frames together 
      if (gl_vertexlights.value && !r_md3fullbright_draw) { 
         l = R_LerpVertexLight (lightverts->lightnormalindex, lastlightverts->lightnormalindex, blend, apitchmd3, ayawmd3); 

         l = min(l, 1);    
      } else { 
         d1 = (shadedots_md3[lastlightverts->lightnormalindex] * shadelightmd3 + ambientlightmd3) - (shadedots2_md3[lightverts->lightnormalindex] * shadelightmd3 + ambientlightmd3); 
         d2 = (shadedots_md3[lastlightverts->lightnormalindex] * shadelightmd3 + ambientlightmd3) - (shadedots2_md3[lightverts->lightnormalindex] * shadelightmd3 + ambientlightmd3);          

         l1 = min(1, (shadedots_md3[lightverts->lightnormalindex] * shadelightmd3 + ambientlightmd3) + (blend * d1) / 256); 
         l2 = min(1, (shadedots_md3[lightverts->lightnormalindex] * shadelightmd3 + ambientlightmd3) + (blend * d2) / 256); 

         l = GL_CalcQ3LightLerp (l1, l2); 
      } 

      if (!r_md3fullbright_draw) { 
         for (i=0 ; i<3 ; i++) { 
            l_v[i] = lightcolor[i] / 256 + l; 
         } 
         glColor4f (l_v[0], l_v[1], l_v[2], alpha); 
      } else { 
         glColor4f (l, l, l, alpha); 
      } 

      if (blend >=1) { 
         glNormalPointer(GL_FLOAT, 6 * sizeof(float), (float *)verts->normal); 
         glEnableClientState(GL_NORMAL_ARRAY); 

         glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (float *)verts->vec); 
         glEnableClientState(GL_VERTEX_ARRAY); 

         glTexCoordPointer(2, GL_FLOAT, 0, (float *)tc); 
         glEnableClientState(GL_TEXTURE_COORD_ARRAY); 

         glDrawElements(GL_TRIANGLES, surf->num_surf_tris*3, GL_UNSIGNED_INT, (int *)tris); 

         glDisableClientState(GL_NORMAL_ARRAY); 
         glDisableClientState(GL_VERTEX_ARRAY); 
         glDisableClientState(GL_TEXTURE_COORD_ARRAY); 
      } else { 
         glBegin(GL_TRIANGLES); 
         //for each triangle 
         for (j = 0; j < surf->num_surf_tris; j++) { 
            //draw the poly 
            for (k=0; k < 3; k++) { 
               //interpolated 
               if(mtex) { 
                  qglMultiTexCoord2f (GL_TEXTURE0_ARB,tc[tris[j].tri[k]].s, tc[tris[j].tri[k]].t); 
                  qglMultiTexCoord2f (GL_TEXTURE1_ARB,tc[tris[j].tri[k]].s, tc[tris[j].tri[k]].t); 
               } else { 
                  glTexCoord2f (tc[tris[j].tri[k]].s, tc[tris[j].tri[k]].t); 
               } 

               // Faster than the former struct 
               glVertex3f(verts[tris[j].tri[k]].vec[0] * blend + vertslast[tris[j].tri[k]].vec[0] * iblend, 
                        verts[tris[j].tri[k]].vec[1] * blend + vertslast[tris[j].tri[k]].vec[1] * iblend, 
                        verts[tris[j].tri[k]].vec[2] * blend + vertslast[tris[j].tri[k]].vec[2] * iblend); 

               // Faster than the former struct 
               glNormal3f(verts[tris[j].tri[k]].normal[0] * blend + vertslast[tris[j].tri[k]].normal[0] * iblend, 
                        verts[tris[j].tri[k]].normal[1] * blend + vertslast[tris[j].tri[k]].normal[1] * iblend, 
                        verts[tris[j].tri[k]].normal[2] * blend + vertslast[tris[j].tri[k]].normal[2] * iblend); 
            } 
         } 
      } 
      glEnd(); // crap i mounted this in the wrong spot before causing all sorts off strange behaviour 
   } 
   surf = (md3surface_mem_t *)((byte *)surf + surf->offs_end); 

   if (alpha < 1.0) { 
      glDisable (GL_BLEND); 
   } 
   glColor3f (1, 1, 1); 
} 

/* 
================= 
R_SetupQ3AliasFrame 

From TQ148 thx Tomaz 
================= 
*/ 
void R_SetupQ3AliasFrame (int frame, md3header_mem_t *header, entity_t *e, qboolean mtex) 
{ 
   float   blend; 

   // im getting confirms on non existing frames here 
   // but dunno if its a bug with the md3 models, so far 
   // it only seems to adhere to the rocketlauncher ? 
   if ((frame >= header->num_frames) || (frame < 0)) { 
      Con_DPrintf ("R_SetupQ3AliasFrame: no such frame %d\n", frame); 
      frame = 0; 
   } 

   //md3 interpolation 
   if (e->draw_lastmodel == e->model) { 
      if (frame != e->draw_pose) { 
         e->draw_lastpose = e->draw_pose; 
         e->draw_pose = frame; 
         e->draw_lerpstart = cl.time; 
         blend = 0; 
      } else { 
         blend = (cl.time - e->draw_lerpstart) * 10.0; 
      } 
   } else { 
      // uninitialized 
      e->draw_lastmodel = e->model; 
      e->draw_lastpose = e->draw_pose = frame; 
      e->draw_lerpstart = cl.time; 
      blend = 0; 
   } 

   if (blend > 1) { 
      blend = 1; 
   } 
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 
   GL_DrawQ3AliasFrame(header, e->draw_lastpose, frame, blend, mtex); 
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 
} 

/* 
================= 
R_DrawQ3Model 

Modified Dr Labmans 
md3 code splitting it 
up to make it easier 
to add dynamic lighting code 
================= 
*/ 
void R_DrawQ3Model(entity_t *e) 
{ 
   md3header_mem_t      *header; 
   md3shader_mem_t      *shader; 
   md3surface_mem_t   *surf; 
   model_t            *clmodel; 
   vec3_t            dist, ang, dlight_color; 
   vec3_t            vertexlightmd3; 
   float            add, ang_ceil, ang_floor, radiusmax = 0.0; 
   int               shaders, i, lnum; 

	if (!gl_notrans.value)	// always true if not -nehahra
	{
		r_modelalpha = currententity->transparency;
		if (r_modelalpha == 0)
			r_modelalpha = 1.0;
	}
	else
	{
		r_modelalpha = 1.0;
	}

	clmodel = e->model; 

   // get lighting information 
   // make thunderbolt and torches full light 
   if (clmodel->modhint & MOD_THUNDERBOLT) { 
      ambientlightmd3 = 210; 
      shadelightmd3 = 0; 
      r_md3fullbright_draw = true; 
   } else if (clmodel->modhint & MOD_FLAME) { 
      ambientlightmd3 = 255; 
      shadelightmd3 = 0; 
      r_md3fullbright_draw = true; 
   } else {    
      // normal lighting 
      r_md3fullbright_draw = false; 
      ambientlightmd3 = shadelightmd3 = R_LightPoint (e->origin); 
      for (lnum = 0 ; lnum < MAX_DLIGHTS ; lnum++) { 
         if (cl_dlights[lnum].die < cl.time || !cl_dlights[lnum].radius)   continue; 
         VectorSubtract (e->origin, cl_dlights[lnum].origin, dist); 
         add = cl_dlights[lnum].radius - VectorLength (dist); 
         if (add > 0) { 
            if (gl_vertexlights.value) { 
               if (!radiusmax || cl_dlights[lnum].radius > radiusmax) { 
                  radiusmax = cl_dlights[lnum].radius; 
                  VectorCopy(cl_dlights[lnum].origin, vertexlightmd3); 
               } 
            } 
            VectorCopy(bubblecolor[cl_dlights[lnum].type], dlight_color); 
            for (i=0 ; i<3 ; i++) { 
               lightcolor[i] = lightcolor[i] + (dlight_color[i] * add) * 2; 
               if (lightcolor[i] > 256) { 
                  switch (i) { 
                  case 0: 
                     lightcolor[1] = lightcolor[1] - (1 * lightcolor[1]/3); 
                     lightcolor[2] = lightcolor[2] - (1 * lightcolor[2]/3); 
                     break; 

                  case 1: 
                     lightcolor[0] = lightcolor[0] - (1 * lightcolor[0]/3); 
                     lightcolor[2] = lightcolor[2] - (1 * lightcolor[2]/3); 
                     break; 

                  case 2: 
                     lightcolor[1] = lightcolor[1] - (1 * lightcolor[1]/3); 
                     lightcolor[0] = lightcolor[0] - (1 * lightcolor[0]/3); 
                     break; 
                  } 
               } 
            } 
         } 
      } 

      // calculate pitch and yaw for vertex lighting 
      if (gl_vertexlights.value) { 
         apitchmd3 = e->angles[0]; 
         ayawmd3 = e->angles[1]; 
         if (!radiusmax) { 
            vlight_pitch = 45; 
            vlight_yaw = 45; 
         } else { 
            VectorSubtract(vertexlightmd3, e->origin, dist); 
            vectoangles(dist, ang); 
            vlight_pitch = ang[0]; 
            vlight_yaw = ang[1]; 
         } 
      } 

      // clamp lighting so it doesn't overbright as much 
      ambientlightmd3 = min(128, ambientlightmd3); 

      if (ambientlightmd3 + shadelightmd3 > 192) { 
         shadelightmd3 = 192 - ambientlightmd3; 
      } 

      // always give the gun some light 
      if (e == &cl.viewent && ambientlightmd3 < 24) { 
         ambientlightmd3 = shadelightmd3 = 24; 
      } 

      // never allow players to go totally black 
      if (clmodel->modhint & MOD_PLAYER) { 
         if (ambientlightmd3 < 8) { 
            ambientlightmd3 = shadelightmd3 = 8; 
            r_md3fullbright_draw = false; 
         } 
      } 
   } 

   if (r_fullbrightskins.value && (clmodel->modhint & MOD_PLAYER)) { 
      ambientlightmd3 = shadelightmd3 = 128; 
      r_md3fullbright_draw = true; 
   } 

   if (gl_vertexlights.value) { 
      // YUCK YUCK YUCK 
      shadedots_md3= r_avertexnormal_dots_md3[((int)(e->angles[1])) & (16 - 1)]; 
   } else { 
      // add pitch angle so lighting changes when looking up/down (mainly for viewmodel) 
      lightlerpoffsetmd3 = e->angles[1] * (16 / 360.0); 

      ang_ceil = ceil(lightlerpoffsetmd3); 
      ang_floor = floor(lightlerpoffsetmd3); 

      lightlerpoffsetmd3 = ang_ceil - lightlerpoffsetmd3; 

      // it gives me the crepaas !! 
      shadedots_md3 = r_avertexnormal_dots_md3[(int)ang_ceil & (16 - 1)]; 
      shadedots2_md3   = r_avertexnormal_dots_md3[(int)ang_floor & (16 - 1)]; 

      if (e->lastShadeLight) 
	  { 
         shadelightmd3 = (shadelightmd3 + e->lastShadeLight) / 2; 
      } 

      if (shadelightmd3) 
	  { 
         e->lastShadeLight = shadelightmd3; 
      } 
	  else 
	  { 
         e->lastShadeLight = 1; 
      } 
   } 

   // Allocate the header 
   header = (md3header_mem_t *)Mod_Extradata (e->model); 

   // locate the proper data "huh surface related"! 
   surf = (md3surface_mem_t *)((byte *)header + header->offs_surfaces); 

   c_alias_polys += surf->num_surf_tris; 

   //get pointer to shaders 
   shader = (md3shader_mem_t *)((byte *)surf + surf->offs_shaders); 

   shaders = shader[(currententity->skinnum%surf->num_surf_shaders)].texnum; 

   if (shaders!=0) { 
      GL_Bind(shaders); 
   } else { 
      GL_Bind(0); 
   } 

   if (*(long *)header->id != MD3IDHEADER){ 
      Con_Printf("MD3 bad model for: %s\n", header->filename); 
      return; 
   } 
   glPushMatrix(); 

   //interpolate unless its the viewmodel 
   if (gl_interpolate_animation.value && e != &cl.viewent) 
   { 
      R_BlendedRotateForEntity (e); 
   } 
   else 
   { 
      R_RotateForEntity (e); 
   } 

   if (gl_smoothmodels.value) { 
      glShadeModel (GL_SMOOTH); 
   } 

   if (gl_affinemodels.value) { 
      glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); 
   } 

   if (gl_mtexable) { 
      R_SetupQ3AliasFrame (e->frame, header, e, true); 
   } else { 
      R_SetupQ3AliasFrame (e->frame, header, e, false); 
   } 
   glShadeModel (GL_FLAT); 

   if (gl_affinemodels.value) { 
      glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 
   } 
   glPopMatrix(); 
} 

/* 
================= 
RadiusFromMD3Bounds 
================= 
*/ 
float RadiusFromMD3Bounds (vec3_t mins, vec3_t maxs) 
{ 
   int         i; 
   vec3_t      corner; 

   for (i=0 ; i<3 ; i++) { 
      corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); 
   } 
   return VectorLength (corner); 
} 

int   md3bboxmins[3], md3bboxmaxs[3]; 

/* 
================= 
Mod_LoadQ3Model 
================= 
*/ 
void Mod_LoadQ3Model(model_t *mod, void *buffer) 
{ 
   md3header_t         *header; 
   md3header_mem_t      *mem_head; 
   md3surface_t      *surf; 
   md3surface_mem_t   *currentsurf; 
   md3shader_mem_t      *shader; 
   md3frame_t         *framein; 
   md3frame_mem_t      *frameout; 
   int               i, j, picmip_flag; 
   int               posn; 
   int               surfstartposn; 
   char            name[128]; 

   //we want to work out the size of the model in memory 
   int               surf_size = 0; 
   int               mem_size = 0; 

   //pointer to header 
   header = (md3header_t *)buffer; 

   //pointer to the surface list 
   surf = (md3surface_t*)((byte *)buffer + header->surface_offs); 

   surf_size = 0; 

   picmip_flag = gl_picmip_all.value ? TEX_MIPMAP : 0; 

   for (i = 0; i < header->num_surfaces; i++) { 
      surf_size += sizeof(md3surface_mem_t); 
      surf_size += surf->num_surf_shaders * sizeof(md3shader_mem_t); 
      surf_size += surf->num_surf_tris * sizeof(md3tri_mem_t); 
      surf_size += surf->num_surf_verts * sizeof(md3st_mem_t); 
      surf_size += surf->num_surf_verts * surf->num_surf_frames * sizeof(md3vert_mem_t); 

      //goto next surf 
      surf = (md3surface_t*)((byte *)surf + surf->end_offs); 
   } 

   //total size =   header size + num_frames * frame size + num_tags * tag size + 
   //      size of surfaces 
   mem_size = sizeof(md3header_mem_t); 
   mem_size += header->num_frames * sizeof(md3frame_mem_t); 
   mem_size += header->num_tags * sizeof(md3tag_mem_t); 
   mem_size += surf_size; 

   Con_DPrintf("Loading md3 model...%s (%s)\n", header->filename, mod->name); 
   
   mem_head = (md3header_mem_t *)Cache_Alloc (&mod->cache, mem_size, mod->name); 
   
   if (!mem_head)
   { 
	  Con_DPrintf("cache alloc failed...%s (%s)\n", header->filename, mod->name); 
      return;   //cache alloc failed 
   } 

   //setup more mem stuff 
   mod->type = mod_md3; 
   mod->aliastype = MD3IDHEADER; 

   //copy stuff across from disk buffer to memory 
   posn = 0; //posn in new buffer 

   //copy header 
   Q_memcpy(mem_head, header, sizeof(md3header_t)); 
   posn += sizeof(md3header_mem_t); 

   //posn of frames 
   mem_head->offs_frames = posn; 

   //copy frames 
   Q_memcpy((byte *)mem_head + mem_head->offs_frames, (byte *)header + header->frame_offs, sizeof(md3frame_t) * header->num_frames); 
   posn += sizeof(md3frame_mem_t)*header->num_frames; 

   //posn of tags 
   mem_head->offs_tags = posn; 

   //copy tags 
   Q_memcpy((byte *)mem_head + mem_head->offs_tags, (byte *)header + header->tag_offs, sizeof(md3tag_t) * header->num_tags); 
   posn += sizeof(md3tag_mem_t)*header->num_tags; 

   //posn of surfaces 
   mem_head->offs_surfaces = posn; 

   //get pointer to surface in file 
   surf = (md3surface_t *)((byte *)header + header->surface_offs); 

   //get pointer to surface in memory 
   currentsurf = (md3surface_mem_t *)((byte *)mem_head + posn); 
   surfstartposn = posn; 

   for (i=0; i < header->num_surfaces; i++) { 
      //copy size of surface 
      Q_memcpy((byte *)mem_head + posn, (byte *)header + header->surface_offs, sizeof(md3surface_t)); 
      posn += sizeof(md3surface_mem_t); 

      //posn of shaders for this surface 
      currentsurf->offs_shaders = posn - surfstartposn; 

      for (j=0; j < surf->num_surf_shaders; j++){ 
         //copy jth shader accross 
         Q_memcpy((byte *)mem_head + posn, (byte *)surf + surf->shader_offs + j * sizeof(md3shader_t), sizeof(md3shader_t)); 
         posn += sizeof(md3shader_mem_t); //copyed non-mem into mem one 
      } 
      //posn of tris for this surface 
      currentsurf->offs_tris = posn - surfstartposn; 

      //copy tri 
      Q_memcpy((byte *)mem_head + posn, (byte *)surf + surf->tris_offs, sizeof(md3tri_t) * surf->num_surf_tris); 
      posn += sizeof(md3tri_mem_t) * surf->num_surf_tris; 

      //posn of tex coords in this surface 
      currentsurf->offs_tc = posn - surfstartposn; 

      //copy st 
      Q_memcpy((byte *)mem_head + posn, (byte *)surf + surf->tc_offs, sizeof(md3st_t) * surf->num_surf_verts); 
      posn += sizeof(md3st_t) * surf->num_surf_verts; 

      //posn points to surface->verts 
      currentsurf->offs_verts = posn - surfstartposn; 

      //next to have to be redone 
      for (j=0; j < surf->num_surf_verts * surf->num_surf_frames; j++) { 
         float lat; 
         float lng; 

         //convert verts from shorts to floats 
         md3vert_mem_t   *mem_vert = (md3vert_mem_t *)((byte *)mem_head + posn); 
         md3vert_t      *disk_vert = (md3vert_t *)((byte *)surf + surf->vert_offs + j * sizeof(md3vert_t)); 

         mem_vert->vec[0] = (float)disk_vert->vec[0] / 64.0f; 
         mem_vert->vec[1] = (float)disk_vert->vec[1] / 64.0f; 
         mem_vert->vec[2] = (float)disk_vert->vec[2] / 64.0f; 

         //work out normals 
         lat = (disk_vert->normal + 255) * (2 * 3.141592654) / 255; 
         lng = ((disk_vert->normal >> 8) & 255) * (2 * 3.141592654) / 255; 

         mem_vert->normal[0] = cos (lat) * sin (lng); 
         mem_vert->normal[1] = sin (lat) * sin (lng); 
         mem_vert->normal[2] = cos (lng); 

         posn += sizeof(md3vert_mem_t); //copyed non-mem into mem one 
      } 

      //point to next surf (or eof) 
      surf = (md3surface_t*)((byte *)surf + surf->end_offs); 

      //posn points to the end of this surface 
      currentsurf->offs_end = posn; 

      //next start of surf (if there is one) 
      surfstartposn = posn; 
   } 

   // load frames 
   // only for lights "took me ages to complete this mess" :/ 
   framein = (void*) ((int) header + LittleLong(header->frame_offs)); 
   frameout = (void*) ((int) mem_head + mem_head->offs_frames); 
   for (i=0 ; i < mem_head->num_frames ; i++) { 
      strcpy (frameout->name, framein->name); 
      frameout->pos[0] = framein->pos[0]; 
      frameout->pos[1] = framein->pos[1]; 
      frameout->pos[2] = framein->pos[2]; 
      // whats the freakin size here ??? 
      // nothing else fits not even the original struct arrgh !!! 
      for (j = 0 ; j < mem_head->num_surfaces ; j++) { 
         VectorCopy (framein->verts[j].v, frameout->verts[j].v); 
         frameout->verts[j].lightnormalindex = framein->verts[j].lightnormalindex; 

         // position ?? 
         // hmm scale this by 64 aswell ?? 
         frameout->mins[0] = frameout->verts[j].v[0] * frameout->pos[0] / 64.0f; 
         frameout->mins[1] = frameout->verts[j].v[1] * frameout->pos[1] / 64.0f; 
         frameout->mins[2] = frameout->verts[j].v[2] * frameout->pos[2] / 64.0f; 

         frameout->maxs[0] = frameout->verts[j].v[0] * frameout->pos[0] / 64.0f; 
         frameout->maxs[1] = frameout->verts[j].v[1] * frameout->pos[1] / 64.0f; 
         frameout->maxs[2] = frameout->verts[j].v[2] * frameout->pos[2] / 64.0f; 

         //hehe nice copy & paste job here 
         frameout->radius = RadiusFromMD3Bounds (frameout->mins, frameout->maxs); 

         // update bounding box 
         if (frameout->mins[0] < md3bboxmins[0]) md3bboxmins[0] = frameout->mins[0]; 
         if (frameout->mins[1] < md3bboxmins[1]) md3bboxmins[1] = frameout->mins[1]; 
         if (frameout->mins[2] < md3bboxmins[2]) md3bboxmins[2] = frameout->mins[2]; 
         if (frameout->maxs[0] > md3bboxmaxs[0]) md3bboxmaxs[0] = frameout->maxs[0]; 
         if (frameout->maxs[1] > md3bboxmaxs[1]) md3bboxmaxs[1] = frameout->maxs[1]; 
         if (frameout->maxs[2] > md3bboxmaxs[2]) md3bboxmaxs[2] = frameout->maxs[2]; 
      } 
      framein = (void*) &framein->verts[j].v[0]; 
      frameout = (void*) &frameout->verts[j].v[0]; 
   } 
   VectorCopy (md3bboxmaxs, mod->maxs); 
   VectorCopy (md3bboxmins, mod->mins); 

   //posn should now equal mem_size 
   if (posn != mem_size) { 
      Con_Printf("Copied different ammount to what was worked out, copied: %i worked out: %i\n",posn, mem_size); 
   } 

   VectorCopy(((md3frame_mem_t *)((byte *)mem_head + mem_head->offs_frames))->mins, mod->mins); 
   VectorCopy(((md3frame_mem_t *)((byte *)mem_head + mem_head->offs_frames))->maxs, mod->maxs); 

   if (!strcmp (mod->name, "progs/g_shot.mdl") || //Hack to give .md3 files renamed to .mdl rotate effects - Eradicator 
      !strcmp (mod->name, "progs/g_nail.mdl") || 
      !strcmp (mod->name, "progs/g_nail2.mdl") || 
      !strcmp (mod->name, "progs/g_rock.mdl") || 
      !strcmp (mod->name, "progs/g_rock2.mdl") || 
      !strcmp (mod->name, "progs/g_light.mdl") || 
      !strcmp (mod->name, "progs/armor.mdl") || 
      !strcmp (mod->name, "progs/backpack.mdl") || 
      !strcmp (mod->name, "progs/w_g_key.mdl") || 
      !strcmp (mod->name, "progs/w_s_key.mdl") || 
      !strcmp (mod->name, "progs/m_g_key.mdl") || 
      !strcmp (mod->name, "progs/m_s_key.mdl") || 
      !strcmp (mod->name, "progs/b_g_key.mdl") || 
      !strcmp (mod->name, "progs/b_s_key.mdl") || 
      !strcmp (mod->name, "progs/quaddama.mdl") || 
      !strcmp (mod->name, "progs/invisibl.mdl") || 
      !strcmp (mod->name, "progs/invulner.mdl") || 
      !strcmp (mod->name, "progs/jetpack.mdl") || 
      !strcmp (mod->name, "progs/cube.mdl") || 
      !strcmp (mod->name, "progs/suit.mdl") || 
      !strcmp (mod->name, "progs/boots.mdl") || 
      !strcmp (mod->name, "progs/end1.mdl") || 
      !strcmp (mod->name, "progs/end2.mdl") || 
      !strcmp (mod->name, "progs/end3.mdl") || 
      !strcmp (mod->name, "progs/end4.mdl")) { 
      mem_head->flags |= EF_ROTATE; 
   } else if (!strcmp (mod->name, "progs/missile.mdl")) { 
      mem_head->flags |= EF_ROCKET; 
   } else if (!strcmp (mod->name, "progs/gib1.mdl") || //EF_GIB 
            !strcmp (mod->name, "progs/gib2.mdl") || 
            !strcmp (mod->name, "progs/gib3.mdl") || 
            !strcmp (mod->name, "progs/h_player.mdl") || 
            !strcmp (mod->name, "progs/h_dog.mdl") || 
            !strcmp (mod->name, "progs/h_mega.mdl") || 
            !strcmp (mod->name, "progs/h_guard.mdl") || 
            !strcmp (mod->name, "progs/h_wizard.mdl") || 
            !strcmp (mod->name, "progs/h_knight.mdl") || 
            !strcmp (mod->name, "progs/h_hellkn.mdl") || 
            !strcmp (mod->name, "progs/h_zombie.mdl") || 
            !strcmp (mod->name, "progs/h_shams.mdl") || 
            !strcmp (mod->name, "progs/h_shal.mdl") || 
            !strcmp (mod->name, "progs/h_ogre.mdl") || 
            !strcmp (mod->name, "progs/armor.mdl") || 
            !strcmp (mod->name, "progs/h_demon.mdl")) { 
      mem_head->flags |= EF_GIB; 
   } else if (!strcmp (mod->name, "progs/grenade.mdl")) { 
      mem_head->flags |= EF_GRENADE; 
   } else if (!strcmp (mod->name, "progs/w_spike.mdl")) { 
      mem_head->flags |= EF_TRACER; 
   } else if (!strcmp (mod->name, "progs/k_spike.mdl")) { 
      mem_head->flags |= EF_TRACER2; 
   } else if (!strcmp (mod->name, "progs/v_spike.mdl")) { 
      mem_head->flags |= EF_TRACER3; 
   } else if (!strcmp (mod->name, "progs/zom_gib.mdl")) { 
      mem_head->flags |= EF_ZOMGIB; 
   } 
    mod->flags = LittleLong (mem_head->flags); 
   mod->numframes = LittleLong (mem_head->num_frames); 

   //get pointer to first surface 
   currentsurf = (md3surface_mem_t *)((byte *)mem_head + mem_head->offs_surfaces); 
   for (i=0; i<mem_head->num_surfaces; i++) { 
      if (*(long *)currentsurf->id != MD3IDHEADER){ 
         Con_Printf("MD3 bad surface for: %s\n", mem_head->filename); 
      } else { 
         shader = (md3shader_mem_t *)((byte *)currentsurf + currentsurf->offs_shaders); 
         for (j=0; j<currentsurf->num_surf_shaders; j++){ 
            sprintf (&name[0], "textures/md3skins/%s", shader[j].name); 
            shader[j].texnum = GL_LoadTextureImage (&name[0], NULL, 0, 0, picmip_flag | TEX_BLEND); 

            if (!shader[j].texnum) { 
               sprintf (&name[0], "textures/md3boxes/%s", shader[j].name); 
               shader[j].texnum = GL_LoadTextureImage (&name[0], NULL, 0, 0, picmip_flag | TEX_BLEND); 
            } 

            if (!shader[j].texnum) { 
               Con_Printf("Model: %s  Texture missing: %s\n", mod->name, shader[j].name); 
            } 
         } 
      } 
      currentsurf = (md3surface_mem_t *)((byte *)currentsurf + currentsurf->offs_end); 
   } 
} 
