//-----------------------------------------------------------------------------
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
//

#include "gl_local.h"
#include "client.h"
#include "gl_sinmdl.h"
#include "qgl.h"
#include <ctype.h>

#ifdef __A3D_GEOM
void A3D_RenderGeometry (void *pA3D, void *pGeom, void *pMat, void *pGeomStatus);
#endif

extern refexport_t GetRefAPI (refimport_t rimp );

void R_Clear (void);
float ColorNormalize (vec3_t in, vec3_t out);
extern void MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
extern void HWGamma_Update (void); /* FS */
gl_viddef_t	vid;
refimport_t	ri;
model_t		*r_worldmodel;
float		gldepthmin, gldepthmax;
glconfig_t gl_config;
glstate_t gl_state;
int gl_majorver, gl_minorver;

image_t		*r_notexture;		// use for bad textures
image_t		*r_particletexture;	// little dot for particles
image_t		*r_specular;
image_t		*r_envmap;
image_t		*r_goldenvmap;
// 2015 code
image_t		*r_gogglesmap;

entity_t	*currententity;
model_t		*currentmodel;
int			r_visframecount;	// bumped when going to a new PVS
int			r_framecount;		// used for dlight push checking

cplane_t	frustum[4];

int			c_brush_polys, c_alias_polys;

float		v_blend[4];			// final blending color

void GL_Strings_f( void );

//
// view origin
//

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

//
// screen size info
//

refdef_t	r_newrefdef;
vec3_t	vup;
vec3_t	vpn;
vec3_t	vright;
vec3_t	r_origin;
int r_viewcluster;
int r_oldviewcluster;
cvar_t	*r_drawentities;
cvar_t	*r_drawworld;
cvar_t	*r_speeds;
cvar_t	*r_fullbright;
cvar_t	*r_novis;
cvar_t	*r_lerpmodels;
cvar_t	*r_lefthand;
cvar_t	*r_lightlevel;	// FIXME: This is a HACK to get the client's light level

int r_viewcluster2,r_oldviewcluster2;

cvar_t	*r_norefresh;
cvar_t	*r_nocull;

cvar_t	*gl_nosubimage;
cvar_t	*gl_allow_software;

cvar_t	*gl_vertex_arrays;

cvar_t	*gl_particle_min_size;
cvar_t	*gl_particle_max_size;
cvar_t	*gl_particle_size;
cvar_t	*gl_particle_att_a;
cvar_t	*gl_particle_att_b;
cvar_t	*gl_particle_att_c;

cvar_t	*gl_ext_swapinterval;
cvar_t	*gl_ext_multitexture;
cvar_t	*gl_ext_pointparameters;
cvar_t	*gl_ext_compiled_vertex_array;

cvar_t	*gl_log;
cvar_t	*gl_bitdepth;
cvar_t	*gl_drawbuffer;
cvar_t	*gl_lightmap;
cvar_t	*gl_shadows;
cvar_t	*gl_dynamic;
cvar_t	*gl_monolightmap;
cvar_t	*gl_modulate;
cvar_t	*gl_nobind;
cvar_t	*gl_round_down;
cvar_t	*gl_skymip;
cvar_t	*gl_showtris;
cvar_t	*gl_ztrick;
cvar_t	*gl_clear;
cvar_t	*gl_cull;
cvar_t	*gl_polyblend;
cvar_t	*gl_flashblend;
cvar_t	*gl_playermip;
cvar_t	*gl_saturatelighting;
cvar_t	*gl_swapinterval;
cvar_t	*gl_texturemode;
cvar_t	*gl_texturealphamode;
cvar_t	*gl_texturesolidmode;
cvar_t	*gl_lockpvs;

cvar_t	*vid_fullscreen;

cvar_t	*gl_drawflat;
cvar_t	*gl_draworder;
cvar_t	*gl_skintexturemode;
cvar_t	*gl_3dlabs_broken;

cvar_t	*gl_showportals;
cvar_t	*r_detailthresh;
cvar_t	*gl_showents;

//2015 code
cvar_t	*r_conchars;
cvar_t	*r_condist;
cvar_t	*r_conoffset;
cvar_t	*r_conthresh;
cvar_t	*r_detaildist;
cvar_t	*r_monolighting;
#ifndef REF_HARD_LINKED
cvar_t	*game2015;
#endif

/* FS */
cvar_t *gl_ignorehwgamma;
cvar_t *gl_displayrefresh;

#ifndef REF_HARD_LINKED
cvar_t *vid_gamma;
cvar_t *gl_driver;
cvar_t *gl_mode;
cvar_t *gl_picmip;
cvar_t *gl_finish;
cvar_t *gl_ext_palettedtexture;
cvar_t *vid_ref;
cvar_t *scr_shotname;
#endif

/* Knightmare */
cvar_t	*gl_render_immediate;
cvar_t	*gl_shadows_stencil;
cvar_t	*gl_bbox;
cvar_t	*gl_lightcutoff;

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

Returns true if the box is completely outside the frustom
=================
*/

#ifndef AMD3D
qboolean R_CullBox (vec3_t mins, vec3_t maxs)
{
	int		i;

	if (r_nocull->value)
		return false;

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


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

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

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

  SPRITE MODELS

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

/*
=================
R_DrawSpriteModel

=================
*/
void R_DrawSpriteModel (entity_t *e)
{
	vec3_t point;
	dsprframe_t *frame;
	int i;
	dsprite_t *psprite;
	// vec3_t tvec;
	float dot, angle, sr, cr;
	vec3_t svup, svright, svpn, origin; // in worldspace
	vec3_t scaled_svright, scaled_svup;
	float alpha=1.0f;
	int num_sprites=1;
	float yaw_inc;
	vec3_t angles;
	qboolean sprite_lefthanded;

	// don't even bother culling, because it's just a single
	// polygon without a surface cache
	psprite = (dsprite_t *)currentmodel->extradata;
	e->frame %= psprite->numframes;
	frame = &psprite->frames[e->frame];

	yaw_inc = 0;/* silence compiler */
	alpha = frame->alpha;
	VectorCopy( currententity->angles, angles );

	if (psprite->type == SPR_FACING_UPRIGHT)
	{
		// generate the sprite's axes, with vup straight up in worldspace, and
		// svright 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

		// FIXME - What is modelorg?
#if 0
		tvec[0] = -modelorg[0];
		tvec[1] = -modelorg[1];
		tvec[2] = -modelorg[2];
		VectorNormalize ( tvec );
		dot = tvec[2];  // same as DotProduct (tvec, svup) because
		                //  svup is 0, 0, 1
		if ((dot > 0.999848) || (dot < -0.999848))  // cos(1 degree) = 0.999848
			return;
		svup[0] = 0;
		svup[1] = 0;
		svup[2] = 1;
		svright[0] = tvec[1];
		// CrossProduct(svup, -modelorg,
		svright[1] = -tvec[0];
		//              svright)
		svright[2] = 0;
		VectorNormalize ( svright );
		svpn[0] = -svright[1];
		svpn[1] = svright[0];
		svpn[2] = 0;
		// CrossProduct (svright, svup,
		//  svpn)
#endif
	}
	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++)
		{
			svup[i] = vup[i];
			svright[i] = vright[i];
			svpn[i] = vpn[i];
		}
	}
	else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
	{
		// generate the sprite's axes, with vup straight up in worldspace, and
		// svright 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, svup) because
		                //  svup is 0, 0, 1
		if ((dot > 0.999848) || (dot < -0.999848))  // cos(1 degree) = 0.999848
			return;
		svup[0] = 0;
		svup[1] = 0;
		svup[2] = 1;
		svright[0] = vpn[1];
		// CrossProduct (svup, vpn,
		svright[1] = -vpn[0];   //  svright)
		svright[2] = 0;
		VectorNormalize ( svright );
		svpn[0] = -svright[1];
		svpn[1] = svright[0];
		svpn[2] = 0;
		// CrossProduct (svright, svup,
		//  svpn)
	} else if (psprite->type == SPR_ORIENTED) {
		// generate the sprite's axes, according to the sprite's world orientation
		AngleVectors ( currententity->angles, svpn, svright, svup );
	} else if (psprite->type == SPR_ORIENTED_CROSS) {
		// generate the sprite's axes, according to the sprite's world orientation
		AngleVectors ( currententity->angles, svpn, svright, svup );
		yaw_inc = 90;
		num_sprites = 2;
	} else if (psprite->type == SPR_ORIENTED_STAR) {
		// generate the sprite's axes, according to the sprite's world orientation
		AngleVectors ( currententity->angles, svpn, svright, svup );
		yaw_inc = 45;
		num_sprites = 4;
	} 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++) {
			svpn[i] = vpn[i];
			svright[i] = vright[i] * cr + vup[i] * sr;
			svup[i] = vright[i] * -sr + vup[i] * cr;
		}
	} else {
		Sys_Error ( "R_DrawSprite: Bad sprite type %d", psprite->type );
	}

	sprite_lefthanded = false;
	VectorCopy( e->origin, origin );
	if (currententity->flags & RF_WEAPONMODEL) {    // hack the depth range to prevent view model from poking into walls
		if ( r_lefthand->value == 2 )
			return;
		if ( r_lefthand->value == 1.0F )
			sprite_lefthanded = true;
		if ( e->flags & RF_XFLIP )
			sprite_lefthanded ^= true;
		if (sprite_lefthanded) {
			vec3_t delta;
			float len;
			VectorSubtract( e->origin, r_origin, delta );
			len = DotProduct( delta, vright );
			VectorMA( e->origin, -2*len, vright, origin );
		}
		qglDepthRange ( gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
	}

	if ( currentmodel->skins[e->frame] )
		GL_BindImage( currentmodel->skins[e->frame] );
	else
		GL_BindImage( r_notexture );

	if ( psprite->twosided ) {
		qglDisable( GL_CULL_FACE );
	}

	qglDepthMask ( 0 );
	qglEnable ( GL_BLEND );
	GL_TexEnv( GL_MODULATE );
	qglDisable( GL_ALPHA_TEST );

	if ( psprite->flags & SPRITE_OVERBRIGHT )
		qglBlendFunc ( GL_ONE, GL_ONE );
	else
		qglBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	if (e->flags & RF_TRANSLUCENT)
		alpha = e->alpha;

	if (e->flags & RF_LENSFLARE) {
		// Scale the colors by alpha, because we are adding instead of alpha blending
		e->color_r *= alpha;
		e->color_g *= alpha;
		e->color_b *= alpha;
		qglColor4f( e->color_r, e->color_g, e->color_b, alpha );
	} else if (psprite->flags & SPRITE_WORLDLIT) {
		vec3_t c;
		R_LightPoint( origin, c, true );
		qglColor4f( c[0],c[1],c[2],alpha );
	} else if (psprite->flags & SPRITE_MODULATE) {
		qglColor4f( e->color_r, e->color_g, e->color_b, alpha );
	} else {
		qglColor4f( 1,1,1, alpha );
	}

	if ( psprite->flags & SPRITE_HASBASE )
	{
		vec3_t up, right, forward;
		frame = &psprite->base;

		AngleVectors ( currententity->angles, up, right, forward );

		GL_BindImage( currentmodel->skins[ psprite->numframes ] );
		// Scale the sprite
		VectorScale( right, psprite->scale * e->scale, scaled_svright );
		VectorScale( up, psprite->scale * e->scale, scaled_svup );

		qglBegin ( GL_QUADS );

		qglTexCoord2f ( 0, 1 );
		VectorMA ( origin, -frame->origin_y, scaled_svup, point );
		VectorMA ( point, -frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );

		qglTexCoord2f ( 0, 0 );
		VectorMA ( origin, frame->height - frame->origin_y, scaled_svup, point );
		VectorMA ( point, -frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );

		qglTexCoord2f ( 1, 0 );
		VectorMA ( origin, frame->height - frame->origin_y, scaled_svup, point );
		VectorMA ( point, frame->width - frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );

		qglTexCoord2f ( 1, 1 );
		VectorMA ( origin, -frame->origin_y, scaled_svup, point );
		VectorMA ( point, frame->width - frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );

		qglEnd ();

		frame = &psprite->frames[e->frame];
		GL_BindImage( currentmodel->skins[e->frame] );
	}

	qglBegin ( GL_QUADS );

	for (i=0; i<num_sprites; i++)
	{
		if (i > 0)
		{
			if ( yaw_inc == 90 )
			{
				vec3_t swap;
				VectorCopy( svpn, swap );
				VectorCopy( svright, svpn );
				VectorCopy( swap, svright );
			}
			else
			{
				angles[ YAW ] += yaw_inc;
				while ( angles[ YAW ] >= 360 )
					angles[ YAW ] -= 360;
				AngleVectors( angles, svpn, svright, svup );
			}
		}

		// Scale the sprite
		VectorScale( svright, psprite->scale * e->scale, scaled_svright );
		VectorScale( svup, psprite->scale * e->scale, scaled_svup );

		qglTexCoord2f ( 0, 1 );
		VectorMA ( origin, -frame->origin_y, scaled_svup, point );
		VectorMA ( point, -frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );

		qglTexCoord2f ( 0, 0 );
		VectorMA ( origin, frame->height - frame->origin_y, scaled_svup, point );
		VectorMA ( point, -frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );

		qglTexCoord2f ( 1, 0 );
		VectorMA ( origin, frame->height - frame->origin_y, scaled_svup, point );
		VectorMA ( point, frame->width - frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );

		qglTexCoord2f ( 1, 1 );
		VectorMA ( origin, -frame->origin_y, scaled_svup, point );
		VectorMA ( point, frame->width - frame->origin_x, scaled_svright, point );
		qglVertex3fv ( point );
	}
	qglEnd ();

	qglDepthMask ( 1 );
	qglBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	qglDisable ( GL_BLEND );
	GL_TexEnv( GL_REPLACE );
	qglDisable ( GL_ALPHA_TEST );
	qglColor4f( 1, 1, 1, 1 );

	if (currententity->flags & RF_WEAPONMODEL) // hack the depth range to prevent view model from poking into walls
		qglDepthRange ( gldepthmin, gldepthmax );

	if ( psprite->twosided )
	{
		qglEnable( GL_CULL_FACE );
	}
}

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

