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

*/
// r_main.c

#include "quakedef.h"

entity_t	r_worldentity;

qboolean	r_cache_thrash;		// compatability

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

mplane_t	frustum[4];

int			c_brush_polys, c_alias_polys;

qboolean	envmap;				// true during envmap command capture

int			currenttexture = -1;		// to avoid unnecessary texture sets

int			cnttextures[2] = {
	-1, -1};     // cached

int			particletexture;	// little dot for particles

int         smoketexture; //qbism//for Topaz particles

int			playertextures;		// up to 16 color translated skins

int			mirrortexturenum;	// quake texturenum, not gltexturenum

qboolean	mirror;
mplane_t	*mirror_plane;

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

float	r_world_matrix[16];
float	r_base_world_matrix[16];

//
// screen size info
//
refdef_t	r_refdef;

mleaf_t		*r_viewleaf, *r_oldviewleaf;

texture_t	*r_notexture_mip;

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


void R_MarkLeaves (void);

cvar_t	r_norefresh = {
	"r_norefresh","0"};
cvar_t	r_drawentities = {
	"r_drawentities","1"};
cvar_t	r_drawviewmodel = {
	"r_drawviewmodel","1"};
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"}; //qbism//jf 2000-08-19 shadows off by default.

cvar_t	r_mirroralpha = {
	"r_mirroralpha","0.3"}; //qbism//00-07-05 Paddy tutorial

cvar_t	r_wateralpha = {
	"r_wateralpha","1"};
cvar_t	r_smoketrailwidth = {
	"r_smoketrailwidth","0.5"}; //qbism//jf 00-07-05 adjustasble by request

cvar_t	r_skybox = { //qbism//00-07-05 Tane Piper tutorial

	"r_skybox","0"};
cvar_t	r_dynamic = {
	"r_dynamic","1"};
cvar_t	r_novis = {
	"r_novis","0"};
cvar_t	r_waterwarp = {
	"r_waterwarp", "1"};	//qbism// 2000-01-02 Missing R_WATERWARP fix by Radix

//qbism//2000-02-27 r_waterripple from QuakeForge.
cvar_t  r_waterripple = {
	"r_waterripple", "3"};

cvar_t	gl_finish = {
	"gl_finish","0"};
cvar_t	gl_clear = {
	"gl_clear","0"};
cvar_t	gl_cull = {
	"gl_cull","1"};
cvar_t	gl_texsort = {
	"gl_texsort","1"};
cvar_t	gl_smoothmodels = {
	"gl_smoothmodels","1"};
cvar_t	gl_affinemodels = {
	"gl_affinemodels","0"};
cvar_t	gl_polyblend = {
	"gl_polyblend","1"};
cvar_t	gl_flashblend = {
	"gl_flashblend","1"};
cvar_t	gl_playermip = {
	"gl_playermip","0"};
cvar_t	gl_nocolors = {
	"gl_nocolors","1"}; //qbism//jf 00-10-15 changed for md2 player model. was 0

cvar_t	gl_keeptjunctions = {
	"gl_keeptjunctions","0"};
cvar_t	gl_reporttjunctions = {
	"gl_reporttjunctions","0"};
cvar_t	gl_doubleeyes = {
	"gl_doubleeys", "1"};

extern	cvar_t	gl_ztrick;

//qbism//00-07-02 NATAS - BramBo - Fog Code
	// Modified by Electro
	cvar_t gl_fogenable = {
	"gl_fogenable", "0"}; //qbism//jf 00-06-30 off by default

cvar_t gl_fogstart = {
	"gl_fogstart", "50.0"};
cvar_t gl_fogend = {
	"gl_fogend", "800.0"};
cvar_t gl_fogdensity = {
	"gl_fogdensity", "0.8"};
cvar_t gl_fogred = {
	"gl_fogred","0.6"};
cvar_t gl_foggreen = {
	"gl_foggreen","0.5"};
cvar_t gl_fogblue = {
	"gl_fogblue","0.4"};
cvar_t gl_fogalpha = {
	"gl_fogalpha", "0.5"};
// END
	cvar_t gl_fisheye = {
	"gl_fisheye", "0"};//qbism//jf GFE 00-07-12 off by default

cvar_t gl_fep1 = {
	"gl_fep1", "0.43"};
cvar_t gl_fep2 = {
	"gl_fep2", "0.31"};
cvar_t gl_fep3 = {
	"gl_fep3", "0.186"};


//qbism// 2000-02-03 fenix@io.com: model interpolation
cvar_t  r_interpolate_model_animation = {
	"r_interpolate_model_animation", "1", true };
cvar_t  r_interpolate_model_transform = {
	"r_interpolate_model_transform", "1", true };

//qbism// 2000-04-10 Torch flares. KH
cvar_t	gl_torchflares = {
	"gl_torchflares", "1", true};

sfx_t  *sfx_exhaust;

/*
=================
R_CullBox

Returns true if the box is completely outside the frustom
=================
*/
qboolean R_CullBox (vec3_t mins, vec3_t maxs)
{
	int		i;

	for (i=0 ; i<4 ; i++)
		if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2)
			return true;
	return false;
}


void R_RotateForEntity (entity_t *e)
{
	glTranslatef (e->origin[0],  e->origin[1],  e->origin[2]);

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






/*
=============
R_BlendedRotateForEntity

fenix@io.com: model transform interpolation
=============
*/
//qbism//00-04-29 muff - definition changed to allow for new shadow code
		void R_BlendedRotateForEntity (entity_t *e, int shadow)
{
	float timepassed;
	float blend;
	vec3_t d, dfudge;
	int i;
	trace_t        movetrace; //qbism//jf CSE 00-05-28 positional Client-Side Extrapolation


	// positional interpolation

	timepassed = realtime - e->translate_start_time;

	if (e->translate_start_time == 0 || timepassed > 1)
	{
		e->translate_start_time = realtime;
		VectorCopy (e->origin, e->origin1);
		VectorCopy (e->origin, e->origin2);
	}

	if (!VectorCompare (e->origin, e->origin2))
	{
		e->translate_start_time = realtime;
		VectorCopy (e->origin2, e->origin1);
		VectorCopy (e->origin,  e->origin2);
		blend = 0;
	}
	else
		{
	//jft	if(cl.scores[cl.viewentity].ping) //qbism 00-11-19 CSE
	//	{
			blend = (realtime - e->translate_start_time)/(cl.mtime[0]-cl.mtime[1]);
	//			((float)cl.scores[cl.viewentity].ping*.001);
			//if(blend>0.1) blend = 0.1;
	//	}
	//	else blend =0.1;

		if (cl.paused || blend > 1) blend = 1;
	}

	VectorSubtract (e->origin2, e->origin1, d);

	memset (&movetrace, 0, sizeof(movetrace));//qbism//jf CSE 00-05-28

	//qbism//jf CSE 00-05-28 check if the movement vector collides with world.  if so, bail.
	if (blend>1)
	{
		SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, e->origin, d, &movetrace);
		if(movetrace.fraction <1) blend =1;
	}
	glTranslatef (
		e->origin1[0] + (d[0]*blend),
		e->origin1[1] + (d[1]*blend),
		e->origin1[2] + (d[2]*blend));

	// orientation interpolation (Euler angles, yuck!)

	//qbism//jf 00-10-15 not used timepassed = realtime - e->rotate_start_time;

	if (e->rotate_start_time == 0) //qbism//jf CSE 00-05-28 removed || timepassed > 1)
			{
		e->rotate_start_time = realtime;
		VectorCopy (e->angles, e->angles1);
		VectorCopy (e->angles, e->angles2);
	}

	if (!VectorCompare (e->angles, e->angles2))
	{
		e->rotate_start_time = realtime;
		VectorCopy (e->angles2, e->angles1);
		VectorCopy (e->angles,  e->angles2);
		blend = 0;
	}
	else
		{
	//jft	if(cl.scores[cl.viewentity].ping) //qbism 00-11-19 CSE
	//	{
			blend = (realtime - e->translate_start_time)/(cl.mtime[0]-cl.mtime[1]);
	//			((float)cl.scores[cl.viewentity].ping*.001);
			//if(blend>0.1) blend = 0.1;
	//	}
	//	else blend =0.1;

		if (cl.paused || blend > 1) blend = 1;
	}


	// 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 ( e->angles1[1] + ( blend * d[1]),  0, 0, 1);
	glRotatef (-e->angles1[0] + (-blend * d[0]),  0, 1, 0);
	glRotatef ( e->angles1[2] + ( blend * d[2]),  1, 0, 0);
}




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

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;
	mspriteframe_t	*frame;
	float		*up, *right;
	vec3_t		v_forward, v_right, v_up;
	msprite_t		*psprite;

	// 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, v_forward, v_right, v_up);
		up = v_up;
		right = v_right;
	}
	else
		{	// normal sprite

		up = vup;
		right = vright;
	}

	glColor3f (1,1,1);

	GL_DisableMultitexture();

	glBindTexture(GL_TEXTURE_2D,frame->gl_texturenum);

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

	glDisable (GL_ALPHA_TEST);
}

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

