/*
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			playertextures;		// up to 16 color translated skins

int			mirrortexturenum;	// quake texturenum, not gltexturenum
qboolean	mirror;
mplane_t	*mirror_plane;

int			internal_skytype, internal_skytype2;

//
// 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_drawbrass = {"r_drawbrass", "1"};
cvar_t	r_drawdecals = {"r_drawdecals", "1"};
cvar_t	r_drawdecallights = {"r_drawdecallights", "1"};
cvar_t	r_maxdecals = {"r_maxdecals", "50"};
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_mirroralpha = {"r_mirroralpha","1"};
cvar_t	r_wateralpha = {"r_wateralpha","0.9"};
cvar_t	r_dynamic = {"r_dynamic","1"};
cvar_t	r_novis = {"r_novis","0"};
cvar_t  r_particledensity = {"r_particledensity", "1"};
cvar_t  r_particleduration = {"r_particleduration", "1"};
cvar_t  r_hudalpha = {"r_hudalpha", "0.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","0"};
cvar_t	gl_playermip = {"gl_playermip","0"};
cvar_t	gl_nocolors = {"gl_nocolors","0"};
cvar_t	gl_keeptjunctions = {"gl_keeptjunctions","1"};
cvar_t	gl_reporttjunctions = {"gl_reporttjunctions","0"};
cvar_t	gl_doubleeyes = {"gl_doubleeys", "1"};

cvar_t gl_fogenable  = {"gl_fogenable", "1", true};
cvar_t gl_fogblue    = {"gl_fogblue", "0.35"};
cvar_t gl_fogred     = {"gl_fogred", "0.3"};
cvar_t gl_foggreen   = {"gl_foggreen", "0.3"};
cvar_t gl_fogstart   = {"gl_fogstart", "0"};
cvar_t gl_fogend     = {"gl_fogend", "4000"};
cvar_t gl_fogdensity = {"gl_fogdensity", "0.2"};
cvar_t gl_fogalpha   = {"gl_fogalpha", "0.1"};

cvar_t gl_skytype = {"gl_skytype", "2"};
cvar_t gl_skyturn = {"gl_skyturn", "0", true};
cvar_t gl_skytype2= {"gl_skytype2", "0"};
cvar_t gl_skyblend = {"gl_skyblend", "0"};

cvar_t  r_interpolate_model_animation = { "r_interpolate_model_animation", "1", true };
cvar_t  r_interpolate_model_transform = { "r_interpolate_model_transform", "1", true };

cvar_t	gl_detailmapenable = {"gl_detailmapenable", "0", false};
cvar_t	gl_detailmapintensity = {"gl_detailmapintensity", "0.15", true};

extern	cvar_t	gl_ztrick;

/*
=================
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
=============
*/
void R_BlendedRotateForEntity (entity_t *e)
{
    float timepassed;
    float blend;
    vec3_t d;
    int i;

    // 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
    {
        blend =  timepassed / 0.1;

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

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

    glTranslatef (
        e->origin1[0] + (blend * d[0]),
        e->origin1[1] + (blend * d[1]),
        e->origin1[2] + (blend * d[2]));

    // orientation interpolation (Euler angles, yuck!)

    timepassed = realtime - e->rotate_start_time; 

    if (e->rotate_start_time == 0 || 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
    {
        blend = timepassed / 0.1;

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

    VectorSubtract (e->angles2, e->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 ( 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)
{
	model_t *clmodel;
	vec3_t	point;
	mspriteframe_t	*frame;
	float		*up, *right, lite;
	vec3_t		v_forward, v_right, v_up;
	msprite_t		*psprite;
	int	i,j;
	dlight_t	*dl;
	vec3_t		dist;
	float		add;

	clmodel = currententity->model;
	if(clmodel->glowmodel == 8)
		return;

	if ( clmodel->glowmodel > 0)
	{
		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
		if(clmodel->glowmodel == 1)
		 radius = 20.0f;
		else if (clmodel->glowmodel == 2)
		 radius = 30.0f;
		else if(clmodel->glowmodel == 3)
		 radius = 50.0f;
		else if(clmodel->glowmodel == 4)
		 radius = 200.0f;
		else if(clmodel->glowmodel == 6)
		 radius = 10.0f * currententity->frame;
		else if(clmodel->glowmodel == 7)
		 radius = 5.0f;
		else if(clmodel->glowmodel == 9)
		 radius = 10.0f + (currententity->frame * 10);

		//VectorSubtract(lightorigin, r_origin, v);
		// See if view is outside the light.
		//distance = Length(v);
		// -- looks stupid
		if (1==1) {

			if (gl_fogenable.value)
				glDisable(GL_FOG);
			glDepthMask (FALSE);
			glDisable (GL_TEXTURE_2D);
			glShadeModel (GL_SMOOTH);
			glEnable (GL_BLEND);
			glBlendFunc (GL_ONE, GL_ONE);

			glPushMatrix();			
			glBegin(GL_TRIANGLE_FAN);

			if (clmodel->glowmodel == 6)
				intensity = ((20 - currententity->frame) * 0.05);
			else
				intensity = 1.0f;
			
			if (clmodel->glowmodel == 7)
				glColor3f(0.8f*intensity, 0.8f*intensity, 0.8f*intensity);
			else if (clmodel->glowmodel == 6)
				glColor3f(0.1f*intensity, 0.4f*intensity, 0.8f*intensity);
			else
				glColor3f(0.8f*intensity, 0.4f*intensity, 0.1f);

			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 (j=0; j<3; j++)
				 v[j] =	lightorigin[j] + 
					vright[j]*cos(a)*radius +
					vup[j]*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 (TRUE);
			if (gl_fogenable.value)
				glEnable(GL_FOG);
		}
	}

	if(clmodel->glowmodel == 7 || clmodel->glowmodel == 9)
		return;
	
	glDepthMask(FALSE);
	// 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;

		up[0] = up[0] * 0.5;
		up[1] = up[1] * 0.5;
		up[2] = up[2] * 0.5;

		right[0] = right[0] * 0.5;
		right[1] = right[1] * 0.5;
		right[2] = right[2] * 0.5;

		if (r_drawdecallights.value)
		{
			lite = R_LightPoint (currententity->origin);
			lite = lite/128;
			
			// add dynamic lights		
			for (i=0 ; i<MAX_DLIGHTS ; i++)
			{
				dl = &cl_dlights[i];
				if (!dl->radius)
					continue;
				if (dl->die < cl.time)
					continue;
				if (!dl->dark)
				{
					VectorSubtract (currententity->origin, dl->origin, dist);
					add = dl->radius - Length(dist);
					if (add > 0)
						lite += add/128;
				}
			}
		}
	}
	else
	{	// normal sprite
		up = vup;
		right = vright;
	}

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	if (psprite->type == SPR_ORIENTED && r_drawdecallights.value)
		glColor4f(lite, lite, lite, 0.75);
	else
		glColor4f(1, 1, 1, 0.75);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	GL_DisableMultitexture();

    GL_Bind(frame->gl_texturenum);

	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 ();
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glDisable(GL_BLEND);
	glDisable(GL_ALPHA_TEST);
	glDepthMask(TRUE);
}

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

  ALIAS MODELS

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


#define NUMVERTEXNORMALS	162

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

vec3_t	shadevector;
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];

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
			l = shadedots[verts->lightnormalindex] * shadelight;
			glColor3f (l, l, l);
			glVertex3f (verts->v[0], verts->v[1], verts->v[2]);
			verts++;
		} while (--count);

		glEnd ();
	}
}