/*
=============
R_DrawNullModel
=============
*/
static void R_DrawNullModel (void)
{
	vec3_t	shadelight;
	int		i;

	if ( currententity->flags & RF_FULLBRIGHT )
		shadelight[0] = shadelight[1] = shadelight[2] = 1.0F;
	else
		R_LightPoint (currententity->origin, shadelight, true);

	qglPushMatrix ();
	R_RotateForEntity (currententity);

	qglDisable (GL_TEXTURE_2D);
	qglColor3fv (shadelight);

	qglBegin (GL_TRIANGLE_FAN);
	qglVertex3f (0, 0, -16);
	for (i=0 ; i<=4 ; i++)
		qglVertex3f (16*cos(i*M_PI/2), 16*sin(i*M_PI/2), 0);
	qglEnd ();

	qglBegin (GL_TRIANGLE_FAN);
	qglVertex3f (0, 0, 16);
	for (i=4 ; i>=0 ; i--)
		qglVertex3f (16*cos(i*M_PI/2), 16*sin(i*M_PI/2), 0);
	qglEnd ();

	qglColor3f (1,1,1);
	qglPopMatrix ();
	qglEnable (GL_TEXTURE_2D);
}

/*
=============
R_DrawEntitiesOnList
=============
*/
static void R_DrawEntitiesOnList (void)
{
	int i;
	int j;
	int entlist[MAX_ENTITIES];
	int numents;
	int farthest = -1;
	entity_t *testent;

	if (!r_drawentities->value || (r_newrefdef.rdflags & RDF_NOENTITIES))
		return;

	// draw non-transparent first
	for (i=0 ; i<r_newrefdef.num_entities ; i++)
	{
		currententity = &r_newrefdef.entities[i];

		if (currententity->flags & RF_DONTDRAW)
			continue;
		if ( currententity->flags & RF_LENSFLARE )
			continue;
		if ( currententity->flags & RF_WEAPONMODEL )
			continue;
		if ( currententity->model && currententity->model->type == mod_sprite )
			continue;
		if ( currententity->flags & RF_DETAIL )
		{
			float dist, delta;
			vec3_t diff;

			VectorSubtract( currententity->origin, r_newrefdef.vieworg, diff );

			dist  = DotProduct( diff,diff );
			dist *= r_newrefdef.fovfactor;

			if ( dist > r_detaildist->value )
			{
				delta = fabs(r_detaildist->value - dist);

				if (delta < r_detailthresh->value)
				{
					currententity->alpha *= ( 1.0f - delta / r_detailthresh->value );
					if ( currententity->alpha < 1 )
						currententity->flags |= RF_TRANSLUCENT;
				}
				else
					continue;
			}
		}

		if (currententity->flags & RF_TRANSLUCENT)
			continue;	// solid

		if ( currententity->flags & RF_BEAM )
		{
			R_DrawBeam( currententity );
		}
		else
		{
			currentmodel = currententity->model;
			if (!currentmodel)
			{
				R_DrawNullModel ();
				continue;
			}
			switch (currentmodel->type)
			{
			case mod_alias:
				R_DrawAliasModel (currententity);
				break;
			case mod_brush:
				R_DrawBrushModel (currententity);
				break;
			case mod_sprite:
				R_DrawSpriteModel (currententity);
				break;
			case mod_sinmdl:
				SINMDL_DrawModel (currententity);
				break;
			default:
				ri.Sys_Error (ERR_DROP, "Bad modeltype");
				break;
			}
		}
	}

	// draw transparent entities
	// we could sort these if it ever becomes a problem...
	qglDepthMask (0);		// no z writes
	numents = 0;
	for (i=0 ; i<r_newrefdef.num_entities ; i++)
	{
		vec3_t diff;
		currententity = &r_newrefdef.entities[i];

		if (currententity->flags & RF_DONTDRAW)
			continue;

		if (currententity->flags & RF_DONTDRAW)
			continue;

		if ( currententity->flags & RF_DETAIL )
		{
			float dist, delta;
			vec3_t diff;

			VectorSubtract( currententity->origin, r_newrefdef.vieworg, diff );

			dist =  DotProduct( diff,diff );
			dist *= r_newrefdef.fovfactor;

			if ( dist > r_detaildist->value )
			{
				delta = fabs(r_detaildist->value - dist);

				if (delta < r_detailthresh->value)
				{
					currententity->alpha *= ( 1.0f - delta / r_detailthresh->value );
					currententity->flags |= RF_TRANSLUCENT;
				}
				else
				{
					continue;
				}
			}
		}

		if ( currententity->flags & RF_LENSFLARE )
			continue;

		if (( currententity->model) &&
			( currententity->model->type != mod_sprite ) &&
			(!(currententity->flags & RF_TRANSLUCENT)))
		{
				continue;	// solid
		}

		entlist[numents++] = i;
		VectorSubtract( currententity->origin, r_origin, diff );
		currententity->radius = VectorLength( diff );
		currententity->gravaxis = 0;
	}

	for (i=0;i<numents;i++)
	{
		farthest = -1;

		for (j=0;j<numents;j++)
		{
			testent = &r_newrefdef.entities[entlist[j]];

			if ( testent->gravaxis < 0 )
				continue;

			if ( farthest < 0 )
			{
				farthest = j;
				currententity = testent;
			}
			else
			{
				if (testent->radius > currententity->radius)
				{
					farthest = j;
					currententity = testent;
				}
			}
		}

		if ( currententity->flags & RF_BEAM )
		{
			R_DrawBeam( currententity );
		}
		else
		{
			currentmodel = currententity->model;

			if (!currentmodel)
			{
				R_DrawNullModel ();
				continue;
			}

			switch (currentmodel->type)
			{
			case mod_alias:
				R_DrawAliasModel (currententity);
				break;
			case mod_brush:
				R_DrawBrushModel (currententity);
				break;
			case mod_sprite:
				R_DrawSpriteModel (currententity);
				break;
			case mod_sinmdl:
				SINMDL_DrawModel (currententity);
				break;
			default:
				ri.Sys_Error (ERR_DROP, "Bad modeltype");
				break;
			}
		}
		currententity->gravaxis = -1;
	}

	qglDepthMask (1);		// back to writing
}

/*
=============
R_DrawLensFlare
=============
*/
static void R_DrawLensFlare (void)
{
	int i;

	if (!r_drawentities->value || (r_newrefdef.rdflags & RDF_NOENTITIES))
		return;

	qglDepthFunc ( GL_ALWAYS );
	for (i=0; i<r_newrefdef.num_entities; i++)
	{
		currententity = &r_newrefdef.entities[i];
		if ( currententity->flags & RF_LENSFLARE )
		{
			currentmodel = currententity->model;
			if (!currentmodel)
			{
				R_DrawNullModel ();
				continue;
			}
			if (currentmodel->type == mod_sprite)
			{
				if ( currententity->skinnum != 255 )
				{
					int lt = currententity->skinnum;
					if ( lt < 0 )
					{
						lt = 0;
					}
					else if ( lt >= MAX_LIGHTSTYLES )
					{
						lt = 0;
					}

					currententity->color_r = r_newrefdef.lightstyles[ lt ].rgb[ 0 ] * 0.5;
					currententity->color_g = r_newrefdef.lightstyles[ lt ].rgb[ 1 ] * 0.5;
					currententity->color_b = r_newrefdef.lightstyles[ lt ].rgb[ 2 ] * 0.5;
				}
				R_DrawSpriteModel ( currententity );
			}
		}
	}
	if (gl_ztrick->value)
	{
		if (ztrickframe & 1)
		{
			qglDepthFunc ( GL_LEQUAL );
		}
		else
		{
			qglDepthFunc ( GL_GEQUAL );
		}
	}
	else
		qglDepthFunc ( GL_LEQUAL );
}


