// gl_main.c

/*
 * $Header: /H2 Mission Pack/gl_rmain.c 4     3/30/98 10:57a Jmonroe $
 */

#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, c_sky_polys;

qboolean	envmap;				// true during envmap command capture 
int			currenttexture;		// to avoid unnecessary texture sets

int			particletexture;	// little dot for particles
int			playertextures;		// up to 16 color translated skins
int			gl_extra_textures[MAX_EXTRA_TEXTURES];   // generic textures for models

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

float		r_time1;
float		r_lasttime1 = 0;

//
// 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","1",true};

cvar_t	r_particle_adv = {"r_particle_adv","1",true};
cvar_t	r_brush_alpha = {"r_brush_alpha",".4",true};
cvar_t	r_model_alpha = {"r_model_alpha",".4",true};
cvar_t	r_fx_alpha = {"r_fx_alpha",".4",true};

cvar_t	r_mirroralpha = {"r_mirroralpha","1",true};
cvar_t	r_wateralpha = {"r_wateralpha",".4", true};
cvar_t	r_dynamic = {"r_dynamic","1",true};
cvar_t	r_novis = {"r_novis","0"};
cvar_t	r_wholeframe = {"r_wholeframe", "1", true};

cvar_t	gl_clear = {"gl_clear","0",true};
cvar_t	gl_cull = {"gl_cull","1",true};
cvar_t	gl_texsort = {"gl_texsort","1",true};
cvar_t	gl_smoothmodels = {"gl_smoothmodels","1",true};
cvar_t	gl_affinemodels = {"gl_affinemodels","0",true};
cvar_t	gl_polyblend = {"gl_polyblend","1",true};
cvar_t	gl_flashblend = {"gl_flashblend","0",true};
cvar_t	gl_playermip = {"gl_playermip","0",true};
cvar_t	gl_nocolors = {"gl_nocolors","0",true};
cvar_t	gl_keeptjunctions = {"gl_keeptjunctions","1",true};
cvar_t	gl_reporttjunctions = {"gl_reporttjunctions","0",true};
//jkrige - cvars
cvar_t	gl_waterripple ={"gl_waterripple", "1.5",true};
//jkrige - cvars
cvar_t	gl_stencilshadow ={"gl_stencilshadow", "1",true};
cvar_t	gl_shadowalpha ={"gl_shadowalpha", "0.2",true};
cvar_t	gl_waterwarp ={"gl_waterwarp", "0.4",true};
cvar_t	gl_water_reflection ={"gl_water_reflection", "1",true};
cvar_t  gl_force_liguid_transparency = {"gl_force_liguid_transparency","1",true}; 

// NATAS - BramBo - Fog Code
cvar_t gl_fogenable =   {"gl_fogenable", "1",true}; 
cvar_t gl_fogstart =    {"gl_fogstart", "800.0",true}; 
cvar_t gl_fogend =      {"gl_fogend", "3000.0",true}; 
cvar_t gl_fogdensity =  {"gl_fogdensity", "0.2",true}; 
cvar_t gl_fogred =      {"gl_fogred","0.5",true}; 
cvar_t gl_foggreen =    {"gl_foggreen","0.5",true}; 
cvar_t gl_fogblue =     {"gl_fogblue","0.5",true}; 
cvar_t gl_fogalpha =    {"gl_fogalpha", "0.5",true};
cvar_t gl_underwaterfog = {"gl_underwaterfog", "1",true};
// END

int    gl_fogenabled =  0;

cvar_t gl_coloredlight =   {"gl_coloredlight","2",true};
cvar_t gl_colored_dynamic_type =   {"gl_colored_dynamic_type","0",true};
cvar_t gl_colored_dynamic_lights  =   {"gl_colored_dynamic_lights","1",true};
cvar_t gl_extra_dynamic_lights  =   {"gl_extra_dynamic_lights","1",true};
cvar_t r_lerp_alias = {"r_lerp_alias", "1", true};

