/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// gl_rmain.c

#include "quakedef.h"

entity_t	r_worldentity;
float		r_world_matrix[16];
qboolean	r_cache_thrash;		// compatibility

vec3_t		modelorg, r_entorigin;
entity_t	*currententity;

int			r_visframecount;	// bumped when going to a new PVS
int			r_framecount;		// used for dlight push checking
int			cl_hurtblur;		// R00k
mplane_t	frustum[4];

float	wateralpha;

int		c_brush_polys, c_alias_polys, c_md3_polys;

int		particletexture;	// little dot for particles
int		playertextures;		// playerfbtextures;
							// up to 16 color translated skins

int		fb_skins[MAX_SCOREBOARD];

int		skyboxtextures;		// by joe
int		underwatertexture, detailtexture;
int		logoTex;
int		chrometexture, glasstexture;
int		solidskytexture2, alphaskytexture2;//R00k
int		decal_blood1,decal_blood2,decal_blood3,decal_burn, decal_mark, decal_glow;


#define INTERP_WEAP_MAXNUM		24
#define INTERP_WEAP_MINDIST		5000
#define INTERP_WEAP_MAXDIST		95000
#define	INTERP_MINDIST			70
#define	INTERP_MAXDIST			300

typedef struct interpolated_weapon
{
	char	name[MAX_QPATH];
	int	maxDistance;
} interp_weapon_t;

static	interp_weapon_t	interpolated_weapons[INTERP_WEAP_MAXNUM];
static	int		interp_weap_num = 0;

int DoWeaponInterpolation (void);

// view origin
vec3_t	vup;
vec3_t	vpn;
vec3_t	vright;
vec3_t	r_origin;

// screen size info
refdef_t	r_refdef;

mleaf_t		*r_viewleaf, *r_oldviewleaf;
mleaf_t		*r_viewleaf2, *r_oldviewleaf2;	// for watervis hack

texture_t	*r_notexture_mip;

int		d_lightstylevalue[256];	// 8.8 fraction of base light value

cvar_t	cl_autodemo			= {"cl_autodemo","0",false,false};	//R00k
cvar_t	cl_gun_fovscale		= {"cl_gun_fovscale","1",true};//R00k
cvar_t	cl_teamskin			= {"cl_teamskin","0",true};//R00k
cvar_t	cl_teamflags		= {"cl_teamflags","0",true};//R00k
cvar_t	r_drawentities		= {"r_drawentities", "1"};
cvar_t	r_drawviewmodel		= {"r_drawviewmodel", "1",true};
cvar_t	r_drawviewmodelsize = {"r_drawviewmodelsize", "1", true};
cvar_t	r_interpolate_light	= {"r_interpolate_light", "0",true};
cvar_t	r_speeds			= {"r_speeds", "0"};
cvar_t	r_fullbright		= {"r_fullbright", "0"};
cvar_t	r_lightmap			= {"r_lightmap", "0"};
cvar_t	r_shadows			= {"r_shadows", "0"};
cvar_t	r_wateralpha		= {"r_wateralpha", "1",true};
cvar_t	r_lavaalpha			= {"r_lavaalpha", "1",true};
cvar_t	r_telealpha			= {"r_telealpha", "1",true};
cvar_t	r_turbalpha_distance = {"r_turbalpha_distance", "512",true};
cvar_t	r_dynamic			= {"r_dynamic", "1",true};
cvar_t	r_novis				= {"r_novis", "0", true};
cvar_t	r_fastsky			= {"r_fastsky", "0",true};
cvar_t	r_fastturb			= {"r_fastturb", "0",true};
cvar_t	r_drawflame			= {"r_drawflame","1",true};
cvar_t	r_drawbbox			= {"r_drawbbox", "0",false};
cvar_t	r_drawlocs			= {"r_drawlocs", "0",false};
cvar_t	r_farclip			= {"r_farclip", "4096"};
qboolean OnChange_r_skybox (cvar_t *var, char *string);
cvar_t	r_skybox			= {"r_skybox", "", false, false, OnChange_r_skybox};
cvar_t	r_skyscroll			= {"r_skyscroll", "0",true};
cvar_t	r_skyspeed			= {"r_skyspeed", "2", true};//MHQuake
cvar_t	r_simpleitems		= {"r_simpleitems", "0",true};
cvar_t	r_outline			= {"r_outline", "0",true};	//QMB
cvar_t	r_celshading		= {"r_celshading", "0",true};//QMB
//cvar_t	r_fullbrightskins	= {"r_fullbrightskins", "0"};
cvar_t	r_waterwarp			= {"r_waterwarp", "0"};
cvar_t  r_waterripple		= {"r_waterripple", "0"};
cvar_t	gl_lavasmoke		= {"gl_lavasmoke", "0",true};	//MHQuake
cvar_t	gl_shiny			= {"gl_shiny", "0",true};
cvar_t	gl_rain				= {"gl_rain", "0",true};

cvar_t	gl_powerupshells	= {"gl_powerupshells", "0", true};
cvar_t	gl_powerupshell_size = {"gl_powerupshell_size", "0", true};
cvar_t	gl_damageshells		= {"gl_damageshells", "0", true};
cvar_t	scr_bloodsplat		= {"scr_bloodsplat", "0", true};

extern cvar_t r_bloom;
extern cvar_t r_bloom_alpha;
extern cvar_t r_bloom_diamond_size;
extern cvar_t r_bloom_intensity;
extern cvar_t r_bloom_darken;
extern cvar_t r_bloom_sample_size;
extern cvar_t r_bloom_fast_sample;
extern cvar_t vid_bpp;

cvar_t  gl_interpolate_transform = {"gl_interpolate_transform", "1",true};

cvar_t	gl_clear			= {"gl_clear", "0",false};
cvar_t	gl_cull				= {"gl_cull", "1",false};
cvar_t	gl_ztrick			= {"gl_ztrick", "0"};//default OFF!
cvar_t	gl_smoothmodels		= {"gl_smoothmodels", "1",false};
cvar_t	gl_affinemodels		= {"gl_affinemodels", "0"};
cvar_t	gl_polyblend		= {"gl_polyblend", "1"};
cvar_t	gl_flashblend		= {"gl_flashblend", "0",true};
cvar_t	gl_playermip		= {"gl_playermip", "0"};
cvar_t	gl_nocolors			= {"gl_nocolors", "0"};
cvar_t	gl_finish			= {"gl_finish", "0"};
cvar_t	gl_loadlitfiles		= {"gl_loadlitfiles", "1",true};
cvar_t	gl_loadq3models		= {"gl_loadq3models", "0",true};
cvar_t	gl_doubleeyes		= {"gl_doubleeyes", "0"};
cvar_t	gl_interdist		= {"gl_interpolate_distance", "512"};//was 17000
cvar_t  gl_waterfog			= {"gl_waterfog", "0",true};
cvar_t  gl_waterfog_density = {"gl_waterfog_density", "1",true};
cvar_t	gl_detail			= {"gl_detail", "0",true};
cvar_t	gl_caustics			= {"gl_caustics", "1",true};
cvar_t	gl_ringalpha		= {"gl_ringalpha", "0.4"};
cvar_t	gl_fb_bmodels		= {"gl_fb_bmodels", "1",true};
cvar_t	gl_fb_models		= {"gl_fb_models", "1", true};
cvar_t  gl_vertexlights		= {"gl_vertexlights", "0",true};
cvar_t	gl_laserpoint		= {"gl_laserpoint", "0",true};
cvar_t	gl_part_explosions	= {"gl_part_explosions", "1",true};
cvar_t	gl_part_trails		= {"gl_part_trails", "1",true};
cvar_t	gl_part_sparks		= {"gl_part_sparks", "1",true};
cvar_t	gl_part_gunshots	= {"gl_part_gunshots", "1",true};
cvar_t	gl_part_blood		= {"gl_part_blood", "1",true};
cvar_t	gl_part_telesplash	= {"gl_part_telesplash", "1",true};
cvar_t	gl_part_blobs		= {"gl_part_blobs", "1",true};
cvar_t	gl_part_lavasplash	= {"gl_part_lavasplash", "1",true};
cvar_t	gl_part_flames		= {"gl_part_flames", "1",true};
cvar_t	gl_part_lightning	= {"gl_part_lightning", "1",true};
cvar_t	gl_part_flies		= {"gl_part_flies", "1",true};
 
cvar_t	gl_beergoggles		= {"gl_beergoggles", "0"};//R00k
cvar_t	gl_nightmare		= {"gl_nightmare", "0"};//R00k
cvar_t	gl_deathblur		= {"gl_deathblur", "0",true};//R00k
cvar_t	gl_hurtblur			= {"gl_hurtblur", "0",true};//R00k
cvar_t	gl_blur_alpha		= {"gl_blur_alpha", "0.8", false};

cvar_t	gl_anisotropic		= {"gl_anisotropic","1", true};
cvar_t  gl_fogenable		= {"gl_fogenable", "0"};
cvar_t	gl_fogdensity		= {"gl_fogdensity", "0.153"};
cvar_t  gl_fogstart			= {"gl_fogstart", "50.0"};
cvar_t  gl_fogend			= {"gl_fogend", "2048.0"};
cvar_t  gl_fogred			= {"gl_fogred", "0.4"};
cvar_t  gl_foggreen			= {"gl_foggreen", "0.4"};
cvar_t  gl_fogblue			= {"gl_fogblue", "0.4"};
cvar_t  gl_fogsky			= {"gl_fogsky", "1"}; 

extern qboolean have_stencil; // Stencil shadows - MrG
void R_MarkLeaves (void);
void R_InitBubble (void);

extern	int	particle_mode;	// off: classic, on: QMB(default)
extern	int	decals_enabled;
extern	cvar_t	scr_fov;

/*
=================
R_CullBox -- replaced with new function from lordhavoc

Returns true if the box is completely outside the frustum
=================
*/
qboolean R_CullBox (vec3_t emins, vec3_t emaxs)
{
	int i;
	mplane_t *p;
	for (i = 0;i < 4;i++)
	{
		p = frustum + i;
		switch(p->signbits)
		{
		default:
		case 0:
			if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist)
				return true;
			break;
		case 1:
			if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist)
				return true;
			break;
		case 2:
			if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist)
				return true;
			break;
		case 3:
			if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist)
				return true;
			break;
		case 4:
			if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist)
				return true;
			break;
		case 5:
			if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist)
				return true;
			break;
		case 6:
			if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist)
				return true;
			break;
		case 7:
			if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist)
				return true;
			break;
		}
	}
	return false;
}

void GL_PolygonOffset (int offset)
{
	if (offset > 0)
	{
		glEnable (GL_POLYGON_OFFSET_FILL);
		glEnable (GL_POLYGON_OFFSET_LINE);
		glPolygonOffset(1, offset);
	}
	else if (offset < 0)
	{
		glEnable (GL_POLYGON_OFFSET_FILL);
		glEnable (GL_POLYGON_OFFSET_LINE);
		glPolygonOffset(-1, offset);
	}
	else
	{
		glDisable (GL_POLYGON_OFFSET_FILL);
		glDisable (GL_POLYGON_OFFSET_LINE);
	}
}

void GL_DrawSimpleBox(vec3_t org, vec3_t mins, vec3_t maxs)
{
		glPushMatrix ();
		glTranslatef(org[0], org[1], org[2]); 
		glEnable (GL_BLEND);
		glBegin(GL_LINE_LOOP); 		
			glLineWidth (2.0f);
		glVertex3f(maxs[0], maxs[1], maxs[2]); 
		glVertex3f(mins[0], maxs[1], maxs[2]); 
		glVertex3f(mins[0], mins[1], maxs[2]); 
		glVertex3f(maxs[0], mins[1], maxs[2]); 		
		glEnd();	 

		glBegin(GL_LINE_LOOP);
			glLineWidth (2.0f);
		glVertex3f(maxs[0], maxs[1], mins[2]); 
		glVertex3f(mins[0], maxs[1], mins[2]); 
		glVertex3f(mins[0], mins[1], mins[2]); 
		glVertex3f(maxs[0], mins[1], mins[2]); 		
		glEnd(); 

		glBegin(GL_LINES);
			glLineWidth (2.0f);
		glVertex3f(maxs[0], maxs[1], maxs[2]); 
		glVertex3f(maxs[0], maxs[1], mins[2]); 
		glVertex3f(mins[0], maxs[1], maxs[2]); 
		glVertex3f(mins[0], maxs[1], mins[2]); 
		glVertex3f(mins[0], mins[1], maxs[2]); 
		glVertex3f(mins[0], mins[1], mins[2]); 
		glVertex3f(maxs[0], mins[1], maxs[2]); 
		glVertex3f(maxs[0], mins[1], mins[2]); 
		glEnd(); 
		glDisable (GL_BLEND);
		glPopMatrix ();
}
/*
=================
R_CullSphere

Returns true if the sphere is completely outside the frustum
=================
*/
qboolean R_CullSphere (vec3_t centre, float radius)
{
	int		i;
	mplane_t	*p;

	for (i=0, p=frustum ; i<4 ; i++, p++)
	{
		if (PlaneDiff(centre, p) <= -radius)		
			return true;
	}

	return false;
}

/*
=============
R_RotateForEntity
=============
*/
void R_RotateForEntity (entity_t *ent, qboolean shadow)
{
	float	lerpfrac, timepassed;
	vec3_t	d, interpolated;
	int	i;

	// positional interpolation
	timepassed = cl.time - ent->translate_start_time;

	if (ent->translate_start_time == 0 || timepassed > 1)
	{
		ent->translate_start_time = cl.time;
		VectorCopy (ent->origin, ent->origin1);
		VectorCopy (ent->origin, ent->origin2);
	}

	if (!VectorCompare(ent->origin, ent->origin2))
	{
		ent->translate_start_time = cl.time;
		VectorCopy (ent->origin2, ent->origin1);
		VectorCopy (ent->origin,  ent->origin2);
		lerpfrac = 0;
	}
	else
	{
		lerpfrac = timepassed / 0.1;
		if (cl.paused || lerpfrac > 1)
			lerpfrac = 1;
	}

	VectorInterpolate (ent->origin1, lerpfrac, ent->origin2, interpolated);
	glTranslatef (interpolated[0], interpolated[1], interpolated[2]);

	// orientation interpolation (Euler angles, yuck!)
	timepassed = cl.time - ent->rotate_start_time; 

	if (ent->rotate_start_time == 0 || timepassed > 1)
	{
		ent->rotate_start_time = cl.time;
		VectorCopy (ent->angles, ent->angles1);
		VectorCopy (ent->angles, ent->angles2);
	}

	if (!VectorCompare(ent->angles, ent->angles2))
	{
		ent->rotate_start_time = cl.time;
		VectorCopy (ent->angles2, ent->angles1);
		VectorCopy (ent->angles,  ent->angles2);
		lerpfrac = 0;
	}
	else
	{
		lerpfrac = timepassed / 0.1;
		if (cl.paused || lerpfrac > 1)
			lerpfrac = 1;
	}

	VectorSubtract (ent->angles2, ent->angles1, d);

	// always interpolate along the shortest path
	for (i=0 ; i<3 ; i++)
	{
		if (d[i] > 180)
			d[i] -= 360;
		else if (d[i] < -180)
			d[i] += 360;
	}

	glRotatef (ent->angles1[1] + (lerpfrac * d[1]), 0, 0, 1);

	if (!shadow)
	{
		glRotatef (-ent->angles1[0] + (-lerpfrac * d[0]), 0, 1, 0);
		glRotatef (ent->angles1[2] + (lerpfrac * d[2]), 1, 0, 0);
	}
}

