Tutorial *98*
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.
open gl_rmain.c

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 ();

}

in the R_DrawAliasModel routine

at the top, with the other variable definitions

add


	md2_t		*pheader; // LH / muff

a bit lower down find


	//

	// locate the proper data

	//

	paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model);

	c_alias_polys += paliashdr->numtris;

and replace with



	//

	// 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;

	}

just below, find the code :-


    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]);

	}

edit so it looks like:-


 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]);

	}

 }

just below find


    anim = (int)(cl.time*10) & 3;

    GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]);

replace with


	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]);

	}

lower down replace


	R_SetupAliasFrame (currententity->frame, paliashdr);

with


	if (clmodel->aliastype == ALIASTYPE_MD2)

		R_SetupQ2AliasFrame (currententity, pheader);

	else

		R_SetupAliasFrame (currententity->frame, paliashdr);

at the end of this procedure find :-


	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 ();

	}

edit to look like this :-


 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 ();

	}

 }

in gl_model.h just above the code that looks like this


//===================================================================



//

// Whole model

//

add


/*

========================================================================



.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

now inside the definition for the model_t structure just below where we've added the code above, just after :-


	modtype_t	type;

add :-


	int			aliastype; // LordHavoc: Q2 model support

in gl_model.c

at the top of gl_model.c add



	void Mod_LoadQ2AliasModel (model_t *mod, void *buffer);

in *Mod_LoadModel procedure just after :-


	case IDPOLYHEADER:

		Mod_LoadAliasModel (mod, buf);

		break;

add


	case MD2IDALIASHEADER: // LordHavoc: added Quake2 model support

		Mod_LoadQ2AliasModel (mod, buf);

		break;

after the Mod_LoadAliasModel procedure

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);

}

in render.h within the entity_t structure definition, just after


	vec3_t					angles;

add


	// 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

now open glquake.h

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);

at the top of gl_draw.c

find this code :-



 typedef struct

 {

	int		texnum;

	char	identifier[64];

	int		width, height;

	qboolean	mipmap;

 } gltexture_t;

and edit to look like this


 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;

at the bottom of gl_draw.c

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;

	}

}

now find the GL_LoadTexture in gl_draw.c and replace with this code


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;

}

still in gl_draw.c do a search for GL_LoadTexture what we now need to do is add the extra variable to the end of all of the calls. In all cases that you find a call add a ,1 at the end of the line in the brackets so


	char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true);

becomes


	char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true,1);

for example.

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);

Now do the same search in gl_model.c and do the same again

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
muff@yakko.globalnet.co.uk

FILES

soldier.qc file

Compiled EXE and PROGS.DAT for the lazy or impatient



 
Sign up
Login:
Passwd:
[Remember Me]