cvar_t gl_glows =   {"gl_glows","1",true};
cvar_t gl_other_glows =   {"gl_other_glows","1",true};
cvar_t gl_missile_glows = {"gl_missile_glows","1",true};
cvar_t gl_multitexture = {"gl_multitexture","1",true};

extern cvar_t v_gamma;

extern qboolean gl_mtexable;
extern GLenum GL_MTexture0;
extern GLenum GL_MTexture1;
extern GLenum GL_MTexture2;

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

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

  SPRITE MODELS

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

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

	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

=================
*/
typedef struct
{
	vec3_t			vup, vright, vpn;	// in worldspace
} spritedesc_t;

void R_DrawSpriteModel (entity_t *e)
{
	vec3_t	point;
	mspriteframe_t	*frame;
	msprite_t		*psprite;
	
	vec3_t			tvec;
	float			dot, angle, sr, cr;
	spritedesc_t			r_spritedesc;
	int i;

    // xp +
    //glDisable(GL_ALPHA_TEST);
    //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //glEnable(GL_BLEND);
    //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    // xp -
    

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

	frame = R_GetSpriteFrame (psprite);
    
	if (currententity->drawflags & DRF_TRANSLUCENT)
	{
		// rjr
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable( GL_BLEND );
		//glColor4f (1,1,1,r_wateralpha.value);
		glColor4f (1,1,1,r_fx_alpha.value);
	}
	else if (currententity->model->flags & EF_TRANSPARENT)
	{
		// rjr
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable( GL_BLEND );
		glColor3f(1,1,1);
	}
	else
	{
		// rjr
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable( GL_BLEND );
		glColor3f(1,1,1);
	}
    
	if (psprite->type == SPR_FACING_UPRIGHT)
	{
	// generate the sprite's axes, with vup straight up in worldspace, and
	// r_spritedesc.vright perpendicular to modelorg.
	// This will not work if the view direction is very close to straight up or
	// down, because the cross product will be between two nearly parallel
	// vectors and starts to approach an undefined state, so we don't draw if
	// the two vectors are less than 1 degree apart
		tvec[0] = -modelorg[0];
		tvec[1] = -modelorg[1];
		tvec[2] = -modelorg[2];
		VectorNormalize (tvec);
		dot = tvec[2];	// same as DotProduct (tvec, r_spritedesc.vup) because
						//  r_spritedesc.vup is 0, 0, 1
		if ((dot > 0.999848) || (dot < -0.999848))	// cos(1 degree) = 0.999848
			return;
		r_spritedesc.vup[0] = 0;
		r_spritedesc.vup[1] = 0;
		r_spritedesc.vup[2] = 1;
		r_spritedesc.vright[0] = tvec[1];
								// CrossProduct(r_spritedesc.vup, -modelorg,
		r_spritedesc.vright[1] = -tvec[0];
								//              r_spritedesc.vright)
		r_spritedesc.vright[2] = 0;
		VectorNormalize (r_spritedesc.vright);
		r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
		r_spritedesc.vpn[1] = r_spritedesc.vright[0];
		r_spritedesc.vpn[2] = 0;
					// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
					//  r_spritedesc.vpn)
	}
	else if (psprite->type == SPR_VP_PARALLEL)
	{
	// generate the sprite's axes, completely parallel to the viewplane. There
	// are no problem situations, because the sprite is always in the same
	// position relative to the viewer
		for (i=0 ; i<3 ; i++)
		{
			r_spritedesc.vup[i] = vup[i];
			r_spritedesc.vright[i] = vright[i];
			r_spritedesc.vpn[i] = vpn[i];
		}
	}
	else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
	{
	// generate the sprite's axes, with vup straight up in worldspace, and
	// r_spritedesc.vright parallel to the viewplane.
	// This will not work if the view direction is very close to straight up or
	// down, because the cross product will be between two nearly parallel
	// vectors and starts to approach an undefined state, so we don't draw if
	// the two vectors are less than 1 degree apart
		dot = vpn[2];	// same as DotProduct (vpn, r_spritedesc.vup) because
						//  r_spritedesc.vup is 0, 0, 1
		if ((dot > 0.999848) || (dot < -0.999848))	// cos(1 degree) = 0.999848
			return;
		r_spritedesc.vup[0] = 0;
		r_spritedesc.vup[1] = 0;
		r_spritedesc.vup[2] = 1;
		r_spritedesc.vright[0] = vpn[1];
										// CrossProduct (r_spritedesc.vup, vpn,
		r_spritedesc.vright[1] = -vpn[0];	//  r_spritedesc.vright)
		r_spritedesc.vright[2] = 0;
		VectorNormalize (r_spritedesc.vright);
		r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
		r_spritedesc.vpn[1] = r_spritedesc.vright[0];
		r_spritedesc.vpn[2] = 0;
					// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
					//  r_spritedesc.vpn)
	}
	else if (psprite->type == SPR_ORIENTED)
	{
	// generate the sprite's axes, according to the sprite's world orientation
		AngleVectors (currententity->angles, r_spritedesc.vpn,
					  r_spritedesc.vright, r_spritedesc.vup);
	}
	else if (psprite->type == SPR_VP_PARALLEL_ORIENTED)
	{
	// generate the sprite's axes, parallel to the viewplane, but rotated in
	// that plane around the center according to the sprite entity's roll
	// angle. So vpn stays the same, but vright and vup rotate
		angle = currententity->angles[ROLL] * (M_PI*2 / 360);
		sr = sin(angle);
		cr = cos(angle);

		for (i=0 ; i<3 ; i++)
		{
			r_spritedesc.vpn[i] = vpn[i];
			r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
			r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
		}
	}
	else
	{
		Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type);
	}