/*
=================
R_DrawWeaponModel
=================
*/
static void R_DrawWeaponModel (void)
{
	int i;

	if (!r_drawentities->value || (r_newrefdef.rdflags & RDF_NOENTITIES))
		return;

	for (i=0; i<r_newrefdef.num_entities; i++)
	{
		currententity = &r_newrefdef.entities[i];
		if ( currententity->flags & RF_WEAPONMODEL )
		{
			currentmodel = currententity->model;

			if (!currentmodel)
			{
				R_DrawNullModel ();
				continue;
			}

			switch (currentmodel->type)
			{
			case mod_alias:
				R_DrawAliasModel ( currententity );
				break;
			case mod_brush:
				R_DrawBrushModel ( currententity );
				break;
			case mod_sprite:
				R_DrawSpriteModel ( currententity );
				break;
			case mod_sinmdl:
				SINMDL_DrawModel ( currententity );
				break;
			default:
				ri.Sys_Error ( ERR_DROP, "Bad modeltype" );
				break;
			}
		}
	}
}

/*
=============
R_DrawDebugLines
=============
*/

static void R_DrawDebugLines(void)
{
	debugline_t *line;
	int i;

	if ( !ri.DebugLines || !ri.numDebugLines )
	{
		return;
	}

	qglDisable ( GL_TEXTURE_2D );
	qglEnable ( GL_BLEND );

	qglBegin ( GL_LINES );

	line = *ri.DebugLines;
	for( i = *ri.numDebugLines; i > 0; i--, line++ )
	{
		qglColor4f( line->color[ 0 ], line->color[ 1 ], line->color[ 2 ], line->alpha );

		qglVertex3fv ( line->start );
		qglVertex3fv ( line->end );
	}

	qglEnd ();

	qglDisable( GL_BLEND );
	qglEnable ( GL_TEXTURE_2D );
}

// had to comment out the particle code in assembly, it sucks ass
//#ifndef AMD3D

/*
** GL_DrawParticles
**
*/
void GL_DrawParticles( int num_particles, const particle_t particles[] )
{
	const particle_t *p;
	int i;
	vec3_t up, right;
	float scale;
	vec3_t tmpup, tmpright;

	GL_BindImage( r_particletexture );
	qglDepthMask( GL_FALSE );	// no z buffering
	qglEnable( GL_BLEND );
	GL_TexEnv( GL_MODULATE );

	// Do the overbright particles first
	qglBlendFunc( GL_ONE, GL_ONE );
	qglBegin( GL_TRIANGLES );

	VectorScale ( vup, 2.5, up );
	VectorScale ( vright, 2.5, right );

	for ( p = particles, i=0; i < num_particles; i++,p++) {
		if(!(p->flags & (PARTICLE_OVERBRIGHT|PARTICLE_FIRE|PARTICLE_ADDITIVE)))
			continue;

		// don't render flame particles that are closer than 32 units
		if(p->flags & PARTICLE_FIRE) {
			VectorSubtract(p->origin, r_origin, tmpup);
			scale = VectorLength(tmpup);
			if(scale < 40)
				continue;
		}

		// hack a scale up to keep particles from disapearing
		scale = ( p->origin[0] - r_origin[0] ) * vpn[0] +
			( p->origin[1] - r_origin[1] ) * vpn[1] +
			( p->origin[2] - r_origin[2] ) * vpn[2];

		// added variable sizes
		if(!p->size) { // partizle size 0 indicated a regular particle
			if (scale < 20)
				scale = 1;
			else
				scale = 1 + scale * 0.004;

			qglColor4fv( p->color );

			qglTexCoord2f( 0.0625, 0.0625 );
			qglVertex3fv( p->origin );

			qglTexCoord2f( 1.0625, 0.0625 );
			qglVertex3f( p->origin[0] + up[0]*scale,
				p->origin[1] + up[1]*scale,
				p->origin[2] + up[2]*scale);

			qglTexCoord2f( 0.0625, 1.0625 );
			qglVertex3f( p->origin[0] + right[0]*scale,
				p->origin[1] + right[1]*scale,
				p->origin[2] + right[2]*scale);

			continue;
		}

		// particles using the added effects are handled below
		if(p->size > 1)
			scale /= p->size;

		if (scale < 20)
			scale = p->size;
		else
			scale = p->size + scale * 0.004;

		qglColor4fv( p->color );

		//calc new up and right vectors based on 1/4 the particle's scale
		// this is to reduce the number of multiplies later
		VectorScale(up, (scale*0.25), tmpup);
		VectorScale(right, (scale*0.25), tmpright);

		// added flame type particles & changed the particle's
		// vertex positions to center around the origin
		if(p->flags & PARTICLE_FIRE) {
			// randomly flip the particles back and forth a bit
			// to break up their regularity
			if ((int)rand()%5)
			{
				qglTexCoord2f(1.03125, 1.03125);
				qglVertex3f( p->origin[0] - tmpup[0] + tmpright[0],
					     p->origin[1] - tmpup[1] + tmpright[1],
					     p->origin[2] - tmpup[2] + tmpright[2]);

				qglTexCoord2f(0.03125, 1.03125);
				qglVertex3f( p->origin[0] - tmpup[0] - tmpright[0]*3,
					     p->origin[1] - tmpup[1] - tmpright[1]*3,
					     p->origin[2] - tmpup[2] - tmpright[2]*3);

				qglTexCoord2f(1.03125, 0.03125);
				qglVertex3f( p->origin[0] + tmpup[0]*3 + tmpright[0],
					     p->origin[1] + tmpup[1]*3 + tmpright[1],
					     p->origin[2] + tmpup[2]*3 + tmpright[2]);
			}
			else
			{
				qglTexCoord2f(1.03125, 1.03125);
				qglVertex3f( p->origin[0] - tmpup[0] - tmpright[0],
					     p->origin[1] - tmpup[1] - tmpright[1],
					     p->origin[2] - tmpup[2] - tmpright[2]);

				qglTexCoord2f(1.03125, 0.03125);
				qglVertex3f( p->origin[0] + tmpup[0]*3 - tmpright[0],
					     p->origin[1] + tmpup[1]*3 - tmpright[1],
					     p->origin[2] + tmpup[2]*3 - tmpright[2]);

				qglTexCoord2f(0.03125, 1.03125);
				qglVertex3f( p->origin[0] - tmpup[0] + tmpright[0]*3,
					     p->origin[1] - tmpup[1] + tmpright[1]*3,
					     p->origin[2] - tmpup[2] + tmpright[2]*3);
			}
		}
		else
		{
			qglTexCoord2f( 0.0625, 0.0625 );
			qglVertex3f( p->origin[0] - tmpup[0] - tmpright[0],
				     p->origin[1] - tmpup[1] - tmpright[1],
				     p->origin[2] - tmpup[2] - tmpright[2]);

			qglTexCoord2f( 1.0625, 0.0625 );
			qglVertex3f( p->origin[0] + tmpup[0]*3 - tmpright[0],
				     p->origin[1] + tmpup[1]*3 - tmpright[1],
				     p->origin[2] + tmpup[2]*3 - tmpright[2]);

			qglTexCoord2f( 0.0625, 1.0625 );
			qglVertex3f( p->origin[0] - tmpup[0] + tmpright[0]*3,
				     p->origin[1] - tmpup[1] + tmpright[1]*3,
				     p->origin[2] - tmpup[2] + tmpright[2]*3);
		}
	}
	qglEnd ();

	// Now do the regular particles
	qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	qglBegin( GL_TRIANGLES );

	VectorScale (vup, 2.5, up);
	VectorScale (vright, 2.5, right);

	for ( p = particles, i=0 ; i < num_particles ; i++,p++)
	{
		// had to add this to not draw fire particles twice
		if(p->flags & (PARTICLE_OVERBRIGHT|PARTICLE_FIRE|PARTICLE_ADDITIVE))
			continue;

		// hack a scale up to keep particles from disapearing
		scale = ( p->origin[0] - r_origin[0] ) * vpn[0] +
			( p->origin[1] - r_origin[1] ) * vpn[1] +
			( p->origin[2] - r_origin[2] ) * vpn[2];

		// added variable sizes
		if(!p->size) // partizle size 0 indicated a regular particle
		{
			if (scale < 20)
				scale = 1;
			else
				scale = 1 + scale * 0.004;

			qglColor4fv( p->color );

			qglTexCoord2f( 0.0625, 0.0625 );
			qglVertex3fv( p->origin );

			qglTexCoord2f( 1.0625, 0.0625 );
			qglVertex3f( p->origin[0] + up[0]*scale,
				     p->origin[1] + up[1]*scale,
				     p->origin[2] + up[2]*scale);

			qglTexCoord2f( 0.0625, 1.0625 );
			qglVertex3f( p->origin[0] + right[0]*scale,
				     p->origin[1] + right[1]*scale,
				     p->origin[2] + right[2]*scale);

			continue;
		}

		// particles using the added effects are handled below
		if(p->size > 0)
			scale /= p->size;

		if (scale < 20)
			scale = p->size;
		else
			scale = p->size + scale * 0.004;

		qglColor4fv( p->color );

		//calc new up and right vectors based on 1/4 the particle's scale
		// this is to reduce the number of multiplies later
		VectorScale(up, (scale*0.25), tmpup);
		VectorScale(right, (scale*0.25), tmpright);

		// added flame type particles & changed the particle's
		// vertex positions to center around the origin
		qglTexCoord2f( 0.0625, 0.0625 );
		qglVertex3f( p->origin[0] - tmpup[0] - tmpright[0],
			     p->origin[1] - tmpup[1] - tmpright[1],
			     p->origin[2] - tmpup[2] - tmpright[2]);

		qglTexCoord2f( 1.0625, 0.0625 );
		qglVertex3f( p->origin[0] + tmpup[0]*3 - tmpright[0],
			     p->origin[1] + tmpup[1]*3 - tmpright[1],
			     p->origin[2] + tmpup[2]*3 - tmpright[2]);

		qglTexCoord2f( 0.0625, 1.0625 );
		qglVertex3f( p->origin[0] - tmpup[0] + tmpright[0]*3,
			     p->origin[1] - tmpup[1] + tmpright[1]*3,
			     p->origin[2] - tmpup[2] + tmpright[2]*3);
	}
	qglEnd ();

	qglDisable( GL_BLEND );
	qglColor4f( 1,1,1,1 );
	qglDepthMask( 1 ); // back to normal Z buffering
	GL_TexEnv( GL_REPLACE );
}

