/*
Copyright (C) 1996-1997 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_surf.c: surface-related refresh code

#include "quakedef.h"

#define	LIGHTMAP_BLOCK_WIDTH	128
#define	LIGHTMAP_BLOCK_HEIGHT	128 
#define MAX_LIGHTMAPS			256//64

static	int	lightmap_textures;
unsigned	blocklights[LIGHTMAP_BLOCK_WIDTH*LIGHTMAP_BLOCK_HEIGHT*3];
int r_renderflags;
#define RF_UNDERWATER	2
#define RF_ABOVEWATER	4

typedef struct glRect_s
{
	unsigned char	l, t, w, h;
} glRect_t;

static	glpoly_t	*lightmap_polys[MAX_LIGHTMAPS];
static	qboolean	lightmap_modified[MAX_LIGHTMAPS];
static	glRect_t	lightmap_rectchange[MAX_LIGHTMAPS];

int	allocated[MAX_LIGHTMAPS][LIGHTMAP_BLOCK_WIDTH];
int	last_lightmap_allocated; //ericw -- optimization: remember the index of the last lightmap AllocBlock stored a surf in

extern cvar_t gl_overbright;
extern cvar_t gl_textureless; //R00k - textureless world

// the lightmap texture data needs to be kept in
// main memory so texsubimage can update properly
///TODO MOVE THIS TO THE HUNK
byte	lightmaps[4*MAX_LIGHTMAPS*LIGHTMAP_BLOCK_WIDTH*LIGHTMAP_BLOCK_HEIGHT];

extern cvar_t r_skyscroll;

#define CHAIN_SURF_F2B(surf, chain_tail)		\
	{						\
		*(chain_tail) = (surf);			\
		(chain_tail) = &(surf)->texturechain;	\
		(surf)->texturechain = NULL;		\
	}

#define CHAIN_SURF_B2F(surf, chain) 			\
	{						\
		(surf)->texturechain = (chain);		\
		(chain) = (surf);			\
	}

msurface_t  *skychain = NULL;
msurface_t  *waterchain = NULL;
msurface_t	*alphachain = NULL;

msurface_t	**skychain_tail = &skychain;
msurface_t	**waterchain_tail = &waterchain;
msurface_t	**alphachain_tail = &alphachain;

void R_RenderDynamicLightmaps (msurface_t *fa);

glpoly_t	*fullbright_polys[MAX_GLTEXTURES];
glpoly_t	*luma_polys[MAX_GLTEXTURES];
//glpoly_t	*gloss_polys[MAX_GLTEXTURES];
qboolean	drawfullbrights = false, drawlumas = false;
glpoly_t	*caustics_polys = NULL;
glpoly_t	*detail_polys = NULL;
glpoly_t	*outline_polys = NULL;
glpoly_t	*chrome_polys = (NULL);
glpoly_t	*glass_polys = (NULL);

/*
================
DrawGLPoly
================
*/
void DrawGLPoly(glpoly_t *p)
{
	int		i;
	float	*v;

	glBegin (GL_POLYGON);

	v = p->verts[0];
	for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
	{
		glTexCoord2f (v[3], v[4]);
		glVertex3fv (v);
	}	 
	glEnd ();	 
}


void DrawGLPolyHighlight(glpoly_t *p) //Outline a single polygon
{
	int		i;
	float	*v;

	glEnable (GL_LINE_SMOOTH);
	glDisable (GL_TEXTURE_2D);
	glCullFace (GL_BACK);
	glPolygonMode (GL_FRONT, GL_LINE);
	glLineWidth (2.0f);
	glColor4f (1,1,0,1);

	glBegin (GL_LINE_LOOP);

	v = p->verts[0];
	for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
	{
		glTexCoord2f (v[3], v[4]);
		glVertex3fv (v);
	}	 

	glEnd ();	 

	glCullFace (GL_FRONT);
	glColor4f(1,1,1,1);
	glDisable (GL_LINE_SMOOTH);
	glEnable (GL_TEXTURE_2D);	
	glPolygonMode (GL_FRONT, GL_FILL);
}

void R_RenderFullbrights (void)
{
	int		i;
	glpoly_t	*p;

	if (!drawfullbrights)
		return;

	glDepthMask (GL_FALSE);	// don't bother writing Z
//	glEnable (GL_ALPHA_TEST);

	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	for (i=1 ; i<MAX_GLTEXTURES ; i++)
	{
		if (!fullbright_polys[i])
			continue;
		GL_Bind (i);
		for (p = fullbright_polys[i] ; p ; p = p->fb_chain)
			DrawGLPoly (p);
		fullbright_polys[i] = NULL;
	}

//	glDisable (GL_ALPHA_TEST);
	glDepthMask (GL_TRUE);

	drawfullbrights = false;
}

void R_RenderLumas (void)
{
	int		i;
	glpoly_t	*p;

	if (!drawlumas)
		return;

	glDepthMask (GL_FALSE);	// don't bother writing Z
	glEnable (GL_BLEND);
	glBlendFunc (GL_ONE, GL_ONE);

	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	for (i=1 ; i<MAX_GLTEXTURES ; i++)
	{
		if (!luma_polys[i])
			continue;
		GL_Bind (i);
		for (p = luma_polys[i] ; p ; p = p->luma_chain)
			DrawGLPoly (p);
		luma_polys[i] = NULL;
	}

	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable (GL_BLEND);
	glDepthMask (GL_TRUE);

	drawlumas = false;
}
void EmitDetailPolys (void)
{
	glpoly_t	*p;
	int		i;
	float		*v;

	if (!detail_polys) 
		return;

	GL_Bind (detailtexture);
	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
	glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR);
	glEnable (GL_BLEND);

	for (p = detail_polys ; p ; p = p->detail_chain)
	{
		glBegin (GL_POLYGON);
		v = p->verts[0];
		for (i=0 ; i<p->numverts ; i++, v+=VERTEXSIZE)
		{
			glTexCoord2f (v[7]*8, v[8]*8);//R00k increase size
			glVertex3fv (v);
		}
		glEnd();
	}
	 
	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable (GL_BLEND);
	
	detail_polys = NULL;
}

//=============================================================
// Dynamic lights

typedef struct dlightinfo_s
{
	int	local[2];
	int	rad;
	int	minlight;	// rad - minlight
	int	type;
} dlightinfo_t;

static dlightinfo_t dlightlist[MAX_DLIGHTS];
static int	numdlights;