//	R_RotateSprite (psprite->beamlength);

    GL_Bind(frame->gl_texturenum);

	glBegin (GL_QUADS);
	glEnable (GL_ALPHA_TEST);
    //glAlphaFunc(GL_GREATER, 0.666);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    
    //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	
	//glTexCoord2f (0, 1);
	glTexCoord2f (0.05, 0.95);
	VectorMA (e->origin, frame->down, r_spritedesc.vup, point);
	VectorMA (point, frame->left, r_spritedesc.vright, point);
	glVertex3fv (point);

	//glTexCoord2f (0, 0);
	glTexCoord2f (0.05, 0.05);
	VectorMA (e->origin, frame->up, r_spritedesc.vup, point);
	VectorMA (point, frame->left, r_spritedesc.vright, point);
	glVertex3fv (point);

	//glTexCoord2f (1, 0);
	glTexCoord2f (0.95, 0.05);
	VectorMA (e->origin, frame->up, r_spritedesc.vup, point);
	VectorMA (point, frame->right, r_spritedesc.vright, point);
	glVertex3fv (point);

	//glTexCoord2f (1, 1);
	glTexCoord2f (0.95, 0.95);
	VectorMA (e->origin, frame->down, r_spritedesc.vup, point);
	VectorMA (point, frame->right, r_spritedesc.vright, point);
	glVertex3fv (point);

	glEnd ();

	//restore tex parms
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	
    glDisable( GL_ALPHA_TEST);
	glDisable( GL_BLEND );
    
    // xp
    //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    
}


typedef struct sortedent_s {
	entity_t *ent;
	vec_t len;
} sortedent_t;

sortedent_t     cl_transvisedicts[MAX_VISEDICTS];
sortedent_t		cl_transwateredicts[MAX_VISEDICTS];

int				cl_numtransvisedicts;
int				cl_numtranswateredicts;

