Q2 models into Quake I think this picture says it all. Right off the bat I would just like to say that most of the code here is by LordHavoc, and he deserves the respect for this. I just de-bugged a coupla things, and then gutted it from his code, where it was all comfy and happy, and adjusted it so all you lot could get your grubby mitts on it. The code here is by not quite perfect as the models are lit at a constant level. This is NOT how LordHavoc left it, merely that his code for shading was different from the standard code base, and so rather than hold up this tutorial any longer I hacked a quick fix in.
Enough talking and onto the good stuff.
just above the R_DrawAliasModel routine, paste these procedures in
/* ============= GL_DrawQ2AliasFrame ============= */ void GL_DrawQ2AliasFrame (entity_t *e, md2_t *pheader, int lastpose, int pose, float lerp) { float ilerp; int *order, count; md2trivertx_t *verts1, *verts2; vec3_t scale1, translate1, scale2, translate2; md2frame_t *frame1, *frame2; glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glShadeModel(GL_SMOOTH); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); // glBlendFunc(GL_ONE, GL_ZERO); // glDisable(GL_BLEND); glDepthMask(1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); ilerp = 1.0 - lerp; //new version by muff - fixes bug, easier to read, faster (well slightly) frame1 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * lastpose)); frame2 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * pose)); VectorCopy(frame1->scale, scale1); VectorCopy(frame1->translate, translate1); VectorCopy(frame2->scale, scale2); VectorCopy(frame2->translate, translate2); verts1 = &frame1->verts[0]; verts2 = &frame2->verts[0]; order = (int *)((int)pheader + pheader->ofs_glcmds); glColor4f (1,1,1,1); // FIXME - temporary shading for tutorial - NOT LordHavoc's original code while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { glTexCoord2f(((float *)order)[0], ((float *)order)[1]); glVertex3f((verts1[order[2]].v[0]*scale1[0]+translate1[0])*ilerp+(verts2[order[2]].v[0]*scale2[0]+translate2[0])*lerp, (verts1[order[2]].v[1]*scale1[1]+translate1[1])*ilerp+(verts2[order[2]].v[1]*scale2[1]+translate2[1])*lerp, (verts1[order[2]].v[2]*scale1[2]+translate1[2])*ilerp+(verts2[order[2]].v[2]*scale2[2]+translate2[2])*lerp); order+=3; } while (--count); glEnd (); } glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // normal alpha blend glDisable(GL_BLEND); glDepthMask(1); // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } /* ============= GL_DrawQ2AliasShadow ============= */ void GL_DrawQ2AliasShadow (entity_t *e, md2_t *pheader, int lastpose, int pose, float lerp) { float ilerp, height, lheight; int *order, count; md2trivertx_t *verts1, *verts2; vec3_t scale1, translate1, scale2, translate2, point; md2frame_t *frame1, *frame2; lheight = currententity->origin[2] - lightspot[2]; height = 0; ilerp = 1.0 - lerp; //new version by muff - fixes bug, easier to read, faster (well slightly) frame1 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * lastpose)); frame2 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * pose)); VectorCopy(frame1->scale, scale1); VectorCopy(frame1->translate, translate1); VectorCopy(frame2->scale, scale2); VectorCopy(frame2->translate, translate2); verts1 = &frame1->verts[0]; verts2 = &frame2->verts[0]; order = (int *)((int) pheader + pheader->ofs_glcmds); height = -lheight + 1.0; while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { point[0] = (verts1[order[2]].v[0]*scale1[0]+translate1[0])*ilerp+(verts2[order[2]].v[0]*scale2[0]+translate2[0])*lerp; point[1] = (verts1[order[2]].v[1]*scale1[1]+translate1[1])*ilerp+(verts2[order[2]].v[1]*scale2[1]+translate2[1])*lerp; point[2] = (verts1[order[2]].v[2]*scale1[2]+translate1[2])*ilerp+(verts2[order[2]].v[2]*scale2[2]+translate2[2])*lerp; point[0] -= shadevector[0]*(point[2]+lheight); point[1] -= shadevector[1]*(point[2]+lheight); point[2] = height; // height -= 0.001; glVertex3fv(point); order+=3; } while (--count); glEnd (); } } /* ================= R_SetupQ2AliasFrame ================= */ void R_SetupQ2AliasFrame (entity_t *e, md2_t *pheader) { int frame; float lerp, lerpscale; frame = e->frame; glPushMatrix (); R_RotateForEntity (e); if ((frame >= pheader->num_frames) || (frame < 0)) { Con_DPrintf ("R_SetupQ2AliasFrame: no such frame %d\n", frame); frame = 0; } 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; lerp = 0; } else lerp = (cl.time - e->draw_lerpstart) * 20.0; } else // uninitialized { e->draw_lastmodel = e->model; e->draw_lastpose = e->draw_pose = frame; e->draw_lerpstart = cl.time; lerp = 0; } if (lerp > 1) lerp = 1; GL_DrawQ2AliasFrame (e, pheader, e->draw_lastpose, frame, lerp); if (r_shadows.value) { glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); glDepthMask(FALSE); // disable zbuffer updates glColor4f (0,0,0,0.5);// * modelalpha); GL_DrawQ2AliasShadow (e, pheader, e->draw_lastpose, frame, lerp); glDepthMask(TRUE); // enable zbuffer updates glEnable (GL_TEXTURE_2D); // glDisable (GL_BLEND); glColor3f (1,1,1); } glPopMatrix (); } at the top, with the other variable definitions add md2_t *pheader; // LH / muff // // locate the proper data // paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); c_alias_polys += paliashdr->numtris; // // locate the proper data // if (clmodel->aliastype == ALIASTYPE_MD2) { pheader = (md2_t *)Mod_Extradata (currententity->model); c_alias_polys += pheader->num_tris; } else { paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); c_alias_polys += paliashdr->numtris; } glPushMatrix (); R_RotateForEntity (e); if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); // double size of eyes, since they are really hard to see in gl glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); } else { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); } if (clmodel->aliastype != ALIASTYPE_MD2) { glPushMatrix (); R_RotateForEntity (e); if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); // double size of eyes, since they are really hard to see in gl glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); } else { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); } } anim = (int)(cl.time*10) & 3; GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]); if (clmodel->aliastype == ALIASTYPE_MD2) GL_Bind(pheader->gl_texturenum[currententity->skinnum]); else { anim = (int)(cl.time*10) & 3; GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]); } R_SetupAliasFrame (currententity->frame, paliashdr); if (clmodel->aliastype == ALIASTYPE_MD2) R_SetupQ2AliasFrame (currententity, pheader); else R_SetupAliasFrame (currententity->frame, paliashdr); glPopMatrix (); if (r_shadows.value) { glPushMatrix (); R_RotateForEntity (e); glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); glColor4f (0,0,0,0.5); GL_DrawAliasShadow (paliashdr, lastposenum); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glColor4f (1,1,1,1); glPopMatrix (); } if (clmodel->aliastype != ALIASTYPE_MD2) { glPopMatrix (); if (r_shadows.value) { glPushMatrix (); R_RotateForEntity (e); glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); glColor4f (0,0,0,0.5); GL_DrawAliasShadow (paliashdr, lastposenum); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glColor4f (1,1,1,1); glPopMatrix (); } } //=================================================================== // // Whole model // /* ======================================================================== .MD2 triangle model file format ======================================================================== */ // LordHavoc: grabbed this from the Q2 utility source, // renamed a things to avoid conflicts #define MD2IDALIASHEADER (('2'<<24)+('P'<<16)+('D'<<8)+'I') #define MD2ALIAS_VERSION 8 #define MD2MAX_TRIANGLES 4096 #define MD2MAX_VERTS 2048 #define MD2MAX_FRAMES 512 #define MD2MAX_SKINS 32 #define MD2MAX_SKINNAME 64 // sanity checking size #define MD2MAX_SIZE (1024*4200) typedef struct { short s; short t; } md2stvert_t; typedef struct { short index_xyz[3]; short index_st[3]; } md2triangle_t; typedef struct { byte v[3]; // scaled byte to fit in frame mins/maxs byte lightnormalindex; } md2trivertx_t; #define MD2TRIVERTX_V0 0 #define MD2TRIVERTX_V1 1 #define MD2TRIVERTX_V2 2 #define MD2TRIVERTX_LNI 3 #define MD2TRIVERTX_SIZE 4 typedef struct { float scale[3]; // multiply byte verts by this float translate[3]; // then add this char name[16]; // frame name from grabbing md2trivertx_t verts[1]; // variable sized } md2frame_t; // the glcmd format: // a positive integer starts a tristrip command, followed by that many // vertex structures. // a negative integer starts a trifan command, followed by -x vertexes // a zero indicates the end of the command list. // a vertex consists of a floating point s, a floating point t, // and an integer vertex index. typedef struct { int ident; int version; int skinwidth; int skinheight; int framesize; // byte size of each frame int num_skins; int num_xyz; int num_st; // greater than num_xyz for seams int num_tris; int num_glcmds; // dwords in strip/fan command list int num_frames; int ofs_skins; // each skin is a MAX_SKINNAME string int ofs_st; // byte offset from start for stverts int ofs_tris; // offset for dtriangles int ofs_frames; // offset for first frame int ofs_glcmds; int ofs_end; // end of file int gl_texturenum[MAX_SKINS]; } md2_t; #define ALIASTYPE_MDL 1 #define ALIASTYPE_MD2 2 modtype_t type; int aliastype; // LordHavoc: Q2 model support at the top of gl_model.c add void Mod_LoadQ2AliasModel (model_t *mod, void *buffer); case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case MD2IDALIASHEADER: // LordHavoc: added Quake2 model support Mod_LoadQ2AliasModel (mod, buf); break; add /* ================= Mod_LoadQ2AliasModel ================= */ int loadtextureimage (int texnum, char* filename, qboolean complain, int matchwidth, int matchheight); void Mod_LoadQ2AliasModel (model_t *mod, void *buffer) { int i, j, version, numframes, numskins, size, *pinglcmd, *poutglcmd, start, end, total; md2_t *pinmodel, *pheader; md2triangle_t *pintriangles, *pouttriangles; md2frame_t *pinframe, *poutframe; char *pinskins, skinname[256], *skinnamebase; start = Hunk_LowMark (); pinmodel = (md2_t *)buffer; version = LittleLong (pinmodel->version); if (version != MD2ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, MD2ALIAS_VERSION); mod->type = mod_alias; mod->aliastype = ALIASTYPE_MD2; // LordHavoc: see pheader ofs adjustment code below for why this is bigger size = LittleLong(pinmodel->ofs_end) + sizeof(md2_t); if (size <= 0 || size >= MD2MAX_SIZE) Sys_Error ("%s is not a valid model", mod->name); pheader = Hunk_AllocName (size, loadname); mod->flags = 0; // there are no MD2 flags // endian-adjust and copy the data, starting with the alias model header for (i = 0;i < 17;i++) // LordHavoc: err... FIXME or something... ((int*)pheader)[i] = LittleLong(((int *)pinmodel)[i]); mod->numframes = numframes = pheader->num_frames; mod->synctype = ST_RAND; if (pheader->ofs_skins <= 0 || pheader->ofs_skins >= pheader->ofs_end) Sys_Error ("%s is not a valid model", mod->name); if (pheader->ofs_st <= 0 || pheader->ofs_st >= pheader->ofs_end) Sys_Error ("%s is not a valid model", mod->name); if (pheader->ofs_tris <= 0 || pheader->ofs_tris >= pheader->ofs_end) Sys_Error ("%s is not a valid model", mod->name); if (pheader->ofs_frames <= 0 || pheader->ofs_frames >= pheader->ofs_end) Sys_Error ("%s is not a valid model", mod->name); if (pheader->ofs_glcmds <= 0 || pheader->ofs_glcmds >= pheader->ofs_end) Sys_Error ("%s is not a valid model", mod->name); if (pheader->num_tris < 1 || pheader->num_tris > MD2MAX_TRIANGLES) Sys_Error ("%s has invalid number of triangles: %i", mod->name, pheader->num_tris); if (pheader->num_xyz < 1 || pheader->num_xyz > MD2MAX_VERTS) Sys_Error ("%s has invalid number of vertices: %i", mod->name, pheader->num_xyz); if (pheader->num_frames < 1 || pheader->num_frames > 256) //MD2MAX_FRAMES) Sys_Error ("%s has invalid number of frames: %i", mod->name, pheader->num_frames); if (pheader->num_skins < 0 || pheader->num_skins > MD2MAX_SKINS) Sys_Error ("%s has invalid number of skins: %i", mod->name, pheader->num_skins); // LordHavoc: adjust offsets in new model to give us some room for the bigger header // cheap offsetting trick, just offset it all by the pheader size...mildly wasteful for (i = 0;i < 7;i++) ((int*)&pheader->ofs_skins)[i] += sizeof(pheader); // load the skins if (pheader->num_skins) { pinskins = (void*)((int) pinmodel + LittleLong(pinmodel->ofs_skins)); for (i = 0;i < pheader->num_skins;i++) { pheader->gl_texturenum[i] = loadtextureimage (-1, pinskins, TRUE, 0, 0); pinskins += MD2MAX_SKINNAME; } } // load triangles pintriangles = (void*)((int) pinmodel + LittleLong(pinmodel->ofs_tris)); pouttriangles = (void*)((int) pheader + pheader->ofs_tris); // swap the triangle list for (i=0 ; i < pheader->num_tris ; i++) { for (j=0 ; j<3 ; j++) { pouttriangles->index_xyz[j] = LittleShort (pintriangles->index_xyz[j]); pouttriangles->index_st[j] = LittleShort (pintriangles->index_st[j]); if (pouttriangles->index_xyz[j] >= pheader->num_xyz) Sys_Error ("%s has invalid vertex indices", mod->name); if (pouttriangles->index_st[j] >= pheader->num_st) Sys_Error ("%s has invalid vertex indices", mod->name); } pintriangles++; pouttriangles++; } // // load the frames // pinframe = (void*) ((int) pinmodel + LittleLong(pinmodel->ofs_frames)); poutframe = (void*) ((int) pheader + pheader->ofs_frames); for (i=0 ; i < numframes ; i++) { for (j = 0;j < 3;j++) { poutframe->scale[j] = LittleFloat(pinframe->scale[j]); poutframe->translate[j] = LittleFloat(pinframe->translate[j]); } for (j = 0;j < 17;j++) poutframe->name[j] = pinframe->name[j]; for (j = 0;j < pheader->num_xyz;j++) { poutframe->verts[j].v[0] = pinframe->verts[j].v[0]; poutframe->verts[j].v[1] = pinframe->verts[j].v[1]; poutframe->verts[j].v[2] = pinframe->verts[j].v[2]; poutframe->verts[j].lightnormalindex = pinframe->verts[j].lightnormalindex; } pinframe = (void*) &pinframe->verts[j].v[0]; poutframe = (void*) &poutframe->verts[j].v[0]; } // LordHavoc: I may fix this at some point mod->mins[0] = mod->mins[1] = mod->mins[2] = -64; mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 64; // load the draw list pinglcmd = (void*) ((int) pinmodel + LittleLong(pinmodel->ofs_glcmds)); poutglcmd = (void*) ((int) pheader + pheader->ofs_glcmds); for (i = 0;i < pheader->num_glcmds;i++) *poutglcmd++ = LittleLong(*pinglcmd++); // move the complete, relocatable alias model to the cache end = Hunk_LowMark (); total = end - start; Cache_Alloc (&mod->cache, total, loadname); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } vec3_t angles; // LordHavoc: added support for Q2 interpolation int draw_lastpose, draw_pose; // for interpolation float draw_lerpstart; // for interpolation struct model_s *draw_lastmodel; // for interpolation find the line that looks like this int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha); add an extra variable at the end to look like this :- int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel); find this code :- typedef struct { int texnum; char identifier[64]; int width, height; qboolean mipmap; } gltexture_t; typedef struct { int texnum; char identifier[64]; int width, height; qboolean mipmap; // LordHavoc: 32bit textures int bytesperpixel; // LordHavoc: checksum sort of thing to identify ALL cache mismatchs int lhcsum; } gltexture_t; add int image_width; int image_height; /* ================================================================= PCX Loading ================================================================= */ typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hres,vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned data; // unbounded } pcx_t; /* ============ LoadPCX ============ */ byte* LoadPCX (FILE *f, int matchwidth, int matchheight) { pcx_t *pcx, pcxbuf; byte palette[768]; byte *pix, *image_rgba; int x, y; int dataByte, runLength; int count; // // parse the PCX file // fread (&pcxbuf, 1, sizeof(pcxbuf), f); pcx = &pcxbuf; if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax >= 320 || pcx->ymax >= 256) { Con_Printf ("Bad pcx file\n"); return NULL; } if (matchwidth && (pcx->xmax+1) != matchwidth) return NULL; if (matchheight && (pcx->ymax+1) != matchheight) return NULL; // seek to palette fseek (f, -768, SEEK_END); fread (palette, 1, 768, f); fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); count = (pcx->xmax+1) * (pcx->ymax+1); image_rgba = malloc( count * 4); for (y=0 ; y<=pcx->ymax ; y++) { pix = image_rgba + 4*y*(pcx->xmax+1); for (x=0 ; x<=pcx->xmax ; ) // muff - fixed - was referencing ymax { dataByte = fgetc(f); if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = fgetc(f); } else runLength = 1; while(runLength-- > 0) { pix[0] = palette[dataByte*3]; pix[1] = palette[dataByte*3+1]; pix[2] = palette[dataByte*3+2]; pix[3] = 255; pix += 4; x++; } } } image_width = pcx->xmax+1; image_height = pcx->ymax+1; return image_rgba; } /* ========================================================= TARGA LOADING ========================================================= */ typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; TargaHeader targa_header; int fgetLittleShort (FILE *f) { byte b1, b2; b1 = fgetc(f); b2 = fgetc(f); return (short)(b1 + b2*256); } int fgetLittleLong (FILE *f) { byte b1, b2, b3, b4; b1 = fgetc(f); b2 = fgetc(f); b3 = fgetc(f); b4 = fgetc(f); return b1 + (b2<<8) + (b3<<16) + (b4<<24); } /* ============= LoadTGA ============= */ byte* LoadTGA (FILE *fin, int matchwidth, int matchheight) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *image_rgba; targa_header.id_length = fgetc(fin); targa_header.colormap_type = fgetc(fin); targa_header.image_type = fgetc(fin); targa_header.colormap_index = fgetLittleShort(fin); targa_header.colormap_length = fgetLittleShort(fin); targa_header.colormap_size = fgetc(fin); targa_header.x_origin = fgetLittleShort(fin); targa_header.y_origin = fgetLittleShort(fin); targa_header.width = fgetLittleShort(fin); targa_header.height = fgetLittleShort(fin); if (matchwidth && targa_header.width != matchwidth) return NULL; if (matchheight && targa_header.height != matchheight) return NULL; targa_header.pixel_size = fgetc(fin); targa_header.attributes = fgetc(fin); if (targa_header.image_type!=2 && targa_header.image_type!=10) Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; image_rgba = malloc (numPixels*4); if (targa_header.id_length != 0) fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment if (targa_header.image_type==2) { // Uncompressed, RGB images for(row=rows-1; row>=0; row--) { pixbuf = image_rgba + row*columns*4; for(column=0; column < columns; column++) { unsigned char red,green,blue,alphabyte; switch (targa_header.pixel_size) { case 24: blue = getc(fin); green = getc(fin); red = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; } } } } else if (targa_header.image_type==10) { // Runlength encoded RGB images unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j; for(row=rows-1; row>=0; row--) { pixbuf = image_rgba + row*columns*4; for(column=0; column < columns; ) { packetHeader=getc(fin); packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) { // run-length packet switch (targa_header.pixel_size) { case 24: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = 255; break; case 32: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = getc(fin); break; } for(j=0;j < packetSize;j++) { *pixbuf++=red; *pixbuf++=green; *pixbuf++=blue; *pixbuf++=alphabyte; column++; if (column==columns) { // run spans across rows column=0; if (row>0) row--; else goto breakOut; pixbuf = image_rgba + row*columns*4; } } } else { // non run-length packet for(j=0;j < packetSize;j++) { switch (targa_header.pixel_size) { case 24: blue = getc(fin); green = getc(fin); red = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; } column++; if (column==columns) { // pixel packet run spans across rows column=0; if (row>0) row--; else goto breakOut; pixbuf = image_rgba + row*columns*4; } } } } breakOut:; } } fclose(fin); image_width = columns; image_height = rows; return image_rgba; } byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight) { FILE *f; char basename[128], name[128]; byte *image_rgba; COM_StripExtension(filename, basename); // strip the extension to allow TGA skins on Q2 models despite the .pcx in the skin name sprintf (name, "%s.tga", basename); COM_FOpenFile (name, &f); if (f) return LoadTGA (f, matchwidth, matchheight); sprintf (name, "%s.pcx", basename); COM_FOpenFile (name, &f); if (f) return LoadPCX (f, matchwidth, matchheight); // if (image_rgba = W_GetTexture(basename, matchwidth, matchheight)) // return image_rgba; if (complain) Con_Printf ("Couldn't load %s.tga or .pcx\n", filename); return NULL; } int loadtextureimage (int texnum, char* filename, qboolean complain, int matchwidth, int matchheight) { byte *data; if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight))) return 0; if (texnum >= 0) // specific texnum, not cached { GL_Bind(texnum); GL_Upload32 (data, image_width, image_height, true, true); free(data); return texnum; } else // any texnum, cached { texnum = GL_LoadTexture (filename, image_width, image_height, data, true, true, 4); free(data); return texnum; } } int lhcsumtable[256]; int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel) { qboolean noalpha; int i, p, s, lhcsum; gltexture_t *glt; // LordHavoc: do a checksum to confirm the data really is the same as previous // occurances. well this isn't exactly a checksum, it's better than that but // not following any standards. lhcsum = 0; s = width*height*bytesperpixel; for (i = 0;i < 256;i++) lhcsumtable[i] = i + 1; for (i = 0;i < s;i++) lhcsum += (lhcsumtable[data[i] & 255]++); // see if the texture is allready present if (identifier[0]) { for (i=0, glt=gltextures ; i < numgltextures ; i++, glt++) { if (!strcmp (identifier, glt->identifier)) { // LordHavoc: everyone hates cache mismatchs, so I fixed it if (lhcsum != glt->lhcsum || width != glt->width || height != glt->height) { Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n"); goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace //Sys_Error ("GL_LoadTexture: cache mismatch"); } return glt->texnum; } } } // LordHavoc: this was an else condition, causing disasterous results, // whoever at id or threewave must've been half asleep... glt = &gltextures[numgltextures]; numgltextures++; strcpy (glt->identifier, identifier); glt->texnum = texture_extension_number; texture_extension_number++; // LordHavoc: label to drop out of the loop into the setup code GL_LoadTexture_setup: glt->lhcsum = lhcsum; // LordHavoc: used to verify textures are identical glt->width = width; glt->height = height; glt->mipmap = mipmap; glt->bytesperpixel = bytesperpixel; if (!isDedicated) { GL_Bind(glt->texnum); // LordHavoc: 32bit textures if (bytesperpixel == 1) GL_Upload8 (data, width, height, mipmap, alpha); else if (bytesperpixel == 4) GL_Upload32 (data, width, height, mipmap, true); else Sys_Error("GL_LoadTexture: unknown bytesperpixel\n"); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } return glt->texnum; } char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true); char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true,1); There are about 3 of these to find, and the code won't compile without you fixing them all. Ignore the one near the end of the file that was part of the code we added before texnum = GL_LoadTexture (filename, image_width, image_height, data, true, true, 4); there are 4 cases here to fix And that should be it for the main source code, so compile. Right now to enable all this and let us see what all our hard work has done. We now need to make a QuakeC mod......AAAAAAAHHHHH I hear you cry. Well Ok then I've attached a new soldier.qc file for you to compile straight in. Anyone interested in how it all hangs together can dig through the code, but essentially the new model is defined at the bottom. The code puts the gunner from Quake2 in place of the soldier in Quake.
The whole 'models/monsters/gunner' tree should be put into the 'id1' directory in your Q1 structure for this to work. I've then gone some of the way to editting the animation frames to reflect the gunner model rather than the original soldier.mdl, and added some sound effect to the walk animation. Hope this is useful
muff FILES
soldier.qc file |