/*
 * 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.
 *
 */
/* gl_mesh.c: triangle model functions */

#include "gl_local.h"

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

/*
 * =============================================================
 *
 * ALIAS MODELS
 *
 * =============================================================
 */

#define NUMVERTEXNORMALS	162

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

static vec4_t	s_lerped[MAX_VERTS];

extern vec3_t	lightspot;

vec3_t		viewdir;
vec3_t		lightdir;

vec3_t		shadevector;
float		shadelight[3];

m_dlight_t	model_dlights[MAX_MODEL_DLIGHTS];
int		model_dlights_num;

extern cvar_t  *gl_shading;

/* precalculated dot products for quantized angles */
#define SHADEDOT_QUANT 16
float		r_avertexnormal_dots[SHADEDOT_QUANT][256] =
#include "anormtab.h"
;

float          *shadedots = r_avertexnormal_dots[0];

void
GL_LerpVerts(int nverts, dtrivertx_t * v, dtrivertx_t * ov, dtrivertx_t * verts, float *lerp, float move[3], float frontv[3], float backv[3])
{
	int		i;

	/* PMM -- added RF_SHELL_DOUBLE, RF_SHELL_HALF_DAM */
	if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) {
		for (i = 0; i < nverts; i++, v++, ov++, lerp += 4) {
			float          *normal = r_avertexnormals[verts[i].lightnormalindex];

			lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0] + normal[0] * POWERSUIT_SCALE;
			lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1] + normal[1] * POWERSUIT_SCALE;
			lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2] + normal[2] * POWERSUIT_SCALE;
		}
	} else {
		for (i = 0; i < nverts; i++, v++, ov++, lerp += 4) {
			lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0];
			lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1];
			lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2];
		}
	}

}

/*
 * ============================ Discoloda's cellshading outline routine
 * =============================
 */

#define OUTLINEDROPOFF 1000.0	/* distance away for it to stop */
void
GL_DrawOutLine(dmdl_t * paliashdr, int posenum, float width)
{
	dtrivertx_t    *verts;
	int            *order;
	int		count;
	float		strength, len;
	daliasframe_t  *frame;
	qboolean	translucent = false;

	frame = (daliasframe_t *) ((byte *) paliashdr + paliashdr->ofs_frames
	    + currententity->frame * paliashdr->framesize);
	verts = frame->verts;

	order = (int *)((byte *) paliashdr + paliashdr->ofs_glcmds);

	if (currententity->flags & RF_TRANSLUCENT)
		return;

	qglEnable(GL_LINE_SMOOTH);
	qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	qglCullFace(GL_BACK);

	/* this makes long distance make line smaller, but never gone... */
	{
		vec3_t		length;

		VectorSubtract(r_refdef.vieworg, currententity->origin, length);
		len = VectorNormalize(length);

		strength = (OUTLINEDROPOFF - len) / OUTLINEDROPOFF;
		if (strength > 1)
			strength = 1;
		if (strength < 0)
			strength = 0;
	}

	qglColor4f(0, 0, 0, 1);
	qglLineWidth(width * strength);

	if (translucent) {
		qglDisable(GL_TEXTURE_2D);
		qglEnable(GL_BLEND);
	}
	/* Now Draw... */
	while (1) {
		/* get the vertex count and primitive type */
		count = *order++;
		if (!count)
			break;	/* done */

		if (count < 0) {
			count = -count;
			qglBegin(GL_TRIANGLE_FAN);
		} else
			qglBegin(GL_TRIANGLE_STRIP);
		{
			do {
				qglVertex3fv(s_lerped[order[2]]);
				order += 3;
			} while (--count);
		}
		qglEnd();
	}

	if (translucent) {
		qglEnable(GL_TEXTURE_2D);
		qglDisable(GL_BLEND);
	}
	qglCullFace(GL_FRONT);
	qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	qglLineWidth(1);
}

/*
 * ============= GL_DrawAliasFrameLerp
 *
 * interpolates between two frames and origins FIXME: batch lerp all vertexes
 * =============
 */