/*
=============
R_DrawEntitiesOnList
=============
*/
void R_DrawEntitiesOnList (void)
{
	int		i;
	qboolean item_trans;
	mleaf_t *pLeaf;

	cl_numtransvisedicts=0;
	cl_numtranswateredicts=0;

	if (!r_drawentities.value)
		return;

	// draw sprites seperately, because of alpha blending
	for (i=0 ; i<cl_numvisedicts ; i++)
	{
		currententity = cl_visedicts[i];
        
		switch (currententity->model->type)
		{
		case mod_alias:
			item_trans = ((currententity->drawflags & DRF_TRANSLUCENT) ||
						  (currententity->model->flags & (EF_TRANSPARENT|EF_HOLEY|EF_SPECIAL_TRANS))) != 0;
			if (!item_trans)
				R_DrawAliasModel (currententity);

			break;

		case mod_brush:
			item_trans = ((currententity->drawflags & DRF_TRANSLUCENT)) != 0;
			if (!item_trans)
				R_DrawBrushModel (currententity,false);

			break;


		case mod_sprite:
			item_trans = true;

			break;

		default:
			item_trans = false;
			break;
		}
		
		if (item_trans) {
			pLeaf = Mod_PointInLeaf (currententity->origin, cl.worldmodel);
//			if (pLeaf->contents == CONTENTS_EMPTY)
			if (pLeaf->contents != CONTENTS_WATER)
				cl_transvisedicts[cl_numtransvisedicts++].ent = currententity;
			else
				cl_transwateredicts[cl_numtranswateredicts++].ent = currententity;
		}
	}
}