ALIAS MODELS

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


#define NUMVERTEXNORMALS	162

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

vec3_t	shadevector;
//qbism//00-06-15 CSL - epca@powerup.com.au
float   shadelight[4];
// CSL
float   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];

//qbism// 2000-02-03 fenix@io.com: model animation interpolation
int lastposenum0;


int	lastposenum;

/*
=============
GL_DrawAliasFrame
=============
*/
void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum)
{
	float	s, t;
	float 	l;
	int		i, j;
	int		index;
	trivertx_t	*v, *verts;
	int		list;
	int		*order;
	vec3_t	point;
	float	*normal;
	int		count;

	lastposenum = posenum;

	verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
	verts += posenum * paliashdr->poseverts;
	order = (int *)((byte *)paliashdr + paliashdr->commands);

	while (1)
	{
		// get the vertex count and primitive type
		count = *order++;
		if (!count)
			break;		// done

		if (count < 0)
		{
			count = -count;
			glBegin (GL_TRIANGLE_FAN);
		}
		else
			glBegin (GL_TRIANGLE_STRIP);

		do
			{
			// texture coordinates come from the draw list
			glTexCoord2f (((float *)order)[0], ((float *)order)[1]);
			order += 2;



			// normals and vertexes come from the frame list
//qbism//00-07-05 CSL - epca@powerup.com.au
			if (shadelight[0] || shadelight[1] || shadelight[2])
			{
				if(bspextensions == BSPVERSION){
					l = shadelight[3] * shadedots[verts->lightnormalindex];

					glColor3f (l+shadelight[0], l+shadelight[1], l+shadelight[2]);

				}
				else
					{
					//qbism//jf 00-06-15 in colored light mode, divide l to get rid of washout.
					l = (shadelight[3] * shadedots[verts->lightnormalindex] )/32;
					glColor3f (l*shadelight[0], l*shadelight[1], l*shadelight[2]);
				}
			}
			else
				{
				l = shadedots[verts->lightnormalindex] * shadelight[3];
				glColor3f (l, l, l);
			}
			//qbism//00-06-15 CSL
			glVertex3f (verts->v[0], verts->v[1], verts->v[2]);
			verts++;
		}
		while (--count);

		glEnd ();
	}
}

/*
=============
GL_DrawAliasBlendedFrame
fenix@io.com: model animation interpolation
=============
*/
void GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend)
{
	float       l;
	trivertx_t* verts1;
	trivertx_t* verts2;
	int*        order;
	int         count;
	vec3_t      d;

	lastposenum0 = pose1;
	lastposenum  = pose2;

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

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

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

	for (;;)
	{
		// get the vertex count and primitive type
	count = *order++;

		if (!count) break;

		if (count < 0)
		{
			count = -count;
			glBegin (GL_TRIANGLE_FAN);
		}
		else
			{
			glBegin (GL_TRIANGLE_STRIP);
		}

		do
			{
			// texture coordinates come from the draw list
		glTexCoord2f (((float *)order)[0], ((float *)order)[1]);
			order += 2;

			// normals and vertexes come from the frame list
		// blend the light intensity from the two frames together

			d[0] = shadedots[verts2->lightnormalindex] -
				shadedots[verts1->lightnormalindex];

			//qbism//00-06-15 csl
				if (shadelight[0] || shadelight[1] || shadelight[2])
			{
				if(bspextensions == BSPVERSION){
					l = shadelight[3] * (shadedots[verts1->lightnormalindex]
							+ (blend * d[0]));
					glColor3f (l+shadelight[0], l+shadelight[1], l+shadelight[2]);

				}
				else
					{
					l = (shadelight[3] * (shadedots[verts1->lightnormalindex] + (blend * d[0])))/32;
					glColor3f (l*shadelight[0], l*shadelight[1], l*shadelight[2]);
				}
			}

			else
				{
				l = shadelight[3] * (shadedots[verts1->lightnormalindex] + (blend * d[0]));
				glColor3f (l, l, l);
			}

			VectorSubtract(verts2->v, verts1->v, d);

			// blend the vertex positions from each frame together
		glVertex3f (
				verts1->v[0] + (blend * d[0]),
				verts1->v[1] + (blend * d[1]),
				verts1->v[2] + (blend * d[2]));

			verts1++;
			verts2++;
		}
		while (--count);
		glEnd ();
	}
}

/*
=============
GL_DrawAliasShadow
=============
*/
extern	vec3_t			lightspot;

void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum)
{
	float    s, t, l;
	int        i, j;
	int        index;
	trivertx_t    *v, *verts;
	int        list;
	int        *order;
	vec3_t    point;
	float    *normal;

	int        count;

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
	trace_t        steptrace, downtrace;
	vec3_t        dest,stop,downmove;
	float        s1,c1; //this will store the sin and cos values as they are used so heavily

	//end of muff




	verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
	verts += posenum * paliashdr->poseverts;
	order = (int *)((byte *)paliashdr + paliashdr->commands);

	//qbism//jf 00-10-15 not used height = -lheight + 1.0;

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
	// better shadowing, now takes angle of ground into account
	// cast a traceline into the floor directly below the player
	// and gets normals from this
	VectorCopy (currententity->origin, downmove);
	downmove[2] = downmove[2] - 4096;
	memset (&downtrace, 0, sizeof(downtrace));
	SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, currententity->origin, downmove, &downtrace);

	// calculate the all important angles to keep speed up
	s1 = sin( currententity->angles[1]/180*M_PI);
	c1 = cos( currententity->angles[1]/180*M_PI);

	// end of muff

	while (1)
	{
		// get the vertex count and primitive type
		count = *order++;
		if (!count)
			break;        // done

		if (count < 0)
		{
			count = -count;
			glBegin (GL_TRIANGLE_FAN);
		}
		else
			glBegin (GL_TRIANGLE_STRIP);

		do
			{
			// texture coordinates come from the draw list
			// (skipped for shadows) glTexCoord2fv ((float *)order);
			order += 2;

			// normals and vertexes come from the frame list
			point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0];
			point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1];
			point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2];

			//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
			// first remove the existing height adjustment, as we are going to do this ourselves
			//original code
			// point[0] -= shadevector[0]*(point[0] +lheight);
			// point[1] -= shadevector[1]*(point[1] +lheight);
			//    point[2] = height;
			//    height -= 0.001;
			// end of original code

			point[0] -= shadevector[0]*(point[0]);
			point[1] -= shadevector[1]*(point[1]);
			point[2] -= shadevector[2]*(point[2]);

			//drop it down to floor
			point[2] = point[2] - (currententity->origin[2] - downtrace.endpos[2]) ;

			// now adjust the point with respect to all the normals of the tracepoint
			// I'm probably missing an easy piece of maths to do this
			// it reads messy, but works ;)
			point[2] +=     (        (point[1] * (s1 * downtrace.plane.normal[0])) -
					(point[0] * (c1 * downtrace.plane.normal[0])) -
					(point[0] * (s1 * downtrace.plane.normal[1])) -
					(point[1] * (c1 * downtrace.plane.normal[1]))
					) +  ((1.0 - downtrace.plane.normal[2])*20)
				+ 0.2 ;

			//end of muff

			glVertex3fv (point);

			verts++;
		}
		while (--count);

		glEnd ();
	}
}


