/*
 * Copyright (C) 1997-2001 Id Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
/* r_main.c */
#include "gl_local.h"

#include "gl_refl.h"		/* MPO */

/***************************************************************************** */
/* nVidia extensions */
/***************************************************************************** */
PFNGLCOMBINERPARAMETERFVNVPROC qglCombinerParameterfvNV;
PFNGLCOMBINERPARAMETERFNVPROC qglCombinerParameterfNV;
PFNGLCOMBINERPARAMETERIVNVPROC qglCombinerParameterivNV;
PFNGLCOMBINERPARAMETERINVPROC qglCombinerParameteriNV;
PFNGLCOMBINERINPUTNVPROC qglCombinerInputNV;
PFNGLCOMBINEROUTPUTNVPROC qglCombinerOutputNV;
PFNGLFINALCOMBINERINPUTNVPROC qglFinalCombinerInputNV;
PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC qglGetCombinerInputParameterfvNV;
PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC qglGetCombinerInputParameterivNV;
PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC qglGetCombinerOutputParameterfvNV;
PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC qglGetCombinerOutputParameterivNV;
PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC qglGetFinalCombinerInputParameterfvNV;
PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC qglGetFinalCombinerInputParameterivNV;

/***************************************************************************** */

int		g_numGlLights = 0;			/* dynamic lights for models .. */
int		numberOfWallLights = 0;			/* wall lights made into dynamic lights .. :) */
wallLight_t    *wallLightArray[MAX_WALL_LIGHTS];	/* array of wall lights .. 500 should be enough */

void		R_Clear(void);

#ifdef QMAX
#define random()	((rand () & 0x7fff) / ((float)0x7fff))
#define crandom()	(2.0 * (random() - 0.5))
#endif

viddef_t	vid;

refimport_t	ri;

int		GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2;	/* MH add extra texture unit here */
int		maxTextureUnits;			/* MH max number of texture units */

model_t        *r_worldmodel;

float		gldepthmin, gldepthmax;

glconfig_t	gl_config;
glstate_t	gl_state;

#ifdef QMAX
image_t        *r_particletextures[PARTICLE_TYPES];	/* list for particles */
image_t        *r_particlebeam;	/* used for beam ents */

#else
image_t        *r_particletexture;	/* little dot for particles */

#endif
image_t        *r_notexture;	/* use for bad textures */
image_t        *r_shelltexture;	/* c14 added shell texture */
image_t        *r_radarmap;	/* wall texture for radar texgen */
image_t        *r_around;
image_t        *r_caustictexture;	/* Water caustic texture */
image_t        *r_bholetexture;

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;

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_world_matrix[16];
float		r_base_world_matrix[16];

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

int		r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2;

cvar_t         *skydistance;	/* DMP - skybox size change */

#ifdef QMAX
cvar_t         *gl_partscale;
cvar_t         *gl_transrendersort;
cvar_t         *gl_particlelighting;
cvar_t         *gl_particledistance;
#else
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_particles;
#endif

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_model_lightlerp;
cvar_t         *r_model_alpha;
cvar_t         *r_lefthand;

cvar_t         *gl_stainmaps;

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

cvar_t         *r_overbrightbits;

cvar_t         *gl_nosubimage;

cvar_t         *gl_vertex_arrays;

cvar_t         *r_cellshading;
cvar_t         *r_cellshading_width;

cvar_t         *font_color;

cvar_t         *gl_ext_multitexture;
cvar_t         *gl_ext_pointparameters;
cvar_t         *gl_ext_compiled_vertex_array;
cvar_t         *gl_ext_palettedtexture;

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_shadows_alpha;
cvar_t         *gl_shellstencil;
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_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_texturemode;
cvar_t         *gl_texturealphamode;
cvar_t         *gl_texturesolidmode;
cvar_t         *gl_lockpvs;

cvar_t         *gl_3dlabs_broken;

cvar_t         *gl_ext_texture_compression;	/* Heffo - ARB Texture Compression */
cvar_t         *gl_ext_mtexcombine;
cvar_t         *gl_anisotropic;
cvar_t         *gl_anisotropic_avail;
cvar_t         *gl_ext_nv_multisample_filter_hint;	/* r1ch extensions */
cvar_t         *gl_nv_fog;
cvar_t         *gl_lightmap_texture_saturation;	/* jitsaturation */
cvar_t         *gl_lightmap_saturation;	/* jitsaturation */
cvar_t         *gl_screenshot_jpeg;	/* Heffo - JPEG Screenshots */
cvar_t         *gl_screenshot_jpeg_quality;	/* Heffo - JPEG Screenshots */
cvar_t         *gl_water_waves;	/* Water waves */
cvar_t         *gl_water_caustics;	/* Water Caustics */

/* Knightmare- allow disabling the nVidia water warp */
cvar_t         *gl_water_pixel_shader_warp;
cvar_t         *gl_blooms;
cvar_t         *gl_motionblur;	/* motionblur */
cvar_t         *gl_motionblur_intensity;	/* motionblur */

/* ========= Engine Fog ============== */
cvar_t         *gl_fogenable;	  /* Enable */
cvar_t         *gl_fogred;	  /* Red */
cvar_t         *gl_foggreen;	  /* Green */
cvar_t         *gl_fogblue;	  /* Blue */
cvar_t         *gl_fogstart;	  /* Start */
cvar_t         *gl_fogend;	  /* End */
cvar_t         *gl_fogdensity;	  /* Density */
cvar_t         *gl_fogunderwater; /* Under Water Fof */
/* ============ End =================== */

cvar_t         *gl_shading;
cvar_t         *gl_decals;
cvar_t         *gl_decals_time;
cvar_t         *gl_glares;
cvar_t         *gl_glares_size;
cvar_t         *gl_glares_intens;
cvar_t         *gl_dlight_cutoff;

cvar_t         *gl_minimap;
cvar_t         *gl_minimap_size;
cvar_t         *gl_minimap_zoom;
cvar_t         *gl_minimap_style;
cvar_t         *gl_minimap_x;
cvar_t         *gl_minimap_y;

cvar_t         *gl_alpha_surfaces;

cvar_t         *gl_flares;
cvar_t         *gl_flare_force_size;
cvar_t         *gl_flare_force_style;
cvar_t         *gl_flare_intensity;
cvar_t         *gl_flare_maxdist;
cvar_t         *gl_flare_scale;

cvar_t         *gl_coloredlightmaps; /* NiceAss */

cvar_t         *vid_fullscreen;
cvar_t         *vid_gamma;
cvar_t         *vid_ref;
cvar_t         *cl_hudscale;
cvar_t         *cl_big_maps;
cvar_t         *cl_3dcam;
cvar_t         *deathmatch;

cvar_t         *gl_reflection_fragment_program;
cvar_t         *gl_reflection;			/* MPO	alpha transparency, 1.0 is full bright */
cvar_t         *gl_reflection_debug;		/* MPO	for debugging the reflection */
cvar_t         *gl_reflection_max;		/* MPO  max number of water reflections */
cvar_t         *gl_reflection_shader;		/* MPO	enable/disable fragment shaders */
cvar_t         *gl_reflection_shader_image;
cvar_t         *gl_reflection_water_surface;	/* MPO	enable/disable fragment shaders */

/* MH - detail textures begin */
cvar_t         *gl_detailtextures;
/* MH - detail textures begin */

/* mpo - needed for fragment shaders */
void            (APIENTRY * qglGenProgramsARB) (GLint n, GLuint * programs);
void            (APIENTRY * qglDeleteProgramsARB) (GLint n, const GLuint * programs);
void            (APIENTRY * qglBindProgramARB) (GLenum target, GLuint program);
void            (APIENTRY * qglProgramStringARB) (GLenum target, GLenum format, GLint len, const void *string);
void            (APIENTRY * qglProgramEnvParameter4fARB) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
void            (APIENTRY * qglProgramLocalParameter4fARB) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);

/* mpo - needed for fragment shaders */

/*
 * ================= GL_Stencil
 *
 * setting stencil buffer =================
 */
extern qboolean	have_stencil;
void
GL_Stencil(qboolean enable)
{
	if (!have_stencil)
		return;

	if (enable) {
		qglEnable(GL_STENCIL_TEST);
		qglStencilFunc(GL_EQUAL, 1, 2);
		qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
	} else {
		qglDisable(GL_STENCIL_TEST);
	}
}

/*
 * ================= 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_DrawSpriteModel
 *
 * =================
 */