void
lightAliasModel(vec3_t baselight, dtrivertx_t * verts, dtrivertx_t * ov, float backlerp, vec3_t lightOut)
{
	int		i;
	float		l;


	if (gl_shading->value) {
		l = 2.0 * VLight_LerpLight(verts->lightnormalindex,
		    ov->lightnormalindex,
		    backlerp, lightdir, currententity->angles, false);

		VectorScale(baselight, l, lightOut);

		if (model_dlights_num)
			for (i = 0; i < model_dlights_num; i++) {


				l = 2.0 * VLight_LerpLight(verts->lightnormalindex,
				    ov->lightnormalindex,
				    backlerp, model_dlights[i].direction, currententity->angles, true);

				VectorMA(lightOut, l, model_dlights[i].color, lightOut);
			}

	} else {
		l = shadedots[verts->lightnormalindex];
		VectorScale(baselight, l, lightOut);
	}

	for (i = 0; i < 3; i++) {
		if (lightOut[i] < 0)
			lightOut[i] = 0;
		if (lightOut[i] > 1)
			lightOut[i] = 1;
	}
}

extern image_t *draw_chars;
void
GL_DrawAliasFrameLerp(dmdl_t * paliashdr, float backlerp)
{
	float		l;
	daliasframe_t  *frame, *oldframe;
	dtrivertx_t    *v, *ov, *verts;
	int            *order;
	int		count;
	float		frontlerp;
	float		alpha;
	vec3_t		move, delta, vectors[3];
	vec3_t		frontv, backv;
	int		i;
	int		index_xyz;
	float          *lerp;

	frame = (daliasframe_t *) ((byte *) paliashdr + paliashdr->ofs_frames
	    + currententity->frame * paliashdr->framesize);
	verts = v = frame->verts;

	oldframe = (daliasframe_t *) ((byte *) paliashdr + paliashdr->ofs_frames
	    + currententity->oldframe * paliashdr->framesize);
	ov = oldframe->verts;

	order = (int *)((byte *) paliashdr + paliashdr->ofs_glcmds);

	if (currententity->flags & RF_TRANSLUCENT)
		alpha = currententity->alpha;
	else
		alpha = 1.0;

	/* PMM - added double shell */
	if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) {
		GL_Bind(r_shelltexture->texnum);
		if (gl_shellstencil->value)
			GL_Stencil(true);
	}
	frontlerp = 1.0 - backlerp;

	/* move should be the delta back to the previous frame * backlerp */
	VectorSubtract(currententity->oldorigin, currententity->origin, delta);
	AngleVectors(currententity->angles, vectors[0], vectors[1], vectors[2]);

	move[0] = DotProduct(delta, vectors[0]);	/* forward */
	move[1] = -DotProduct(delta, vectors[1]);	/* left */
	move[2] = DotProduct(delta, vectors[2]);	/* up */

	VectorAdd(move, oldframe->translate, move);

	for (i = 0; i < 3; i++) {
		move[i] = backlerp * move[i] + frontlerp * frame->translate[i];
	}

	for (i = 0; i < 3; i++) {
		frontv[i] = frontlerp * frame->scale[i];
		backv[i] = backlerp * oldframe->scale[i];
	}

	lerp = s_lerped[0];

	GL_LerpVerts(paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv);

	if (!(currententity->flags & RF_VIEWERMODEL)) {
		if (gl_vertex_arrays->value) {
			float		colorArray[MAX_VERTS * 4];

			qglEnableClientState(GL_VERTEX_ARRAY);
			qglVertexPointer(3, GL_FLOAT, 16, s_lerped);	/* padded for SIMD */

			if (currententity->flags & 
			    (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) {
				if (!(currententity->flags & RF_TRANS_ADDITIVE))
					qglColor4f(shadelight[0], shadelight[1], shadelight[2], 1.0);
			} else {
				qglEnableClientState(GL_COLOR_ARRAY);
				qglColorPointer(3, GL_FLOAT, 0, colorArray);

				/* pre light everything */
				for (i = 0; i < paliashdr->num_xyz; i++) {
					float	l = shadedots[verts[i].lightnormalindex];

					colorArray[i * 3 + 0] = l * shadelight[0];
					colorArray[i * 3 + 1] = l * shadelight[1];
					colorArray[i * 3 + 2] = l * shadelight[2];
				}
			}

			if (qglLockArraysEXT != 0)
				qglLockArraysEXT(0, paliashdr->num_xyz);

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

				/* PMM - added double damage shell */
				if (currententity->flags & 
				    (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) {
					do {
						index_xyz = order[2];
						order += 3;
						/* c14 added the line below */
						qglTexCoord2f((s_lerped[index_xyz][1] + s_lerped[index_xyz][0]) / 40.0,
								s_lerped[index_xyz][2] / 40.0 - r_refdef.time / 2.0);
						qglVertex3fv(s_lerped[index_xyz]);

					} while (--count);
				} else {
					do {

						/*
						 * texture coordinates come
						 * from the draw list
						 */
						qglTexCoord2f(((float *)order)[0], ((float *)order)[1]);
						index_xyz = order[2];

						order += 3;
						
						qglNormal3fv(r_avertexnormals[verts[index_xyz].lightnormalindex]);
						qglArrayElement(index_xyz);

					} while (--count);
				}
				qglEnd();
			}

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

				if (currententity->flags & 
				    (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) {
					do {
						index_xyz = order[2];
						order += 3;

						if (!(currententity->flags & RF_TRANS_ADDITIVE)) {
							float		thisalpha = alpha;

							if (currententity->renderfx & RF2_CAMERAMODEL && 
							    currententity->flags & RF_TRANSLUCENT) {
								vec3_t		vert_len;

								VectorSubtract(r_refdef.vieworg, s_lerped[index_xyz], vert_len);

								if (thisalpha > alpha)
									thisalpha = alpha;
							}

							qglColor4f(shadelight[0], shadelight[1], shadelight[2], 1.0);
							/* c14 added the line below */
							qglTexCoord2f((s_lerped[index_xyz][1] + s_lerped[index_xyz][0]) / 40.0,
									s_lerped[index_xyz][2] / 40.0 - r_refdef.time / 2.0);
						}
						qglVertex3fv(s_lerped[index_xyz]);

					} while (--count);
				} else {
					do {
						vec3_t		lightcolor;

						/*
						 * texture coordinates come
						 * from the draw list
						 */
						qglTexCoord2f(((float *)order)[0], ((float *)order)[1]);
						index_xyz = order[2];
						order += 3;

						/*
						 * normals and vertexes come
						 * from the frame list
						 */
						l = shadedots[verts[index_xyz].lightnormalindex];

						qglNormal3fv(r_avertexnormals[verts[index_xyz].lightnormalindex]);

						VectorScale(shadelight, l, lightcolor);

						if (!(currententity->flags & RF_TRANS_ADDITIVE)) {
							float		thisalpha = alpha;

							if (currententity->renderfx & RF2_CAMERAMODEL && 
							    currententity->flags & RF_TRANSLUCENT) {
								vec3_t		vert_len;

								VectorSubtract(r_refdef.vieworg, s_lerped[index_xyz], vert_len);

								if (thisalpha > alpha)
									thisalpha = alpha;
							}
							if (r_cellshading->value && !(currententity->flags & RF_TRANSLUCENT)) {
								float		highest = 0;
								float		cellcolors[16] = {
									0.1, 0.1,
									0.3, 0.3, 0.3,
									0.5, 0.5, 0.5, 0.5,
									1.0, 1.0, 1.0, 1.0, 1.0, 1.0
								};

								for (i = 0; i < 3; i++) {
									lightcolor[i] = cellcolors[(int)(lightcolor[i] * 16.0)];
									if (lightcolor[i] > highest)
										highest = lightcolor[i];
								}
								for (i = 0; i < 3; i++)
									lightcolor[i] = highest;
							}
							qglColor4f(lightcolor[0], lightcolor[1], lightcolor[2], thisalpha);
						}
						qglVertex3fv(s_lerped[index_xyz]);
					} while (--count);
				}

				qglEnd();
			}
		}

		/* cell shading */
		if (r_cellshading->value)
			GL_DrawOutLine(paliashdr, currententity->frame, r_cellshading_width->value);
	}
	if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) {
		qglEnable(GL_TEXTURE_2D);
		if (gl_shellstencil->value)
			GL_Stencil(false);
	}
}