/*
===============
R_DrawParticles
===============
*/
static void R_DrawParticles (void)
{
	if ( !r_newrefdef.num_particles )
		return;

	if ( gl_ext_pointparameters->value && qglPointParameterfEXT && ( !game2015->value ) ) {
		int i;
		const particle_t *p;
	
		qglDepthMask( GL_FALSE );
		qglEnable( GL_BLEND );
		qglDisable( GL_TEXTURE_2D );
	
		qglPointSize( gl_particle_size->value );
	
		qglBegin( GL_POINTS );
		for ( i = 0, p = r_newrefdef.particles; i < r_newrefdef.num_particles; i++, p++ ) {
			qglColor4fv( p->color );
			qglVertex3fv( p->origin );
		}
		qglEnd();
		
		qglDisable( GL_BLEND );
		qglColor4f( 1.0F, 1.0F, 1.0F, 1.0F );
		qglDepthMask( GL_TRUE );
		qglEnable( GL_TEXTURE_2D );
	
	} else {
		GL_DrawParticles( r_newrefdef.num_particles, r_newrefdef.particles );
	}
}

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

	qglDisable (GL_ALPHA_TEST);
	qglEnable (GL_BLEND);
	qglDisable (GL_DEPTH_TEST);
	qglDisable (GL_TEXTURE_2D);

	qglLoadIdentity ();

	// FIXME: get rid of these
	qglRotatef (-90, 1, 0, 0);	// put Z going up
	qglRotatef ( 90, 0, 0, 1);	// put Z going up

	qglColor4fv (v_blend);

	qglBegin (GL_QUADS);

	qglVertex3f (10, 100, 100);
	qglVertex3f (10, -100, 100);
	qglVertex3f (10, -100, -100);
	qglVertex3f (10, 100, -100);
	qglEnd ();

	qglDisable (GL_BLEND);
	qglEnable (GL_TEXTURE_2D);
	qglEnable (GL_ALPHA_TEST);

	qglColor4f(1,1,1,1);
}

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

static int SignbitsForPlane (cplane_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;
}


static void R_SetFrustum (void)
{
	int		i;

	// rotate VPN right by FOV_X/2 degrees
	RotatePointAroundVector( frustum[0].normal, vup, vpn, -(90-r_newrefdef.fov_x / 2 ) );
	// rotate VPN left by FOV_X/2 degrees
	RotatePointAroundVector( frustum[1].normal, vup, vpn, 90-r_newrefdef.fov_x / 2 );
	// rotate VPN up by FOV_X/2 degrees
	RotatePointAroundVector( frustum[2].normal, vright, vpn, 90-r_newrefdef.fov_y / 2 );
	// rotate VPN down by FOV_X/2 degrees
	RotatePointAroundVector( frustum[3].normal, vright, vpn, -( 90 - r_newrefdef.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
===============
*/
static void R_SetupFrame (void)
{
	int i;
	mleaf_t	*leaf;

	r_framecount++;

// build the transformation matrix for the given view angles
	VectorCopy (r_newrefdef.vieworg, r_origin);

	AngleVectors (r_newrefdef.viewangles, vpn, vright, vup);

// current viewcluster
	if ( !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) )
	{
		r_oldviewcluster = r_viewcluster;
		r_oldviewcluster2 = r_viewcluster2;
		leaf = Mod_PointInLeaf (r_origin, r_worldmodel);
		r_viewcluster = r_viewcluster2 = leaf->cluster;

		// check above and below so crossing solid water doesn't draw wrong
		if (!leaf->contents)
		{	// look down a bit
			vec3_t	temp;

			VectorCopy (r_origin, temp);
			temp[2] -= 16;
			leaf = Mod_PointInLeaf (temp, r_worldmodel);
			if ( !(leaf->contents & CONTENTS_SOLID) &&
					(leaf->cluster != r_viewcluster2) )
				r_viewcluster2 = leaf->cluster;
		}
		else
		{	// look up a bit
			vec3_t	temp;

			VectorCopy (r_origin, temp);
			temp[2] += 16;
			leaf = Mod_PointInLeaf (temp, r_worldmodel);
			if ( !(leaf->contents & CONTENTS_SOLID) &&
					(leaf->cluster != r_viewcluster2) )
				r_viewcluster2 = leaf->cluster;
		}
	}

	for (i=0 ; i<4 ; i++)
		v_blend[i] = r_newrefdef.blend[i];

	c_brush_polys = 0;
	c_alias_polys = 0;

	// clear out the portion of the screen that the NOWORLDMODEL defines
	if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL )
	{
		qglEnable( GL_SCISSOR_TEST );
		qglClearColor( 0.3, 0.3, 0.3, 1 );
		qglScissor( r_newrefdef.x, vid.height - r_newrefdef.height - r_newrefdef.y, r_newrefdef.width, r_newrefdef.height );
		if ( r_newrefdef.rdflags & RDF_NOCLEAR )
		{
			qglClear( GL_DEPTH_BUFFER_BIT );
		}
		else
		{
			qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
		}
		qglClearColor( 1, 0, 0.5, 0.5 );
		qglDisable( GL_SCISSOR_TEST );
	}
}


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;

	xmin += -( 2 * gl_state.camera_separation ) / zNear;
	xmax += -( 2 * gl_state.camera_separation ) / zNear;

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


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

	//
	// set up viewport
	//
	x = floor((float)(r_newrefdef.x * vid.width / vid.width));
	x2 = ceil((float)((r_newrefdef.x + r_newrefdef.width) * vid.width / vid.width));
	y = floor((float)(vid.height - r_newrefdef.y * vid.height / vid.height));
	y2 = ceil((float)(vid.height - (r_newrefdef.y + r_newrefdef.height) * vid.height / vid.height));

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

	qglViewport (x, y2, w, h);

	//
	// set up projection matrix
	//
	screenaspect = (float)r_newrefdef.width/r_newrefdef.height;
//	yfov = 2*atan((float)r_newrefdef.height/r_newrefdef.width)*180/M_PI;
	qglMatrixMode(GL_PROJECTION);
	qglLoadIdentity ();
	MYgluPerspective (r_newrefdef.fov_y, screenaspect, 4, 7000);

	qglCullFace(GL_FRONT);

	qglMatrixMode(GL_MODELVIEW);
	qglLoadIdentity ();

	qglRotatef (-90, 1, 0, 0);	// put Z going up
	qglRotatef ( 90, 0, 0, 1);	// put Z going up
	qglRotatef (-r_newrefdef.viewangles[2], 1, 0, 0);
	qglRotatef (-r_newrefdef.viewangles[0], 0, 1, 0);
	qglRotatef (-r_newrefdef.viewangles[1], 0, 0, 1);
	qglTranslatef (-r_newrefdef.vieworg[0], -r_newrefdef.vieworg[1], -r_newrefdef.vieworg[2]);

//	if ( gl_state.camera_separation != 0 && gl_state.stereo_enabled )
//		qglTranslatef ( gl_state.camera_separation, 0, 0 );

	qglGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix);

	//
	// set drawing parms
	//
	if (gl_cull->value)
		qglEnable(GL_CULL_FACE);
	else
		qglDisable(GL_CULL_FACE);

	qglDisable(GL_BLEND);
	qglDisable(GL_ALPHA_TEST);
	qglEnable(GL_DEPTH_TEST);
}

/*
=============
R_Clear
=============
*/
int ztrickframe;