/*
=============
R_RotateForViewEntity
=============
*/
void R_RotateForViewEntity (entity_t *ent)
{
	glTranslatef (ent->origin[0], ent->origin[1], ent->origin[2]);

	glRotatef (ent->angles[1], 0, 0, 1);
	glRotatef (-ent->angles[0], 0, 1, 0);
	glRotatef (ent->angles[2], 1, 0, 0);
}


void GL_FullscreenQuad(int texture, float alpha)
{
	int vwidth = 1, vheight = 1;
	float vs, vt;

	while (vwidth < glwidth)
	{
		vwidth *= 2;
	}
	while (vheight < glheight)
	{
		vheight *= 2;
	}

	glViewport (glx, gly, glwidth, glheight);

	GL_Bind(texture);

	// go 2d
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity ();
	glOrtho  (0, glwidth, 0, glheight, -99999, 99999);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity ();

	vs = ((float)glwidth / vwidth);
	vt = ((float)glheight / vheight) ;

	glDisable (GL_DEPTH_TEST);
	glDisable (GL_CULL_FACE);
	glDisable (GL_ALPHA_TEST);
	glEnable (GL_BLEND);
	
	glAlphaFunc(GL_GREATER,0.000f);
	glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glDepthMask(0);

	glColor4f(1, 1, 1, alpha);

	glBegin(GL_QUADS);
		glTexCoord2f(0, 0);
		glVertex2f(0, 0);
		glTexCoord2f(vs , 0);
		glVertex2f(glwidth, 0);
		glTexCoord2f(vs , vt);
		glVertex2f(glwidth, glheight);
		glTexCoord2f(0, vt);
		glVertex2f(0, glheight);
	glEnd();

	glEnable(GL_DEPTH_TEST);
	glDepthMask(1);
	glDisable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER,0.666f);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glColor3f (1,1,1);   
    glDisable (GL_BLEND);   
    glEnable (GL_TEXTURE_2D);   
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);   
	//?glEnable(GL_CULL_FACE);
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();		
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();		
}
/*
===============================================================================

				SPRITE MODELS

===============================================================================
*/

/*
================
R_GetSpriteFrame
================
*/
mspriteframe_t *R_GetSpriteFrame (entity_t *currententity)
{
	msprite_t	*psprite;
	mspritegroup_t	*pspritegroup;
	mspriteframe_t	*pspriteframe;
	int		i, numframes, frame;
	float		*pintervals, fullinterval, targettime, time;

	psprite = currententity->model->cache.data;
	frame = currententity->frame;

	if ((frame >= psprite->numframes) || (frame < 0))
	{
		Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
		frame = 0;
	}

	if (psprite->frames[frame].type == SPR_SINGLE)
	{
		pspriteframe = psprite->frames[frame].frameptr;
	}
	else
	{
		pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
		pintervals = pspritegroup->intervals;
		numframes = pspritegroup->numframes;
		fullinterval = pintervals[numframes-1];

		time = cl.time + currententity->syncbase;

	// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
	// are positive, so we don't have to worry about division by 0
		targettime = time - ((int)(time / fullinterval)) * fullinterval;

		for (i=0 ; i<(numframes-1) ; i++)
		{
			if (pintervals[i] > targettime)
				break;
		}

		pspriteframe = pspritegroup->frames[i];
	}

	return pspriteframe;
}

/*
=================
R_DrawSpriteModel
=================
*/
void R_DrawSpriteModel (entity_t *e)
{
	vec3_t	point, forward, right, up;
	mspriteframe_t *frame;
	msprite_t *psprite;
	extern void R_SetupLighting (entity_t *ent);
	float out[3], l, alpha;
	extern float shadelight, ambientlight;

	R_SetupLighting(e);
	l = ((shadelight + ambientlight) / 256);
	out[0]=out[1]=out[2]= 0.99609375;

	// don't even bother culling, because it's just a single
	// polygon without a surface cache
	frame = R_GetSpriteFrame (e);
	psprite = currententity->model->cache.data;

	if (psprite->type == SPR_ORIENTED)
	{
		// bullet marks on walls
		AngleVectors (currententity->angles, forward, right, up);
	}
	else if (psprite->type == SPR_FACING_UPRIGHT)
	{
		VectorSet (up, 0, 0, 1);
		right[0] = e->origin[1] - r_origin[1];
		right[1] = -(e->origin[0] - r_origin[0]);
		right[2] = 0;
		VectorNormalize (right);
	}
	else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
	{
		VectorSet (up, 0, 0, 1);
		VectorCopy (vright, right);
	}
	else
	{	// normal sprite
		VectorCopy (vup, up);
		VectorCopy (vright, right);
	}

	GL_Bind (frame->gl_texturenum);

	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
     
	alpha = 1;
/*
	if (ISTRANSPARENT(e))//R00k: support for transparent sprites
	{
		glEnable (GL_BLEND);
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glBlendFunc (GL_SRC_COLOR, GL_SRC_ALPHA);
		alpha = 0.4;
	}
*/	
	VectorScale(out, l, out);// lower lightlevel in shade
	glColor4f(out[0],out[1],out[2],alpha);

	glBegin (GL_QUADS);

	glTexCoord2f (0, 1);
	VectorMA (e->origin, frame->down, up, point);
	VectorMA (point, frame->left, right, point);
	glVertex3fv (point);

	glTexCoord2f (0, 0);
	VectorMA (e->origin, frame->up, up, point);
	VectorMA (point, frame->left, right, point);
	glVertex3fv (point);

	glTexCoord2f (1, 0);
	VectorMA (e->origin, frame->up, up, point);
	VectorMA (point, frame->right, right, point);
	glVertex3fv (point);

	glTexCoord2f (1, 1);
	VectorMA (e->origin, frame->down, up, point);
	VectorMA (point, frame->right, right, point);
	glVertex3fv (point);
	
	glEnd ();
/*
	if (ISTRANSPARENT(e))//R00k: support for transparent sprites
	{
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDisable (GL_BLEND);
	}
*/
	glColor4f(1,1,1,1);
}

/*
===============================================================================

				ALIAS MODELS

===============================================================================
*/

//#define NUMVERTEXNORMALS	162

float r_avertexnormals[NUMVERTEXNORMALS][3] = 
#include "anorms.h"
;

vec3_t	shadevector;

qboolean full_light;
float	shadelight, ambientlight;

// precalculated dot products for quantized angles
#define SHADEDOT_QUANT 16

float	r_avertexnormal_dots[SHADEDOT_QUANT][256] = 
#include "anorm_dots.h"
;

float	*shadedots = r_avertexnormal_dots[0];

float	apitch, ayaw;

void R_DrawAliasShellFrame (int frame, aliashdr_t *paliashdr, entity_t *ent, int distance, float col[3], float alpha/*, size*/)
{
	int			*order, count, pose, numposes;
	float		lerpfrac;
	trivertx_t	*verts1, *verts2;
	float scroll[2], v[3], shell_size;

	shell_size = bound (0, gl_powerupshell_size.value, 10);

	vaEnableVertexArray (3);
	vaEnableTexCoordArray (GL_TEXTURE0, VA_TEXTURE0, 2);

	if (have_stencil) 
	{
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_EQUAL,1,2);
		glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
	}

	if ((frame >= paliashdr->numframes) || (frame < 0))
	{
		Con_DPrintf ("R_DrawAliasFrame: no such frame %d\n", frame);
		frame = 0;
	}

	pose = paliashdr->frames[frame].firstpose;
	numposes = paliashdr->frames[frame].numposes;

	if (numposes > 1) 
	{
		ent->frame_interval = paliashdr->frames[frame].interval;
		pose += (int)(cl.time / ent->frame_interval) % numposes;
	}
	else
	{
		ent->frame_interval = 0.1;
	}

	if (ent->pose2 != pose)
	{
		ent->frame_start_time = cl.time;
		ent->pose1 = ent->pose2;
		ent->pose2 = pose;
		ent->framelerp = 0;		
	}
	else
	{
		ent->framelerp = (cl.time - ent->frame_start_time) / ent->frame_interval;		
	}
	
	if (cl.paused || ent->framelerp > 1)
		ent->framelerp = 1;

	verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
	verts2 = verts1;

	verts1 += ent->pose1 * paliashdr->poseverts;
	verts2 += ent->pose2 * paliashdr->poseverts;

	order = (int *)((byte *)paliashdr + paliashdr->commands);
	lerpfrac = VectorL2Compare(verts1->v, verts2->v, distance) ? ent->framelerp : 1;

	GL_Bind (underwatertexture);
	
	glEnable (GL_BLEND);

	glBlendFunc(GL_ONE, GL_ONE);
	glColor4f (col[0], col[1], col[2], alpha); 

	//rotate the image in a circle clockwise
	scroll[0] = (cos(cl.time)/2);
	scroll[1] = (sin(cl.time)/2);
	
	while ((count = *order++))
	{
		if (count < 0)
		{
			count = -count;
			vaBegin(GL_TRIANGLE_FAN);
		}
		else
			vaBegin(GL_TRIANGLE_STRIP);

		do
		{
			v[0] = r_avertexnormals[verts1->lightnormalindex][0] * shell_size + verts1->v[0];
			v[1] = r_avertexnormals[verts1->lightnormalindex][1] * shell_size + verts1->v[1];
			v[2] = r_avertexnormals[verts1->lightnormalindex][2] * shell_size + verts1->v[2];
			v[0] += lerpfrac * (r_avertexnormals[verts2->lightnormalindex][0] * shell_size + verts2->v[0] - v[0]);
			v[1] += lerpfrac * (r_avertexnormals[verts2->lightnormalindex][1] * shell_size + verts2->v[1] - v[1]);
			v[2] += lerpfrac * (r_avertexnormals[verts2->lightnormalindex][2] * shell_size + verts2->v[2] - v[2]);
			
			vaTexCoord2f (((float *) order)[0] * 2.0f + scroll[0], ((float *) order)[1] * 2.0f + scroll[1]);
		
			vaVertex3fv (v);				

			order += 2;
			verts1++;
			verts2++;
		} while (--count);

		vaEnd();
	}

	vaDisableArrays();

	if (have_stencil) 
	{
		glDisable(GL_STENCIL_TEST);
		glClearStencil(1);
		glClear(GL_STENCIL_BUFFER_BIT);
	}

	glDisable (GL_BLEND);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	
}

//These colors now coincide with the (unofficial) Quake Network Protocol Specs
void ColorMeBad(int in, float alpha)
{
	float out[3], l;

	l = ((shadelight + ambientlight) / 192);

	switch (in)
	{
		case 0:
			out[0]=out[1]=out[2]= 123;//Id's original color 0 is grey not white!
			break;	
		case 1:
			out[0]=83;
			out[1]=59;
			out[2]=27;
			break;
		case 2:	
			out[0]=79;
			out[1]=79;
			out[2]=115;
			break;
		case 3:	
			out[0]=55;
			out[1]=55;
			out[2]=7;
			break;
		case 4:	
			out[0]=71;
			out[1]=0;
			out[2]=0;
			break;
		case 5:	
			out[0]=95;
			out[1]=71;
			out[2]=7;
			break;
		case 6:	
			out[0]= 143;
			out[1]= 67;
			out[2]= 51;
			break;
		case 7:	
			out[0]= 127;
			out[1]= 83;
			out[2]= 63;
			break;
		case 8:	
			out[0]= 87;
			out[1]= 55;
			out[2]= 67;
			break;
		case 9:	
			out[0]= 95;
			out[1]= 51;
			out[2]= 63;
			break;
		case 10:
			out[0]= 107;
			out[1]= 87;
			out[2]= 71;
			break;
		case 11:
			out[0]= 47;
			out[1]= 67;
			out[2]= 55;
			break;
		case 12:
			out[0]= 123;
			out[1]= 99;
			out[2]= 7;
			break;
		case 13:
			out[0]= 47;
			out[1]= 47;
			out[2]= 127;
		break;
	}
	VectorScale(out, 0.00390625, out);// out/256
	VectorScale(out, l, out);// lower lightlevel in shade
	glColor4f(out[0],out[1],out[2],alpha);
}

void R_DrawAliasSkinFrame  (int frame, aliashdr_t *paliashdr, entity_t *ent, int distance, int color, int texture)
{
	int			*order, count, pose, numposes;
	trivertx_t	*verts1, *verts2;
	float		lerpfrac;
	vec3_t		interpolated_verts;

	//cell shading
	extern	GLuint	celtexture, vertextexture;
	float			iblend;
	GLfloat			normal[3];

	if ((have_stencil) && (ISTRANSPARENT(ent)))
	{
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_EQUAL,1,2);
		glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
	}
	
	GL_PolygonOffset (-2);

	if ((frame >= paliashdr->numframes) || (frame < 0))
	{
		Con_DPrintf ("R_DrawAliasFrame: no such frame %d\n", frame);
		frame = 0;
	}

	pose = paliashdr->frames[frame].firstpose;
	numposes = paliashdr->frames[frame].numposes;

	if (numposes > 1) 
	{
		ent->frame_interval = paliashdr->frames[frame].interval;
		pose += (int)(cl.time / ent->frame_interval) % numposes;
	}
	else
	{
		ent->frame_interval = 0.1;
	}

	if (ent->pose2 != pose)
	{
		ent->frame_start_time = cl.time;
		ent->pose1 = ent->pose2;
		ent->pose2 = pose;
		ent->framelerp = 0;		
	}
	else
	{
		ent->framelerp = (cl.time - ent->frame_start_time) / ent->frame_interval;		
	}

	if (cl.paused || ent->framelerp > 1)
		ent->framelerp = 1;

	verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
	verts2 = verts1;

	verts1 += ent->pose1 * paliashdr->poseverts;
	verts2 += ent->pose2 * paliashdr->poseverts;

	order = (int *)((byte *)paliashdr + paliashdr->commands);

	GL_Bind(texture);

	if ((r_celshading.value) && (gl_mtexable)) 
	{	
		//setup for shading
		iblend = 1.0 - (ent->framelerp);
		GL_SelectTexture(GL_TEXTURE1);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		glEnable(GL_TEXTURE_1D);

		if (r_celshading.value > 1)		
			glBindTexture (GL_TEXTURE_1D, vertextexture);
		else
			glBindTexture (GL_TEXTURE_1D, celtexture);		
	}
	else
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);		
	glEnable (GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);
	
	ColorMeBad(color, ent->transparency);	

	while ((count = *order++))
	{
		if (count < 0)
		{
			count = -count;
			glBegin(GL_TRIANGLE_FAN);
		}
		else
		{
			glBegin(GL_TRIANGLE_STRIP);
		}

		do 
		{		
			if ((r_celshading.value) && (gl_mtexable))
			{				
				normal[0] = (r_avertexnormals[verts1->lightnormalindex][0] * iblend + r_avertexnormals[verts2->lightnormalindex][0] * (ent->framelerp));
				normal[1] = (r_avertexnormals[verts1->lightnormalindex][1] * iblend + r_avertexnormals[verts2->lightnormalindex][1] * (ent->framelerp));
				normal[2] = (r_avertexnormals[verts1->lightnormalindex][2] * iblend + r_avertexnormals[verts2->lightnormalindex][2] * (ent->framelerp));
				
				glNormal3f(normal[0],normal[1],normal[2]);

				// texture coordinates come from the draw list
				qglMultiTexCoord1f(GL_TEXTURE1, bound(0,DotProduct(shadevector,normal),1));
				qglMultiTexCoord2f(GL_TEXTURE0, ((float *)order)[0], ((float *)order)[1]);	
			}
			else
			{			
				glTexCoord2f (((float *)order)[0], ((float *)order)[1]);
			}

			lerpfrac = VectorL2Compare(verts1->v, verts2->v, distance) ? ent->framelerp : 1;
			VectorInterpolate (verts1->v, lerpfrac, verts2->v, interpolated_verts);
			glVertex3fv (interpolated_verts);				

			order += 2;
			verts1++;
			verts2++;
		} while (--count);

		glEnd();
	}

	if ((r_celshading.value) && (gl_mtexable))
	{
		glDisable(GL_TEXTURE_1D);		
		GL_SelectTexture(GL_TEXTURE0);
	}

	if ((have_stencil) && (ISTRANSPARENT(ent)))
	{		
		glDisable(GL_STENCIL_TEST);
		glClearStencil(1);
		glClear(GL_STENCIL_BUFFER_BIT);
	}

	GL_PolygonOffset (0);
	glDisable (GL_BLEND);	
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	
}