/*
 * ============= GL_DrawAliasShadow =============
 */
/* stencilbuffer shadows */
extern qboolean	have_stencil;

#ifdef QMAX
void
RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees);
void
GL_DrawAliasShadow(dmdl_t * paliashdr, int posenum, qboolean mirrored)
{
	dtrivertx_t    *verts;
	int            *order;
	vec3_t		temp[4], point, angles, vectors[3], forward, right,
			up;
	float		height, lheight;
	int		count;
	daliasframe_t  *frame;

	VectorSet(forward, 0.5, 0.5, 0);
	VectorSet(right, 0, 1, 0);
	VectorSet(up, 0, 0, 1);

	VectorCopy(currententity->angles, angles);
	angles[YAW] = -angles[YAW];
	AngleVectors(angles, vectors[0], vectors[1], vectors[2]);
	angles[YAW] = -angles[YAW];

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

	frame = (daliasframe_t *) ((byte *) paliashdr + paliashdr->ofs_frames
	    + currententity->frame * paliashdr->framesize);
	verts = frame->verts;

	height = 0;

	order = (int *)((byte *) paliashdr + paliashdr->ofs_glcmds);

	height = -lheight + 0.1;

	/* stencilbuffer shadows */
	if (have_stencil && gl_shellstencil->value) {
		qglEnable(GL_STENCIL_TEST);
		qglStencilFunc(GL_EQUAL, 1, 2);
		qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
	}
	/* End Stencil shadows - MrG */
	GL_BlendFunction(GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA);


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

		do {
			if (true) {	/* relative origin/angle -- faster... */

				memcpy(point, s_lerped[order[2]], sizeof(point));


				point[0] -= shadevector[0] * (point[2] + lheight);
				point[1] -= shadevector[1] * (point[2] + lheight);
				point[2] = height;
			} else {/* absolute origin/angle -- much slower... */
				memcpy(temp[0], s_lerped[order[2]], sizeof(temp[0]));

				RotatePointAroundVector(temp[1], forward, temp[0], angles[ROLL]);
				RotatePointAroundVector(temp[2], up, temp[0], angles[YAW]);
				RotatePointAroundVector(temp[3], right, temp[2], angles[PITCH]);

				VectorAdd(temp[3], currententity->origin, point);

				point[0] -= shadevector[0] * (point[2] + lheight);
				point[1] -= shadevector[1] * (point[2] + lheight);
				point[2] = lightspot[2] + 0.1;
			}

			qglVertex3fv(point);

			order += 3;

		} while (--count);

		qglEnd();
	}

	/* stencilbuffer shadows */
	if (have_stencil && gl_shellstencil->value)
		qglDisable(GL_STENCIL_TEST);

	angles[YAW] = -angles[YAW];
}
#else
void
GL_DrawAliasShadow(dmdl_t * paliashdr, int posenum)
{
	dtrivertx_t    *verts;
	int            *order;
	vec3_t		point;
	float		height, lheight;
	int		count;
	daliasframe_t  *frame;

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

	frame = (daliasframe_t *) ((byte *) paliashdr + paliashdr->ofs_frames
	    + currententity->frame * paliashdr->framesize);
	verts = frame->verts;

	height = 0;

	order = (int *)((byte *) paliashdr + paliashdr->ofs_glcmds);

	height = -lheight + 0.1;

	/* stencilbuffer shadows */
	if (have_stencil && gl_shellstencil->value) {
		qglEnable(GL_STENCIL_TEST);
		qglStencilFunc(GL_EQUAL, 1, 2);
		qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
	}
	/* End Stencil shadows - MrG */
	GL_BlendFunction(GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA);

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

		do {
			/* normals and vertexes come from the frame list */

			/*
			 * point[0] = verts[order[2]].v[0] * frame->scale[0]
			 * + frame->translate[0]; point[1] =
			 * verts[order[2]].v[1] * frame->scale[1] +
			 * frame->translate[1]; point[2] =
			 * verts[order[2]].v[2] * frame->scale[2] +
			 * frame->translate[2];
			 */

			memcpy(point, s_lerped[order[2]], sizeof(point));

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

			order += 3;

			/* verts++; */

		} while (--count);

		qglEnd();
	}

	/* stencilbuffer shadows */
	if (have_stencil && gl_shellstencil->value)
		qglDisable(GL_STENCIL_TEST);
}
#endif