/*
=============
GL_DrawAliasBlendedFrame
=============
*/
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];

			l = shadelight * (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;
	float	height, lheight;
	int		count;

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

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

	height = -lheight + 1.0;

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

		do
		{
			// 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];

			point[0] -= shadevector[0]*(point[2]+lheight);
			point[1] -= shadevector[1]*(point[2]+lheight);
			point[2] = height;
//			height -= 0.001;
			glVertex3fv (point);

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

		glEnd ();
	}	
}

/*
=============
GL_DrawAliasBlendedShadow

fenix@io.com: model animation interpolation
=============
*/
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;

   blend = (realtime - e->frame_start_time) / e->frame_interval;

   if (blend > 1) blend = 1;

   lheight = e->origin[2] - lightspot[2];
   height  = -lheight + 1.0;

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

         glVertex3f (point1[0] + (blend * d[0]), 
                     point1[1] + (blend * d[1]), 
                     height);

         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
=================
*/
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)
	{
		e->frame_interval = paliashdr->frames[frame].interval;
		pose += (int)(cl.time / e->frame_interval) % numposes;
	}
	else 
	{
		/* 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. */
		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;
	GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend);
}

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

=================
*/
void R_DrawAliasModel (entity_t *e, int gloss)
{
	int			i, j;
	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;
	FILE		*f;

	clmodel = currententity->model;

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

	if (R_CullBox (mins, maxs))
		return;

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

	if (clmodel->glowmodel != 5 && clmodel->glowmodel != 4 && clmodel->glowmodel != 10)
	{
		//
		// get lighting information
		//
		ambientlight = shadelight = R_LightPoint (currententity->origin);
		// allways give the gun some light
		if (e == &cl.viewent && ambientlight < 24)
			ambientlight = shadelight = 24;

		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
					shadelight += add;
				}
			}
		}

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

		// 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)
				ambientlight = shadelight = 8;

		// HACK HACK HACK -- no fullbright colors, so make torches full light
		if (!strcmp (clmodel->name, "progs/flame2.mdl")
			|| !strcmp (clmodel->name, "progs/flame.mdl") )
			ambientlight = shadelight = 256;

		shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
		shadelight = shadelight / 200.0;
		
		an = e->angles[1]/180*M_PI;
		shadevector[0] = cos(-an);
		shadevector[1] = sin(-an);
		shadevector[2] = 1;
		VectorNormalize (shadevector);

		// locate the proper data
		paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model);

		c_alias_polys += paliashdr->numtris;

		// draw all the triangles
		GL_DisableMultitexture();
		glPushMatrix ();

		if (r_interpolate_model_transform.value && (strcmp(clmodel->name, "progs/shell.mdl")))
		{
			R_BlendedRotateForEntity (e);
		}
		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]);
		}

		anim = (int)(cl.time*10) & 3;
		GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]);

		// 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") */)
				GL_Bind(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);
		
		if (r_interpolate_model_animation.value && (strcmp(clmodel->name, "progs/shell.mdl")))
			R_SetupAliasBlendedFrame (currententity->frame, paliashdr, currententity);
		else
			R_SetupAliasFrame (currententity->frame, paliashdr);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

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

		glPopMatrix ();
	}

	if ( clmodel->glowmodel > 0)
	{
		vec3_t	lightorigin;
		vec3_t	v;
		float		radius;
		float		distance;
		float		intensity;

		VectorCopy(currententity->origin, lightorigin);

		//set radius based on what model we are doing here
		if(clmodel->glowmodel == 1)
			radius = 20.0f;
		else if (clmodel->glowmodel == 2 )
			radius = 30.0f;
		else if(clmodel->glowmodel == 3)
			radius = 50.0f;
		else if(clmodel->glowmodel == 4)
			radius = currententity->frame * 30.0f;
		else if(clmodel->glowmodel == 5)
			radius = currententity->frame * 5.0f;
		else if(clmodel->glowmodel == 9)
			radius = 20.0f;
		else if(clmodel->glowmodel == 10)
			radius = 1.0f;

		VectorSubtract(lightorigin, r_origin, v);
		// check if glow is too close to viewer
		distance = Length(v);
		if (distance > radius) {

			if (gl_fogenable.value)
				glDisable(GL_FOG);

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

			glPushMatrix();			
			glBegin(GL_TRIANGLE_FAN);

			if (clmodel->glowmodel == 5)
				intensity = (currententity->frame * 0.05);
			else
				intensity = 1.0f;

			// set the colour of the glow
			if (clmodel->glowmodel == 9)
				glColor3f(0.2, 0.1, 0.7);
			else if (clmodel->glowmodel == 5)
				glColor3f(0.4 * intensity, 0.4 * intensity, 0.5 * intensity);
			else
				glColor3f(0.7f*intensity, 0.55f*intensity, 0.4f);

			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 (j=0; j<3; j++)
				 v[j] =	lightorigin[j] + 
					vright[j]*cos(a)*radius +
					vup[j]*sin(a)*radius;
				glVertex3fv(v);
			}
			glEnd();

			glPopMatrix();
			glColor3f (1,1,1);
			glDisable (GL_BLEND);
			glEnable (GL_TEXTURE_2D);
			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glDepthMask (1);
			if (gl_fogenable.value)
				glEnable(GL_FOG);
		}
	}

	if (r_shadows.value)
	{
		glPushMatrix ();
		if (r_interpolate_model_transform.value)
		{
			R_BlendedRotateForEntity (e);
		}
		else
		{
			R_RotateForEntity (e);
		}
		glDisable (GL_TEXTURE_2D);
		glEnable (GL_BLEND);
		glColor4f (0,0,0,0.5);
		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 ();
	}
}

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