void
R_DrawSpriteModel(entity_t * e)
{
	float		alpha = 1.0;
	vec3_t		point, up, right;
	dsprframe_t    *frame;
	dsprite_t      *psprite;

	/* 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 (!frame)
		return;

	/* normal sprite */
	VectorCopy(vup, up);
	VectorCopy(vright, right);


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

	if (!currentmodel->skins[e->frame])
		return;

	GL_Bind(currentmodel->skins[e->frame]->texnum);

	if ((currententity->flags & RF_TRANS_ADDITIVE) && (alpha != 1.0)) {
		GLSTATE_ENABLE_BLEND
		    GL_TexEnv(GL_MODULATE);

		GLSTATE_DISABLE_ALPHATEST
		    GL_BlendFunction(GL_SRC_ALPHA, GL_ONE);

		qglColor4ub(255, 255, 255, alpha * 254);
	} else {
		if (alpha != 1.0)
			GLSTATE_ENABLE_BLEND

			    GL_TexEnv(GL_MODULATE);

		if (alpha == 1.0) {
			GLSTATE_ENABLE_ALPHATEST
			    else
			GLSTATE_DISABLE_ALPHATEST
		}
		qglColor4f(1, 1, 1, alpha);
	}

	qglBegin(GL_QUADS);

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

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

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

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

	qglEnd();


	GL_BlendFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	GLSTATE_DISABLE_ALPHATEST

	    GL_TexEnv(GL_REPLACE);

	if (alpha != 1.0)
		GLSTATE_DISABLE_BLEND

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

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

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

	if (currententity->flags & RF_FULLBRIGHT)
		shadelight[0] = shadelight[1] = shadelight[2] = 1.0;
	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();

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

/*
 * ============= R_DrawEntitiesOnList =============
 */
#ifdef QMAX
typedef struct sortedent_s {
	entity_t       *ent;
	vec_t		len;
	qboolean	inwater;
} sortedent_t;

sortedent_t	theents[MAX_ENTITIES];

int
transCompare(const void *arg1, const void *arg2)
{
	const sortedent_t *a1, *a2;

	a1 = (sortedent_t *) arg1;
	a2 = (sortedent_t *) arg2;
	return (a2->len - a1->len);
}

sortedent_t
NewSortEnt(entity_t * ent, qboolean waterstart)
{
	vec3_t		result;
	sortedent_t	s_ent;

	s_ent.ent = ent;

	VectorSubtract(ent->origin, r_origin, result);
	s_ent.len = (result[0] * result[0]) + (result[1] * result[1]) + (result[2] * result[2]);

	s_ent.inwater = (Mod_PointInLeaf(ent->origin, r_worldmodel)->contents & MASK_WATER);
	if (ent->flags & RF_WEAPONMODEL || ent->flags & RF_VIEWERMODEL)
		s_ent.inwater = waterstart;

	return s_ent;
}
void
R_SortEntitiesOnList(qboolean inwater)
{
	int		i;

	for (i = 0; i < r_refdef.num_entities; i++)
		theents[i] = NewSortEnt(&r_refdef.entities[i], inwater);

	qsort((void *)theents, r_refdef.num_entities, sizeof(sortedent_t), transCompare);
}

void
R_DrawEntitiesOnList(qboolean inWater, qboolean solids)
{
	vec3_t		origin;
	int		i, j;

	if (!r_drawentities->value)
		return;

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

			if (currententity->flags & RF_TRANSLUCENT || currententity->flags & RF_VIEWERMODEL)
				continue;

			for (j = 0; j < 3; j++)
				origin[j] = currententity->origin[j];

			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;
					/* Harven MD3 ++ */
				case mod_alias_md3:
					R_DrawAliasMD3Model(currententity);
					break;
					/* Harven MD3 -- */
				case mod_brush:
					R_DrawBrushModel(currententity);
					break;
				case mod_sprite:
					R_DrawSpriteModel(currententity);
					break;
				default:
					ri.Sys_Error(ERR_DROP, "Bad modeltype");
					break;
				}
			}
		}

	qglDepthMask(0);
	for (i = 0; i < r_refdef.num_entities; i++) {
		if (gl_transrendersort->value && !(r_refdef.rdflags & RDF_NOWORLDMODEL)) {
			currententity = theents[i].ent;

			if (!(currententity->flags & RF_TRANSLUCENT || currententity->flags & RF_VIEWERMODEL) ||
			    (inWater && !theents[i].inwater) || (!inWater && theents[i].inwater))
				continue;	/* solid */
		} else if (inWater) {
			currententity = &r_refdef.entities[i];

			if (!(currententity->flags & RF_TRANSLUCENT || currententity->flags & RF_VIEWERMODEL))
				continue;	/* solid */
		} else
			continue;

		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;
				/* Harven MD3 ++ */
			case mod_alias_md3:
				R_DrawAliasMD3Model(currententity);
				break;
				/* Harven MD3 -- */
			case mod_brush:
				R_DrawBrushModel(currententity);
				break;
			case mod_sprite:
				R_DrawSpriteModel(currententity);
				break;
			default:
				ri.Sys_Error(ERR_DROP, "Bad modeltype");
				break;
			}
		}
	}
	qglDepthMask(1);	/* back to writing */

}
#else
void
R_DrawEntitiesOnList(void)
{
	int		i;

	if (!r_drawentities->value)
		return;

	/* draw non-transparent first */
	for (i = 0; i < r_refdef.num_entities; i++) {
		currententity = &r_refdef.entities[i];
		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;
				/* Harven MD3 ++ */
			case mod_alias_md3:
				R_DrawAliasMD3Model(currententity);
				break;
				/* Harven MD3 -- */
			case mod_brush:
				R_DrawBrushModel(currententity);
				break;
			case mod_sprite:
				R_DrawSpriteModel(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_refdef.num_entities; i++) {
		currententity = &r_refdef.entities[i];
		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;
				/* Harven MD3 ++ */
			case mod_alias_md3:
				R_DrawAliasMD3Model(currententity);
				break;
				/* Harven MD3 -- */
			case mod_brush:
				R_DrawBrushModel(currententity);
				break;
			case mod_sprite:
				R_DrawSpriteModel(currententity);
				break;
			default:
				ri.Sys_Error(ERR_DROP, "Bad modeltype");
				break;
			}
		}
	}
	qglDepthMask(1);	/* back to writing */

}
#endif

/*
 * * GL_DrawParticles *
 *
 */

#ifdef QMAX
typedef struct sortedpart_s {
	particle_t     *p;
	vec_t		len;
	qboolean	inwater;
} sortedpart_t;

sortedpart_t	theparts[MAX_PARTICLES];

sortedpart_t
NewSortPart(particle_t * p)
{
	vec3_t		result;
	sortedpart_t	s_part;

	s_part.p = p;
	VectorSubtract(p->origin, r_origin, result);
	s_part.len = (result[0] * result[0]) + (result[1] * result[1]) + (result[2] * result[2]);
	s_part.inwater = (Mod_PointInLeaf(p->origin, r_worldmodel)->contents & MASK_WATER);

	return s_part;
}

void
R_SortParticlesOnList(int num_particles, const particle_t particles[])
{
	int		i;

	for (i = 0; i < num_particles; i++)
		theparts[i] = NewSortPart((particle_t *) & particles[i]);

	qsort((void *)theparts, num_particles, sizeof(sortedpart_t), transCompare);
}

int
texParticle(int type)
{
	image_t        *part_img;

	part_img = r_particletextures[type];

	return part_img->texnum;
}

void
vectoanglerolled(vec3_t value1, float angleyaw, vec3_t angles)
{
	float		forward , yaw, pitch;

	yaw = (int)(atan2(value1[1], value1[0]) * 180 / M_PI);
	forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
	pitch = (int)(atan2(value1[2], forward) * 180 / M_PI);

	if (pitch < 0)
		pitch += 360;

	angles[PITCH] = -pitch;
	angles[YAW] = yaw;
	angles[ROLL] = -angleyaw;
}