/*
===============
R_BuildDlightList
===============
*/
void R_BuildDlightList (msurface_t *surf)
{
	int				lnum, i, smax, tmax;
	float			dist;
	vec3_t			impact;
	mtexinfo_t		*tex;
	int				irad, iminlight, local[2], tdmin, sdmin, distmin;
	dlightinfo_t	*light;

	numdlights = 0;

	smax = (surf->extents[0] >> 4) + 1;
	tmax = (surf->extents[1] >> 4) + 1;
	tex = surf->texinfo;

	for (lnum = 0 ; lnum < MAX_DLIGHTS ; lnum++)
	{
		if (cl_dlights[lnum].die < cl.time)
			continue;		// dead light

		if (!(surf->dlightbits[lnum >> 5] & (1 << (lnum & 31))))		//MH
			continue;

		dist = PlaneDiff(cl_dlights[lnum].origin, surf->plane);
		irad = (cl_dlights[lnum].radius - fabs(dist)) * 256;
		iminlight = cl_dlights[lnum].minlight * 256;

		if (irad < iminlight)
			continue;

		iminlight = irad - iminlight;
		
		for (i=0 ; i<3 ; i++)
			impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist;
		
		local[0] = DotProduct(impact, tex->vecs[0]) + tex->vecs[0][3] - surf->texturemins[0];
		local[1] = DotProduct(impact, tex->vecs[1]) + tex->vecs[1][3] - surf->texturemins[1];
		
		// check if this dlight will touch the surface
		if (local[1] > 0)
		{
			tdmin = local[1] - (tmax << 4);
			if (tdmin < 0)
				tdmin = 0;
		}
		else
		{
			tdmin = -local[1];
		}

		if (local[0] > 0)
		{
			sdmin = local[0] - (smax << 4);
			if (sdmin < 0)
				sdmin = 0;
		}
		else
		{
			sdmin = -local[0];
		}

		distmin = (sdmin > tdmin) ? (sdmin << 8) + (tdmin << 7) : (tdmin << 8) + (sdmin << 7);

		if (distmin < iminlight)
		{
			// save dlight info
			light = &dlightlist[numdlights];
			light->minlight = iminlight;
			light->rad = irad;
			light->local[0] = local[0];
			light->local[1] = local[1];
			light->type = cl_dlights[lnum].type;
			numdlights++;
		}
	}
}

//note these are in RGB format, the render sends them to the gpu as BGRA
int dlightcolor[NUM_DLIGHTTYPES][3] = 
{
	{100,	90,		80	},		// dimlight or brightlight
	{100,	50,		10	},		// muzzleflash
	{100,	50,		10	},		// explosion
	{90,	60,		7	},		// rocket
	{128,	0,		0	},		// red
	{0,		0,		128	},		// blue
	{128,	0,		128	},		// red + blue
	{0,		128,	0	},		// Green //R00k
	{128,	128,	128	},		// WHITE //R00k
};

/*
===============
R_AddDynamicLights

NOTE: R_BuildDlightList must be called first!
===============
*/
void R_AddDynamicLights (msurface_t *surf)
{
	int		i, j, smax, tmax, s, t, sd, td, _sd, _td;
	int		irad, idist, iminlight, color[3], tmp;
	dlightinfo_t	*light;
	unsigned	*dest;

	smax = (surf->extents[0] >> 4) + 1;
	tmax = (surf->extents[1] >> 4) + 1;

	for (i = 0, light = dlightlist ; i < numdlights ; i++, light++)
	{
		for (j=0 ; j<3 ; j++)
		{
			if (light->type == lt_explosion2 || light->type == lt_explosion3)
				color[j] = (int)(ExploColor[j] * 255);
			else
				color[j] = dlightcolor[light->type][j];
		}

		irad = light->rad;
		iminlight = light->minlight;

		_td = light->local[1];
		dest = blocklights;
		for (t=0 ; t<tmax ; t++)
		{
			td = _td;
			if (td < 0)
				td = -td;
			_td -= 16;
			_sd = light->local[0];

			for (s=0 ; s<smax ; s++)
			{
				sd = _sd < 0 ? -_sd : _sd;
				_sd -= 16;
				idist = (sd > td) ? (sd << 8) + (td << 7) : (td << 8) + (sd << 7);
				if (idist < iminlight)
				{
					tmp = (irad - idist) >> 7;
					dest[0] += tmp * (unsigned)color[0];
					dest[1] += tmp * (unsigned)color[1];
					dest[2] += tmp * (unsigned)color[2];
				}
				dest += 3;
			}
		}
	}
}

/*
===============
R_BuildLightMap

Combine and scale multiple lightmaps into the 8.8 format in blocklights
===============
*/
void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
{
	int			smax, tmax, t, i, j, size, maps, blocksize;
	byte		*lightmap;
	unsigned	scale, *bl;
	
	surf->cached_dlight = !!numdlights;

	smax = (surf->extents[0] >> 4) + 1;
	tmax = (surf->extents[1] >> 4) + 1;
	size = smax * tmax;
	blocksize = size * 3;
	lightmap = surf->samples;

	// set to full bright if no light data
	if ((developer.value && r_fullbright.value) || (!cl.worldmodel->lightdata))
	{
		for (i=0 ; i<blocksize ; i++)
		{
			blocklights[i] = 255 * 256;
		}
		goto store;
	}

	// clear to no light
	memset (blocklights, 0, blocksize * sizeof(int));

	// add all the lightmaps
	if (lightmap)
	{
		for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++)
		{
			scale = d_lightstylevalue[surf->styles[maps]];
			surf->cached_light[maps] = scale;	// 8.8 fraction
			bl = blocklights;
			for (i=0 ; i<blocksize ; i++)
			{
				*bl++ += lightmap[i] * scale;
			}
			lightmap += blocksize;	// skip to next lightmap
		}
	}
	
	if (numdlights)
	{
		R_AddDynamicLights (surf);
	}

	// bound, invert, and shift