void GL_DrawAliasBlendedShadow (aliashdr_t *paliashdr, int pose1, int pose2, entity_t* e)
{
	trivertx_t* verts1;
	trivertx_t* verts2;
	int*        order;
	vec3_t      point1;
	vec3_t      point2;
	vec3_t      d;
	float       height;
	float       lheight;
	int         count;
	float       blend;

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
	trace_t        steptrace, downtrace;
	vec3_t        dest,stop,downmove;
	float        s1,c1; //this will store the sin and cos values as they are used so heavily

	//end of muff

	//qbism//jf CSE 00-10-15 stretch out animation to match lag.  Smoothness.
	blend = (realtime - e->frame_start_time) / e->frame_interval;


	//qbism//jf CSE 00-06-07 shadow Client-Side Extrapolation- match animation extrapolation
	// wierd things start happening if blend passes 1

	if (cl.paused || blend >1) blend = 1; //qbism//jf 10-15-00 this is much less likely to happen now.

	lheight = e->origin[2] - lightspot[2];
	//qbism//jf 00-10-15 not used height  = -lheight + 1.0;

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
	// better shadowing, now takes angle of ground into account
	// cast a traceline into the floor directly below the player
	// and gets normals from this
	// for proper surface shadowing, each point should be taken
	// seperately, we do it this way for speed
	// NOTE need fixing for when at liquid surface
	VectorCopy (currententity->origin, downmove);
	downmove[2] = downmove[2] - 4096;
	memset (&downtrace, 0, sizeof(downtrace));
	SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, currententity->origin, downmove, &downtrace);

	// calculate the all important angles
	s1 = sin( currententity->angles[1]/180*M_PI);
	c1 = cos( currententity->angles[1]/180*M_PI);

	// end of muff

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

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

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

	for (;;)
	{
		// get the vertex count and primitive type
			count = *order++;

		if (!count) break;

		if (count < 0)
		{
			count = -count;
			glBegin (GL_TRIANGLE_FAN);
		}
		else
			{
			glBegin (GL_TRIANGLE_STRIP);
		}

		do
			{
			order += 2;

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

			VectorSubtract(point2, point1, d);


			//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
			point1[0] = point1[0] + (blend * d[0]);  //+lheight); //muff

			point1[1] = point1[1] + (blend * d[1]);  //+lheight); //muff

			point1[2] = point1[2] + (blend * d[2]);  //+lheight); //muff

			//drop it down to floor
			point1[2] =  - (currententity->origin[2] - downtrace.endpos[2]) ;

			//now move the z-coordinate as appropriate
			point1[2] += (    (point1[1] * (s1 * downtrace.plane.normal[0])) -
					(point1[0] * (c1 * downtrace.plane.normal[0])) -
					(point1[0] * (s1 * downtrace.plane.normal[1])) -
					(point1[1] * (c1 * downtrace.plane.normal[1]))
					) +  ((1.0 - downtrace.plane.normal[2])*20)
				+ 0.2 ;


			// now load the point we have just calculated
			glVertex3fv (point1);

			// remove this as we have new commands in above
			//  glVertex3f (point1[0] + (blend * d[0]),
						//  point1[1] + (blend * d[1]),
						//  height);


			//end of muff

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

		glEnd ();
	}
}



/*
=================
R_SetupAliasFrame

=================
*/
void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr)
{
	int				pose, numposes;
	float			interval;

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

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

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

	GL_DrawAliasFrame (paliashdr, pose);
}


/*
=================
R_SetupAliasBlendedFrame
fenix@io.com: model animation interpolation
=================
*/
void R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t* e)
{
	int   pose;
	int   numposes;
	float blend;

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

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

	if (numposes > 1)
	{
		if(cl.scores[cl.viewentity].ping < paliashdr->frames[frame].interval)
			e->frame_interval = paliashdr->frames[frame].interval;
		else e->frame_interval = cl.scores[cl.viewentity].ping;
		pose += (int)(cl.time / e->frame_interval) % numposes;
	}
	else
		{
		//qbism//jf 00-10-15 we can do better than this:
		/* One tenth of a second is a good for most Quake animations.
		If the nextthink is longer then the animation is usually meant to pause
		(e.g. check out the shambler magic animation in shambler.qc).  If its
		shorter then things will still be smoothed partly, and the jumps will be
		less noticable because of the shorter time.  So, this is probably a good
		assumption. */

		//qbism//jf 10-15-00 interval based on ping, if known.
		if(cl.scores[cl.viewentity].ping)
			e->frame_interval = ((float)cl.scores[cl.viewentity].ping*.001);
		else e->frame_interval = 0.1;
	}

	if (e->pose2 != pose)
	{
		e->frame_start_time = realtime;
		e->pose1 = e->pose2;
		e->pose2 = pose;
		blend = 0;
	}
	else
		{
		blend = (realtime - e->frame_start_time) / e->frame_interval;
	}

	// wierd things start happening if blend passes 1
	if (cl.paused || blend>1) blend = 1; //qbism//jf CSE 00-10-15 this is much less likely to happen now.

	GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend);

}

//qbism//00-10-15 MD2