float
AngleFind(float input)
{
	return 180.0 / input;
}
void
GL_DrawParticles(int num_particles, qboolean inWater)
{
	int		i;
	float		size, lighting = gl_particlelighting->value;
	int		oldrender = 0, rendertype = 0;

	byte		alpha;

	vec3_t		up = {vup[0] * 0.75, vup[1] * 0.75, vup[2] * 0.75};
	vec3_t		right = {vright[0] * 0.75, vright[1] * 0.75, vright[2] * 0.75};
	vec3_t		coord   [4], shadelight;

	particle_t     *p;

	VectorAdd(up, right, coord[0]);
	VectorSubtract(right, up, coord[1]);
	VectorNegate(coord[0], coord[2]);
	VectorNegate(coord[1], coord[3]);

	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_MODULATE);
	qglDepthMask(false);
	qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
	qglEnable(GL_BLEND);
	qglShadeModel(GL_SMOOTH);



	for (i = 0; i < num_particles; i++) {
		if (!(r_refdef.rdflags & RDF_NOWORLDMODEL)) {
			if (gl_transrendersort->value == 2) {	/* sorted with ents etc */
				p = theparts[i].p;

				if ((inWater && !theparts[i].inwater) || (!inWater && theparts[i].inwater))
					continue;

				if (gl_particledistance->value > 0 && theparts[i].len > (100.0 * gl_particledistance->value))
					continue;
			} else if (gl_transrendersort->value) {	/* sorted by itself */
				p = theparts[i].p;

				if ((inWater && !theparts[i].inwater) || (!inWater && theparts[i].inwater))
					continue;

				if (gl_particledistance->value > 0 && theparts[i].len > (100.0 * gl_particledistance->value))
					continue;
			} else if (inWater)
				p = &r_refdef.particles[i];
			else
				continue;
		} else if (inWater)
			p = &r_refdef.particles[i];
		else
			continue;

		oldrender = rendertype;
		rendertype = (p->flags & PART_TRANS) ? 1 : 0;
		if (rendertype != oldrender) {
			if (rendertype == 1)
				qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			else
				qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
		}
		if (!(p->flags & PART_SPARK))
			GL_Bind(texParticle(p->image));

		alpha = p->alpha * 254.0;
		size = (p->size > 0.1) ? p->size : 0.1;

		if (p->flags & PART_SHADED) {
			int		j;
			float		lightest = 0;

			R_LightPoint(p->origin, shadelight);

			shadelight[0] = (lighting * shadelight[0] + (1 - lighting)) * p->red;
			shadelight[1] = (lighting * shadelight[1] + (1 - lighting)) * p->green;
			shadelight[2] = (lighting * shadelight[2] + (1 - lighting)) * p->blue;

			/* this cleans up the lighting */
			{
				for (j = 0; j < 3; j++)
					if (shadelight[j] > lightest)
						lightest = shadelight[j];
				if (lightest > 255)
					for (j = 0; j < 3; j++)
						shadelight[j] *= 255 / lightest;
			}

			qglColor4ub(shadelight[0], shadelight[1], shadelight[2], alpha);
		} else
			qglColor4ub(p->red, p->green, p->blue, alpha);

		/*
		 * id move this somewhere less dirty, but im too damn lazy -
		 * psychospaz
		 */
#define DEPTHHACK_RANGE_SHORT	0.9985f
#define DEPTHHACK_RANGE_MID	0.975f
#define DEPTHHACK_RANGE_LONG	0.95f

		if (p->flags & PART_DEPTHHACK_SHORT)	/* nice little
							 * poly-peeking -
							 * psychospaz */
			qglDepthRange(gldepthmin, gldepthmin + DEPTHHACK_RANGE_SHORT * (gldepthmax - gldepthmin));
		if (p->flags & PART_DEPTHHACK_MID)
			qglDepthRange(gldepthmin, gldepthmin + DEPTHHACK_RANGE_MID * (gldepthmax - gldepthmin));
		if (p->flags & PART_DEPTHHACK_LONG)
			qglDepthRange(gldepthmin, gldepthmin + DEPTHHACK_RANGE_LONG * (gldepthmax - gldepthmin));

		if (p->flags & PART_SPARK) {	/* QMB style particles */
			int		angle;
			vec3_t		v;

			qglDisable(GL_TEXTURE_2D);

			qglPushMatrix();
			{
				qglBegin(GL_TRIANGLE_FAN);
				{
#define MAGICNUMBER 32
					float		magicnumber = AngleFind(MAGICNUMBER);


					qglVertex3fv(p->origin);
					qglColor4ub(0, 0, 0, 0);

					for (angle = magicnumber * fabs(1 - MAGICNUMBER); angle >= 0; angle -= magicnumber) {
						v[0] = p->origin[0] - p->angle[0] * size + (right[0] * cos(angle) + up[0] * sin(angle));
						v[1] = p->origin[1] - p->angle[1] * size + (right[1] * cos(angle) + up[1] * sin(angle));
						v[2] = p->origin[2] - p->angle[2] * size + (right[2] * cos(angle) + up[2] * sin(angle));
						qglVertex3fv(v);
					}
				}
				qglEnd();
			}
			qglPopMatrix();

			qglEnable(GL_TEXTURE_2D);
		} else if (p->flags & PART_BEAM) {	/* given start and end
							 * positions, will have
			 				 * fun here :) 
							 */
			/* p->angle = start */
			/* p->origin= end */
			vec3_t		angl_coord[4];
			vec3_t		ang_up, ang_right, vdelta;

			VectorSubtract(p->origin, p->angle, vdelta);
			VectorNormalize(vdelta);

			VectorCopy(vdelta, ang_up);
			VectorSubtract(r_refdef.vieworg, p->angle, vdelta);
			CrossProduct(ang_up, vdelta, ang_right);
			if (!VectorCompare(ang_right, vec3_origin))
				VectorNormalize(ang_right);

			VectorScale(ang_right, size, ang_right);

			VectorAdd(p->origin, ang_right, angl_coord[0]);
			VectorAdd(p->angle, ang_right, angl_coord[1]);
			VectorSubtract(p->angle, ang_right, angl_coord[2]);
			VectorSubtract(p->origin, ang_right, angl_coord[3]);

			qglPushMatrix();
			{
				qglBegin(GL_QUADS);
				{
					qglTexCoord2f(0, 1);
					qglVertex3fv(angl_coord[0]);
					qglTexCoord2f(0, 0);
					qglVertex3fv(angl_coord[1]);
					qglTexCoord2f(1, 0);
					qglVertex3fv(angl_coord[2]);
					qglTexCoord2f(1, 1);
					qglVertex3fv(angl_coord[3]);
				}
				qglEnd();

			}
			qglPopMatrix();
		} else if (p->flags & PART_LIGHTNING) {	/* given start and end
							 * positions, will have
			 				 * fun here :) 
							 */
			/* p->angle = start */
			/* p->origin= end */
			float		tlen, halflen, len, dec = size, warpfactor,
					warpadd, oldwarpadd, warpsize, time, i = 0,
					factor;
			vec3_t		move, lastvec, thisvec, tempvec;
			vec3_t		angl_coord[4];
			vec3_t		ang_up, ang_right, vdelta;

			oldwarpadd = 0;
			warpsize = dec * 0.01;
			warpfactor = M_PI * 1.0;
			time = -r_refdef.time * 10.0;

			VectorSubtract(p->origin, p->angle, move);
			len = VectorNormalize(move);

			VectorCopy(move, ang_up);
			VectorSubtract(r_refdef.vieworg, p->angle, vdelta);
			CrossProduct(ang_up, vdelta, ang_right);
			if (!VectorCompare(ang_right, vec3_origin))
				VectorNormalize(ang_right);

			VectorScale(ang_right, dec, ang_right);
			VectorScale(ang_up, dec, ang_up);

			VectorScale(move, dec, move);
			VectorCopy(p->angle, lastvec);
			VectorAdd(lastvec, move, thisvec);
			VectorCopy(thisvec, tempvec);

			/* lightning starts at point and then flares out */
#define LIGHTNINGWARPFUNCTION 0.25*sin(time+i+warpfactor)*(dec/len)

			warpadd = LIGHTNINGWARPFUNCTION;
			factor = 1;
			halflen = len / 2.0;


			thisvec[0] = (thisvec[0] * 2 + crandom() * 5) / 2;
			thisvec[1] = (thisvec[1] * 2 + crandom() * 5) / 2;
			thisvec[2] = (thisvec[2] * 2 + crandom() * 5) / 2;

			/*
			 * thisvec[0]= (thisvec[0]*2 + 5)/2; thisvec[1]=
			 * (thisvec[1]*2 + 5)/2; thisvec[2]= (thisvec[2]*2 +
			 * 5)/2;
			 */
			while (len > dec) {
				i += warpsize;

				VectorAdd(thisvec, ang_right, angl_coord[0]);
				VectorAdd(lastvec, ang_right, angl_coord[1]);
				VectorSubtract(lastvec, ang_right, angl_coord[2]);
				VectorSubtract(thisvec, ang_right, angl_coord[3]);

				qglPushMatrix();
				{
					qglBegin(GL_QUADS);
					{
						qglTexCoord2f(0 + warpadd, 1);
						qglVertex3fv(angl_coord[0]);
						qglTexCoord2f(0 + oldwarpadd, 0);
						qglVertex3fv(angl_coord[1]);
						qglTexCoord2f(1 + oldwarpadd, 0);
						qglVertex3fv(angl_coord[2]);
						qglTexCoord2f(1 + warpadd, 1);
						qglVertex3fv(angl_coord[3]);
					}
					qglEnd();

				}
				qglPopMatrix();

				tlen = (len < halflen) ? fabs(len - halflen) : fabs(len - halflen);
				factor = tlen / (size * size);

				if (factor > 4)
					factor = 4;
				else if (factor < 1)
					factor = 1;

				oldwarpadd = warpadd;
				warpadd = LIGHTNINGWARPFUNCTION;

				len -= dec;
				VectorCopy(thisvec, lastvec);
				VectorAdd(tempvec, move, tempvec);
				VectorAdd(lastvec, move, thisvec);


				/*
				 * thisvec[0]= ((thisvec[0] +
				 * (crandom()*size)) +
				 * tempvec[0]*factor)/(factor+1); thisvec[1]=
				 * ((thisvec[1] + (crandom()*size)) +
				 * tempvec[1]*factor)/(factor+1); thisvec[2]=
				 * ((thisvec[2] + (crandom()*size)) +
				 * tempvec[2]*factor)/(factor+1);
				 */
				thisvec[0] = ((thisvec[0] + (size)) + tempvec[0] * factor) / (factor + 1);
				thisvec[1] = ((thisvec[1] + (size)) + tempvec[1] * factor) / (factor + 1);
				thisvec[2] = ((thisvec[2] + (size)) + tempvec[2] * factor) / (factor + 1);

			}

			i += warpsize;

			/* one more time */
			if (len > 0) {
				VectorAdd(p->origin, ang_right, angl_coord[0]);
				VectorAdd(lastvec, ang_right, angl_coord[1]);
				VectorSubtract(lastvec, ang_right, angl_coord[2]);
				VectorSubtract(p->origin, ang_right, angl_coord[3]);

				qglPushMatrix();
				{
					qglBegin(GL_QUADS);
					{
						qglTexCoord2f(0 + warpadd, 1);
						qglVertex3fv(angl_coord[0]);
						qglTexCoord2f(0 + oldwarpadd, 0);
						qglVertex3fv(angl_coord[1]);
						qglTexCoord2f(1 + oldwarpadd, 0);
						qglVertex3fv(angl_coord[2]);
						qglTexCoord2f(1 + warpadd, 1);
						qglVertex3fv(angl_coord[3]);
					}
					qglEnd();

				}
				qglPopMatrix();
			}
		} else if (p->flags & PART_DIRECTION) {	/* streched out in
							 * direction...tracers
							 * etc... */

			/*
			 * never dissapears because of angle like other
			 * engines :D
			 */
			float		len, dot;
			vec3_t		angl_coord[4];
			vec3_t		ang_up, ang_right, vdelta;

			VectorCopy(p->angle, ang_up);
			len = VectorNormalize(ang_up);
			VectorSubtract(r_refdef.vieworg, p->origin, vdelta);
			CrossProduct(ang_up, vdelta, ang_right);
			if (!VectorCompare(ang_right, vec3_origin))
				VectorNormalize(ang_right);


			/*
			 * the more you look into ang_up, the more it looks
			 * like normal sprite (recalc ang_up)
			 */
			if (false) {
				vec3_t		temp_up [3], tempfor;

				AngleVectors(r_refdef.viewangles, tempfor, NULL, NULL);
				dot = 1 - fabs(DotProduct(ang_up, tempfor));

				/*
				 * if (DotProduct (vdelta, ang_up)<0 ||
				 * DotProduct (vdelta, ang_right)<0)
				 */
				/* VectorScale(tempfor, -1.0, tempfor); */

				VectorSubtract(p->origin, r_refdef.vieworg, vdelta);
				Com_Printf("up:%f right:%f\n", DotProduct(vdelta, ang_up), DotProduct(vdelta, ang_right));

				ProjectPointOnPlane(temp_up[2], ang_up, tempfor);
				VectorNormalize(temp_up[2]);

				VectorScale(ang_up, dot, temp_up[0]);
				VectorScale(temp_up[2], 1 - dot, temp_up[1]);
				VectorAdd(temp_up[1], temp_up[0], ang_up);

				len = 1 + dot * (len - 1);

				/* dont mess with stuff harder than needed */
				if (dot < 0.015) {
					len = 1;
					VectorCopy(up, ang_up);
					VectorCopy(right, ang_right);
				}
			}
			VectorScale(ang_right, 0.75, ang_right);
			VectorScale(ang_up, 0.75 * len, ang_up);

			VectorAdd(ang_up, ang_right, angl_coord[0]);
			VectorSubtract(ang_right, ang_up, angl_coord[1]);
			VectorNegate(angl_coord[0], angl_coord[2]);
			VectorNegate(angl_coord[1], angl_coord[3]);

			qglPushMatrix();
			{
				qglTranslatef(p->origin[0], p->origin[1], p->origin[2]);
				qglScalef(size, size, size);

				qglBegin(GL_QUADS);
				{
					qglTexCoord2f(0, 1);
					qglVertex3fv(angl_coord[0]);
					qglTexCoord2f(0, 0);
					qglVertex3fv(angl_coord[1]);
					qglTexCoord2f(1, 0);
					qglVertex3fv(angl_coord[2]);
					qglTexCoord2f(1, 1);
					qglVertex3fv(angl_coord[3]);
				}
				qglEnd();
			}
			qglPopMatrix();
		} else if (p->flags & PART_ANGLED) {	/* facing direction...
							 * (decal wannabes) */
			vec3_t		angl_coord[4];
			vec3_t		ang_up, ang_right, ang_forward;

			AngleVectors(p->angle, ang_forward, ang_right, ang_up);

			VectorScale(ang_right, 0.75, ang_right);
			VectorScale(ang_up, 0.75, ang_up);

			VectorAdd(ang_up, ang_right, angl_coord[0]);
			VectorSubtract(ang_right, ang_up, angl_coord[1]);
			VectorNegate(angl_coord[0], angl_coord[2]);
			VectorNegate(angl_coord[1], angl_coord[3]);

			if (p->flags & PART_DEPTHHACK) {
				qglDisable(GL_CULL_FACE);
				qglPushMatrix();
				{
					if (p->flags & (PART_DEPTHHACK_SHORT | PART_DEPTHHACK_MID | PART_DEPTHHACK_LONG)) {
						VectorMA(p->origin, size, angl_coord[0], angl_coord[0]);
						VectorMA(p->origin, size, angl_coord[1], angl_coord[1]);
						VectorMA(p->origin, size, angl_coord[2], angl_coord[2]);
						VectorMA(p->origin, size, angl_coord[3], angl_coord[3]);
					} else {
						qglTranslatef(p->origin[0], p->origin[1], p->origin[2]);
						qglScalef(size, size, size);
					}

					qglBegin(GL_QUADS);
					{
						qglTexCoord2f(0, 1);
						qglVertex3fv(angl_coord[0]);
						qglTexCoord2f(0, 0);
						qglVertex3fv(angl_coord[1]);
						qglTexCoord2f(1, 0);
						qglVertex3fv(angl_coord[2]);
						qglTexCoord2f(1, 1);
						qglVertex3fv(angl_coord[3]);
					}
					qglEnd();
				}
				qglPopMatrix();
				qglEnable(GL_CULL_FACE);
			}
		} else {	/* normal sprites */
			qglPushMatrix();
			{
				if (!(p->flags & PART_DEPTHHACK)) {
					qglTranslatef(p->origin[0], p->origin[1], p->origin[2]);
					qglScalef(size, size, size);
				}
				qglBegin(GL_QUADS);
				{
					if (p->angle[2]) {	/* if we have roll, we
								 * do calcs */
						vec3_t		angl_coord[4];
						vec3_t		ang_up, ang_right,
								ang_forward;

						VectorSubtract(p->origin, r_refdef.vieworg, angl_coord[0]);

						vectoanglerolled(angl_coord[0], p->angle[2], angl_coord[1]);
						AngleVectors(angl_coord[1], ang_forward, ang_right, ang_up);

						VectorScale(ang_forward, 0.75, ang_forward);
						VectorScale(ang_right, 0.75, ang_right);
						VectorScale(ang_up, 0.75, ang_up);

						VectorAdd(ang_up, ang_right, angl_coord[0]);
						VectorSubtract(ang_right, ang_up, angl_coord[1]);
						VectorNegate(angl_coord[0], angl_coord[2]);
						VectorNegate(angl_coord[1], angl_coord[3]);

						if (p->flags & PART_DEPTHHACK) {
							VectorMA(p->origin, size, angl_coord[0], angl_coord[0]);
							VectorMA(p->origin, size, angl_coord[1], angl_coord[1]);
							VectorMA(p->origin, size, angl_coord[2], angl_coord[2]);
							VectorMA(p->origin, size, angl_coord[3], angl_coord[3]);
						}
						qglTexCoord2f(0, 1);
						qglVertex3fv(angl_coord[0]);
						qglTexCoord2f(0, 0);
						qglVertex3fv(angl_coord[1]);
						qglTexCoord2f(1, 0);
						qglVertex3fv(angl_coord[2]);
						qglTexCoord2f(1, 1);
						qglVertex3fv(angl_coord[3]);
					} else if (p->flags & PART_DEPTHHACK) {
						vec3_t		angl_coord[4];

						VectorMA(p->origin, size, coord[0], angl_coord[0]);
						VectorMA(p->origin, size, coord[1], angl_coord[1]);
						VectorMA(p->origin, size, coord[2], angl_coord[2]);
						VectorMA(p->origin, size, coord[3], angl_coord[3]);

						qglTexCoord2f(0, 1);
						qglVertex3fv(angl_coord[0]);
						qglTexCoord2f(0, 0);
						qglVertex3fv(angl_coord[1]);
						qglTexCoord2f(1, 0);
						qglVertex3fv(angl_coord[2]);
						qglTexCoord2f(1, 1);
						qglVertex3fv(angl_coord[3]);
					} else {
						qglTexCoord2f(0, 1);
						qglVertex3fv(coord[0]);
						qglTexCoord2f(0, 0);
						qglVertex3fv(coord[1]);
						qglTexCoord2f(1, 0);
						qglVertex3fv(coord[2]);
						qglTexCoord2f(1, 1);
						qglVertex3fv(coord[3]);

					}
				}
				qglEnd();
			}
			qglPopMatrix();
		}

		if (p->flags & PART_DEPTHHACK)
			qglDepthRange(gldepthmin, gldepthmax);
	}

	qglDepthRange(gldepthmin, gldepthmax);
	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	GL_TexEnv(GL_MODULATE);
	qglDepthMask(true);
	qglDisable(GL_BLEND);
	qglColor4f(1, 1, 1, 1);
}
#else
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->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);
}
#endif