store:
	bl = blocklights;
	stride -= smax * 4;
	
	for (i = 0 ; i < tmax ; i++, dest += stride)
	{
#ifndef USEFAKEGL
		if (gl_overbright.value)
		{
			if (gl_overbright.value >= 2)
			{
				for (j = smax ; j ; j--)
				{
					t = bl[0];
					t = t >> 7;
					t = min(t, 255);
					dest[2] = 255 - t;//B
		
					t = bl[1];
					t = t >> 7;
					t = min(t, 255);
					dest[1] = 255 - t;//G
		
					t = bl[2];
					t = t >> 7;
					t = min(t, 255);
					dest[0] = 255 - t;//R
					dest[3] = 255;		//alpha

					bl += 3;
					dest += 4;
				}
			}
			else
			{
				for (j = smax ; j ; j--)
				{
					t = bl[0];
					t = (t >> 8) + (t >> 9);
					t = min(t, 255);
					dest[2] = 255 - t;	//B
		
					t = bl[1];
					t = (t >> 8) + (t >> 9);
					t = min(t, 255);
					dest[1] = 255 - t;	//G
		
					t = bl[2];
					t = (t >> 8) + (t >> 9);
					t = min(t, 255);
					dest[0] = 255 - t;	//R

					dest[3] = 255;		//A

					bl += 3;
					dest += 4;
				}
			}
		}
		else
		{
			for (j = smax ; j ; j--)
			{
				t = bl[0] >> 8;				
				t = min(t, 255);
				dest[2] = 255 - t;	//B
	
				t = bl[1] >> 8;				
				t = min(t, 255);
				dest[1] = 255 - t;	//G
	
				t = bl[2] >> 8;		
				t = min(t, 255);
				dest[0] = 255 - t;	//R

				dest[3] = 255;		//A

				bl += 3;
				dest += 4;
			}
		}
#else
		if (gl_overbright.value)
		{
			if (gl_overbright.value >= 2)
			{
				for (j = smax ; j ; j--)
				{
					t = bl[0];
					t = t >> 7;
					t = min(t, 255);
					dest[0] = 255 - t;//R	
		
					t = bl[1];
					t = t >> 7;
					t = min(t, 255);
					dest[1] = 255 - t;//G
		
					t = bl[2];
					t = t >> 7;
					t = min(t, 255);
					dest[2] = 255 - t;//B	
					dest[3] = 255;		//alpha

					bl += 3;
					dest += 4;
				}
			}
			else
			{
				for (j = smax ; j ; j--)
				{
					t = bl[0];
					t = (t >> 8) + (t >> 9);
					t = min(t, 255);
					dest[0] = 255 - t;	//R
							
					t = bl[1];
					t = (t >> 8) + (t >> 9);
					t = min(t, 255);
					dest[1] = 255 - t;	//G
		
					t = bl[2];
					t = (t >> 8) + (t >> 9);
					t = min(t, 255);
					dest[2] = 255 - t;	//B

					dest[3] = 255;		//A

					bl += 3;
					dest += 4;
				}
			}
		}
		else
		{
			for (j = smax ; j ; j--)
			{
				t = bl[0] >> 8;				
				t = min(t, 255);
				dest[0] = 255 - t;	//R

				t = bl[1] >> 8;				
				t = min(t, 255);
				dest[1] = 255 - t;	//G

				t = bl[2] >> 8;		
				t = min(t, 255);
				dest[2] = 255 - t;	//B

				dest[3] = 255;		//A

				bl += 3;
				dest += 4;
			}
		}
#endif
	}
}

void R_UploadLightMap (int lightmapnum)
{
	glRect_t	*theRect;
	extern int	gl_filter_max;

	if (!lightmap_modified[lightmapnum])
		return;

	lightmap_modified[lightmapnum] = false;
	theRect = &lightmap_rectchange[lightmapnum];

#ifndef USEFAKEGL	
	glTexSubImage2D (GL_TEXTURE_2D, 0, 0, theRect->t, LIGHTMAP_BLOCK_WIDTH, theRect->h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, lightmaps + (lightmapnum * LIGHTMAP_BLOCK_HEIGHT + theRect->t) * LIGHTMAP_BLOCK_WIDTH * 4);//http://forums.inside3d.com/viewtopic.php?f=3&t=3115#p34428
#else
	glTexSubImage2D (GL_TEXTURE_2D, 0, 0, theRect->t, LIGHTMAP_BLOCK_WIDTH, theRect->h, GL_RGBA, GL_UNSIGNED_BYTE, lightmaps + (lightmapnum * LIGHTMAP_BLOCK_HEIGHT + theRect->t) * LIGHTMAP_BLOCK_WIDTH * 4);
#endif

	theRect->l = LIGHTMAP_BLOCK_WIDTH;
	theRect->t = LIGHTMAP_BLOCK_HEIGHT;
	theRect->h = 0;
	theRect->w = 0;
}

/*
===============
R_TextureAnimation

Returns the proper texture for a given time and base texture
===============
*/
texture_t *R_TextureAnimation (texture_t *base)
{
	int	relative, count;

	if (currententity->frame)
	{
		if (base->alternate_anims)
			base = base->alternate_anims;
	}
	
	if (!base->anim_total)
		return base;

	relative = (int)(cl.time*10) % base->anim_total;

	count = 0;	
	while (base->anim_min > relative || base->anim_max <= relative)
	{
		base = base->anim_next;
		if (!base)
			Sys_Error ("R_TextureAnimation: broken cycle");
		if (++count > 100)
			Sys_Error ("R_TextureAnimation: infinite cycle");
	}

	return base;
}

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

				BRUSH MODELS

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

void EmitOutlinePolys(void)
{
	glpoly_t	*p;
	int			i;
	float		*v;

	if (!outline_polys) 
		return;

	if ((r_outline_surf.value > 1)||(r_viewleaf && r_viewleaf->contents == CONTENTS_SOLID))
	{
		glColor4f(0.3,0.5,0.1,0.1);//GREEN OUTLINE if we are in noclip
	}
	else
		glColor4f(0,0,0,0.2);

	glEnable (GL_LINE_SMOOTH);
	glDisable (GL_TEXTURE_2D);
	glCullFace (GL_BACK);

	glPolygonMode (GL_FRONT, GL_LINE);

	glLineWidth (1.0f);
	
	for (p = outline_polys ; p ; p = p->outline_chain)
	{
		glBegin (GL_LINE_LOOP);		

		v = p->verts[0];
		for (i=0 ; i<p->numverts ; i++, v+=VERTEXSIZE)
		{
			glVertex3fv (v);
		}
		glEnd();
	}

	glCullFace (GL_FRONT);
	glColor4f(1,1,1,1);
	glDisable (GL_LINE_SMOOTH);
	glPolygonMode (GL_FRONT, GL_FILL);
	outline_polys = NULL;
	glEnable (GL_TEXTURE_2D);	
}

/*
================
R_BlendLightmaps
================
*/
void R_BlendLightmaps (void)
{
	int			i, j;
	glpoly_t	*p;
	float		*v;

	glDepthMask (GL_FALSE);		// don't bother writing Z

	glEnable (GL_BLEND);//<---- hmm this was missing?!

	glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR);

	glEnable(GL_ALPHA_TEST);
	glAlphaFunc ( GL_GREATER, 0.5 );

	for (i=0 ; i<MAX_LIGHTMAPS ; i++)
	{
		if (!lightmap_polys[i])
			continue;

		GL_Bind (lightmap_textures + i);
		
		if (lightmap_modified[i])
			R_UploadLightMap (i);
		
		for (p = lightmap_polys[i] ; p ; p = p->chain)
		{
			glBegin (GL_POLYGON);
			
			v = p->verts[0];
			
			for (j = 0 ; j < p->numverts ; j++, v += VERTEXSIZE)
			{
				glTexCoord2f (v[5], v[6]);
				glVertex3fv (v);
			}
			 
			glEnd ();
		}
		lightmap_polys[i] = NULL;
	}

	glDisable (GL_BLEND);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glDisable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER,0.666f);

	glDepthMask (GL_TRUE);		// back to normal Z buffering
}

