// r_main.c
#include	"gl_local.h"
#include	"gl_protex.h"

void R_Clear (void);

//	Nelno:	FIXME:	for testing particle volume culling, remove later
int		g_PVCullCount;
int		g_PVDrawCount;

//	Nelno:	for doing time-based stuff in the render dll, exported in gl_local.h
int		ref_curTime;
int		ref_lastTime;
float	ref_frameTime;
float	ref_globalTime;	//	total time passed since R_Init, in seconds

static	float	ref_laserRotation = 0.0F;
static	int		ref_laserDir = 1;

//#define	ROTATING_SNOW	//	if you uncomment this, do it in cl_fx.c, too!
#ifdef	ROTATING_SNOW	
//	Nelno:	for rotating particles
#define	MAX_PARTICLE_ROTATIONS	16
static	int		generated_particle_rotations;		//	cleared each frame
static	vec3_t	rpart_angles	[MAX_PARTICLE_ROTATIONS];
static	vec3_t	rpart_ang_speed [MAX_PARTICLE_ROTATIONS];
//static	vec3_t	rpart_forward	[MAX_PARTICLE_ROTATIONS];
static	vec3_t	rpart_up		[MAX_PARTICLE_ROTATIONS];
static	vec3_t	rpart_right		[MAX_PARTICLE_ROTATIONS];
static	int		rpart_change_time = 0;
static	int		rpart_change_max = 3000;
static	int		rpart_change_rotations = 0;
#endif

viddef_t	vid;

refimport_t	ri;

model_t		*r_worldmodel;

float		gldepthmin, gldepthmax;

glconfig_t gl_config;
glstate_t  gl_state;

image_t		*r_notexture;		// use for bad textures
//	Nelno:	changed to an array for complex particle types
image_t		*r_particletexture [NUM_PARTICLETYPES];
image_t		*r_lasertexture,*r_novalasertexture,*r_tracertexture;
image_t		*r_lightningtexture1,*r_lightningtexture2,*r_lightningtexture3;

entity_t	*currententity;
model_t		*currentmodel;

cplane_t	frustum[4];

int			r_visframecount;	// bumped when going to a new PVS
int			r_framecount;		// used for dlight push checking

int			c_brush_polys, c_alias_polys;
int			c_texture_memory;

float		v_blend[4];			// final blending color

void GL_Strings_f( void );

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

float	r_proj_matrix [16];
int		r_viewport [4];
float	r_world_matrix[16];
float	r_base_world_matrix[16];

//
// screen size info
//
refdef_t	r_newrefdef;

int		r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2;

cvar_t	*r_countpvolumes;
cvar_t	*r_countsnow;
cvar_t	*r_countents;	//	Nelson: show number of entities being drawn
cvar_t	*r_drawbmodels;		//	Nelson
cvar_t	*r_drawalias;		//	Nelson
cvar_t	*r_drawsprites;		//	Nelson
cvar_t	*r_drawweapon;		//	Nelson
cvar_t	*r_drawcomparticles;
int		ent_draw_count;

cvar_t	*r_norefresh;
cvar_t	*r_drawentities;
cvar_t	*r_drawworld;
cvar_t	*r_speeds;
cvar_t	*r_fullbright;
cvar_t	*r_novis;
cvar_t	*r_nocull;
cvar_t	*r_lerpmodels;
cvar_t	*r_lefthand;

cvar_t	*r_lightlevel;	// FIXME: This is a HACK to get the client's light level

cvar_t	*gl_nosubimage;
cvar_t	*gl_allow_software;

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_gamma_control;
cvar_t	*gl_ext_palettedtexture;
cvar_t	*gl_ext_multitexture;
cvar_t	*gl_ext_pointparameters;

cvar_t	*gl_log;
cvar_t	*gl_bitdepth;
cvar_t	*gl_drawbuffer;
cvar_t  *gl_driver;
cvar_t	*gl_lightmap;
cvar_t	*gl_shadows;
cvar_t	*gl_mode;
cvar_t	*gl_dynamic;
cvar_t  *gl_monolightmap;
cvar_t	*gl_modulate;
cvar_t	*gl_nobind;
cvar_t	*gl_round_down;
cvar_t	*gl_picmip;
cvar_t	*gl_skymip;
cvar_t	*gl_showtris;
cvar_t	*gl_ztrick;
cvar_t	*gl_finish;
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_lockpvs;
cvar_t	*gl_drawflat;

cvar_t	*gl_3dlabs_broken;

cvar_t	*vid_fullscreen;
cvar_t	*vid_gamma;
cvar_t	*vid_ref;

cvar_t *snapShotMode;		// added by corrinne 

//	Shawn:  added for palette per level
cvar_t	*r_palettedir;

//	Shawn:  added for testing particle speed
cvar_t	*r_drawparticleflag;

//  Shawn:  added for image log 
cvar_t	*r_useimagelog;

//	Nelno:	added for per-level console backgrounds/characters
cvar_t	*r_resourcedir;

// Shawn:  added for testing alpha surface lightmaps
cvar_t	*gl_alphalightmap;

float	global_color[GC_NUMCOLORS][3];

/*
=================
R_BestColor
=================
*/
unsigned char R_BestColor (byte r, byte g, byte b)
{
	int	i;
	int	dr, dg, db;
	int	bestdistortion, distortion;
	int	bestcolor;
	unsigned	*pal;

//
// let any color go to 0 as a last resort
//
	bestdistortion = 256*256*4;
	bestcolor = 0;

	pal = d_8to24table;
	for (i=0; i<= 255 ; i++)
	{
		dr = r - (int)(pal[i]>>0);
		dg = g - (int)(pal[i]>>8);
		db = b - (int)(pal[i]>>16);

		distortion = dr*dr + dg*dg + db*db;
		if (distortion < bestdistortion)
		{
			if (!distortion)
				return i;		// perfect match

			bestdistortion = distortion;
			bestcolor = i;
		}
	}

	return bestcolor;
}