/*
 * * R_CullAliasModel
 */
static qboolean
R_CullAliasModel(vec3_t bbox[8], entity_t * e)
{
	int		i;
	vec3_t		mins, maxs;
	dmdl_t         *paliashdr;
	vec3_t		vectors[3];
	vec3_t		thismins, oldmins, thismaxs, oldmaxs;
	daliasframe_t  *pframe, *poldframe;
	vec3_t		angles;

	paliashdr = (dmdl_t *) currentmodel->extradata;

	if ((e->frame >= paliashdr->num_frames) || (e->frame < 0)) {
		ri.Con_Printf(PRINT_DEVELOPER, "R_CullAliasModel %s: no such frame %d\n",
		    currentmodel->name, e->frame);
		e->frame = 0;
	}
	if ((e->oldframe >= paliashdr->num_frames) || (e->oldframe < 0)) {
		ri.Con_Printf(PRINT_DEVELOPER, "R_CullAliasModel %s: no such oldframe %d\n",
		    currentmodel->name, e->oldframe);
		e->oldframe = 0;
	}
	pframe = (daliasframe_t *) ((byte *) paliashdr +
	    paliashdr->ofs_frames +
	    e->frame * paliashdr->framesize);

	poldframe = (daliasframe_t *) ((byte *) paliashdr +
	    paliashdr->ofs_frames +
	    e->oldframe * paliashdr->framesize);

	/*
	 * * compute axially aligned mins and maxs
	 */
	if (pframe == poldframe) {
		for (i = 0; i < 3; i++) {
			mins[i] = pframe->translate[i];
			maxs[i] = mins[i] + pframe->scale[i] * 255;
		}
	} else {
		for (i = 0; i < 3; i++) {
			thismins[i] = pframe->translate[i];
			thismaxs[i] = thismins[i] + pframe->scale[i] * 255;

			oldmins[i] = poldframe->translate[i];
			oldmaxs[i] = oldmins[i] + poldframe->scale[i] * 255;

			if (thismins[i] < oldmins[i])
				mins[i] = thismins[i];
			else
				mins[i] = oldmins[i];

			if (thismaxs[i] > oldmaxs[i])
				maxs[i] = thismaxs[i];
			else
				maxs[i] = oldmaxs[i];
		}
	}

	/*
	 * * compute a full bounding box
	 */
	for (i = 0; i < 8; i++) {
		vec3_t		tmp;

		if (i & 1)
			tmp[0] = mins[0];
		else
			tmp[0] = maxs[0];

		if (i & 2)
			tmp[1] = mins[1];
		else
			tmp[1] = maxs[1];

		if (i & 4)
			tmp[2] = mins[2];
		else
			tmp[2] = maxs[2];

		VectorCopy(tmp, bbox[i]);
	}

	/*
	 * * rotate the bounding box
	 */
	VectorCopy(e->angles, angles);
	angles[YAW] = -angles[YAW];
	AngleVectors(angles, vectors[0], vectors[1], vectors[2]);

	for (i = 0; i < 8; i++) {
		vec3_t		tmp;

		VectorCopy(bbox[i], tmp);

		bbox[i][0] = DotProduct(vectors[0], tmp);
		bbox[i][1] = -DotProduct(vectors[1], tmp);
		bbox[i][2] = DotProduct(vectors[2], tmp);

		VectorAdd(e->origin, bbox[i], bbox[i]);
	}

	{
		int		p         , f, aggregatemask = ~0;

		for (p = 0; p < 8; p++) {
			int		mask = 0;

			for (f = 0; f < 4; f++) {
				float		dp = DotProduct(frustum[f].normal, bbox[p]);

				if ((dp - frustum[f].dist) < 0) {
					mask |= (1 << f);
				}
			}

			aggregatemask &= mask;
		}

		if (aggregatemask) {
			return true;
		}
		return false;
	}
}