/*
 * =============== R_DrawParticles ===============
 */
#ifdef QMAX
void
R_DrawParticles(qboolean inWater)
{
	qglDisable(GL_FOG);	/* no fogging my precious */

	GL_DrawParticles(r_refdef.num_particles, inWater);

	/* Dont know if it was on... */
	/* qglEnable(GL_FOG); */
}
#else
void
R_DrawParticles(void)
{
	if (gl_particles->value) {
		const particle_t *p;
		int		i, k;
		vec3_t		up, right;
		float		scale, size = 1.0, r, g, b;

		qglDepthMask(GL_FALSE);	/* no z buffering */
		qglEnable(GL_BLEND);
		GL_TexEnv(GL_MODULATE);
		qglEnable(GL_TEXTURE_2D);
		qglBlendFunc(GL_SRC_ALPHA, GL_ONE);

		/* Vertex arrays  */
		qglEnableClientState(GL_VERTEX_ARRAY);
		qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
		qglEnableClientState(GL_COLOR_ARRAY);

		qglTexCoordPointer(2, GL_FLOAT, sizeof(tex_array[0]), tex_array[0]);
		qglVertexPointer(3, GL_FLOAT, sizeof(vert_array[0]), vert_array[0]);
		qglColorPointer(4, GL_FLOAT, sizeof(col_array[0]), col_array[0]);

		GL_Bind(r_particletexture->texnum);

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

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

			scale = (scale < 20) ? 1 : 1 + scale * 0.0004;

			r = d_8to24tablef[p->color & 0xFF][0];
			g = d_8to24tablef[p->color & 0xFF][1];
			b = d_8to24tablef[p->color & 0xFF][2];

			VA_SetElem2(tex_array[k], 0, 0);
			VA_SetElem4(col_array[k], r, g, b, p->alpha);
			VA_SetElem3(vert_array[k], p->origin[0] - (up[0] * scale) - (right[0] * scale), p->origin[1] - (up[1] * scale) - (right[1] * scale), p->origin[2] - (up[2] * scale) - (right[2] * scale));

			VA_SetElem2(tex_array[k + 1], 1, 0);
			VA_SetElem4(col_array[k + 1], r, g, b, p->alpha);
			VA_SetElem3(vert_array[k + 1], p->origin[0] + (up[0] * scale) - (right[0] * scale), p->origin[1] + (up[1] * scale) - (right[1] * scale), p->origin[2] + (up[2] * scale) - (right[2] * scale));

			VA_SetElem2(tex_array[k + 2], 1, 1);
			VA_SetElem4(col_array[k + 2], r, g, b, p->alpha);
			VA_SetElem3(vert_array[k + 2], p->origin[0] + (right[0] * scale) + (up[0] * scale), p->origin[1] + (right[1] * scale) + (up[1] * scale), p->origin[2] + (right[2] * scale) + (up[2] * scale));

			VA_SetElem2(tex_array[k + 3], 0, 1);
			VA_SetElem4(col_array[k + 3], r, g, b, p->alpha);
			VA_SetElem3(vert_array[k + 3], p->origin[0] + (right[0] * scale) - (up[0] * scale), p->origin[1] + (right[1] * scale) - (up[1] * scale), p->origin[2] + (right[2] * scale) - (up[2] * scale));
		}

		qglDrawArrays(GL_QUADS, 0, k);

		qglDisable(GL_BLEND);
		qglColor3f(1, 1, 1);
		qglDepthMask(1);/* back to normal Z buffering */
		GL_TexEnv(GL_REPLACE);
		qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		qglDisableClientState(GL_VERTEX_ARRAY);
		qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
		qglDisableClientState(GL_COLOR_ARRAY);
	} else 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_refdef.particles; i < r_refdef.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.0, 1.0, 1.0, 1.0);
		qglDepthMask(GL_TRUE);
		qglEnable(GL_TEXTURE_2D);

	} else {
		GL_DrawParticles(r_refdef.num_particles, r_refdef.particles, d_8to24table);
	}
}
#endif

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

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

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

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

/*
 * =============== R_SetupFrame ===============
 */