void R_DrawAliasFastSkinFrame  (int frame, aliashdr_t *paliashdr, entity_t *ent, int distance, int color, int texture)
{
	int			*order, count, pose, numposes;
	trivertx_t	*verts1, *verts2;
	float		lerpfrac;
	vec3_t		interpolated_verts;

	vaEnableVertexArray (3);
	vaEnableTexCoordArray (GL_TEXTURE0, VA_TEXTURE0, 2);

	if ((have_stencil) && (ISTRANSPARENT(ent)))
	{
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_EQUAL,1,2);
		glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
	}

	GL_PolygonOffset (-1);
	if ((frame >= paliashdr->numframes) || (frame < 0))
	{
		Con_DPrintf ("R_DrawAliasFrame: no such frame %d\n", frame);
		frame = 0;
	}

	pose = paliashdr->frames[frame].firstpose;
	numposes = paliashdr->frames[frame].numposes;

	if (numposes > 1)
	{
		ent->frame_interval = paliashdr->frames[frame].interval;
		pose += (int)(cl.time / ent->frame_interval) % numposes;
	}
	else
	{
		ent->frame_interval = 0.1;
	}

	if (ent->pose2 != pose)
	{
		ent->frame_start_time = cl.time;
		ent->pose1 = ent->pose2;
		ent->pose2 = pose;
		ent->framelerp = 0;		
	}
	else
	{
		ent->framelerp = (cl.time - ent->frame_start_time) / ent->frame_interval;		
	}

	if (cl.paused || ent->framelerp > 1)
		ent->framelerp = 1;

	verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
	verts2 = verts1;

	verts1 += ent->pose1 * paliashdr->poseverts;
	verts2 += ent->pose2 * paliashdr->poseverts;

	order = (int *)((byte *)paliashdr + paliashdr->commands);

	GL_Bind (texture);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);		
	glEnable (GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);
	
	ColorMeBad(color, ent->transparency);	

	while ((count = *order++))
	{
		if (count < 0)
		{
			count = -count;
			vaBegin (GL_TRIANGLE_FAN);
		}
		else
		{
			vaBegin (GL_TRIANGLE_STRIP);
		}

		do 
		{		
			vaTexCoord2f (((float *)order)[0], ((float *)order)[1]);

			lerpfrac = VectorL2Compare(verts1->v, verts2->v, distance) ? ent->framelerp : 1;
			VectorInterpolate (verts1->v, lerpfrac, verts2->v, interpolated_verts);
				
			vaVertex3fv (interpolated_verts);
			order += 2;
			verts1++;
			verts2++;
		} while (--count);

		vaEnd ();
	}

	vaDisableArrays();

	if ((have_stencil) && (ISTRANSPARENT(ent)))
	{		
		glDisable(GL_STENCIL_TEST);
		glClearStencil(1);
		glClear(GL_STENCIL_BUFFER_BIT);
	}

	GL_PolygonOffset (0);
	glDisable (GL_BLEND);	
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	
}
/*
=============
R_DrawAliasFrame
=============
*/
void R_DrawAliasFrame (int frame, aliashdr_t *paliashdr, entity_t *ent, int distance, int cell)
{
	int			i, *order, count, pose, numposes;
	float		l, lerpfrac;
	vec3_t		lightvec, interpolated_verts;
	trivertx_t	*verts1, *verts2;
	qboolean	lerpmdl = true;

	//cel shading
	extern	GLuint	
				celtexture, 
				vertextexture;

	float		iblend;
	GLfloat		normal[3];
	int			nocelshading;

	nocelshading = ISUNDERWATER(TruePointContents(ent->origin));//use svc_ for qc control?

	if ((have_stencil) && (ISTRANSPARENT(ent)))
	{
		glCullFace (GL_FRONT);
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_EQUAL,1,2);
		glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
	}

	if (cell)
	{
		glCullFace (GL_BACK);
		//glEnable(GL_BLEND);
		glPolygonMode (GL_FRONT, GL_LINE);
		glLineWidth (2.0f);
		GL_PolygonOffset (-0.7);
		glEnable (GL_LINE_SMOOTH);
	}

	if ((frame >= paliashdr->numframes) || (frame < 0))
	{
		Con_DPrintf ("R_DrawAliasFrame: no such frame %d\n", frame);
		frame = 0;
	}

	pose = paliashdr->frames[frame].firstpose;
	numposes = paliashdr->frames[frame].numposes;

	if (numposes > 1) 
	{
		ent->frame_interval = paliashdr->frames[frame].interval;
		pose += (int)(cl.time / ent->frame_interval) % numposes;
	}
	else
	{
		ent->frame_interval = 0.1;
	}

	//MH
	if (ent->pose2 != pose) 
		lerpmdl = false;			// don't interpolate between different poses

	if (paliashdr->numposes == 1) 
		lerpmdl = false;			// only one pose	

	if (r_framecount < 10) 
		lerpmdl = false;			// reliving their dying throes

	if (ent->pose1 == ent->pose2) 
		lerpmdl = false;
	//MH

	if (!lerpmdl)
	{
		ent->pose1 = ent->pose2;
		ent->pose2 = pose;
		ent->framelerp = 0;		
		ent->frame_start_time = cl.time;
	}
	else
	{
		ent->framelerp = (cl.time - ent->frame_start_time) / ent->frame_interval;		
	}

	// weird things start happening if blend passes 1
	if (cl.paused || ent->framelerp > 1) 
		ent->framelerp = 1;

	verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
	verts2 = verts1;

	verts1 += ent->pose1 * paliashdr->poseverts;
	verts2 += ent->pose2 * paliashdr->poseverts;

	order = (int *)((byte *)paliashdr + paliashdr->commands);

	if ((ISTRANSPARENT(ent)))
	{
		glEnable (GL_BLEND);		
	}

	if ((r_celshading.value) && (gl_mtexable) && (!nocelshading)) 
	{	
		//QMB: setup for shading
		iblend = 1.0 - (ent->framelerp);
		GL_SelectTexture(GL_TEXTURE1);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		glEnable(GL_TEXTURE_1D);

		if (r_celshading.value > 1)		
			glBindTexture (GL_TEXTURE_1D, vertextexture);
		else
			glBindTexture (GL_TEXTURE_1D, celtexture);		
	}

	while ((count = *order++))
	{
		// get the vertex count and primitive type
		if (count < 0)
		{
			count = -count;

			glBegin(GL_TRIANGLE_FAN);
		}
		else
		{
			glBegin(GL_TRIANGLE_STRIP);
		}

		do {
			// normals and vertexes come from the frame list
			// blend the light intensity from the two frames together
			if (!cell)
			{
				if (gl_vertexlights.value == 1 && !full_light)
				{
					l = R_LerpVertexLight (anorm_pitch[verts1->lightnormalindex], anorm_yaw[verts1->lightnormalindex],
								anorm_pitch[verts2->lightnormalindex], anorm_yaw[verts2->lightnormalindex], lerpfrac, apitch, ayaw);
					l = min(l, 1);

					for (i=0 ; i<3 ; i++)
						lightvec[i] = lightcolor[i] / 256 + l;
					glColor4f (lightvec[0], lightvec[1], lightvec[2], ent->transparency);
				}
				else
				{
					l = FloatInterpolate (shadedots[verts1->lightnormalindex], lerpfrac, shadedots[verts2->lightnormalindex]);
					l = (l * shadelight + ambientlight) / 256;
					l = min(l, 1);
					glColor4f (l, l, l, ent->transparency);
				}
			}

			if ((r_celshading.value) && (gl_mtexable) && (!nocelshading)) 
			{				
				normal[0] = (r_avertexnormals[verts1->lightnormalindex][0] * iblend + r_avertexnormals[verts2->lightnormalindex][0] * (ent->framelerp));
				normal[1] = (r_avertexnormals[verts1->lightnormalindex][1] * iblend + r_avertexnormals[verts2->lightnormalindex][1] * (ent->framelerp));
				normal[2] = (r_avertexnormals[verts1->lightnormalindex][2] * iblend + r_avertexnormals[verts2->lightnormalindex][2] * (ent->framelerp));
				
				glNormal3f(normal[0],normal[1],normal[2]);

				// texture coordinates come from the draw list
				qglMultiTexCoord1f(GL_TEXTURE1, bound(0,DotProduct(shadevector,normal),1));
				qglMultiTexCoord2f(GL_TEXTURE0, ((float *)order)[0], ((float *)order)[1]);	
			}
			else
			{			
				if (gl_mtexable)
				{
					qglMultiTexCoord2f (GL_TEXTURE0, ((float *)order)[0], ((float *)order)[1]);
					qglMultiTexCoord2f (GL_TEXTURE1, ((float *)order)[0], ((float *)order)[1]);
				}
				else
				{
					glTexCoord2f (((float *)order)[0], ((float *)order)[1]);
				}
			}

			lerpfrac = VectorL2Compare(verts1->v, verts2->v, distance) ? ent->framelerp : 1;
			VectorInterpolate (verts1->v, lerpfrac, verts2->v, interpolated_verts);
			glVertex3fv (interpolated_verts);				

			order += 2;
			verts1++;
			verts2++;
		} while (--count);
		
		glEnd();
	}

	if ((r_celshading.value) && (gl_mtexable) && (!nocelshading)) 
	{
		glDisable(GL_TEXTURE_1D);		
		GL_SelectTexture(GL_TEXTURE0);
	}

	if ((ISTRANSPARENT(ent)))
	{
		glDisable (GL_BLEND);
	}

	if ((have_stencil) && (ISTRANSPARENT(ent)))
	{
		glDisable(GL_STENCIL_TEST);
		glClearStencil(1);
		glClear(GL_STENCIL_BUFFER_BIT);
	}

	if (cell)
	{
		GL_PolygonOffset (0);
		glPolygonMode (GL_FRONT, GL_FILL);
		//glDisable (GL_BLEND);
		glCullFace (GL_FRONT);
	}
}

/*
=============
R_DrawAliasShadow
=============
*/
void R_DrawAliasShadow (aliashdr_t *paliashdr, entity_t *ent, int distance, trace_t downtrace)
{
	int			*order, count;
	float		lheight, lerpfrac, s1, c1;
	vec3_t		point1, point2, interpolated;

	trivertx_t	*verts1, *verts2;
	
	vaEnableVertexArray (3);
	vaEnableTexCoordArray (GL_TEXTURE0, VA_TEXTURE0, 2);

	lheight = ent->origin[2] - lightspot[2];

	s1 = sin(ent->angles[1] / 180 * M_PI);
	c1 = cos(ent->angles[1] / 180 * M_PI);

	verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
	verts2 = verts1;

	verts1 += ent->pose1 * paliashdr->poseverts;
	verts2 += ent->pose2 * paliashdr->poseverts;

	order = (int *)((byte *)paliashdr + paliashdr->commands);

	if (have_stencil) 
	{
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_EQUAL,1,2);
		glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
	}

	while ((count = *order++))
	{
		// get the vertex count and primitive type
		if (count < 0)
		{
			count = -count;
			vaBegin (GL_TRIANGLE_FAN);
		}
		else
		{
			vaBegin (GL_TRIANGLE_STRIP);
		}

		do {
			order += 2;

			lerpfrac = VectorL2Compare(verts1->v, verts2->v, distance) ? ent->framelerp : 1;

			point1[0] = verts1->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0];
			point1[1] = verts1->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1];
			point1[2] = verts1->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2];

			point1[0] -= shadevector[0] * (point1[2] + lheight);
			point1[1] -= shadevector[1] * (point1[2] + lheight);

			point2[0] = verts2->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0];
			point2[1] = verts2->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1];
			point2[2] = verts2->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2];

			point2[0] -= shadevector[0] * (point2[2] + lheight);
			point2[1] -= shadevector[1] * (point2[2] + lheight);

			VectorInterpolate (point1, lerpfrac, point2, interpolated);

			interpolated[2] = -(ent->origin[2] - downtrace.endpos[2]);

			interpolated[2] += ((interpolated[1] * (s1 * downtrace.plane.normal[0])) - 
						(interpolated[0] * (c1 * downtrace.plane.normal[0])) - 
						(interpolated[0] * (s1 * downtrace.plane.normal[1])) - 
						(interpolated[1] * (c1 * downtrace.plane.normal[1]))) + 
						((1 - downtrace.plane.normal[2]) * 20) + 0.2;

			vaVertex3fv (interpolated);
			
			verts1++;
			verts2++;
		} while (--count);

		vaEnd ();
	}       

	vaDisableArrays();

	if (have_stencil)
	{		
		glDisable(GL_STENCIL_TEST);
		glClearStencil(1);
		glClear(GL_STENCIL_BUFFER_BIT);
	}
}

/*
=============
R_SetupLighting
=============
*/
extern corona_t *R_AddDefaultCorona (entity_t *e);
void R_SetupLighting (entity_t *ent)
{
	int	i, lnum;
	float	add;
	vec3_t	dist, dlight_color;
	model_t	*clmodel = ent->model;

	// make thunderbolt and torches full light
	if (clmodel->modhint == MOD_THUNDERBOLT)
	{
		ambientlight = 210;
		shadelight = 0;
		full_light = ent->noshadow = true;
		return;
	}
	else if (clmodel->modhint == MOD_FLAME)
	{
		ambientlight = 255;
		shadelight = 0;
		full_light = ent->noshadow = true;

		if(!ent->corona)
		{
			corona_t *c;
			if((c = (void*)R_AddDefaultCorona(currententity)))
			{
				c->offset[2] = 6;
				if(!strcmp (clmodel->name, "progs/flame2.mdl"))
					c->offset[2] -= 2;
			}
		}
		return;
	}

	// normal lighting
	ambientlight = shadelight = R_LightPoint (ent->origin);
 	full_light = false;
	ent->noshadow = false;

	for (lnum = 0 ; lnum < MAX_DLIGHTS ; lnum++)
	{
		if (cl_dlights[lnum].die < cl.time || !cl_dlights[lnum].radius)
			continue;

		VectorSubtract (ent->origin, cl_dlights[lnum].origin, dist);
		add = cl_dlights[lnum].radius - VectorLength (dist);

		if (add > 0)
		{
		// joe: only allow colorlight affection if dynamic lights are on
			if (r_dynamic.value)
			{
				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] - (lightcolor[1] / 3);
							lightcolor[2] = lightcolor[2] - (lightcolor[2] / 3);
							break;

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

						case 2:
							lightcolor[1] = lightcolor[1] - (lightcolor[1] / 3);
							lightcolor[0] = lightcolor[0] - (lightcolor[0] / 3);
							break;
						}
					}
				}
			}
			ambientlight += add;
			shadelight += add;
		}
	}

	// calculate pitch and yaw for vertex lighting
	if (gl_vertexlights.value)
	{
		apitch = ent->angles[0];
		ayaw = ent->angles[1];
	}

	// clamp lighting so it doesn't overbright as much
	ambientlight = min(128, ambientlight);
	if (ambientlight + shadelight > 192)
		shadelight = 192 - ambientlight;

	if (ent == &cl.viewent)
	{
		ent->noshadow = true;
		// always give the gun some light
		if (ambientlight < 24)
			ambientlight = shadelight = 24;
	}

	// never allow players to go totally black
	//if ((clmodel->modhint == MOD_PLAYER) || (clmodel->modhint == MOD_EYES))//R00k
	{
		if (ambientlight < 8)
			ambientlight = shadelight = 8;
	}
}