/*
=============
GL_DrawQ2AliasFrame
=============
*/
void GL_DrawQ2AliasFrame (entity_t *e, md2_t *pheader, int lastpose, int pose, float lerp)
{
	float   ilerp;
	int             *order, count;
	md2trivertx_t   *verts1, *verts2;
	vec3_t  scale1, translate1, scale2, translate2;
	md2frame_t *frame1, *frame2;

	//qbism//jf 00-10-15 MD2SHADING needed for shading
	vec3_t      d;
	float l;
	int i;
	int	lnum;
	vec3_t	dist;
	int *j;
	float		add;


	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glShadeModel(GL_SMOOTH);


	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	//              glBlendFunc(GL_ONE, GL_ZERO);
					//              glDisable(GL_BLEND);
									glDepthMask(1);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);


	ilerp = 1.0 - lerp;

	//new version by muff - fixes bug, easier to read, faster (well slightly)
							frame1 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * lastpose));
	frame2 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * pose));

	VectorCopy(frame1->scale, scale1);
	VectorCopy(frame1->translate, translate1);
	VectorCopy(frame2->scale, scale2);
	VectorCopy(frame2->translate, translate2);
	verts1 = &frame1->verts[0];
	verts2 = &frame2->verts[0];
	order = (int *)((int)pheader + pheader->ofs_glcmds);


	while (1)
	{
		// get the vertex count and primitive type
									count = *order++;
		if (!count)
			break;          // done

		if (count < 0)
		{
			count = -count;
			glBegin (GL_TRIANGLE_FAN);
		}
		else
			glBegin (GL_TRIANGLE_STRIP);

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


			//qbism//jf 00-10-15 MD2SHADING

			d[0] = shadedots[verts2->lightnormalindex] -
				shadedots[verts1->lightnormalindex];

			//qbism//00-06-15 csl
				if (shadelight[0] || shadelight[1] || shadelight[2])
			{
				if(bspextensions == BSPVERSION){
					l = shadelight[3] * (shadedots[verts1->lightnormalindex]
							+ (lerp * d[0]));
					glColor3f (l+shadelight[0], l+shadelight[1], l+shadelight[2]);

				}
				else
					{
					l = (shadelight[3] * (shadedots[verts1->lightnormalindex] + (lerp * d[0])))/32;
					glColor3f (l*shadelight[0], l*shadelight[1], l*shadelight[2]);
				}
			}

			else
				{
				l = shadelight[3] * (shadedots[verts1->lightnormalindex] + (lerp * d[0]));
				glColor3f (l, l, l);
			}

			//qbism//jf 00-10-15 MD2SHADING



			glVertex3f((verts1[order[2]].v[0]*scale1[0]+translate1[0])*ilerp+(verts2[order[2]].v[0]*scale2[0]+translate2[0])*lerp,
				(verts1[order[2]].v[1]*scale1[1]+translate1[1])*ilerp+(verts2[order[2]].v[1]*scale2[1]+translate2[1])*lerp,
				(verts1[order[2]].v[2]*scale1[2]+translate1[2])*ilerp+(verts2[order[2]].v[2]*scale2[2]+translate2[2])*lerp);

			order+=3;
		}
		while (--count);

		glEnd ();
	}


	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // normal alpha blend

	glDisable(GL_BLEND);
	glDepthMask(1);
	//      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
					}


/*
=============
GL_DrawQ2AliasShadow
=============
*/
void GL_DrawQ2AliasShadow (entity_t *e, md2_t *pheader, int lastpose, int pose, float lerp)
{
	float   ilerp;
	int             *order, count;
	md2trivertx_t   *verts1, *verts2;
	vec3_t  scale1, translate1, scale2, translate2, point;
	md2frame_t *frame1, *frame2;

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
	trace_t        steptrace, downtrace;
	vec3_t        dest,stop,downmove;
	float        s1,c1; //this will store the sin and cos values as they are used so heavily

	//end of muff





	ilerp = 1.0 - lerp;

	//new version by muff - fixes bug, easier to read, faster (well slightly)
							frame1 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * lastpose));
	frame2 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * pose));

	VectorCopy(frame1->scale, scale1);
	VectorCopy(frame1->translate, translate1);
	VectorCopy(frame2->scale, scale2);
	VectorCopy(frame2->translate, translate2);
	verts1 = &frame1->verts[0];
	verts2 = &frame2->verts[0];
	order = (int *)((int) pheader + pheader->ofs_glcmds);

	//qbism//jf 00-10-15 not used height = lheight + 1.0;

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
	// better shadowing, now takes angle of ground into account
	// cast a traceline into the floor directly below the player
	// and gets normals from this
	VectorCopy (currententity->origin, downmove);
	downmove[2] = downmove[2] - 4096;
	memset (&downtrace, 0, sizeof(downtrace));
	SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, currententity->origin, downmove, &downtrace);

	// calculate the all important angles to keep speed up
	s1 = sin( currententity->angles[1]/180*M_PI);
	c1 = cos( currententity->angles[1]/180*M_PI);

	// end of muff

	while (1)
	{
		// get the vertex count and primitive type
									count = *order++;
		if (!count)
			break;          // done

		if (count < 0)
		{
			count = -count;
			glBegin (GL_TRIANGLE_FAN);
		}
		else
			glBegin (GL_TRIANGLE_STRIP);

		do
			{
			point[0] = (verts1[order[2]].v[0]*scale1[0]+translate1[0])*ilerp+(verts2[order[2]].v[0]*scale2[0]+translate2[0])*lerp;
			point[1] = (verts1[order[2]].v[1]*scale1[1]+translate1[1])*ilerp+(verts2[order[2]].v[1]*scale2[1]+translate2[1])*lerp;
			point[2] = (verts1[order[2]].v[2]*scale1[2]+translate1[2])*ilerp+(verts2[order[2]].v[2]*scale2[2]+translate2[2])*lerp;

			//qbism//00-04-29 muff@yakko.globalnet.co.uk - 17 Mar 2000
			// first remove the existing height adjustment, as we are going to do this ourselves
			//original code
			// point[0] -= shadevector[0]*(point[0] +lheight);
			// point[1] -= shadevector[1]*(point[1] +lheight);
			//    point[2] = height;
			//    height -= 0.001;
			// end of original code

			point[0] -= shadevector[0]*(point[0]);
			point[1] -= shadevector[1]*(point[1]);
			point[2] -= shadevector[2]*(point[2]);

			//drop it down to floor
			point[2] = point[2] - (currententity->origin[2] - downtrace.endpos[2]) ;

			// now adjust the point with respect to all the normals of the tracepoint
			// I'm probably missing an easy piece of maths to do this
			// it reads messy, but works ;)
			point[2] +=     (        (point[1] * (s1 * downtrace.plane.normal[0])) -
					(point[0] * (c1 * downtrace.plane.normal[0])) -
					(point[0] * (s1 * downtrace.plane.normal[1])) -
					(point[1] * (c1 * downtrace.plane.normal[1]))
					) +  ((1.0 - downtrace.plane.normal[2])*20)
				+ 0.2 ;

			//end of muff

			glVertex3fv (point);
			order+=3;
		}
		while (--count);

		glEnd ();
	}

}