void R_DrawGlow (entity_t *e)
{
	model_t		*clmodel;
	static float	tmatrix[3][4];

	clmodel = currententity->model;


    // Torches & Flames
	if ((gl_glows.value && (clmodel->ex_flags & XF_TORCH_GLOW || clmodel->ex_flags & XF_BIGTORCH_GLOW)) ||
        (gl_missile_glows.value && (clmodel->ex_flags & XF_MISSILE_GLOW || clmodel->ex_flags & XF_BIGMISS_GLOW || clmodel->ex_flags & XF_FADEMISS_GLOW)) || 
        (gl_other_glows.value && (clmodel->ex_flags & XF_GLOW)))
        {

    	// Draw torch flares. KH
		// NOTE: It would be better if we batched these up.
		//       All those state changes are not nice. KH

		// This relies on unchanged game code!
		const int TORCH_STYLE = 1; // Flicker.
        const int MISSILE_STYLE = 6; // Flicker.
        const int PULSE_STYLE = 11; // Slow pulse

		vec3_t	lightorigin;	// Origin of torch.
		vec3_t	glow_vect;	    // Vector to torch.
		float	radius;			// Radius of torch flare.
		float	distance;		// Vector distance to torch.
		float	intensity;		// Intensity of torch flare.
        int     i,j;
        vec3_t	vp2;

		// NOTE: I don't think this is centered on the model.
		VectorCopy(currententity->origin, lightorigin);

		VectorSubtract(lightorigin, r_origin, vp2);

		// See if view is outside the light.
		distance = Length(glow_vect);
		
        // See if view is outside the light.
		distance = Length(vp2);

		if (clmodel->ex_flags & XF_MISSILE_GLOW)
		{
			radius = 9.0f;
			intensity = 1.00f;
		}
		else if(clmodel->ex_flags & XF_BIGMISS_GLOW)
		{
			radius = 16.0f;
			intensity = 0.5f;
		}
		else if (clmodel->ex_flags & XF_FADEMISS_GLOW)
		{
			radius = 32.0f;
			// Diminish torch flare inversely with distance.
			intensity = distance * 0.0008f;
			// Clamp, but don't let the flare disappear.
			if (intensity > 0.3f) intensity = 0.3f;
			if (intensity < 0.05f) intensity = 0.05f;
		}		
		else if (clmodel->ex_flags & XF_TORCH_GLOW )
		{
			radius = 7.0f + (distance * 0.022f);
			if (radius > 32.0f) radius = 32.0f;
			if (radius < 7.0f) radius = 7.0f;
			// Diminish torch flare inversely with distance.
			intensity = 0.40f - distance * 0.0005f;
			// Invert (fades as you approach).
			//intensity = (1.0f - intensity);
			// Clamp, but don't let the flare disappear.
			if (intensity > 0.40f) intensity = 0.40f;
			if (intensity < 0.16f) intensity = 0.16f;
		}
		else if (clmodel->ex_flags & XF_BIGTORCH_GLOW)
		{
			radius = 11.0f + (distance * 0.022f);
			if (radius > 44.0f) radius = 44.0f;
			if (radius < 11.0f) radius = 11.0f;
			// Diminish torch flare inversely with distance.
			intensity = 0.40f - distance * 0.0005f;
			// Invert (fades as you approach).
			//intensity = (1.0f - intensity);
			// Clamp, but don't let the flare disappear.
			if (intensity > 0.40f) intensity = 0.40f;
			if (intensity < 0.22f) intensity = 0.22f;
		}
		else
		{
			radius = 24.0f;
			intensity = distance * 0.001f;
			if (intensity > 0.35f) intensity = 0.35f;
			if (intensity < 0.05f) intensity = 0.05f;
		}

		if (distance > radius) 
            {
			VectorNormalize(vp2);

            //if (glowtexture > 0 && clmodel->ex_flags & XF_TORCH_GLOW)
            //    {
            //    GL_Bind (glowtexture);
            //    glEnable(GL_TEXTURE_2D);
            //    }

			glPushMatrix();

            // Translate the glow to coincide with the flame. KH
            if (clmodel->ex_flags & XF_TORCH_GLOW)
                {			
                // egypt torch fix
                if (!strnicmp (clmodel->name, "models/eflmtrch",15))
                    {
                    glTranslatef( cos(e->angles[1]/180*M_PI)*8.0f,sin(e->angles[1]/180*M_PI)*8.0f, 16.0f);
                    }
                else 
                    glTranslatef(0.0f, 0.0f, 8.0f);
                }

			// jeank add from Hammer of Tyrion
			// 'floating' movement
			if (clmodel->flags & EF_ROTATE)
				glTranslatef (0, 0, sin(currententity->origin[0]+currententity->origin[1]+(cl.time*3))*5.5);			
			//jeank
            
            glBegin(GL_TRIANGLE_FAN);

			// Now modulate with flicker.
			if (clmodel->ex_flags & XF_TORCH_GLOW || clmodel->ex_flags & XF_BIGTORCH_GLOW)
                {
                i = (int)(cl.time*10);
			    if (!cl_lightstyle[TORCH_STYLE].length) {
				    j = 256;
			        } 
                else {
				    j = i % cl_lightstyle[TORCH_STYLE].length;
				    j = cl_lightstyle[TORCH_STYLE].map[j] - 'a';
				    j = j*22;
			        }
                }
            else if (clmodel->ex_flags & XF_MISSILE_GLOW || clmodel->ex_flags & XF_BIGMISS_GLOW || clmodel->ex_flags & XF_FADEMISS_GLOW)
                {
                i = (int)(cl.time*10);
			    if (!cl_lightstyle[MISSILE_STYLE].length) {
				    j = 256;
			        } 
                else {
				    j = i % cl_lightstyle[MISSILE_STYLE].length;
				    j = cl_lightstyle[MISSILE_STYLE].map[j] - 'a';
				    j = j*22;
			        }
                }
            else if (clmodel->ex_flags & XF_GLOW)
                {
                i = (int)(cl.time*10);
			    if (!cl_lightstyle[PULSE_STYLE].length) {
				    j = 256;
			        } 
                else {
				    j = i % cl_lightstyle[PULSE_STYLE].length;
				    j = cl_lightstyle[PULSE_STYLE].map[j] - 'a';
				    j = j*22;
			        }
                }
                			
            intensity *= ((float)j / 255.0f);


			if (clmodel->ex_flags & XF_TORCH_GLOW || clmodel->ex_flags & XF_BIGTORCH_GLOW)
			{
                // Set yellow intensity
			    //glColor4f(0.8f*intensity, 0.4f*intensity, 0.1f*intensity,1.0f);
				glColor4f(0.9f*intensity, 0.35f*intensity, 0.1f*intensity,1.0f);
			}
			else 
			{
                glColor4f(clmodel->glow_color[0]*intensity, 
                          clmodel->glow_color[1]*intensity, 
                          clmodel->glow_color[2]*intensity,
                          0.5f);
			}
                
            for (i=0 ; i<3 ; i++)
				glow_vect[i] = lightorigin[i] - vp2[i]*radius;
			
            glVertex3fv(glow_vect);
            
            //if (glowtexture > 0 && clmodel->ex_flags & XF_TORCH_GLOW)
            //    glTexCoord2f (0.5,0.5);
			
            glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
			
            for (i=16; i>=0; i--) {
				float a = i/16.0f * M_PI*2;

				for (j=0; j<3; j++)	
                    glow_vect[j] = lightorigin[j] + 
						   vright[j]*cos(a)*radius +
					  	   vup[j]*sin(a)*radius;

				glVertex3fv(glow_vect);
                //if (glowtexture > 0 && clmodel->ex_flags & XF_TORCH_GLOW)
                //    glTexCoord2f (0.0,1.0);
			    }
			glEnd();

			
			glColor4f (0.0f,0.0f,0.0f,1.0f);

            // Restore previous matrix! KH
			glPopMatrix();		        
		    
            //if (glowtexture > 0 && clmodel->ex_flags & XF_TORCH_GLOW)
            //    {
            //    glDisable(GL_TEXTURE_2D);
            //    }

            }
	    }
    // end of glows    
}