void
R_SetupFrame(void)
{
	int		i;
	mleaf_t        *leaf;

	r_framecount++;

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

	if (!g_drawing_refl) {

		/* build the transformation matrix for the given view angles */
		AngleVectors(r_refdef.viewangles, vpn, vright, vup);
	}
	/* start MPO */

	/*
	 * we want to look from the mirrored origin's perspective when
	 * drawing reflections
	 */
	if (g_drawing_refl) {
		float		distance;

		distance = DotProduct(r_origin, waterNormals[g_active_refl]) - g_waterDistance2[g_active_refl];
		VectorMA(r_refdef.vieworg, distance * -2, waterNormals[g_active_refl], r_origin);

		if (!(r_refdef.rdflags & RDF_NOWORLDMODEL)) {
			vec3_t		temp;

			temp[0] = g_refl_X[g_active_refl];	/* sets PVS over x */
			temp[1] = g_refl_Y[g_active_refl];	/* and y coordinate of water surface */

			VectorCopy(r_origin, temp);
			if (r_refdef.rdflags & RDF_UNDERWATER) {
				temp[2] = g_refl_Z[g_active_refl] - 1;	/* just above water level */
			} else {
				temp[2] = g_refl_Z[g_active_refl] + 1;	/* just below water level */
			}

			leaf = Mod_PointInLeaf(temp, r_worldmodel);

			if (!(leaf->contents & CONTENTS_SOLID) &&
			    (leaf->cluster != r_viewcluster)) {
				r_viewcluster2 = leaf->cluster;
			}
		}
		return;
	}
	/* stop MPO */

	/* current viewcluster */
	if (!(r_refdef.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_refdef.blend[i];

	c_brush_polys = 0;
	c_alias_polys = 0;

	/* clear out the portion of the screen that the NOWORLDMODEL defines */
	if (r_refdef.rdflags & RDF_NOWORLDMODEL) {
		qglEnable(GL_SCISSOR_TEST);
		qglScissor(r_refdef.x, vid.height - r_refdef.height - r_refdef.y, r_refdef.width, r_refdef.height);
		if (!(r_refdef.rdflags & RDF_NOCLEAR)) {
			qglClearColor(0.3, 0.3, 0.3, 1);
			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 =============
 */
void
R_SetupGL(void)
{
	static GLdouble	farz;	/* DMP skybox size change */
	GLdouble	boxsize;/* DMP skybox size change */
	int		x, x2, y2, y, w, h;
	float		screenaspect;

	/* Knightmare- update gl_modulate in real time */
	/* FIXME NightHunters demos */
    	if (gl_modulate->modified && (r_worldmodel)) { /* Don't do this if no map is loaded */
		msurface_t *surf; 
		int i;
		
        	for (i=0,surf = r_worldmodel->surfaces; i<r_worldmodel->numsurfaces; i++,surf++)
			surf->cached_light[0]=0; 
        		gl_modulate->modified = 0; 
	} 

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

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

	if (skydistance->modified) {
		skydistance->modified = false;
		boxsize = skydistance->value;
		boxsize -= 252 * ceil(boxsize / 2300);
		farz = 1.0;
		while (farz < boxsize) {	/* DMP: make this value a
						 * power-of-2 */
			farz *= 2.0;
			if (farz >= 65536.0)	/* DMP: don't make it larger
						 * than this */
				break;
		}
		farz *= 2.0;	/* DMP: double since boxsize is distance from camera to */
		/* edge of skybox - not total size of skybox */
		ri.Con_Printf(PRINT_DEVELOPER, "farz now set to %g\n", farz);
	}

	/*
	 * MPO : we only want to set the viewport if we aren't drawing the
	 * reflection
	 */
	if (!g_drawing_refl) {
		qglViewport(x, y2, w, h); /* MPO : note this happens every 
		                           * frame interestingly enough 
					   */
	} else {
		qglViewport(0, 0, g_reflTexW, g_reflTexH); /* width/height of texture size, 
		                                            * not screen size 
							    */
	}
	/* stop MPO */
	
	/* set up projection matrix */
	screenaspect = (float)r_refdef.width / r_refdef.height;

	/*
	 * yfov =
	 * 2*atan((float)r_refdef.height/r_refdef.width)*180/M_PI;
	 */
	qglMatrixMode(GL_PROJECTION);
	qglLoadIdentity();

	/* MYgluPerspective (r_refdef.fov_y,  screenaspect,  4,  4096); */
	MYgluPerspective(r_refdef.fov_y, screenaspect, 4, farz);	/* DMP skybox */


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

	/*
	 * MPO : +Z is up, +X is forward, +Y is left according to my
	 * calculations
	 */

	/* start MPO */
	/* standard transformation */
	if (!g_drawing_refl) {

		qglRotatef(-r_refdef.viewangles[2], 1, 0, 0);	/* MPO : this handles rolling (ie when we
								* strafe side to side we roll slightly) */
		qglRotatef(-r_refdef.viewangles[0], 0, 1, 0);	/* MPO : this handles up/down rotation */
		qglRotatef(-r_refdef.viewangles[1], 0, 0, 1);	/* MPO : this handles left/right rotation */
		qglTranslatef(-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]);

		/*
		 * MPO : this translate call puts the player at the proper
		 * spot in the map
		 */
		/* MPO : The map is drawn to absolute coordinates */
	}
	/* mirrored transformation for reflection */
	else {

		R_DoReflTransform(true);
	}
	/* end MPO */
	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) && (!g_drawing_refl)) /* MPO : we must disable culling when drawing the reflection */
		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_clear->value) {
		qglClearColor(0.5f, 0.5f, 0.5f, 1.0f);
		qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}
	else {
		qglClear(GL_DEPTH_BUFFER_BIT);
	}
	
	if (have_stencil) {
		qglClearStencil(1);
		qglClear(GL_STENCIL_BUFFER_BIT);
	}
	
	gldepthmin = 0;
	gldepthmax = 1;
	qglDepthFunc(GL_LEQUAL);
	
	qglDepthRange(gldepthmin, gldepthmax);

}

void
R_Flash(void)
{
	R_PolyBlend();
}

void
setupModelLighting(void)
{

	int		lnum;
	int		newNumGlLights;
	float		vec4[4] = {1, 1, 1, 1};

	lnum = 0;
	newNumGlLights = 0;

	qglLightModelfv(GL_LIGHT_MODEL_AMBIENT, vec4);
	qglMaterialfv(GL_FRONT, GL_DIFFUSE, vec4);
	/* qglMaterialfv	( GL_FRONT, GL_AMBIENT,   vec4	); */
	qglColorMaterial(GL_FRONT, GL_AMBIENT);


	for (; (lnum < r_refdef.num_dlights) && (newNumGlLights < 8); lnum++) {
		dlight_t       *dl = &r_refdef.dlights[lnum];

		if (dl->intensity <= 64)
			continue;

		memcpy(vec4, dl->origin, sizeof(dl->origin));
		qglLightfv(GL_LIGHT0 + newNumGlLights, GL_POSITION, vec4);

		qglLightf(GL_LIGHT0 + newNumGlLights,
		    GL_QUADRATIC_ATTENUATION,
		    0.000000001 * dl->intensity);

		memcpy(vec4, dl->color, sizeof(dl->color));
		vec4[0] *= gl_modulate->value;
		vec4[1] *= gl_modulate->value;
		vec4[2] *= gl_modulate->value;
		qglLightfv(GL_LIGHT0 + newNumGlLights, GL_DIFFUSE, vec4);

		++newNumGlLights;
	}

	if (newNumGlLights < g_numGlLights) {
		while (g_numGlLights != newNumGlLights) {
			--g_numGlLights;
			qglDisable(GL_LIGHT0 + g_numGlLights);
		}
	} else if (g_numGlLights != newNumGlLights)
		g_numGlLights = newNumGlLights;
}

/*
 * ================ R_RenderView
 *
 * r_refdef must be set before the first call ================
 */
void
R_RenderView(refdef_t * fd)
{
#ifdef QMAX
	qboolean	inWater;
#endif
	vec3_t		colors;	/* Fog */

	if (r_norefresh->value)
		return;

	r_refdef = *fd;

	/* (gl_scale) NiceAss: Scale back up from client's false width/height */
	r_refdef.width *= cl_hudscale->value;
	r_refdef.height *= cl_hudscale->value;
	r_refdef.x *= cl_hudscale->value;
	r_refdef.y *= cl_hudscale->value;

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

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

	if (gl_finish->value)
		qglFinish();

	if ((r_refdef.num_newstains > 0) && (gl_stainmaps->value)) {
		R_ApplyStains();
	}
	R_SetupGL();		/* MPO moved here .. */

	R_SetupFrame();

	R_SetFrustum();

	setupClippingPlanes();	/* MPO clipping planes for water */

	/* R_SetupGL (); */

	R_MarkLeaves();		/* done here so we know if we're in water */

	drawPlayerReflection();

	R_DrawWorld();

#ifdef QMAX
	if (r_refdef.rdflags & RDF_NOWORLDMODEL || !gl_transrendersort->value) {
		inWater = false;
	} else {
		inWater = Mod_PointInLeaf(fd->vieworg, r_worldmodel)->contents;
	}
	if (!(r_refdef.rdflags & RDF_NOWORLDMODEL) && gl_transrendersort->value) {
		R_SortParticlesOnList(r_refdef.num_particles, r_refdef.particles);
		R_SortEntitiesOnList(inWater);
	}
#endif

	R_AddDecals(); /* Decals */
	
	if (gl_flares->value) {
		if (gl_fogenable->value /*|| gl_fogunderwater->value*/) {
			qglDisable(GL_FOG);
			R_RenderFlares();
			qglEnable(GL_FOG);
		}
		else {
			R_RenderFlares();
		}
	}

	setupModelLighting();	/* dukey sets up opengl dynamic lighting for entitites. */

#ifdef QMAX
	R_DrawEntitiesOnList((inWater) ? false : true, true);
#else
	R_DrawEntitiesOnList();
#endif

	/* R_RenderDlights (); */
#ifdef QMAX
	R_DrawParticles((inWater) ? false : true);
#else

	if (!gl_ext_texture_compression->value) {
		R_BloomBlend(fd);	/* BLOOMS */
	}
	R_DrawParticles();
#endif

	if (gl_alpha_surfaces->value) {
		R_DrawAlphaSurfaces_Jitspoe();
	} else {
		R_DrawAlphaSurfaces();
	}

#ifdef QMAX
	R_DrawEntitiesOnList((inWater) ? true : false, false);

	if (!gl_ext_texture_compression->value) {
		R_BloomBlend(fd);	/* BLOOMS */
	}
	R_DrawParticles((inWater) ? true : false);
#else
	R_DrawParticles();
#endif

	/* start MPO */
	/* if we are doing a reflection, turn off clipping now */
	if (g_drawing_refl) {
		qglDisable(GL_CLIP_PLANE0);
	}

	/*
	 * if we aren't doing a reflection then we can do the flash and r
	 * speeds
	 */
	/* (we don't want them showing up in our reflection) */
	else {
		R_Flash();
	}
	/* stop MPO */
	/* ===================== Engine Fog ================= */
	qglDisable(GL_FOG);
	
	if (gl_fogenable->value && !(r_refdef.rdflags & RDF_UNDERWATER)) {
		qglEnable(GL_FOG);
		qglFogi(GL_FOG_MODE, GL_BLEND);
		colors[0] = gl_fogred->value;
		colors[1] = gl_foggreen->value;
		colors[2] = gl_fogblue->value;

		qglFogf(GL_FOG_DENSITY, gl_fogdensity->value);
		qglFogfv(GL_FOG_COLOR, colors);
		qglFogf(GL_FOG_START, gl_fogstart->value);
		qglFogf(GL_FOG_END, gl_fogend->value);
		
	} else if (gl_fogunderwater->value && (r_refdef.rdflags & RDF_UNDERWATER)) {
		qglEnable(GL_FOG);
		
		qglFogi(GL_FOG_MODE, GL_BLEND);
		colors[0] = gl_fogred->value;
		colors[1] = gl_foggreen->value;
		colors[2] = gl_fogblue->value;
        	
		qglFogf(GL_FOG_START, 0.0);
	    	qglFogf(GL_FOG_END, 2048);
		qglFogf(GL_FOG_DENSITY, 0.0);
	
		if (inlava) {
			colors[0] = 0.7;
       		}
		if (inslime) {
			colors[1] = 0.7;
        	}
		if (inwater) {
			colors[2] = 0.6;
		}

		qglFogf(GL_FOG_DENSITY, 0.001);
		qglFogf(GL_FOG_START, 0.0);
		qglFogfv(GL_FOG_COLOR, colors);
		qglFogf(GL_FOG_END, 450);
		
	} else {
		qglDisable(GL_FOG);
	}
	
	/* ====================== End ======================= */

	if (!deathmatch->value && gl_minimap_size->value > 32 && !(r_refdef.rdflags & RDF_IRGOGGLES)) {
		{
			GLSTATE_DISABLE_ALPHATEST;
			GL_DrawRadar();
		}
		numRadarEnts = 0;
	}
}

int
R_DrawRSpeeds(char *S)
{
	return sprintf(S, "%4i wpoly %4i epoly %i tex %i lmaps",

	c_brush_polys,
	c_alias_polys,
	c_visible_textures,
	c_visible_lightmaps);
}

unsigned int	blurtex = 0;
void
R_MotionBlur (void) 
{

	if (!gl_state.tex_rectangle)
		return;
		
	if (gl_state.tex_rectangle) {
		if (blurtex) {
			GL_TexEnv(GL_MODULATE);
			qglDisable(GL_TEXTURE_2D);
			qglEnable(GL_TEXTURE_RECTANGLE_NV);
			qglEnable(GL_BLEND);
			qglDisable(GL_ALPHA_TEST);
			qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			if (gl_motionblur_intensity->value >= 1.0)
				qglColor4f(1.0, 1.0, 1.0, 0.45);
			else
				qglColor4f(1.0, 1.0, 1.0, gl_motionblur_intensity->value);
			qglBegin(GL_QUADS);
			{
				qglTexCoord2f(0, vid.height);
				qglVertex2f(0, 0);
				qglTexCoord2f(vid.width, vid.height);
				qglVertex2f(vid.width, 0);
				qglTexCoord2f(vid.width, 0);
				qglVertex2f(vid.width, vid.height);
				qglTexCoord2f(0, 0);
				qglVertex2f(0, vid.height);
			}
			qglEnd();
			qglDisable(GL_TEXTURE_RECTANGLE_NV);
			qglEnable(GL_TEXTURE_2D);
		}
		if (!blurtex)
			qglGenTextures(1, &blurtex);
		qglBindTexture(GL_TEXTURE_RECTANGLE_NV, blurtex);
		qglCopyTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGB, 0, 0, vid.width, vid.height, 0);
		qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
}

void
R_SetGL2D(void)
{
	/* set 2D virtual screen size */
	qglViewport(0, 0, vid.width, vid.height);
	qglMatrixMode(GL_PROJECTION);
	qglLoadIdentity();
	/* NiceAss: Scale the screen */
	qglOrtho(0, vid.width / cl_hudscale->value, vid.height / cl_hudscale->value, 0, -99999, 99999);
	/* qglOrtho  (0, vid.width, vid.height, 0, -99999, 99999); */

	qglMatrixMode(GL_MODELVIEW);
	qglLoadIdentity();
	qglDisable(GL_DEPTH_TEST);
	qglDisable(GL_CULL_FACE);
	if (gl_motionblur->value == 1 && r_refdef.rdflags & RDF_UNDERWATER)
		R_MotionBlur();
	else if (gl_motionblur->value == 2)
		R_MotionBlur();
	qglDisable(GL_BLEND);
	qglEnable(GL_ALPHA_TEST);
	qglColor4f(1, 1, 1, 1);
	/* ECHON */
	if (r_speeds->value) {
		char		S[128];
		int		n = 0;
		int		i;

		n = R_DrawRSpeeds(S);

		for (i = 0; i < n; i++)
			Draw_Char(r_refdef.width - 4 + ((i - n) * 8), r_refdef.height - 40, 128 + S[i], 255);
	}
}

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_refdef.rdflags & RDF_NOWORLDMODEL)
		return;

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

	R_LightPoint(r_refdef.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_RenderFrame
 *
 * @@@@@@@@@@@@@@@@@@@@@
 */
void
R_RenderFrame(refdef_t * fd)
{
	/* start MPO */
	if (gl_reflection->value) {

		r_refdef = *fd;	/* need to do this so eye coords
					 * update b4 frame is drawn */

		R_clear_refl();	/* clear our reflections found in last frame */
		R_RecursiveFindRefl(r_worldmodel->nodes);	/* find reflections for
								 * this frame */
		R_UpdateReflTex(fd);	/* render reflections to textures */
	} else {
		R_clear_refl();
	}
	/* end MPO */
	R_PreRenderDynamic(fd);
	R_RenderView(fd);
	R_SetLightLevel();
	R_SetGL2D();
	R_RenderGlares(fd);

	/* start MPO */
	/* if debugging is enabled and reflections are enabled.. draw it */
	if ((gl_reflection_debug->value) && (g_refl_enabled)) {
		R_DrawDebugReflTexture();
	}
	/* end MPO */
}


void
R_Register(void)
{

	cl_hudscale = ri.Cvar_Get("cl_hudscale", "1", CVAR_ARCHIVE);
	cl_big_maps = ri.Cvar_Get("cl_big_maps", "1",  0);
	cl_3dcam = ri.Cvar_Get("cl_3dcam", "0", CVAR_ARCHIVE);

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

	font_color = ri.Cvar_Get("font_color", "default", CVAR_ARCHIVE);

#ifdef QMAX
	gl_partscale = ri.Cvar_Get("gl_partscale", "100", 0);
	gl_transrendersort = ri.Cvar_Get("gl_transrendersort", "1", 0);
	gl_particlelighting = ri.Cvar_Get("gl_particlelighting", "0.75", 0);
	gl_particledistance = ri.Cvar_Get("gl_particledistance", "0", 0);
#else
	gl_particle_min_size = ri.Cvar_Get("gl_particle_min_size", "2", 0);
	gl_particle_max_size = ri.Cvar_Get("gl_particle_max_size", "40", 0);
	gl_particle_size = ri.Cvar_Get("gl_particle_size", "40", 0);
	gl_particle_att_a = ri.Cvar_Get("gl_particle_att_a", "0.01", 0);
	gl_particle_att_b = ri.Cvar_Get("gl_particle_att_b", "0.0", 0);
	gl_particle_att_c = ri.Cvar_Get("gl_particle_att_c", "0.01", 0);
	gl_particles = ri.Cvar_Get("gl_particles", "0", CVAR_ARCHIVE);
#endif
	gl_stainmaps = ri.Cvar_Get("gl_stainmaps", "0", CVAR_ARCHIVE);
	gl_dlight_cutoff = ri.Cvar_Get("gl_dlight_cutoff", "0", CVAR_ARCHIVE);
	gl_shellstencil = ri.Cvar_Get("gl_shellstencil", "1", CVAR_ARCHIVE);
	gl_nosubimage = ri.Cvar_Get("gl_nosubimage", "0", 0);
	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", "4", CVAR_ARCHIVE);
	gl_lightmap = ri.Cvar_Get("gl_lightmap", "0", 0);
	gl_shadows = ri.Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE);
	gl_shadows_alpha = ri.Cvar_Get("gl_shadows_alpha", "0.5", 0);
	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_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", "libGL.so", CVAR_ARCHIVE);
	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_ext_mtexcombine = ri.Cvar_Get("gl_ext_mtexcombine", "1", CVAR_ARCHIVE);
	gl_vertex_arrays = ri.Cvar_Get("gl_vertex_arrays", "0", CVAR_ARCHIVE);
	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_ext_compiled_vertex_array = ri.Cvar_Get("gl_ext_compiled_vertex_array", "1", CVAR_ARCHIVE);
	gl_ext_palettedtexture = ri.Cvar_Get("gl_ext_palettedtexture", "0", 0);
	gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0);
	gl_saturatelighting = ri.Cvar_Get("gl_saturatelighting", "0", 0);
	gl_3dlabs_broken = ri.Cvar_Get("gl_3dlabs_broken", "1", CVAR_ARCHIVE);
	gl_ext_texture_compression = ri.Cvar_Get("gl_ext_texture_compression", "0", CVAR_ARCHIVE);
	gl_anisotropic = ri.Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE);
	gl_anisotropic_avail = ri.Cvar_Get("gl_anisotropic_avail", "0", 0);
	gl_nv_fog = ri.Cvar_Get("gl_nv_fog", "0", CVAR_ARCHIVE);
	gl_ext_nv_multisample_filter_hint = ri.Cvar_Get("gl_ext_nv_multisample_filter_hint", "fastest", 0);
	gl_lightmap_texture_saturation = ri.Cvar_Get("gl_lightmap_texture_saturation", "1", 0);
	gl_lightmap_saturation = ri.Cvar_Get("gl_lightmap_saturation", "1", 0);
	gl_alpha_surfaces = ri.Cvar_Get("gl_alpha_surfaces", "0", 0);
	gl_screenshot_jpeg = ri.Cvar_Get("gl_screenshot_jpeg", "1", CVAR_ARCHIVE);
	gl_screenshot_jpeg_quality = ri.Cvar_Get("gl_screenshot_jpeg_quality", "95", CVAR_ARCHIVE);
	gl_water_waves = ri.Cvar_Get("gl_water_waves", "0", CVAR_ARCHIVE);
	gl_water_caustics = ri.Cvar_Get("gl_water_caustics", "1", CVAR_ARCHIVE);
	gl_water_pixel_shader_warp = ri.Cvar_Get("gl_water_pixel_shader_warp", "1", CVAR_ARCHIVE);
	gl_blooms = ri.Cvar_Get("gl_blooms", "0", CVAR_ARCHIVE);
	gl_motionblur = ri.Cvar_Get("gl_motionblur", "0", CVAR_ARCHIVE);
	gl_motionblur_intensity = ri.Cvar_Get("gl_motionblur_intensity", "0.6", CVAR_ARCHIVE);
	gl_fogenable = ri.Cvar_Get("gl_fogenable", "0", CVAR_ARCHIVE);
	gl_fogstart = ri.Cvar_Get("gl_fogstart", "50.0", CVAR_ARCHIVE);
	gl_fogend = ri.Cvar_Get("gl_fogend", "1500.0", CVAR_ARCHIVE);
	gl_fogdensity = ri.Cvar_Get("gl_fogdensity", "0.001", CVAR_ARCHIVE);
	gl_fogred = ri.Cvar_Get("gl_fogred", "0.4", CVAR_ARCHIVE);
	gl_foggreen = ri.Cvar_Get("gl_foggreen", "0.4", CVAR_ARCHIVE);
	gl_fogblue = ri.Cvar_Get("gl_fogblue", "0.4", CVAR_ARCHIVE);
	gl_fogunderwater = ri.Cvar_Get("gl_fogunderwater", "0", CVAR_ARCHIVE);
	gl_reflection_fragment_program = ri.Cvar_Get("gl_reflection_fragment_program", "0", CVAR_ARCHIVE);
	gl_reflection = ri.Cvar_Get("gl_reflection", "0", CVAR_ARCHIVE);
	gl_reflection_debug = ri.Cvar_Get("gl_reflection_debug", "0", 0);
	gl_reflection_max = ri.Cvar_Get("gl_reflection_max", "2", 0);
	gl_reflection_shader = ri.Cvar_Get("gl_reflection_shader", "0", CVAR_ARCHIVE);
	gl_reflection_shader_image = ri.Cvar_Get("gl_reflection_shader_image",
	                                          "/textures/water/distortion.pcx", CVAR_ARCHIVE);
	gl_reflection_water_surface = ri.Cvar_Get("gl_reflection_water_surface", "1", 0);
	gl_minimap = ri.Cvar_Get("gl_minimap", "0", CVAR_ARCHIVE);
	gl_minimap_size = ri.Cvar_Get("gl_minimap_size", "400", CVAR_ARCHIVE);
	gl_minimap_zoom = ri.Cvar_Get("gl_minimap_zoom", "1.5", CVAR_ARCHIVE);
	gl_minimap_style = ri.Cvar_Get("gl_minimap_style", "1", CVAR_ARCHIVE);
	gl_minimap_x = ri.Cvar_Get("gl_minimap_x", "800", CVAR_ARCHIVE);
	gl_minimap_y = ri.Cvar_Get("gl_minimap_y", "400", CVAR_ARCHIVE);
	gl_detailtextures = ri.Cvar_Get("gl_detailtextures", "0.0", CVAR_ARCHIVE);
	gl_shading = ri.Cvar_Get("gl_shading", "1", CVAR_ARCHIVE);
	gl_decals = ri.Cvar_Get("gl_decals", "1", CVAR_ARCHIVE);
	gl_decals_time = ri.Cvar_Get("gl_decals_time", "30", CVAR_ARCHIVE);
	gl_glares = ri.Cvar_Get("gl_glares", "0", CVAR_ARCHIVE);
	gl_glares_size = ri.Cvar_Get("gl_glares_size", "128", CVAR_ARCHIVE);
	gl_glares_intens = ri.Cvar_Get("gl_glares_intens", "0.35", CVAR_ARCHIVE);
	gl_flares = ri.Cvar_Get("gl_flares", "1", CVAR_ARCHIVE);
	gl_flare_force_size = ri.Cvar_Get("gl_flare_force_size", "0", CVAR_ARCHIVE);
	gl_flare_force_style = ri.Cvar_Get("gl_flare_force_style", "1", CVAR_ARCHIVE);
	gl_flare_intensity = ri.Cvar_Get("gl_flare_intensity", "1", CVAR_ARCHIVE);
	gl_flare_maxdist = ri.Cvar_Get("gl_flare_maxdist", "150", CVAR_ARCHIVE);
	gl_flare_scale = ri.Cvar_Get("gl_flare_scale", "1.5", CVAR_ARCHIVE);
	gl_coloredlightmaps = ri.Cvar_Get( "gl_coloredlightmaps", "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_model_lightlerp = ri.Cvar_Get("r_model_lightlerp", "1", 0);
	r_model_alpha = ri.Cvar_Get("r_model_alpha", "0", 0);
	r_speeds = ri.Cvar_Get("r_speeds", "0", 0);
	r_lightlevel = ri.Cvar_Get("r_lightlevel", "0", 0);
	r_overbrightbits = ri.Cvar_Get("r_overbrightbits", "2", CVAR_ARCHIVE);
	r_cellshading = ri.Cvar_Get("r_cellshading", "0", CVAR_ARCHIVE);
	r_cellshading_width = ri.Cvar_Get("r_cellshading_width", "4", CVAR_ARCHIVE);

	skydistance = ri.Cvar_Get("skydistance", "2300", 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", "q2glx", CVAR_ARCHIVE);

	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);
	ri.Cmd_AddCommand("pngshot", GL_ScreenShot_PNG);
}

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

	fullscreen = vid_fullscreen->value;

	skydistance->modified = true;	/* DMP skybox size change */
	vid_fullscreen->modified = false;
	gl_mode->modified = false;
	gl_coloredlightmaps->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, "Video ref::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, "Video ref::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, "Video ref::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];
	char		vendor_buffer[1000];
	int		err;
	int		j;
	extern float	r_turbsin[256];
	int		init_time;

	init_time = Sys_Milliseconds();

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

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

	Draw_GetPalette();

	R_Register();

	VLight_Init();

	/* initialize our QGL dynamic bindings */
	if (!QGL_Init(gl_driver->string)) {
		QGL_Shutdown();
		ri.Con_Printf(PRINT_ALL, "Video ref::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, "Video ref::R_Init() - could not R_SetMode()\n");
		return -1;
	}
	ri.Vid_MenuInit();

	/*
	 * * get our various GL strings
	 */
	gl_config.vendor_string = (char *)qglGetString(GL_VENDOR);
	ri.Con_Printf(PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string);
	gl_config.renderer_string = (char *)qglGetString(GL_RENDERER);
	ri.Con_Printf(PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string);
	gl_config.version_string = (char *)qglGetString(GL_VERSION);
	ri.Con_Printf(PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string);
	gl_config.extensions_string = (char *)qglGetString(GL_EXTENSIONS);
	ri.Con_Printf(PRINT_ALL, "GL_EXTENSIONS: %s\n", gl_config.extensions_string);

	Q_strncpyz(renderer_buffer, gl_config.renderer_string, sizeof(renderer_buffer));
	Q_strlwr(renderer_buffer);

	Q_strncpyz(vendor_buffer, gl_config.vendor_string, sizeof(vendor_buffer));
	Q_strlwr(vendor_buffer);

	if (strcasecmp(renderer_buffer, "voodoo")) {
		if (!strcasecmp(renderer_buffer, "rush"))
			gl_config.renderer = GL_RENDERER_VOODOO;
		else
			gl_config.renderer = GL_RENDERER_VOODOO_RUSH;
	} else if (strcasecmp(vendor_buffer, "sgi"))
		gl_config.renderer = GL_RENDERER_SGI;
	else if (strcasecmp(renderer_buffer, "permedia"))
		gl_config.renderer = GL_RENDERER_PERMEDIA2;
	else if (strcasecmp(renderer_buffer, "glint"))
		gl_config.renderer = GL_RENDERER_GLINT_MX;
	else if (strcasecmp(renderer_buffer, "glzicd"))
		gl_config.renderer = GL_RENDERER_REALIZM;
	else if (strcasecmp(renderer_buffer, "gdi"))
		gl_config.renderer = GL_RENDERER_MCD;
	else if (strcasecmp(renderer_buffer, "pcx2"))
		gl_config.renderer = GL_RENDERER_PCX2;
	else if (strcasecmp(renderer_buffer, "verite"))
		gl_config.renderer = GL_RENDERER_RENDITION;
	else
		gl_config.renderer = GL_RENDERER_OTHER;

	if (!(gl_monolightmap->string[1] == 'F' ||
	    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");
	}

#if defined (__unix__)
	ri.Cvar_SetValue("gl_finish", 1);
#endif

	/* 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
	 */
	if (strstr(gl_config.extensions_string, "GL_EXT_compiled_vertex_array") ||
	    strstr(gl_config.extensions_string, "GL_SGI_compiled_vertex_array")) {
		ri.Con_Printf(PRINT_ALL, "...enabling GL_EXT_compiled_vertex_array\n");
		qglLockArraysEXT = (void *)qwglGetProcAddress("glLockArraysEXT");
		qglUnlockArraysEXT = (void *)qwglGetProcAddress("glUnlockArraysEXT");
	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_EXT_compiled_vertex_array 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_ARB_multitexture")) {
		if (gl_ext_multitexture->value) {
			ri.Con_Printf(PRINT_ALL, "...using GL_ARB_multitexture\n");
			qglMTexCoord2fSGIS = (void *)qwglGetProcAddress("glMultiTexCoord2fARB");
			qglActiveTextureARB = (void *)qwglGetProcAddress("glActiveTextureARB");
			qglClientActiveTextureARB = (void *)qwglGetProcAddress("glClientActiveTextureARB");
			qglMultiTexCoord3fvARB = (void *)qwglGetProcAddress("glMultiTexCoord3fvARB");	/* mpo */

			qglGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits);/* MH - see how many
										    * texture units card
										    * supports */
			GL_TEXTURE0 = GL_TEXTURE0_ARB;
			GL_TEXTURE1 = GL_TEXTURE1_ARB;
			GL_TEXTURE2 = GL_TEXTURE2_ARB;	/* MH - add extra
							 * texture unit here */

			ri.Con_Printf(PRINT_ALL, "Texture units available: %d\n", maxTextureUnits);
		} else {
			ri.Con_Printf(PRINT_ALL, "...ignoring GL_ARB_multitexture\n");
		}
	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_ARB_multitexture not found\n");
	}

	/* retreive information */
	qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_state.maxtexsize);
	if (gl_state.maxtexsize <= 0)
		gl_state.maxtexsize = 256;

	Com_Printf("Maximum Texture Size: %ix%i\n", gl_state.maxtexsize, gl_state.maxtexsize);

	if (strstr(gl_config.extensions_string, "GL_SGIS_multitexture")) {
		if (qglActiveTextureARB) {
			ri.Con_Printf(PRINT_ALL, "...GL_SGIS_multitexture deprecated in favor of ARB_multitexture\n");
		} else if (gl_ext_multitexture->value) {
			ri.Con_Printf(PRINT_ALL, "...using GL_SGIS_multitexture\n");
			qglMTexCoord2fSGIS = (void *)qwglGetProcAddress("glMTexCoord2fSGIS");
			qglSelectTextureSGIS = (void *)qwglGetProcAddress("glSelectTextureSGIS");

			qglGetIntegerv(GL_MAX_TEXTURE_UNITS_SGIS, &maxTextureUnits);	/* MH - see how many
											 * texture units card
											 * supports */
			GL_TEXTURE0 = GL_TEXTURE0_SGIS;
			GL_TEXTURE1 = GL_TEXTURE1_SGIS;
			GL_TEXTURE2 = GL_TEXTURE2_SGIS;	/* MH - add extra
							 * texture unit */
		} else {
			ri.Con_Printf(PRINT_ALL, "...ignoring GL_SGIS_multitexture\n");
		}
	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_SGIS_multitexture not found\n");
	}

	if (strstr(gl_config.extensions_string, "GL_SGIS_generate_mipmap")) {
		ri.Con_Printf(PRINT_ALL, "...using GL_SGIS_generate_mipmap\n");
		gl_state.sgis_mipmap = true;
	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_SGIS_generate_mipmap not found\n");
		gl_state.sgis_mipmap = false;
	}

	/* Vic - begin */
	gl_config.mtexcombine = false;

	if (strstr(gl_config.extensions_string, "GL_ARB_texture_env_combine")) {
		if (gl_ext_mtexcombine->value) {
			Com_Printf("...using GL_ARB_texture_env_combine\n");
			gl_config.mtexcombine = true;
		} else {
			Com_Printf("...ignoring GL_ARB_texture_env_combine\n");
		}
	} else {
		Com_Printf("...GL_ARB_texture_env_combine not found\n");
	}

	if (!gl_config.mtexcombine) {
		if (strstr(gl_config.extensions_string, "GL_EXT_texture_env_combine")) {
			if (gl_ext_mtexcombine->value) {
				Com_Printf("...using GL_EXT_texture_env_combine\n");
				gl_config.mtexcombine = true;
			} else {
				Com_Printf("...ignoring GL_EXT_texture_env_combine\n");
			}
		} else {
			Com_Printf("...GL_EXT_texture_env_combine not found\n");
		}
	}
	/* Vic - end */

	/* NeVo - Anisotropic Filtering Support */
	gl_config.anisotropic = false;
	if (strstr(gl_config.extensions_string, "GL_EXT_texture_filter_anisotropic")) {
		ri.Con_Printf(PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic\n");
		gl_config.anisotropic = true;
		qglGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_config.max_anisotropy);
		ri.Cvar_SetValue("gl_anisotropic_avail", gl_config.max_anisotropy);
	} else {
		ri.Con_Printf(PRINT_ALL, "..GL_EXT_texture_filter_anisotropic not found\n");
		gl_config.anisotropic = false;
		gl_config.max_anisotropy = 0.0;
		ri.Cvar_SetValue("gl_anisotropic_avail", 0.0);
	}
	ri.Con_Printf(PRINT_ALL, "Maximum anisotropy level: %g\n", gl_config.max_anisotropy);

	gl_state.nv_fog = false;
	
	if (strstr(gl_config.extensions_string, "GL_NV_fog_distance")) {
		if (!gl_nv_fog->value) {

			ri.Con_Printf(PRINT_ALL, "...ignoring GL_NV_fog_distance\n");
			gl_state.nv_fog = false;

		} else {

			ri.Con_Printf(PRINT_ALL, "...using GL_NV_fog_distance\n");
			gl_state.nv_fog = true;
		}

	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_NV_fog_distance not found\n");
		gl_state.nv_fog = false;
		ri.Cvar_Set("gl_nv_fog", "0");

	}

	/*
	 * ri.Con_Printf( PRINT_ALL, "Initializing NVIDIA-only extensions:\n" );
	 */

	if (strstr(gl_config.extensions_string, "GL_NV_texture_rectangle")) {
		Com_Printf("...using GL_NV_texture_rectangle\n");
		gl_state.tex_rectangle = true;
	} else if (strstr(gl_config.extensions_string, "GL_EXT_texture_rectangle")) {
		Com_Printf("...using GL_EXT_texture_rectangle\n");
		gl_state.tex_rectangle = true;
	} else {
		Com_Printf("...GL_NV_texture_rectangle not found\n");
		gl_state.tex_rectangle = false;
	}

	if (strstr(gl_config.extensions_string, "GL_NV_register_combiners")) {
		ri.Con_Printf(PRINT_ALL, "...using GL_NV_register_combiners\n");

		qglCombinerParameterfvNV = (PFNGLCOMBINERPARAMETERFVNVPROC) qwglGetProcAddress("glCombinerParameterfvNV");
		qglCombinerParameterfNV = (PFNGLCOMBINERPARAMETERFNVPROC) qwglGetProcAddress("glCombinerParameterfNV");
		qglCombinerParameterivNV = (PFNGLCOMBINERPARAMETERIVNVPROC) qwglGetProcAddress("glCombinerParameterivNV");
		qglCombinerParameteriNV = (PFNGLCOMBINERPARAMETERINVPROC) qwglGetProcAddress("glCombinerParameteriNV");
		qglCombinerInputNV = (PFNGLCOMBINERINPUTNVPROC) qwglGetProcAddress("glCombinerInputNV");
		qglCombinerOutputNV = (PFNGLCOMBINEROUTPUTNVPROC) qwglGetProcAddress("glCombinerOutputNV");
		qglFinalCombinerInputNV = (PFNGLFINALCOMBINERINPUTNVPROC) qwglGetProcAddress("glFinalCombinerInputNV");
		qglGetCombinerInputParameterfvNV = (PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) qwglGetProcAddress("glGetCombinerInputParameterfvNV");
		qglGetCombinerInputParameterivNV = (PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) qwglGetProcAddress("glGetCombinerInputParameterivNV");
		qglGetCombinerOutputParameterfvNV = (PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) qwglGetProcAddress("glGetCombinerOutputParameterfvNV");
		qglGetCombinerOutputParameterivNV = (PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) qwglGetProcAddress("glGetCombinerOutputParameterivNV");
		qglGetFinalCombinerInputParameterfvNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) qwglGetProcAddress("glGetFinalCombinerInputParameterfvNV");
		qglGetFinalCombinerInputParameterivNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) qwglGetProcAddress("glGetFinalCombinerInputParameterivNV");

		gl_state.reg_combiners = true;
	} else {
		ri.Con_Printf(PRINT_ALL, "...ignoring GL_NV_register_combiners\n");
		gl_state.reg_combiners = false;
	}

	/* Texture Shader support - MrG */
	if (strstr(gl_config.extensions_string, "GL_NV_texture_shader")) {
		ri.Con_Printf(PRINT_ALL, "...using GL_NV_texture_shader\n");
		gl_config.NV_texshaders = true;
	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_NV_texture_shader not found\n");
		gl_config.NV_texshaders = false;
	}

	if (strstr(gl_config.extensions_string, "GL_NV_multisample_filter_hint")) {
		gl_config.GL_EXT_nv_multisample_filter_hint = true;
		ri.Con_Printf(PRINT_ALL, "...using GL_NV_multisample_filter_hint\n");
	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_NV_multisample_filter_hint not found\n");
		gl_config.GL_EXT_nv_multisample_filter_hint = true;
	}

	if (strstr(gl_config.extensions_string, "GL_ARB_texture_compression")) {
		if (!gl_ext_texture_compression->value) {
			ri.Con_Printf(PRINT_ALL, "...ignoring GL_ARB_texture_compression\n");
			gl_state.texture_compression = false;
		} else {
			ri.Con_Printf(PRINT_ALL, "...using GL_ARB_texture_compression\n");
			gl_state.texture_compression = true;
		}
	} else {
		ri.Con_Printf(PRINT_ALL, "...GL_ARB_texture_compression not found\n");
		gl_state.texture_compression = false;
		ri.Cvar_Set("gl_ext_texture_compression", "0");
	}

	/* mpo === jitwater */
	if (strstr(gl_config.extensions_string, "GL_ARB_fragment_program")) {
		
		if (maxTextureUnits >= 3 && (gl_reflection_fragment_program->value))
			gl_state.fragment_program = true;

		ri.Con_Printf(PRINT_ALL, "...using GL_ARB_fragment_program\n");

		qglGenProgramsARB = (void *)qwglGetProcAddress("glGenProgramsARB");
		qglDeleteProgramsARB = (void *)qwglGetProcAddress("glDeleteProgramsARB");
		qglBindProgramARB = (void *)qwglGetProcAddress("glBindProgramARB");
		qglProgramStringARB = (void *)qwglGetProcAddress("glProgramStringARB");
		qglProgramEnvParameter4fARB = (void *)qwglGetProcAddress("glProgramEnvParameter4fARB");
		qglProgramLocalParameter4fARB = (void *)qwglGetProcAddress("glProgramLocalParameter4fARB");
		
		if (!gl_reflection_fragment_program->value) {
			gl_state.fragment_program = false;
			ri.Con_Printf(PRINT_ALL, "\x02" "GL_ARB_fragment_program available but disabled, set the variable gl_reflection_fragment_program to 1 and type vid_restart\n");
		} 

		if (!(qglGenProgramsARB && 
		      qglDeleteProgramsARB && 
		      qglBindProgramARB &&
		      qglProgramStringARB && 
		      qglProgramEnvParameter4fARB && 
		      qglProgramLocalParameter4fARB)) {
				gl_state.fragment_program = false;
				ri.Cvar_Set("gl_reflection_fragment_program", "0");
				ri.Con_Printf(PRINT_ALL, "... Failed!  Fragment programs disabled\n");
		}
	} 
	else {
		gl_state.fragment_program = false;
		ri.Con_Printf(PRINT_ALL,"\x02" "GL_ARB_fragment_program not found\n");
		ri.Con_Printf(PRINT_ALL,"\x02" "Don't try to run water distorsion, it will crash\n");
	}
	/* mpo === jitwater */
	
	GL_SetDefaultState();

	/*
	 * * draw our stereo patterns
	 */
	/* #if 0 // commented out until H3D pays us the money they owe us */
	GL_DrawStereoPattern();
	/* #endif */

	/* jitanisotropy(make sure nobody goes out of bounds) */
	if (gl_anisotropic->value < 0)
		ri.Cvar_Set("gl_anisotropic", "0");
	else if (gl_anisotropic->value > gl_config.max_anisotropy)
		ri.Cvar_SetValue("gl_anisotropic", gl_config.max_anisotropy);

	if (gl_lightmap_texture_saturation->value > 1 || gl_lightmap_texture_saturation->value < 0)
		ri.Cvar_Set("gl_lightmap_texture_saturation", "1");	/* jitsaturation */

	GL_InitImages();
	Mod_Init();
	R_InitParticleTexture();
	Draw_InitLocal();

	ri.Con_Printf(PRINT_ALL, "-------------------------------\n");
	ri.Con_Printf(PRINT_ALL, "\x02" "Render Initialised in: %.2fs\n", (Sys_Milliseconds() - init_time) * 0.001);
	ri.Con_Printf(PRINT_ALL, "-------------------------------\n");

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

	R_init_refl(gl_reflection_max->value);	/* MPO : init reflections */

	return 0;
}

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

	Mod_FreeAll();

	GL_ShutdownImages();

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

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

	/*
	 * * shutdown our reflective arrays
	 */
	R_shutdown_refl();
}



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

	gl_state.camera_separation = camera_separation;

	if (font_color->modified) {
		RefreshFont();
	}

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

		ref = ri.Cvar_Get("vid_ref", "q2glx", 0);
		ref->modified = true;
	}
	if (gl_ext_nv_multisample_filter_hint->modified) {
		gl_ext_nv_multisample_filter_hint->modified = false;

		if (gl_config.GL_EXT_nv_multisample_filter_hint) {
			if (!strcmp(gl_ext_nv_multisample_filter_hint->string, "nicest"))
				qglHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
			else
				qglHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST);
		}
	}
	/* NiceAss: scale -Maniac */
	if (cl_hudscale->modified) {
		int		width     , height;

		if (cl_hudscale->value < 1.0)
			ri.Cvar_SetValue("cl_hudscale", 1.0);

		/* get the current resolution */
		ri.Vid_GetModeInfo(&width, &height, gl_mode->value);
		/* lie to client about new scaled window size */
		ri.Vid_NewWindow(width / cl_hudscale->value, height / cl_hudscale->value);

		cl_hudscale->modified = false;
	}
	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 (gl_state.hwgamma) {
			UpdateHardwareGamma();
		} else if (gl_config.renderer & (GL_RENDERER_VOODOO)) {
			char		envbuffer[1024];
			float		g;

			g = 2.00 * (0.8 - (vid_gamma->value - 0.5)) + 1.0;
			Com_sprintf(envbuffer, sizeof(envbuffer), "SSTV2_GAMMA=%f", g);
			putenv(envbuffer);
			Com_sprintf(envbuffer, sizeof(envbuffer), "SST_GAMMA=%f", g);
			putenv(envbuffer);
		}
	}