/*
=================
R_SetupQ2AliasFrame

=================
*/
void R_SetupQ2AliasFrame (entity_t *e, md2_t *pheader)
{
	int                             frame;
	float                   lerp, lerpscale;
	vec3_t		mins; //qbism//jf 00-10-15 for muff shadows (shadows follow slope)

	model_t		*clmodel; //qbism//jf 00-10-15 for muff shadows

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 05 Mar 00 - added
	trace_t        steptrace, downtrace;
	vec3_t        dest,stop,downmove;
	//end of muff

	frame = e->frame;

	glPushMatrix ();
	R_BlendedRotateForEntity (e,1); //qbism//jf 00-10-15 MD2 was r_rota...

	if ((frame >= pheader->num_frames) || (frame < 0))
	{
		Con_DPrintf ("R_SetupQ2AliasFrame: no such frame %d\n", frame);
		frame = 0;
	}

	if (e->draw_lastmodel == e->model)
	{
		if (frame != e->draw_pose)
		{
			e->draw_lastpose = e->draw_pose;
			e->draw_pose = frame;
			e->draw_lerpstart = cl.time;
			lerp = 0;
		}
		else
			//qbism//jf 00-10-15 FIXME: how does calc of "lerp"(md2) compare to "blend"(mdl) ?
			lerp = (cl.time - e->draw_lerpstart) * 20.0;
	}
	else // uninitialized
							{
		e->draw_lastmodel = e->model;
		e->draw_lastpose = e->draw_pose = frame;
		e->draw_lerpstart = cl.time;
		lerp = 0;
	}


	if (lerp > 1) lerp = 1;

	GL_DrawQ2AliasFrame (e, pheader, e->draw_lastpose, frame, lerp);


	if (r_shadows.value)
	{

		shadevector[0] = 0;
		shadevector[1] = 0;
		shadevector[2] = 1;
		VectorNormalize (shadevector);

		clmodel = e->model;
		VectorAdd (e->origin, clmodel->mins, mins);


		//qbism//2000-04-29 muff@yakko.globalnet.co.uk - 14 Mar 2000
		// this is so we can calculate height of the ground accurately
		VectorCopy (e->origin, downmove);

		downmove[2] = downmove[2] - 4096;
		memset (&downtrace, 0, sizeof(downtrace));
		SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, e->origin, downmove, &downtrace);
		// end of muff


		glDisable (GL_TEXTURE_2D);
		glEnable (GL_BLEND);
		glDepthMask(FALSE); // disable zbuffer updates

		//qbism//2000-04-29 muff@yakko.globalnet.co.uk - 14 Mar 2000
		// set the shade of the shadow based on height off ground
		glColor4f (0,0,0,1.0 - ((mins[2]-downtrace.endpos[2])/60)); //qbism//jf 00-10-15 was always black, added /60. mins-downtrace ~=0?

		//glColor4f (0,0,0,0.5);// * modelalpha);

		GL_DrawQ2AliasShadow (e, pheader, e->draw_lastpose, frame, lerp);
		glDepthMask(TRUE); // enable zbuffer updates

		glEnable (GL_TEXTURE_2D);
		//              glDisable (GL_BLEND);
									glColor3f (1,1,1);
	}
	glPopMatrix ();
}