void R_Clear (void)
{
	GLbitfield clearbits = 0;	/* Knightmare added */

	if (gl_draworder->value)
	{
		if (gl_clear->value)
			clearbits |= (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		else
			clearbits |= (GL_DEPTH_BUFFER_BIT);
		gldepthmin = 1;
		gldepthmax = 0;
		qglDepthFunc (GL_LEQUAL);
	}
	else if (gl_ztrick->value)
	{
		if (gl_clear->value)
			clearbits |=  GL_COLOR_BUFFER_BIT;

		ztrickframe++;
		if (ztrickframe & 1)
		{
			gldepthmin = 0;
			gldepthmax = 0.49999;
			qglDepthFunc (GL_LEQUAL);
		}
		else
		{
			gldepthmin = 1;
			gldepthmax = 0.5;
			qglDepthFunc (GL_GEQUAL);
		}
	}
	else
	{
		if (gl_clear->value)
			clearbits |=  (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	/* Knightmare changed */
		else
			clearbits |=  GL_DEPTH_BUFFER_BIT;	/* Knightmare changed */
		gldepthmin = 0;
		gldepthmax = 1;
		qglDepthFunc (GL_LEQUAL);
	}

	qglDepthRange (gldepthmin, gldepthmax);

	/* Knightmare- stencil buffer */
	if (gl_config.have_stencil)
	{
		qglClearStencil(1);
		clearbits |=  GL_STENCIL_BUFFER_BIT;	/* Knightmare changed */
	}

	if (clearbits)
		qglClear(clearbits);
	/* end Knigthmare */
}

static void R_Flash( void )
{
	R_PolyBlend ();
}

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

r_newrefdef must be set before the first call
================
*/
void R_RenderView (refdef_t *fd)
{
	entity_t	ent;

	if (r_norefresh->value)
		return;

	// R_PushDlights depends on currententity being set, so create a fake entity
	memset (&ent, 0, sizeof(ent));
	ent.frame = (int)(r_newrefdef.time*2);
	currententity = &ent;

	r_newrefdef = *fd;

	if (!r_worldmodel && !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) )
		ri.Sys_Error (ERR_DROP, "R_RenderView: NULL worldmodel");

	r_newrefdef.fovfactor = r_newrefdef.fov_x / 90.0f;

	if ( gl_showents->value )
	{
		ri.Con_Printf (PRINT_ALL, "time %6.2f ------------------------------------------------------------\n", fd->time );
	}

	if (r_speeds->value) {
		c_brush_polys = 0;
		c_alias_polys = 0;
	}
	R_PushDlights ();
	
	if (gl_finish->value) {
		qglFinish ();
	}
	R_SetupFrame ();
	R_SetFrustum ();
	R_SetupGL ();
	R_MarkLeaves ();	// done here so we know if we're in water
	R_DrawWorld ();
	R_DrawEntitiesOnList ();
	R_RenderDlights ();
	R_DrawParticles ();
	R_DrawAlphaSurfaces ();
	R_DrawTriangles ();
	R_DrawLensFlare ();
	R_DrawWeaponModel ();
	R_DrawDebugLines();
	GL_DrawPortals();
	R_Flash();

	if (r_speeds->value)
	{
		static int			fpsindex = 0;
		static double		fpstimes[16] = {0};
		static double		fpstotal = 0;
		static double		fpslasttime = 0;
		double fpstime;

		fpstime   = Sys_Milliseconds() / 1000.0f;
		fpstotal -= fpstimes[ fpsindex ];
		fpstimes[ fpsindex ] = fpstime - fpslasttime;
		fpstotal += fpstimes[ fpsindex ];
		fpsindex  = ( fpsindex + 1 ) & 15;
		fpslasttime = fpstime;

		ri.SCR_CenterPrintf ("%4i wpoly %4i epoly%2i tex%2i lmap %4.1f fps\n",
			c_brush_polys,
			c_alias_polys,
			c_visible_textures,
			c_visible_lightmaps,
			16.0f / fpstotal
		);
	}
}


static void	R_SetGL2D (void)
{
	// set 2D virtual screen size
	qglViewport (0,0, vid.width, vid.height);
	qglMatrixMode(GL_PROJECTION);
	qglLoadIdentity ();
	qglOrtho (0, vid.width, 0, vid.height, -99999, 99999);
	qglMatrixMode(GL_MODELVIEW);
	qglLoadIdentity ();
	qglDisable (GL_DEPTH_TEST);
	qglDisable (GL_CULL_FACE);
	qglDisable (GL_BLEND);
	qglEnable (GL_ALPHA_TEST);
	qglColor4f (1,1,1,1);
	consoleInfo.surface_width  = vid.width;
	consoleInfo.surface_height = vid.height;
	consoleInfo.u[0] = 1; consoleInfo.u[1] = 0; consoleInfo.u[2] = 0;
	consoleInfo.v[0] = 0; consoleInfo.v[1] = 1; consoleInfo.v[2] = 0;
	consoleInfo.n[0] = 0; consoleInfo.n[1] = 0; consoleInfo.n[2] = 1;
	consoleInfo.alpha = 1.0f;
}

static void GL_DrawColoredStereoLinePair( float r, float g, float b, float y )
{
	qglColor3f( r, g, b );
	qglVertex2f( 0, y );
	qglVertex2f( vid.width, y );
	qglColor3f( 0, 0, 0 );
	qglVertex2f( 0, y + 1 );
	qglVertex2f( vid.width, y + 1 );
}

static void GL_DrawStereoPattern( void )
{
#ifdef macintosh
	return;
#else
	int i;

	if ( !( gl_config.renderer & GL_RENDERER_INTERGRAPH ) )
		return;

	if ( !gl_state.stereo_enabled )
		return;

	R_SetGL2D();

	qglDrawBuffer( GL_BACK_LEFT );

	for ( i = 0; i < 20; i++ )
	{
		qglBegin( GL_LINES );
			GL_DrawColoredStereoLinePair( 1, 0, 0, 0 );
			GL_DrawColoredStereoLinePair( 1, 0, 0, 2 );
			GL_DrawColoredStereoLinePair( 1, 0, 0, 4 );
			GL_DrawColoredStereoLinePair( 1, 0, 0, 6 );
			GL_DrawColoredStereoLinePair( 0, 1, 0, 8 );
			GL_DrawColoredStereoLinePair( 1, 1, 0, 10);
			GL_DrawColoredStereoLinePair( 1, 1, 0, 12);
			GL_DrawColoredStereoLinePair( 0, 1, 0, 14);
		qglEnd();

		GLimp_EndFrame();
	}
#endif
}


/*
====================
R_SetLightLevel

====================
*/
static void R_SetLightLevel (void)
{
	vec3_t		shadelight;

	if (r_newrefdef.rdflags & RDF_NOWORLDMODEL)
		return;

	// save off light value for server to look at (BIG HACK!)

	R_LightPoint (r_newrefdef.vieworg, shadelight, true);

	// pick the greatest component, which should be the same
	// as the mono value returned by software
	if (shadelight[0] > shadelight[1])
	{
		if (shadelight[0] > shadelight[2])
			r_lightlevel->value = 150*shadelight[0];
		else
			r_lightlevel->value = 150*shadelight[2];
	}
	else
	{
		if (shadelight[1] > shadelight[2])
			r_lightlevel->value = 150*shadelight[1];
		else
			r_lightlevel->value = 150*shadelight[2];
	}

}

/*
@@@@@@@@@@@@@@@@@@@@@
R_RenderFrame

@@@@@@@@@@@@@@@@@@@@@
*/
static void R_RenderFrame (refdef_t *fd)
{
	R_RenderView( fd );
	R_SetLightLevel ();
	R_SetGL2D ();
}


static void R_Register( void )
{
	r_lefthand = ri.Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE );
	r_norefresh = ri.Cvar_Get ("r_norefresh", "0", 0);
	r_fullbright = ri.Cvar_Get ("r_fullbright", "0", 0);
	r_drawentities = ri.Cvar_Get ("r_drawentities", "1", 0);
	r_drawworld = ri.Cvar_Get ("r_drawworld", "1", 0);
	r_novis = ri.Cvar_Get ("r_novis", "0", 0);
	r_nocull = ri.Cvar_Get ("r_nocull", "0", 0);
	r_lerpmodels = ri.Cvar_Get ("r_lerpmodels", "1", 0);
	r_speeds = ri.Cvar_Get ("r_speeds", "0", 0);

	r_lightlevel = ri.Cvar_Get ("r_lightlevel", "0", 0);

	gl_nosubimage = ri.Cvar_Get( "gl_nosubimage", "0", 0 );
	gl_allow_software = ri.Cvar_Get( "gl_allow_software", "0", 0 );

	gl_particle_min_size = ri.Cvar_Get( "gl_particle_min_size", "2", CVAR_ARCHIVE );
	gl_particle_max_size = ri.Cvar_Get( "gl_particle_max_size", "40", CVAR_ARCHIVE );
	gl_particle_size = ri.Cvar_Get( "gl_particle_size", "40", CVAR_ARCHIVE );
	gl_particle_att_a = ri.Cvar_Get( "gl_particle_att_a", "0.01", CVAR_ARCHIVE );
	gl_particle_att_b = ri.Cvar_Get( "gl_particle_att_b", "0.0", CVAR_ARCHIVE );
	gl_particle_att_c = ri.Cvar_Get( "gl_particle_att_c", "0.01", CVAR_ARCHIVE );

	gl_modulate = ri.Cvar_Get ("gl_modulate", "1", CVAR_ARCHIVE );
	gl_log = ri.Cvar_Get( "gl_log", "0", 0 );
	gl_bitdepth = ri.Cvar_Get( "gl_bitdepth", "0", 0 );
	gl_mode = ri.Cvar_Get( "gl_mode", "0", CVAR_ARCHIVE );
	gl_lightmap = ri.Cvar_Get ("gl_lightmap", "0", 0);
	gl_shadows = ri.Cvar_Get ("gl_shadows", "0", CVAR_ARCHIVE);
	gl_shadows_stencil = ri.Cvar_Get ("gl_shadows_stencil", "1", CVAR_ARCHIVE);
	gl_bbox = ri.Cvar_Get ("gl_bbox", "0", 0);
	gl_dynamic = ri.Cvar_Get ("r_dynamic", "1", CVAR_ARCHIVE);
	gl_nobind = ri.Cvar_Get ("gl_nobind", "0", 0);
	gl_round_down = ri.Cvar_Get ("gl_round_down", "1", 0);
	gl_picmip = ri.Cvar_Get ("gl_picmip", "0", 0);
	gl_skymip = ri.Cvar_Get ("gl_skymip", "0", 0);
	gl_showtris = ri.Cvar_Get ("gl_showtris", "0", 0);
	gl_ztrick = ri.Cvar_Get ("gl_ztrick", "0", CVAR_ARCHIVE);
	gl_finish = ri.Cvar_Get ("gl_finish", "0", 0);
	gl_clear = ri.Cvar_Get ("gl_clear", "0", 0);
	gl_cull = ri.Cvar_Get ("gl_cull", "1", 0);
	gl_polyblend = ri.Cvar_Get ("gl_polyblend", "1", CVAR_ARCHIVE);
	gl_flashblend = ri.Cvar_Get ("gl_flashblend", "0", 0);
	gl_playermip = ri.Cvar_Get ("gl_playermip", "0", 0);
	gl_monolightmap = ri.Cvar_Get( "gl_monolightmap", "0", 0 );
#ifdef __DJGPP__ /* FS: Don't clash with win32's usage of opengl32 and mini drivers */
	gl_driver = ri.Cvar_Get( "gl_dosdriver", "gl.dxe", CVAR_ARCHIVE);
#elif defined(GLIMP_SDL) /* to load SDL's default opengl driver -- see qgl_linux.c */
	gl_driver = ri.Cvar_Get( "gl_driver", "", CVAR_ARCHIVE );
#elif defined(_WIN32)
	gl_driver = ri.Cvar_Get( "gl_driver", "opengl32", CVAR_ARCHIVE );
#else /* assuming unix */
	gl_driver = ri.Cvar_Get( "gl_driver", "libGL.so.1", CVAR_ARCHIVE );
#endif
	gl_texturemode = ri.Cvar_Get( "gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE );
	gl_texturealphamode = ri.Cvar_Get( "gl_texturealphamode", "default", CVAR_ARCHIVE );
	gl_texturesolidmode = ri.Cvar_Get( "gl_texturesolidmode", "default", CVAR_ARCHIVE );
	gl_lockpvs = ri.Cvar_Get( "gl_lockpvs", "0", 0 );

	gl_vertex_arrays = ri.Cvar_Get( "gl_vertex_arrays", "0", CVAR_ARCHIVE );
	gl_skintexturemode = ri.Cvar_Get( "gl_skintexturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE );
	gl_drawflat  = ri.Cvar_Get ("r_drawflat", "0", 0);
	gl_draworder = ri.Cvar_Get ("r_draworder", "0", 0);
	r_conchars   = ri.Cvar_Get ("r_conchars", "pics/conchars.tga", 0);
	r_condist    = ri.Cvar_Get ("r_condist", "100000", CVAR_ARCHIVE);
	r_detaildist = ri.Cvar_Get ("r_detaildist", "450000", CVAR_ARCHIVE);
	r_detailthresh = ri.Cvar_Get ("r_detailthresh", "100000", CVAR_ARCHIVE);
	r_conoffset  = ri.Cvar_Get ("r_conoffset", "0.1", CVAR_ARCHIVE);
	r_conthresh  = ri.Cvar_Get ("r_conthresh", "100000", CVAR_ARCHIVE);
	gl_showents  = ri.Cvar_Get ("gl_showents", "0", 0);
	r_monolighting = ri.Cvar_Get ("r_monolighting", "0", 0);
	gl_ext_swapinterval = ri.Cvar_Get( "gl_ext_swapinterval", "1", CVAR_ARCHIVE );
	gl_ext_palettedtexture = ri.Cvar_Get( "gl_ext_palettedtexture", "2", CVAR_ARCHIVE );

#ifdef __DJGPP__
	gl_ext_multitexture = ri.Cvar_Get( "gl_ext_multitexture", "0", CVAR_ARCHIVE ); /* FS: About a ~10fps loss on average with this on.  So let's set it to be off by default */
#else
	gl_ext_multitexture = ri.Cvar_Get( "gl_ext_multitexture", "1", CVAR_ARCHIVE );
#endif

#ifdef __DJGPP__
	gl_ext_pointparameters = ri.Cvar_Get( "gl_ext_pointparameters", "0", CVAR_ARCHIVE ); /* FS: Hardlocks when it's time to draw a particle in fxMesa 3*/
#else
	gl_ext_pointparameters = ri.Cvar_Get( "gl_ext_pointparameters", "1", CVAR_ARCHIVE );
#endif

	gl_ext_compiled_vertex_array = ri.Cvar_Get( "gl_ext_compiled_vertex_array", "1", CVAR_ARCHIVE );

	gl_drawbuffer = ri.Cvar_Get( "gl_drawbuffer", "GL_BACK", 0 );
	gl_swapinterval = ri.Cvar_Get( "gl_swapinterval", "1", CVAR_ARCHIVE );

	gl_saturatelighting = ri.Cvar_Get( "gl_saturatelighting", "0", 0 );

	gl_3dlabs_broken = ri.Cvar_Get( "gl_3dlabs_broken", "1", CVAR_ARCHIVE );

	vid_fullscreen = ri.Cvar_Get( "vid_fullscreen", "1", CVAR_ARCHIVE );
	vid_gamma = ri.Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE );
	vid_ref = ri.Cvar_Get( "vid_ref", "soft", CVAR_ARCHIVE );

	/* FS */
	gl_ignorehwgamma = ri.Cvar_Get ("gl_ignorehwgamma", "0", CVAR_ARCHIVE);
	gl_displayrefresh = ri.Cvar_Get ("gl_displayrefresh", "0", CVAR_ARCHIVE);

	// 2015 code
	game2015 = ri.Cvar_Get ("game2015", "0", 0);

	/* Knightmare */
	gl_render_immediate = ri.Cvar_Get ("gl_render_immediate", "0", 0);
	gl_lightcutoff = ri.Cvar_Get ("gl_lightcutoff", "8", 0);

	scr_shotname = ri.Cvar_Get( "scr_shotname", SCREEN_SHOT_NAME, CVAR_ARCHIVE );
	ri.Cmd_AddCommand( "loadportals", GL_LoadPortals );
	gl_showportals = ri.Cvar_Get( "gl_showportals", "0", 0 );
	ri.Cmd_AddCommand( "initfont", GL_Draw_InitLocal );
	ri.Cmd_AddCommand( "loadpal", GL_LoadPalFile_f );

	ri.Cmd_AddCommand( "imagelist", GL_ImageList_f );
	ri.Cmd_AddCommand( "screenshot", GL_ScreenShot_f );
	ri.Cmd_AddCommand( "modellist", Mod_Modellist_f );
	ri.Cmd_AddCommand( "gl_strings", GL_Strings_f );
}

/*
==================
R_SetMode
==================
*/
static qboolean R_SetMode (void)
{
	rserr_t err;
	qboolean fullscreen;

	/* cds check only needed with WIN32 to deal with a broken driver */
	#ifdef _WIN32
	if ( vid_fullscreen->modified && !gl_config.allow_cds )
	{
		ri.Con_Printf( PRINT_ALL, "R_SetMode() - CDS not allowed with this driver\n" );
		ri.Cvar_SetValue( "vid_fullscreen", !vid_fullscreen->value );
		vid_fullscreen->modified = false;
	}
	#endif
	fullscreen = vid_fullscreen->value;

	vid_fullscreen->modified = false;
	gl_mode->modified = false;

	if ( ( err = (rserr_t)GLimp_SetMode( &vid.width, &vid.height, gl_mode->value, fullscreen ) ) == rserr_ok ) {
		gl_state.prev_mode = gl_mode->value;
	} else {
		if ( err == rserr_invalid_fullscreen )
		{
			ri.Cvar_SetValue( "vid_fullscreen", 0);
			vid_fullscreen->modified = false;
			ri.Con_Printf( PRINT_ALL, "ref_gl::R_SetMode() - fullscreen unavailable in this mode\n" );
			if ( ( err = (rserr_t)GLimp_SetMode( &vid.width, &vid.height, gl_mode->value, false ) ) == rserr_ok ) {
				return true;
			}
		} else if ( err == rserr_invalid_mode ) {
			ri.Cvar_SetValue( "gl_mode", gl_state.prev_mode );
			gl_mode->modified = false;
			ri.Con_Printf( PRINT_ALL, "ref_gl::R_SetMode() - invalid mode\n" );
		}

		// try setting it back to something safe
		if ( ( err = (rserr_t)GLimp_SetMode( &vid.width, &vid.height, gl_state.prev_mode, false ) ) != rserr_ok ) {
			ri.Con_Printf( PRINT_ALL, "ref_gl::R_SetMode() - could not revert to safe mode\n" );
			return false;
		}
	}
	return true;
}

/*
===============
GL_ParseExtensionList
-from QuakeForge code
===============
*/
static qboolean GL_ParseExtensionList (const char *list, const char *name)
{
	const char	*start;
	const char	*where, *terminator;

	if (!list || !name || !*name)
		return false;
	if (strchr(name, ' ') != NULL)
		return false;	// extension names must not have spaces

	start = list;
	while (1) {
		where = strstr (start, name);
		if (!where)
			break;
		terminator = where + strlen (name);
		if (where == start || where[-1] == ' ')
			if (*terminator == ' ' || *terminator == '\0')
				return true;
		start = terminator;
	}
	return false;
}

/*
===============
R_Init
===============
*/
int R_Init( void *hinstance, void *hWnd )
{
	char renderer_buffer[1000];
	char vendor_buffer[1000];
	int err, vercheck;
	int		j;
	extern float r_turbsin[256];

	for ( j = 0; j < 256; j++ ) {
		r_turbsin[j] *= 0.5;
	}

	ri.Con_Printf (PRINT_ALL, "ref_gl version: " REF_VERSION "\n");

	R_Register();

	if ( !QGL_Init( gl_driver->string ) ) {
		QGL_Shutdown();
		ri.Con_Printf (PRINT_ALL, "ref_gl::R_Init() - could not load \"%s\"\n", gl_driver->string );
		return -1;
	}

	// initialize OS-specific parts of OpenGL
	if ( !GLimp_Init( hinstance, hWnd ) ) {
		QGL_Shutdown();
		return -1;
	}

	// set our "safe" modes
	gl_state.prev_mode = 3;

	// create the window and set up the context
	if ( !R_SetMode () ) {
		QGL_Shutdown();
		ri.Con_Printf (PRINT_ALL, "ref_gl::R_Init() - could not R_SetMode()\n" );
		return -1;
	}

	ri.Vid_MenuInit();

	/*
	** get our various GL strings
	*/
	gl_config.vendor_string = (const char *)qglGetString (GL_VENDOR);
	ri.Con_Printf (PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string );
	gl_config.renderer_string = (const char *)qglGetString (GL_RENDERER);
	ri.Con_Printf (PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string );
	gl_config.version_string = (const char *)qglGetString (GL_VERSION);
	sscanf(gl_config.version_string, "%d.%d", &gl_majorver, &gl_minorver);
	ri.Con_Printf (PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string );
	vercheck = gl_majorver*1000 + gl_minorver; /* GL1.1 */
	gl_config.extensions_string = (const char *)qglGetString (GL_EXTENSIONS);
	ri.Con_Printf (PRINT_DEVELOPER, "GL_EXTENSIONS: %s\n", gl_config.extensions_string ); /* FS: Changed to developer */

	strcpy( renderer_buffer, gl_config.renderer_string );
	Q_strlwr( renderer_buffer );

	strcpy( vendor_buffer, gl_config.vendor_string );
	Q_strlwr( vendor_buffer );

#ifndef macintosh

	if (!Q_strncasecmp(renderer_buffer, "3dfx", 4)		||
	    !Q_strncasecmp(renderer_buffer, "SAGE Glide", 10)	||
	    !Q_strncasecmp(renderer_buffer, "Glide ", 6)	|| /* possible with Mesa 3.x/4.x/5.0.x */
	    !Q_strncasecmp(renderer_buffer, "Mesa Glide", 10))
	{
		gl_config.renderer = GL_RENDERER_VOODOO;
	}
	else if (strstr	(vendor_buffer, "sgi"))			gl_config.renderer = GL_RENDERER_SGI;
	else if (strstr	(renderer_buffer, "permedia"))	gl_config.renderer = GL_RENDERER_PERMEDIA2;
	else if (strstr	(renderer_buffer, "glint"))		gl_config.renderer = GL_RENDERER_GLINT_MX;
	else if (strstr	(renderer_buffer, "glzicd"))	gl_config.renderer = GL_RENDERER_REALIZM;
	#ifdef _WIN32
	else if (strstr (renderer_buffer, "gdi"))		gl_config.renderer = GL_RENDERER_MCD;
	#endif
	else if (strstr	(renderer_buffer, "pcx2"))		gl_config.renderer = GL_RENDERER_PCX2;
	else if (strstr	(renderer_buffer, "verite"))	gl_config.renderer = GL_RENDERER_RENDITION;
	else if (strstr (renderer_buffer, "voodoo"))	gl_config.renderer = GL_RENDERER_VOODOO;
	else											gl_config.renderer = GL_RENDERER_OTHER;

	if ( toupper( gl_monolightmap->string[1] ) != 'F' )
	{
		if ( gl_config.renderer == GL_RENDERER_PERMEDIA2 )
		{
			ri.Cvar_Set( "gl_monolightmap", "A" );
			ri.Con_Printf( PRINT_ALL, "...using gl_monolightmap 'a'\n" );
		}
		else if ( gl_config.renderer & GL_RENDERER_POWERVR )
		{
			ri.Cvar_Set( "gl_monolightmap", "0" );
		}
		else
		{
			ri.Cvar_Set( "gl_monolightmap", "0" );
		}
	}

	// power vr can't have anything stay in the framebuffer, so
	// the screen needs to redraw the tiled background every frame
	if ( gl_config.renderer & GL_RENDERER_POWERVR )
	{
		ri.Cvar_Set( "scr_drawall", "1" );
	}
	else
	{
		ri.Cvar_Set( "scr_drawall", "0" );
	}

	/* windows-only stuff to deal with broken drivers */
	#ifdef _WIN32
	// MCD has buffering issues
	if ( gl_config.renderer == GL_RENDERER_MCD )
	{
		ri.Cvar_SetValue( "gl_finish", 1 );
	}

	if ( gl_config.renderer & GL_RENDERER_3DLABS )
	{
		if ( gl_3dlabs_broken->value )
			gl_config.allow_cds = false;
		else
			gl_config.allow_cds = true;
	}
	else
	{
		gl_config.allow_cds = true;
	}

	if ( gl_config.allow_cds )
	{
		ri.Con_Printf( PRINT_ALL, "...allowing CDS\n" );
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...disabling CDS\n" );
	}
	#else
	gl_config.allow_cds = true;
	#endif

#else
	if ( toupper( gl_monolightmap->string[1] ) != 'F' )
	{
		ri.Cvar_Set( "gl_monolightmap", "0" );
	}

	ri.Cvar_Set( "scr_drawall", "0" );

	if ( strstr( renderer_buffer, "Voodoo" ) )
	{
		if ( strstr( renderer_buffer, "Voodoo 2" ) )
		{
			gl_config.renderer = GL_RENDERER_VOODOO2;
		}
		else if ( strstr( renderer_buffer, "Voodoo 3" ) )
		{
			gl_config.renderer = GL_RENDERER_VOODOO3;
		}
		else
		{
			gl_config.renderer = GL_RENDERER_VOODOO;
		}
	}

	gl_config.allow_cds = true;
#endif /* macintosh */

	/*
	** grab extensions
	*/

	// GL_EXT_compiled_vertex_array
	if (GL_ParseExtensionList(gl_config.extensions_string, "GL_EXT_compiled_vertex_array") ||
	    GL_ParseExtensionList(gl_config.extensions_string, "GL_SGI_compiled_vertex_array"))
	{
		if(gl_ext_compiled_vertex_array->value)
		{
#ifdef macintosh
			ri.Con_Printf( PRINT_ALL, "...enabling GL_EXT_compiled_vertex_array\n" );
			qglLockArraysEXT = glLockArraysEXT;
			qglUnlockArraysEXT = glUnlockArraysEXT;
#else
			qglLockArraysEXT = ( void (APIENTRY *) ( GLint, GLsizei ) ) qwglGetProcAddress( "glLockArraysEXT" );
			qglUnlockArraysEXT = ( void (APIENTRY *) ( void ) ) qwglGetProcAddress( "glUnlockArraysEXT" );
			if (qglUnlockArraysEXT && qglLockArraysEXT)
				ri.Con_Printf( PRINT_ALL, "...enabling GL_EXT_compiled_vertex_array\n" );
			else {
				qglUnlockArraysEXT = NULL;
				qglLockArraysEXT = NULL;
				ri.Con_Printf( PRINT_ALL, "...failed loading GL_EXT_compiled_vertex_array\n" );
			}
		}
		else
		{
			ri.Con_Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
		}
#endif /* macintosh */
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
	}

#ifndef macintosh
#ifdef _WIN32
	// WGL_EXT_swap_control
	if (GL_ParseExtensionList(gl_config.extensions_string, "WGL_EXT_swap_control"))
		qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" );
	if ( qwglSwapIntervalEXT )
	{
		if (gl_ext_swapinterval->value)
		{
			qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" );
			if ( qwglSwapIntervalEXT )
			{
				ri.Con_Printf( PRINT_ALL, "...enabling WGL_EXT_swap_control\n" );
			}
			else
			{
				ri.Con_Printf( PRINT_ALL, "...failed loading WGL_EXT_swap_control\n" );
			}
		}
		else
		{
			ri.Con_Printf( PRINT_ALL, "...ignoring WGL_EXT_swap_control\n" );
		}
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...WGL_EXT_swap_control not found\n" );
	}
#endif /* _WIN32 */

	// GL_EXT_point_parameters
	if (GL_ParseExtensionList(gl_config.extensions_string, "GL_EXT_point_parameters"))
	{
		if ( gl_ext_pointparameters->value )
		{
			qglPointParameterfEXT = ( void (APIENTRY *)( GLenum, GLfloat ) ) qwglGetProcAddress( "glPointParameterfEXT" );
			qglPointParameterfvEXT = ( void (APIENTRY *)( GLenum, const GLfloat * ) ) qwglGetProcAddress( "glPointParameterfvEXT" );
			if (qglPointParameterfEXT && qglPointParameterfvEXT)
				ri.Con_Printf( PRINT_ALL, "...using GL_EXT_point_parameters\n" );
			else {
				qglPointParameterfEXT = NULL;
				qglPointParameterfvEXT = NULL;
				ri.Con_Printf( PRINT_ALL, "...failed loading GL_EXT_point_parameters\n" );
			}
		}
		else
		{
			ri.Con_Printf( PRINT_ALL, "...ignoring GL_EXT_point_parameters\n" );
		}
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...GL_EXT_point_parameters not found\n" );
	}

	// GL_EXT_paletted_texture / GL_EXT_shared_texture_palette
	if (GL_ParseExtensionList(gl_config.extensions_string, "GL_EXT_paletted_texture"))
	{
		qboolean shared = false;
		if (GL_ParseExtensionList(gl_config.extensions_string, "GL_EXT_shared_texture_palette"))
			shared = true;
		if ( gl_ext_palettedtexture->value )
		{
			shared_palette_support = shared;
			qglColorTableEXT = (void (APIENTRY *) (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *)) qwglGetProcAddress("glColorTableEXT");
			if (!qglColorTableEXT)
				ri.Con_Printf( PRINT_ALL, "...failed loading GL_EXT_shared_texture_palette\n" );
			else if (shared)
				ri.Con_Printf( PRINT_ALL, "...using GL_EXT_shared_texture_palette\n" );
			else
				ri.Con_Printf( PRINT_ALL, "...using GL_EXT_texture_palette\n" );
		}
		else
		{
			ri.Con_Printf( PRINT_ALL, "...ignoring GL_EXT_shared_texture_palette\n" );
		}
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...GL_EXT_shared_texture_palette not found\n" );
	}

	// GL_ARB_multitexture
	if (GL_ParseExtensionList(gl_config.extensions_string, "GL_ARB_multitexture"))
	{
		if ( gl_ext_multitexture->value )
		{
			qglMultiTexCoord2fARB = (void (APIENTRY *)(GLenum,GLfloat,GLfloat)) qwglGetProcAddress( "glMultiTexCoord2fARB" );
			qglActiveTextureARB = (void (APIENTRY *)(GLenum)) qwglGetProcAddress( "glActiveTextureARB" );
			qglClientActiveTextureARB = (void (APIENTRY *)(GLenum)) qwglGetProcAddress( "glClientActiveTextureARB" );
			if (qglMultiTexCoord2fARB && qglActiveTextureARB) {
				GLint max_texunits = 0;
				qglGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &max_texunits);
				if (max_texunits > 1)
					ri.Con_Printf( PRINT_ALL, "...using GL_ARB_multitexture, %i TMUs\n", (int)max_texunits );
				else {
					qglMultiTexCoord2fARB = NULL;
					qglActiveTextureARB = NULL;
					qglClientActiveTextureARB = NULL;
					ri.Con_Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 TMUs\n" );
				}
			}
			else {
				qglMultiTexCoord2fARB = NULL;
				qglActiveTextureARB = NULL;
				qglClientActiveTextureARB = NULL;
				ri.Con_Printf( PRINT_ALL, "...failed loading GL_ARB_multitexture\n" );
			}
		}
		else
		{
			ri.Con_Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
		}
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
	}

	// glDrawElements
	if (vercheck < 1001 || !qglDrawElements) {
		ri.Con_Printf( PRINT_ALL, "...glDrawElements not supported\n" );
	}
	else {
		ri.Con_Printf( PRINT_ALL, "...using glDrawElements\n" );
	}

	gl_config.havedrawrangeelements = false;
	// GL_EXT_draw_range_elements
	if (vercheck >= 1002 && qglDrawElements != NULL &&
	    /* check qglClientActiveTextureARB too?? */
	    GL_ParseExtensionList(gl_config.extensions_string, "GL_EXT_draw_range_elements"))
	{
		qglDrawRangeElementsEXT = (void (APIENTRY *)(GLenum, GLuint, GLuint, GLsizei, GLenum, const GLvoid *)) qwglGetProcAddress("glDrawRangeElementsEXT");
		if (!qglDrawRangeElementsEXT) {
			ri.Con_Printf( PRINT_ALL, "...failed loading GL_EXT_draw_range_elements\n" );
		}
		else {
			ri.Con_Printf( PRINT_ALL, "...using GL_EXT_draw_range_elements\n" );
			gl_config.havedrawrangeelements = true;
		}
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...GL_EXT_draw_range_elements not found\n" );
	}
#endif /* macintosh */

	R_InitVertexArrays ();

	GL_SetDefaultState();

	/*
	** draw our stereo patterns
	*/
	GL_DrawStereoPattern();

	GL_InitImages ();
	Mod_Init ();
	R_InitParticleTexture ();
	GL_Draw_InitLocal ();

	{
		int i;
		for (i=0;i<256;i++) {
			float rand1=0,rand2=0,rand3=0;
			while ((rand1 == 0.0f) && (rand2 == 0.0f) && (rand3 == 0.0f)) {
				rand1 = (float)rand() / (float)RAND_MAX;
				rand2 = (float)rand() / (float)RAND_MAX;
				rand3 = (float)rand() / (float)RAND_MAX;
			}
			r_solidcolors[i*3+0] = rand1;
			r_solidcolors[i*3+1] = rand2;
			r_solidcolors[i*3+2] = rand3;
			ColorNormalize(&r_solidcolors[i], &r_solidcolors[i]);
		}
	}

	err = qglGetError();
	if ( err != GL_NO_ERROR ) {
		ri.Con_Printf (PRINT_ALL, "glGetError() = 0x%x\n", err);
	}
	return 0;
}