#ifdef QMAX
	if (gl_particlelighting->modified) {
		gl_particlelighting->modified = false;
		if (gl_particlelighting->value < 0)
			gl_particlelighting->value = 0;
		if (gl_particlelighting->value > 1)
			gl_particlelighting->value = 1;
	}
#endif

	GLimp_BeginFrame(camera_separation);

	/*
	 * * go into 2D mode
	 */
	qglViewport(0, 0, vid.width, vid.height);
	qglMatrixMode(GL_PROJECTION);
	qglLoadIdentity();
	/* NiceAss: Scale the screen */
	qglOrtho(0, vid.width / cl_hudscale->value, vid.height / cl_hudscale->value, 0, -99999, 99999);
	/* 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 (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_texturealphamode->modified) {
		GL_TextureAlphaMode(gl_texturealphamode->string);
		gl_texturealphamode->modified = false;
	}
	if (gl_texturesolidmode->modified) {
		GL_TextureSolidMode(gl_texturesolidmode->string);
		gl_texturesolidmode->modified = false;
	}

	/* 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++) {
#ifdef QMAX
			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;
#else
			rp[i * 4 + 0] = LittleLong(d_8to24table[i]) & 0xff;
			rp[i * 4 + 1] = (LittleLong(d_8to24table[i]) >> 8) & 0xff;
			rp[i * 4 + 2] = (LittleLong(d_8to24table[i]) >> 16) & 0xff;
#endif
			rp[i * 4 + 3] = 0xff;
		}
	}
#ifndef QMAX
	GL_SetTexturePalette(r_rawpalette);
#endif

	qglClearColor(0, 0, 0, 0);
	qglClear(GL_COLOR_BUFFER_BIT);
	qglClearColor(1, 0, 0.5, 0.5);

}

/*
 * * R_DrawBeam
 */