void R_DrawAllGlows(void) {

    int i;


	if (!r_drawentities.value)
		return;

    if (gl_fogenabled)
        {
		glDisable(GL_FOG);
		}

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

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

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

		default:
			break;
		}		

	}

    if (gl_fogenabled)
        {
		glEnable(GL_FOG);
		}
            
    glDisable (GL_BLEND);
	glEnable (GL_TEXTURE_2D);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDepthMask (1);
    glShadeModel (GL_FLAT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	
}

/*
================
R_DrawTransEntitiesOnList
Implemented by: jack
================
*/

int transCompare(const void *arg1, const void *arg2 ) {
	const sortedent_t *a1, *a2;
	a1 = (sortedent_t *) arg1;
	a2 = (sortedent_t *) arg2;
	return (a2->len - a1->len); // Sorted in reverse order.  Neat, huh?
}

void R_DrawTransEntitiesOnList ( qboolean inwater) {
	int i;
	int numents;
	sortedent_t *theents;
	int depthMaskWrite = 0;
	vec3_t result;

	theents = (inwater) ? cl_transwateredicts : cl_transvisedicts;
	numents = (inwater) ? cl_numtranswateredicts : cl_numtransvisedicts;

	for (i=0; i<numents; i++) {
		VectorSubtract(
			theents[i].ent->origin, 
			r_origin, 
			result);
//		theents[i].len = Length(result);
		theents[i].len = (result[0] * result[0]) + (result[1] * result[1]) + (result[2] * result[2]);
	}

	qsort((void *) theents, numents, sizeof(sortedent_t), transCompare);
	// Add in BETTER sorting here

	glDepthMask(0);
	for (i=0;i<numents;i++) {
		currententity = theents[i].ent;

		switch (currententity->model->type)
		{
		case mod_alias:
			if (!depthMaskWrite) {
				depthMaskWrite = 1;
				glDepthMask(1);
			}
			R_DrawAliasModel (currententity);
			break;
		case mod_brush:
			if (!depthMaskWrite) {
				depthMaskWrite = 1;
				glDepthMask(1);
			}
			R_DrawBrushModel (currententity,true);
			break;
		case mod_sprite:
			if (depthMaskWrite) {
				depthMaskWrite = 0;
				glDepthMask(0);
			}
			R_DrawSpriteModel (currententity);
			break;
		}
	}
	if (!depthMaskWrite) 
		glDepthMask(1);
}