/*
=============
R_SetupInterpolateDistance
=============
*/
void R_SetupInterpolateDistance (entity_t *ent, int *distance)
{
	*distance = INTERP_MAXDIST;

	if (ent->model->modhint == MOD_FLAME)
		*distance = 0;

	else if (ent->model->modhint == MOD_WEAPON)
	{
		if ((*distance = DoWeaponInterpolation()) == -1)
			*distance = (int)gl_interdist.value;
	}
	else if ((ent->modelindex == cl_modelindex[mi_player])||(ent->modelindex == cl_modelindex[mi_md3_player]))
	{
		// nailatt
		if (ent->frame == 103)
			*distance = 0;
		else if (ent->frame == 104)
			*distance = 59;
		// rockatt
		else if (ent->frame == 107)
			*distance = 0;
		else if (ent->frame == 108)
			*distance = 115;
		// shotatt
		else if (ent->frame == 113)
			*distance = 76;
		else if (ent->frame == 115)
			*distance = 79;
	}
	else if (ent->modelindex == cl_modelindex[mi_soldier])
	{
		if (ent->frame == 84)
			*distance = 63;
		else if (ent->frame == 85)
			*distance = 49;
		else if (ent->frame == 86)
			*distance = 106;
	}
	else if (ent->modelindex == cl_modelindex[mi_enforcer])
	{
		if (ent->frame == 36)
			*distance = 115;
		else if (ent->frame == 37)
			*distance = 125;
	}
}

extern	corona_t	*R_AddDefaultCorona (entity_t *e);
extern	cvar_t		r_powerupglow;