/*
================
R_RenderDynamicLightmaps
================
*/
void R_RenderDynamicLightmaps (msurface_t *fa)
{
	byte		*base;
	int			maps, smax, tmax;
	glRect_t	*theRect;
	qboolean	lightstyle_modified = false;

	c_brush_polys++;

	if (!r_dynamic.value)
		return;

	if (fa->overbright != gl_overbright.value)//MH
	{
		fa->overbright = gl_overbright.value;
		goto update;
	}

	// check for lightmap modification
	for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ; maps++)
	{
		if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps])
		{
			lightstyle_modified = true;
			break;
		}
	}

	if (fa->dlightframe == r_framecount)
		R_BuildDlightList (fa);
	else
		numdlights = 0;
	
	if (numdlights == 0 && !fa->cached_dlight && !lightstyle_modified)
		return;

update:
	lightmap_modified[fa->lightmaptexturenum] = true;
	theRect = &lightmap_rectchange[fa->lightmaptexturenum];
	if (fa->light_t < theRect->t)
	{
		if (theRect->h)
			theRect->h += theRect->t - fa->light_t;
		theRect->t = fa->light_t;
	}
	if (fa->light_s < theRect->l)
	{
		if (theRect->w)
			theRect->w += theRect->l - fa->light_s;
		theRect->l = fa->light_s;
	}
	smax = (fa->extents[0] >> 4) + 1;
	tmax = (fa->extents[1] >> 4) + 1;
	if (theRect->w + theRect->l < fa->light_s + smax)
		theRect->w = fa->light_s - theRect->l + smax;
	if (theRect->h + theRect->t < fa->light_t + tmax)
		theRect->h = fa->light_t - theRect->t + tmax;
	base = lightmaps + fa->lightmaptexturenum * LIGHTMAP_BLOCK_WIDTH * LIGHTMAP_BLOCK_HEIGHT * 4;
	base += (fa->light_t * LIGHTMAP_BLOCK_WIDTH + fa->light_s) * 4;
	R_BuildLightMap (fa, base, LIGHTMAP_BLOCK_WIDTH * 4);
}

extern float turbsin[];

void R_DrawWaterSurfaces (void)
{
	msurface_t *s;
	float *vert;
	vec3_t alphadist;
	float dist;
	float alpha = 1;
	char *name;

	if (!waterchain)
		return;

	GL_DisableMultitexture ();

	for (s = waterchain; s; s = s->texturechain)
	{


		name = s->texinfo->texture->name;

		if (ISLAVATEX(name)) 
		{
			if (r_lavaalpha.value < 1.0)
				alpha = r_lavaalpha.value;
		}
		else
		{
			if (ISTELETEX(name))
			{
				 if (r_telealpha.value < 1.0)
					alpha = r_telealpha.value;
			}
			else
			{
				if (r_wateralpha.value < 1.0) 
					alpha = r_wateralpha.value;//default
			}
		}

		if (alpha < 1.0)
		{		
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
			glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
			glEnable (GL_BLEND);


			vert = s->polys->verts[0];
			vert += VERTEXSIZE;

			//R00k: this dynamically changes the alpha based on the distance from the water.
			if (r_turbalpha_distance.value)
			{
				VectorSubtract(r_refdef.vieworg,vert,alphadist);
			
				dist = (alphadist[2]);//R00k v1.82 only compare Z plane not radial
				
				alpha = (alpha + (alpha * (dist / r_turbalpha_distance.value)));//this looks better...(v1.84)
			}

			glColor4f (1, 1, 1, alpha);
		}
		else
			glColor4f (1, 1, 1, 1);	

		EmitWaterPolys (s);
	}

	waterchain = NULL;
	waterchain_tail = &waterchain;

	if (alpha < 1.0)
	{
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glColor4f (1,1,1,1);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDisable (GL_BLEND);

	}
}

void R_DrawAlphaChain (void)
{
	int		k;
	msurface_t	*s;
	texture_t	*t;
	float		*v;

	if (!alphachain)
		return;

	glEnable (GL_ALPHA_TEST);

	for (s = alphachain ; s ; s = s->texturechain)
	{
		t = s->texinfo->texture;
		R_RenderDynamicLightmaps (s);

		// bind the world texture
		GL_DisableMultitexture ();
		GL_Bind (t->gl_texturenum);

		if (gl_mtexable)
		{
			// bind the lightmap texture
			GL_EnableMultitexture ();
			GL_Bind (lightmap_textures + s->lightmaptexturenum);
			glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
			// update lightmap if its modified by dynamic lights
			k = s->lightmaptexturenum;
			if (lightmap_modified[k])
				R_UploadLightMap (k);
		}
		
		glBegin (GL_POLYGON);
		v = s->polys->verts[0];
		for (k = 0 ; k < s->polys->numverts ; k++, v += VERTEXSIZE)
		{
			if (gl_mtexable)
			{
				qglMultiTexCoord2f (GL_TEXTURE0, v[3], v[4]);
				qglMultiTexCoord2f (GL_TEXTURE1, v[5], v[6]);
			}
			else
			{
				glTexCoord2f (v[3], v[4]);
			}
			glVertex3fv (v);
		}		 
		glEnd ();
	}	 

	alphachain = NULL;
	alphachain_tail = &alphachain;

	glDisable (GL_ALPHA_TEST);
	GL_DisableMultitexture ();
}

static void R_ClearTextureChains (model_t *clmodel)
{
	int		i, waterline;
	texture_t	*texture;

	memset (lightmap_polys, 0, sizeof(lightmap_polys));
	memset (fullbright_polys, 0, sizeof(fullbright_polys));
	memset (luma_polys, 0, sizeof(luma_polys));

	for (i=0 ; i<clmodel->numtextures ; i++)
	{
		for (waterline=0 ; waterline<2 ; waterline++)
		{
			if ((texture = clmodel->textures[i]))
			{				
				texture->texturechain[waterline] = NULL;
				texture->texturechain_tail[waterline] = &texture->texturechain[waterline];
			}
		}
	}

	r_notexture_mip->texturechain[0] = NULL;	
	r_notexture_mip->texturechain_tail[0] = &r_notexture_mip->texturechain[0];
	r_notexture_mip->texturechain[1] = NULL;
	r_notexture_mip->texturechain_tail[1] = &r_notexture_mip->texturechain[1];

	skychain = NULL;	
	skychain_tail = &skychain;

	if (clmodel == cl.worldmodel)
	{
		waterchain = NULL;
		waterchain_tail = &waterchain;
	}

	alphachain = NULL;
	alphachain_tail = &alphachain;
}