float ColorNormalize (vec3_t in, vec3_t out)
{
	float	max, scale;

	max = in[0];
	if (in[1] > max)
		max = in[1];
	if (in[2] > max)
		max = in[2];

	if (max == 0)
		return 0;

	scale = 1.0 / max;
	VectorScale (in, scale, out);
	return max;
}

/*
===============
R_Shutdown
===============
*/
void R_Shutdown (void)
{
#ifdef BATCH_LM_UPDATES
	int i;
#endif	// BATCH_LM_UPDATES

	ri.Cmd_RemoveCommand ("modellist");
	ri.Cmd_RemoveCommand ("screenshot");
	ri.Cmd_RemoveCommand ("imagelist");
	ri.Cmd_RemoveCommand ("gl_strings");
	ri.Cmd_RemoveCommand ("initfont");
	ri.Cmd_RemoveCommand ("loadportals");
	ri.Cmd_RemoveCommand( "loadpal" );
	
	Draw_EndCinematic();			/* Make SURE I have released my movie memory */
	Mod_FreeAll ();

#ifdef BATCH_LM_UPDATES
	/* Knightmare- free lightmap update buffers */
	for (i=0; i<MAX_LIGHTMAPS; i++)
	{
		if (gl_lms.lightmap_update[i])
		{
			free(gl_lms.lightmap_update[i]);
			gl_lms.lightmap_update[i]=NULL;
		}
	}
	/* end Knigthmare */
#endif	// BATCH_LM_UPDATES

	GL_ShutdownImages ();
	free(gl_state.d_16to8table);
	gl_state.d_16to8table = 0;
	/*
	** shut down OS specific OpenGL stuff like contexts, etc.
	*/
	GLimp_Shutdown();
	
	/*
	** shutdown our QGL subsystem
	*/
	QGL_Shutdown();
}