/*
=================
R_DrawAliasModel
=================
*/
void R_DrawAliasModel (entity_t *ent)
{	
	int				i, anim, skinnum, distance, texture, fb_texture, st_texture, pt_texture;
	vec3_t			mins, maxs;
	aliashdr_t		*paliashdr;
	model_t			*clmodel = ent->model;
	qboolean		isLumaSkin;
	float			scale, color[3], theta;
	static	float	shadescale = 0;

	if (ent->cullFrame != r_framecount)
	{
		VectorAdd (ent->origin, clmodel->mins, mins);
		VectorAdd (ent->origin, clmodel->maxs, maxs);

		if (ent->angles[0] || ent->angles[1] || ent->angles[2])
			ent->culled = R_CullSphere(ent->origin, clmodel->radius);
		else
			ent->culled = R_CullBox(mins, maxs);

		ent->cullFrame = r_framecount;
	}

	if (ent->culled)
	{
		return;
	}

	color[0] = 0.4;
	color[1] = 0.4;
	color[2] = 0.4;

	if ((r_shadows.value)||(r_celshading.value))
	{
		if (!shadescale)
			shadescale = 1 / sqrt(2);
		theta = -ent->angles[1] / 180 * M_PI;

		VectorSet (shadevector, cos(theta) * shadescale, sin(theta) * shadescale, shadescale);
	}

	VectorCopy (ent->origin, r_entorigin);
	VectorSubtract (r_origin, r_entorigin, modelorg);

	// get lighting information
	R_SetupLighting (ent);
	R_SetupInterpolateDistance (ent, &distance);

	shadedots = r_avertexnormal_dots[((int)(ent->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];

	// locate the proper data
	paliashdr = (aliashdr_t *)Mod_Extradata (clmodel);

	c_alias_polys += paliashdr->numtris;

	// draw all the triangles

	glPushMatrix ();	

	if (ent == &cl.viewent)
	{
		R_RotateForViewEntity (ent);
	}
	else
	{		
		R_RotateForEntity (ent, false);
	}

	if (clmodel->modhint == MOD_EYES && gl_doubleeyes.value)
	{
		glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8));
		glScalef (paliashdr->scale[0] * 2, paliashdr->scale[1] * 2, paliashdr->scale[2] * 2);
	}
	else
	{		
		if ((ent == &cl.viewent) && (cl_gun_fovscale.value) && (scr_fov.value != 0))
		{			
			if (scr_fov.value <= 90)
				scale = 1.0f;
			else
				scale = 1.0f / tan(DEG2RAD(scr_fov.value/2));//Phoenix
			glTranslatef(paliashdr->scale_origin[0]*scale, paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
			glScalef(paliashdr->scale[0] * scale, paliashdr->scale[1], paliashdr->scale[2]);			
		}
		else
		{
			if ((ent == &cl.viewent) && (r_drawviewmodelsize.value > 0))
			{				
				scale = 0.5 + bound(0, r_drawviewmodelsize.value, 1) / 2;
				glTranslatef(paliashdr->scale_origin[0]*scale, paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
				glScalef(paliashdr->scale[0] * scale, paliashdr->scale[1], paliashdr->scale[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 = (lrint(cl.time)*10) & 3;//r00k
	skinnum = ent->skinnum;

	if ((skinnum >= paliashdr->numskins) || (skinnum < 0))
	{
		Con_DPrintf ("R_DrawAliasModel: no such skin # %d\n", skinnum);
		skinnum = 0;
	}

	texture	= paliashdr->gl_texturenum[skinnum][anim];

	if (clmodel->modhint == MOD_PLAYER && cl_teamskin.value)
	{		
		st_texture = paliashdr->gl_textureshirt[skinnum][anim];
		pt_texture = paliashdr->gl_texturepants[skinnum][anim];
	}
	else
	{
		if (gl_fb_models.value)
		{
			isLumaSkin	= paliashdr->isLumaSkin[skinnum][anim];
			fb_texture	= paliashdr->fb_texturenum[skinnum][anim];
		}
	}

	// we can't dynamically colormap textures, so they are cached
	// seperately for the players. Heads are just uncolored.
	if (ent->colormap != vid.colormap && !gl_nocolors.value && !cl_teamskin.value)
	{
		i = ent - cl_entities;

		if (i > 0 && i <= cl.maxclients)
		{
			texture = playertextures - 1 + i;
			fb_texture = fb_skins[i-1];
		}
	}

	if (full_light || !gl_fb_models.value || r_celshading.value)//R00k
		fb_texture = 0;

	if(gl_fogenable.value)//R00k
	{
			glEnable (GL_FOG);
	}

	if (gl_smoothmodels.value)
		glShadeModel (GL_SMOOTH);

	if (gl_affinemodels.value == 1)
		glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
	
	GL_DisableMultitexture ();
	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	GL_Bind (texture);

//*********************************************************************************************************************
	if ((cl_teamskin.value) && (clmodel->modhint == MOD_PLAYER))
	{					
		R_DrawAliasFrame (ent->frame, paliashdr, ent, distance, false);		

		if ((r_outline.value))
		{
			ColorMeBad(ent->pantscolor,ent->transparency);
			R_DrawAliasFrame (ent->frame, paliashdr, ent, distance,true);
			glColor4f (1, 1, 1, 1);	
		}

		if ((pt_texture) && (st_texture))
		{			
			if (r_celshading.value)
			{
				R_DrawAliasSkinFrame (ent->frame, paliashdr, ent, distance, ent->pantscolor, pt_texture);			
				R_DrawAliasSkinFrame (ent->frame, paliashdr, ent, distance, ent->shirtcolor, st_texture);
			}
			else
			{
				R_DrawAliasFastSkinFrame (ent->frame, paliashdr, ent, distance, ent->pantscolor, pt_texture);			
				R_DrawAliasFastSkinFrame (ent->frame, paliashdr, ent, distance, ent->shirtcolor, st_texture);
			}
		}
	}
//*********************************************************************************************************************
	else
	{
		if (fb_texture && gl_mtexable)
		{
			GL_EnableMultitexture ();
			if (isLumaSkin)
			{
				if (gl_add_ext)
				{
					glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
					GL_Bind (fb_texture);
				}
			}
			else
			{
				glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
				GL_Bind (fb_texture);
			}

			R_DrawAliasFrame (ent->frame, paliashdr, ent, distance,false);

			if (isLumaSkin && !gl_add_ext)
			{
				glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
				GL_Bind (fb_texture);

				glDepthMask (GL_FALSE);
				glEnable (GL_BLEND);
				glBlendFunc (GL_ONE, GL_ONE);

				R_DrawAliasFrame (ent->frame, paliashdr, ent, distance,false);

				glDisable (GL_BLEND);
				glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				glDepthMask (GL_TRUE);
			}
			GL_DisableMultitexture ();
		}
		else
		{
			R_DrawAliasFrame (ent->frame, paliashdr, ent, distance,false);

			if (fb_texture)
			{
				glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
				GL_Bind (fb_texture);

				glDepthMask (GL_FALSE);
				if (isLumaSkin)
				{
					glEnable (GL_BLEND);
					glBlendFunc (GL_ONE, GL_ONE);
				}
				else
				{
					glEnable (GL_ALPHA_TEST);
				}

				R_DrawAliasFrame (ent->frame, paliashdr, ent, distance,false);

				if (isLumaSkin)
				{
					glDisable (GL_BLEND);
					glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				}
				else
				{
					glDisable (GL_ALPHA_TEST);
				}			
				glDepthMask (GL_TRUE);
			}			
		}
		if ((r_outline.value))
		{
			if (clmodel->modhint == MOD_PLAYER)
				ColorMeBad(ent->pantscolor,ent->transparency);
			else
				glColor4f (0, 0, 0, 1);	

			R_DrawAliasFrame (ent->frame, paliashdr, ent, distance,true);
			glColor4f (1, 1, 1, 1);	
		}
	}

	//R00k underwater caustics on alias models
	if (gl_caustics.value)
	if (!((cl_teamskin.value) && (clmodel->modhint == MOD_PLAYER)))//teamskins break caustics, skip this pass...
	if ((underwatertexture && gl_mtexable && ISUNDERWATER(TruePointContents(ent->origin))))
	{
//		if (!(r_celshading.value))
		{			
			GL_EnableMultitexture ();

			GL_Bind (underwatertexture);

			glMatrixMode (GL_TEXTURE);
			glLoadIdentity ();
			glScalef (0.5, 0.5, 1);
			glRotatef (cl.time * 10, 1, 0, 0);
			glRotatef (cl.time * 10, 0, 1, 0);
			glMatrixMode (GL_MODELVIEW);

			GL_Bind (underwatertexture);

			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);		
			
			glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
			glEnable (GL_BLEND);

			R_DrawAliasFrame (ent->frame, paliashdr, ent, distance,false);

			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glDisable(GL_BLEND);			

			GL_SelectTexture(GL_TEXTURE1);
			glTexEnvi (GL_TEXTURE_ENV, GL_RGB_SCALE, 1);
			glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
			glDisable (GL_TEXTURE_2D);

			glMatrixMode (GL_TEXTURE);
			glLoadIdentity ();
			glMatrixMode (GL_MODELVIEW);
			
			GL_DisableMultitexture ();
		}
	}

	if ((ent==&cl_entities[cl.viewentity]||ent == &cl.viewent)&&(cl.time <= cl.faceanimtime) && (gl_damageshells.value))//If I am hit, Glow deflection shield...
	{
		color[0] = 1.0;color[1] = 0.50;color[2] = 0.0;
		R_DrawAliasShellFrame (ent->frame, paliashdr, ent, distance, color, 0.65);
	}
	else
	if ((gl_powerupshells.value) && ((ent->effects & EF_DIMLIGHT) || ((ent == &cl.viewent)&&(cl.items & IT_QUAD)) || ((ent == &cl.viewent)&&(cl.items & IT_INVULNERABILITY))))
	{
			if (ent != &cl.viewent)
			{
				if (r_powerupglow.value == 0)
				{
					color[0] = 0.4;
					color[1] = 0.4;
					color[2] = 0.4;
				}
				else				
				{
					if (ent->effects & EF_RED)
					{
						color[0] = 0.50;color[1] = 0.0;color[2] = 0.0;
					}
					
					if (ent->effects & EF_BLUE)
					{
						color[0] = 0.0;color[1] = 0.0;color[2] = 0.50;
					}
				}				
			}
			else
			{
				if (r_powerupglow.value == 0)
				{
					color[0] = 0.4;
					color[1] = 0.4;
					color[2] = 0.4;
				}
				else				
				{
					if ((ent == &cl.viewent)&&(cl.items & IT_QUAD))
					{
							color[0] = 0;color[1] = 0;color[2] = 0.5;
					}
					
					if ((ent == &cl.viewent)&&(cl.items & IT_INVULNERABILITY))
					{
							color[0] = 0.5;color[1] = 0;color[2] = 0;
					}
				}
			}
			
			R_DrawAliasShellFrame (ent->frame, paliashdr, ent, distance,color, 0.65);
	}

	glShadeModel (GL_FLAT);
	
	if (gl_affinemodels.value)
		glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glPopMatrix ();	

	if (r_shadows.value && !ent->noshadow) 
	{
		int				farclip;
		vec3_t			downmove;
		trace_t			downtrace;
		extern	void	R_SpawnDecal(vec3_t center, vec3_t normal, vec3_t tangent, int tex, int size, float dtime, float dalpha);
		
		farclip = max((int)r_farclip.value, 4096);

		glPushMatrix ();
		
		R_RotateForEntity (ent, true);//interpolate origin/angles

		VectorCopy (ent->origin, downmove);
		downmove[2] -= farclip;
		memset (&downtrace, 0, sizeof(downtrace));

		SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, ent->origin, downmove, &downtrace);

		if (downtrace.fraction < 1)//R00k
		{
			glDepthMask (GL_FALSE);
			glDisable (GL_TEXTURE_2D);
			glEnable (GL_BLEND);
			glColor4f (0, 0, 0,(((shadelight - (mins[2] - downtrace.endpos[2]))*r_shadows.value)*0.0066)*ent->transparency);//R00k
			R_DrawAliasShadow (paliashdr, ent, distance, downtrace);
			glDepthMask (GL_TRUE);
			glEnable (GL_TEXTURE_2D);
			glDisable (GL_BLEND);
		}
		glPopMatrix ();
	}

	//R00k added for debugging....
	if ((clmodel->modhint == MOD_PLAYER) && (r_drawbbox.value) && (developer.value))
	{	
		vec3_t mins = {-16, -16, -24}; 
		vec3_t maxs = {16, 16, 32}; 	
		vec3_t mins2 = {-32, -32, -24}; 
		vec3_t maxs2 = {32, 32, 64}; 	

		glColor4f(0,0,0,ambientlight/182);//R00k no hacks please! max alpha 70%
		GL_DrawSimpleBox(currententity->origin, mins, maxs);
	}	
	glColor4f(1,1,1,1);
}

void Set_Interpolated_Weapon_f (void)
{
	int	i;
	char	str[MAX_QPATH];

	if (cmd_source != src_command)
		return;

	if (Cmd_Argc() == 2)
	{
		for (i=0 ; i<interp_weap_num ; i++)
		{
			if (!Q_strcasecmp(Cmd_Argv(1), interpolated_weapons[i].name))
			{
				Con_Printf ("%s`s distance is %d\n", Cmd_Argv(1), interpolated_weapons[i].maxDistance);
				return;
			}
		}
		Con_Printf ("%s`s distance is default (%d)\n", Cmd_Argv(1), (int)gl_interdist.value);
		return;
	}

	if (Cmd_Argc() != 3)
	{
		Con_Printf ("Usage: set_interpolated_weapon <model> <distance>\n");
		return;
	}

	strcpy (str, Cmd_Argv(1));
	for (i=0 ; i<interp_weap_num ; i++)
		if (!Q_strcasecmp(str, interpolated_weapons[i].name))
			break;
	if (i == interp_weap_num)
	{
		if (interp_weap_num == INTERP_WEAP_MAXNUM)
		{
			Con_Printf ("interp_weap_num == INTERP_WEAP_MAXNUM\n");
			return;
		}
		else
		{
			interp_weap_num++;
		}
	}

	strcpy (interpolated_weapons[i].name, str);
	interpolated_weapons[i].maxDistance = (int)Q_atof(Cmd_Argv(2));
}

int DoWeaponInterpolation (void)
{
	int	i;

	if (currententity != &cl.viewent)
		return -1;

	for (i=0 ; i<interp_weap_num ; i++)
	{
		if (!interpolated_weapons[i].name[0])
			return -1;

		if (!Q_strcasecmp(currententity->model->name, va("%s.mdl", interpolated_weapons[i].name)) || 
		    !Q_strcasecmp(currententity->model->name, va("progs/%s.mdl", interpolated_weapons[i].name)))
			return interpolated_weapons[i].maxDistance;
	}

	return -1;
}
/*
===============================================================================

				Q3 MODELS

===============================================================================
*/

void R_RotateForTagEntity (tagentity_t *tagent, md3tag_t *tag, float *m)
{
	int	i;
	float	lerpfrac, timepassed;

	// positional interpolation
	timepassed = cl.time - tagent->tag_translate_start_time;

	if (tagent->tag_translate_start_time == 0 || timepassed > 1)
	{
		tagent->tag_translate_start_time = cl.time;
		VectorCopy (tag->pos, tagent->tag_pos1);
		VectorCopy (tag->pos, tagent->tag_pos2);
	}

	if (!VectorCompare(tag->pos, tagent->tag_pos2))
	{
		tagent->tag_translate_start_time = cl.time;
		VectorCopy (tagent->tag_pos2, tagent->tag_pos1);
		VectorCopy (tag->pos, tagent->tag_pos2);
		lerpfrac = 0;
	}
	else
	{
		lerpfrac = timepassed / 0.1;
		if (cl.paused || lerpfrac > 1)
			lerpfrac = 1;
	}

	VectorInterpolate (tagent->tag_pos1, lerpfrac, tagent->tag_pos2, m + 12);
	m[15] = 1;

	for (i=0 ; i<3 ; i++)
	{
		// orientation interpolation (Euler angles, yuck!)
		timepassed = cl.time - tagent->tag_rotate_start_time[i];

		if (tagent->tag_rotate_start_time[i] == 0 || timepassed > 1)
		{
			tagent->tag_rotate_start_time[i] = cl.time;
			VectorCopy (tag->rot[i], tagent->tag_rot1[i]);
			VectorCopy (tag->rot[i], tagent->tag_rot2[i]);
		}

		if (!VectorCompare(tag->rot[i], tagent->tag_rot2[i]))
		{
			tagent->tag_rotate_start_time[i] = cl.time;
			VectorCopy (tagent->tag_rot2[i], tagent->tag_rot1[i]);
			VectorCopy (tag->rot[i], tagent->tag_rot2[i]);
			lerpfrac = 0;
		}
		else
		{
			lerpfrac = timepassed / 0.1;
			if (cl.paused || lerpfrac > 1)
				lerpfrac = 1;
		}

		VectorInterpolate (tagent->tag_rot1[i], lerpfrac, tagent->tag_rot2[i], m + i*4);
		m[i*4+3] = 0;
	}
}

int		bodyframe = 0, legsframe = 0;
animtype_t	bodyanim, legsanim;

void R_ReplaceQ3Frame (int frame)
{
	animdata_t		*currbodyanim, *currlegsanim;
	static	animtype_t	oldbodyanim, oldlegsanim;
	static	float		bodyanimtime, legsanimtime;
	static	qboolean	deathanim = false;

	if (deathanim)
	{
		bodyanim = oldbodyanim;
		legsanim = oldlegsanim;
	}

	if (frame < 41 || frame > 102)
		deathanim = false;

	if (frame >= 0 && frame <= 5)		// axrun
	{
		bodyanim = torso_stand2;
		legsanim = legs_run;
	}
	else if (frame >= 6 && frame <= 11)	// rockrun
	{
		bodyanim = torso_stand;
		legsanim = legs_run;
	}
	else if ((frame >= 12 && frame <= 16) || (frame >= 35 && frame <= 40))	// stand, pain
	{
		bodyanim = torso_stand;
		legsanim = legs_idle;
	}
	else if ((frame >= 17 && frame <= 28) || (frame >= 29 && frame <= 34))	// axstand, axpain
	{
		bodyanim = torso_stand2;
		legsanim = legs_idle;
	}
	else if (frame >= 41 && frame <= 102 && !deathanim)	// axdeath, deatha, b, c, d, e
	{
		bodyanim = legsanim = both_death1;
		deathanim = true;
	}
	else if (frame > 103 && frame <= 118)	// gun attacks
	{
		bodyanim = torso_attack;
	}
	else if (frame >= 119)			// axe attacks
	{
		bodyanim = torso_attack2;
	}

	currbodyanim = &anims[bodyanim];
	currlegsanim = &anims[legsanim];

	if (bodyanim == oldbodyanim)
	{
		if (cl.time >= bodyanimtime + currbodyanim->interval)
		{
			if (currbodyanim->loop_frames && bodyframe + 1 >= currbodyanim->offset + currbodyanim->loop_frames)
				bodyframe = currbodyanim->offset;
			else if (bodyframe + 1 < currbodyanim->offset + currbodyanim->num_frames)
				bodyframe++;
			bodyanimtime = cl.time;
		}
	}
	else
	{
		bodyframe = currbodyanim->offset;
		bodyanimtime = cl.time;
	}

	if (legsanim == oldlegsanim)
	{
		if (cl.time >= legsanimtime + currlegsanim->interval)
		{
			if (currlegsanim->loop_frames && legsframe + 1 >= currlegsanim->offset + currlegsanim->loop_frames)
				legsframe = currlegsanim->offset;
			else if (legsframe + 1 < currlegsanim->offset + currlegsanim->num_frames)
				legsframe++;
			legsanimtime = cl.time;
		}
	}
	else
	{
		legsframe = currlegsanim->offset;
		legsanimtime = cl.time;
	}

	oldbodyanim = bodyanim;
	oldlegsanim = legsanim;
}

int		multimodel_level;
qboolean	surface_transparent;


void R_DrawQ3SkinFrame (int frame, md3header_t *pmd3hdr, md3surface_t *pmd3surf, entity_t *ent, int distance, int color, int texture)
{
	int		i,numtris, pose, pose1, pose2;
	float		lerpfrac;
	vec3_t		interpolated_verts;
	unsigned int	*tris;
	md3tc_t		*tc;
	md3vert_mem_t	*verts, *v1, *v2;
	model_t		*clmodel = ent->model;

	GL_PolygonOffset (-3);

	if ((frame >= pmd3hdr->numframes) || (frame < 0))
	{
		Con_DPrintf ("R_DrawQ3Frame: no such frame %d\n", frame);
		frame = 0;
	}

	if (ent->pose1 >= pmd3hdr->numframes)
		ent->pose1 = 0;

	pose = frame;

	if (!strcmp(clmodel->name, "progs/player/lower.md3"))
		ent->frame_interval = anims[legsanim].interval;
	else if (!strcmp(clmodel->name, "progs/player/upper.md3"))
		ent->frame_interval = anims[bodyanim].interval;
	else
		ent->frame_interval = 0.1;

	if (ent->pose2 != pose)
	{
		ent->frame_start_time = cl.time;
		ent->pose1 = ent->pose2;
		ent->pose2 = pose;
		ent->framelerp = 0;
	}
	else
	{
		ent->framelerp = (cl.time - ent->frame_start_time) / ent->frame_interval;
	}

	// weird things start happening if blend passes 1
	if (cl.paused || ent->framelerp > 1)
		ent->framelerp = 1;

	verts = (md3vert_mem_t *)((byte *)pmd3hdr + pmd3surf->ofsverts);
	tc = (md3tc_t *)((byte *)pmd3surf + pmd3surf->ofstc);
	tris = (unsigned int *)((byte *)pmd3surf + pmd3surf->ofstris);
	numtris = pmd3surf->numtris * 3;
	pose1 = ent->pose1 * pmd3surf->numverts;
	pose2 = ent->pose2 * pmd3surf->numverts;

	if (surface_transparent)
	{
		glEnable (GL_BLEND);
		glBlendFunc (GL_ONE, GL_ONE);
		glDepthMask (GL_FALSE);
		glDisable (GL_CULL_FACE);
	}
	else 
	if (ISTRANSPARENT(ent))
		glEnable (GL_BLEND);

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);		
	glEnable (GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);

	GL_Bind(texture);

	ColorMeBad(color, ent->transparency);	

	glBegin (GL_TRIANGLES);
	for (i=0 ; i<numtris ; i++)
	{
		float	s, t;

		v1 = verts + *tris + pose1;
		v2 = verts + *tris + pose2;

		s = tc[*tris].s, t = tc[*tris].t;


		if (gl_mtexable)
		{
			qglMultiTexCoord2f (GL_TEXTURE0, s, t);
			qglMultiTexCoord2f (GL_TEXTURE1, s, t);
		}
		else
		{
			glTexCoord2f (s, t);
		}

		lerpfrac = VectorL2Compare(v1->vec, v2->vec, distance) ? ent->framelerp : 1;

		VectorInterpolate (v1->vec, lerpfrac, v2->vec, interpolated_verts);
		glVertex3fv (interpolated_verts);

		*tris++;
	}
	glEnd ();

	if (surface_transparent)
	{
		glDisable (GL_BLEND);
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDepthMask (GL_TRUE);
		glEnable (GL_CULL_FACE);
	}
	else 
	if (ISTRANSPARENT(ent))
		glDisable (GL_BLEND);

	GL_PolygonOffset (0);
	glDisable (GL_BLEND);	
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
/*
=================
R_DrawQ3Frame
=================
*/
void R_DrawQ3Frame (int frame, md3header_t *pmd3hdr, md3surface_t *pmd3surf, entity_t *ent, int distance)
{
	int		i, j, numtris, pose, pose1, pose2;
	float		l, lerpfrac;
	vec3_t		lightvec, interpolated_verts;
	unsigned int	*tris;
	md3tc_t		*tc;
	md3vert_mem_t	*verts, *v1, *v2;
	model_t		*clmodel = ent->model;

	if ((frame >= pmd3hdr->numframes) || (frame < 0))
	{
		Con_DPrintf ("R_DrawQ3Frame: no such frame %d\n", frame);
		frame = 0;
	}

	if (ent->pose1 >= pmd3hdr->numframes)
		ent->pose1 = 0;

	pose = frame;

	if (!strcmp(clmodel->name, "progs/player/lower.md3"))
		ent->frame_interval = anims[legsanim].interval;
	else if (!strcmp(clmodel->name, "progs/player/upper.md3"))
		ent->frame_interval = anims[bodyanim].interval;
	else
		ent->frame_interval = 0.1;

	if (ent->pose2 != pose)
	{
		ent->frame_start_time = cl.time;
		ent->pose1 = ent->pose2;
		ent->pose2 = pose;
		ent->framelerp = 0;
	}
	else
	{
		ent->framelerp = (cl.time - ent->frame_start_time) / ent->frame_interval;
	}

	// weird things start happening if blend passes 1
	if (cl.paused || ent->framelerp > 1)
		ent->framelerp = 1;

	verts = (md3vert_mem_t *)((byte *)pmd3hdr + pmd3surf->ofsverts);
	tc = (md3tc_t *)((byte *)pmd3surf + pmd3surf->ofstc);
	tris = (unsigned int *)((byte *)pmd3surf + pmd3surf->ofstris);
	numtris = pmd3surf->numtris * 3;
	pose1 = ent->pose1 * pmd3surf->numverts;
	pose2 = ent->pose2 * pmd3surf->numverts;

	if (surface_transparent)
	{
		glEnable (GL_BLEND);
		glBlendFunc (GL_ONE, GL_ONE);
		glDepthMask (GL_FALSE);
		glDisable (GL_CULL_FACE);
	}
	else 
	if (ISTRANSPARENT(ent))
		glEnable (GL_BLEND);

	glBegin (GL_TRIANGLES);
	for (i=0 ; i<numtris ; i++)
	{
		float	s, t;

		v1 = verts + *tris + pose1;
		v2 = verts + *tris + pose2;

		s = tc[*tris].s, t = tc[*tris].t;


		if (gl_mtexable)
		{
			qglMultiTexCoord2f (GL_TEXTURE0, s, t);
			qglMultiTexCoord2f (GL_TEXTURE1, s, t);
		}
		else
		{
			glTexCoord2f (s, t);
		}

		lerpfrac = VectorL2Compare(v1->vec, v2->vec, distance) ? ent->framelerp : 1;

		if (gl_vertexlights.value && !full_light)
		{
			l = R_LerpVertexLight (v1->anorm_pitch, v1->anorm_yaw, v2->anorm_pitch, v2->anorm_yaw, lerpfrac, apitch, ayaw);
			l = min(l, 1);

			for (j=0 ; j<3 ; j++)
				lightvec[j] = lightcolor[j] / 256 + l;
			glColor4f (lightvec[0], lightvec[1], lightvec[2], ent->transparency);
		}
		else
		{
			l = FloatInterpolate (shadedots[v1->oldnormal>>8], lerpfrac, shadedots[v2->oldnormal>>8]);
			l = (l * shadelight + ambientlight) / 256;
			l = min(l, 1);

			glColor4f (l, l, l, ent->transparency);
		}

		VectorInterpolate (v1->vec, lerpfrac, v2->vec, interpolated_verts);
		glVertex3fv (interpolated_verts);

		*tris++;
	}
	glEnd ();

	if (surface_transparent)
	{
		glDisable (GL_BLEND);
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDepthMask (GL_TRUE);
		glEnable (GL_CULL_FACE);
	}
	else 
	if (ISTRANSPARENT(ent))
		glDisable (GL_BLEND);
}

/*
=================
R_DrawQ3Shadow
=================
*/
void R_DrawQ3Shadow (entity_t *ent, float lheight, float s1, float c1, trace_t downtrace)
{
	int		i, j, numtris, pose1, pose2;
	vec3_t		point1, point2, interpolated;
	md3header_t	*pmd3hdr;
	md3surface_t	*pmd3surf;
	unsigned int	*tris;
	md3vert_mem_t	*verts;
	model_t		*clmodel = ent->model;
#if 0
	float		m[16];
	md3tag_t	*tag;
	tagentity_t	*tagent;
#endif

	pmd3hdr = (md3header_t *)Mod_Extradata (clmodel);

	pmd3surf = (md3surface_t *)((byte *)pmd3hdr + pmd3hdr->ofssurfs);
	for (i=0 ; i<pmd3hdr->numsurfs ; i++)
	{
		verts = (md3vert_mem_t *)((byte *)pmd3hdr + pmd3surf->ofsverts);
		tris = (unsigned int *)((byte *)pmd3surf + pmd3surf->ofstris);
		numtris = pmd3surf->numtris * 3;
		pose1 = ent->pose1 * pmd3surf->numverts;
		pose2 = ent->pose2 * pmd3surf->numverts;

		glBegin (GL_TRIANGLES);
		for (j=0 ; j<numtris ; j++)
		{
			// normals and vertexes come from the frame list
			VectorCopy (verts[*tris+pose1].vec, point1);

			point1[0] -= shadevector[0] * (point1[2] + lheight);
			point1[1] -= shadevector[1] * (point1[2] + lheight);

			VectorCopy (verts[*tris+pose2].vec, point2);

			point2[0] -= shadevector[0] * (point2[2] + lheight);
			point2[1] -= shadevector[1] * (point2[2] + lheight);

			VectorInterpolate (point1, ent->framelerp, point2, interpolated);

			interpolated[2] = -(ent->origin[2] - downtrace.endpos[2]);

			interpolated[2] += ((interpolated[1] * (s1 * downtrace.plane.normal[0])) - 
					    (interpolated[0] * (c1 * downtrace.plane.normal[0])) - 
					    (interpolated[0] * (s1 * downtrace.plane.normal[1])) - 
					    (interpolated[1] * (c1 * downtrace.plane.normal[1]))) + 
					    ((1 - downtrace.plane.normal[2]) * 20) + 0.2;

			glVertex3fv (interpolated);

			*tris++;
		}
		glEnd ();

		pmd3surf = (md3surface_t *)((byte *)pmd3surf + pmd3surf->ofsend);
	}

	if (!pmd3hdr->numtags)	// single model, done
		return;

// no multimodel shadow support yet
#if 0
	tag = (md3tag_t *)((byte *)pmd3hdr + pmd3hdr->ofstags);
	tag += ent->pose2 * pmd3hdr->numtags;
	for (i=0 ; i<pmd3hdr->numtags ; i++, tag++)
	{
		if (multimodel_level == 0 && !strcmp(tag->name, "tag_torso"))
		{
			tagent = &q3player_body;
			ent = &q3player_body.ent;
			multimodel_level++;
		}
		else if (multimodel_level == 1 && !strcmp(tag->name, "tag_head"))
		{
			tagent = &q3player_head;
			ent = &q3player_head.ent;
			multimodel_level++;
		}
		else
		{
			continue;
		}

		glPushMatrix ();
		R_RotateForTagEntity (tagent, tag, m);
		glMultMatrixf (m);
		R_DrawQ3Shadow (ent, lheight, s1, c1, downtrace);
		glPopMatrix ();		
	}
#endif
}

#define	ADD_EXTRA_TEXTURE(_texture, _param)				\
	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, _param);	\
	GL_Bind (_texture);						\
									\
	glDepthMask (GL_FALSE);						\
	glEnable (GL_BLEND);						\
	glBlendFunc (GL_ONE, GL_ONE);					\
									\
	R_DrawQ3Frame (frame, pmd3hdr, pmd3surf, ent, INTERP_MAXDIST);	\
									\
	glDisable (GL_BLEND);						\
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);		\
	glDepthMask (GL_TRUE);

/*
=================
R_SetupQ3Frame
=================
*/
void R_SetupQ3Frame (entity_t *ent)
{
	int		i, j, frame, shadernum, texture, fb_texture, st_texture, pt_texture;
	float		m[16];
	md3header_t	*pmd3hdr;
	md3surface_t	*pmd3surf;
	md3tag_t	*tag;
	model_t		*clmodel = ent->model;
	tagentity_t	*tagent;

	if (!strcmp(clmodel->name, "progs/player/lower.md3"))
		frame = legsframe;
	else if (!strcmp(clmodel->name, "progs/player/upper.md3"))
		frame = bodyframe;
	else
	{
		frame = ent->frame;
		if (frame > 125)//R00k: FIXME! This is a crappy hack because the pak0 md3 model has only 1 working axe frame
		{
			if (frame > 137)
			{
				frame = ((frame - 138) + 120);				
			}
			else
			{
				if (frame > 131)
				{
					frame = ((frame - 132) + 120);
				}
				else
				{
					frame = ((frame - 126) + 120);
				}
			}
			if (frame > 125)
				frame = 18;
		}
	}
	// locate the proper data
	pmd3hdr = (md3header_t *)Mod_Extradata (clmodel);

	// draw all the triangles

	// draw non-transparent surfaces first, then the transparent ones
	for (i=0 ; i<2 ; i++)
	{
		pmd3surf = (md3surface_t *)((byte *)pmd3hdr + pmd3hdr->ofssurfs);
		for (j=0 ; j<pmd3hdr->numsurfs ; j++)
		{
			md3shader_mem_t	*shader;

			surface_transparent =  (strstr(pmd3surf->name, "energy") || 
						strstr(pmd3surf->name, "f_") || 
						strstr(pmd3surf->name, "flare") || 
						strstr(pmd3surf->name, "flash") || 
						strstr(pmd3surf->name, "Sphere") || 
						strstr(pmd3surf->name, "telep"));

			if ((!i && surface_transparent) || (i && !surface_transparent))
			{
				pmd3surf = (md3surface_t *)((byte *)pmd3surf + pmd3surf->ofsend);
				continue;
			}

			c_md3_polys += pmd3surf->numtris;

			shadernum = ent->skinnum;
			if ((shadernum >= pmd3surf->numshaders) || (shadernum < 0))
			{
				Con_DPrintf ("R_SetupQ3Frame: no such skin # %d\n", shadernum);
				shadernum = 0;
			}

			shader = (md3shader_mem_t *)((byte *)pmd3hdr + pmd3surf->ofsshaders);
			
			texture = shader[shadernum].gl_texnum;

			st_texture = shader[shadernum].st_texnum;
			pt_texture = shader[shadernum].pt_texnum;
			fb_texture = shader[shadernum].fb_texnum;

			//*********************************************************************************************************************
			if (cl_teamskin.value)
			{		
				GL_DisableMultitexture ();
				glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
				GL_Bind (texture);
				R_DrawQ3Frame (frame, pmd3hdr, pmd3surf, ent, INTERP_MAXDIST);
				if ((pt_texture) && (st_texture))
				{			
					R_DrawQ3SkinFrame (frame, pmd3hdr, pmd3surf, ent, INTERP_MAXDIST, ent->pantscolor,pt_texture);
					R_DrawQ3SkinFrame (frame, pmd3hdr, pmd3surf, ent, INTERP_MAXDIST, ent->shirtcolor,st_texture);
				}
			}
			//*********************************************************************************************************************
			else
			{
				if (fb_texture && gl_mtexable)
				{
					GL_DisableMultitexture ();
					glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
					GL_Bind (texture);

					GL_EnableMultitexture ();
					if (gl_add_ext)
					{
						glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
						GL_Bind (fb_texture);
					}

					R_DrawQ3Frame (frame, pmd3hdr, pmd3surf, ent, INTERP_MAXDIST);

					if (!gl_add_ext)
					{
						ADD_EXTRA_TEXTURE(fb_texture, GL_DECAL);
					}

					GL_DisableMultitexture ();
				}
				else
				{
					GL_DisableMultitexture ();
					glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
					GL_Bind (texture);

					R_DrawQ3Frame (frame, pmd3hdr, pmd3surf, ent, INTERP_MAXDIST);

					if (fb_texture)
					{
						ADD_EXTRA_TEXTURE(fb_texture, GL_REPLACE);
					}
				}
			}

			pmd3surf = (md3surface_t *)((byte *)pmd3surf + pmd3surf->ofsend);
		}
	}

	if (!pmd3hdr->numtags)	// single model, done
		return;

	tag = (md3tag_t *)((byte *)pmd3hdr + pmd3hdr->ofstags);
	tag += frame * pmd3hdr->numtags;
	for (i=0 ; i<pmd3hdr->numtags ; i++, tag++)
	{
		if (multimodel_level == 0 && !strcmp(tag->name, "tag_torso"))
		{
			tagent = &q3player_body;
			ent = &q3player_body.ent;
			multimodel_level++;
		}
		else if (multimodel_level == 1 && !strcmp(tag->name, "tag_head"))
		{
			tagent = &q3player_head;
			ent = &q3player_head.ent;
			multimodel_level++;
		}
		else
		{
			continue;
		}

		glPushMatrix ();		
		R_RotateForTagEntity (tagent, tag, m);
		glMultMatrixf (m);
		R_SetupQ3Frame (ent);
		glPopMatrix ();		
	}
}

/*
=================
R_DrawQ3Model
=================
*/
void R_DrawQ3Model (entity_t *ent)
{
	vec3_t		mins, maxs, md3_scale_origin = {0, 0, 0};
	model_t		*clmodel = ent->model;
	float scale;
	
	VectorAdd (ent->origin, clmodel->mins, mins);
	VectorAdd (ent->origin, clmodel->maxs, maxs);

	if (ent->angles[0] || ent->angles[1] || ent->angles[2])
	{
		if (R_CullSphere(ent->origin, clmodel->radius))
			return;
	}
	else
	{
		if (R_CullBox(mins, maxs))
			return;
	}

	VectorCopy (ent->origin, r_entorigin);
	VectorSubtract (r_origin, r_entorigin, modelorg);

	// get lighting information
	R_SetupLighting (ent);

	shadedots = r_avertexnormal_dots[((int)(ent->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];

	glPushMatrix ();	

	if (ent == &cl.viewent)
		R_RotateForViewEntity (ent);
	else
		R_RotateForEntity(ent, false);

	if ((ent == &cl.viewent) && (cl_gun_fovscale.value) && (scr_fov.value != 0))
	{
		if (scr_fov.value <= 90)
			scale = 1.0f;
		else
			scale = 1.0f / tan( DEG2RAD(scr_fov.value/2));

		glTranslatef (md3_scale_origin[0]*scale, md3_scale_origin[1], md3_scale_origin[2]);
		glScalef (scale, 1, 1);
	}
	else
	{
		glTranslatef (md3_scale_origin[0], md3_scale_origin[1], md3_scale_origin[2]);
	}

	if (gl_smoothmodels.value)
		glShadeModel (GL_SMOOTH);

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

	if ((!strcmp(ent->model->name, "progs/player/lower.md3"))||(!strcmp(ent->model->name, "progs/player/upper.md3")))
	{
		q3player_body.ent.transparency = q3player_head.ent.transparency = cl_entities[cl.viewentity].transparency;
		R_ReplaceQ3Frame (ent->frame);
		ent->noshadow = true;
	}

	multimodel_level = 0;
	R_SetupQ3Frame (ent);

	glShadeModel (GL_FLAT);
	if (gl_affinemodels.value)
		glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glPopMatrix ();	

	if (r_shadows.value && !ent->noshadow)
	{
		int		farclip;
		float		theta, lheight, s1, c1;
		vec3_t		downmove;
		trace_t		downtrace;
		static	float	shadescale = 0;

		farclip = max((int)r_farclip.value, 4096);

		if (!shadescale)
			shadescale = 1 / sqrt(2);
		theta = -ent->angles[1] / 180 * M_PI;

		VectorSet (shadevector, cos(theta) * shadescale, sin(theta) * shadescale, shadescale);

		glPushMatrix ();
		
		R_RotateForEntity (ent, true);

		VectorCopy (ent->origin, downmove);
		downmove[2] -= farclip;
		memset (&downtrace, 0, sizeof(downtrace));
		SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, ent->origin, downmove, &downtrace);

		lheight = ent->origin[2] - lightspot[2];

		s1 = sin(ent->angles[1] / 180 * M_PI);
		c1 = cos(ent->angles[1] / 180 * M_PI);

		glDepthMask (GL_FALSE);
		glDisable (GL_TEXTURE_2D);
		glEnable (GL_BLEND);
		glColor4f (0, 0, 0,((ambientlight - (mins[2] - downtrace.endpos[2]))*r_shadows.value)*0.0066*ent->transparency);//R00k ACK! it works!

		multimodel_level = 0;

		if (have_stencil) 
		{
			glEnable(GL_STENCIL_TEST);
			glStencilFunc(GL_EQUAL,1,2);
			glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
		}

		R_DrawQ3Shadow (ent, lheight, s1, c1, downtrace);

		if (have_stencil) 
			glDisable(GL_STENCIL_TEST);

		glDepthMask (GL_TRUE);
		glEnable (GL_TEXTURE_2D);
		glDisable (GL_BLEND);

		glPopMatrix ();		
	}

	glColor4f(1,1,1,1);
}
//==================================================================================

// joe: from FuhQuake
void R_SetSpritesState(qboolean state)
{
	static qboolean	r_state = false;

	if (r_state == state)
		return;

	r_state = state;

	if (state)
	{
		if (currententity->model->modhint == MOD_SPR32)
		{
			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glEnable (GL_BLEND);
			glDepthMask (GL_FALSE);	// disable zbuffer updates
		}
		else
		{
			GL_DisableMultitexture ();
			glEnable (GL_ALPHA_TEST);
		}
	}
	else
	{
		if (currententity->model->modhint == MOD_SPR32)
		{
			glDisable (GL_BLEND);
			glDepthMask (GL_TRUE);	// enable zbuffer updates
		}
		else
		{
			glDisable (GL_ALPHA_TEST);
		}
	}
}

void SortEntitiesByTransparency (void)
{
	int		i, j;
	entity_t	*tmp;

	for (i = 0 ; i < cl_numvisedicts ; i++)
	{
		if (cl_visedicts[i]->transparency < 1 && cl_visedicts[i]->transparency > 0)
		{
			for (j = cl_numvisedicts - 1 ; j > i ; j--)
			{
				// if not transparent, exchange with transparent
				if (cl_visedicts[j]->transparency == 1)
				{
					tmp = cl_visedicts[i];
					cl_visedicts[i] = cl_visedicts[j];
					cl_visedicts[j] = tmp;
					break;
				}
				else
					continue;
			}
			if (j == i)
				return;
		}
	}
}

float SpriteForMDL(void)
{
	if ((!strcmp(currententity->model->name, "maps/b_shell0.bsp")) || (!strcmp(currententity->model->name, "maps/b_shell1.bsp")))
	{		
		return mi_2dshells;
	}
	
	if ((!strcmp(currententity->model->name, "maps/b_batt0.bsp")) || (!strcmp(currententity->model->name, "maps/b_batt1.bsp")))
	{		
		return mi_2dcells;
	}
	
	if ((!strcmp(currententity->model->name, "maps/b_rock0.bsp")) || (!strcmp(currententity->model->name, "maps/b_rock1.bsp")))
	{
		return mi_2drockets;
	}
	
	if ((!strcmp(currententity->model->name, "maps/b_nail0.bsp")) || (!strcmp(currententity->model->name, "maps/b_nail1.bsp")))
	{		
		return mi_2dnails;
	}
	
	if (!strcmp(currententity->model->name, "maps/b_bh100.bsp"))
	{
		return mi_2dmega;
	}

	if (!strcmp(currententity->model->name, "maps/b_bh10.bsp"))
	{
		return mi_2dhealth10;
	}
	
	if (!strcmp(currententity->model->name, "maps/b_bh25.bsp"))
	{
		return mi_2dhealth25;
	}

	//------------------------
	if (!strcmp(currententity->model->name, "progs/invulner.mdl"))
	{
		return mi_2dpent;
	}

	if (!strcmp(currententity->model->name, "progs/quaddama.mdl"))
	{		
		return mi_2dquad;
	}

	if (!strcmp(currententity->model->name, "progs/invisibl.mdl"))
	{		
		return mi_2dring;
	}

	if (!strcmp(currententity->model->name, "progs/suit.mdl"))
	{		
		return mi_2dsuit;
	}
	//---------------------------------------------
	if (!strcmp(currententity->model->name, "progs/armor.mdl"))
	{
		if (currententity->skinnum == 0)
			return mi_2darmor1;
		if (currententity->skinnum == 1)
			return mi_2darmor2;
		if (currententity->skinnum == 2)
			return mi_2darmor3;
	}
	
	if(!strcmp(currententity->model->name, "progs/backpack.mdl"))
    {
		return mi_2dbackpack;		
	}

	if(!strcmp(currententity->model->name, "progs/g_rock2.mdl"))
    {
		return mi_2drl;		
	}

	if(!strcmp(currententity->model->name, "progs/g_rock.mdl"))
    {
		return mi_2dgl;		
	}

	if(!strcmp(currententity->model->name, "progs/g_light.mdl"))
    {
		return mi_2dlg;		
	}

	if(!strcmp(currententity->model->name, "progs/g_nail.mdl"))
    {
		return mi_2dng;		
	}
	if(!strcmp(currententity->model->name, "progs/g_nail2.mdl"))
    {
		return mi_2dsng;		
	}
	if(!strcmp(currententity->model->name, "progs/g_shot.mdl"))
    {
		return mi_2dssg;		
	}

	return -1;
}

int SetFlameModelState(void)
{
	dlight_t	*dl;
	float frametime;

	frametime = (fabs(cl.ctime - cl.oldtime)*10);

	if (!gl_part_flames.value && !strcmp(currententity->model->name, "progs/flame0.mdl"))
	{
		if (!r_drawflame.value) //R00k
			return -1;

		currententity->model = cl.model_precache[cl_modelindex[mi_flame1]];

		if ((r_drawflame.value == 2)&&(r_dynamic.value)) //R00k
		{
			dl = CL_AllocDlight (0);					
			VectorCopy (currententity->origin, dl->origin);					
			dl->radius = (rand()%100+1);	
			dl->die = cl.time + frametime;						
			dl->type = lt_explosion;
		}
	}
	else if (gl_part_flames.value)
	{
		vec3_t	liteorg;

		VectorCopy (currententity->origin, liteorg);
		if (currententity->baseline.modelindex == cl_modelindex[mi_flame0])
		{
			if (!r_drawflame.value) //R00k
				return -1;

			liteorg[2] += 5.5;
			QMB_TorchFlame(liteorg);
			
			if ((r_drawflame.value == 2)&&(r_dynamic.value)) //R00k
			{
				dl = CL_AllocDlight (0);					
				VectorCopy (liteorg, dl->origin);					
				dl->radius = (rand()%100+1);	
				dl->die = cl.time + frametime;						
				dl->type = lt_explosion;
			}
		}
		else if (currententity->baseline.modelindex == cl_modelindex[mi_flame1])
		{
			if (!r_drawflame.value) //R00k
				return -1;

			liteorg[2] += 5.5;
			QMB_TorchFlame(liteorg);
			currententity->model = cl.model_precache[cl_modelindex[mi_flame0]];

			if ((r_drawflame.value == 2)&&(r_dynamic.value)) //R00k
			{
				dl = CL_AllocDlight (0);					
				VectorCopy (liteorg, dl->origin);					
				dl->radius = (rand()%100+1);	
				dl->die = cl.time + frametime;						
				dl->type = lt_explosion;
			}
		}
		else if (currententity->baseline.modelindex == cl_modelindex[mi_flame2])
		{
			if (!r_drawflame.value) //R00k
				return -1;

			liteorg[2] -= 1;
			QMB_BigTorchFlame(liteorg);

			if ((r_drawflame.value == 2)&&(r_dynamic.value)) //R00k
			{
				dl = CL_AllocDlight (0);					
				VectorCopy (liteorg, dl->origin);					
				dl->radius = (rand()%200+1);	
				dl->die = cl.time + frametime;						
				dl->type = lt_explosion;
			}

			return -1;	//continue;
		}		
		else
		if (gl_part_flames.value && !strcmp(currententity->model->name, "progs/wyvflame.mdl"))
		{
			if (!r_drawflame.value) //R00k
				return -1;

			liteorg[2] -= 1;
			QMB_BigTorchFlame(liteorg);
			return -1;	//continue;
		}
		else
		if ((currententity->baseline.modelindex == cl_modelindex[mi_md3_flame0])||(currententity->model->modhint == MOD_FLAME))
		{
			if (!r_drawflame.value) //R00k
				return -1;

			liteorg[2] += 5.5;
			QMB_TorchFlame(liteorg);
			
			if ((r_drawflame.value == 2)&&(r_dynamic.value)) //R00k
			{
				dl = CL_AllocDlight (0);					
				VectorCopy (liteorg, dl->origin);					
				dl->radius = (rand()%50+1);	
				dl->die = cl.time + frametime;						
				dl->type = lt_explosion;
			}
		}
	}
	return 0;
}

/*
=============
R_DrawEntitiesOnList
=============
*/
void R_DrawEntitiesOnList(void)
{
	int	i, idx;

	if (!r_drawentities.value)
		return;

	SortEntitiesByTransparency();//Joe

	for (i=0 ; i<cl_numvisedicts ; i++)
	{
		currententity = cl_visedicts[i];

		if (r_simpleitems.value)
		{
			idx = SpriteForMDL();
			if (idx >=0)
			{
				currententity->model = cl.model_precache[cl_modelindex[idx]];
			}
		}

		if ((currententity == &cl_entities[cl.viewentity]) && (chase_active.value))
		{
				currententity->angles[PITCH] = bound(-45,currententity->angles[PITCH],45);//R00k: limit the pitch of the player model in chase view.
		}

		switch (currententity->model->type)
		{
			case mod_alias:
				
				if (qmb_initialized && SetFlameModelState() == -1)
					continue;
				
				//R00k, replace laser.mdl with particle effect, if using QMB particles
				if (((qmb_initialized) && (particle_mode)) && !strcmp(currententity->model->name, "progs/laser.mdl"))
				{				
					currententity->model->flags |= EF_TRACER2;
					continue;
				}

				//R00k replace keys with flag model for CTF -- start 
				if (cl_teamflags.value)
				{		
					if (currententity->baseline.modelindex == cl_modelindex[mi_w_g_key])
					{	
						currententity->skinnum = 0;
						currententity->model = cl.model_precache[cl_modelindex[mi_flag]];									
						currententity->origin[2] -= 22;
						//R00k dont animate since qc doesnt update frames for the "key" models
						currententity->frame = currententity->baseline.frame;
						currententity->pose1 = currententity->pose2 = 0;
						currententity->noshadow = true;
					}
					else
					{
						if (currententity->baseline.modelindex == cl_modelindex[mi_w_s_key])
						{							
							currententity->model = cl.model_precache[cl_modelindex[mi_flag]];									
							currententity->origin[2] -= 22;
							//R00k dont animate since qc doesnt update frames for the "key" models
							currententity->frame = currententity->baseline.frame;
							currententity->pose1 = currententity->pose2 = 0;
							currententity->skinnum = 1;
							currententity->noshadow = true;
						}
					}
				}

				R_DrawAliasModel(currententity);							
				break;
			
			case mod_md3: 
				R_DrawQ3Model (currententity); 
				break;
		
			case mod_brush:
				R_DrawBrushModel (currententity);
				break;
			
			case mod_sprite:
					R_SetSpritesState (true);
					R_DrawSpriteModel (currententity);
					R_SetSpritesState (false);
				break;

			case mod_spr32:
					R_SetSpritesState (true);
					R_DrawSpriteModel (currententity);
					R_SetSpritesState (false);
				break;

			default:
				break;
		}
	}
}


/*
=============
R_DrawViewModel
=============
*/
extern void QMB_LaserSight (void);	
void R_DrawViewModel(void)
{
	currententity = &cl.viewent;

	if (!r_drawviewmodel.value || chase_active.value || !r_drawentities.value || (cl.stats[STAT_HEALTH] <= 0) || !currententity->model)
	{
		return;
	}
	
	// currententity->transparency is not used when drawing the weapon model
	currententity->transparency = (cl.items & IT_INVISIBILITY) ? gl_ringalpha.value : bound(0, r_drawviewmodel.value, 1);

	// hack the depth range to prevent view model from poking into walls
	glDepthRange (gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin));

	switch (currententity->model->type)
	{
	case mod_alias:
		R_DrawAliasModel(currententity);
		break;

	case mod_md3:
		R_DrawQ3Model (currententity);
		break;
	}

	glDepthRange (gldepthmin, gldepthmax);
}

/*
============
R_PolyBlend
============
*/
void R_PolyBlend (void)
{
	if ((vid_hwgamma_enabled && gl_hwblend.value) || !v_blend[3])
		return;
 	
	GL_DisableMultitexture();

	glDisable (GL_ALPHA_TEST);
	glEnable (GL_BLEND);
	glDisable (GL_DEPTH_TEST);
	glDisable (GL_TEXTURE_2D);

    glLoadIdentity ();

	if (r_bloom.value)
		VectorScale(v_blend,0.10,v_blend);

	glColor4fv (v_blend);

	glBegin (GL_QUADS);
	glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y);
	glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y);
	glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y + r_refdef.vrect.height);
	glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height);
	glEnd ();

	glDisable (GL_BLEND);
	glEnable (GL_TEXTURE_2D);
	glEnable (GL_ALPHA_TEST);

	glColor4f(1,1,1,1);
}