/*
================
DrawTextureChains
================
*/
void DrawTextureChains (model_t *model, int contents)
{
	int		i, k, waterline, GL_LIGHTMAP_TEXTURE, GL_FB_TEXTURE;
	msurface_t	*s;
	texture_t	*t;
	float		*v;
	qboolean	mtex_lightmaps, mtex_fbs, doMtex1 = false, doMtex2 = false, render_lightmaps = false;
	static int r;
	qboolean	draw_fbs, draw_mtex_fbs, can_mtex_lightmaps, can_mtex_fbs;

	if (gl_fb_bmodels.value)
	{
		can_mtex_lightmaps = gl_mtexable;
		can_mtex_fbs = gl_textureunits >= 3;
	}
	else
	{
		can_mtex_lightmaps = gl_textureunits >= 3;
		can_mtex_fbs = gl_textureunits >= 3 && gl_add_ext;
	}
	GL_DisableMultitexture ();

	if (r_viewleaf && r_viewleaf->contents == CONTENTS_SOLID)// if spectating from off the map then make the walls transparent
	{
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		if (r_novis.value)
			glDepthMask (GL_FALSE);
		glEnable (GL_BLEND);
		glColor4f (1, 1, 1, 0.5);
	}

	for (i=0 ; i<model->numtextures ; i++)
	{
		if (!model->textures[i] || (!model->textures[i]->texturechain[0] && !model->textures[i]->texturechain[1]))
			continue;	

		t = R_TextureAnimation (model->textures[i]);

		// bind the world texture
		GL_SelectTexture (GL_TEXTURE0);
		GL_Bind (t->gl_texturenum);

		draw_fbs = t->isLumaTexture || gl_fb_bmodels.value;
		draw_mtex_fbs = draw_fbs && can_mtex_fbs;
		if (gl_mtexable)
		{
			if (t->isLumaTexture && !gl_fb_bmodels.value)
			{
				if (gl_add_ext)
				{
					doMtex1 = true;
					GL_FB_TEXTURE = GL_TEXTURE1;
					GL_EnableTMU (GL_FB_TEXTURE);
					glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
					GL_Bind (t->fb_texturenum);

					mtex_lightmaps = can_mtex_lightmaps;
					mtex_fbs = true;

					if (mtex_lightmaps)
					{
						doMtex2 = true;
						GL_LIGHTMAP_TEXTURE = GL_TEXTURE2;
						GL_EnableTMU (GL_LIGHTMAP_TEXTURE);
						glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
					}
					else
					{
						doMtex2 = false;
						render_lightmaps = true;
					}
				}
				else
				{
					GL_DisableTMU (GL_TEXTURE1);
					render_lightmaps = true;
					doMtex1 = doMtex2 = mtex_lightmaps = mtex_fbs = false;
				}
			}
			else
			{
				doMtex1 = true;
				GL_LIGHTMAP_TEXTURE = GL_TEXTURE1;
				GL_EnableTMU (GL_LIGHTMAP_TEXTURE);
				glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);

				mtex_lightmaps = true;
				mtex_fbs = t->fb_texturenum && draw_mtex_fbs;

				if (mtex_fbs)
				{
					doMtex2 = true;
					GL_FB_TEXTURE = GL_TEXTURE2;
					GL_EnableTMU (GL_FB_TEXTURE);
					GL_Bind (t->fb_texturenum);
					glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, t->isLumaTexture ? GL_ADD : GL_DECAL);
				}
				else
				{
					doMtex2 = false;
				}
			}
		}
		else
		{
			render_lightmaps = true;
			doMtex1 = doMtex2 = mtex_lightmaps = mtex_fbs = false;
		}

		for (waterline = 0 ; waterline < 2 ; waterline++)
		{
			if (!(s = model->textures[i]->texturechain[waterline]))
				continue;

			for ( ; s ; s = s->texturechain)
			{
				if (mtex_lightmaps)
				{
					// bind the lightmap texture
					GL_SelectTexture (GL_LIGHTMAP_TEXTURE);
					GL_Bind (lightmap_textures + s->lightmaptexturenum);

					// update lightmap if its modified by dynamic lights
					R_RenderDynamicLightmaps (s);
					k = s->lightmaptexturenum;
					if (lightmap_modified[k])
						R_UploadLightMap (k);
				}
				else
				{
					s->polys->chain = lightmap_polys[s->lightmaptexturenum];
					lightmap_polys[s->lightmaptexturenum] = s->polys;
					
					R_RenderDynamicLightmaps (s);
				}
				glBegin (GL_POLYGON);

				v = s->polys->verts[0];

				for (k = 0 ; k < s->polys->numverts ; k++, v += VERTEXSIZE)
				{						
					if (doMtex1)
					{
						if (gl_textureless.value) //R00k - multitexture textureless world
						{								
							qglMultiTexCoord2f (GL_TEXTURE0_ARB, 0, 0);
														
							if (mtex_lightmaps)
								qglMultiTexCoord2f (GL_LIGHTMAP_TEXTURE, v[5], v[6]);

//							if (mtex_fbs)
//								qglMultiTexCoord2f (GL_FB_TEXTURE, v[3], v[4]);
						}
						else
						{
							qglMultiTexCoord2f (GL_TEXTURE0_ARB, v[3], v[4]);

							if (mtex_lightmaps)
								qglMultiTexCoord2f (GL_LIGHTMAP_TEXTURE, v[5], v[6]);

							if (mtex_fbs)
								qglMultiTexCoord2f (GL_FB_TEXTURE, v[3], v[4]);
						}
					}
					else
					{//This is probably useless now since Qrack has become a bloated whore!
						if (gl_textureless.value)
						{								
							glTexCoord2f (0, 0);
						}
						else
						{
							glTexCoord2f (v[3], v[4]);
						}
					}
					glVertex3fv (v);
				}

				glEnd ();				

				if ((waterline && gl_caustics.value && underwatertexture))
				{						
					s->polys->caustics_chain = caustics_polys;
					caustics_polys = s->polys;									
				}

				if (((model->type == mod_brush) && gl_caustics.value && underwatertexture && ISUNDERWATER(contents)))
				{
					s->polys->caustics_chain = caustics_polys;
					caustics_polys = s->polys;					
				}

				if (!waterline && gl_detail.value && detailtexture)
				{
					s->polys->detail_chain = detail_polys;
					detail_polys = s->polys;					
				} 

				if (!waterline && s->sflags & SURF_METAL && gl_shiny.value && chrometexture)
				{
					s->polys->chrome_chain = chrome_polys;
					chrome_polys = s->polys;
				}

				if (!waterline && s->sflags & SURF_MIRROR && gl_shiny.value && glasstexture)
				{					
					s->polys->glass_chain = glass_polys;
					glass_polys = s->polys;
				}

				if (t->fb_texturenum && draw_fbs && !mtex_fbs)
				{
					if (t->isLumaTexture)
					{
						s->polys->luma_chain = luma_polys[t->fb_texturenum];
						luma_polys[t->fb_texturenum] = s->polys;
						drawlumas = true;
					}
					else
					{
						s->polys->fb_chain = fullbright_polys[t->fb_texturenum];
						fullbright_polys[t->fb_texturenum] = s->polys;
						drawfullbrights = true;
					}
				}
			}
		}

		if (doMtex1)
			GL_DisableTMU (GL_TEXTURE1);
		if (doMtex2)
			GL_DisableTMU (GL_TEXTURE2);
	}

	if (gl_mtexable)
		GL_SelectTexture (GL_TEXTURE0);

	if (gl_fb_bmodels.value)
	{
		if (render_lightmaps)
			R_BlendLightmaps ();
		if (drawfullbrights)
			R_RenderFullbrights ();
		if (drawlumas)
			R_RenderLumas ();
	}
	else
	{
		if (drawlumas)
			R_RenderLumas ();
		if (render_lightmaps)
			R_BlendLightmaps ();
		if (drawfullbrights)
			R_RenderFullbrights ();
	}

	if (r_outline_surf.value)
		EmitOutlinePolys();//R00k

	if (gl_caustics.value)//r00k
		EmitCausticsPolys ();

	if (gl_detail.value)//r00k
	{
		EmitDetailPolys ();
	}

	if (gl_shiny.value)
	{
		EmitGlassPolys();
		EmitChromePolys();
	}