int Depth_Compare( const void *arg1, const void *arg2 )
{
	draworder *pOrder1, *pOrder2;

	pOrder1 = (draworder*) arg1;
	pOrder2 = (draworder*) arg2;

	if (pOrder1->depth > pOrder2->depth)
		return -1;
	else if (pOrder1->depth < pOrder2->depth)
		return 1;
	else
		return 0;
}

/*
=============
R_DrawEntitiesOnList
=============
*/
void R_DrawEntitiesOnList (void)
{
	int			i;
	draworder	sprite_order[MAX_VISEDICTS];
	vec3_t		sprite_org, vec;
	entity_t	*tempentity;

	if (!r_drawentities.value)
		return;

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

		if (currententity == &cl_entities[cl.viewentity]) 
			currententity->angles[0] *= 0.3;

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

		case mod_brush:
			R_DrawBrushModel (currententity);
			break;

		default:
			break;
		}
	}
}

/*
=============
R_DrawEntitiesOnList
=============
*/
void R_DrawSpritesOnList (void)
{
	int			i;
	draworder	sprite_order[MAX_VISEDICTS];
	vec3_t		sprite_org, vec;
	entity_t	*tempentity;

	if (!r_drawentities.value)
		return;

	// make new list of ents by depth and current order in list
	for (i=0 ; i<cl_numvisedicts ; i++)
	{
		tempentity = cl_visedicts[i];

		VectorCopy(tempentity->origin, sprite_org);
		VectorSubtract(sprite_org, r_origin, vec);
		sprite_order[i].depth = Length(vec);
		sprite_order[i].order = i;
	}

	// sort this new list by depth, back to front
	qsort(sprite_order, cl_numvisedicts, sizeof(draworder), Depth_Compare);

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

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

/*
=============
R_DrawDetailmapEntitiesOnList.
=============
*/
void R_DrawDetailmapEntitiesOnList (void)
{
	int		i;

	if (!r_drawentities.value)
		return;

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

		switch (currententity->model->type) {
		case mod_brush:
			R_DrawDetailmapBrushModel (currententity);
			break;
		}
	}
}