/*
=================
R_DrawAliasModel

=================
*/
void R_DrawAliasModel (entity_t *e)
{
	int         i,k;
	//qbism// 00-06-15  CSL - epca@powerup.com.au
	int         *j;
	// CSL
	int			lnum;
	vec3_t		dist;
	float		add;
	model_t		*clmodel;
	vec3_t		mins, maxs;
	aliashdr_t	*paliashdr;
	trivertx_t	*verts, *v;
	int			index;
	float		s, t, an;
	int			anim;
	//qbism//00-10-15 MD2
		md2_t           *pheader; // LH / muff

	qboolean	torch = false; //qbism//2000-04-10 KH Flags is this model is a torch

	//qbism//00-04-29 muff@yakko.globalnet.co.uk - 05 Mar 00 - added
	trace_t        steptrace, downtrace;
	vec3_t        dest,stop,downmove;
	//end of muff

	sfx_exhaust = S_PrecacheSound ("exhaust.wav"); //qbism//jf 00-11-08 missile exhaust

	clmodel = currententity->model;

	VectorAdd (currententity->origin, clmodel->mins, mins);
	VectorAdd (currententity->origin, clmodel->maxs, maxs);

	//qbism//00-11-08 moving rocket sound
	if(!strcmp (clmodel->name, "progs/missile.mdl"))
	S_StartSound (-1, 0, sfx_exhaust, currententity->origin, 1, 1);



	//qbism 00-10-10//jf FIXME- a visible model is sometimes culled, esp. w/low fov.
	if (R_CullBox (mins, maxs))
		return;


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

	//
	// get lighting information
	//

	//qbism//00-06-15 CSL - epca@powerup.com.au
	j = R_LightPoint (currententity->origin);
	// Can't assign int* to float[4]...damn
	shadelight[0] = (float) j[0];
	shadelight[1] = (float) j[1];
	shadelight[2] = (float) j[2];
	shadelight[3] = (float) j[3];
	ambientlight = shadelight[3];
	// CSL

	// allways give the gun some light
	if (e == &cl.viewent && ambientlight < 24)
	{
		//qbism//00-06-15 CSL - epca@powerup.com.au
		ambientlight = shadelight[3] = 24;
		// CSL
																								}

	for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
	{
		if (cl_dlights[lnum].die >= cl.time)
		{
			VectorSubtract (currententity->origin,
				cl_dlights[lnum].origin,
				dist);
			add = cl_dlights[lnum].radius - Length(dist);

			if (add > 0) {
				ambientlight += add;
				//ZOID models should be affected by dlights as well
				//qbism//00-06-15 CSL - epca@powerup.com.au
				shadelight[0] += cl_dlights[lnum].colour[0];
				shadelight[1] += cl_dlights[lnum].colour[1];
				shadelight[2] += cl_dlights[lnum].colour[2];
				shadelight[3] += add;
				// CSL
																																																																								}
		}
	}

	if (ambientlight > 128)
		ambientlight = 128;
	//qbism//00-06-15 CSL - epca@powerup.com.au
	if (ambientlight + shadelight[3] > 255) //qbism//jf 00-10-15 was 192
		shadelight[3] = 192 - ambientlight;
	// CSL

	// ZOID: never allow players to go totally black
	i = currententity - cl_entities;
	if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
		if (ambientlight < 8)
			//qbism//00-06-15 CSL - epca@powerup.com.au
			ambientlight = shadelight[3] = 8;
	// CSL


	// HACK HACK HACK -- no fullbright colors, so make torches full light
	if (!strcmp (clmodel->name, "progs/flame2.mdl")||
			!strcmp (clmodel->name, "progs/flame.mdl"))
	{
		//qbism//00-06-15 CSL - epca@powerup.com.au
		ambientlight = shadelight[3] = 255; //qbism//00-10-15 LordHavoc: was 256, changed to 255

		// CSL
		torch = true; //qbism//2000-04-10 This model is a torch. KH

	}
	if (   !strcmp (clmodel->name, "progs/laser.mdl")	//qbism//jf 2000-02-21 more bright things
		|| !strcmp (clmodel->name, "progs/bolt.mdl") )	//qbism//jf 2000-02-21
		//qbism//00-06-15 CSL - epca@powerup.com.au
		ambientlight = shadelight[3] = 256;
	// CSL
	shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
	//qbism//00-06-15 CSL - epca@powerup.com.au
	shadelight[3] = shadelight[3] / 200.0;
	// CSL
	an = e->angles[1]/180*M_PI;
	shadevector[0] = cos(-an);
	shadevector[1] = sin(-an);
	shadevector[2] = 1;
	VectorNormalize (shadevector);

	//qbism//00-10-15 MD2
  //
							// locate the proper data
							//
	if (clmodel->aliastype == ALIASTYPE_MD2)
	{
		pheader = (md2_t *)Mod_Extradata (currententity->model);
		c_alias_polys += pheader->num_tris;
	}
	else
		{
		paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model);
		c_alias_polys += paliashdr->numtris;
	}
	//qbism//00-10-15 end MD2

	//
	// draw all the triangles
	//

	GL_DisableMultitexture();

	//qbism//00-10-15 MD2
	if (clmodel->aliastype != ALIASTYPE_MD2)
	{
		//qbism//00-10-15 end MD2
	glPushMatrix ();

		//qbism// 2000-02-03 fenix@io.com: model transform interpolation
	if (r_interpolate_model_transform.value)
		{
			R_BlendedRotateForEntity (e, 0);//qbism//jf "0" instead of "1" 00-05-06 muff tutorial

		}
		else
			{
			R_RotateForEntity (e);
		}

		if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) {
			glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8));
			// double size of eyes, since they are really hard to see in gl
		glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2);
		}
		else {
			glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
			glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
		}
	}


	if (clmodel->aliastype == ALIASTYPE_MD2)
		glBindTexture(GL_TEXTURE_2D,pheader->gl_texturenum[currententity->skinnum]);
	else
		{
		anim = (int)(cl.time*10) & 3;
		glBindTexture(GL_TEXTURE_2D,paliashdr->gl_texturenum[currententity->skinnum][anim]);
		//qbism//00-10-15 MD2

	}
	//qbism//00-10-15 end MD2

	// we can't dynamically colormap textures, so they are cached
	// seperately for the players.  Heads are just uncolored.
	if (currententity->colormap != vid.colormap && !gl_nocolors.value)
	{
		i = currententity - cl_entities;
		if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
			glBindTexture(GL_TEXTURE_2D,playertextures - 1 + i);
	}

	if (gl_smoothmodels.value)
		glShadeModel (GL_SMOOTH);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

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

	// fenix@io.com: model animation interpolation
		if (r_interpolate_model_animation.value && !torch)
	{
		if (clmodel->aliastype == ALIASTYPE_MD2)
			R_SetupQ2AliasFrame (currententity, pheader);
		else
			R_SetupAliasBlendedFrame (currententity->frame, paliashdr, currententity);
	}
	else
		{
		if (clmodel->aliastype == ALIASTYPE_MD2)
			R_SetupQ2AliasFrame (currententity, pheader);
		else
			R_SetupAliasFrame (currententity->frame, paliashdr);
		//qbism//00-10-15 MD2
			}
	//qbism//00-10-15 end MD2

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

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

	//qbism//00-10-15 MD2
		if (clmodel->aliastype != ALIASTYPE_MD2)
	{
		//qbism//00-10-15 MD2 end
							glPopMatrix ();

		//qbism//2000-04-10 glow effects from muff's tutorial
		// Based on code originally from UNOFFICAL GLQUAKE code
		//qbism//2000-04-29 muff@yakko.globalnet.co.uk
		// added lightning glow code  -  20 Mar 2000
		// added rocket tailglow code -  20 Mar 2000
		// other can be added as later (such as 'laser.mdl')
		if ((!strncmp (clmodel->name, "progs/flame",11))      ||
				(!strncmp (clmodel->name, "progs/bolt",10))       ||
				(!strcmp  (clmodel->name, "progs/missile.mdl"))   ||
				(!strcmp  (clmodel->name, "progs/quaddama.mdl"))  ||
				(!strcmp  (clmodel->name, "progs/invulner.mdl"))
				)
		{

			const int TORCH_STYLE = 1;      // Flicker.

			vec3_t  lightorigin;    // Origin of glow.

			vec3_t  v;                      // Vector to torch.

			float           radius;         // Radius of torch flare.

			float           distance;               // Vector distance to torch.

			float           intensity;              // Intensity of torch flare.

			VectorCopy(currententity->origin, lightorigin);

			//set radius based on what model we are doing here
		//qbism//jf 00-05-28 set the radius large when missile first appears
		if(torch && gl_torchflares.value)
				radius = 30.0;
			else if(!strcmp (clmodel->name, "progs/missile.mdl"))
			{
				if(currententity->forcelink == true) radius = 80.0f;
				else {
					radius = 20.0f;
					//qbism//00-11-08 moving rocket sound
					//move up before culling- S_StartSound (-1, 0, sfx_exhaust, currententity->origin, 1, 1);
				}
			}
			else if (!strncmp (clmodel->name, "progs/bolt",10))
				radius = 15.0f;
			else if( (!strcmp (clmodel->name, "progs/quaddama.mdl")) ||
					(!strcmp (clmodel->name, "progs/invulner.mdl"))
					)
				radius = 50.0f;

			VectorSubtract(lightorigin, r_origin, v);

			// See if view is outside the light.
				distance = Length(v);
			if (distance > radius) {

				glDepthMask (0);
				glDisable (GL_TEXTURE_2D);
				glShadeModel (GL_SMOOTH);
				glEnable (GL_BLEND);
				glBlendFunc (GL_ONE, GL_ONE);

				//qbism//2000-04-29 Translate the glow to coincide with the flame. KH
						// or be at the tail of the missile - muff
						// or the Quad or Pent - muff
			glPushMatrix();
				if (torch && gl_torchflares.value)
					glTranslatef(0.0f, 0.0f, 10.0f); //qbism//jf 2000-04-10 was 0,0,8... up a tad.

				else if (!strcmp (clmodel->name, "progs/missile.mdl"))
				{
					//move the glow behind the rocket
					glTranslatef( cos( e->angles[1]/180*M_PI)*(-20.0f),
						sin( e->angles[1]/180*M_PI)*(-20.0f),
						sin( e->angles[0]/180*M_PI)*(-20.0f)   );
				}
				else if( (!strcmp (clmodel->name, "progs/quaddama.mdl")) ||
						(!strcmp (clmodel->name, "progs/invulner.mdl"))
						)
					glTranslatef(0.0f, 0.0f, 20.0f);


				glBegin(GL_TRIANGLE_FAN);


				// Invert (fades as you approach) - if we are a torch
						if (torch && gl_torchflares.value)
				{
					// Diminish torch flare inversely with distance.
							intensity = (1024.0f - distance) / 1024.0f;
					intensity = (1.0f - intensity);
				}
				// or fix settings if lightning or missile
						else if (!strncmp (clmodel->name, "progs/bolt",10))
					intensity = 0.15f;
				else if (!strcmp (clmodel->name, "progs/missile.mdl"))
					intensity = 0.3f;
				else if( (!strcmp (clmodel->name, "progs/quaddama.mdl")) ||
						(!strcmp (clmodel->name, "progs/invulner.mdl")) )
					intensity = 0.3f;

				// Clamp, but don't let the flare disappear.
						if (intensity > 1.0f) intensity = 1.0f;
				if (intensity < 0.0f) intensity = 0.0f;

				// Now modulate with flicker.
						i = (int)(cl.time*10);
				if (!cl_lightstyle[TORCH_STYLE].length) {
					j[3] = 256;
				}
				else {
					j[3] = i % cl_lightstyle[TORCH_STYLE].length;
					j[3] = cl_lightstyle[TORCH_STYLE].map[j[3]] - 'a';
					j[3] = j[3]*22;
				}
				intensity *= ((float)j[3] / 255.0f);

				// Set yellow intensity
		#if 0
				// Testing
						glColor3f(0.8f, 0.4f, 0.1f);
#else
				//qbism//2000-04-10 set the colour of the glow - muff
						if ((torch && gl_torchflares.value))
					glColor3f(0.4f*intensity, 0.2f*intensity, 0.05f);
				else if (!strcmp (clmodel->name, "progs/missile.mdl"))
					glColor3f(0.9f*intensity, 0.2f*intensity, 0.1f*intensity);
				else if (!strncmp (clmodel->name, "progs/bolt",10))
					glColor3f(0.2f*intensity, 0.2f*intensity, 0.8f*intensity);
				else if (!strcmp (clmodel->name, "progs/quaddama.mdl"))
					glColor3f(0.1f*intensity, 0.1f*intensity, 0.8f*intensity);
				else if (!strcmp (clmodel->name, "progs/invulner.mdl"))
					glColor3f(0.8f*intensity, 0.1f*intensity, 0.1f*intensity);

#endif
				for (i=0 ; i<3 ; i++)
					v[i] = lightorigin[i] - vpn[i]*radius;
				glVertex3fv(v);
				glColor3f(0.0f, 0.0f, 0.0f);
				for (i=16; i>=0; i--) {
					float a = i/16.0f * M_PI*2;
					for (k=0; k<3; k++)
						v[k] = lightorigin[k] +
							vright[k]*cos(a)*radius +
							vup[k]*sin(a)*radius;
					glVertex3fv(v);
				}
				glEnd();

				// Restore previous matrix! KH
						glPopMatrix();

				glColor3f (1,1,1);
				glDisable (GL_BLEND);
				glEnable (GL_TEXTURE_2D);
				glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				glDepthMask (1);

			}
		}


		if (r_shadows.value)
		{


			//qbism//2000-04-29  muff@yakko.globalnet.co.uk - 05 Mar 00
		// lightning bolts really shouldn't cast a shadow
		// Neither should rockets
		// lots of other to go in here no doubt, but we'll add them as and when
		if ( (!strncmp (clmodel->name, "progs/bolt",10)) ||
					// (!strcmp  (clmodel->name, "progs/missile.mdl")) ||//qbism//jf 00-05-24 missile shadow
			(!strcmp  (clmodel->name, "progs/laser.mdl")) ||
					(!strcmp  (clmodel->name, "progs/flame.mdl")) ||//qbism//jf 00-04-27 no torch shadow
			(!strcmp  (clmodel->name, "progs/flame2.mdl")) ||//qbism//jf 00-04-27 no torch shadow
			(!strcmp  (clmodel->name, "progs/lavaball.mdl")) ||
					(!strcmp  (clmodel->name, "progs/quaddama.mdl")) ||
					(!strcmp  (clmodel->name, "progs/invulner.mdl"))
					)
				return;

			// in first person the engine currently draws the player's weapon,
		// but no player. This means a weapon shadow is drawn
		// we'll remove weapon shadow until this is fixed
		if (!strncmp (clmodel->name, "progs/v_",8))
				return;
			// end of muff

			glPushMatrix ();

			//qbism//2000-04-29 muff@yakko.globalnet.co.uk - 05 Mar 00
		// this determines the shadow vector relative to origin of entity
		// changed always be directly below entity for the moment.
		// will alter later (hopefully) when I got proper light source shadows working
		shadevector[0] = 0;
			shadevector[1] = 0;
			shadevector[2] = 1;
			VectorNormalize (shadevector);

			//original code
		//        R_RotateForEntity (e);


			//qbism//2000-04-29 muff@yakko.globalnet.co.uk - 050300 - added
		// this translates the location of the shadow, and rotates in horizontal plane only
		// this means that shadows now work properly for chase_cam, grenade etc
		// hence removal of code above
		if (r_interpolate_model_animation.value)
				R_BlendedRotateForEntity (e, 1); // we've re-written this to work properly

			else
				{
				glTranslatef (e->origin[0],  e->origin[1],  e->origin[2]);
				glRotatef (e->angles[1],  0, 0, 1);
			}
			//end of muff


			//qbism//2000-04-29 muff@yakko.globalnet.co.uk - 14 Mar 2000
		// this is so we can calculate height of the ground accurately
		VectorCopy (e->origin, downmove);

			downmove[2] = downmove[2] - 4096;
			memset (&downtrace, 0, sizeof(downtrace));
			SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, e->origin, downmove, &downtrace);
			// end of muff

			glDisable (GL_TEXTURE_2D);
			glEnable (GL_BLEND);

			// original code
		//    glColor4f (0,0,0,0.5);


			//qbism//2000-04-29 muff@yakko.globalnet.co.uk - 14 Mar 2000
		// set the shade of the shadow based on height off ground
		glColor4f (0,0,0,1.0 - ((mins[2]-downtrace.endpos[2])/60));
			//end of muff

			//original code
		//    GL_DrawAliasShadow (paliashdr, lastposenum);

			// fenix@io.com: model animation interpolation
		if (r_interpolate_model_animation.value)
				GL_DrawAliasBlendedShadow (paliashdr, lastposenum0, lastposenum, currententity);
			else
				GL_DrawAliasShadow (paliashdr, lastposenum);

			glEnable (GL_TEXTURE_2D);
			glDisable (GL_BLEND);
			glColor4f (1,1,1,1);
			glPopMatrix ();
		}
		//qbism//00-10-15 MD2
				}
	//qbism//00-10-15 MD2 end
}

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

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

	if (!r_drawentities.value)
		return;

	// draw sprites seperately, because of alpha blending
	for (i=0 ;
	i<cl_numvisedicts ;
	i++)
		{
		currententity = cl_visedicts[i];

		//qbism// 2000-01-09 ChaseCam fix by FrikaC  start
		if (currententity == &cl_entities[cl.viewentity])
		{
			// chase_active.value not checked as player model is necessary for shadows
			currententity->angles[0] *= 0.3;
		}
		//qbism// 2000-01-09 ChaseCam fix by FrikaC  end

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

		case mod_brush:
			R_DrawBrushModel (currententity);
			break;

		default:
			break;
		}
	}

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

		switch (currententity->model->type)
		{
		case mod_sprite:
			R_DrawSpriteModel (currententity);
			break;
		}
	}
}