/*
	if (gl_gloss.value)
	{
		EmitGlossPolys();
	}
*/
	if ((r_viewleaf && r_viewleaf->contents == CONTENTS_SOLID))// if spectating from off the map then make the walls transparent
	{
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDisable (GL_BLEND);
		glDepthMask (GL_TRUE);
	}
	
	glColor4f(1,1,1,1);
}
#ifndef USEFAKEGL
extern void R_DrawSkyChain2 (void);
#endif
/*
=================
R_DrawBrushModel
=================
*/
void R_DrawBrushModel (entity_t *ent)
{
	int		i, underwater;
	float		dot;

	msurface_t	*psurf;
	mplane_t	*pplane;
	model_t		*clmodel = ent->model;
	qboolean	rotated = false;

	currenttexture = -1;

	if (ent->cullFrame != r_framecount)
	{ //rww
		vec3_t		mins, maxs;
		if (ent->angles[0] || ent->angles[1] || ent->angles[2])
		{
			rotated = true;
			for (i=0 ; i<3 ; i++)
			{
				mins[i] = ent->origin[i] - clmodel->radius;
				maxs[i] = ent->origin[i] + clmodel->radius;
			}
		}
		else
		{
			rotated = false;
			VectorAdd (ent->origin, clmodel->mins, mins);
			VectorAdd (ent->origin, clmodel->maxs, maxs);
		}

		ent->culled = R_CullBox(mins, maxs);
		ent->cullFrame = r_framecount;
	}

	if (ent->culled)
	{ //rww - since these things go through passes for so many things, only do a cull check once per frame
		return;
	}

	if (ISTRANSPARENT(ent))
	{
		glEnable (GL_BLEND);

		if (r_novis.value)
			glDepthMask (GL_FALSE);

		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		glColor4f (1, 1, 1, ent->transparency);		
	}	

	VectorSubtract (r_refdef.vieworg, ent->origin, modelorg);

	if (rotated)
	{
		vec3_t	temp, forward, right, up;

		VectorCopy (modelorg, temp);
		AngleVectors (ent->angles, forward, right, up);
		modelorg[0] = DotProduct (temp, forward);
		modelorg[1] = -DotProduct (temp, right);
		modelorg[2] = DotProduct (temp, up);
	}

	psurf = &clmodel->surfaces[clmodel->firstmodelsurface];

	// calculate dynamic lighting for bmodel if it's not an instanced model
	if (clmodel->firstmodelsurface != 0) 
		R_PushDlights (clmodel->nodes + clmodel->hulls[0].firstclipnode);	//MH

	glPushMatrix ();

	// MH: hack the origin to prevent bmodel z-fighting
//	if (gl_zfix.value)
//	{
	ent->origin[0] -= DIST_EPSILON;
	ent->origin[1] -= DIST_EPSILON;
	ent->origin[2] -= DIST_EPSILON;

	glTranslatef (ent->origin[0], ent->origin[1], ent->origin[2]);
	glRotatef (ent->angles[1], 0, 0, 1);
	glRotatef (ent->angles[0], 0, 1, 0);
	glRotatef (ent->angles[2], 1, 0, 0);

	// MH: hack the origin to prevent bmodel z-fighting
//	if (gl_zfix.value)
//	{
	ent->origin[0] += DIST_EPSILON;
	ent->origin[1] += DIST_EPSILON;
	ent->origin[2] += DIST_EPSILON;

	R_ClearTextureChains (clmodel);

	// draw texture
	for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++)
	{
		// find which side of the node we are on
		pplane = psurf->plane;
		dot = PlaneDiff(modelorg, pplane);

		// draw the polygon
		if ((r_outline.value)||((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
		{
			if (psurf->flags & SURF_DRAWSKY)
			{
				CHAIN_SURF_B2F(psurf, skychain);
			}
			else 
			{
				if (psurf->flags & SURF_DRAWTURB)
				{
					EmitWaterPolys (psurf);
				}
				else 
				{
					if (psurf->flags & SURF_DRAWALPHA) 
					{						
						CHAIN_SURF_B2F(psurf, alphachain);
					} 
					else
					{
						underwater = (psurf->flags & SURF_UNDERWATER) ? 1 : 0;
						CHAIN_SURF_B2F(psurf, psurf->texinfo->texture->texturechain[underwater]);
					}
				}
			}

			if (r_outline_surf.value)
			{
				psurf->polys->outline_chain = outline_polys;
				outline_polys = psurf->polys;					
			}				
		}
	}

	if (gl_caustics.value)
		DrawTextureChains (clmodel,(TruePointContents(ent->origin)));//R00k added contents point for underwater bmodels
	else
		DrawTextureChains (clmodel,0);//R00k: (4/27/2015): Don't spam SV_HullPointContents unless we have caustics turned on!! ARrrgh!

	if (r_skyboxloaded)
	{
		R_DrawSkyBox ();
	}
	else
	{
#ifndef USEFAKEGL
		if ((r_skyscroll.value)&&(!(r_fastsky.value)))
		{
			R_DrawSkyChain2 ();//MHQuake's uber nice sky scrolling
		}
		else
#endif
		{
			R_DrawSkyChain ();
		}
	}

	R_DrawAlphaChain ();

	glPopMatrix ();

	if (ISTRANSPARENT(ent))
	{
		if (r_novis.value)
			glDepthMask (GL_TRUE);
		glColor4f(1,1,1,1);
		glDisable (GL_BLEND);
	}
}


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

				WORLD MODEL

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

/*
================
R_RecursiveWorldNode
================
*/
void R_RecursiveWorldNode (mnode_t *node, int clipflags)
{
	int			c, side, sidebit, underwater;
	mplane_t	*plane, *clipplane;
	msurface_t	*surf, **mark;
	mleaf_t		*pleaf;
	double		dot;

	if (node->contents == CONTENTS_SOLID) return;	// solid

	if (node->visframe != r_visframecount) return;

	for (c = 0, clipplane = frustum ; c < 4 ; c++, clipplane++) 
	{
		if (!(clipflags & (1 << c))) continue;															// don't need to clip against it
		
		if (BOX_ON_PLANE_SIDE(node->minmaxs, node->minmaxs+3, clipplane) == 2) return;					// fully clipped

		if (BOX_ON_PLANE_SIDE(node->minmaxs, node->minmaxs+3, clipplane) == 1)
		{
			clipflags &= ~(1 << c);																		// node is entirely on screen
		}
	}
		
	// if a leaf node, draw stuff
	if (node->contents < 0) 
	{
		pleaf = (mleaf_t *)node;
		mark = pleaf->firstmarksurface;
		c = pleaf->nummarksurfaces;

		if (c) 
		{
			do 
			{
				(*mark)->visframe = r_framecount;
				mark++;
			} while (--c);
		}

		// deal with model fragments in this leaf
		if (pleaf->efrags) 
		{
			R_StoreEfrags (&pleaf->efrags);
		}

		return;
	}

	// node is just a decision point, so go down the apropriate sides
	// find which side of the node we are on
	plane = node->plane;
	dot = PlaneDiff(modelorg, plane);
	
	if (dot >= 0)
	{
		side = 0;
		sidebit = 0;
	}
	else
	{
		side = 1;
		sidebit = SURF_PLANEBACK;
	}

	// recurse down the children, front side first
	R_RecursiveWorldNode (node->children[side], clipflags);


	// draw stuff
	for ( c = node->numsurfaces, surf = cl.worldmodel->surfaces + node->firstsurface; c ; c--, surf++)
	{
		if (surf->visframe != r_framecount)	continue;

		if ((surf->flags & SURF_PLANEBACK) != sidebit )
			continue;		// wrong side

		if (surf->flags & SURF_UNDERWATER)
			r_renderflags |= RF_UNDERWATER;
		else
			r_renderflags |= RF_ABOVEWATER;

		// if sorting by texture, just store it out
		if (surf->flags & SURF_DRAWSKY)
		{
			CHAIN_SURF_F2B(surf, skychain_tail);
		}
		else if (surf->flags & SURF_DRAWTURB)
		{
			CHAIN_SURF_F2B(surf, waterchain_tail);			
		}
		else
		{
			underwater = (surf->flags & SURF_UNDERWATER) ? 1 : 0;
	
			CHAIN_SURF_F2B(surf, surf->texinfo->texture->texturechain_tail[underwater]);

			if (r_outline_surf.value)
			{
				surf->polys->outline_chain = outline_polys;
				outline_polys = surf->polys;															
			}
		}
	}

	// recurse down the back side
	R_RecursiveWorldNode (node->children[!side], clipflags);
}
/*
=============
R_DrawWorld
=============
*/
void R_DrawWorld (void)
{
	entity_t	ent;

	memset (&ent, 0, sizeof(ent));
	ent.model = cl.worldmodel;

	R_ClearTextureChains (cl.worldmodel);

	VectorCopy (r_refdef.vieworg, modelorg);
	
	currententity = &ent;
	currenttexture = -1;

	r_renderflags = 0;

	R_PushDlights (cl.worldmodel->nodes);//MH

	// set up texture chains for the world
	R_RecursiveWorldNode (cl.worldmodel->nodes, 15);

	if (r_skyboxloaded)
	{
		R_DrawSkyBox ();
	}
	else
	{
#ifndef USEFAKEGL
		if (r_skyscroll.value)//R00k
		{
			R_DrawSkyChain2 ();
		}
		else
#endif
		{
			R_DrawSkyChain ();
		}
	}

	DrawTextureChains (cl.worldmodel, 0);// draw the world

	// draw the world alpha textures
	R_DrawAlphaChain ();	
}

/*
===============
R_MarkLeaves
===============
*/
void R_MarkLeaves (void)
{
	int	i;
	byte	*vis, solid[MAX_MAP_LEAFS/8];
	mnode_t	*node;

	if (!r_novis.value && r_oldviewleaf == r_viewleaf && r_oldviewleaf2 == r_viewleaf2)	// watervis hack
		return;

	if (!(cl.worldmodel))
		return;

	r_visframecount++;
	r_oldviewleaf = r_viewleaf;
	
	if (r_novis.value == 1 || r_viewleaf->compressed_vis == NULL)
	{
		vis = solid;
		memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3);
	}
	else
	{
		vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel);

		if (r_viewleaf2)
		{
			int		i, count;
			unsigned	*src, *dest;

			// merge visibility data for two leafs
			count = (cl.worldmodel->numleafs+7)>>3;
			memcpy (solid, vis, count);
			src = (unsigned *)Mod_LeafPVS (r_viewleaf2, cl.worldmodel);
			dest = (unsigned *)solid;
			count = (count + 3) >> 2;
			for (i=0 ; i<count ; i++)
				*dest++ |= *src++;
			vis = solid;
		}
	}

	for (i=0 ; i<cl.worldmodel->numleafs ; i++)
	{
		if (vis[i>>3] & (1 << (i & 7)))
		{
			node = (mnode_t *)&cl.worldmodel->leafs[i+1];
			do 
			{
				if (node->visframe == r_visframecount)
					break;
				node->visframe = r_visframecount;
				node = node->parent;
			} while (node);
		}
	}
}

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

				LIGHTMAP ALLOCATION

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