/*
=============
R_DrawViewModel
=============
*/
void R_DrawViewModel (void)
{
	float		ambient[4], diffuse[4];
	int			j;
	int			lnum;
	vec3_t		dist;
	float		add;
	dlight_t	*dl;
	int			ambientlight, shadelight;
	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.stats[STAT_HEALTH] <= 0)
		return;

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

	j = R_LightPoint (currententity->origin);

	if (j < 24)
		j = 24;		// allways give some light on gun
	ambientlight = j;
	shadelight = j;

// 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)
			ambientlight += add;
	}

	ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128;
	diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight / 128;

	// hack the depth range to prevent view model from poking into walls
	glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
	old_interpolate_model_transform = r_interpolate_model_transform.value;
	r_interpolate_model_transform.value = false;
	R_DrawAliasModel (currententity, 1);
	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);
}

extern int DetailmapName;

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

	// added water surfaces.
	R_DrawWaterSurfaces ();
	
	R_DrawEntitiesOnList ();

	GL_DisableMultitexture();

	R_RenderDlights ();

	R_DrawUltraParticles ();

	R_DrawSpritesOnList ();
}


/*
=============
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);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	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);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	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] = {(GLfloat) gl_fogred.value, (GLfloat) gl_foggreen.value, (GLfloat) gl_fogblue.value, (GLfloat) gl_fogalpha.value};

	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
	glFogi(GL_FOG_MODE, GL_LINEAR);
	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);
	if( gl_fogenable.value)
		glEnable(GL_FOG);

	R_RenderScene ();
	R_DrawViewModel ();

	//normal water draw
	//R_DrawWaterSurfaces ();

	if( gl_fogenable.value)
		glDisable(GL_FOG);

	// render mirror view
	R_Mirror ();

	R_PolyBlend ();

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