#ifdef QMAX
void		R_RenderBeam(vec3_t start, vec3_t end, float size, float red, float green, float blue, float alpha);
void
R_DrawBeam(entity_t * e)
{

	R_RenderBeam(e->origin, e->oldorigin, e->frame,
	    (d_8to24table[e->skinnum & 0xFF]) & 0xFF,
	    (d_8to24table[e->skinnum & 0xFF] >> 8) & 0xFF,
	    (d_8to24table[e->skinnum & 0xFF] >> 16) & 0xFF,
	    e->alpha * 254);
}

void
R_RenderBeam(vec3_t start, vec3_t end, float size, float red, float green, float blue, float alpha)
{
	float		len;
	vec3_t		coord[4], ang_up, ang_right, vdelta;

	GL_TexEnv(GL_MODULATE);
	qglDepthMask(false);
	qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
	qglEnable(GL_BLEND);
	qglShadeModel(GL_SMOOTH);
	GL_Bind(r_particlebeam->texnum);
	qglColor4ub(red, green, blue, alpha);

	VectorSubtract(start, end, ang_up);
	len = VectorNormalize(ang_up);

	VectorSubtract(r_refdef.vieworg, start, vdelta);
	CrossProduct(ang_up, vdelta, ang_right);
	if (!VectorCompare(ang_right, vec3_origin))
		VectorNormalize(ang_right);

	VectorScale(ang_right, size * 3.0, ang_right);

	VectorAdd(start, ang_right, coord[0]);
	VectorAdd(end, ang_right, coord[1]);
	VectorSubtract(end, ang_right, coord[2]);
	VectorSubtract(start, ang_right, coord[3]);

	qglPushMatrix();
	{
		qglBegin(GL_QUADS);
		{
			qglTexCoord2f(0, 1);
			qglVertex3fv(coord[0]);
			qglTexCoord2f(0, 0);
			qglVertex3fv(coord[1]);
			qglTexCoord2f(1, 0);
			qglVertex3fv(coord[2]);
			qglTexCoord2f(1, 1);
			qglVertex3fv(coord[3]);
		}
		qglEnd();
	}
	qglPopMatrix();

	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	GL_TexEnv(GL_MODULATE);
	qglDepthMask(true);
	qglDisable(GL_BLEND);
	qglColor4f(1, 1, 1, 1);
}