/*
 * ================= checkModelLights
 *
 * enables lights if they are in range .. =================
 */
qboolean	g_glLighting = false;
int
checkModelLights(void)
{

	int		lnum = 0;
	int		glLight = 0;
	int		noEnabledLights = 0;

	if (g_numGlLights != 0) {

		if (shadelight[0] >= 1.5 && shadelight[1] >= 1.5 && shadelight[2] >= 1.5) {

			g_glLighting = false;
			return 0;
		}
		for (; (lnum < r_refdef.num_dlights) && (glLight < g_numGlLights); lnum++) {

			dlight_t       *dl = &r_refdef.dlights[lnum];
			float		displacement[3];
			float		distSquared;

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

			VectorSubtract(dl->origin, currententity->origin, displacement);
			distSquared = displacement[0] * displacement[0];
			distSquared += displacement[1] * displacement[1];
			distSquared += displacement[2] * displacement[2];

			if (distSquared < dl->intensity * dl->intensity) {
				qglEnable(GL_LIGHT0 + glLight);
				g_glLighting = true;
				noEnabledLights++;
			} else {
				qglDisable(GL_LIGHT0 + glLight);
			}

			++glLight;
		}
	}
	return noEnabledLights; /* return number of lights that were enabled */
}

/*
 * ================= checkWallLights
 *
 * enables lights if they are in range of a wall light =================
 */