// returns a texture number and the position inside it
int AllocBlock (int w, int h, int *x, int *y)
{
	int	i, j, best, best2, texnum;

	// ericw -- rather than searching starting at lightmap 0 every time,
	// start at the last lightmap we allocated a surface in.
	// This makes AllocBlock much faster on large levels (can shave off 3+ seconds
	// of load time on a level with 180 lightmaps), at a cost of not quite packing
	// lightmaps as tightly vs. not doing this (uses ~5% more lightmaps)

	for (texnum=last_lightmap_allocated ; texnum<MAX_LIGHTMAPS ; texnum++, last_lightmap_allocated++)
//	for (texnum=0 ; texnum<MAX_LIGHTMAPS ; texnum++)
	{
		best = LIGHTMAP_BLOCK_HEIGHT;

		for (i=0 ; i<LIGHTMAP_BLOCK_WIDTH-w ; i++)
		{
			best2 = 0;

			for (j=0 ; j<w ; j++)
			{
				if (allocated[texnum][i+j] >= best)
					break;
				if (allocated[texnum][i+j] > best2)
					best2 = allocated[texnum][i+j];
			}
			if (j == w)
			{	// this is a valid spot
				*x = i;
				*y = best = best2;
			}
		}

		if (best + h > LIGHTMAP_BLOCK_HEIGHT)
			continue;

		for (i = 0 ; i < w ; i++)
			allocated[texnum][*x + i] = best + h;

		return texnum;
	}

	Con_Printf ("Current map exceeds MAX_LIGHTMAPS: %i\n", texnum);
	Host_Error ("AllocBlock: full");
	return 0;
}



mvertex_t	*r_pcurrentvertbase;
model_t		*currentmodel;