/*
@@@@@@@@@@@@@@@@@@@@@
R_BeginFrame
@@@@@@@@@@@@@@@@@@@@@
*/
void R_BeginFrame( float camera_separation )
{

	gl_state.camera_separation = camera_separation;

	/*
	** change modes if necessary
	*/
	if ( gl_mode->modified || vid_fullscreen->modified ) {
		// FIXME: only restart of CDS is required
		cvar_t	*ref;

		ref = ri.Cvar_Get ("vid_ref", "gl", 0);
		ref->modified = true;
	}

	if ( gl_log->modified )
	{
		GLimp_EnableLogging( gl_log->value );
		gl_log->modified = false;
	}

	if ( gl_log->value )
	{
		GLimp_LogNewFrame();
	}

	/*
	** update 3Dfx gamma -- it is expected that a user will do a vid_restart
	** after tweaking this value
	*/
	if ( vid_gamma->modified )
	{
		vid_gamma->modified = false;

#ifdef _WIN32
		if ((gl_config.renderer & GL_RENDERER_VOODOO) && (!qwglSetDeviceGammaRamp3DFX))
		{
			char envbuffer[1024];
			float g;
		//	g = 2.00 * ( 0.8 - ( vid_gamma->value - 0.5 ) ) + 1.0F;
			g = 2.00 * ( 1.0 - ( vid_gamma->value - 0.3 ) ) + 1.0F;	// Knightmare 5/30/12- changed range to 0.3f to 1.3f
			Com_sprintf( envbuffer, sizeof(envbuffer), "SSTV2_GAMMA=%f", g );
			putenv( envbuffer );
			Com_sprintf( envbuffer, sizeof(envbuffer), "SST_GAMMA=%f", g );
			putenv( envbuffer );
		}
#endif

		HWGamma_Update ();	// Knightmare added
	}

	GLimp_BeginFrame( camera_separation );

	/*
	** go into 2D mode
	*/
	qglViewport (0,0, vid.width, vid.height);
	qglMatrixMode(GL_PROJECTION);
	qglLoadIdentity();
	qglOrtho(0, vid.width, 0, vid.height, -99999, 99999);
	consoleInfo.surface_width = vid.width;
	consoleInfo.surface_height = vid.height;
	consoleInfo.u[0] = 1; consoleInfo.u[1] = 0; consoleInfo.u[2] = 0;
	consoleInfo.v[0] = 0; consoleInfo.v[1] = 1; consoleInfo.v[2] = 0;
	consoleInfo.n[0] = 0; consoleInfo.n[1] = 0; consoleInfo.n[2] = 1;
	consoleInfo.alpha = 1.0f;
	qglMatrixMode(GL_MODELVIEW);
	qglLoadIdentity ();
	qglDisable (GL_DEPTH_TEST);
	qglDisable (GL_CULL_FACE);
	qglDisable (GL_BLEND);
	qglEnable (GL_ALPHA_TEST);
	qglColor4f (1,1,1,1);

	/*
	** draw buffer stuff
	*/
	if ( gl_drawbuffer->modified ) {
		gl_drawbuffer->modified = false;
		if ( gl_state.camera_separation == 0 || !gl_state.stereo_enabled ) {
			if ( Q_stricmp( gl_drawbuffer->string, "GL_FRONT" ) == 0 ) {
				qglDrawBuffer( GL_FRONT );
			} else {
				qglDrawBuffer( GL_BACK );
			}
		}
	}

	/*
	** texturemode stuff
	*/
	if ( gl_texturemode->modified ) {
		GL_TextureMode( gl_texturemode->string );
		gl_texturemode->modified = false;
	}

	if ( gl_skintexturemode->modified ) {
		GL_SkinTextureMode( gl_skintexturemode->string );
		gl_skintexturemode->modified = false;
	}

	if ( gl_texturealphamode->modified ) {
		GL_TextureAlphaMode( gl_texturealphamode->string );
		gl_texturealphamode->modified = false;
	}

	if ( gl_texturesolidmode->modified ) {
		GL_TextureSolidMode( gl_texturesolidmode->string );
		gl_texturesolidmode->modified = false;
	}
	/*
	** swapinterval stuff
	*/
	GL_UpdateSwapInterval();

	//
	// clear screen if desired
	//
	R_Clear ();
}