void
checkWallLights(int numberOfEnabledLights)
{

	float		displacement[3];
	float		distSquared;
	int		i;
	int		glLight;

	if (shadelight[0] >= 1.5 && shadelight[1] >= 1.5 && shadelight[2] >= 1.5) {
		return;
	}
	glLight = numberOfEnabledLights;

	for (i = numberOfEnabledLights; i < numberOfWallLights && (glLight < 8); i++) {

		if (wallLightArray[i - numberOfEnabledLights] == NULL)
			continue;

		VectorSubtract(wallLightArray[i - numberOfEnabledLights]->origin, currententity->origin, displacement);
		distSquared = displacement[0] * displacement[0];
		distSquared += displacement[1] * displacement[1];
		distSquared += displacement[2] * displacement[2];

		if (distSquared < 250 * 250) {

			qglEnable(GL_LIGHT0 + glLight);
			qglLightfv(GL_LIGHT0 + glLight, GL_DIFFUSE, wallLightArray[i - numberOfEnabledLights]->color);
			qglLightfv(GL_LIGHT0 + glLight, GL_POSITION, wallLightArray[i - numberOfEnabledLights]->origin);
			qglLightf(GL_LIGHT0 + glLight, GL_LINEAR_ATTENUATION, 0.01);


			qglLightf(GL_LIGHT0 + glLight, GL_SPOT_CUTOFF, 180);
			g_glLighting = true;

			++glLight;
		}
	}

	for (; glLight < 8; ++glLight) {
		qglDisable(GL_LIGHT0 + glLight);
	}

}


/*
 * ================= R_DrawAliasModel
 *
 * =================
 */
void
GL_FlipModel(qboolean on)
{
	extern void	MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);

	if (on) {
		qglMatrixMode(GL_PROJECTION);
		qglPushMatrix();
		qglLoadIdentity();
		qglScalef(-1, 1, 1);
		MYgluPerspective(r_refdef.fov_y, (float)r_refdef.width / r_refdef.height, 4, 4096);
		qglMatrixMode(GL_MODELVIEW);

		qglCullFace(GL_BACK);
	} else {
		qglMatrixMode(GL_PROJECTION);
		qglPopMatrix();
		qglMatrixMode(GL_MODELVIEW);
		qglCullFace(GL_FRONT);
	}
}