#else
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 = (LittleLong(d_8to24table[e->skinnum & 0xFF])) & 0xFF;
	g = (LittleLong(d_8to24table[e->skinnum & 0xFF]) >> 8) & 0xFF;
	b = (LittleLong(d_8to24table[e->skinnum & 0xFF]) >> 16) & 0xFF;

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

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

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

#endif
/* =================================================================== */


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

void		R_RenderFrame(refdef_t * fd);

struct image_s *Draw_FindPic(char *name);

void		Draw_ScaledPic(int x, int y, float scale, float alpha, char *pic, float red, float green, float blue, qboolean fixcoords, qboolean repscale);
void		Draw_Pic  (int x, int y, char *name, float alpha);
void		Draw_Char (int x, int y, int num, int alpha);
void		Draw_TileClear(int x, int y, int w, int h, char *name);
void		Draw_Fill (int x, int y, int w, int h, int c);
void		Draw_FadeScreen(void);

#ifdef QMAX
void		SetParticlePicture(int num, char *name);

#endif

/*
 * @@@@@@@@@@@@@@@@@@@@@ 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;
#ifdef QMAX
	re.SetParticlePicture = SetParticlePicture;
#endif

	re.DrawGetPicSize = Draw_GetPicSize;
	re.DrawScaledPic = Draw_ScaledPic;
	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;

	re.AddDecal = R_AddDecal;

	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