/*
=============
R_DrawViewModel
=============
*/
void R_DrawViewModel (void)
{
	float		ambient[4], diffuse[4];
	//qbism//00-06-15 CSL - epca@powerup.com.au
	int         *j;
	int         shadelight[4];
	// CSL
	int			lnum;
	vec3_t		dist;
	float		add;
	dlight_t	*dl;
	int			ambientlight;

	//qbism// 2000-02-03 fenix@io.com: model transform interpolation
	float old_interpolate_model_transform;



	if (!r_drawviewmodel.value)
		return;

	if (chase_active.value)
		return;

	if (envmap)
		return;

	if (!r_drawentities.value)
		return;

	if (cl.items & IT_INVISIBILITY)
		return;

	if (cl.stats[STAT_HEALTH] <= 0)
		return;

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

	j = R_LightPoint (currententity->origin);

	//qbism//00-06-15 CSL - epca@powerup.com.au
	if (j[3] < 24)
		j[3] = 24;      // allways give some light on gun

	ambientlight = j[3];
	// Can't assign int* to int[4]...damn
	shadelight[0] = j[0];
	shadelight[1] = j[1];
	shadelight[2] = j[2];
	shadelight[3] = j[3];
	// CSL

	// add dynamic lights
	for (lnum=0 ;
	lnum<MAX_DLIGHTS ;
	lnum++)
		{
		dl = &cl_dlights[lnum];
		if (!dl->radius)
			continue;
		if (!dl->radius)
			continue;
		if (dl->die < cl.time)
			continue;

		VectorSubtract (currententity->origin, dl->origin, dist);
		add = dl->radius - Length(dist);
		if (add > 0)
		{
			//qbism//00-06-15 CSL - epca@powerup.com.au
			shadelight[0] += dl->colour[0];
			shadelight[1] += dl->colour[1];
			shadelight[2] += dl->colour[2];
			shadelight[3] += add;
			// CSL
			ambientlight += add;
		}
	}

	//qbism//jf 00-10-15 not used    ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128;
	//qbism//00-06-15 CSL - epca@powerup.com.au
	//qbism//jf 00-10-15 not used    diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight[3] / 128;
	// CSL

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

	//qbism// 2000-02-03 fenix@io.com: model transform interpolation
	old_interpolate_model_transform = r_interpolate_model_transform.value;
	r_interpolate_model_transform.value = false;
	R_DrawAliasModel (currententity);
	r_interpolate_model_transform.value = old_interpolate_model_transform;




	glDepthRange (gldepthmin, gldepthmax);
}