/*
=================
R_SetGlobalColors
=================
*/
//#define ONEDIV255 0.00390625
void R_SetGlobalColors (void)
{
	
	global_color[GC_BLACK][0] = global_color[GC_BLACK][1] = global_color[GC_BLACK][2] = 0 * ONEDIV255;
	global_color[GC_WHITE][0] =  global_color[GC_WHITE][1] = global_color[GC_WHITE][2] = 255 * ONEDIV255;
	global_color[GC_LYGRAY][0] = global_color[GC_LYGRAY][1] = global_color[GC_LYGRAY][2] = 128 * ONEDIV255;
	global_color[GC_DKGRAY][0] = global_color[GC_DKGRAY][1] = global_color[GC_DKGRAY][2] = 64 * ONEDIV255;

	global_color[GC_BLUE][0] = 0 * ONEDIV255;
	global_color[GC_BLUE][1] = 0 * ONEDIV255;
	global_color[GC_BLUE][2] = 128 * ONEDIV255;

	global_color[GC_GREEN][0] = 0 * ONEDIV255;
	global_color[GC_GREEN][1] = 128 * ONEDIV255;
	global_color[GC_GREEN][2] = 0 * ONEDIV255;

	global_color[GC_CYAN][0] = 0 * ONEDIV255;
	global_color[GC_CYAN][1] = 0 * ONEDIV255;
	global_color[GC_CYAN][2] = 0 * ONEDIV255;

	global_color[GC_RED][0] = 128 * ONEDIV255;
	global_color[GC_RED][1] = 0 * ONEDIV255;
	global_color[GC_RED][2] = 0 * ONEDIV255;

	global_color[GC_PURPLE][0] = 128 * ONEDIV255;
	global_color[GC_PURPLE][1] = 0 * ONEDIV255;
	global_color[GC_PURPLE][2] = 128 * ONEDIV255;

	global_color[GC_BROWN][0] = 128 * ONEDIV255;
	global_color[GC_BROWN][1] = 64 * ONEDIV255;
	global_color[GC_BROWN][2] = 0 * ONEDIV255;

	global_color[GC_LTBLUE][0] = 64 * ONEDIV255;
	global_color[GC_LTBLUE][1] = 64 * ONEDIV255;
	global_color[GC_LTBLUE][2] = 255 * ONEDIV255;

	global_color[GC_LTGRN][0] = 64 * ONEDIV255;
	global_color[GC_LTGRN][1] = 255 * ONEDIV255;
	global_color[GC_LTGRN][2] = 64 * ONEDIV255;

	global_color[GC_LTCYAN][0] = 64 * ONEDIV255;
	global_color[GC_LTCYAN][1] = 255 * ONEDIV255;
	global_color[GC_LTCYAN][2] = 255 * ONEDIV255;

	global_color[GC_LTRED][0] = 255 * ONEDIV255;
	global_color[GC_LTRED][1] = 64 * ONEDIV255;
	global_color[GC_LTRED][2] = 64 * ONEDIV255;

	global_color[GC_LTPURPLE][0] = 255 * ONEDIV255;
	global_color[GC_LTPURPLE][1] = 64 * ONEDIV255;
	global_color[GC_LTPURPLE][1] = 255 * ONEDIV255;

	global_color[GC_YELLOW][0] = 255 * ONEDIV255;
	global_color[GC_YELLOW][1] = 255 * ONEDIV255;
	global_color[GC_YELLOW][2] = 64 * ONEDIV255;

}

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

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

	if (r_nocull->value)
		return false;

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


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_DrawAlphaSprite
=================
*/
void R_DrawAlphaSprite (entity_t *e)
{
	float midalpha, edgealpha;
	dsprframe_t	*frame;
	float		up [3], right [3];
	dsprite_t		*psprite;
	int			origin_x, origin_y;
	int			height, width;
	vec3_t		points[5];

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

/*
	if (e->flags & SPR_ORIENTED)
	{
		vec3_t		v_forward, v_right, v_up;

		AngleVectors (currententity->angles, v_forward, v_right, v_up);
		up [0] = v_up [0];
		up [1] = v_up [1];
		up [2] = v_up [2];
		right [0] = v_right [0];
		right [1] = v_right [1];
		right [2] = v_right [2];
	}
	else
*/
	
	{
		up [0] = vup [0];
		up [1] = vup [1];
		up [2] = vup [2];
		right [0] = vright [0];
		right [1] = vright [1];
		right [2] = vright [2];
//		up = vup;
//		right = vright;
	}

	if (currententity->render_scale [0] == 0 && currententity->render_scale [1] == 0 &&
		currententity->render_scale [2] == 0)
		ri.Sys_Error (ERR_FATAL, "Entity with sprite %s has 0, 0, 0 render_scale.\n", currentmodel->name);


	up [0] *= currententity->render_scale [1];
	up [1] *= currententity->render_scale [1];
	up [2] *= currententity->render_scale [1];

	right [0] *= currententity->render_scale [0];
	right [1] *= currententity->render_scale [0];
	right [2] *= currententity->render_scale [0];

	height = frame->height * currententity->render_scale [1];
	width = frame->width * currententity->render_scale [0];
	origin_y = frame->origin_y * currententity->render_scale [1];
	origin_x = frame->origin_x * currententity->render_scale [0];

	qglEnable( GL_BLEND );

	protex_GL_Bind(currentmodel->skins[e->frame]);

	GL_TexEnv( GL_MODULATE );

	qglEnable (GL_ALPHA_TEST);

	// generate points
	VectorMA (e->origin, -origin_y, up, points[1]);
	VectorMA (points[1], -origin_x, right, points[1]);
	
	VectorMA (e->origin, height - origin_y, up, points[2]);
	VectorMA (points[2], -origin_x, right, points[2]);

	VectorMA (e->origin, height - origin_y, up, points[3]);
	VectorMA (points[3], width - origin_x, right, points[3]);

	VectorMA (e->origin, -origin_y, up, points[4]);
	VectorMA (points[4], width - origin_x, right, points[4]);

	VectorMA (e->origin, -origin_y, up, points[0]);
	VectorMA (points[0], width - origin_x, right, points[0]);
	VectorMA (points[0], -4, right, points[0]);

	points[0][0] = points[1][0] + points[2][0] / 2;
	points[0][1] = points[1][1] + points[4][1] / 2;
	points[0][2] = points[1][2];
	
	qglDisable (GL_CULL_FACE);

	midalpha = 0; 
	edgealpha = 1;
	qglBegin (GL_TRIANGLES);

	qglColor4f(1, 1, 1, midalpha);
	qglTexCoord2f(0.5, 0.5);
	qglColor4f(1, 1, 1, edgealpha);
	qglVertex3fv (points[0]);
	qglTexCoord2f(0, 1);
	qglVertex3fv (points[1]);
	qglTexCoord2f(0, 0);
	qglVertex3fv (points[2]);

///*
	qglColor4f(1, 1, 1, midalpha);
	qglTexCoord2f(0.5, 0.5);
	qglColor4f(1, 1, 1, edgealpha);
	qglVertex3fv (points[0]);
	qglTexCoord2f(0, 0);
	qglVertex3fv (points[2]);
	qglTexCoord2f(1, 0);
	qglVertex3fv (points[3]);

	qglColor4f(1, 1, 1, midalpha);
	qglTexCoord2f(0.5, 0.5);
	qglColor4f(1, 1, 1, edgealpha);
	qglVertex3fv (points[0]);
	qglTexCoord2f(1, 0);
	qglVertex3fv (points[3]);
	qglTexCoord2f(1, 1);
	qglVertex3fv (points[4]);

	qglColor4f(1, 1, 1, midalpha);
	qglTexCoord2f(0.5, 0.5);
	qglColor4f(1, 1, 1, edgealpha);
	qglVertex3fv (points[0]);
	qglTexCoord2f(1, 1);
	qglVertex3fv (points[4]);
	qglTexCoord2f(0, 1);
	qglVertex3fv (points[1]);
//*/

	qglEnd ();

	qglDisable (GL_ALPHA_TEST);

	GL_TexEnv( GL_REPLACE );

	qglEnable( GL_CULL_FACE );

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

/*
=================
R_DrawSpriteModel
=================
*/
void R_DrawSpriteModel (entity_t *e)
{
	float alpha = 1.0F;
	vec3_t	point;
	dsprframe_t	*frame;
	float		up [3], right [3];
	dsprite_t		*psprite;

	int			origin_x, origin_y;
	int			height, width;
	image_t		*image;

	// don't even bother culling, because it's just a single
	// polygon without a surface cache

	image = currentmodel->skins[e->frame];

	psprite = (dsprite_t *)currentmodel->extradata;

	if (e->frame < 0 || e->frame >= psprite->numframes)
	{
//		ri.Con_Printf (PRINT_ALL, "no such sprite frame %i\n", e->frame);
		e->frame = 0;
	}

	e->frame %= psprite->numframes;

	frame = &psprite->frames[e->frame];

	if (e->flags & SPR_ORIENTED)
	{
		vec3_t		v_forward, v_right, v_up;

		AngleVectors (currententity->angles, v_forward, v_right, v_up);
		up [0] = v_up [0];
		up [1] = v_up [1];
		up [2] = v_up [2];
		right [0] = v_right [0];
		right [1] = v_right [1];
		right [2] = v_right [2];
	}
	else
	{
		up [0] = vup [0];
		up [1] = vup [1];
		up [2] = vup [2];
		right [0] = vright [0];
		right [1] = vright [1];
		right [2] = vright [2];
//		up = vup;
//		right = vright;
	}

	if (currententity->render_scale [0] == 0 && currententity->render_scale [1] == 0 &&
		currententity->render_scale [2] == 0)
		ri.Sys_Error (ERR_FATAL, "Entity with sprite %s has 0, 0, 0 render_scale.\n", currentmodel->name);


	up [0] *= currententity->render_scale [1];
	up [1] *= currententity->render_scale [1];
	up [2] *= currententity->render_scale [1];

	right [0] *= currententity->render_scale [0];
	right [1] *= currententity->render_scale [0];
	right [2] *= currententity->render_scale [0];

	height = frame->height * currententity->render_scale [1];
	width = frame->width * currententity->render_scale [0];
	origin_y = frame->origin_y * currententity->render_scale [1];
	origin_x = frame->origin_x * currententity->render_scale [0];

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

	if ( alpha != 1.0F)
		qglEnable( GL_BLEND );

	if (!image)
		image = r_notexture;

	protex_GL_Bind(image);

	GL_TexEnv( GL_MODULATE );

	if ( alpha == 1.0)
		qglEnable (GL_ALPHA_TEST);
	else
		qglDisable( GL_ALPHA_TEST );

	// setup for alpha blended flares
	if (e->flags & SPR_ALPHACHANNEL)
	{
		alpha = 1.0f;
		qglDepthMask (GL_FALSE);
		qglEnable( GL_BLEND );
		qglDisable( GL_ALPHA_TEST );
	}

	// double sided sprites
	if (e->flags & SPR_ORIENTED)
		qglDisable( GL_CULL_FACE );

	qglColor4f( 1, 1, 1, alpha );

	qglBegin (GL_QUADS);

	qglTexCoord2f (0, 1);

	VectorMA (e->origin, -origin_y, up, point);
	VectorMA (point, -origin_x, right, point);
	qglVertex3fv (point);

	qglTexCoord2f (0, 0);
	VectorMA (e->origin, height - origin_y, up, point);
	VectorMA (point, -origin_x, right, point);
	qglVertex3fv (point);

	qglTexCoord2f (1, 0);
	VectorMA (e->origin, height - origin_y, up, point);
	VectorMA (point, width - origin_x, right, point);
	qglVertex3fv (point);

	qglTexCoord2f (1, 1);
	VectorMA (e->origin, -origin_y, up, point);
	VectorMA (point, width - origin_x, right, point);
	qglVertex3fv (point);

	qglEnd ();

	qglDisable (GL_ALPHA_TEST);

	GL_TexEnv( GL_REPLACE );

	if (e->flags & SPR_ALPHACHANNEL)
	{
		qglDepthMask (GL_TRUE);
		qglDisable( GL_BLEND );
	}

	if ( alpha != 1.0F)
		qglDisable( GL_BLEND );

	if (e->flags & SPR_ORIENTED)
		qglEnable( GL_CULL_FACE );

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

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

///////////////////////////////////////////////////////////////////////////////
//	R_GetProjectedVert
//
//	Nelno:	this isn't working yet
//	qglGetIntegerv seems to be returning monkey fuck as does qglGetDoublev
///////////////////////////////////////////////////////////////////////////////

#ifdef	gluProject_DOESNT_WORK_YET
int	R_GetProjectedVert (vec3_t org, int *x, int *y)
{
	GLdouble	mv_matrix [16];
	GLdouble	proj_matrix [16];
	GLdouble	winx, winy, winz;

	float		unfucked_mv_matrix [16];
	float		unfucked_proj_matrix [16];
	int			i, result;

	//	getting these with qglGetDoublev returns totally fucked values

//	qglGetDoublev (GL_MODELVIEW_MATRIX, mv_matrix);
	qglGetFloatv (GL_MODELVIEW_MATRIX, unfucked_mv_matrix);

//	qglGetDoublev (GL_PROJECTION_MATRIX, proj_matrix);
	qglGetFloatv (GL_PROJECTION_MATRIX, unfucked_proj_matrix);

	winx = winy = winz = 0;

	for (i = 0; i < 16; i++)
	{
		mv_matrix [i] = unfucked_mv_matrix [i];
		proj_matrix [i] = r_proj_matrix [i];
	}

	result = gluProject (org [0], org [1], org [2], mv_matrix, proj_matrix, r_viewport, &winx, &winy, &winz);

	if (result == GL_TRUE)
	{
		*x = (int)winx;
		*y = (int)winy;
		return	true;
	}
	else
	{
		*x = -1;
		*y = -1;
		return	false;
	}
}
#endif

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

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

//	R_GetProjectedVert (currententity->origin, &x, &y);
//	ri.dk_printxy (x, y, "NULL model");

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

/*
=============
R_DrawEntitiesOnList
=============
*/
void R_DrawBeam2( entity_t *e );
void R_DrawLaser( entity_t *e ,image_t *lasertexture);
void R_DrawLightning( entity_t *e ,image_t *lasertexture);
void R_DrawEntitiesOnList (void)
{
	int		i;
	int		ent_count = 0;

	if (!r_drawentities->value)
		return;

	ent_draw_count = 0;

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

		ent_count++;
/*
		if ( currententity->flags & RF_BEAM )
		{
			R_DrawLaser( currententity );
		}
		else if ( currententity->flags & RF_SPOTLIGHT )
		{
			R_DrawBeam2( currententity );
		}
		else
*/
		{
			currentmodel = currententity->model;
			if (!currentmodel)
			{
				R_DrawNullModel ();
				continue;
			}
			switch (currentmodel->type)
			{
			case mod_alias:
				if (r_drawalias && r_drawalias->value)
					R_DrawAliasModel (currententity);
				break;
			case mod_brush:
				if (r_drawbmodels && r_drawbmodels->value)
					R_DrawBrushModel (currententity);
				break;
			case mod_sprite:
				if (r_drawsprites && r_drawsprites->value)
					R_DrawSpriteModel (currententity);
//					R_DrawAlphaSprite (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

	for (i=0 ; i<r_newrefdef.num_entities ; i++)
	{
		currententity = &r_newrefdef.entities[i];

		if ( currententity->flags & RF_TRACER)
		{
			R_DrawLaser ( currententity , r_tracertexture);
			continue;
		}

		if ( currententity->flags & (RF_BEAM | RF_LASERBEAM | RF_BEAM_MOVING))
		{
			R_DrawLaser ( currententity , r_lasertexture);
			continue;
		}

		if ( currententity->flags & RF_LIGHTNING)
		{
         image_t *list[]={r_lightningtexture1,r_lightningtexture2,r_lightningtexture3};

			R_DrawLightning(currententity,list[rand()%1]);
			continue;
		}

		if ( currententity->flags & RF_NOVALASER)
		{
			R_DrawLaser ( currententity , r_novalasertexture);
			continue;
		}

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

		ent_count++;
/*
		if ( currententity->flags & RF_BEAM )
		{
			R_DrawBeam( currententity );
		}
		else 
*/
		if ( currententity->flags & RF_SPOTLIGHT )
		{
			R_DrawBeam2( currententity );
		}
		else
		{
			currentmodel = currententity->model;

			if (!currentmodel)
			{
				R_DrawNullModel ();
				continue;
			}
			switch (currentmodel->type)
			{
			case mod_alias:
				if (r_drawalias && r_drawalias->value)
					R_DrawAliasModel (currententity);
				break;
			case mod_brush:
				if (r_drawbmodels && r_drawbmodels->value)
					R_DrawBrushModel (currententity);
				break;
			case mod_sprite:
				if (r_drawsprites && r_drawsprites->value)
					R_DrawSpriteModel (currententity);
//					R_DrawAlphaSprite (currententity);
				break;
			default:
				ri.Sys_Error (ERR_DROP, "Bad modeltype");
				break;
			}
		}
	}
	qglDepthMask (1);		// back to writing

	if (r_countents && r_countents->value)
		ri.Con_Printf (PRINT_ALL, "%i entities drawn.\n", ent_draw_count);
}

/*
** GL_DrawParticles
**
*/
void GL_DrawParticles( int num_particles, const particle_t particles[], const unsigned colortable[768] )
{
	const particle_t *p;
	int				i;
	vec3_t			up, right;
	float			scale;
	byte			color[4];

    GL_Bind(r_particletexture [PARTICLE_SIMPLE]->texnum);
	qglDepthMask( GL_FALSE );		// no z buffering
	qglEnable( GL_BLEND );
	GL_TexEnv( GL_MODULATE );
	qglBegin( GL_TRIANGLES );

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

	for ( p = particles, i=0 ; i < num_particles ; i++,p++)
	{
		// 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];

		if (scale < 20)
			scale = 1;
		else
			scale = 1 + scale * 0.004;

		*(int *)color = colortable[p->color];
		color[3] = p->alpha*255;

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

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

///////////////////////////////////////////////////////////////////////////////
//	GL_GenerateParticleRotations
//
//	only done once per frame for all rotating particles
///////////////////////////////////////////////////////////////////////////////

#ifdef	ROTATING_SNOW
void	GL_GenerateParticleRotations (void)
{
	int		i;
	vec3_t	ass;

	//	generate MAX_ROTATING_PARTICLES sets of angle vectors for rotating snow
	if (!generated_particle_rotations)
	{
		for (i = 0; i < MAX_PARTICLE_ROTATIONS; i++)
			AngleVectors (rpart_angles [i], ass, rpart_right [i], rpart_up [i]);

		//	so that vectors are only generated when rotating particles
		//	are present
		generated_particle_rotations = true;
	}
}
#endif

///////////////////////////////////////////////////////////////////////////////
//	GL_DrawComplexParticles
//
///////////////////////////////////////////////////////////////////////////////

void GL_DrawComplexParticles( int num_particles, const particle_t particles[], const unsigned colortable[768] )
{
	const			particle_t *p;
	int				i, p_type;
	vec3_t			up, right;
	float			scale;
	byte			color[4];
	int				snow_count = 0;
#ifdef	ROTATING_SNOW
	int				rotation_vec;
#endif

	if (num_particles == 0)
		return;

	qglDepthMask( GL_FALSE );		// no z buffering
	qglEnable( GL_BLEND );

	GL_TexEnv( GL_MODULATE );

	for (p_type = PARTICLE_SNOW; p_type < NUM_PARTICLETYPES; p_type++)	
	{
		//	Nelno:	should bind correct texture for particle type here
		GL_Bind(r_particletexture [p_type]->texnum);

		switch	(p_type)
		{
			case	PARTICLE_RAIN:
				color [0] = color [1] = color [2] = 255;
				VectorScale (vup, 7.0, up);
				VectorScale (vright, 1.5, vright);
				break;
			case	PARTICLE_SNOW:
				//	snow is two-sided so that it can twist
#ifdef	ROTATING_SNOW
				qglDisable (GL_CULL_FACE);

				GL_GenerateParticleRotations ();
#endif
				color [0] = color [1] = color [2] = color [3] = 255;
				VectorScale (vup, 2.3, up);
				VectorScale (vright, 2.3, right);		
				break;
			case	PARTICLE_SMOKE:
				VectorScale (vup, 7, up);
				VectorScale (vright, 7, right);		
				break;
			case	PARTICLE_BIG_SPARKS:
				*(int *) color = colortable [72];
				VectorScale (vup, 3, up);
				VectorScale (vright, 3, right);
				break;
			case	PARTICLE_POISON:
				*(int *) color = colortable [108];
				VectorScale (vup, 1, up);
				VectorScale (vright, 1, right);
				break;
			case	PARTICLE_BLOOD:
				*(int *) color = colortable [69];
				VectorScale (vup, 1.5, up);
				VectorScale (vright, 1.5, right);
				break;
			case	PARTICLE_SPARKS:
				*(int *) color = colortable [72];
				VectorScale (vup, 1.5, up);
				VectorScale (vright, 1.5, right);		
				break;
			case	PARTICLE_BLUE_SPARKS:
				color [0] = color [1] = 128;
				color [2] = 255;
				VectorScale (vup, 3.0, up);
				VectorScale (vright, 3.0, right);		
				break;
			case	PARTICLE_ICE:
//            *(int *)color = colortable[p->color];
//				color [0] = color [1] = 128;
//				color [2] = 255;
//				VectorScale (vup, 3.0, up);
//				VectorScale (vright, 3.0, right);		
				break;
			default:
				color [0] = color [1] = color [2] = 255;
				VectorScale (vup, 1.5, up);
				VectorScale (vright, 1.5, right);		
				break;
		}

		qglBegin( GL_TRIANGLES );

		//	Nelno:	FIXME this is a hack...
		if (p_type == PARTICLE_SMOKE)
		{
			for ( p = particles, i=0 ; i < num_particles ; i++,p++)
			{
				//	smoke particles take color info from p->color
				if (p->type != p_type)
					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];

				if (scale < 20)
					scale = 1;
				else
					scale = 1 + scale * 0.004;

            if (p->pscale)
               scale=scale*p->pscale;

				*(int *)color = colortable [p->color];
				color[3] = p->alpha*255;

				qglColor4ubv( 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);
			}
		}
		//	Nelno:	FIXME:	make type flag for particle rotation
		else if (p_type == PARTICLE_SNOW)
		{
			for ( p = particles, i=0 ; i < num_particles ; i++,p++)
			{
				if (p->type != p_type)
					continue;

				snow_count++;

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

				if (scale < 20)
					scale = 1;
				else
					scale = 1 + scale * 0.004;

				//			*(int *)color = colortable[p->color];
//				color[3] = p->alpha*255;

#ifdef	ROTATING_SNOW
				scale *= 2.3 * p->size_scale;

				//	vector num for this particle
				rotation_vec = p->rotation_vec;

				qglColor4ubv( color );

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

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

				qglTexCoord2f(0.0625, 1.0625);
				qglVertex3f( p->origin [0] + rpart_right [rotation_vec][0]*scale, 
							 p->origin [1] + rpart_right [rotation_vec][1]*scale, 
							 p->origin [2] + rpart_right [rotation_vec][2]*scale);
#else
				scale *= p->size_scale;

				qglColor4ubv( 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);
#endif
			}
		}
		else
		{
			for ( p = particles, i=0 ; i < num_particles ; i++,p++)
			{
				if (p->type != p_type)
					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];

				if (scale < 20)
					scale = 1;
				else
					scale = 1 + scale * 0.004;

            if (p->pscale)
               scale=scale*p->pscale;

 				//	*(int *)color = colortable [p->color];
      		color[3] = p->alpha*255;

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

		}
		
		qglEnd ();
	}
	qglEnable (GL_CULL_FACE);
	qglDisable( GL_BLEND );
	qglColor4f( 1,1,1,1 );
	qglDepthMask( 1 );		// back to normal Z buffering

	GL_TexEnv( GL_REPLACE );
}

/*
===============
R_DrawParticles
===============
*/
void R_DrawParticles (void)
{
	qglDisable (GL_FOG);

	if (!r_drawparticleflag->value)
		return; 

	if ( gl_ext_pointparameters->value && qglPointParameterfEXT )
	{
		int i;
		unsigned char color[4];
		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++ )
		{
			*(int *)color = d_8to24table[p->color];
			color[3] = p->alpha*255;
			qglColor4ubv( 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, d_8to24table );

	if (r_newrefdef.foginfo.active)
		qglEnable (GL_FOG);
}


/*
============
R_DrawComplexParticles
============
*/
void R_DrawComplexParticles (void)
{
	if (!r_drawparticleflag->value || !r_drawcomparticles->value)
		return; 

	qglDisable (GL_FOG);

	GL_DrawComplexParticles( r_newrefdef.num_comparticles, r_newrefdef.comparticles, d_8to24table );

	if (r_newrefdef.foginfo.active)
		qglEnable (GL_FOG);
}

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

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

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


void R_SetFrustum (void)
{
	int		i;

#if 0
	/*
	** this code is wrong, since it presume a 90 degree FOV both in the
	** horizontal and vertical plane
	*/
	// 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);

	// we theoretically don't need to normalize these vectors, but I do it
	// anyway so that debugging is a little easier
	VectorNormalize( frustum[0].normal );
	VectorNormalize( frustum[1].normal );
	VectorNormalize( frustum[2].normal );
	VectorNormalize( frustum[3].normal );
#else
	// 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 ) );
#endif

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

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

/*
===============
R_SetupFrame
===============
*/
void R_SetupFrame (void)
{
	int 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_CLEARCOLORBUFF)
			qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
		else
			qglClear( 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
=============
*/
void R_SetupGL (void)
{
	float	screenaspect;
//	float	yfov;
	int		x, x2, y2, y, w, h;
	GLfloat fogcolor[4];

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

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

	//	Nelno:	save for getting projected vertices
	r_viewport [0] = x;
	r_viewport [1] = y2;
	r_viewport [2] = w;
	r_viewport [3] = h;

	qglViewport (x, y2, w, h);

	// setup fog
	if (r_newrefdef.foginfo.active)
	{
		fogcolor[0] = r_newrefdef.foginfo.r;
		fogcolor[1] = r_newrefdef.foginfo.g;
		fogcolor[2] = r_newrefdef.foginfo.b;
		fogcolor[3] = 0.20;

		qglEnable (GL_FOG);
		qglFogi (GL_FOG_MODE, GL_LINEAR);
		qglFogfv (GL_FOG_COLOR, fogcolor);
		qglHint (GL_FOG_HINT, GL_NICEST);
		qglFogf (GL_FOG_START, r_newrefdef.foginfo.start);
		qglFogf (GL_FOG_END, r_newrefdef.foginfo.end);
	}
	else
		qglDisable (GL_FOG);

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

	qglGetFloatv (GL_PROJECTION_MATRIX, r_proj_matrix);

	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
=============
*/
void R_Clear (void)
{
	if (gl_ztrick->value)
	{
		static int trickframe;

		if (gl_clear->value)
			qglClear (GL_COLOR_BUFFER_BIT);

		trickframe++;
		if (trickframe & 1)
		{
			gldepthmin = 0;
			gldepthmax = 0.49999;
			qglDepthFunc (GL_LEQUAL);
		}
		else
		{
			gldepthmin = 1;
			gldepthmax = 0.5;
			qglDepthFunc (GL_GEQUAL);
		}
	}
	else
	{
		if (gl_clear->value)
			qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		else
			qglClear (GL_DEPTH_BUFFER_BIT);
		gldepthmin = 0;
		gldepthmax = 1;
		qglDepthFunc (GL_LEQUAL);
	}

	qglDepthRange (gldepthmin, gldepthmax);

}

void R_Flash( void )
{
#if 0
	if ( qwglSetDeviceGammaRampEXT != 0 )
	{
		static qboolean flashed;

		unsigned char red[256], green[256], blue[256];
		int i;

		if ( ( v_blend[3] == 0.0F ) && flashed )
		{
			flashed = false;

			qwglSetDeviceGammaRampEXT( gl_state.originalRedGammaTable, 
									   gl_state.originalGreenGammaTable,
									   gl_state.originalBlueGammaTable );
		}
		else if ( v_blend[3] != 0.0F )
		{
			flashed = true;

			for ( i = 0; i < 256; i++ )
			{
				red[i] = i;
				green[i] = 0;
				blue[i] = 0;
			}

			qwglSetDeviceGammaRampEXT( red, green, blue );
		}
	}
	else
#endif
	{
		R_PolyBlend ();
	}
}

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

r_newrefdef must be set before the first call
================
*/
void R_RenderView (refdef_t *fd)
{
	if (r_norefresh->value)
		return;

	r_newrefdef = *fd;

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

	if (r_speeds->value)
	{
		c_brush_polys = 0;
		c_alias_polys = 0;
	}

	g_PVCullCount = 0;
	g_PVDrawCount = 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_DrawComplexParticles ();		//  Shawn:  Added for Daikatana

//	R_DrawAlphaLightSurfaces ();

	R_DrawAlphaSurfaces ();
	
	R_Flash ();

	if (r_speeds->value)
	{
		ri.Con_Printf (PRINT_ALL, "%4i wpoly %4i epoly %i tex %i lmaps %.2f lmap MBs\n",
			c_brush_polys, 
			c_alias_polys, 
			c_visible_textures, 
			c_visible_lightmaps, c_visible_lightmap_bytes / 1024000.0); 

		ri.Con_Printf (PRINT_ALL, "%9i bytes image memory\n", c_texture_memory);
	}

	if (r_countpvolumes->value)
		ri.dk_printxy (0, 64, "Drew %i, culled %i\n", g_PVDrawCount, g_PVCullCount);
}


void	R_SetGL2D (void)
{
	// set 2D virtual screen size
	qglViewport (0,0, vid.width, vid.height);
	qglMatrixMode(GL_PROJECTION);
    qglLoadIdentity ();
	qglOrtho  (0, vid.width, vid.height, 0, -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);
}

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


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

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

	// 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_InitTiming
//
//	initialize timing vars when render dll starts up
/////////////////////////////////////////////////////////////////////////////////

void	R_InitTiming (void)
{
	ref_curTime = 0;
	ref_frameTime = 0;
	ref_lastTime = timeGetTime ();
	ref_globalTime = 0;
}

#ifdef	ROTATING_SNOW

///////////////////////////////////////////////////////////////////////////////
//	R_InitRotationSpeeds
//
///////////////////////////////////////////////////////////////////////////////
void	R_InitRotationSpeeds (void)
{
	int	i;

	for (i = 0; i < MAX_PARTICLE_ROTATIONS; i++)
	{
		rpart_ang_speed [i][0] = (rand () / RAND_MAX) * 720 + 180;
		rpart_ang_speed [i][1] = (rand () / RAND_MAX) * 720 + 180;
		rpart_ang_speed [i][2] = (rand () / RAND_MAX) * 720 + 180;
	}
}

///////////////////////////////////////////////////////////////////////////////
//	R_InitRotations
//
///////////////////////////////////////////////////////////////////////////////

void	R_InitRotations (void)
{
	int		i;

	ref_laserRotation = 0;
	ref_curTime = ref_lastTime = timeGetTime ();

	for (i = 0; i < MAX_PARTICLE_ROTATIONS; i++)
	{
		rpart_angles [i][0] = (rand () / RAND_MAX) * 360.0;
		rpart_angles [i][1] = (rand () / RAND_MAX) * 360.0;
		rpart_angles [i][2] = 0;
	}

	R_InitRotationSpeeds ();
}

#endif

///////////////////////////////////////////////////////////////////////////////
//	R_DoRotations
//
///////////////////////////////////////////////////////////////////////////////

void	R_DoRotations (void)
{
#ifdef	ROTATING_SNOW
	int		i;
#endif

	ref_laserRotation = (ref_laserRotation + (60 * ref_frameTime * (float) ref_laserDir));
	if (ref_laserDir == 1 && ref_laserRotation > 360)
	{
//		ref_laserDir = -1;
//		ref_laserRotation = 360.0;
		ref_laserRotation -= 360.0;
	}
/*
	else if (ref_laserRotation < 0)
	{
		ref_laserDir = 1;
		ref_laserRotation = 0,0;
	}
*/

	///////////////////////////////////////////////////////////////////////////
	//	calc angles for rotating particles
	///////////////////////////////////////////////////////////////////////////

#ifdef	ROTATING_SNOW
	for (i = 0; i < MAX_PARTICLE_ROTATIONS; i++)
	{
		if (i % 2)
		{
			//	odd numbered particles rotate left
			rpart_angles [i][0] += (rpart_ang_speed [i][0] * ref_frameTime);
			
			if (rpart_angles [i][0] > 360)
				rpart_angles [i][0] -= 360;

			rpart_angles [i][1] += (rpart_ang_speed [i][1] * ref_frameTime);
			
			if (rpart_angles [i][1] > 360)
				rpart_angles [i][1] -= 360;

			rpart_angles [i][2] += (rpart_ang_speed [i][2] * ref_frameTime);
			
			if (rpart_angles [i][2] > 360)
				rpart_angles [i][2] -= 360;
		}
		else
		{
			//	even numbered particles rotate right
			rpart_angles [i][0] -= (rpart_ang_speed [i][0] * ref_frameTime);

			if (rpart_angles [i][0] < 0)
				rpart_angles [i][0] += 360;

			rpart_angles [i][1] -= (rpart_ang_speed [i][1] * ref_frameTime);

			if (rpart_angles [i][1] < 0)
				rpart_angles [i][1] += 360;

			rpart_angles [i][2] -= (rpart_ang_speed [i][2] * ref_frameTime);

			if (rpart_angles [i][2] < 0)
				rpart_angles [i][2] += 360;
		}
	}

	generated_particle_rotations = false;
#endif
}

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

@@@@@@@@@@@@@@@@@@@@@
*/
void R_RenderFrame (refdef_t *fd)
{
	///////////////////////////////////////////////////////////////////////////
	//	calc frame time for time-based animations, rotations and procedural stuff
	///////////////////////////////////////////////////////////////////////////
	ref_curTime = timeGetTime ();
	ref_frameTime = ((float) (ref_curTime - ref_lastTime)) / 1000.0;
	ref_lastTime = ref_curTime;
	ref_globalTime += ref_frameTime;

	R_DoRotations ();

	//	init all procedural texture for this frame
	protex_InitForFrame ();

	R_RenderView (fd);

	R_SetLightLevel ();
	R_SetGL2D ();
}

//extern void LOD_GetSnapShot ( void );

void R_Register( void )
{
	r_countpvolumes = ri.Cvar_Get ("r_pvolumes", "0", 0);
	r_countsnow = ri.Cvar_Get ("r_countsnow", "0", 0);
	r_countents = ri.Cvar_Get ("r_countents", "0", 0);
	r_drawbmodels = ri.Cvar_Get ("r_drawbmodels", "1", 0);
	r_drawalias = ri.Cvar_Get ("r_drawalias", "1", 0);
	r_drawsprites = ri.Cvar_Get ("r_drawsprites", "1", 0);	
	r_drawweapon = ri.Cvar_Get ("r_drawweapon", "1", 0);
	r_drawcomparticles = ri.Cvar_Get ("r_drawcomparticles", "1", 0);

	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", "3", CVAR_ARCHIVE);
	gl_lightmap = ri.Cvar_Get ("gl_lightmap", "0", 0);
	gl_shadows = ri.Cvar_Get ("gl_shadows", "0", CVAR_ARCHIVE);
	gl_dynamic = ri.Cvar_Get ("gl_dynamic", "1", 0);
	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", 0);
	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", 0);
	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 );
	gl_driver = ri.Cvar_Get( "gl_driver", "opengl32", CVAR_ARCHIVE );
	gl_texturemode = ri.Cvar_Get( "gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE );
	gl_lockpvs = ri.Cvar_Get ("gl_lockpvs", "0", 0);
	gl_drawflat = ri.Cvar_Get ("gl_drawflat", "0", 0);
	
	gl_alphalightmap = ri.Cvar_Get ("gl_alphalightmap", "1", 0);

	gl_ext_swapinterval = ri.Cvar_Get( "gl_ext_swapinterval", "1", CVAR_ARCHIVE );
	gl_ext_gamma_control = ri.Cvar_Get( "gl_ext_gamma", "1", CVAR_ARCHIVE );
	gl_ext_palettedtexture = ri.Cvar_Get( "gl_ext_palettedtexture", "0", 0 );
	gl_ext_multitexture = ri.Cvar_Get( "gl_ext_multitexture", "1", CVAR_ARCHIVE );
	gl_ext_pointparameters = ri.Cvar_Get( "gl_ext_pointparameters", "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", "0", CVAR_ARCHIVE );
	vid_gamma = ri.Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE );
	vid_ref = ri.Cvar_Get( "vid_ref", "soft", CVAR_ARCHIVE );

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

	r_drawparticleflag = ri.Cvar_Get( "r_drawparticleflag", "1", 0 );
	r_useimagelog = ri.Cvar_Get( "r_useimagelog", "0", CVAR_ARCHIVE );

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

	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
==================
*/
qboolean R_SetMode (void)
{
	rserr_t err;
	qboolean fullscreen;

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

	fullscreen = vid_fullscreen->value;

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

	if ( ( err = 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 = 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 = 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;
}

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

	R_InitTiming ();		//	Nelno:	set up timing vars

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

	c_texture_memory = 0;

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

	R_Register();

	Draw_GetPalette ();

	// initialize our QGL dynamic bindings
	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 = qglGetString (GL_VENDOR);
	ri.Con_Printf (PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string );
	gl_config.renderer_string = qglGetString (GL_RENDERER);
	ri.Con_Printf (PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string );
	gl_config.version_string = qglGetString (GL_VERSION);
	ri.Con_Printf (PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string );
	gl_config.extensions_string = qglGetString (GL_EXTENSIONS);
	ri.Con_Printf (PRINT_ALL, "GL_EXTENSIONS: %s\n", gl_config.extensions_string );

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

	if ( strstr( renderer_buffer, "voodoo" ) )
	{
		if ( !strstr( renderer_buffer, "rush" ) )
			gl_config.renderer = GL_RENDERER_VOODOO;
		else
			gl_config.renderer = GL_RENDERER_VOODOO_RUSH;
	}

	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;
	else if ( strstr( renderer_buffer, "gdi" ) )
		gl_config.renderer = GL_RENDERER_MCD;
	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
		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" );
	}

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

	/*
	** grab extensions
	*/
#ifdef WIN32
	if ( strstr( gl_config.extensions_string, "WGL_EXT_swap_control" ) )
	{
		qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" );
		ri.Con_Printf( PRINT_ALL, "...enabling WGL_EXT_swap_control\n" );
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...WGL_EXT_swap_control not found\n" );
	}

	if ( strstr( gl_config.extensions_string, "WGL_NVIDIA_gamma_control" ) ||
		 strstr( gl_config.extensions_string, "WGL_EXT_gamma_control" ) )
	{
		if ( gl_ext_gamma_control->value )
		{
			qwglGetDeviceGammaRampEXT = ( BOOL (WINAPI *)(unsigned char*, unsigned char*, unsigned char *)) qwglGetProcAddress( "wglGetDeviceGammaRampNVIDIA" );
			qwglSetDeviceGammaRampEXT = ( BOOL (WINAPI *)(const unsigned char *, const unsigned char *, const unsigned char *)) qwglGetProcAddress( "wglSetDeviceGammaRampNVIDIA" );

			ri.Con_Printf( PRINT_ALL, "...enabling WGL_EXT_gamma_control\n" );

			qwglGetDeviceGammaRampEXT(	gl_state.originalRedGammaTable,
										gl_state.originalGreenGammaTable,
										gl_state.originalBlueGammaTable );
		}
		else
		{
			ri.Con_Printf( PRINT_ALL, "...ignoring WGL_EXT_gamma_control\n" );
		}
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...WGL_EXT_gamma_control not found\n" );
	}

	if ( strstr( 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" );
			ri.Con_Printf( PRINT_ALL, "...using 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" );
	}

	if ( strstr( gl_config.extensions_string, "GL_EXT_paletted_texture" ) && 
		 strstr( gl_config.extensions_string, "GL_EXT_shared_texture_palette" ) )
	{
		if ( gl_ext_palettedtexture->value )
		{
			ri.Con_Printf( PRINT_ALL, "...using GL_EXT_shared_texture_palette\n" );
			qglColorTableEXT = ( void ( APIENTRY * ) ( int, int, int, int, int, const void * ) ) qwglGetProcAddress( "glColorTableEXT" );
		}
		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" );
	}

	if ( strstr( gl_config.extensions_string, "GL_SGIS_multitexture" ) )
	{
		if ( gl_ext_multitexture->value )
		{
			ri.Con_Printf( PRINT_ALL, "...using GL_SGIS_multitexture\n" );
			qglMTexCoord2fSGIS = ( void * ) qwglGetProcAddress( "glMTexCoord2fSGIS" );
			qglSelectTextureSGIS = ( void * ) qwglGetProcAddress( "glSelectTextureSGIS" );
		}
		else
		{
			ri.Con_Printf( PRINT_ALL, "...ignoring GL_SGIS_multitexture\n" );
		}
	}
	else
	{
		ri.Con_Printf( PRINT_ALL, "...GL_SGIS_multitexture not found\n" );
	}
#endif

	GL_SetDefaultState();

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

	R_OpenImageLog ();				//  Shawn:  added for image logging

	GL_InitImages ();
	Mod_Init ();
	R_InitParticleTexture ();
	R_InitMiscTextures ();
	protex_InitTextures ();			//	Nelno:	init procedural texture system

	Draw_InitLocal ();

	R_SetGlobalColors ();

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

#ifdef	ROTATING_SNOW
	R_InitRotations ();
#endif
}

/*
===============
R_Shutdown
===============
*/
void R_Shutdown (void)
{	
	ri.Cmd_RemoveCommand ("modellist");
	ri.Cmd_RemoveCommand ("screenshot");
	ri.Cmd_RemoveCommand ("imagelist");
	ri.Cmd_RemoveCommand ("gl_strings");

	protex_FreeTextures ();

	Mod_FreeAll ();

	GL_ShutdownImages ();

	/*
	** shut down OS specific OpenGL stuff like contexts, etc.
	*/
	GLimp_Shutdown();

	/*
	** shutdown our QGL subsystem
	*/
	QGL_Shutdown();

	R_CloseImageLog ();				//  Shawn:  added for image logging
}



/*
@@@@@@@@@@@@@@@@@@@@@
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 if 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;

		if ( qwglSetDeviceGammaRampEXT != 0 )
		{
		}
		else if ( gl_config.renderer & ( GL_RENDERER_VOODOO ) )
		{
			char envbuffer[1024];
			float g;

			g = 2.00 * ( 0.8 - ( vid_gamma->value - 0.5 ) ) + 1.0F;
			Com_sprintf( envbuffer, sizeof(envbuffer), "SSTV2_GAMMA=%f", g );
			putenv( envbuffer );
			Com_sprintf( envbuffer, sizeof(envbuffer), "SST_GAMMA=%f", g );
			putenv( envbuffer );
		}
	}

	GLimp_BeginFrame( camera_separation );

	/*
	** go into 2D mode
	*/
	qglViewport (0,0, vid.width, vid.height);
	qglMatrixMode(GL_PROJECTION);
    qglLoadIdentity ();
	qglOrtho  (0, vid.width, vid.height, 0, -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);

	/*
	** draw buffer stuff
	*/
	if ( gl_drawbuffer->modified )
	{
		gl_drawbuffer->modified = false;

		if ( gl_state.camera_separation == 0 || !gl_state.stereo_enabled )
		{
			if ( 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;
	}

	/*
	** swapinterval stuff
	*/
	GL_UpdateSwapInterval();

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

/*
=============
R_SetPalette
=============
*/
unsigned r_rawpalette[256];

void R_SetPalette ( const unsigned char *palette)
{
	int		i;

	byte *rp = ( byte * ) r_rawpalette;

	if ( palette )
	{
		for ( i = 0; i < 256; i++ )
		{
			rp[i*4+0] = palette[i*3+0];
			rp[i*4+1] = palette[i*3+1];
			rp[i*4+2] = palette[i*3+2];
			rp[i*4+3] = 0xff;
		}
	}
	else
	{
		for ( i = 0; i < 256; i++ )
		{
			rp[i*4+0] = d_8to24table[i] & 0xff;
			rp[i*4+1] = ( d_8to24table[i] >> 8 ) & 0xff;
			rp[i*4+2] = ( d_8to24table[i] >> 16 ) & 0xff;
			rp[i*4+3] = 0xff;
		}
	}
	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, 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 );

	r = ( d_8to24table[e->skinnum & 0xFF] ) & 0xFF;
	g = ( d_8to24table[e->skinnum & 0xFF] >> 8 ) & 0xFF;
	b = ( d_8to24table[e->skinnum & 0xFF] >> 16 ) & 0xFF;

	r *= 1/255.0F;
	g *= 1/255.0F;
	b *= 1/255.0F;

	qglColor4f( r, g, b, e->alpha );
	//qglColor4f( 1.0, 1.0, 1.0, 1.0 );

	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_TEXTURE_2D );
	qglDisable( GL_BLEND );
	qglDepthMask( GL_TRUE );
}

///////////////////////////////////////////////////////////////////////////////
//	R_DrawLaser
//
// mike: added param so novabeam could use different texture
///////////////////////////////////////////////////////////////////////////////
void R_DrawLaser (entity_t *e,image_t *lasertexture)
{
	int			index;
	float		alpha = 1.0F;
	float		up [3], right [3];
	float width,height,origin_x,origin_y,frame;
   short loop;

	vec3_t		point, angles;

	VectorCopy (e->angles, angles);
   if (!(e->flags & RF_TRACER))
	   angles [2] += ref_laserRotation;
	AngleVectors (angles, NULL, right, up);

   // mike: added this so ripgun laser length could be modified
   // render_scale[0] == length
   // mins[] is the forward vector
   if (e->flags & RF_BEAM_MOVING)
   {
      for (loop=0; loop<3; loop++)
         e->oldorigin[loop]=e->origin[loop]-e->mins[loop]*e->render_scale[0];
   }

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

	if (e->frame < 0)             // -frame is fractional, 1000 = 1  THIS IS FUCKED, but, i can get fracs from an int
		frame = -(float)e->frame / 1000.0;
	else
		frame=e->frame;            // non-neg is normal little frame ...

	width = frame;
	height = frame;
	origin_x = frame;
	origin_y = frame;

	GL_Bind (lasertexture->texnum);

	GL_TexEnv (GL_MODULATE);

	qglDepthMask (GL_FALSE);

	qglDisable (GL_ALPHA_TEST);

	qglEnable( GL_BLEND );

	index = currententity->skinnum & GC_NUMCOLORS - 1;

//	qglColor4f (1, 1, 1, alpha);

	qglColor4f (global_color[index][0], global_color[index][1], global_color[index][2], alpha);

	qglDisable (GL_CULL_FACE);

	qglBegin (GL_QUADS);

   // mike: changed "0" to "0.1" because tracertexture was wrapping
	qglTexCoord2f (0.1, 1);
	VectorMA (e->oldorigin, -height, up, point);
	qglVertex3fv (point);

	qglTexCoord2f (0.1, 0.1);
	VectorMA (e->oldorigin, height, up, point);
	qglVertex3fv (point);

	qglTexCoord2f (1, 0.1);
	VectorMA (e->origin, height, up, point);
	qglVertex3fv (point);

	qglTexCoord2f (1, 1);
	VectorMA (e->origin, -height, up, point);
	qglVertex3fv (point);

//   if (!(e->flags & RF_TRACER))
   {
   	///////////////////////////////////////////////////////////////////////////
	   //	draw second beam rolled 90 degrees
   	///////////////////////////////////////////////////////////////////////////

   	angles [2] += 90;
	   AngleVectors (angles, NULL, right, up);

   	qglTexCoord2f (0.1, 1);                         // mike: added, texture was warped
   	VectorMA (e->oldorigin, -height, up, point);
   	qglVertex3fv (point);

   	qglTexCoord2f (0.1, 0.1);
   	VectorMA (e->oldorigin, height, up, point);
   	qglVertex3fv (point);

   	qglTexCoord2f (1, 0.1);
   	VectorMA (e->origin, height, up, point);
   	qglVertex3fv (point);

   	qglTexCoord2f (1, 1);
   	VectorMA (e->origin, -height, up, point);
   	qglVertex3fv (point);
   }

	qglEnd ();

	qglEnable( GL_CULL_FACE );
	
	qglDisable (GL_ALPHA_TEST);

	GL_TexEnv( GL_REPLACE );

	qglDepthMask (GL_TRUE);
	qglDisable( GL_BLEND );

	qglDisable( GL_BLEND );

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

/*
** R_DrawBeam2
*/
void R_DrawBeam2 (entity_t *e)
{
	int		i, j;
	float	a;
	vec3_t	length;
	float	radius1, radius2;
	vec3_t	start_points[17], end_points[17];
	vec3_t	forward, right, up;

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

	qglDisable (GL_FOG);

	qglDepthMask (0);
	qglDisable (GL_TEXTURE_2D);
	qglShadeModel (GL_SMOOTH);
	qglEnable (GL_BLEND);
	qglBlendFunc (GL_ONE, GL_ONE);

	VectorSubtract (e->oldorigin, e->origin, length);
	radius1 = e->frame;
	radius2 = radius1 + (VectorLength (length) * 0.2);

// create start points
	for (i = 16; i>=0 ; i--)
	{
		a = i/16.0 * M_PI*2;
		for (j=0 ; j<3 ; j++)
			start_points[i][j] = e->origin[j] + forward[j]*radius1 + right[j]*cos(a)*radius1 + up[j]*sin(a)*radius1;
	}

// create end points
	for (i = 16; i>=0 ; i--)
	{
		a = i/16.0 * M_PI*2;
		for (j=0 ; j<3 ; j++)
			end_points[i][j] = e->oldorigin[j] + forward[j]*radius2 + right[j]*cos(a)*radius2 + up[j]*sin(a)*radius2;
	}

// draw the tube (uh, tube?)
	qglBegin (GL_TRIANGLE_STRIP);
		for (i=0 ; i<16 ; i++)
		{
//			qglColor4f (.4, .4, .4, 0.2);
			qglColor4f (global_color[e->skinnum][0], global_color[e->skinnum][1], global_color[e->skinnum][2], 0.2);
			qglVertex3fv (start_points[i]);
			qglVertex3fv (start_points[(i+1)%16]);

			qglColor4f (0, 0, 0, 0.2);
			qglVertex3fv (end_points[i]);
			qglVertex3fv (end_points[(i+1)%16]);
		}
//		qglColor4f (.4, .4, .4, 0.2);
		qglColor4f (global_color[e->skinnum][0], global_color[e->skinnum][1], global_color[e->skinnum][2], 0.2);
		qglVertex3fv (start_points[0]);
		qglColor4f (0, 0, 0, 0.2);
		qglVertex3fv (end_points[0]);
//		qglColor4f (.4, .4, .4, 0.2);
		qglColor4f (global_color[e->skinnum][0], global_color[e->skinnum][1], global_color[e->skinnum][2], 0.2);
		qglVertex3fv (start_points[1]);

	qglEnd ();

	qglColor3f (1,1,1);
	qglDisable (GL_BLEND);
	qglEnable (GL_TEXTURE_2D);
	qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	qglDepthMask (1);

	if (r_newrefdef.foginfo.active)
		qglEnable (GL_FOG);
}

///////////////////////////////////////////////////////////////////////////////
//	R_DrawLightning
///////////////////////////////////////////////////////////////////////////////
void R_DrawLightning(entity_t *e,image_t *lasertexture)
{
	float		alpha = 1.0F;
	float		up [3], right [3];
	float width,height,origin_x,origin_y,frame;
   short loop;

	vec3_t		point, angles;

	VectorCopy (e->angles, angles);
//   if (!(e->flags & RF_TRACER))
//	   angles [2] += ref_laserRotation;
	AngleVectors (angles, NULL, right, up);

   // mike: added this so ripgun laser length could be modified
   // render_scale[0] == length
   // mins[] is the forward vector
   if (e->flags & RF_BEAM_MOVING)
   {
      for (loop=0; loop<3; loop++)
         e->oldorigin[loop]=e->origin[loop]-e->mins[loop]*e->render_scale[0];
   }

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

	if (e->frame < 0)             // -frame is fractional, 1000 = 1  THIS IS FUCKED, but, i can get fracs from an int
		frame = -(float)e->frame / 1000.0;
	else
		frame=e->frame;            // non-neg is normal little frame ...

	width = frame;
	height = frame;
	origin_x = frame;
	origin_y = frame;

	GL_Bind (lasertexture->texnum);

	GL_TexEnv (GL_MODULATE);

	qglDepthMask (GL_FALSE);

	qglDisable (GL_ALPHA_TEST);

	qglEnable( GL_BLEND );

	qglColor4f (1, 1, 1, alpha);

	qglDisable (GL_CULL_FACE);

	qglBegin (GL_QUADS);

	qglTexCoord2f (0, 1);
	VectorMA (e->oldorigin, -height, up, point);
	qglVertex3fv (point);

	qglTexCoord2f (0, 0);
	VectorMA (e->oldorigin, height, up, point);
	qglVertex3fv (point);

	qglTexCoord2f (1, 0);
	VectorMA (e->origin, height, up, point);
	qglVertex3fv (point);

	qglTexCoord2f (1, 1);
	VectorMA (e->origin, -height, up, point);
	qglVertex3fv (point);

   ///////////////////////////////////////////////////////////////////////////
	//	draw second beam rolled 90 degrees
   ///////////////////////////////////////////////////////////////////////////

   angles [2] += 90;
	AngleVectors (angles, NULL, right, up);

   qglTexCoord2f (0, 1);                         // mike: added, texture was warped
   VectorMA (e->oldorigin, -height, up, point);
   qglVertex3fv (point);

   qglTexCoord2f (0, 0);
   VectorMA (e->oldorigin, height, up, point);
   qglVertex3fv (point);

   qglTexCoord2f (1, 0);
   VectorMA (e->origin, height, up, point);
   qglVertex3fv (point);

   qglTexCoord2f (1, 1);
   VectorMA (e->origin, -height, up, point);
   qglVertex3fv (point);

	qglEnd ();

	qglEnable( GL_CULL_FACE );
	
	qglDisable (GL_ALPHA_TEST);

	GL_TexEnv( GL_REPLACE );

	qglDepthMask (GL_TRUE);
	qglDisable( GL_BLEND );

	qglDisable( GL_BLEND );

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

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


void	R_BeginRegistration (const char *map);
struct model_s	*R_RegisterModel (const char *name, int flags);
struct image_s	*R_RegisterSkin (const char *name, int flags);
void R_SetSky (const char *name, float rotate, vec3_t axis);
void	R_EndRegistration (void);

void	R_RenderFrame (refdef_t *fd);

struct image_s	*Draw_FindPic (const char *name, int flags);

void	Draw_Pic (int x, int y, const char *name);
void	Draw_Char (int x, int y, int c);
void	Draw_TileClear (int x, int y, int w, int h, const char *name);
void	Draw_Fill (int x, int y, int w, int h, int c);
void	Draw_FadeScreen (void);

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

@@@@@@@@@@@@@@@@@@@@@
*/
refexport_t GetRefAPI (refimport_t rimp )
{
	refexport_t	re;

	ri = rimp;

	re.api_version = API_VERSION;

	re.PurgeResources = GL_PurgeResources;
	re.BeginRegistration = R_BeginRegistration;
	re.RegisterModel = R_RegisterModel;
	re.RegisterSkin = R_RegisterSkin;
	re.RegisterPic = Draw_FindPic;
	re.SetSky = R_SetSky;
	re.SetPaletteDir = R_SetPaletteDir;
	
	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.DrawStretchRaw = Draw_StretchRaw;

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

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

	re.AppActivate = GLimp_AppActivate;

	//	Nelnonic additions
	//	Nelno:	added for per-level console backgrounds and chars
	re.SetResourceDir = R_SetResourceDir;
	re.DrawConsolePic = Draw_ConsolePic;

	//  Shawn:  added this for getting frame name
	re.GetFrameName = R_FrameNameForModel;
	re.VertInfo = R_VertInfo;
	re.SurfIndex = R_SurfIndex;
	re.TriVerts = R_TriVerts;

	re.RegisterEpisodeSkins = Mod_RegisterEpisodeSkins;

	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 (char *error, ...)
{
	va_list		argptr;
	char		text[1024];

	va_start (argptr, error);
	vsprintf (text, error, argptr);
	va_end (argptr);

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

void Com_Printf (char *fmt, ...)
{
	va_list		argptr;
	char		text[1024];

	va_start (argptr, fmt);
	vsprintf (text, fmt, argptr);
	va_end (argptr);

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

#endif