/*
=============
R_SetPalette
=============
*/
void R_SetPalette ( const unsigned char *palette)
{
	int i;
	byte *rp;
	byte r_rawpalette[1024];
	
	rp = r_rawpalette;

	if (!palette) {
		palette = d_8to24table;
	}
	i = 256;
	do {
		rp[0] = palette[0];
		rp[1] = palette[1];
		rp[2] = palette[2];
		rp[3] = 255;
		palette+=4;
		rp+=4;
	} while (--i);

	GL_SetTexturePalette( r_rawpalette );
	qglClearColor (0,0,0,0);
	qglClear (GL_COLOR_BUFFER_BIT);
	qglClearColor (1,0, 0.5 , 0.5);
}

/*
** R_DrawBeam
*/
void R_DrawBeam( entity_t *e )
{
#define NUM_BEAM_SEGS 6

	int	i;
	float r, g, b;

	vec3_t perpvec;
	vec3_t direction, normalized_direction;
	vec3_t	start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS];
	vec3_t oldorigin, origin;

	oldorigin[0] = e->oldorigin[0];
	oldorigin[1] = e->oldorigin[1];
	oldorigin[2] = e->oldorigin[2];

	origin[0] = e->origin[0];
	origin[1] = e->origin[1];
	origin[2] = e->origin[2];

	normalized_direction[0] = direction[0] = oldorigin[0] - origin[0];
	normalized_direction[1] = direction[1] = oldorigin[1] - origin[1];
	normalized_direction[2] = direction[2] = oldorigin[2] - origin[2];

	if ( VectorNormalize( normalized_direction ) == 0 )
		return;

	PerpendicularVector( perpvec, normalized_direction );
	VectorScale( perpvec, (float)e->frame / 2, perpvec );

	for ( i = 0; i < 6; i++ ) {
		RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i );
		VectorAdd( start_points[i], origin, start_points[i] );
		VectorAdd( start_points[i], direction, end_points[i] );
	}
	
	qglDisable( GL_TEXTURE_2D );
	qglEnable( GL_BLEND );
	qglDepthMask( GL_FALSE );
	
	qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	qglDisable( GL_CULL_FACE );
	if ( e->skinnum == 255 ) {
		r = e->color_r;
		g = e->color_g;
		b = e->color_b;
		qglColor4f( r, g, b, e->alpha );
	} else {
		float a;
	
		i = e->skinnum;
	
		if ( i < 0 ) {
			i = 0;
		} else if ( i >= MAX_LIGHTSTYLES ) {
			i = 0;
		}
	
		r = r_newrefdef.lightstyles[ i ].rgb[ 0 ] * 0.5f;
		g = r_newrefdef.lightstyles[ i ].rgb[ 1 ] * 0.5f;
		b = r_newrefdef.lightstyles[ i ].rgb[ 2 ] * 0.5f;
		a = r_newrefdef.lightstyles[ i ].rgb[ 3 ];
		if ( a < 0 ) {
			a = e->alpha;
		}
		qglColor4f( r, g, b, a );
	}

	qglBegin( GL_TRIANGLE_STRIP );
	for ( i = 0; i < NUM_BEAM_SEGS; i++ ) {
		qglVertex3fv( start_points[i] );
		qglVertex3fv( end_points[i] );
		qglVertex3fv( start_points[(i+1)%NUM_BEAM_SEGS] );
		qglVertex3fv( end_points[(i+1)%NUM_BEAM_SEGS] );
	}
	qglEnd();

	qglEnable( GL_CULL_FACE );
	qglEnable( GL_TEXTURE_2D );
	qglDisable( GL_BLEND );
	qglDepthMask( GL_TRUE );
}

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

/*
@@@@@@@@@@@@@@@@@@@@@
GetRefAPI

@@@@@@@@@@@@@@@@@@@@@
*/

refexport_t GetRefAPI (refimport_t rimp )
{
	refexport_t	re;

	ri = rimp;

	re.api_version = API_VERSION;

	re.BeginRegistration = R_BeginRegistration;
	re.RegisterModel = R_RegisterModel;
	re.RegisterSkin = R_RegisterSkin;
	re.RegisterPic = Draw_FindPic;
	re.SetSky = R_SetSky;
	re.EndRegistration = R_EndRegistration;

	re.RenderFrame = R_RenderFrame;

	re.DrawGetPicSize = Draw_GetPicSize;
	re.DrawPic = Draw_Pic;
	re.DrawStretchPic = Draw_StretchPic;
	re.DrawChar = Draw_Char;
	re.DrawTileClear = Draw_TileClear;
	re.DrawFill = Draw_Fill;
	re.DrawFadeScreen = Draw_FadeScreen;

	re.Init = R_Init;
	re.Shutdown = R_Shutdown;

	re.CinematicSetPalette = R_SetPalette;
	re.BeginFrame = R_BeginFrame;
	re.EndFrame = GLimp_EndFrame;

	re.AppActivate = GLimp_AppActivate;

	re.DrawChar3D = Draw_Char3D;
	re.FlushChar3D = Flush_Char3D; /* FS */
	re.DrawPic3D = Draw_Pic3D;
	re.DrawFill3D = Draw_Fill3D;
	re.DrawAltFill3D = Draw_AltFill3D;
	re.DrawStretchPic3D = Draw_StretchPic3D;
	re.DrawTilePic3D = Draw_TilePic3D;
	re.SINMDL_GetDefHandle = SINMDL_GetDefHandle;
	re.TesselateModel = GL_TesselateModel;
	re.BurnLightmap = R_BurnPoint;
	re.GetNumFrames = R_GetNumFrames;
	re.BeginCinematic = Draw_BeginCinematic;
	re.EndCinematic = Draw_EndCinematic;
	re.DrawCinematicFrame = Draw_CinematicFrame;
#ifdef __A3D_GEOM
	re.A3D_RenderGeometry = A3D_RenderGeometry;
#endif
	Swap_Init ();

	return re;
}

#ifndef REF_HARD_LINKED
// this is only here so the functions in q_shared.c and q_shwin.c can link
void Sys_Error (const char *error, ...)
{
	va_list		argptr;
	char		text[4096]; /* FS: MAXPRINTMSG */

	va_start (argptr, error);
	Q_vsnprintf (text, sizeof(text), error, argptr);
	va_end (argptr);
	text[sizeof(text)-1] = 0;

	ri.Sys_Error (ERR_FATAL, "%s", text);
}

void Com_Printf (const char *fmt, ...)
{
	va_list		argptr;
	char		text[4096]; /* FS: MAXPRINTMSG */

	va_start (argptr, fmt);
	Q_vsnprintf (text, sizeof(text), fmt, argptr);
	va_end (argptr);
	text[sizeof(text)-1] = 0;

	ri.Con_Printf (PRINT_ALL, "%s", text);
}
#endif