/*
============
R_PolyBlend
============
*/
//some code from LordHavoc early DP version
void R_PolyBlend (void)
{
	if (!gl_polyblend.value)
		return;

	//GL_DisableMultitexture();

	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	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

	if (v_blend[3])
	{
		glColor4fv (v_blend);

		glBegin (GL_QUADS);

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

	}

	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable (GL_BLEND); // muff
	glEnable (GL_TEXTURE_2D);
	glEnable (GL_ALPHA_TEST); //muff
}


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;

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

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

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

    // inwater = false;
	V_SetContentsColor (r_viewleaf->contents);
	V_CalcBlend ();

	r_cache_thrash = false;

	c_brush_polys = c_alias_polys = c_sky_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)
{
	extern	int glwidth, glheight;
	int		x, x2, y2, y, w, h;
	//jeank for FoV code
	float yfov;
	float screenaspect;
	float f;

	//
	// set up viewpoint
	//
	glMatrixMode(GL_PROJECTION);
    glLoadIdentity ();
	// JACK: Changes for non-scaled
	x = r_refdef.vrect.x * glwidth/vid.width /*320*/;
	x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width /*320*/;
	y = (vid.height/*200*/-r_refdef.vrect.y) * glheight/vid.height /*200*/;
	y2 = (vid.height/*200*/ - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height /*200*/;

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

	//jeank - old code
	//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 (yfov,  screenaspect,  4,  4096);

	//jeank
	//yfov = scr_fov.value;	//jeank - if repair culling may be used
	yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI;
	screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height;

	if ((r_viewleaf->contents == CONTENTS_WATER || r_viewleaf->contents == CONTENTS_LAVA || r_viewleaf->contents == CONTENTS_SLIME) && waterscreendeform.value >0)
	{
		f = sin(cl.time * 0.1 * (M_PI * 2.7));
		screenaspect = screenaspect - (1-f)*0.05*waterscreendeform.value;
		yfov = yfov - (1 + f)*waterscreendeform.value;
		MYgluPerspective (yfov,  screenaspect,  4,  4096);
	}
	else
	{
		MYgluPerspective (yfov,  screenaspect,  4,  4096);
	
	}
	//jeank

	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 ()
{
    //if (gl_multitexture && gl_mtexable) ... 

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

    R_DrawAllGlows();

	R_ExtraBumpPass ();

    R_RenderDlights ();
	
    
#if 0
	if (!Translucent)
	{
		Test_Draw ();
	}
#endif

	//glDepthMask( 1 );
}


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

    // Stencil shadows - MrG
    if (have_stencil == true && gl_stencilshadow.value > 0 && r_shadows.value > 0) {
	    glClearStencil(1);
	    glClear(GL_STENCIL_BUFFER_BIT);
        }

}

/*
=============
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 ();

	glDepthMask(0);

	R_DrawParticles ();
// THIS IS THE F*S*D(KCING MIRROR ROUTINE!  Go down!!!
	R_DrawTransEntitiesOnList( true ); // This restores the depth mask

	R_DrawWaterSurfaces ();

	R_DrawTransEntitiesOnList( false );

	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, true);
	cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL;
	glDisable (GL_BLEND);
	glColor4f (1,1,1,1);
}

/*
=============
R_PrintTimes
=============
*/
void R_PrintTimes(void)
{
	float r_time2;
	float ms, fps;

	r_lasttime1 = r_time2 = Sys_FloatTime();

	ms = 1000*(r_time2-r_time1);
	fps = 1000/ms;

	Con_Printf("%3.1f fps %5.0f ms\n%4i wpoly  %4i epoly %4i spoly\n",
		fps, ms, c_brush_polys, c_alias_polys, c_sky_polys);
}

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

r_refdef must be set before the first call
================
*/
void R_RenderView (void)
{
	if (r_norefresh.value)
		return;

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

	if (r_speeds.value)
	{
		glFinish ();
		if (r_wholeframe.value)
		   r_time1 = r_lasttime1;
	   else
		   r_time1 = Sys_FloatTime ();
		c_brush_polys = 0;
		c_alias_polys = 0;
	}

	mirror = false;

//	glFinish ();

	R_Clear ();

	//jeank removed fog code 2

	// render normal view
	R_RenderScene ();

	glDepthMask(0);

	R_DrawParticles ();

	R_DrawTransEntitiesOnList( r_viewleaf->contents == CONTENTS_EMPTY ); // This restores the depth mask

	R_DrawWaterSurfaces ();

	R_DrawTransEntitiesOnList( r_viewleaf->contents != CONTENTS_EMPTY );

	R_DrawViewModel();

	// render mirror view
	// R_Mirror ();

	R_PolyBlend ();

	R_BloomBlend ();

	if (r_speeds.value)
		R_PrintTimes ();

	glDepthMask(1);

}

/*
 * $Log: /H2 Mission Pack/gl_rmain.c $
 * 
 * 4     3/30/98 10:57a Jmonroe
 * 
 * 3     2/26/98 9:19p Jmonroe
 * shortened memory struct for sprites, added sprite orientation code in
 * gl (need to test)
 * 
 * 2     1/18/98 8:06p Jmonroe
 * all of rick's patch code is in now
 * 
 * 38    10/28/97 6:11p Jheitzman
 * 
 * 36    9/25/97 2:10p Rjohnson
 * Smaller status bar
 * 
 * 35    9/23/97 9:47p Rjohnson
 * Fix for dedicated gl server and color maps for sheeps
 * 
 * 34    9/15/97 1:26p Rjohnson
 * Fixed sprites
 * 
 * 33    9/09/97 5:24p Rjohnson
 * Play skin updates
 * 
 * 32    9/03/97 9:10a Rjohnson
 * Update
 * 
 * 31    8/31/97 9:27p Rjohnson
 * GL Updates
 * 
 * 30    8/21/97 11:31a Rjohnson
 * Fix for color map
 * 
 * 29    8/20/97 10:38p Rjohnson
 * Fix for view model drawing
 * 
 * 28    8/17/97 4:14p Rjohnson
 * Fix for model lighting
 * 
 * 27    8/15/97 3:10p Rjohnson
 * Precache Update
 * 
 * 26    8/06/97 2:59p Rjohnson
 * Fix for getting the light on the gl version
 * 
 * 25    7/24/97 5:31p Rjohnson
 * Updates to the gl version for color tinting
 * 
 * 24    6/17/97 10:03a Rjohnson
 * GL Updates
 * 
 * 23    6/16/97 5:47p Bgokey
 * 
 * 22    6/16/97 4:20p Rjohnson
 * Added a sky poly count
 * 
 * 21    6/16/97 5:28a Rjohnson
 * Minor fixes
 * 
 * 20    6/14/97 2:30p Rjohnson
 * Changed the defaults of some of the console variables
 * 
 * 19    6/14/97 1:59p Rjohnson
 * Updated the fps display in the gl size, as well as meshing directories
 * 
 * 18    5/31/97 3:43p Rjohnson
 * Fix for translucent water
 * 
 * 17    6/02/97 3:42p Gmctaggart
 * GL Catchup
 * 
 * 16    5/31/97 3:43p Rjohnson
 * Drawing items in the right order
 * 
 * 15    5/31/97 11:12a Rjohnson
 * GL Updates
 * 
 * 14    5/28/97 3:54p Rjohnson
 * Effect to make a model always face you
 * 
 * 13    5/15/97 6:34p Rjohnson
 * Code Cleanup
 * 
 * 12    4/17/97 2:38p Bgokey
 * 
 * 11    4/17/97 2:30p Bgokey
 * 
 * 10    4/17/97 12:15p Rjohnson
 * Fixed some compiling problems
 * 
 * 9     4/15/97 9:02p Bgokey
 * 
 * 8     3/25/97 12:48a Bgokey
 * 
 * 7     3/22/97 5:19p Rjohnson
 * Changed the stone drawing flag to just a generic way based upon the
 * skin number
 * 
 * 6     3/22/97 3:23p Rjohnson
 * Reversed a debugging test
 * 
 * 5     3/22/97 3:22p Rjohnson
 * Added the stone texture ability for models
 * 
 * 4     3/13/97 10:54p Rjohnson
 * Added support for transparent sprites
 * 
 * 3     3/13/97 12:24p Rjohnson
 * Added the dynamic scaling of models to the gl version
 */