/*
================
R_BrightenScreen
================
*/
void R_BrightenScreen (void)
{
	extern	float	vid_gamma;
	float		f;

	if (vid_hwgamma_enabled || v_contrast.value <= 1.0)
		return;

	f = min(v_contrast.value, 3);
	f = pow (f, vid_gamma);
	
	glDisable (GL_TEXTURE_2D);
	glEnable (GL_BLEND);
	glBlendFunc (GL_DST_COLOR, GL_ONE);
	glBegin (GL_QUADS);
	while (f > 1)
	{
		if (f >= 2)
			glColor3f (1, 1, 1);
		else
			glColor3f (f - 1, f - 1, f - 1);
		glVertex2f (0, 0);
		glVertex2f (vid.width, 0);
		glVertex2f (vid.width, vid.height);
		glVertex2f (0, vid.height);
		f *= 0.5;
	}
	glEnd ();
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable (GL_TEXTURE_2D);
	glDisable (GL_BLEND);
	glColor4f(1,1,1,1);
}

int SignbitsForPlane (mplane_t *out)
{
	int	bits, j;

	// for fast box on planeside test

	bits = 0;
	for (j=0 ; j<3 ; j++)
	{
		if (out->normal[j] < 0)
			bits |= 1<<j;
	}
	return bits;
}