/*
================
BuildSurfaceDisplayList
================
*/
void BuildSurfaceDisplayList (msurface_t *fa)
{
	int		i, lindex, lnumverts, vertpage;
	medge_t		*pedges, *r_pedge;
	float		*vec, s, t;
	glpoly_t	*poly;


	// reconstruct the polygon
	pedges = currentmodel->edges;
	lnumverts = fa->numedges;
	vertpage = 0;

	// draw texture
	poly = Hunk_AllocName (sizeof(glpoly_t) + (lnumverts - 4) * VERTEXSIZE * sizeof(float),"glpoly_t");
	poly->next = fa->polys;
	fa->polys = poly;
	poly->numverts = lnumverts;

	for (i=0 ; i<lnumverts ; i++)
	{
		lindex = currentmodel->surfedges[fa->firstedge + i];

		if (lindex > 0)
		{
			r_pedge = &pedges[lindex];
			vec = r_pcurrentvertbase[r_pedge->v[0]].position;
		}
		else
		{
			r_pedge = &pedges[-lindex];
			vec = r_pcurrentvertbase[r_pedge->v[1]].position;
		}

		s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
		s /= fa->texinfo->texture->width;

		t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
		t /= fa->texinfo->texture->height;

		VectorCopy (vec, poly->verts[i]);


		poly->verts[i][3] = s;
		poly->verts[i][4] = t;

		// lightmap texture coordinates
		s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
		s -= fa->texturemins[0];
		s += fa->light_s * 16;
		s += 8;
		s /= LIGHTMAP_BLOCK_WIDTH * 16;	//fa->texinfo->texture->width;

		t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
		t -= fa->texturemins[1];
		t += fa->light_t * 16;
		t += 8;
		t /= LIGHTMAP_BLOCK_HEIGHT * 16;	//fa->texinfo->texture->height;

		poly->verts[i][5] = s;
		poly->verts[i][6] = t;

		s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
		s /= 128;

		t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
		t /= 128;

		VectorCopy (vec, poly->verts[i]);
		poly->verts[i][7] = s;
		poly->verts[i][8] = t;
	}

	poly->numverts = lnumverts;

}

/*
========================
GL_CreateSurfaceLightmap
========================
*/
void GL_CreateSurfaceLightmap (msurface_t *surf)
{
	int		smax, tmax;
	byte	*base;

	if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB))
		return;
	smax = (surf->extents[0] >> 4) + 1;
	tmax = (surf->extents[1] >> 4) + 1;

	if (smax > LIGHTMAP_BLOCK_WIDTH)
		Host_Error ("GL_CreateSurfaceLightmap: smax = %d > LIGHTMAP_BLOCK_WIDTH", smax);
	if (tmax > LIGHTMAP_BLOCK_HEIGHT)
		Host_Error ("GL_CreateSurfaceLightmap: tmax = %d > LIGHTMAP_BLOCK_HEIGHT", tmax);
	if (smax * tmax > 49152)
		Host_Error ("GL_CreateSurfaceLightmap: smax * tmax = %d > MAX_LIGHTMAP_SIZE", smax * tmax);


	surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t);
	
	base = lightmaps + surf->lightmaptexturenum * LIGHTMAP_BLOCK_WIDTH * LIGHTMAP_BLOCK_HEIGHT * 4;
	base += (surf->light_t * LIGHTMAP_BLOCK_WIDTH + surf->light_s) * 4;
	numdlights = 0;
	R_BuildLightMap (surf, base, LIGHTMAP_BLOCK_WIDTH * 4);
}

/*
==================
GL_BuildLightmaps

Builds the lightmap texture
with all the surfaces from all brush models
==================
*/
extern int	gl_filter_min, gl_filter_max;

void GL_BuildLightmaps (void)
{
	int	i, j;
	model_t	*m;
	extern cvar_t gl_mipmap_lightmaps;

	memset (allocated, 0, sizeof(allocated));
	last_lightmap_allocated = 0;//ericw(QuakeSpasm)

	r_framecount = 1;		// no dlightcache

	if (!lightmap_textures)
	{
		lightmap_textures = texture_extension_number;
		texture_extension_number += MAX_LIGHTMAPS;
	}

	for (j=1 ; j<MAX_MODELS ; j++)
	{
		if (!(m = cl.model_precache[j]))
			break;

		if (m->name[0] == '*')
			continue;

		r_pcurrentvertbase = m->vertexes;
		currentmodel = m;

		for (i=0 ; i<m->numsurfaces ; i++)
		{
			if (m->surfaces[i].flags & SURF_DRAWTILED)
				continue;
	
			GL_CreateSurfaceLightmap (m->surfaces + i);
			BuildSurfaceDisplayList (m->surfaces + i);
		}
	}

 	if (gl_mtexable)
 		GL_EnableMultitexture ();

	// upload all lightmaps that were filled
	for (i=0; i<MAX_LIGHTMAPS; i++)
	{
		if (!allocated[i][0])
			break;		// no more used

		lightmap_modified[i] = false;
		lightmap_rectchange[i].l = LIGHTMAP_BLOCK_WIDTH;
		lightmap_rectchange[i].t = LIGHTMAP_BLOCK_HEIGHT;
		lightmap_rectchange[i].w = 0;
		lightmap_rectchange[i].h = 0;
		
		GL_Bind (lightmap_textures + i);

		if (gl_mipmap_lightmaps.value)
		{
			glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
			glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
		}
		else		
		{
			glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		}
#ifndef USEFAKEGL
		glTexImage2D (GL_TEXTURE_2D, 0, gl_lightmap_format, LIGHTMAP_BLOCK_WIDTH, LIGHTMAP_BLOCK_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, lightmaps + i * LIGHTMAP_BLOCK_WIDTH * LIGHTMAP_BLOCK_HEIGHT * 4);
#else
		glTexImage2D (GL_TEXTURE_2D, 0, gl_lightmap_format, LIGHTMAP_BLOCK_WIDTH, LIGHTMAP_BLOCK_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, lightmaps + i * LIGHTMAP_BLOCK_WIDTH * LIGHTMAP_BLOCK_HEIGHT * 4);
#endif
	}

	if (gl_mtexable)
 		GL_DisableMultitexture ();
}

//R00k this just runs through the allocated lightmap textures and alters the texture parameters for texture mapping.
void GL_ReBuildLightmaps (void)
{
	int	i;	

	// upload all lightmaps that were changed
	for (i=0 ; i<MAX_LIGHTMAPS ; i++)
	{
		if (!allocated[i][0])
			break;		// not in use

		lightmap_modified[i] = false;
		lightmap_rectchange[i].l = LIGHTMAP_BLOCK_WIDTH;
		lightmap_rectchange[i].t = LIGHTMAP_BLOCK_HEIGHT;
		lightmap_rectchange[i].w = 0;
		lightmap_rectchange[i].h = 0;

		GL_Bind (lightmap_textures + i);
		
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
	}
}