/*
============
R_PolyBlend
============
*/
void R_PolyBlend (void)
{
	if (!gl_polyblend.value)
		return;
	if (!v_blend[3])
		return;

	GL_DisableMultitexture();

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

	glLoadIdentity ();

	glRotatef (-90,  1, 0, 0);	    // put Z going up

	glRotatef (90,  0, 0, 1);	    // put Z going up

	glColor4fv (v_blend);

	glBegin (GL_QUADS);

	glVertex3f (10, 100, 100);
	glVertex3f (10, -100, 100);
	glVertex3f (10, -100, -100);
	glVertex3f (10, 100, -100);
	glEnd ();

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


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


void R_SetFrustum (void)
{
	int		i;

	if (r_refdef.fov_x == 90)
	{
		// front side is visible

		VectorAdd (vpn, vright, frustum[0].normal);
		VectorSubtract (vpn, vright, frustum[1].normal);

		VectorAdd (vpn, vup, frustum[2].normal);
		VectorSubtract (vpn, vup, frustum[3].normal);
	}
	else
		{
		// rotate VPN right by FOV_X/2 degrees
		RotatePointAroundVector( frustum[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) );
		// rotate VPN left by FOV_X/2 degrees
		RotatePointAroundVector( frustum[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 );
		// rotate VPN up by FOV_X/2 degrees
		RotatePointAroundVector( frustum[2].normal, vright, vpn, 90-r_refdef.fov_y / 2 );
		// rotate VPN down by FOV_X/2 degrees
		RotatePointAroundVector( frustum[3].normal, vright, vpn, -( 90 - r_refdef.fov_y / 2 ) );
	}

	for (i=0 ; i<4 ; i++)
	{
		frustum[i].type = PLANE_ANYZ;
		frustum[i].dist = DotProduct (r_origin, frustum[i].normal);
		frustum[i].signbits = SignbitsForPlane (&frustum[i]);
	}
}



/*
===============
R_SetupFrame
===============
*/
void R_SetupFrame (void)
{
	int				edgecount;
	vrect_t			vrect;
	float			w, h;

	// don't allow cheats in multiplayer
	if (cl.maxclients > 1)
		Cvar_Set ("r_fullbright", "0");

	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_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel);

	V_SetContentsColor (r_viewleaf->contents);
	V_CalcBlend ();

	r_cache_thrash = false;

	c_brush_polys = 0;
	c_alias_polys = 0;

}


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

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

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

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


/*
=============
R_SetupGL
=============
*/
void R_SetupGL (void)
{
	float	screenaspect;
	float	yfov;
	int		i;
	extern	int glwidth, glheight;
	int		x, x2, y2, y, w, h;

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

	// fudge around because of frac screen scale
	if (x > 0)
		x--;
	if (x2 < glwidth)
		x2++;
	if (y2 < 0)
		y2--;
	if (y < glheight)
		y++;

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

	if (envmap)
	{
		x = y2 = 0;
		w = h = 256;
	}

	glViewport (glx + x, gly + y2, w, h);
	screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height;
	//	yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI;
	MYgluPerspective (r_refdef.fov_y,  screenaspect,  4,  4096);

	if (mirror)
	{
		if (mirror_plane->normal[2])
			glScalef (1, -1, 1);
		else
			glScalef (-1, 1, 1);
		glCullFace(GL_BACK);
	}
	else
		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_RenderScene

r_refdef must be set before the first call
================
*/
void R_RenderScene (void)
{
	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 ();

	GL_DisableMultitexture();

	R_RenderDlights ();

	R_DrawParticles ();

#ifdef GLTEST
	Test_Draw ();
#endif

}


/*
=============
R_Clear
=============
*/
void R_Clear (void)
{
	if (r_mirroralpha.value != 1.0)
	{
		if (gl_clear.value)
			glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		else
			glClear (GL_DEPTH_BUFFER_BIT);
		gldepthmin = 0;
		gldepthmax = 0.5;
		glDepthFunc (GL_LEQUAL);
	}
	else if (gl_ztrick.value)
	{
		static int trickframe;

		if (gl_clear.value)
			glClear (GL_COLOR_BUFFER_BIT);

		trickframe++;
		if (trickframe & 1)
		{
			gldepthmin = 0;
			gldepthmax = 0.49999;
			glDepthFunc (GL_LEQUAL);
		}
		else
			{
			gldepthmin = 1;
			gldepthmax = 0.5;
			glDepthFunc (GL_GEQUAL);
		}
	}
	else
		{
		if (gl_clear.value)
			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);
}

/*
=============
R_Mirror
=============
*/
void R_Mirror (void)
{
	float		d;
	msurface_t	*s;
	entity_t	*ent;

	if (!mirror)
		return;

	memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix));

	d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist;
	VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg);

	d = DotProduct (vpn, mirror_plane->normal);
	VectorMA (vpn, -2*d, mirror_plane->normal, vpn);

	r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180;
	r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180;
	r_refdef.viewangles[2] = -r_refdef.viewangles[2];

	ent = &cl_entities[cl.viewentity];
	if (cl_numvisedicts < MAX_VISEDICTS)
	{
		cl_visedicts[cl_numvisedicts] = ent;
		cl_numvisedicts++;
	}

	gldepthmin = 0.5;
	gldepthmax = 1;
	glDepthRange (gldepthmin, gldepthmax);
	glDepthFunc (GL_LEQUAL);

	R_RenderScene ();
	R_DrawWaterSurfaces ();

	gldepthmin = 0;
	gldepthmax = 0.5;
	glDepthRange (gldepthmin, gldepthmax);
	glDepthFunc (GL_LEQUAL);

	// blend on top
	glEnable (GL_BLEND);
	glMatrixMode(GL_PROJECTION);
	if (mirror_plane->normal[2])
		glScalef (1,-1,1);
	else
		glScalef (-1,1,1);
	glCullFace(GL_FRONT);
	glMatrixMode(GL_MODELVIEW);

	glLoadMatrixf (r_base_world_matrix);

	glColor4f (1,1,1,r_mirroralpha.value);
	s = cl.worldmodel->textures[mirrortexturenum]->texturechain;
	for ( ; s ; s=s->texturechain)
		R_RenderBrushPoly (s);
	cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL;
	glDisable (GL_BLEND);
	glColor4f (1,1,1,1);
}

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

r_refdef must be set before the first call
================
*/
void R_RenderView (void)
{
	double	time1, time2;
	GLfloat colors[4];

	if (r_norefresh.value)
		return;

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

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

	mirror = false;

	if (gl_finish.value)
		glFinish ();

	R_Clear ();

	// render normal view

	////qbism//00-06-15 NATAS - BramBo - fog code
	if( gl_fogenable.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);
		glFogf(GL_FOG_DENSITY, gl_fogdensity.value);
		glEnable(GL_FOG);
	}
	else
		{
		glDisable(GL_FOG);
	}
	// END

	R_RenderScene ();
	R_DrawViewModel ();
	R_DrawWaterSurfaces ();

	//qbism//00-06-15  NATAS - BramBo - Fog code
		glDisable(GL_FOG);
	// END

	// render mirror view
	R_Mirror ();

	R_PolyBlend ();

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