/*
===============
TurnVector -- johnfitz

turn forward towards side on the plane defined by forward and side
if angle = 90, the result will be equal to side
assumes side and forward are perpendicular, and normalized
to turn away from side, use a negative angle
===============
*/
//#define DEG2RAD( a ) ( (a) * M_PI_DIV_180 )
void TurnVector (vec3_t out, const vec3_t forward, const vec3_t side, float angle)
{
	float scale_forward, scale_side;

	scale_forward = cos( DEG2RAD( angle ) );
	scale_side = sin( DEG2RAD( angle ) );

	out[0] = scale_forward*forward[0] + scale_side*side[0];
	out[1] = scale_forward*forward[1] + scale_side*side[1];
	out[2] = scale_forward*forward[2] + scale_side*side[2];
}

/*
===============
R_SetFrustum 
===============
*/

static void R_SetFrustum (void)
{
	// LordHavoc: note to all quake engine coders, the special case for 90
	// degrees assumed a square view (wrong), so I removed it, Quake2 has it
	// disabled as well.

	// rotate R_VIEWFORWARD right by FOV_X/2 degrees
	RotatePointAroundVector( frustum[0].normal, vup, vpn, -(90 - r_refdef.fov_x / 2));
	frustum[0].dist = DotProduct (r_origin, frustum[0].normal);
	PlaneClassify(&frustum[0]);

	// rotate R_VIEWFORWARD left by FOV_X/2 degrees
	RotatePointAroundVector( frustum[1].normal, vup, vpn, (90 - r_refdef.fov_x / 2));
	frustum[1].dist = DotProduct (r_origin, frustum[1].normal);
	PlaneClassify(&frustum[1]);

	// rotate R_VIEWFORWARD up by FOV_X/2 degrees
	RotatePointAroundVector( frustum[2].normal, vright, vpn, -(90 - r_refdef.fov_y / 2));
	frustum[2].dist = DotProduct (r_origin, frustum[2].normal);
	PlaneClassify(&frustum[2]);

	// rotate R_VIEWFORWARD down by FOV_X/2 degrees
	RotatePointAroundVector( frustum[3].normal, vright, vpn, (90 - r_refdef.fov_y / 2));
	frustum[3].dist = DotProduct (r_origin, frustum[3].normal);
	PlaneClassify(&frustum[3]);
}

/*
===============
R_SetupFrame
===============
*/
void R_SetupFrame (void)
{
	vec3_t		testorigin;
	mleaf_t		*leaf;

	if (nehahra)
	{
		if (oldsky.value && r_skybox.string[0])
			Cvar_Set ("r_skybox", "");
		if (!oldsky.value && !r_skybox.string[0])
			Cvar_Set ("r_skybox", prev_skybox);
	}

	wateralpha = r_wateralpha.value;

	R_AnimateLight ();

	r_framecount++;

// build the transformation matrix for the given view angles
	VectorCopy (r_refdef.vieworg, r_origin);
	AngleVectors (r_refdef.viewangles, vpn, vright, vup);

// current viewleaf
	r_oldviewleaf = r_viewleaf;
	r_oldviewleaf2 = r_viewleaf2;

	r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel);
	r_viewleaf2 = NULL;

	// check above and below so crossing solid water doesn't draw wrong
	if (r_viewleaf->contents <= CONTENTS_WATER && r_viewleaf->contents >= CONTENTS_LAVA)
	{
		// look up a bit
		VectorCopy (r_origin, testorigin);
		testorigin[2] += 10;
		leaf = Mod_PointInLeaf (testorigin, cl.worldmodel);
		if (leaf->contents == CONTENTS_EMPTY)
			r_viewleaf2 = leaf;
	}
	else if (r_viewleaf->contents == CONTENTS_EMPTY)
	{
		// look down a bit
		VectorCopy (r_origin, testorigin);
		testorigin[2] -= 10;
		leaf = Mod_PointInLeaf (testorigin, cl.worldmodel);
		if (leaf->contents <= CONTENTS_WATER &&	leaf->contents >= CONTENTS_LAVA)
			r_viewleaf2 = leaf;
	}

	V_SetContentsColor (r_viewleaf->contents);

	V_AddWaterfog (r_viewleaf->contents);	
	
	if ((nehahra) || (gl_fogenable.value))
		Neh_SetupFrame ();

	V_CalcBlend ();

	r_cache_thrash = false;

	c_brush_polys = 0;
	c_alias_polys = 0;
}

__inline void MYgluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
{
	GLdouble	xmin, xmax, ymin, ymax;

	ymax = zNear * tan(fovy * M_PI / 360.0f);
	ymin = -ymax;

	xmin = ymin * aspect;
	xmax = ymax * aspect;

	glFrustum (xmin, xmax, ymin, ymax, zNear, zFar);	
}