void
R_DrawAliasModel(entity_t * e)
{
	int		i;
	dmdl_t         *paliashdr;
	float		an;
	vec3_t		bbox[8];
	image_t        *skin;
	int		lightNumber = 0;

#ifdef QMAX
	qboolean	mirrormodel = currententity->flags & RF_MIRRORMODEL || (r_lefthand->value == 1.0 &&
	    currententity->flags & RF_WEAPONMODEL);
#endif

	if (!(e->flags & RF_WEAPONMODEL || e->flags & RF_VIEWERMODEL || e->renderfx & RF2_CAMERAMODEL)) {
		if (R_CullAliasModel(bbox, e))
			return;
	}
	if (e->flags & RF_WEAPONMODEL) {
		if (r_lefthand->value == 2 || g_drawing_refl)	/* MPO */
			return;
	}
	paliashdr = (dmdl_t *) currentmodel->extradata;

	/* get lighting information */
	
	/* PMM - rewrote, reordered to handle new shells & mixing */

	/*
	 * PMM - 3.20 code .. replaced with original way of doing it to keep
	 * mod authors happy
	 */
	if (currententity->flags & (RF_SHELL_HALF_DAM | RF_SHELL_GREEN | RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE)) {
		VectorClear(shadelight);
		if (currententity->flags & RF_SHELL_HALF_DAM) {
			shadelight[0] = 0.56;
			shadelight[1] = 0.59;
			shadelight[2] = 0.45;
		}
		if (currententity->flags & RF_SHELL_DOUBLE) {
			shadelight[0] = 0.9;
			shadelight[1] = 0.7;
		}
		if (currententity->flags & RF_SHELL_RED)
			shadelight[0] = 1.0;
		if (currententity->flags & RF_SHELL_GREEN)
			shadelight[1] = 1.0;
		if (currententity->flags & RF_SHELL_BLUE)
			shadelight[2] = 1.0;
	} else if (currententity->flags & RF_FULLBRIGHT) {
		for (i = 0; i < 3; i++)
			shadelight[i] = 1.0;
	} else {
		/* Set up basic lighting... */
		if (gl_shading->value) {
			int		max = 3;

			if (max < 0)
				max = 0;
			if (max > MAX_MODEL_DLIGHTS)
				max = MAX_MODEL_DLIGHTS;

			R_LightPointDynamics(currententity->origin, shadelight, model_dlights, &model_dlights_num, max);
		} else {
			R_LightPoint(currententity->origin, shadelight);
			model_dlights_num = 0;
		}

		/* player lighting hack for communication back to server */
		/* big hack! */
		if (currententity->flags & RF_WEAPONMODEL) {

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

		}
		if (gl_monolightmap->string[0] != '0') {
			float		s = shadelight[0];

			if (s < shadelight[1])
				s = shadelight[1];
			if (s < shadelight[2])
				s = shadelight[2];

			shadelight[0] = s;
			shadelight[1] = s;
			shadelight[2] = s;
		}
	}

	if (currententity->flags & RF_MINLIGHT) {
		for (i = 0; i < 3; i++)
			if (shadelight[i] > 0.1)
				break;
		if (i == 3) {
			shadelight[0] = 0.1;
			shadelight[1] = 0.1;
			shadelight[2] = 0.1;
		}
	}
	if (currententity->flags & RF_GLOW) {	/* bonus items will pulse with time */
		float		scale;
		float		min;

		scale = 0.1 * sin(r_refdef.time * 7);
		for (i = 0; i < 3; i++) {
			min = shadelight[i] * 0.8;
			shadelight[i] += scale;
			if (shadelight[i] < min)
				shadelight[i] = min;
		}
	}
	/* ================= */
	/* PGM	ir goggles color override */
	if (r_refdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE) {
		shadelight[0] = 1.0;
		shadelight[1] = 0.0;
		shadelight[2] = 0.0;
	}
	/* PGM	 */
	/* ================= */

	shadedots = r_avertexnormal_dots[((int)(currententity->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];

	an = currententity->angles[1] / 180 * M_PI;
	shadevector[0] = cos(-an);
	shadevector[1] = sin(-an);
	shadevector[2] = 1;
	VectorNormalize(shadevector);

	/* locate the proper data */
	c_alias_polys += paliashdr->num_tris;

	/* draw all the triangles */
	if (currententity->flags & RF_DEPTHHACK)	/* hack the depth range
							 * to prevent view model
							 * from poking into
							 * walls */
		qglDepthRange(gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin));

#if 0
	if (mirrormodel)
		GL_FlipModel(true);
#else
	if ((currententity->flags & RF_WEAPONMODEL) && (r_lefthand->value == 1.0)) {
		extern void	MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);

		qglMatrixMode(GL_PROJECTION);
		qglPushMatrix();
		qglLoadIdentity();
		qglScalef(-1, 1, 1);
		MYgluPerspective(r_refdef.fov_y, (float)r_refdef.width / r_refdef.height, 4, 4096);
		qglMatrixMode(GL_MODELVIEW);

		qglCullFace(GL_BACK);
	}
#endif


	if (gl_shading->value && !cl_3dcam->value) {
		lightNumber = checkModelLights();	/* reduce lights if needed */
		checkWallLights(lightNumber);		/* setup wall lighting */
	}
	qglPushMatrix();
	e->angles[PITCH] = -e->angles[PITCH];	/* sigh. */
	R_RotateForEntity(e);
	e->angles[PITCH] = -e->angles[PITCH];	/* sigh. */

	/* select skin */
	if (currententity->skin)
		skin = currententity->skin;	/* custom player skin */
	else {
		if (currententity->skinnum >= MAX_MD2SKINS)
			skin = currentmodel->skins[0];
		else {
			skin = currentmodel->skins[currententity->skinnum];
			if (!skin)
				skin = currentmodel->skins[0];
		}
	}
	if (!skin)
		skin = r_notexture;	/* fallback... */

	/* draw it */
	if ((currententity->frame >= paliashdr->num_frames)
	    || (currententity->frame < 0)) {
		ri.Con_Printf(PRINT_DEVELOPER, "R_DrawAliasModel %s: no such frame %d\n",
		    currentmodel->name, currententity->frame);
		currententity->frame = 0;
		currententity->oldframe = 0;
	}
	if ((currententity->oldframe >= paliashdr->num_frames)
	    || (currententity->oldframe < 0)) {
		ri.Con_Printf(PRINT_DEVELOPER, "R_DrawAliasModel %s: no such oldframe %d\n",
		    currentmodel->name, currententity->oldframe);
		currententity->frame = 0;
		currententity->oldframe = 0;
	}
	if (!r_lerpmodels->value)
		currententity->backlerp = 0;

	GL_Bind(skin->texnum);

	if (r_model_alpha->value) {
		qglDepthMask(true);
		qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
		qglEnable(GL_BLEND);
	}
	
	qglShadeModel(GL_SMOOTH);

	GL_TexEnv(GL_MODULATE);
	if (currententity->flags & RF_TRANSLUCENT) {
		qglEnable(GL_BLEND);
	}
	if (g_glLighting) {

		qglEnable(GL_LIGHTING);
		qglEnable(GL_COLOR_MATERIAL);

		GL_DrawAliasFrameLerp(paliashdr, currententity->backlerp);

		qglDisable(GL_LIGHTING);
		qglDisable(GL_COLOR_MATERIAL);

		g_glLighting = false;
	} else {
		GL_DrawAliasFrameLerp(paliashdr, currententity->backlerp);
	}

	if (currententity->flags & (RF_SHELL_HALF_DAM | RF_SHELL_GREEN | RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE)) {
		if (have_stencil) {
			qglDisable(GL_STENCIL_TEST);
		}
	}
	GL_TexEnv(GL_REPLACE);
	qglShadeModel(GL_FLAT);

	qglPopMatrix();

#if 0
	if (mirrormodel)
		GL_FlipModel(false);

	if (currententity->flags & RF_TRANSLUCENT) {
		if (currententity->flags & RF_TRANS_ADDITIVE) {
			qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			qglDepthMask(true);
		}
		qglDisable(GL_BLEND);
	}
#else

	if ((currententity->flags & RF_WEAPONMODEL) && (r_lefthand->value == 1.0)) {
		qglMatrixMode(GL_PROJECTION);
		qglPopMatrix();
		qglMatrixMode(GL_MODELVIEW);
		qglCullFace(GL_FRONT);
	}
	if (currententity->flags & RF_TRANSLUCENT) {
		qglDisable(GL_BLEND);
	}
#endif
	if (currententity->flags & RF_DEPTHHACK) {
		qglDepthRange(gldepthmin, gldepthmax);
	}
	if (!(r_refdef.rdflags & RDF_NOWORLDMODEL))	/* no shadows */
		if (!(currententity->renderfx & RF2_NOSHADOW) &&
		    ((currententity->renderfx & RF2_FORCE_SHADOW)
		    || !(currententity->flags & (RF_WEAPONMODEL | RF_NOSHADOW)))
		    ) {
			switch ((int)(gl_shadows->value)) {
			case 0:
				break;
			case 2:
				/* dynamic lighted shadows - psychospaz */
				R_ShadowLight(currententity->origin, shadevector);
			default:
				{
					if (currententity->flags & RF_TRANSLUCENT)
						qglColor4f(0, 0, 0, 0.3 * currententity->alpha);
					else
						qglColor4f(0, 0, 0, 0.3);

					qglPushMatrix();

					/*
					 * don't rotate shadows on ungodly
					 * axes
					 */
					/* R_RotateForEntity (e, false); */
					qglTranslatef(e->origin[0], e->origin[1], e->origin[2]);
					qglRotatef(e->angles[1], 0, 0, 1);

					qglDisable(GL_TEXTURE_2D);
					qglEnable(GL_BLEND);
					/* GL_Stencil(true); */

					if (currententity->flags & RF_TRANSLUCENT) {
						qglColor4f(0, 0, 0, gl_shadows_alpha->value * currententity->alpha);
					} else {
						qglColor4f(0, 0, 0, gl_shadows_alpha->value);
					}
#ifdef QMAX
					GL_DrawAliasShadow(paliashdr, currententity->frame, mirrormodel);
#else
					GL_DrawAliasShadow(paliashdr, currententity->frame);
#endif

					/* GL_Stencil(false); */
					qglEnable(GL_TEXTURE_2D);
					qglDisable(GL_BLEND);
					qglPopMatrix();
				}
				break;
			}
		}
	if (gl_minimap->value) {

		if (numRadarEnts >= MAX_RADAR_ENTS)
			return;
		if (currententity->flags & RF_VIEWERMODEL)
			return;
		if (currententity->flags & RF_WEAPONMODEL)
			return;
		if (currententity->flags & RF_NOSHADOW)
			return;
		if (currententity->renderfx & RF2_CAMERAMODEL)
			return;
		if (currententity->flags & RF2_NOSHADOW)
			return;

		if (currententity->flags & (RF_GLOW | EF_ROTATE)) {
			RadarEnts[numRadarEnts].color[0] = 0.0;
			RadarEnts[numRadarEnts].color[1] = 1.0;
			RadarEnts[numRadarEnts].color[2] = 0.0;
			RadarEnts[numRadarEnts].color[3] = 0.0;
		} else {
			RadarEnts[numRadarEnts].color[0] = 1.0;
			RadarEnts[numRadarEnts].color[1] = 0.0;
			RadarEnts[numRadarEnts].color[2] = 0.0;
			RadarEnts[numRadarEnts].color[3] = 0.0;
		}

		VectorCopy(currententity->origin, RadarEnts[numRadarEnts].org);
		numRadarEnts++;
	}
	qglColor4f(1, 1, 1, 1);
}