void R_SetupGL (void)
{
	float	screenaspect;
	int	x, x2, y2, y, w, h, farclip;

	// set up viewpoint
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	x = r_refdef.vrect.x * glwidth/vid.width;
	x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width;
	y = (vid.height-r_refdef.vrect.y) * glheight/vid.height;
	y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height;

	w = x2 - x;
	h = y - y2;

	glViewport (glx + x, gly + y2, w, h);

	screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height;
	farclip = max((int)r_farclip.value, 4096);
	
	MYgluPerspective (r_refdef.fov_y, screenaspect, 1, farclip);

	glCullFace (GL_FRONT);

	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity ();

	glRotatef (-90, 1, 0, 0);	    // put Z going up
	glRotatef (90, 0, 0, 1);	    // put Z going up
	glRotatef (-r_refdef.viewangles[2], 1, 0, 0);
	glRotatef (-r_refdef.viewangles[0], 0, 1, 0);
	glRotatef (-r_refdef.viewangles[1], 0, 0, 1);
	glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]);

	glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix);

	// set drawing parms
	if (gl_cull.value)
		glEnable (GL_CULL_FACE);
	else
		glDisable (GL_CULL_FACE);

	glDisable (GL_BLEND);
	glDisable (GL_ALPHA_TEST);
	glEnable (GL_DEPTH_TEST);
}

/*
===============
R_Init
===============
*/

extern void R_ToggleParticles_f (void);
extern void R_InitSmokes (void);
extern void R_InitDecals (void);
extern void R_ToggleDecals_f (void);//R00k
extern void R_InitBloomTextures( void );


void R_Init (void)
{	
	Cmd_AddCommand ("timerefresh", R_TimeRefresh_f);
	Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
	Cmd_AddCommand ("toggleparticles", R_ToggleParticles_f);
	Cmd_AddCommand ("set_interpolated_weapon", Set_Interpolated_Weapon_f);
	Cmd_AddCommand ("toggledecals", R_ToggleDecals_f);//R00k

	Cvar_RegisterVariable (&r_lightmap);
	Cvar_RegisterVariable (&r_interpolate_light);
	Cvar_RegisterVariable (&r_fullbright);
	Cvar_RegisterVariable (&r_drawentities);
	Cvar_RegisterVariable (&r_drawviewmodel);
	Cvar_RegisterVariable (&r_drawviewmodelsize);//R00k
	Cvar_RegisterVariable (&r_shadows);
	Cvar_RegisterVariable (&r_wateralpha);
	Cvar_RegisterVariable (&r_lavaalpha);
	Cvar_RegisterVariable (&r_telealpha);
	Cvar_RegisterVariable (&r_turbalpha_distance);
	Cvar_RegisterVariable (&r_dynamic);
	Cvar_RegisterVariable (&r_novis);
	Cvar_RegisterVariable (&r_speeds);
	Cvar_RegisterVariable (&r_fastsky);
	Cvar_RegisterVariable (&r_fastturb);
	Cvar_RegisterVariable (&r_skybox);
	Cvar_RegisterVariable (&r_skyscroll);
	Cvar_RegisterVariable (&r_skyspeed);
	Cvar_RegisterVariable (&r_farclip);
	Cvar_RegisterVariable (&r_drawflame);//R00k	
	Cvar_RegisterVariable (&r_drawbbox);
	Cvar_RegisterVariable (&r_drawlocs);
	Cvar_RegisterVariable (&r_outline);	
	Cvar_RegisterVariable (&r_celshading);
//	Cvar_RegisterVariable (&r_fullbrightskins);
	Cvar_RegisterVariable (&r_waterwarp);
	Cvar_RegisterVariable (&r_waterripple);
	Cvar_RegisterVariable (&gl_interpolate_transform);	
	Cvar_RegisterVariable (&gl_finish);
	Cvar_RegisterVariable (&gl_clear);
	Cvar_RegisterVariable (&gl_cull);
	Cvar_RegisterVariable (&gl_ztrick);
	Cvar_RegisterVariable (&gl_smoothmodels);
	Cvar_RegisterVariable (&gl_affinemodels);
	Cvar_RegisterVariable (&gl_polyblend);
	Cvar_RegisterVariable (&gl_flashblend);
	Cvar_RegisterVariable (&gl_playermip);
	Cvar_RegisterVariable (&gl_nocolors);
	Cvar_RegisterVariable (&gl_loadlitfiles);
	Cvar_RegisterVariable (&gl_loadq3models);
	Cvar_RegisterVariable (&gl_doubleeyes);
	Cvar_RegisterVariable (&gl_interdist);
	Cvar_RegisterVariable (&gl_waterfog);
	Cvar_RegisterVariable (&gl_waterfog_density);
	Cvar_RegisterVariable (&gl_detail);
	Cvar_RegisterVariable (&gl_caustics);
	Cvar_RegisterVariable (&gl_ringalpha);
	Cvar_RegisterVariable (&gl_fb_bmodels);
	Cvar_RegisterVariable (&gl_fb_models);	
	Cvar_RegisterVariable (&gl_vertexlights);
	
	Cvar_RegisterVariable (&gl_part_explosions);
	Cvar_RegisterVariable (&gl_part_trails);
	Cvar_RegisterVariable (&gl_part_sparks);
	Cvar_RegisterVariable (&gl_part_gunshots);
	Cvar_RegisterVariable (&gl_part_blood);
	Cvar_RegisterVariable (&gl_part_telesplash);
	Cvar_RegisterVariable (&gl_part_blobs);
	Cvar_RegisterVariable (&gl_part_lavasplash);
	Cvar_RegisterVariable (&gl_part_flames);
	Cvar_RegisterVariable (&gl_part_lightning);
	Cvar_RegisterVariable (&gl_part_flies);
	Cvar_RegisterVariable (&gl_damageshells);
	Cvar_RegisterVariable (&gl_powerupshells);
	Cvar_RegisterVariable (&gl_powerupshell_size);
	Cvar_RegisterVariable (&gl_anisotropic);		
	Cvar_RegisterVariable (&gl_beergoggles);//R00k -start
	Cvar_RegisterVariable (&gl_nightmare);
	Cvar_RegisterVariable (&gl_deathblur);
	Cvar_RegisterVariable (&gl_hurtblur);
	Cvar_RegisterVariable (&gl_blur_alpha);
	Cvar_RegisterVariable (&gl_laserpoint);
	Cvar_RegisterVariable (&gl_lavasmoke);//R00k -end
	Cvar_RegisterVariable (&gl_fogenable); 
	Cvar_RegisterVariable (&gl_fogdensity);
	Cvar_RegisterVariable (&gl_fogstart); 
	Cvar_RegisterVariable (&gl_fogend); 
	Cvar_RegisterVariable (&gl_fogsky);
	Cvar_RegisterVariable (&gl_fogred); 
	Cvar_RegisterVariable (&gl_fogblue);
	Cvar_RegisterVariable (&gl_foggreen);
	Cvar_RegisterVariable (&cl_autodemo);
	Cvar_RegisterVariable (&cl_teamskin);
	Cvar_RegisterVariable (&cl_teamflags);
	Cvar_RegisterVariable (&cl_gun_fovscale);
	Cvar_RegisterVariable (&gl_shiny);
	Cvar_RegisterVariable (&gl_rain);//R00k
//	Cvar_RegisterVariable (&gl_rain_level);//R00k
	Cvar_RegisterVariable (&r_bloom);
	Cvar_RegisterVariable (&r_bloom_darken);
	Cvar_RegisterVariable (&r_bloom_alpha);
	Cvar_RegisterVariable (&r_bloom_diamond_size);
	Cvar_RegisterVariable (&r_bloom_intensity);
	Cvar_RegisterVariable (&r_bloom_sample_size);
	Cvar_RegisterVariable (&r_bloom_fast_sample);
	Cvar_RegisterVariable (&scr_bloodsplat);
	Cvar_RegisterVariable (&r_simpleitems);


	Cmd_AddLegacyCommand ("loadsky", "r_skybox");	

	R_InitTextures ();
	R_InitOtherTextures ();

	R_InitBubble ();
	R_InitParticles ();
	
	R_InitSmokes ();//MHQuake
	R_InitVertexLights ();

	R_InitDecals ();

	R_InitBloomTextures(); 
	// by joe
	skyboxtextures = texture_extension_number;
	texture_extension_number += 6;

	playertextures = texture_extension_number;
	texture_extension_number += 16;

	// fullbright skins
	texture_extension_number += 16;
}

/*
================
R_RenderScene

r_refdef must be set before the first call
================
*/
extern void R_UpdateCoronas ();
extern void R_SmokeFrame (void);
extern void R_DrawDecals (void);
extern void R_DrawLocs (void);
extern void QMB_LetItRain(void);

void R_RenderScene (void)
{
	vec3_t		colors;
		
	R_SetupFrame ();

	R_SetFrustum ();

	R_SetupGL ();

	R_MarkLeaves ();	// done here so we know if we're in water

	R_DrawWorld ();		// adds static entities to the list

	S_ExtraUpdate ();	// don't let sound get messed up if going slow
	
	R_DrawEntitiesOnList ();

	if ((particle_mode) && (decals_enabled))
		R_DrawDecals();

	R_DrawWaterSurfaces ();

	if (gl_coronas.value)
		R_UpdateCoronas();

	if (gl_lavasmoke.value)
		R_SmokeFrame ();

	if (gl_laserpoint.value)
		QMB_LaserSight ();// R00k

	if (gl_rain.value)
		QMB_LetItRain();//R00k added
	
//	if ((cl.items & IT_FLASHLIGHT)&&(cl.flashlighttime>cl.time))//R00k future feature
//		QMB_FlashLight();

	GL_DisableMultitexture ();	

	if ((gl_fogenable.value) && (gl_fogstart.value >= 0) && (gl_fogstart.value < gl_fogend.value))
	{
		glFogi(GL_FOG_MODE, GL_LINEAR);
		colors[0] = gl_fogred.value;
		colors[1] = gl_foggreen.value;
		colors[2] = gl_fogblue.value; 
		glFogfv(GL_FOG_COLOR, colors); 
		glFogf(GL_FOG_START, gl_fogstart.value); 
		glFogf(GL_FOG_END, gl_fogend.value); 
		glEnable(GL_FOG);
	}
	else
		glDisable(GL_FOG);
}

/*
=============
R_Clear
=============
*/
int	gl_ztrickframe = 0;

void R_Clear (void)
{
	static qboolean	cleartogray;
	qboolean	clear = false;
	
	if (gl_clear.value)
	{
		clear = true;
		if (cleartogray)
		{
			if (gl_fogenable.value)
				glClearColor(gl_fogred.value,gl_foggreen.value,gl_fogblue.value,0.5);//Tei custom clear color
			else
				glClearColor (0, 0, 0, 0);
			cleartogray = false;
		}
	}
	else if (!vid_hwgamma_enabled && v_contrast.value > 1)
	{
		clear = true;
		if (!cleartogray)
		{
			if (gl_fogenable.value)
				glClearColor(gl_fogred.value,gl_foggreen.value,gl_fogblue.value,0.5);//Tei custom clear color
			else
				glClearColor (0.1, 0.1, 0.1, 0);
			cleartogray = true;
		}
	}

	if (gl_ztrick.value)
	{
		if (clear)
			glClear (GL_COLOR_BUFFER_BIT);

		gl_ztrickframe = !gl_ztrickframe;
		if (gl_ztrickframe)
		{
			gldepthmin = 0;
			gldepthmax = 0.49999;
			glDepthFunc (GL_LEQUAL);
		}
		else
		{
			gldepthmin = 1;
			gldepthmax = 0.5;
			glDepthFunc (GL_GEQUAL);
		}
	}
	else
	{
		if (clear)
			glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		else
			glClear (GL_DEPTH_BUFFER_BIT);
		gldepthmin = 0;
		gldepthmax = 1;
		glDepthFunc (GL_LEQUAL);
	}

	glDepthRange (gldepthmin, gldepthmax);

	// Stencil shadows - MrG
	if (r_shadows.value) 
	{
		glClearStencil(1);
		glClear(GL_STENCIL_BUFFER_BIT);		
	}
}

/*
================
R_RenderView

r_refdef must be set before the first call
================
*/
void R_RenderView (void)
{
	double	time1 = 0, time2;
	extern int	sceneblur_texture;
	extern void R_BloomBlend (int bloom);
	static int r;

	if (!r_worldentity.model || !cl.worldmodel)
		Sys_Error ("R_RenderView: NULL worldmodel");

	if (r_speeds.value)
	{
		glFinish ();
		time1 = Sys_DoubleTime ();
		c_brush_polys = c_alias_polys = c_md3_polys = 0;
	}

	if (gl_finish.value)
		glFinish ();

	R_Clear();

	// render normal view
	R_RenderScene();

	R_RenderDlights();	
	
	R_DrawParticles();

	R_DrawViewModel();

	if (r_drawlocs.value)
		R_DrawLocs();

	if (r_speeds.value)
	{
		time2 = Sys_DoubleTime ();
		Con_Printf ("%3i ms  %4i wpoly %4i epoly %4i md3poly\n", (int)((time2 - time1) * 1000), c_brush_polys, c_alias_polys, c_md3_polys);
	}

	if ((vid_bpp.value > 16) && (((cl.stats[STAT_HEALTH]< 1) && (gl_deathblur.value)) || ((gl_hurtblur.value )&&(cl_hurtblur > cl.time))||(gl_beergoggles.value)||(gl_nightmare.value)))
	{
		int vwidth = 1, vheight = 1;
		float vs, vt;

		while (vwidth < glwidth)
		{
			vwidth *= 2;
		}
		while (vheight < glheight)
		{
			vheight *= 2;
		}

		glViewport (glx, gly, glwidth, glheight);

		GL_Bind(sceneblur_texture);

		// go 2d
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity ();
		glOrtho  (0, glwidth, 0, glheight, -99999, 99999);
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity ();

		vs = ((float)glwidth / vwidth);
		vt = ((float)glheight / vheight) ;

		glDisable (GL_DEPTH_TEST);
		glDisable (GL_CULL_FACE);
		glDisable (GL_ALPHA_TEST);
		glEnable(GL_BLEND);

		glColor4f(1, 1, 1, gl_blur_alpha.value);//<-- alpha of blur      

		glBegin(GL_QUADS);
			glTexCoord2f(0, 0);
			glVertex2f(0, 0);
			glTexCoord2f(vs , 0);
			glVertex2f(glwidth, 0);
			glTexCoord2f(vs , vt);
			glVertex2f(glwidth, glheight);
			glTexCoord2f(0, vt);
			glVertex2f(0, glheight);
		glEnd();

		glMatrixMode(GL_PROJECTION);
		glPopMatrix();		
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();		

		if (gl_nightmare.value)
			glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE , glx, gly, vwidth, vheight, 0);//greycolors
		else
			glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB , glx, gly, vwidth, vheight, 0);//colors

		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	  	
	}

	R_BloomBlend(1);//BLOOMS

	if (scr_bloodsplat.value)
	{
		if (cl.faceanimtime > cl.time)	
		{
			if (r == 0)
			{
				r = rand()&3;
			}
			else 
			{
				if (r > 2)
				{
					GL_FullscreenQuad(decal_blood1, (0.1 + (cl.faceanimtime - cl.time)));
				}
				else
				{
					if (r > 1)
					{
						GL_FullscreenQuad(decal_blood2, (0.1 + (cl.faceanimtime - cl.time)));
					}
					else
					{
						if (r >= 0)
						{
							GL_FullscreenQuad(decal_blood3, (0.1 + (cl.faceanimtime - cl.time)));
						}
					}
				}
			}
		}
		else
			r = 0;
	}
}
