//FIXME: Light visibility is decided from weather the light's pvs overlaps the view pvs.
//This doesn't take light radius into account. This means that lights around corners that will never be visible are drawn in full per-pixel goodness.
//This is bad. lights*3, 33% framerate for no worthwhile effect.

#include "quakedef.h"
#ifdef RGLQUAKE
#include "glquake.h"
#include "shader.h"

#define qglGetError() 0

//these are shared with gl_rsurf - move to header
void R_MirrorChain (msurface_t *s);
void GL_SelectTexture (GLenum target);
void R_RenderDynamicLightmaps (msurface_t *fa, int shift);
void R_BlendLightmaps (void);


void PPL_BeginShadowMesh(dlight_t *dl);
void PPL_FinishShadowMesh(dlight_t *dl);
void PPL_FlushShadowMesh(dlight_t *dl);
void PPL_Shadow_Cache_Surface(msurface_t *surf);	//only caches for lighting
void PPL_Shadow_Cache_Leaf(mleaf_t *leaf);

extern qboolean r_inmirror;
extern int gldepthfunc;
extern int		*lightmap_textures;
extern int		lightmap_bytes;		// 1, 2, or 4

extern cvar_t		gl_detail;
extern cvar_t		gl_detailscale;
extern cvar_t		gl_overbright, gl_overbright_all;
extern cvar_t		r_fb_bmodels;
extern cvar_t		gl_part_flame;

extern cvar_t		gl_maxshadowlights;
extern cvar_t		r_shadow_realtime_world;
extern cvar_t		r_shadow_realtime_world_lightmaps;
extern cvar_t		r_shadow_glsl_offsetmapping;
extern cvar_t		r_shadow_glsl_offsetmapping_scale;
extern cvar_t		r_shadow_glsl_offsetmapping_bias;
extern int		detailtexture;
extern cvar_t gl_bump;
extern cvar_t gl_specular;
extern cvar_t gl_mylumassuck;
//end header confict

extern cvar_t gl_schematics;
extern cvar_t r_drawflat;
extern cvar_t r_wallcolour;
extern cvar_t r_floorcolour;

float r_lightmapintensity;	//1 or r_shadow_realtime_world_lightmaps 

int overbright;

extern lightmapinfo_t **lightmap;

extern model_t *currentmodel;
extern int		*deluxmap_textures;

extern int normalisationCubeMap;

int r_shadowframe;

int shadowsurfcount;
int shadowedgecount;
int shadowlightfaces;
int shadowemittedeges;

int ppl_specular_shader;
int ppl_specular_shader_vieworg;
int ppl_specular_shader_texr;
int ppl_specular_shader_texu;
int ppl_specular_shader_texf;

int GLR_LightmapShift (model_t *model);

//#define glBegin glEnd

qboolean PPL_ShouldDraw(void)
{
	if (r_inmirror)
	{
		if (currententity->flags & Q2RF_WEAPONMODEL)
			return false;
	}
	else
	{
		if (currententity->flags & Q2RF_EXTERNALMODEL)
			return false;
		if (currententity->keynum == (cl.viewentity[r_refdef.currentplayernum]?cl.viewentity[r_refdef.currentplayernum]:(cl.playernum[r_refdef.currentplayernum]+1)))
			return false;
//		if (cl.viewentity[r_refdef.currentplayernum] && currententity->keynum == cl.viewentity[r_refdef.currentplayernum])
//			continue;
		if (!Cam_DrawPlayer(0, currententity->keynum-1))
			return false;
	}
	return true;
}

typedef struct {
	int count;
	msurface_t **s;
} shadowmeshsurfs_t;
typedef struct shadowmesh_s {
	int numindicies;
	int numverts;
	int *indicies;
	vec3_t *verts;

	//we also have a list of all the surfaces that this light lights.
	int numsurftextures;
	shadowmeshsurfs_t *litsurfs;

	unsigned char *litleaves;
} shadowmesh_t;


#define	Q2RF_WEAPONMODEL		4		// only draw through eyes

struct {
	short count;
	short count2;
	int next;
	int prev;
} edge[MAX_MAP_EDGES];
int firstedge;

vec3_t lightorg = {0, 0, 0};
float lightradius;



typedef struct {
	float xyz[3];	//xyz world coordinates
	float stw[2];	//base texture/normalmap/specular map st coords
	float stl[3];	//lightmap/deluxmap st coords (or attenuated distance*colour)
	float ncm[3];	//normalisation cube map (reflected light dir)
} surfvertexarray_t;

#define MAXARRAYVERTS	2048
static surfvertexarray_t varray_v[MAXARRAYVERTS];
static unsigned int varray_i[MAXARRAYVERTS];
//static unsigned int varray_i_forward[MAXARRAYVERTS];
//static unsigned int varray_i_polytotri[MAXARRAYVERTS];	//012 023 034 045...
int varray_ic;
int varray_vc;

#define inline static

extern qboolean varrayactive;	//used by the backend

inline void PPL_EnableVertexArrays(void)
{
	varrayactive = false;

	qglDisableClientState(GL_COLOR_ARRAY);
	qglEnableClientState(GL_VERTEX_ARRAY);
	qglVertexPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->xyz);
}
inline void PPL_FlushArrays(void)
{
	if (varray_ic)
		qglDrawElements(GL_TRIANGLES, varray_ic, GL_UNSIGNED_INT, varray_i);
	varray_ic = 0;
	varray_vc = 0;
}
static void PPL_GenerateArrays(msurface_t *surf)
{
	int vi;

	if (!surf->mesh)
		return;
	if (surf->mesh->numindexes > MAXARRAYVERTS)
		return;
	if (surf->mesh->numvertexes > MAXARRAYVERTS)
		return;
	if (!surf->mesh->st_array)
		return;
	if (!surf->mesh->lmst_array)
		return;
	if (varray_ic)	//FIXME: go muuuch faster please
		PPL_FlushArrays();
	for (vi = 0; vi < surf->mesh->numindexes; vi++)
		varray_i[vi] = surf->mesh->indexes[vi];
	for (vi = 0; vi < surf->mesh->numvertexes; vi++)
	{
		VectorCopy(surf->mesh->xyz_array[vi], varray_v[vi].xyz);
		varray_v[vi].stw[0] = surf->mesh->st_array[vi][0];
		varray_v[vi].stw[1] = surf->mesh->st_array[vi][1];
		varray_v[vi].stl[0] = surf->mesh->lmst_array[vi][0];
		varray_v[vi].stl[1] = surf->mesh->lmst_array[vi][1];
	}

	varray_vc = surf->mesh->numvertexes;
	varray_ic = surf->mesh->numindexes;
}
static void PPL_GenerateDetailArrays(msurface_t *surf)
{
	int vi;

	if (!surf->mesh)
		return;
	if (surf->mesh->numindexes > MAXARRAYVERTS)
		return;
	if (surf->mesh->numvertexes > MAXARRAYVERTS)
		return;
	if (!surf->mesh->st_array)
		return;
	if (!surf->mesh->lmst_array)
		return;
	if (varray_ic)	//FIXME: go muuuch faster please
		PPL_FlushArrays();
	for (vi = 0; vi < surf->mesh->numindexes; vi++)
		varray_i[vi] = surf->mesh->indexes[vi];
	for (vi = 0; vi < surf->mesh->numvertexes; vi++)
	{
		VectorCopy(surf->mesh->xyz_array[vi], varray_v[vi].xyz);
		varray_v[vi].stw[0] = surf->mesh->st_array[vi][0]*gl_detailscale.value;
		varray_v[vi].stw[1] = surf->mesh->st_array[vi][1]*gl_detailscale.value;
	}

	varray_vc = surf->mesh->numvertexes;
	varray_ic = surf->mesh->numindexes;
}

/*
static void PPL_BaseChain_NoLightmap(msurface_t *first, texture_t *tex)
{
	Sys_Error("1 TMU is disabled for now (surface has no lightmap)\n");
}
*/

static void PPL_BaseChain_NoBump_1TMU(msurface_t *first, texture_t *tex)
{
	int vi;
	glRect_t    *theRect;
	msurface_t *s;

	PPL_EnableVertexArrays();

	qglDisable(GL_BLEND);
	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	GL_TexEnv(GL_REPLACE);
	GL_Bind (tex->gl_texturenum);

	for (s=first; s ; s=s->texturechain)
	{
		PPL_GenerateArrays(s);
	}
	PPL_FlushArrays();

	qglEnable(GL_BLEND);
	GL_TexEnv(GL_MODULATE);

	if (gl_lightmap_format == GL_LUMINANCE || gl_lightmap_format == GL_RGB)
		qglBlendFunc (GL_ZERO, GL_SRC_COLOR);
	else if (gl_lightmap_format == GL_INTENSITY)
	{
		qglColor4f (0,0,0,1);
		qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
	else if (gl_lightmap_format == GL_RGBA)
		qglBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);

	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);

	vi = -1;
	for (s=first; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			PPL_FlushArrays();
			vi = s->lightmaptexturenum;

			GL_BindType(GL_TEXTURE_2D, lightmap_textures[vi] );
			if (lightmap[vi]->modified)
			{
				lightmap[vi]->modified = false;
				theRect = &lightmap[vi]->rectchange;
				qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
					lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
		}

		PPL_GenerateArrays(s);
	}
	PPL_FlushArrays();

	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

/*static void PPL_BaseChain_NoBump_2TMU(msurface_t *s, texture_t *tex)
{	//doesn't merge surfaces, but tells gl to do each vertex arrayed surface individually, which means no vertex copying.
	int vi;
	glRect_t    *theRect;

	PPL_EnableVertexArrays();

	if (tex->alphaed)
	{
		qglEnable(GL_BLEND);
		GL_TexEnv(GL_MODULATE);
	}
	else
	{
		qglDisable(GL_BLEND);
		GL_TexEnv(GL_REPLACE);
	}


	GL_MBind(GL_TEXTURE0_ARB, tex->gl_texturenum);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	GL_TexEnv(GL_MODULATE);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	vi = -1;
	for (; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			if (vi<0)
				qglEnable(GL_TEXTURE_2D);
			vi = s->lightmaptexturenum;

			if (vi>=0)
			{
				GL_Bind(lightmap_textures[vi] );
				if (lightmap[vi]->modified)
				{
					lightmap[vi]->modified = false;
					theRect = &lightmap[vi]->rectchange;
					qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
						LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
						lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
					theRect->l = LMBLOCK_WIDTH;
					theRect->t = LMBLOCK_HEIGHT;
					theRect->h = 0;
					theRect->w = 0;
				}
			}
			else
				qglDisable(GL_TEXTURE_2D);
		}

		qglClientActiveTextureARB(GL_TEXTURE0_ARB);
		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->st_array);
		qglClientActiveTextureARB(GL_TEXTURE1_ARB);
		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->lmst_array);

		qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);

		qglDrawRangeElements(GL_TRIANGLES, 0, s->mesh->numvertexes, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
		//qglDrawElements(GL_TRIANGLES, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
	}

	qglDisable(GL_TEXTURE_2D);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_SelectTexture(GL_TEXTURE0_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
}*/

static void PPL_BaseChain_NoBump_2TMU_Overbright(msurface_t *s, texture_t *tex)
{	//doesn't merge surfaces, but tells gl to do each vertex arrayed surface individually, which means no vertex copying.
	int vi;
	glRect_t    *theRect;

	PPL_EnableVertexArrays();

	if (tex->alphaed || currententity->shaderRGBAf[3]<1)
	{
		qglEnable(GL_BLEND);
		GL_TexEnv(GL_MODULATE);
	}
	else
	{
		qglDisable(GL_BLEND);
		GL_TexEnv(GL_REPLACE);
	}


	GL_MBind(GL_TEXTURE0_ARB, tex->gl_texturenum);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_TexEnv(GL_MODULATE);


	if (overbright != 1)
	{
		GL_TexEnv(GL_COMBINE_ARB);
		qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
		qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
		qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
		qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, overbright);	//this is the key
	}

	vi = -1;
	for (; s ; s=s->texturechain)
	{
		if (!s->mesh)	//urm.
			continue;
		if (vi != s->lightmaptexturenum)
		{
			if (vi<0)
				qglEnable(GL_TEXTURE_2D);
			vi = s->lightmaptexturenum;

			if (vi>=0)
			{
				GL_Bind(lightmap_textures[vi] );
				if (lightmap[vi]->modified)
				{
					lightmap[vi]->modified = false;
					theRect = &lightmap[vi]->rectchange;
					qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
						LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
						lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
					theRect->l = LMBLOCK_WIDTH;
					theRect->t = LMBLOCK_HEIGHT;
					theRect->h = 0;
					theRect->w = 0;
				}
			}
			else
				qglDisable(GL_TEXTURE_2D);
		}

		qglClientActiveTextureARB(GL_TEXTURE0_ARB);
		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->st_array);
		qglClientActiveTextureARB(GL_TEXTURE1_ARB);
		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->lmst_array);

		qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);

		qglDrawRangeElements(GL_TRIANGLES, 0, s->mesh->numvertexes, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
	}

	if (overbright != 1)
	{
		qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);	//just in case
		GL_TexEnv(GL_MODULATE);
	}

	qglDisable(GL_TEXTURE_2D);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_SelectTexture(GL_TEXTURE0_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

/*
static void PPL_BaseChain_NoBump_2TMU_TEST(msurface_t *s, texture_t *tex)
{	//this was just me testing efficiency between arrays/glbegin.
	int vi, i;
	glRect_t    *theRect;

	PPL_EnableVertexArrays();

	if (tex->alphaed)
	{
		qglEnable(GL_BLEND);
		GL_TexEnv(GL_MODULATE);
	}
	else
	{
		qglDisable(GL_BLEND);
		GL_TexEnv(GL_REPLACE);
	}

	qglCullFace(GL_BACK);


	GL_MBind(GL_TEXTURE0_ARB, tex->gl_texturenum);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	GL_TexEnv(GL_MODULATE);

	vi = -1;
	for (; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			if (vi<0)
				qglEnable(GL_TEXTURE_2D);
			vi = s->lightmaptexturenum;

			if (vi>=0)
			{
				GL_Bind(lightmap_textures[vi] );
				if (lightmap[vi]->modified)
				{
					lightmap[vi]->modified = false;
					theRect = &lightmap[vi]->rectchange;
					qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
						LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
						lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
					theRect->l = LMBLOCK_WIDTH;
					theRect->t = LMBLOCK_HEIGHT;
					theRect->h = 0;
					theRect->w = 0;
				}
			}
			else
				qglDisable(GL_TEXTURE_2D);
		}

		qglBegin(GL_POLYGON);
		switch(s->mesh->numvertexes)
		{
		default:
			for (i = s->mesh->numvertexes-1; i >= 6; i--)
			{
				qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, s->mesh->st_array[i][0], s->mesh->st_array[i][1]);
				qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, s->mesh->lmst_array[i][0], s->mesh->lmst_array[i][1]);
				qglVertex3fv(s->mesh->xyz_array[i]);
			}
		case 6:
			qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, s->mesh->st_array[5][0], s->mesh->st_array[5][1]);
			qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, s->mesh->lmst_array[5][0], s->mesh->lmst_array[5][1]);
			qglVertex3fv(s->mesh->xyz_array[5]);
		case 5:
			qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, s->mesh->st_array[4][0], s->mesh->st_array[4][1]);
			qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, s->mesh->lmst_array[4][0], s->mesh->lmst_array[4][1]);
			qglVertex3fv(s->mesh->xyz_array[4]);
		case 4:
			qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, s->mesh->st_array[3][0], s->mesh->st_array[3][1]);
			qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, s->mesh->lmst_array[3][0], s->mesh->lmst_array[3][1]);
			qglVertex3fv(s->mesh->xyz_array[3]);
		case 3:
			qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, s->mesh->st_array[2][0], s->mesh->st_array[2][1]);
			qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, s->mesh->lmst_array[2][0], s->mesh->lmst_array[2][1]);
			qglVertex3fv(s->mesh->xyz_array[2]);
		case 2:
			qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, s->mesh->st_array[1][0], s->mesh->st_array[1][1]);
			qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, s->mesh->lmst_array[1][0], s->mesh->lmst_array[1][1]);
			qglVertex3fv(s->mesh->xyz_array[1]);
		case 1:
			qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, s->mesh->st_array[0][0], s->mesh->st_array[0][1]);
			qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, s->mesh->lmst_array[0][0], s->mesh->lmst_array[0][1]);
			qglVertex3fv(s->mesh->xyz_array[0]);
		case 0:
			break;
		}
		qglEnd();
	}

	qglDisable(GL_TEXTURE_2D);

	GL_SelectTexture(GL_TEXTURE0_ARB);
}
*/

static void PPL_BaseChain_Bump_2TMU(msurface_t *first, texture_t *tex)
{
	int vi;
	glRect_t    *theRect;
	msurface_t *s;
	PPL_EnableVertexArrays();

	if (tex->alphaed)
	{
		qglEnable(GL_BLEND);
		GL_TexEnv(GL_MODULATE);
	}
	else
	{
		qglDisable(GL_BLEND);
		GL_TexEnv(GL_REPLACE);
	}

	//Bind normal map to texture unit 0
	GL_MBind(GL_TEXTURE0_ARB, tex->gl_texturenumbumpmap);
	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_COMBINE_ARB);
	qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	GL_MBind(GL_TEXTURE1_ARB, tex->gl_texturenumbumpmap);
	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_COMBINE_ARB);
	qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);


	vi = -1;
	for (s=first; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			PPL_FlushArrays();
			vi = s->lightmaptexturenum;

			GL_Bind(deluxmap_textures[vi] );
			if (lightmap[vi]->deluxmodified)
			{
				lightmap[vi]->deluxmodified = false;
				theRect = &lightmap[vi]->deluxrectchange;
				qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
					lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*3);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
		}

		PPL_GenerateArrays(s);
	}
	PPL_FlushArrays();

	GL_MBind(GL_TEXTURE0_ARB, tex->gl_texturenum);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_MODULATE);

	vi = -1;
	for (s=first; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			PPL_FlushArrays();
			vi = s->lightmaptexturenum;

			GL_Bind(lightmap_textures[vi] );
			if (lightmap[vi]->modified)
			{
				lightmap[vi]->modified = false;
				theRect = &lightmap[vi]->rectchange;
				qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
					lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
		}

		PPL_GenerateArrays(s);
	}
	PPL_FlushArrays();

	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	GL_SelectTexture(GL_TEXTURE0_ARB);
}

static void PPL_BaseChain_Bump_4TMU(msurface_t *s, texture_t *tex)
{
	int vi;
	glRect_t    *theRect;

	PPL_EnableVertexArrays();

	//Bind normal map to texture unit 0
	GL_MBind(GL_TEXTURE0_ARB, tex->gl_texturenumbumpmap);
	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_REPLACE);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	//1 gets the deluxmap
	GL_SelectTexture(GL_TEXTURE1_ARB);
	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_COMBINE_ARB);
	qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);

	//2 gets the diffusemap
	GL_MBind(GL_TEXTURE2_ARB, tex->gl_texturenum);
	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_MODULATE);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	//3 gets the lightmap
	GL_SelectTexture(GL_TEXTURE3_ARB);
	qglEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_MODULATE);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);

	vi = -1;
	for (; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			PPL_FlushArrays();
			vi = s->lightmaptexturenum;

			GL_MBind(GL_TEXTURE1_ARB, deluxmap_textures[vi] );
			if (lightmap[vi]->deluxmodified)
			{
				lightmap[vi]->deluxmodified = false;
				theRect = &lightmap[vi]->deluxrectchange;
				qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, GL_RGB, GL_UNSIGNED_BYTE,
					lightmap[vi]->deluxmaps+(theRect->t) *LMBLOCK_WIDTH*3);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
			GL_MBind(GL_TEXTURE3_ARB, lightmap_textures[vi] );
			if (lightmap[vi]->modified)
			{
				lightmap[vi]->modified = false;
				theRect = &lightmap[vi]->rectchange;
				qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
					lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
		}

		PPL_GenerateArrays(s);
	}
	PPL_FlushArrays();

	GL_SelectTexture(GL_TEXTURE3_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisable(GL_TEXTURE_2D);

	GL_SelectTexture(GL_TEXTURE2_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisable(GL_TEXTURE_2D);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	GL_TexEnv(GL_MODULATE);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisable(GL_TEXTURE_2D);

	GL_SelectTexture(GL_TEXTURE0_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);


	GL_TexEnv(GL_MODULATE);
}

#ifdef SPECULAR
//Draw a texture chain with specular exponant 1.
//erm...
//this uses the wrong stuff to work on gf4tis.
/*
static void PPL_BaseChain_Specular_4TMU(msurface_t *first, texture_t *tex)
{
//if I ever do write this function, It'll take a couple of passes.
	int vi;
	glRect_t    *theRect;
	msurface_t *s;

	glColorMask(1,1,1,0);

	PPL_EnableVertexArrays();

	if (qglGetError())
		Con_Printf("Error before PPL_BaseChain_Specular\n");

	//first 4 texture units: (N.((L+V)/2))^2
glDisable(GL_BLEND);
	qglActiveTextureARB(GL_TEXTURE0_ARB);
	GL_BindType(GL_TEXTURE_2D, tex->gl_texturenumbumpmap);
	glEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_REPLACE);
	qglClientActiveTextureARB(GL_TEXTURE0_ARB);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	if (qglGetError())
		Con_Printf("Error binding dot3 tmu1\n");

	qglActiveTextureARB(GL_TEXTURE1_ARB);
	glDisable(GL_TEXTURE_2D);
	GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap);
	glEnable(GL_TEXTURE_CUBE_MAP_ARB);
	if (qglGetError())
		Con_Printf("Error binding dot3 cubemap\n");
	GL_TexEnv(GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);
	if (qglGetError())
		Con_Printf("Error binding dot3 combine\n");
	qglClientActiveTextureARB(GL_TEXTURE1_ARB);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->ncm);

	if (qglGetError())
		Con_Printf("Error binding dot3 tmu2\n");

	//prev*prev (the exponential)
	qglActiveTextureARB(GL_TEXTURE2_ARB);
	GL_BindType(GL_TEXTURE_2D, tex->gl_texturenumbumpmap);	//need to bind something.
	glEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);

	if (qglGetError())
		Con_Printf("Error binding prev*prev\n");

	qglActiveTextureARB(GL_TEXTURE3_ARB);
	GL_BindType(GL_TEXTURE_2D, tex->gl_texturenumspec);
	glEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
	qglClientActiveTextureARB(GL_TEXTURE3_ARB);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	if (qglGetError())
		Con_Printf("Error binding specular in PPL_BaseChain_Specular\n");

	for (s = first; s ; s=s->texturechain)
	{
		PPL_GenerateArraysBlinnCubeMap(s);
	}
	PPL_FlushArrays();

glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR, GL_ZERO);
	// Add normal dot delux times diffusemap then multiple the entire lot by the lightmap.
	qglActiveTextureARB(GL_TEXTURE0_ARB);
	GL_TexEnv(GL_REPLACE);

	qglActiveTextureARB(GL_TEXTURE1_ARB);
	glDisable(GL_TEXTURE_CUBE_MAP_ARB);
	glEnable(GL_TEXTURE_2D);
	GL_TexEnv(GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);
	qglClientActiveTextureARB(GL_TEXTURE1_ARB);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);

	qglActiveTextureARB(GL_TEXTURE2_ARB);
	glEnable(GL_TEXTURE_2D);
	GL_BindType(GL_TEXTURE_2D, tex->gl_texturenum);
	GL_TexEnv(GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
	qglClientActiveTextureARB(GL_TEXTURE2_ARB);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	qglActiveTextureARB(GL_TEXTURE3_ARB);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_TEXTURE_2D);
	GL_TexEnv(GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
	qglClientActiveTextureARB(GL_TEXTURE3_ARB);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);

	vi = -1;
	for (s = first; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			PPL_FlushArrays();
			vi = s->lightmaptexturenum;

			qglActiveTextureARB(GL_TEXTURE1_ARB);
			GL_BindType(GL_TEXTURE_2D, deluxmap_textures[vi] );
			if (lightmap[vi]->deluxmodified)
			{
				lightmap[vi]->deluxmodified = false;
				theRect = &lightmap[vi]->deluxrectchange;
				glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, GL_RGB, GL_UNSIGNED_BYTE,
					lightmap[vi]->deluxmaps+(theRect->t) *LMBLOCK_WIDTH*3);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
			qglActiveTextureARB(GL_TEXTURE3_ARB);
			GL_BindType(GL_TEXTURE_2D, lightmap_textures[vi] );
			if (lightmap[vi]->modified)
			{
				lightmap[vi]->modified = false;
				theRect = &lightmap[vi]->rectchange;
				glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
					lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
		}
		PPL_GenerateArrays(s);
	}
	PPL_FlushArrays();

	glColorMask(1,1,1,0);

	if (qglGetError())
		Con_Printf("Error drawing in PPL_BaseChain_Specular\n");

	
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	qglClientActiveTextureARB(GL_TEXTURE2_ARB);
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	qglClientActiveTextureARB(GL_TEXTURE1_ARB);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisable(GL_TEXTURE_2D);

	qglActiveTextureARB(GL_TEXTURE0_ARB);
	qglClientActiveTextureARB(GL_TEXTURE0_ARB);
}
*/

void PPL_LoadSpecularFragmentProgram(void)
{
//#define SMOOOOOTH	//define this to calculate everything per-pixel as opposed to interpolating the halfdir
	char *vert = 
		"varying vec2 tcbase;\n"
		"varying vec2 tclm;\n"
		"uniform vec3 vieworg;\n"
#ifdef SMOOOOOTH
		"varying vec3 fragpos;\n"
		"varying vec3 norm;\n"
#else
		"uniform vec3 texr, texu, texf;\n"
		"varying vec3 halfnorm;\n"
#endif
		"void main (void)\n"
		"{\n"
		"	gl_Position = ftransform();\n"

		"	tcbase = gl_MultiTexCoord0.xy;\n"	//pass the texture coords straight through
		"	tclm = gl_MultiTexCoord1.xy;\n"

#ifdef SMOOOOOTH
		" fragpos = vec3(gl_Vertex.xyz);\n"
		"norm = gl_Normal;\n"
#else
		"	vec3 eye = normalize(vieworg - vec3(gl_Vertex.xyz));\n"
		"	vec3 halfdir = (eye + texf) / 2.0;\n"
		"	halfnorm.x = dot(texr, halfdir);\n"	//put halfnorm into object space
		"	halfnorm.y = dot(texu, halfdir);\n"
		"	halfnorm.z = dot(texf, halfdir);\n"
#endif
		"}\n"
		;

/*
			VectorSubtract(r_refdef.vieworg, v, eye);
		VectorNormalize(eye);
		VectorAdd(eye, (v+7), halfdir);	//v+7 is the light dir (or plane normal)
		varray_v[varray_vc].ncm[0] = DotProduct(surf->texinfo->vecs[0], halfdir);
		varray_v[varray_vc].ncm[1] = DotProduct(surf->texinfo->vecs[1], halfdir);
		if (surf->flags & SURF_PLANEBACK)
			varray_v[varray_vc].ncm[2] = -DotProduct(surf->plane->normal, halfdir);
		else
			varray_v[varray_vc].ncm[2] = DotProduct(surf->plane->normal, halfdir);
*/
	char *frag =
		"uniform sampler2D baset;\n"
		"uniform sampler2D bumpt;\n"
		"uniform sampler2D lightmapt;\n"
		"uniform sampler2D deluxt;\n"
		"uniform sampler2D speculart;\n"
		"varying vec2 tcbase;\n"
		"varying vec2 tclm;\n"

#ifdef SMOOOOOTH
		"uniform vec3 vieworg;\n"
		"varying vec3 fragpos;\n"
		"uniform vec3 texr, texu, texf;\n"
#else
		"varying vec3 halfnorm;\n"
#endif
		"void main (void)\n"
		"{\n"
		"	vec3 bases = vec3(texture2D(baset, tcbase));\n"
		"	vec3 bumps = vec3(texture2D(bumpt, tcbase)) * 2.0 - 1.0;\n"
		"	vec3 deluxs = vec3(texture2D(deluxt, tclm)) * 2.0 - 1.0;\n"
		"	vec3 lms = vec3(texture2D(lightmapt, tclm));\n"
		"	vec3 specs = vec3(texture2D(speculart, tcbase));\n"
		"	vec3 diff, spec;\n"

#ifdef SMOOOOOTH
		"	vec3 eye = normalize(vieworg - fragpos);\n"
		"	vec3 halfdir = (eye + texf) / 2.0;\n"
		"	vec3 halfnorm;\n"
		"	halfnorm.x = dot(texr, halfdir);\n"	//put halfnorm into object space
		"	halfnorm.y = dot(texu, halfdir);\n"
		"	halfnorm.z = dot(texf, halfdir);\n"
#endif

		"	diff = bases * dot(bumps, deluxs);\n"
		"	float dv = dot(normalize(halfnorm), bumps);\n"
		"	spec = pow(dv, 8.0) * specs;\n"
		"	gl_FragColor = vec4((diff+spec)*lms, 1.0);\n"
		"}\n"
		;

	ppl_specular_shader = GLSlang_CreateProgram(NULL, vert, frag);

	if (ppl_specular_shader)
	{
		GLSlang_UseProgram(ppl_specular_shader);

		qglUniform1iARB(qglGetUniformLocationARB(ppl_specular_shader, "baset"), 0);
		qglUniform1iARB(qglGetUniformLocationARB(ppl_specular_shader, "bumpt"), 1);
		qglUniform1iARB(qglGetUniformLocationARB(ppl_specular_shader, "lightmapt"), 2);
		qglUniform1iARB(qglGetUniformLocationARB(ppl_specular_shader, "deluxt"), 3);
		qglUniform1iARB(qglGetUniformLocationARB(ppl_specular_shader, "speculart"), 4);

		ppl_specular_shader_vieworg = qglGetUniformLocationARB(ppl_specular_shader, "vieworg");
		ppl_specular_shader_texr = qglGetUniformLocationARB(ppl_specular_shader, "texr");
		ppl_specular_shader_texu = qglGetUniformLocationARB(ppl_specular_shader, "texu");
		ppl_specular_shader_texf = qglGetUniformLocationARB(ppl_specular_shader, "texf");

		GLSlang_UseProgram(0);
	}
}

static void PPL_BaseChain_Specular_FP(msurface_t *s, texture_t *tex)
{
	int vi;
	glRect_t    *theRect;

	PPL_EnableVertexArrays();

	GLSlang_UseProgram(ppl_specular_shader);

	if (qglGetError())
		Con_Printf("GL Error on shadow lighting\n");

	GL_MBind(GL_TEXTURE0_ARB, tex->gl_texturenum);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_MBind(GL_TEXTURE1_ARB, tex->gl_texturenumbumpmap);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

//	GL_MBind(GL_TEXTURE2_ARB, lightmap_textures[vi] );

//	GL_MBind(GL_TEXTURE3_ARB, deluxmap_textures[vi] );

	GL_MBind(GL_TEXTURE4_ARB, tex->gl_texturenumspec);

	qglUniform3fvARB(ppl_specular_shader_vieworg, 1, r_refdef.vieworg);

	if (qglGetError())
		Con_Printf("GL Error early during PPL_BaseChain_Specular_FP\n");

	vi = -1;
	for (; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			vi = s->lightmaptexturenum;

			GL_MBind(GL_TEXTURE3_ARB, deluxmap_textures[vi] );
			if (lightmap[vi]->deluxmodified)
			{
				lightmap[vi]->deluxmodified = false;
				theRect = &lightmap[vi]->deluxrectchange;
				qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, GL_RGB, GL_UNSIGNED_BYTE,
					lightmap[vi]->deluxmaps+(theRect->t) *LMBLOCK_WIDTH*3);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
			GL_MBind(GL_TEXTURE2_ARB, lightmap_textures[vi] );
			if (lightmap[vi]->modified)
			{
				lightmap[vi]->modified = false;
				theRect = &lightmap[vi]->rectchange;
				qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
					LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
					lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
				theRect->l = LMBLOCK_WIDTH;
				theRect->t = LMBLOCK_HEIGHT;
				theRect->h = 0;
				theRect->w = 0;
			}
		}

		qglUniform3fvARB(ppl_specular_shader_texr, 1, s->texinfo->vecs[0]);
		qglUniform3fvARB(ppl_specular_shader_texu, 1, s->texinfo->vecs[1]);
		if (s->flags & SURF_PLANEBACK)
			qglUniform3fARB(ppl_specular_shader_texf, -s->plane->normal[0], -s->plane->normal[1], -s->plane->normal[2]);
		else
			qglUniform3fvARB(ppl_specular_shader_texf, 1, s->plane->normal);

		qglClientActiveTextureARB(GL_TEXTURE0_ARB);
		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->st_array);
		qglClientActiveTextureARB(GL_TEXTURE1_ARB);
		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->lmst_array);
		
		qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);
		qglDrawElements(GL_TRIANGLES, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
	}

	GLSlang_UseProgram(0);

	GL_SelectTexture(GL_TEXTURE2_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_SelectTexture(GL_TEXTURE0_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);


	if (qglGetError())
		Con_Printf("GL Error on specular lighting\n");
}

#endif

//single textured.
static void PPL_BaseChain_Flat(msurface_t *first)
{
	static vec_t wallcolour[4] = {0,0,0,1};
	static vec_t floorcolour[4] = {0,0,0,1};
	msurface_t *s;
	int iswall = -1;
	int vi=-10;
	glRect_t    *theRect;
	
	if (!r_lightmapintensity)
	{	//these are bad. :(

		PPL_EnableVertexArrays();
		qglColor4f(0,0,0,1);
		qglDisable(GL_TEXTURE_2D);	//texturing? who wants texturing?!?!
		for (s = first; s ; s=s->texturechain)
			PPL_GenerateArrays(s);
		PPL_FlushArrays();
		qglEnable(GL_TEXTURE_2D);
		return;
	}
	else
	{
		if (r_wallcolour.modified)
		{
			r_wallcolour.modified = false;

			SCR_StringToRGB(r_wallcolour.string, wallcolour, 1);
		}
		if (r_floorcolour.modified)
		{
			r_floorcolour.modified = false;

			SCR_StringToRGB(r_floorcolour.string, floorcolour, 1);
		}
	}

	PPL_EnableVertexArrays();
	GL_TexEnv(GL_MODULATE);

	for (s = first; s ; s=s->texturechain)
	{
		if (s->mesh->numvertexes < 3) continue;
		if (vi != s->lightmaptexturenum)
		{
			if (vi < 0)
			{
				qglEnable(GL_TEXTURE_2D);
				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
			}
			vi = s->lightmaptexturenum;
			if (s->lightmaptexturenum < 0)
			{
				qglDisable(GL_TEXTURE_2D);
				qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
			}

			if (vi>=0)
			{
				GL_Bind(lightmap_textures[vi] );
				if (lightmap[vi]->modified)
				{
					lightmap[vi]->modified = false;
					theRect = &lightmap[vi]->rectchange;
					qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
						LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
						lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
					theRect->l = LMBLOCK_WIDTH;
					theRect->t = LMBLOCK_HEIGHT;
					theRect->h = 0;
					theRect->w = 0;
				}
			}
		}

		if ((s->plane->normal[2]*s->plane->normal[2]) <= 0.5*0.5)
		{
			if (iswall != 0)
			{
				iswall=0;
				qglColor4fv(wallcolour);
			}
		}
		else if (iswall != 1)
		{
			iswall=1;
			qglColor4fv(floorcolour);
		}

		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->lmst_array);
		qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);
		qglDrawRangeElements(GL_TRIANGLES, 0, s->mesh->numvertexes, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
	}

	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglColor3f(1,1,1);
}

static void PPL_BaseChain_NPR_Sketch(msurface_t *first)
{
	msurface_t *s;
	int vi=-10;
	int i;
	glRect_t    *theRect;

	static int textures[10];

	GL_SelectTexture(GL_TEXTURE0_ARB);
	if (r_drawflat.modified)	//reload textures
	{
		r_drawflat.modified = false;
		for (i = 0; i < sizeof(textures)/sizeof(textures[0]); i++)
		{
			textures[i] = Mod_LoadHiResTexture(va("sketch%i", i+1), "sketch", true, false, false);
			if (!textures[i])
			{
				int data[128*128];
				FILE *file;
				unsigned char *f;
				int p;

				file = fopen(va("textures/tex%i_3_128_128.raw", i+1), "rb");
			
				if (file)
				{
					f = Hunk_TempAlloc(128*128*3);
					if (fread(f, 128*3, 128, file) == 128)
					{
						for (p = 0; p < 128*128; p++)
							data[p] = LittleLong(f[p*3] + (f[p*3+1]<<8) + (f[p*3+2]<<16) + (255<<24));
						textures[i] = GL_LoadTexture32 (va("textures/tex%i_3_128_128.raw", i+1), 128, 128, data, true, false);
					}
					fclose(file);
				}
			}
		}
	}

	PPL_EnableVertexArrays();

//draw the surface properly
	qglEnable(GL_TEXTURE_2D);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

	GL_TexEnv(GL_MODULATE);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	GL_TexEnv(GL_MODULATE);
	qglEnable(GL_TEXTURE_2D);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);

	qglColor3f(1,1,1);
	for (s = first; s ; s=s->texturechain)
	{
		if (vi != s->lightmaptexturenum)
		{
			PPL_FlushArrays();
			vi = s->lightmaptexturenum;

			GL_MBind(GL_TEXTURE0_ARB, textures[rand()%10]);

			if (vi < 0)
				GL_MBind(GL_TEXTURE1_ARB, 0 );
			else
			{
				GL_MBind(GL_TEXTURE1_ARB, lightmap_textures[vi] );
				if (lightmap[vi]->modified)
				{
					lightmap[vi]->modified = false;
					theRect = &lightmap[vi]->rectchange;
					qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
						LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
						lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
					theRect->l = LMBLOCK_WIDTH;
					theRect->t = LMBLOCK_HEIGHT;
					theRect->h = 0;
					theRect->w = 0;
				}
			}
		}
		PPL_GenerateArrays(s);
	}
	PPL_FlushArrays();
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisable(GL_TEXTURE_2D);

	GL_SelectTexture(GL_TEXTURE0_ARB);
	qglDisable(GL_TEXTURE_2D);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	qglDisableClientState(GL_VERTEX_ARRAY);
	qglDisableClientState(GL_COLOR_ARRAY);

	//draw some extra lines around the edge for added coolness.
	qglColor3f(0,0,0);

	for (vi = 0; vi < 2; vi++)
	{
		for (s = first; s ; s=s->texturechain)
		{
			if (!s->mesh)
				continue;

			for (i=0; i<s->mesh->numindexes; i+=3)
			{
				qglBegin(GL_LINE_LOOP);
				qglVertex3f(s->mesh->xyz_array[s->mesh->indexes[i+0]][0]+5*(rand()/(float)RAND_MAX-0.5),
							s->mesh->xyz_array[s->mesh->indexes[i+0]][1]+5*(rand()/(float)RAND_MAX-0.5),
							s->mesh->xyz_array[s->mesh->indexes[i+0]][2]+5*(rand()/(float)RAND_MAX-0.5));
				qglVertex3f(s->mesh->xyz_array[s->mesh->indexes[i+1]][0]+5*(rand()/(float)RAND_MAX-0.5),
							s->mesh->xyz_array[s->mesh->indexes[i+1]][1]+5*(rand()/(float)RAND_MAX-0.5),
							s->mesh->xyz_array[s->mesh->indexes[i+1]][2]+5*(rand()/(float)RAND_MAX-0.5));
				qglVertex3f(s->mesh->xyz_array[s->mesh->indexes[i+2]][0]+5*(rand()/(float)RAND_MAX-0.5),
							s->mesh->xyz_array[s->mesh->indexes[i+2]][1]+5*(rand()/(float)RAND_MAX-0.5),
							s->mesh->xyz_array[s->mesh->indexes[i+2]][2]+5*(rand()/(float)RAND_MAX-0.5));
				qglEnd();
			}
		}
	}

	qglEnable(GL_TEXTURE_2D);
}

static void PPL_BaseTextureChain(msurface_t *first)
{
	texture_t	*t;
	if (r_drawflat.value||!r_lightmapintensity)
	{
		if (r_drawflat.value == 2)
		{
			if (gl_mtexarbable >= 2)	//shiesh!.
			{
				PPL_BaseChain_NPR_Sketch(first);
				return;
			}
		}
		else
		{
			PPL_BaseChain_Flat(first);	//who cares about texture? :/
			return;
		}
	}
#ifdef Q3SHADERS
	if (first->texinfo->texture->shader)
	{
		meshbuffer_t mb;
		msurface_t *s;
		int vi=-1;
		int redraw = false;
		int dlb;

		glRect_t    *theRect;
		if (first->texinfo->texture->shader->flags & SHADER_FLARE )
		{
			dlight_t *dl;
			while(first)
			{	//a quick hack to convert to a dlight
				dl = CL_AllocDlight(0);
				VectorCopy(first->mesh->xyz_array[0], dl->origin);
				dl->color[0] = 0.2;
				dl->color[1] = 0.2;
				dl->color[2] = 0.2;
				dl->radius = 50;

				//flashblend only
				dl->noppl = true;
				dl->nodynamic = true;

				first = first->texturechain;
			}
			return;
		}

		if (!varrayactive)
			R_IBrokeTheArrays();

		mb.entity = &r_worldentity;
		mb.shader = first->texinfo->texture->shader;
		mb.mesh = NULL;
		mb.fog = NULL;
		mb.infokey = -2;
		if (first->dlightframe == r_framecount)
			mb.dlightbits = first->dlightbits;
		else
			mb.dlightbits = 0;

		GL_DisableMultitexture();

		qglShadeModel(GL_SMOOTH);

		{
			for (s = first; s ; s=s->texturechain)
			{
				if (vi != s->lightmaptexturenum)
				{
					vi = s->lightmaptexturenum;
					if (vi >= 0)
					{
						if (gl_bump.value)
						if (lightmap[vi]->deluxmodified)
						{
							GL_BindType(GL_TEXTURE_2D, deluxmap_textures[vi] );
							lightmap[vi]->deluxmodified = false;
							theRect = &lightmap[vi]->deluxrectchange;
							qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
								LMBLOCK_WIDTH, theRect->h, GL_RGB, GL_UNSIGNED_BYTE,
								lightmap[vi]->deluxmaps+(theRect->t) *LMBLOCK_WIDTH*3);
							theRect->l = LMBLOCK_WIDTH;
							theRect->t = LMBLOCK_HEIGHT;
							theRect->h = 0;
							theRect->w = 0;
						}
						if (lightmap[vi]->modified)
						{
							GL_BindType(GL_TEXTURE_2D, lightmap_textures[vi] );
							lightmap[vi]->modified = false;
							theRect = &lightmap[vi]->rectchange;
							qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, 
								LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
								lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes);
							theRect->l = LMBLOCK_WIDTH;
							theRect->t = LMBLOCK_HEIGHT;
							theRect->h = 0;
							theRect->w = 0;
						}
					}
				}

				if (s->mesh)
				{
					if (s->dlightframe == r_framecount)
						dlb = s->dlightbits;
					else
						dlb = 0;
					redraw = mb.dlightbits != dlb || mb.fog != s->fog || mb.infokey != vi||R_MeshWillExceed(s->mesh);

					if (redraw)
					{
						if (mb.mesh)
							R_RenderMeshBuffer ( &mb, false );
						redraw = false;
					}

					mb.infokey = vi;
					mb.mesh = s->mesh;
					mb.fog = s->fog;
					mb.dlightbits = dlb;
					R_PushMesh(s->mesh, mb.shader->features);
				}
			}
		}

		if (mb.mesh)
			R_RenderMeshBuffer ( &mb, false );

		return;
	}
#endif
	qglEnable(GL_TEXTURE_2D);







	t = GLR_TextureAnimation (first->texinfo->texture);

	if (first->flags & SURF_DRAWTURB)
	{
		GL_DisableMultitexture();
		qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		GL_Bind (t->gl_texturenum);
		for (; first ; first=first->texturechain)
			EmitWaterPolys (first, currententity->shaderRGBAf[3]);

		qglDisable(GL_BLEND);
		qglColor4f(1,1,1, 1);

		t->texturechain = NULL;	//no lighting effects. (good job these don't animate eh?)
		return;
	}
/*	else if (s->lightmaptexturenum < 0)	//no lightmap
	{
		PPL_BaseChain_NoLightmap(first, t);
	}*/
	else if (gl_mtexarbable < 2)
	{	//multitexture isn't supported.
		PPL_BaseChain_NoBump_1TMU(first, t);
	}
	else
	{
		if (gl_bump.value && currentmodel->deluxdata && t->gl_texturenumbumpmap)
		{
			if (gl_mtexarbable>=4)
			{
				if (t->gl_texturenumspec && gl_specular.value)
				{
					if (ppl_specular_shader)
						PPL_BaseChain_Specular_FP(first, t);
//					else if (gl_mtexarbable>=8)
//						PPL_BaseChain_Specular_8TMU(first, t);
					else
						PPL_BaseChain_Bump_4TMU(first, t);	//can't do specular.
				}
				else
					PPL_BaseChain_Bump_4TMU(first, t);
			}
			else
				PPL_BaseChain_Bump_2TMU(first, t);
		}
		else
		{
//			PPL_BaseChain_NoBump_2TMU_TEST(first, t);
//			PPL_BaseChain_NoBump_2TMU(first, t);
			PPL_BaseChain_NoBump_2TMU_Overbright(first, t);
		}
	}
}


static void PPL_FullBrightTextureChain(msurface_t *first)
{
	texture_t	*t;
	msurface_t	*s;

	t = GLR_TextureAnimation (first->texinfo->texture);

	if (detailtexture && gl_detail.value)
	{
		GL_Bind(detailtexture);
		qglBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);

		PPL_EnableVertexArrays();
		qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
		qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);
		for (s = first; s ; s=s->texturechain)
		{
			PPL_GenerateDetailArrays(s);
		}
		PPL_FlushArrays();
	}

	if (t->gl_texturenumfb && r_fb_bmodels.value && cls.allow_luma)
	{
		GL_Bind(t->gl_texturenumfb);
		qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
		if (gl_mylumassuck.value)
			qglEnable(GL_ALPHA_TEST);

		PPL_EnableVertexArrays();
		qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
		qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);
		for (s = first; s ; s=s->texturechain)
		{
			PPL_GenerateArrays(s);
		}
		PPL_FlushArrays();

		if (gl_mylumassuck.value)
			qglDisable(GL_ALPHA_TEST);
	}
}

//requires multitexture
void PPL_BaseTextures(model_t *model)
{
	int		i;
	msurface_t	*s;
	texture_t	*t;

	currententity = &r_worldentity;

	GL_DoSwap();

	currententity->shaderRGBAf[0] = 1;
	currententity->shaderRGBAf[1] = 1;
	currententity->shaderRGBAf[2] = 1;
	currententity->shaderRGBAf[3] = 1;

	qglDisable(GL_BLEND);
	qglDisable(GL_ALPHA_TEST);
	qglColor4fv(currententity->shaderRGBAf);
//	qglDepthFunc(GL_LESS);

	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	qglShadeModel(GL_FLAT);

	overbright = 1;
	if (gl_overbright_all.value || (model->engineflags & MDLF_NEEDOVERBRIGHT))
	{
		if (gl_overbright.value>=2)
			overbright = 4;
		else if (gl_overbright.value)
			overbright = 2;
	}

	currentmodel = model;

	if (model == cl.worldmodel && skytexturenum>=0)
	{
		t = model->textures[skytexturenum];
		if (t)
		{
			s = t->texturechain;
			if (s)
			{
				t->texturechain = NULL;
				R_DrawSkyChain (s);
			}
		}
	}
	if (mirrortexturenum>=0 && model == cl.worldmodel && r_mirroralpha.value != 1.0)
	{
		t = model->textures[mirrortexturenum];
		if (t)
		{
			s = t->texturechain;
			if (s)
			{
				t->texturechain = NULL;
				if (!r_inmirror)
					R_MirrorChain (s);
			}
		}
	}

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

		if ((s->flags & SURF_DRAWTURB) && r_wateralphaval != 1.0)
			continue;	// draw translucent water later

		PPL_BaseTextureChain(s);
	}

	GL_DisableMultitexture();
}

void PPL_BaseBModelTextures(entity_t *e)
{
	extern msurface_t  *r_alpha_surfaces;
	int i, k;
	int shift;
	model_t *model;
	msurface_t *s;
	msurface_t *chain = NULL;

	qglPushMatrix();
	R_RotateForEntity(e);
	currentmodel = model = e->model;
	s = model->surfaces+model->firstmodelsurface;

	shift = GLR_LightmapShift(currentmodel);

	if (currententity->drawflags & DRF_TRANSLUCENT)
		currententity->shaderRGBAf[3]=0.5;
	if ((currententity->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT)
	{
		currententity->shaderRGBAf[0] =
		currententity->shaderRGBAf[1] =
		currententity->shaderRGBAf[2] = currententity->abslight/255.0f;
	}

	if (currententity->shaderRGBAf[3]<1)
	{
		GL_TexEnv(GL_MODULATE);
		qglEnable(GL_BLEND);
	}
	else
	{
		GL_TexEnv(GL_REPLACE);
		qglDisable(GL_BLEND);
	}

	qglColor4fv(currententity->shaderRGBAf);

	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// calculate dynamic lighting for bmodel if it's not an
// instanced model
	if (model->fromgame != fg_quake3)
	{
		if (currentmodel->nummodelsurfaces != 0 && r_dynamic.value)
		{
			for (k=0 ; k<MAX_SWLIGHTS ; k++)
			{
				if (!cl_dlights[k].radius)
					continue;
				if (cl_dlights[k].nodynamic)
					continue;

				currentmodel->funcs.MarkLights (&cl_dlights[k], 1<<k,
					currentmodel->nodes + currentmodel->hulls[0].firstclipnode);
			}
		}

//update lightmaps.
		for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)
			R_RenderDynamicLightmaps (s, shift);
	}


	for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)
	{
		if (s->texinfo->flags & SURF_TRANS33 || s->texinfo->flags & SURF_TRANS66)
		{
			s->ownerent = currententity;
			s->nextalphasurface = r_alpha_surfaces;
			r_alpha_surfaces = s;
			continue;
		}
		else if (chain && s->texinfo->texture != chain->texinfo->texture)	//last surface or not the same as the next
		{
			PPL_BaseTextureChain(chain);
			chain = NULL;
		}

		s->texturechain = chain;
		chain = s;
	}

	if (chain)
		PPL_BaseTextureChain(chain);

	qglPopMatrix();
	GL_DisableMultitexture();
	qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


	if (!varrayactive)
		R_IBrokeTheArrays();
}

#ifdef Q3SHADERS
void R_DrawLightning(entity_t *e)
{
	vec3_t v;
	vec3_t dir, cr;
	float scale = e->scale;
	float length;

	vec3_t points[4];
	vec2_t texcoords[4] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
	int indexarray[6] = {0, 1, 2, 0, 2, 3};

	mesh_t mesh;
	meshbuffer_t mb;

	if (!e->forcedshader)
		return;

	if (!scale)
		scale = 10;


	VectorSubtract(e->origin, e->oldorigin, dir);
	length = Length(dir);

	//this seems to be about right.
	texcoords[2][0] = length/128;
	texcoords[3][0] = length/128;

	VectorSubtract(r_refdef.vieworg, e->origin, v);
	CrossProduct(v, dir, cr);
	VectorNormalize(cr);

	VectorMA(e->origin, -scale/2, cr, points[0]);
	VectorMA(e->origin, scale/2, cr, points[1]);

	VectorSubtract(r_refdef.vieworg, e->oldorigin, v);
	CrossProduct(v, dir, cr);
	VectorNormalize(cr);

	VectorMA(e->oldorigin, scale/2, cr, points[2]);
	VectorMA(e->oldorigin, -scale/2, cr, points[3]);

	mesh.xyz_array = points;
	mesh.indexes = indexarray;
	mesh.numindexes = sizeof(indexarray)/sizeof(indexarray[0]);
	mesh.colors_array = NULL;
	mesh.lmst_array = NULL;
	mesh.normals_array = NULL;
	mesh.numvertexes = 4;
	mesh.st_array = texcoords;

	mb.entity = e;
	mb.mesh = &mesh;
	mb.shader = e->forcedshader;
	mb.infokey = 0;
	mb.fog = NULL;
	mb.infokey = currententity->keynum;
	mb.dlightbits = 0;


	R_IBrokeTheArrays();

	R_PushMesh(&mesh, mb.shader->features | MF_NONBATCHED);

	R_RenderMeshBuffer ( &mb, false );
}
void R_DrawRailCore(entity_t *e)
{
	vec3_t v;
	vec3_t dir, cr;
	float scale = e->scale;
	float length;

	mesh_t mesh;
	meshbuffer_t mb;
	vec3_t points[4];
	vec2_t texcoords[4] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
	int indexarray[6] = {0, 1, 2, 0, 2, 3};
	int colors[4];
	qbyte colorsb[4];

	if (!e->forcedshader)
		return;

	if (!scale)
		scale = 10;


	VectorSubtract(e->origin, e->oldorigin, dir);
	length = Length(dir);

	//this seems to be about right.
	texcoords[2][0] = length/128;
	texcoords[3][0] = length/128;

	VectorSubtract(r_refdef.vieworg, e->origin, v);
	CrossProduct(v, dir, cr);
	VectorNormalize(cr);

	VectorMA(e->origin, -scale/2, cr, points[0]);
	VectorMA(e->origin, scale/2, cr, points[1]);

	VectorSubtract(r_refdef.vieworg, e->oldorigin, v);
	CrossProduct(v, dir, cr);
	VectorNormalize(cr);

	VectorMA(e->oldorigin, scale/2, cr, points[2]);
	VectorMA(e->oldorigin, -scale/2, cr, points[3]);

	colorsb[0] = e->shaderRGBAf[0]*255;
	colorsb[1] = e->shaderRGBAf[1]*255;
	colorsb[2] = e->shaderRGBAf[2]*255;
	colorsb[3] = e->shaderRGBAf[3]*255;
	colors[0] = colors[1] = colors[2] = colors[3] = *(int*)colorsb;

	mesh.xyz_array = points;
	mesh.indexes = indexarray;
	mesh.numindexes = sizeof(indexarray)/sizeof(indexarray[0]);
	mesh.colors_array = (byte_vec4_t*)colors;
	mesh.lmst_array = NULL;
	mesh.normals_array = NULL;
	mesh.numvertexes = 4;
	mesh.st_array = texcoords;

	mb.entity = e;
	mb.mesh = &mesh;
	mb.shader = e->forcedshader;
	mb.infokey = 0;
	mb.fog = NULL;
	mb.infokey = currententity->keynum;
	mb.dlightbits = 0;


	R_IBrokeTheArrays();

	R_PushMesh(&mesh, mb.shader->features | MF_NONBATCHED | MF_COLORS);

	R_RenderMeshBuffer ( &mb, false );
}
#endif

void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees );
void PerpendicularVector( vec3_t dst, const vec3_t src );
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	points[NUM_BEAM_SEGS*2];
	vec3_t oldorigin, origin;
	float scale;

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

	scale = e->scale;
	if (!scale)
		scale = e->frame;
	if (!scale)
		scale = 6;
	VectorScale( perpvec, scale / 2, perpvec );

	for ( i = 0; i < 6; i++ )
	{
		RotatePointAroundVector( points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i );
		VectorAdd( points[i], origin, points[i] );
		VectorAdd( points[i], direction, points[i+NUM_BEAM_SEGS] );
	}

#ifdef Q3SHADERS
	if (e->forcedshader)
	{
		int indexarray[NUM_BEAM_SEGS*6];
		vec2_t texcoords[NUM_BEAM_SEGS*2];
		mesh_t mesh;
		meshbuffer_t mb;

		mesh.xyz_array = points;
		mesh.indexes = indexarray;
		mesh.numindexes = sizeof(indexarray)/sizeof(indexarray[0]);
		mesh.colors_array = NULL;
		mesh.lmst_array = NULL;
		mesh.normals_array = NULL;
		mesh.numvertexes = NUM_BEAM_SEGS*2;
		mesh.st_array = texcoords;

		mb.entity = e;
		mb.mesh = &mesh;
		mb.shader = e->forcedshader;
		mb.infokey = 0;
		mb.fog = NULL;
		mb.infokey = currententity->keynum;
		mb.dlightbits = 0;

		for (i = 0; i < NUM_BEAM_SEGS; i++)
		{
			indexarray[i*6+0] = i+0;
			indexarray[i*6+1] = (i+1)%NUM_BEAM_SEGS;
			indexarray[i*6+2] = indexarray[i*6+1]+NUM_BEAM_SEGS;

			indexarray[i*6+3] = indexarray[i*6+0];
			indexarray[i*6+4] = indexarray[i*6+2];
			indexarray[i*6+5] = i+0+NUM_BEAM_SEGS;

			texcoords[i][1] = (float)i/NUM_BEAM_SEGS+0.35;
			texcoords[i][0] = 0;
			texcoords[i+NUM_BEAM_SEGS][1] = (float)i/NUM_BEAM_SEGS+0.35;
			texcoords[i+NUM_BEAM_SEGS][0] = 1;
		}

		R_IBrokeTheArrays();

		R_PushMesh(&mesh, mb.shader->features | MF_NONBATCHED);

		R_RenderMeshBuffer ( &mb, false );
	}
	else
#endif
	{
		qglDisable( GL_TEXTURE_2D );
		qglEnable( GL_BLEND );
		qglDepthMask( GL_FALSE );
		qglDisable(GL_ALPHA_TEST);

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

		r *= e->shaderRGBAf[0]/255.0F;
		g *= e->shaderRGBAf[1]/255.0F;
		b *= e->shaderRGBAf[2]/255.0F;

		qglColor4f( r, g, b, e->shaderRGBAf[3] );

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

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

void PPL_DrawEnt(entity_t *e, void *parm)
{
	qglEnd();
	currententity = e;

	qglDepthMask(1);
	qglDisable(GL_POLYGON_OFFSET_FILL);
	
		R_IBrokeTheArrays();
		R_DrawGAliasModel (currententity);

	P_FlushRenderer();
	qglBegin(GL_QUADS);
}

void PPL_BaseEntTextures(void)
{
	extern model_t *currentmodel;
	int		i;

	if (!r_drawentities.value)
		return;

	// draw sprites seperately, because of alpha blending
	for (i=0 ; i<cl_numvisedicts ; i++)
	{
		currententity = &cl_visedicts[i];

		if (!PPL_ShouldDraw())
			continue;

		if (currententity->rtype)
			continue;
		if (currententity->flags & Q2RF_BEAM)
		{
			R_DrawBeam(currententity);
			continue;
		}
		if (!currententity->model)
			continue;

		if (cls.allow_anyparticles || currententity->visframe)	//allowed or static
		{
			if (currententity->model->engineflags & MDLF_ENGULPHS)
			{
				if (gl_part_flame.value)
					continue;
			}
		}

		switch (currententity->model->type)
		{
			//FIXME: We want to depth sort with particles, but we also want depth. :(
			//Until then, we have broken model lighting.
		case mod_alias:
			R_DrawGAliasModel (currententity);
//			if (currententity->flags & Q2RF_WEAPONMODEL)
//				RQ_AddDistReorder(PPL_DrawEnt, currententity, NULL, r_refdef.vieworg);
//			else
//				RQ_AddDistReorder(PPL_DrawEnt, currententity, NULL, currententity->origin);
			break;

		case mod_brush:
			qglDepthFunc ( gldepthfunc );
			qglEnable(GL_DEPTH_TEST);
			qglDepthMask(1);
			PPL_BaseBModelTextures (currententity);
			break;

		default:
			break;
		}
	}

	currentmodel = cl.worldmodel;
}

#ifdef PPL
static void PPL_GenerateLightArrays(msurface_t *surf, vec3_t relativelightorigin, dlight_t *light, vec3_t colour)
{
	int vi;
	float *v, *stw;
	surfvertexarray_t *out;

	vec3_t lightdir;
	float dist;

	shadowlightfaces++;

	v = surf->mesh->xyz_array[0];
	stw = surf->mesh->st_array[0];

	out = &varray_v[0];

	for (vi=0 ; vi<surf->mesh->numvertexes ; vi++, v+=3, stw+=2, out++)
	{
		out->stw[0] = stw[0];
		out->stw[1] = stw[1];
		lightdir[0] = relativelightorigin[0] - v[0];
		lightdir[1] = relativelightorigin[1] - v[1];
		lightdir[2] = relativelightorigin[2] - v[2];
		dist = 1-(sqrt(	(lightdir[0])*(lightdir[0]) +
						(lightdir[1])*(lightdir[1]) +
						(lightdir[2])*(lightdir[2])) / light->radius);
		VectorNormalize(lightdir);
		out->stl[0] = colour[0]*dist;
		out->stl[1] = colour[1]*dist;
		out->stl[2] = colour[2]*dist;
		out->ncm[0] = DotProduct(lightdir, surf->texinfo->vecs[0]);
		out->ncm[1] = -DotProduct(lightdir, surf->texinfo->vecs[1]);
		if (surf->flags & SURF_PLANEBACK)
			out->ncm[2] = -DotProduct(lightdir, surf->plane->normal);
		else
			out->ncm[2] = DotProduct(lightdir, surf->plane->normal);
	}
}

//flags
enum{
PERMUTATION_GENERIC = 0,
PERMUTATION_BUMPMAP = 1,
PERMUTATION_SPECULAR = 2,
PERMUTATION_BUMP_SPEC = 3,
PERMUTATION_OFFSET = 4,
PERMUTATION_OFFSET_BUMP = 5,
PERMUTATION_OFFSET_SPEC = 6,
PERMUTATION_OFFSET_BUMP_SPEC = 7,

PERMUTATIONS
};
int ppl_light_shader[PERMUTATIONS];
int ppl_light_shader_eyeposition[PERMUTATIONS];
int ppl_light_shader_lightposition[PERMUTATIONS];
int ppl_light_shader_lightcolour[PERMUTATIONS];
int ppl_light_shader_lightradius[PERMUTATIONS];
int ppl_light_shader_offset_scale[PERMUTATIONS];
int ppl_light_shader_offset_bias[PERMUTATIONS];

void PPL_CreateLightTexturesProgram(void)
{
	int i;

	char *permutation[PERMUTATIONS] = {
		"",
		"#define BUMP\n",
		"#define SPECULAR\n",
		"#define SPECULAR\n#define BUMP\n",
		"#define USEOFFSETMAPPING\n",
		"#define USEOFFSETMAPPING\n#define BUMP\n",
		"#define USEOFFSETMAPPING\n#define SPECULAR\n",
		"#define USEOFFSETMAPPING\n#define SPECULAR\n#define BUMP\n"
	};
	char *vert = 
		"varying vec2 tcbase;\n"
		"uniform vec3 texr, texu, texf;\n"
		"varying vec3 LightVector;\n"
		"uniform vec3 LightPosition;\n"

		"#if defined(SPECULAR) || defined(USEOFFSETMAPPING)\n"
		"uniform vec3 EyePosition;\n"
		"varying vec3 EyeVector;\n"
		"#endif\n"

		"void main (void)\n"
		"{\n"
		"	gl_Position = ftransform();\n"

		"	tcbase = gl_MultiTexCoord0.xy;\n"	//pass the texture coords straight through

		"	vec3 lightminusvertex = LightPosition - gl_Vertex.xyz;\n"
		"	LightVector.x = dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
		"	LightVector.y = dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
		"	LightVector.z = dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"

		"#if defined(SPECULAR)||defined(USEOFFSETMAPPING)\n"
		"	vec3 eyeminusvertex = EyePosition - gl_Vertex.xyz;\n"
		"	EyeVector.x = dot(eyeminusvertex, gl_MultiTexCoord1.xyz);\n"
		"	EyeVector.y = dot(eyeminusvertex, gl_MultiTexCoord2.xyz);\n"
		"	EyeVector.z = dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
		"#endif\n"
		"}\n"
		;

	char *frag =
		"uniform sampler2D baset;\n"
		"#if defined(BUMP) || defined(SPECULAR) || defined(USEOFFSETMAPPING)\n"
		"uniform sampler2D bumpt;\n"
		"#endif\n"
		"#ifdef SPECULAR\n"
		"uniform sampler2D speculart;\n"
		"#endif\n"

		"varying vec2 tcbase;\n"
		"varying vec3 LightVector;\n"

		"uniform float lightradius;\n"
		"uniform vec3 LightColour;\n"

		"#if defined(SPECULAR) || defined(USEOFFSETMAPPING)\n"
		"varying vec3 EyeVector;\n"
		"#endif\n"

"#ifdef USEOFFSETMAPPING\n"
"uniform float OffsetMapping_Scale;\n"
"uniform float OffsetMapping_Bias;\n"
"#endif\n"


		"void main (void)\n"
		"{\n"
"#ifdef USEOFFSETMAPPING\n"
"	// this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
"	vec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
"	vec2 TexCoordOffset = tcbase + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(bumpt, tcbase).w);\n"
"	TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(bumpt, TexCoordOffset).w);\n"
"	TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(bumpt, TexCoordOffset).w);\n"
"#define tcbase TexCoordOffset\n"
"#endif\n"


		"#ifdef BUMP\n"
		"	vec3 bases = vec3(texture2D(baset, tcbase));\n"
		"#else\n"
		"	vec3 diff = vec3(texture2D(baset, tcbase));\n"
		"#endif\n"
		"#if defined(BUMP) || defined(SPECULAR)\n"
		"	vec3 bumps = vec3(texture2D(bumpt, tcbase)) * 2.0 - 1.0;\n"
		"#endif\n"
		"#ifdef SPECULAR\n"
		"	vec3 specs = vec3(texture2D(speculart, tcbase));\n"
		"#endif\n"

		"	vec3 nl = normalize(LightVector);\n"
		"	float colorscale = max(1.0 - dot(LightVector, LightVector)/(lightradius*lightradius), 0.0);\n"

		"#ifdef BUMP\n"
		"	vec3 diff;\n"
		"	diff = bases * max(dot(bumps, nl), 0.0);\n"
		"#endif\n"
		"#ifdef SPECULAR\n"
		"	vec3 halfdir = (normalize(EyeVector) + normalize(LightVector))/2.0;\n"
		"	float dv = dot(halfdir, bumps);\n"
		"	diff += pow(dv, 8.0) * specs;\n"
		"#endif\n"
		"	gl_FragColor.rgb = diff*colorscale*LightColour;\n"
		"}\n"
		;

	for (i = 0; i < PERMUTATIONS; i++)
	{
		ppl_light_shader[i] = GLSlang_CreateProgram(permutation[i], vert, frag);

		if (ppl_light_shader[i])
		{
			GLSlang_UseProgram(ppl_light_shader[i]);

			qglUniform1iARB(qglGetUniformLocationARB(ppl_light_shader[i], "baset"), 0);
			qglUniform1iARB(qglGetUniformLocationARB(ppl_light_shader[i], "bumpt"), 1);
			qglUniform1iARB(qglGetUniformLocationARB(ppl_light_shader[i], "speculart"), 2);

			ppl_light_shader_eyeposition[i] = qglGetUniformLocationARB(ppl_light_shader[i], "EyePosition");
			ppl_light_shader_lightposition [i]= qglGetUniformLocationARB(ppl_light_shader[i], "LightPosition");
			ppl_light_shader_lightcolour[i] = qglGetUniformLocationARB(ppl_light_shader[i], "LightColour");
			ppl_light_shader_lightradius[i] = qglGetUniformLocationARB(ppl_light_shader[i], "lightradius");
			ppl_light_shader_offset_scale[i] = qglGetUniformLocationARB(ppl_light_shader[i], "OffsetMapping_Scale");
			ppl_light_shader_offset_bias[i] = qglGetUniformLocationARB(ppl_light_shader[i], "OffsetMapping_Bias");

			GLSlang_UseProgram(0);
		}
	}
};


void PPL_LightTexturesFP_Cached(model_t *model, vec3_t modelorigin, dlight_t *light, vec3_t colour)
{
	int i, j;
	texture_t	*t;
	msurface_t	*s;
	int p, lp=-1;
	extern cvar_t gl_specular;
	shadowmesh_t *shm = light->worldshadowmesh;
	

	vec3_t relativelightorigin;
	vec3_t relativeeyeorigin;

	if (qglGetError())
		Con_Printf("GL Error before lighttextures\n");

	VectorSubtract(light->origin, modelorigin, relativelightorigin);
	VectorSubtract(r_refdef.vieworg, modelorigin, relativeeyeorigin);

	qglEnable(GL_BLEND);
	GL_TexEnv(GL_MODULATE);
	qglBlendFunc(GL_ONE, GL_ONE);
	qglDisableClientState(GL_COLOR_ARRAY);
	if (qglGetError())
		Con_Printf("GL Error early in lighttextures\n");

	for (j=0 ; j<shm->numsurftextures ; j++)
	{
		if (!shm->litsurfs[j].count)
			continue;

		s = shm->litsurfs[j].s[0];
		t = s->texinfo->texture;
		t = GLR_TextureAnimation (t);

		for (i=0 ; i<shm->litsurfs[j].count ; i++)
		{
			s = shm->litsurfs[j].s[i];

			if (s->visframe != r_framecount)
				continue;

			if (s->flags & SURF_PLANEBACK)
			{//inverted normal.
				if (DotProduct(s->plane->normal, relativelightorigin)-s->plane->dist > lightradius)
					continue;
			}
			else
			{
				if (-DotProduct(s->plane->normal, relativelightorigin)+s->plane->dist > lightradius)
					continue;
			}

	//		if ((s->flags & SURF_DRAWTURB) && r_wateralphaval != 1.0)
	//			continue;	// draw translucent water later



			p = 0;
			if (t->gl_texturenumbumpmap && ppl_light_shader[p|PERMUTATION_BUMPMAP])
				p |= PERMUTATION_BUMPMAP;
			if (gl_specular.value && t->gl_texturenumspec && ppl_light_shader[p|PERMUTATION_SPECULAR])
				p |= PERMUTATION_SPECULAR;
			if (r_shadow_glsl_offsetmapping.value && t->gl_texturenumbumpmap && ppl_light_shader[p|PERMUTATION_OFFSET])
				p |= PERMUTATION_OFFSET;

			if (p != lp)
			{
				lp = p;
				GLSlang_UseProgram(ppl_light_shader[p]);
				if (ppl_light_shader_eyeposition[p] != -1)
					qglUniform3fvARB(ppl_light_shader_eyeposition[p], 1, relativeeyeorigin);
				qglUniform3fvARB(ppl_light_shader_lightposition[p], 1, relativelightorigin);
				qglUniform3fvARB(ppl_light_shader_lightcolour[p], 1, colour);
				qglUniform1fARB(ppl_light_shader_lightradius[p], light->radius);

				if (ppl_light_shader_offset_scale[p]!=-1)
					qglUniform1fARB(ppl_light_shader_offset_scale[p], r_shadow_glsl_offsetmapping_scale.value);
				if (ppl_light_shader_offset_bias[p]!=-1)
					qglUniform1fARB(ppl_light_shader_offset_bias[p], r_shadow_glsl_offsetmapping_bias.value);
			}


			if (p & PERMUTATION_BUMPMAP)
				GL_MBind(GL_TEXTURE1_ARB, t->gl_texturenumbumpmap);
			if (p & PERMUTATION_SPECULAR)
				GL_MBind(GL_TEXTURE2_ARB, t->gl_texturenumspec);

			GL_MBind(GL_TEXTURE0_ARB, t->gl_texturenum);
			qglEnableClientState(GL_TEXTURE_COORD_ARRAY);



			qglMultiTexCoord3fARB(GL_TEXTURE1_ARB, s->texinfo->vecs[0][0], s->texinfo->vecs[0][1], s->texinfo->vecs[0][2]);
			qglMultiTexCoord3fARB(GL_TEXTURE2_ARB, -s->texinfo->vecs[1][0], -s->texinfo->vecs[1][1], -s->texinfo->vecs[1][2]);

			if (s->flags & SURF_PLANEBACK)
				qglMultiTexCoord3fARB(GL_TEXTURE3_ARB, -s->plane->normal[0], -s->plane->normal[1], -s->plane->normal[2]);
			else
				qglMultiTexCoord3fARB(GL_TEXTURE3_ARB, s->plane->normal[0], s->plane->normal[1], s->plane->normal[2]);

			qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->st_array);

			qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);
			qglDrawElements(GL_TRIANGLES, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
		}
	}
	GLSlang_UseProgram(0);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	if (qglGetError())
		Con_Printf("GL Error during lighttextures\n");
}

void PPL_LightTexturesFP(model_t *model, vec3_t modelorigin, dlight_t *light, vec3_t colour)
{
	int i;
	texture_t	*t;
	msurface_t	*s;
	int p, lp=-1;
	extern cvar_t gl_specular;

	vec3_t relativelightorigin;
	vec3_t relativeeyeorigin;

	if (qglGetError())
		Con_Printf("GL Error before lighttextures\n");

	VectorSubtract(light->origin, modelorigin, relativelightorigin);
	VectorSubtract(r_refdef.vieworg, modelorigin, relativeeyeorigin);

	qglEnable(GL_BLEND);
	GL_TexEnv(GL_MODULATE);
	qglBlendFunc(GL_ONE, GL_ONE);
	qglDisableClientState(GL_COLOR_ARRAY);
	if (qglGetError())
		Con_Printf("GL Error early in lighttextures\n");

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

//		if ((s->flags & SURF_DRAWTURB) && r_wateralphaval != 1.0)
//			continue;	// draw translucent water later

		t = GLR_TextureAnimation (t);


		p = 0;
		if (t->gl_texturenumbumpmap && ppl_light_shader[p|PERMUTATION_BUMPMAP])
			p |= PERMUTATION_BUMPMAP;
		if (gl_specular.value && t->gl_texturenumspec && ppl_light_shader[p|PERMUTATION_SPECULAR])
			p |= PERMUTATION_SPECULAR;

		if (p != lp)
		{
			lp = p;
			GLSlang_UseProgram(ppl_light_shader[p]);
			if (ppl_light_shader_eyeposition[p] != -1)
				qglUniform3fvARB(ppl_light_shader_eyeposition[p], 1, relativeeyeorigin);
			qglUniform3fvARB(ppl_light_shader_lightposition[p], 1, relativelightorigin);
			qglUniform3fvARB(ppl_light_shader_lightcolour[p], 1, colour);
			qglUniform1fARB(ppl_light_shader_lightradius[p], light->radius);
		}


		if (p & PERMUTATION_BUMPMAP)
			GL_MBind(GL_TEXTURE1_ARB, t->gl_texturenumbumpmap);
		if (p & PERMUTATION_SPECULAR)
			GL_MBind(GL_TEXTURE2_ARB, t->gl_texturenumspec);

		GL_MBind(GL_TEXTURE0_ARB, t->gl_texturenum);
		qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

		for (; s; s=s->texturechain)
		{
			if (s->shadowframe != r_shadowframe)
				continue;

			if (s->flags & SURF_PLANEBACK)
			{//inverted normal.
				if (DotProduct(s->plane->normal, relativelightorigin)-s->plane->dist > lightradius)
					continue;
			}
			else
			{
				if (-DotProduct(s->plane->normal, relativelightorigin)+s->plane->dist > lightradius)
					continue;
			}

			qglMultiTexCoord3fARB(GL_TEXTURE1_ARB, s->texinfo->vecs[0][0], s->texinfo->vecs[0][1], s->texinfo->vecs[0][2]);
			qglMultiTexCoord3fARB(GL_TEXTURE2_ARB, -s->texinfo->vecs[1][0], -s->texinfo->vecs[1][1], -s->texinfo->vecs[1][2]);

			if (s->flags & SURF_PLANEBACK)
				qglMultiTexCoord3fARB(GL_TEXTURE3_ARB, -s->plane->normal[0], -s->plane->normal[1], -s->plane->normal[2]);
			else
				qglMultiTexCoord3fARB(GL_TEXTURE3_ARB, s->plane->normal[0], s->plane->normal[1], s->plane->normal[2]);

			qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->st_array);

			qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);
			qglDrawElements(GL_TRIANGLES, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
		}

	}
	GLSlang_UseProgram(0);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	if (qglGetError())
		Con_Printf("GL Error during lighttextures\n");
}

void PPL_LightTextures(model_t *model, vec3_t modelorigin, dlight_t *light, vec3_t colour)
{
	int		i;
	msurface_t	*s;
	texture_t	*t;

	vec3_t relativelightorigin;

	if (ppl_light_shader[0])
	{
		if (model == cl.worldmodel && light->worldshadowmesh)
			PPL_LightTexturesFP_Cached(model, modelorigin, light, colour);
		else
			PPL_LightTexturesFP(model, modelorigin, light, colour);
		return;
	}

	PPL_EnableVertexArrays();

	VectorSubtract(light->origin, modelorigin, relativelightorigin);
	qglShadeModel(GL_SMOOTH);
	for (i=0 ; i<model->numtextures ; i++)
	{
		t = model->textures[i];
		if (!t)
			continue;
		s = t->texturechain;
		if (!s)
			continue;

		if ((s->flags & SURF_DRAWTURB) && r_wateralphaval != 1.0)
			continue;	// draw translucent water later


		{
			extern int normalisationCubeMap;

			t = GLR_TextureAnimation (t);


			qglEnableClientState(GL_COLOR_ARRAY);
			qglColorPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);
			if (t->gl_texturenumbumpmap && gl_mtexarbable>3)
			{
				GL_MBind(GL_TEXTURE0_ARB, t->gl_texturenumbumpmap);
				qglEnable(GL_TEXTURE_2D);
				//Set up texture environment to do (tex0 dot tex1)*color
				GL_TexEnv(GL_REPLACE);	//make texture normalmap available.

				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

				GL_SelectTexture(GL_TEXTURE1_ARB);
				GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap);
				qglEnable(GL_TEXTURE_CUBE_MAP_ARB);
				GL_TexEnv(GL_COMBINE_ARB);	//normalisation cubemap . normalmap
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
				qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);

				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->ncm);

				GL_MBind(GL_TEXTURE2_ARB, t->gl_texturenumbumpmap);	//a dummy
				qglEnable(GL_TEXTURE_2D);
				GL_TexEnv(GL_COMBINE_ARB);	//bumps * color		(the attenuation)
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); //(doesn't actually use the bound texture)
				qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
				qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);

				GL_MBind(GL_TEXTURE3_ARB, t->gl_texturenum);
				qglEnable(GL_TEXTURE_2D);
				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);
			}
			else
			{
				if (gl_mtexarbable>3)
				{
					GL_TexEnv(GL_MODULATE);
					qglDisable(GL_TEXTURE_2D);
					qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

					GL_SelectTexture(GL_TEXTURE2_ARB);
					qglDisable(GL_TEXTURE_2D);
					qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
				}
				GL_SelectTexture(GL_TEXTURE1_ARB);
				GL_TexEnv(GL_MODULATE);
				qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
				qglDisable(GL_TEXTURE_CUBE_MAP_ARB);

				GL_SelectTexture(GL_TEXTURE0_ARB);
				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);
				qglDisable(GL_TEXTURE_2D);
			}

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

				if (s->shadowframe != r_shadowframe)
					continue;

				if (s->flags & SURF_PLANEBACK)
				{//inverted normal.
					if (DotProduct(s->plane->normal, relativelightorigin)-s->plane->dist > lightradius)
						continue;
				}
				else
				{
					if (-DotProduct(s->plane->normal, relativelightorigin)+s->plane->dist > lightradius)
						continue;
				}

				PPL_GenerateLightArrays(s, relativelightorigin, light, colour);

				qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);
				qglDrawElements(GL_TRIANGLES, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
				varray_ic = 0;
				varray_vc = 0;

			}
		}
	}

	if (gl_mtexarbable>2)
	{
		GL_TexEnv(GL_MODULATE);
		qglDisable(GL_TEXTURE_2D);
		qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

		GL_SelectTexture(GL_TEXTURE2_ARB);
		qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	GL_TexEnv(GL_MODULATE);
	qglDisable(GL_TEXTURE_2D);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	GL_TexEnv(GL_MODULATE);
	qglDisable(GL_TEXTURE_CUBE_MAP_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	GL_SelectTexture(GL_TEXTURE0_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisable(GL_TEXTURE_2D);

}

void PPL_LightBModelTexturesFP(entity_t *e, dlight_t *light, vec3_t colour)
{
	int i;
	texture_t	*t;
	msurface_t	*s;
	model_t *model = e->model;
	texture_t *tnum = NULL;
	int p, lp = -1;

	vec3_t relativelightorigin;
	vec3_t relativeeyeorigin;



	if (qglGetError())
		Con_Printf("GL Error before lighttextures\n");
//Fixme: rotate
	VectorSubtract(light->origin, e->origin, relativelightorigin);
	VectorSubtract(r_refdef.vieworg, e->origin, relativeeyeorigin);

	qglEnable(GL_BLEND);
	qglBlendFunc(GL_ONE, GL_ONE);

	if (qglGetError())
		Con_Printf("GL Error early in lighttextures\n");

	for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)
	{
		if (tnum != s->texinfo->texture)
		{
			tnum = s->texinfo->texture;

			t = GLR_TextureAnimation (tnum);

			p = 0;
			if (t->gl_texturenumbumpmap && ppl_light_shader[p|PERMUTATION_BUMPMAP])
				p |= PERMUTATION_BUMPMAP;
			if (gl_specular.value && t->gl_texturenumspec && ppl_light_shader[p|PERMUTATION_SPECULAR])
				p |= PERMUTATION_SPECULAR;
			if (p != lp)
			{
				lp = p;
				GLSlang_UseProgram(ppl_light_shader[p]);
				if (ppl_light_shader_eyeposition[p] != -1)
					qglUniform3fvARB(ppl_light_shader_eyeposition[p], 1, relativeeyeorigin);
				qglUniform3fvARB(ppl_light_shader_lightposition[p], 1, relativelightorigin);
				qglUniform3fvARB(ppl_light_shader_lightcolour[p], 1, colour);
				qglUniform1fARB(ppl_light_shader_lightradius[p], light->radius);
			}

			GL_MBind(GL_TEXTURE0_ARB, t->gl_texturenum);
			qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
			GL_MBind(GL_TEXTURE1_ARB, t->gl_texturenumbumpmap);
			GL_MBind(GL_TEXTURE2_ARB, t->gl_texturenumspec);
			GL_SelectTexture(GL_TEXTURE0_ARB);
		}

		qglMultiTexCoord3fARB(GL_TEXTURE1_ARB, -s->texinfo->vecs[0][0], -s->texinfo->vecs[0][1], -s->texinfo->vecs[0][2]);
		qglMultiTexCoord3fARB(GL_TEXTURE2_ARB, s->texinfo->vecs[1][0], s->texinfo->vecs[1][1], s->texinfo->vecs[1][2]);

		if (s->flags & SURF_PLANEBACK)
			qglMultiTexCoord3fARB(GL_TEXTURE3_ARB, -s->plane->normal[0], -s->plane->normal[1], -s->plane->normal[2]);
		else
			qglMultiTexCoord3fARB(GL_TEXTURE3_ARB, s->plane->normal[0], s->plane->normal[1], s->plane->normal[2]);

		qglTexCoordPointer(2, GL_FLOAT, 0, s->mesh->st_array);
		qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);
		qglDrawElements(GL_TRIANGLES, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);


	}
	GLSlang_UseProgram(0);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	if (qglGetError())
		Con_Printf("GL Error during lighttextures\n");
}

void PPL_LightBModelTextures(entity_t *e, dlight_t *light, vec3_t colour)
{
	int i;
	model_t *model = e->model;

	msurface_t	*s;
	texture_t	*t;

	vec3_t relativelightorigin;

	qglPushMatrix();
	R_RotateForEntity(e);

	if (ppl_light_shader[0])
	{
		PPL_LightBModelTexturesFP(e, light, colour);
		qglPopMatrix();
		return;
	}

	qglColor4f(1, 1, 1, 1);

	PPL_EnableVertexArrays();


		VectorSubtract(light->origin, e->origin, relativelightorigin);
		qglShadeModel(GL_SMOOTH);

		for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)
		{
			t = GLR_TextureAnimation (s->texinfo->texture);

			qglEnableClientState(GL_COLOR_ARRAY);
			qglColorPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stl);
			if (t->gl_texturenumbumpmap && gl_mtexarbable>3)
			{
				GL_MBind(GL_TEXTURE0_ARB, t->gl_texturenumbumpmap);
				qglEnable(GL_TEXTURE_2D);
				//Set up texture environment to do (tex0 dot tex1)*color
				GL_TexEnv(GL_REPLACE);	//make texture normalmap available.

				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);

				GL_SelectTexture(GL_TEXTURE1_ARB);
				GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap);
				qglEnable(GL_TEXTURE_CUBE_MAP_ARB);
				GL_TexEnv(GL_COMBINE_ARB);	//normalisation cubemap * normalmap
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
				qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);

				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->ncm);

				GL_MBind(GL_TEXTURE2_ARB, t->gl_texturenumbumpmap);
				qglEnable(GL_TEXTURE_2D);
				GL_TexEnv(GL_COMBINE_ARB);	//bumps * color		(the attenuation)
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); //(doesn't actually use the bound texture)
				qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
				qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
				qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);

				GL_MBind(GL_TEXTURE3_ARB, t->gl_texturenum);
				qglEnable(GL_TEXTURE_2D);
				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);
			}
			else
			{
				if (gl_mtexarbable>3)
				{
					GL_TexEnv(GL_MODULATE);
					qglDisable(GL_TEXTURE_2D);
					qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

					GL_SelectTexture(GL_TEXTURE2_ARB);
					qglDisable(GL_TEXTURE_2D);
					qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
				}

				GL_TexEnv(GL_MODULATE);
				qglDisable(GL_TEXTURE_2D);
				GL_SelectTexture(GL_TEXTURE1_ARB);
				GL_TexEnv(GL_MODULATE);
				qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
				qglDisable(GL_TEXTURE_CUBE_MAP_ARB);
				GL_SelectTexture(GL_TEXTURE0_ARB);


				qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
				qglTexCoordPointer(2, GL_FLOAT, sizeof(surfvertexarray_t), varray_v->stw);
			}

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

//				if (s->shadowframe != r_shadowframe)
//					continue;

			/*	if (fabs(s->center[0] - lightorg[0]) > lightradius+s->radius ||
					fabs(s->center[1] - lightorg[1]) > lightradius+s->radius ||
					fabs(s->center[2] - lightorg[2]) > lightradius+s->radius)
					continue;*/


				if (s->flags & SURF_PLANEBACK)
				{//inverted normal.
					if (-DotProduct(s->plane->normal, relativelightorigin)+s->plane->dist > lightradius)
						continue;
				}
				else
				{
					if (DotProduct(s->plane->normal, relativelightorigin)-s->plane->dist > lightradius)
						continue;
				}
				PPL_GenerateLightArrays(s, relativelightorigin, light, colour);

				qglVertexPointer(3, GL_FLOAT, 0, s->mesh->xyz_array);
				qglDrawElements(GL_TRIANGLES, s->mesh->numindexes, GL_UNSIGNED_INT, s->mesh->indexes);
				varray_ic = 0;
				varray_vc = 0;
			}
			PPL_FlushArrays();
		}

	if (gl_mtexarbable>2)
	{
		GL_TexEnv(GL_MODULATE);
		qglDisable(GL_TEXTURE_2D);
		qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

		GL_SelectTexture(GL_TEXTURE2_ARB);
		qglDisable(GL_TEXTURE_2D);
		qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	GL_TexEnv(GL_MODULATE);
	qglDisable(GL_TEXTURE_2D);

	GL_SelectTexture(GL_TEXTURE1_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	GL_TexEnv(GL_MODULATE);
	qglDisable(GL_TEXTURE_CUBE_MAP_ARB);

	GL_SelectTexture(GL_TEXTURE0_ARB);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisable(GL_TEXTURE_2D);

	qglPopMatrix();
}

//draw the bumps on the models for each light.
void PPL_DrawEntLighting(dlight_t *light, vec3_t colour)
{
	int i;

	PPL_LightTextures(cl.worldmodel, r_worldentity.origin, light, colour);

	if (!r_drawentities.value)
		return;

	for (i=0 ; i<cl_numvisedicts ; i++)
	{
		currententity = &cl_visedicts[i];

		if (!PPL_ShouldDraw())
			continue;
		if (r_inmirror)
		{
			if (currententity->flags & Q2RF_WEAPONMODEL)
				continue;
		}
		else
		{
			if (currententity->keynum == (cl.viewentity[r_refdef.currentplayernum]?cl.viewentity[r_refdef.currentplayernum]:(cl.playernum[r_refdef.currentplayernum]+1)))
				continue;
//			if (cl.viewentity[r_refdef.currentplayernum] && currententity->keynum == cl.viewentity[r_refdef.currentplayernum])
//				continue;
			if (!Cam_DrawPlayer(0, currententity->keynum-1))
				continue;
		}

		if (currententity->flags & Q2RF_BEAM)
		{
			continue;
		}
		if (!currententity->model)
			continue;

		switch (currententity->model->type)
		{
		case mod_alias:
			if (!varrayactive)
				R_IBrokeTheArrays();
			R_DrawGAliasModelLighting (currententity, light->origin, colour, light->radius);
			break;

		case mod_brush:
			PPL_LightBModelTextures (currententity, light, colour);
			break;

		default:
			break;
		}
	}
}
#endif

void PPL_FullBrights(model_t *model)
{
	int		tn;
	msurface_t	*s;
	texture_t	*t;

	qglColor3f(1,1,1);

	qglDepthMask(0);	//don't bother writing depth

	GL_TexEnv(GL_MODULATE);

	qglShadeModel(GL_FLAT);

	qglEnable(GL_BLEND);
	qglEnable(GL_TEXTURE_2D);

	for (tn=0 ; tn<model->numtextures ; tn++)
	{
		t = model->textures[tn];
		if (!t)
			continue;
		s = t->texturechain;
		if (!s)
			continue;

		if ((s->flags & SURF_DRAWTURB) && r_wateralphaval != 1.0)
			continue;	// draw translucent water later

		PPL_FullBrightTextureChain(s);

		t->texturechain=NULL;
	}

	GL_TexEnv(GL_REPLACE);
	qglDepthMask(1);
}

void PPL_FullBrightBModelTextures(entity_t *e)
{
	int i;
	model_t *model;
	msurface_t *s;
	msurface_t *chain = NULL;

	qglPushMatrix();
	R_RotateForEntity(e);
	currentmodel = model = e->model;
	s = model->surfaces+model->firstmodelsurface;

	qglColor4f(1, 1, 1, 1);
	qglDepthMask(0);	//don't bother writing depth

	GL_TexEnv(GL_MODULATE);
	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	qglShadeModel(GL_FLAT);

	qglEnable(GL_BLEND);
	qglEnable(GL_TEXTURE_2D);

	for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)
	{
		if (chain && s->texinfo->texture != chain->texinfo->texture)	//last surface or not the same as the next
		{
			PPL_FullBrightTextureChain(chain);
			chain = NULL;
		}

		s->texturechain = chain;
		chain = s;
	}

	if (chain)
		PPL_FullBrightTextureChain(chain);

	qglPopMatrix();
	qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	qglDepthMask(1);
}

//draw the bumps on the models for each light.
void PPL_DrawEntFullBrights(void)
{
	int		i;

	currententity = &r_worldentity;
//	if (gl_detail.value || (r_fb_bmodels.value && cls.allow_luma))
		PPL_FullBrights(cl.worldmodel);

	if (!r_drawentities.value)
		return;

	for (i=0 ; i<cl_numvisedicts ; i++)
	{
		currententity = &cl_visedicts[i];

		if (cl.viewentity[r_refdef.currentplayernum] && currententity->keynum == cl.viewentity[r_refdef.currentplayernum])
			continue;

		if (!Cam_DrawPlayer(0, currententity->keynum-1))
			continue;

		if (currententity->flags & Q2RF_BEAM)
			continue;

		if (!currententity->model)
			continue;

		if (cls.allow_anyparticles || currententity->visframe)	//allowed or static
		{
			if (currententity->model->engineflags & MDLF_ENGULPHS)
			{
				if (gl_part_flame.value)
					continue;
			}
		}

		switch (currententity->model->type)
		{
		case mod_alias:
//			R_DrawGAliasModelLighting (currententity);
			break;

		case mod_brush:
			PPL_FullBrightBModelTextures (currententity);
			break;

		default:
			break;
		}
	}
}


void PPL_SchematicsTextureChain(msurface_t *first)
{
	extern int char_texture;
	msurface_t *s;
	float *v1, *v2;
	float len;
	unsigned char str[64];
	int sl, c;
	vec3_t dir;
	vec3_t pos, v;
	const float size = 0.0625;
	float frow, fcol;
	int e, en;

	if (!cl.worldmodel->surfedges)
		return;

	qglEnable(GL_ALPHA_TEST);

	if (qglPolygonOffset)
		qglPolygonOffset(-1, 0);

	frow = rand()/(float)RAND_MAX;
	frow=frow/2+0.5;
	qglColor3f(frow, frow, 0);

	//draw the distances
	if (gl_schematics.value != 2)
	{
		qglEnable(GL_POLYGON_OFFSET_FILL);

		qglEnable(GL_TEXTURE_2D);
		GL_Bind(char_texture);

		qglBegin(GL_QUADS);
		for (s = first; s ; s=s->texturechain)
		{
			for (e = s->numedges; e >= 0; e--)
			{
				en = cl.worldmodel->surfedges[e+s->firstedge];
				if (en<0)	//backwards
				{
					en = -en;
					v2 = cl.worldmodel->vertexes[cl.worldmodel->edges[en].v[0]].position;
					v1 = cl.worldmodel->vertexes[cl.worldmodel->edges[en].v[1]].position;
				}
				else
				{
					v1 = cl.worldmodel->vertexes[cl.worldmodel->edges[en].v[0]].position;
					v2 = cl.worldmodel->vertexes[cl.worldmodel->edges[en].v[1]].position;
				}



				VectorSubtract(v1, v2, dir);
				len = Length(dir);
				VectorNormalize(dir);
				sprintf(str, "%i", (len<1)?1:(int)len);
				sl = strlen(str);
				VectorMA(v2, len/2 + sl*4, dir, pos);

				for (c = 0; c < sl; c++)
				{
					frow = (str[c]>>4)*size;
					fcol = (str[c]&15)*size;

					qglTexCoord2f (fcol, frow + size);
					qglVertex3fv(pos);
					if (s->flags & SURF_PLANEBACK)
						VectorMA(pos, -8, s->plane->normal, v);
					else
						VectorMA(pos, 8, s->plane->normal, v);
					qglTexCoord2f (fcol, frow);
					qglVertex3fv(v);
					VectorMA(pos, -8, dir, pos);
					if (s->flags & SURF_PLANEBACK)
						VectorMA(pos, -8, s->plane->normal, v);
					else
						VectorMA(pos, 8, s->plane->normal, v);
					qglTexCoord2f (fcol + size, frow);
					qglVertex3fv(v);
					qglTexCoord2f (fcol + size, frow + size);
					qglVertex3fv(pos);
				}
			}
		}
		qglEnd();

		qglDisable(GL_POLYGON_OFFSET_FILL);
	}

	qglEnable(GL_POLYGON_OFFSET_LINE);

	qglDisable(GL_TEXTURE_2D);

	qglBegin(GL_LINES);
	for (s = first; s ; s=s->texturechain)
	{
		for (e = s->numedges; e >= 0; e--)
		{
			en = cl.worldmodel->surfedges[e+s->firstedge];
			if (en<0)	//backwards
				en = -en;
			v1 = cl.worldmodel->vertexes[cl.worldmodel->edges[en].v[0]].position;
			v2 = cl.worldmodel->vertexes[cl.worldmodel->edges[en].v[1]].position;


			VectorSubtract(v2, v1, dir);
			len = Length(dir);
			VectorNormalize(dir);

			if (gl_schematics.value != 2)
			{
				sprintf(str, "%i", (len<1)?1:(int)len);
				sl = strlen(str);
			}
			else
				sl = 0;

			//left side. (find arrowhead part)
			if (s->flags & SURF_PLANEBACK)
				VectorMA(v1, -4, s->plane->normal, pos);
			else
				VectorMA(v1, 4, s->plane->normal, pos);

			VectorMA(pos, 4, dir, v);
			if (s->flags & SURF_PLANEBACK)
				VectorMA(v, 4, s->plane->normal, v);
			else
				VectorMA(v, -4, s->plane->normal, v);
			qglVertex3fv(v);
			qglVertex3fv(pos);

			if (s->flags & SURF_PLANEBACK)
				VectorMA(v, -8, s->plane->normal, v);
			else
				VectorMA(v, 8, s->plane->normal, v);
			qglVertex3fv(v);
			qglVertex3fv(pos);

			//the line
			qglVertex3fv(pos);
			VectorMA(pos, len/2 - sl*4, dir, pos);
			qglVertex3fv(pos);

			//right hand side.
			if (s->flags & SURF_PLANEBACK)
				VectorMA(v2, -4, s->plane->normal, pos);
			else
				VectorMA(v2, 4, s->plane->normal, pos);

			VectorMA(pos, -4, dir, v);
			if (s->flags & SURF_PLANEBACK)
				VectorMA(v, 4, s->plane->normal, v);
			else
				VectorMA(v, -4, s->plane->normal, v);
			qglVertex3fv(v);
			qglVertex3fv(pos);

			if (s->flags & SURF_PLANEBACK)
				VectorMA(v, -8, s->plane->normal, v);
			else
				VectorMA(v, 8, s->plane->normal, v);
			qglVertex3fv(v);
			qglVertex3fv(pos);

			//the line
			qglVertex3fv(pos);
			VectorMA(pos, -(len/2 - sl*4), dir, pos);
			qglVertex3fv(pos);

		}
	}
	qglEnd();
	qglDisable(GL_POLYGON_OFFSET_LINE);

	qglEnable(GL_TEXTURE_2D);
}

// :)
void PPL_Schematics(void)
{
	int		tn;
	msurface_t	*s;
	texture_t	*t;
	model_t *model;

	qglColor3f(1,1,1);

	qglDepthMask(0);	//don't bother writing depth

	GL_TexEnv(GL_MODULATE);

	qglShadeModel(GL_FLAT);

	qglEnable(GL_BLEND);
	qglDisable(GL_TEXTURE_2D);

	model = cl.worldmodel;
	for (tn=0 ; tn<model->numtextures ; tn++)
	{
		t = model->textures[tn];
		if (!t)
			continue;
		s = t->texturechain;
		if (!s)
			continue;

		PPL_SchematicsTextureChain(s);

		t->texturechain=NULL;
	}

	GL_TexEnv(GL_REPLACE);
	qglDepthMask(1);
}









#ifdef PPL





qboolean PPL_VisOverlaps(qbyte *v1, qbyte *v2)
{
	int i, m;
	m = (cl.worldmodel->numleafs-1)>>3;
	for (i=0 ; i<m ; i++)
	{
		if (v1[i] & v2[i])
			return true;
	}
	return false;
}
qboolean PPL_LeafInView(qbyte *lightvis)
{
	int i;
	int m = (cl.worldmodel->numleafs+7)/8;
	mleaf_t *wl = cl.worldmodel->leafs;
	unsigned char lv;
	for (i = 0; i < m; i++)
	{
		lv = lightvis[i];
		if (lv&1   && wl[(i<<3)+0].visframe == r_visframecount) return true;
		if (lv&2   && wl[(i<<3)+1].visframe == r_visframecount) return true;
		if (lv&4   && wl[(i<<3)+2].visframe == r_visframecount) return true;
		if (lv&8   && wl[(i<<3)+3].visframe == r_visframecount) return true;
		if (lv&16  && wl[(i<<3)+4].visframe == r_visframecount) return true;
		if (lv&32  && wl[(i<<3)+5].visframe == r_visframecount) return true;
		if (lv&64  && wl[(i<<3)+6].visframe == r_visframecount) return true;
		if (lv&128 && wl[(i<<3)+7].visframe == r_visframecount) return true;
	}

	return false;
}

void PPL_RecursiveWorldNode_r (mnode_t *node)
{
	int			c, side;
	mplane_t	*plane;
	msurface_t	*surf, **mark;
	mleaf_t		*pleaf;
	double		dot;
	int v;

	float *v1;
	vec3_t v3;

	if (node->shadowframe != r_shadowframe)
		return;

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


	//if light areabox is outside node, ignore node + children
	for (c = 0; c < 3; c++)
	{
		if (lightorg[c] + lightradius < node->minmaxs[c])
			return;
		if (lightorg[c] - lightradius > node->minmaxs[3+c])
			return;
	}
	
// if a leaf node, draw stuff
	if (node->contents < 0)
	{
		pleaf = (mleaf_t *)node;
		PPL_Shadow_Cache_Leaf(pleaf);

		mark = pleaf->firstmarksurface;
		c = pleaf->nummarksurfaces;

		if (c)
		{
			do
			{
				(*mark++)->shadowframe = r_shadowframe;
			} while (--c);
		}
		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;

	switch (plane->type)
	{
	case PLANE_X:
		dot = modelorg[0] - plane->dist;
		break;
	case PLANE_Y:
		dot = modelorg[1] - plane->dist;
		break;
	case PLANE_Z:
		dot = modelorg[2] - plane->dist;
		break;
	default:
		dot = DotProduct (modelorg, plane->normal) - plane->dist;
		break;
	}

	if (dot >= 0)
		side = 0;
	else
		side = 1;

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

// draw stuff
  	c = node->numsurfaces;

	if (c)
	{
		surf = cl.worldmodel->surfaces + node->firstsurface;

		{
			for ( ; c ; c--, surf++)
			{
				if (surf->shadowframe != r_shadowframe)
					continue;

//				if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))
//					continue;		// wrong side

//				if (surf->flags & SURF_PLANEBACK)
//					continue;

				if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
				{	// no shadows
					continue;
				}

				//is the light on the right side?
				if (surf->flags & SURF_PLANEBACK)
				{//inverted normal.
					if (-DotProduct(surf->plane->normal, lightorg)+surf->plane->dist >= lightradius)
						continue;
				}
				else
				{
					if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= lightradius)
						continue;
				}

/*				if (fabs(surf->center[0] - lightorg[0]) > lightradius+surf->radius ||
					fabs(surf->center[1] - lightorg[1]) > lightradius+surf->radius ||
					fabs(surf->center[2] - lightorg[2]) > lightradius+surf->radius)
					continue;
*/				

				PPL_Shadow_Cache_Surface(surf);



#define PROJECTION_DISTANCE (float)(lightradius*2)//0x7fffffff

				//build a list of the edges that are to be drawn.
				for (v = 0; v < surf->numedges; v++)
				{
					int e, delta;
					shadowemittedeges++;
					e = cl.worldmodel->surfedges[surf->firstedge+v];
					//negative edge means backwards edge.
					if (e < 0)
					{
						e=-e;
						delta = -1;
					}
					else
					{
						delta = 1;
					}

					if (!edge[e].count)
					{
						if (firstedge)
							edge[firstedge].prev = e;
						edge[e].next = firstedge;
						edge[e].prev = 0;
						firstedge = e;
						edge[e].count = delta;
					}
					else
					{
						edge[e].count += delta;

						if (!edge[e].count)	//unlink
						{
							if (edge[e].next)
							{
								edge[edge[e].next].prev = edge[e].prev;
							}
							if (edge[e].prev)
								edge[edge[e].prev].next = edge[e].next;
							else
								firstedge = edge[e].next;
						}
					}
				}

				shadowsurfcount++;

				qglVertexPointer(3, GL_FLOAT, 0, surf->mesh->xyz_array);
				qglDrawElements(GL_TRIANGLES, surf->mesh->numindexes, GL_UNSIGNED_INT, surf->mesh->indexes);

				//fixme:this only works becuse q1bsps don't have combined meshes yet...
					//back (depth precision doesn't matter)
					qglBegin(GL_POLYGON);
					for (v = surf->mesh->numvertexes-1; v >=0; v--)
					{
						v1 = surf->mesh->xyz_array[v];
						v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE;
						v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE;
						v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE;

						qglVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] );
					}
					qglEnd();
			}
		}
	}

// recurse down the back side
	PPL_RecursiveWorldNode_r (node->children[!side]);
}

//2 changes, but otherwise the same
void PPL_RecursiveWorldNodeQ2_r (mnode_t *node)
{
	int			c, side;
	mplane_t	*plane;
	msurface_t	*surf, **mark;
	mleaf_t		*pleaf;
	double		dot;
	int v;

	float *v1;
	vec3_t v3;

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

	if (node->shadowframe != r_shadowframe)
		return;
//	if (R_CullBox (node->minmaxs, node->minmaxs+3))
//		return;
	
// if a leaf node, draw stuff
	if (node->contents != -1)
	{
		pleaf = (mleaf_t *)node;

		mark = pleaf->firstmarksurface;
		c = pleaf->nummarksurfaces;

		if (c)
		{
			do
			{
				(*mark++)->shadowframe = r_shadowframe;
			} while (--c);
		}
		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;

	switch (plane->type)
	{
	case PLANE_X:
		dot = modelorg[0] - plane->dist;
		break;
	case PLANE_Y:
		dot = modelorg[1] - plane->dist;
		break;
	case PLANE_Z:
		dot = modelorg[2] - plane->dist;
		break;
	default:
		dot = DotProduct (modelorg, plane->normal) - plane->dist;
		break;
	}

	if (dot >= 0)
		side = 0;
	else
		side = 1;

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

// draw stuff
  	c = node->numsurfaces;

	if (c)
	{
		surf = cl.worldmodel->surfaces + node->firstsurface;

		{
			for ( ; c ; c--, surf++)
			{
				if (surf->shadowframe != r_shadowframe)
					continue;
/*
				if (surf->lightframe == r_shadowframe)	//done this one!
					continue;
				surf->lightframe = r_shadowframe;
*/
//				if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))
//					continue;		// wrong side

//				if (surf->flags & SURF_PLANEBACK)
//					continue;

				if (surf->flags & SURF_PLANEBACK)
				{//inverted normal.
					if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= 0)
						continue;
				}
				else
				{
					if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= 0)
						continue;
				}
//#define PROJECTION_DISTANCE (float)0x7fffffff
				if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
				{	// no shadows
					continue;
				}

				//build a list of the edges that are to be drawn.
				for (v = 0; v < surf->numedges; v++)
				{
					int e, delta;
					e = cl.worldmodel->surfedges[surf->firstedge+v];
					//negative edge means backwards edge.
					if (e < 0)
					{
						e=-e;
						delta = -1;
					}
					else
					{
						delta = 1;
					}

					if (!edge[e].count)
					{
						if (firstedge)
							edge[firstedge].prev = e;
						edge[e].next = firstedge;
						edge[e].prev = 0;
						firstedge = e;
						edge[e].count = delta;
					}
					else
					{
						edge[e].count += delta;

						if (!edge[e].count)	//unlink
						{
							if (edge[e].next)
							{
								edge[edge[e].next].prev = edge[e].prev;
							}
							if (edge[e].prev)
								edge[edge[e].prev].next = edge[e].next;
							else
								firstedge = edge[e].next;
						}
					}
				}

				//front face
				qglVertexPointer(3, GL_FLOAT, 0, surf->mesh->xyz_array);
				qglDrawElements(GL_TRIANGLES, surf->mesh->numindexes, GL_UNSIGNED_INT, surf->mesh->indexes);

				//fixme:this only works becuse q1bsps don't have combined meshes yet...
				//back (depth precision doesn't matter)
				qglBegin(GL_POLYGON);
				for (v = surf->mesh->numvertexes-1; v >=0; v--)
				{
					v1 = surf->mesh->xyz_array[v];
					v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE;
					v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE;
					v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE;

					qglVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] );
				}
				qglEnd();

			}
		}
	}

// recurse down the back side
	PPL_RecursiveWorldNodeQ2_r (node->children[!side]);
}

void PPL_RecursiveWorldNodeQ3_r (mnode_t *node)
{
	int			c, side;
	mplane_t	*plane;
	msurface_t	*surf, **mark;
	mleaf_t		*pleaf;
	double		dot;
//	glpoly_t *p;
//	int v;

//	float *v2;
//	vec3_t v4;

//	vec3_t v3;

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

	if (node->shadowframe != r_shadowframe)
		return;
//	if (R_CullBox (node->minmaxs, node->minmaxs+3))
//		return;
	
// if a leaf node, draw stuff
	if (node->contents != -1)
	{
		pleaf = (mleaf_t *)node;

		mark = pleaf->firstmarksurface;
		c = pleaf->nummarksurfaces;

		if (c)
		{
			do
			{
				surf = *mark;
				(*mark++)->shadowframe = r_shadowframe;

/*				if (surf->shadowframe != r_shadowframe)
					continue;
*/
//				if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))
//					continue;		// wrong side

//				if (surf->flags & SURF_PLANEBACK)
//					continue;

				if (surf->flags & SURF_PLANEBACK)
				{//inverted normal.
					if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= -lightradius)
						continue;
				}
				else
				{
					if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= lightradius)
						continue;
				}
//#define PROJECTION_DISTANCE (float)0x7fffffff
				/*if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
				{	// no shadows
					continue;
				}*/


				Sys_Error("PPL_RecursiveWorldNodeQ3_r needs work");
				/*

				for (p = surf->polys; p; p=p->next)
				{
					//front face
					qglVertexPointer(3, GL_FLOAT, sizeof(GLfloat)*VERTEXSIZE, p->verts);
					qglDrawElements(GL_TRIANGLES, (p->numverts-2)*3, GL_UNSIGNED_INT, varray_i_polytotri);
//fixme...
					for (v = 0; v < p->numverts; v++)
					{
					//border
						v1 = p->verts[v];
						v2 = p->verts[( v+1 )%p->numverts];

						//get positions of v3 and v4 based on the light position
						v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE;
						v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE;
						v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE;

						v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE;
						v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE;
						v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE;

						//Now draw the quad from the two verts to the projected light
						//verts
						qglBegin( GL_QUAD_STRIP );
							qglVertex3f( v1[0], v1[1], v1[2] );
							qglVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] );
							qglVertex3f( v2[0], v2[1], v2[2] );
							qglVertex3f( v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2] );
						qglEnd();
					}

//back
					qglBegin(GL_POLYGON);
					for (v = p->numverts-1; v >=0; v--)
					{
						v1 = p->verts[v];
						v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE;
						v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE;
						v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE;

						qglVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] );
					}
					qglEnd();
					
				}
				*/
			} while (--c);
		}
		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;

	switch (plane->type)
	{
	case PLANE_X:
		dot = modelorg[0] - plane->dist;
		break;
	case PLANE_Y:
		dot = modelorg[1] - plane->dist;
		break;
	case PLANE_Z:
		dot = modelorg[2] - plane->dist;
		break;
	default:
		dot = DotProduct (modelorg, plane->normal) - plane->dist;
		break;
	}

	if (dot >= 0)
		side = 0;
	else
		side = 1;

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

// draw stuff
/*  	c = node->numsurfaces;

	if (c)
	{
		surf = cl.worldmodel->surfaces + node->firstsurface;

		{
			for ( ; c ; c--, surf++)
			{

			}
		}
	}
*/
// recurse down the back side
	PPL_RecursiveWorldNodeQ3_r (node->children[!side]);
}

void PPL_RecursiveWorldNode (dlight_t *dl)
{
	float *v1, *v2;
	vec3_t v3, v4;

	lightradius = dl->radius;

	lightorg[0] = dl->origin[0]+0.5;
	lightorg[1] = dl->origin[1]+0.5;
	lightorg[2] = dl->origin[2]+0.5;

	modelorg[0] = lightorg[0];
	modelorg[1] = lightorg[1];
	modelorg[2] = lightorg[2];

	if (dl->worldshadowmesh)
	{
		qglEnableClientState(GL_VERTEX_ARRAY);
		qglVertexPointer(3, GL_FLOAT, 0, dl->worldshadowmesh->verts);
		qglDrawRangeElements(GL_TRIANGLES, 0, dl->worldshadowmesh->numverts, dl->worldshadowmesh->numindicies, GL_UNSIGNED_INT, dl->worldshadowmesh->indicies);
		return;
	}

	PPL_BeginShadowMesh(dl);


	qglEnableClientState(GL_VERTEX_ARRAY);

	if (qglGetError())
		Con_Printf("GL Error on entities\n");
	if (cl.worldmodel->fromgame == fg_quake3)
		PPL_RecursiveWorldNodeQ3_r(cl.worldmodel->nodes);
	else if (cl.worldmodel->fromgame == fg_quake2)
		PPL_RecursiveWorldNodeQ2_r(cl.worldmodel->nodes);
	else
		PPL_RecursiveWorldNode_r(cl.worldmodel->nodes);
	if (qglGetError())
		Con_Printf("GL Error on entities\n");

	qglVertexPointer(3, GL_FLOAT, sizeof(surfvertexarray_t), varray_v[0].xyz);
	if (qglGetError())
		Con_Printf("GL Error on entities\n");
	while(firstedge)
	{
		//border
		v1 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[0]].position;
		v2 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[1]].position;

		//get positions of v3 and v4 based on the light position
		v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE;
		v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE;
		v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE;

		v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE;
		v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE;
		v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE;

		if (varray_vc + 4>MAXARRAYVERTS)
		{
			qglDrawArrays(GL_QUADS, 0, varray_vc);
			if (qglGetError())
				Con_Printf("GL Error on entities\n");
			varray_vc=0;
		}

		if (edge[firstedge].count > 0)
		{
			varray_v[varray_vc].xyz[0] = v1[0]+v3[0];
			varray_v[varray_vc].xyz[1] = v1[1]+v3[1];
			varray_v[varray_vc].xyz[2] = v1[2]+v3[2];
			varray_vc++;
			varray_v[varray_vc].xyz[0] = v2[0]+v4[0];
			varray_v[varray_vc].xyz[1] = v2[1]+v4[1];
			varray_v[varray_vc].xyz[2] = v2[2]+v4[2];
			varray_vc++;
			varray_v[varray_vc].xyz[0] = v2[0];
			varray_v[varray_vc].xyz[1] = v2[1];
			varray_v[varray_vc].xyz[2] = v2[2];
			varray_vc++;
			varray_v[varray_vc].xyz[0] = v1[0];
			varray_v[varray_vc].xyz[1] = v1[1];
			varray_v[varray_vc].xyz[2] = v1[2];
			varray_vc++;
		}
		else
		{
			varray_v[varray_vc].xyz[0] = v1[0];
			varray_v[varray_vc].xyz[1] = v1[1];
			varray_v[varray_vc].xyz[2] = v1[2];
			varray_vc++;
			varray_v[varray_vc].xyz[0] = v2[0];
			varray_v[varray_vc].xyz[1] = v2[1];
			varray_v[varray_vc].xyz[2] = v2[2];
			varray_vc++;
			varray_v[varray_vc].xyz[0] = v2[0]+v4[0];
			varray_v[varray_vc].xyz[1] = v2[1]+v4[1];
			varray_v[varray_vc].xyz[2] = v2[2]+v4[2];
			varray_vc++;
			varray_v[varray_vc].xyz[0] = v1[0]+v3[0];
			varray_v[varray_vc].xyz[1] = v1[1]+v3[1];
			varray_v[varray_vc].xyz[2] = v1[2]+v3[2];
			varray_vc++;
		}
		edge[firstedge].count=0;

		firstedge = edge[firstedge].next;

		shadowedgecount++;
	}
	qglDrawArrays(GL_QUADS, 0, varray_vc);

	if (qglGetError())
		Con_Printf("GL Error on entities\n");

	varray_vc=0;

	firstedge=0;

	PPL_FinishShadowMesh(dl);
}

void PPL_DrawBrushModelShadow(dlight_t *dl, entity_t *e)
{
	int v;
	float *v1, *v2;
	vec3_t v3, v4;

	int i;
	model_t *model;
	msurface_t *surf;

	RotateLightVector(e->axis, e->origin, dl->origin, lightorg);

	qglPushMatrix();
	R_RotateForEntity(e);
	model = e->model;
	surf = model->surfaces+model->firstmodelsurface;
	for (i = 0; i < model->nummodelsurfaces; i++, surf++)
	{
		if (surf->flags & SURF_PLANEBACK)
		{//inverted normal.
			if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= -0.1)
				continue;
		}
		else
		{
			if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= 0.1)
				continue;
		}
//#define PROJECTION_DISTANCE (float)0x7fffffff
		if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
		{	// no shadows
			continue;
		}

			//front face
		qglVertexPointer(3, GL_FLOAT, 0, surf->mesh->xyz_array);
		qglDrawArrays(GL_POLYGON, 0, surf->mesh->numvertexes);

		for (v = 0; v < surf->mesh->numvertexes; v++)
		{
		//border
			v1 = surf->mesh->xyz_array[v];
			v2 = surf->mesh->xyz_array[( v+1 )%surf->mesh->numvertexes];

			//get positions of v3 and v4 based on the light position
			v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE;
			v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE;
			v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE;

			v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE;
			v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE;
			v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE;

			//Now draw the quad from the two verts to the projected light
			//verts
			qglBegin( GL_QUAD_STRIP );
				qglVertex3fv(v1);
				qglVertex3f	(v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2]);
				qglVertex3fv(v2);
				qglVertex3f (v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2]);
			qglEnd();
		}
			
//back
			//the same applies as earlier
		qglBegin(GL_POLYGON);
		for (v = surf->mesh->numvertexes-1; v >=0; v--)
		{
			v1 = surf->mesh->xyz_array[v];
			v3[0] = (v1[0]-lightorg[0])*PROJECTION_DISTANCE;
			v3[1] = (v1[1]-lightorg[1])*PROJECTION_DISTANCE;
			v3[2] = (v1[2]-lightorg[2])*PROJECTION_DISTANCE;

			qglVertex3f(v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2]);
		}
		qglEnd();
	}
	qglPopMatrix();
}

void PPL_DrawShadowMeshes(dlight_t *dl)
{
	int		i;

	if (!r_drawentities.value)
		return;

	// draw sprites seperately, because of alpha blending
	for (i=0 ; i<cl_numvisedicts ; i++)
	{
		currententity = &cl_visedicts[i];

		if (r_inmirror)
		{
			if (currententity->flags & Q2RF_WEAPONMODEL)
				continue;
		}
		else
		{
			if (currententity->keynum == dl->key)
				continue;
		}

		if (currententity->flags & Q2RF_BEAM)
		{
			R_DrawBeam(currententity);
			continue;
		}
		if (!currententity->model)
			continue;

		if (cls.allow_anyparticles || currententity->visframe)	//allowed or static
		{
			if (currententity->model->engineflags & MDLF_ENGULPHS)
			{
				if (gl_part_flame.value)
					continue;
			}
		}

		switch (currententity->model->type)
		{
		case mod_alias:
			R_DrawGAliasShadowVolume (currententity, dl->origin, dl->radius);
			break;

		case mod_brush:
			PPL_DrawBrushModelShadow (dl, currententity);
			break;

		default:
			break;
		}
	}
}

void PPL_UpdateNodeShadowFrames(qbyte	*lvis)
{
	int i;
	mnode_t *node;

	if (!lvis)	//using a cached light, we don't need shadowframes
		return;



#ifdef Q3BSPS
	if (cl.worldmodel->fromgame == fg_quake3)
	{
		mleaf_t	*leaf;
		r_shadowframe++;
		for (i=0, leaf=cl.worldmodel->leafs; i<cl.worldmodel->numleafs ; i++, leaf++)
		{
			node = (mnode_t *)leaf;
			while (node)
			{
				if (node->shadowframe == r_shadowframe)
					break;
				node->shadowframe = r_shadowframe;
				node = node->parent;
			}
		}
	}
	else
#endif
#ifdef Q2BSPS
		 if (cl.worldmodel->fromgame == fg_quake2)
	{
		mleaf_t	*leaf;
		int cluster;
		r_shadowframe++;

		for (i=0, leaf=cl.worldmodel->leafs; i<cl.worldmodel->numleafs ; i++, leaf++)
		{
			cluster = leaf->cluster;
			if (cluster == -1)
				continue;
			if (lvis[cluster>>3] & (1<<(cluster&7)))
			{
				node = (mnode_t *)leaf;
				do
				{
					if (node->shadowframe == r_shadowframe)
						break;
					node->shadowframe = r_shadowframe;
					node = node->parent;
				} while (node);
			}
		}
	}
	else
#endif
	{
		if (r_novis.value != 2)
		{
			r_shadowframe++;

			//variation on mark leaves
			for (i=0 ; i<cl.worldmodel->numleafs ; i++)
			{
				if (lvis[i>>3] & (1<<(i&7)))// && vvis[i>>3] & (1<<(i&7)))
				{
					node = (mnode_t *)&cl.worldmodel->leafs[i+1];
					do
					{
						if (node->shadowframe == r_shadowframe)
							break;
						node->shadowframe = r_shadowframe;
						node = node->parent;
					} while (node);
				}
			}
		}
	}
}

#if 1 //DP's stolen code
static void GL_Scissor (int x, int y, int width, int height)
{
#if 0	//visible scissors
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho  (0, glwidth, glheight, 0, -99999, 99999);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
//	GL_Set2D();

	glColor4f(1,1,1,1);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_SCISSOR_TEST);
	glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE );
	glDisable(GL_TEXTURE_2D);
	GL_TexEnv(GL_REPLACE);

	glBegin(GL_LINE_LOOP);
	glVertex2f(x, y);
	glVertex2f(x+glwidth, y);
	glVertex2f(x+glwidth, y+glheight);
	glVertex2f(x, y+glheight);
	glEnd();

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
#endif
	qglScissor(x, glheight - (y + height),width,height);
}

#define BoxesOverlap(a,b,c,d) ((a)[0] <= (d)[0] && (b)[0] >= (c)[0] && (a)[1] <= (d)[1] && (b)[1] >= (c)[1] && (a)[2] <= (d)[2] && (b)[2] >= (c)[2])
qboolean PPL_ScissorForBox(vec3_t mins, vec3_t maxs)
{
	int i, ix1, iy1, ix2, iy2;
	float x1, y1, x2, y2, x, y, f;
	vec3_t smins, smaxs;
	vec4_t v, v2;
	int r_view_x = 0;
	int r_view_y = 0;
	int r_view_width = glwidth;
	int r_view_height = glheight;
	if (0)//!r_shadow_scissor.integer)
	{
		GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
		return false;
	}
	// if view is inside the box, just say yes it's visible
	if (BoxesOverlap(r_refdef.vieworg, r_refdef.vieworg, mins, maxs))
	{
		GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
		return false;
	}
	for (i = 0;i < 3;i++)
	{
		if (vpn[i] >= 0)
		{
			v[i] = mins[i];
			v2[i] = maxs[i];
		}
		else
		{
			v[i] = maxs[i];
			v2[i] = mins[i];
		}
	}
	f = DotProduct(vpn, r_refdef.vieworg) + 1;
	if (DotProduct(vpn, v2) <= f)
	{
		// entirely behind nearclip plane
		GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
		return true;
	}
	if (DotProduct(vpn, v) >= f)
	{
		// entirely infront of nearclip plane
		x1 = y1 = x2 = y2 = 0;
		for (i = 0;i < 8;i++)
		{
			v[0] = (i & 1) ? mins[0] : maxs[0];
			v[1] = (i & 2) ? mins[1] : maxs[1];
			v[2] = (i & 4) ? mins[2] : maxs[2];
			v[3] = 1.0f;
			ML_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, (float)vid.width/vid.height, r_refdef.fov_y);
			v2[0]*=r_view_width;
			v2[1]*=r_view_height;
//			GL_TransformToScreen(v, v2);
			//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
			x = v2[0];
			y = v2[1];
			if (i)
			{
				if (x1 > x) x1 = x;
				if (x2 < x) x2 = x;
				if (y1 > y) y1 = y;
				if (y2 < y) y2 = y;
			}
			else
			{
				x1 = x2 = x;
				y1 = y2 = y;
			}
		}
	}
	else
	{
		// clipped by nearclip plane
		// this is nasty and crude...
		// create viewspace bbox
		for (i = 0;i < 8;i++)
		{
			v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_refdef.vieworg[0];
			v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_refdef.vieworg[1];
			v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_refdef.vieworg[2];
			v2[0] = DotProduct(v, vright);
			v2[1] = DotProduct(v, vup);
			v2[2] = DotProduct(v, vpn);
			if (i)
			{
				if (smins[0] > v2[0]) smins[0] = v2[0];
				if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
				if (smins[1] > v2[1]) smins[1] = v2[1];
				if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
				if (smins[2] > v2[2]) smins[2] = v2[2];
				if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
			}
			else
			{
				smins[0] = smaxs[0] = v2[0];
				smins[1] = smaxs[1] = v2[1];
				smins[2] = smaxs[2] = v2[2];
			}
		}
		// now we have a bbox in viewspace
		// clip it to the view plane
		if (smins[2] < 1)
			smins[2] = 1;
		// return true if that culled the box
		if (smins[2] >= smaxs[2])
			return true;
		// ok some of it is infront of the view, transform each corner back to
		// worldspace and then to screenspace and make screen rect
		// initialize these variables just to avoid compiler warnings
		x1 = y1 = x2 = y2 = 0;
		for (i = 0;i < 8;i++)
		{
			v2[0] = (i & 1) ? smins[0] : smaxs[0];
			v2[1] = (i & 2) ? smins[1] : smaxs[1];
			v2[2] = (i & 4) ? smins[2] : smaxs[2];
			v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_refdef.vieworg[0];
			v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_refdef.vieworg[1];
			v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_refdef.vieworg[2];
			v[3] = 1.0f;
			ML_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, vid.width/vid.height, r_refdef.fov_y);
			v2[0]*=r_view_width;
			v2[1]*=r_view_height;
//			GL_TransformToScreen(v, v2);
			//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
			x = v2[0];
			y = v2[1];
			if (i)
			{
				if (x1 > x) x1 = x;
				if (x2 < x) x2 = x;
				if (y1 > y) y1 = y;
				if (y2 < y) y2 = y;
			}
			else
			{
				x1 = x2 = x;
				y1 = y2 = y;
			}
		}
#if 0
		// this code doesn't handle boxes with any points behind view properly
		x1 = 1000;x2 = -1000;
		y1 = 1000;y2 = -1000;
		for (i = 0;i < 8;i++)
		{
			v[0] = (i & 1) ? mins[0] : maxs[0];
			v[1] = (i & 2) ? mins[1] : maxs[1];
			v[2] = (i & 4) ? mins[2] : maxs[2];
			v[3] = 1.0f;
			GL_TransformToScreen(v, v2);
			v2[0]*=r_view_width;
			v2[1]*=r_view_height;
			//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
			if (v2[2] > 0)
			{
				x = v2[0];
				y = v2[1];

				if (x1 > x) x1 = x;
				if (x2 < x) x2 = x;
				if (y1 > y) y1 = y;
				if (y2 < y) y2 = y;
			}
		}
#endif
	}
	ix1 = x1 - 1.0f;
	iy1 = y1 - 1.0f;
	ix2 = x2 + 1.0f;
	iy2 = y2 + 1.0f;
	//Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
	if (ix1 < r_view_x) ix1 = r_view_x;
	if (iy1 < r_view_y) iy1 = r_view_y;
	if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
	if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
	if (ix2 <= ix1 || iy2 <= iy1)
		return true;
	// set up the scissor rectangle
	qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
	//qglEnable(GL_SCISSOR_TEST);
	return false;
}
#endif

void CL_NewDlight (int key, float x, float y, float z, float radius, float time,
				   int type);
//generates stencil shadows of the world geometry.
//redraws world geometry
qboolean PPL_AddLight(dlight_t *dl)
{
	int i;
	int sdecrw;
	int sincrw;
	int leaf;
	qbyte *lvis;
	qbyte *vvis;
	vec3_t colour;

	qbyte	lvisb[MAX_MAP_LEAFS/8];
	qbyte	vvisb[MAX_MAP_LEAFS/8];

	vec3_t mins;
	vec3_t maxs;

	mins[0] = dl->origin[0] - dl->radius;
	mins[1] = dl->origin[1] - dl->radius;
	mins[2] = dl->origin[2] - dl->radius;

	maxs[0] = dl->origin[0] + dl->radius;
	maxs[1] = dl->origin[1] + dl->radius;
	maxs[2] = dl->origin[2] + dl->radius;

	colour[0] = dl->color[0];
	colour[1] = dl->color[1];
	colour[2] = dl->color[2];
	if (dl->style)
	{
		if (cl_lightstyle[dl->style-1].colour & 1)
			colour[0] *= d_lightstylevalue[dl->style-1]/255.0f;
		else
			colour[0] = 0;
		if (cl_lightstyle[dl->style-1].colour & 2)
			colour[1] *= d_lightstylevalue[dl->style-1]/255.0f;
		else
			colour[1] = 0;
		if (cl_lightstyle[dl->style-1].colour & 4)
			colour[2] *= d_lightstylevalue[dl->style-1]/255.0f;
		else
			colour[2] = 0;
	}

	if (colour[0] < 0.1 && colour[1] < 0.1 && colour[2] < 0.1)
		return false;	//just switch these off.

	if (PPL_ScissorForBox(mins, maxs))
		return false;	//was culled.

	if (dl->worldshadowmesh)
	{
		if (!PPL_LeafInView(dl->worldshadowmesh->litleaves))
			return false;
/*
		if (cl.worldmodel->fromgame == fg_quake2 || cl.worldmodel->fromgame == fg_quake3)
			i = cl.worldmodel->funcs.LeafForPoint(r_refdef.vieworg, cl.worldmodel);
		else
			i = r_viewleaf - cl.worldmodel->leafs;
		vvis = cl.worldmodel->funcs.LeafPVS(i, cl.worldmodel, vvisb);

	//	if (!(lvis[i>>3] & (1<<(i&7))))	//light might not be visible, but it's effects probably should be.
	//		return;
		if (!PPL_VisOverlaps(dl->worldshadowmesh->litleaves, vvis))	//The two viewing areas do not intersect.
			return;
*/
		lvis = NULL;
	}
	else
	{
		if (cl.worldmodel->fromgame == fg_quake2 || cl.worldmodel->fromgame == fg_quake3)
			i = cl.worldmodel->funcs.LeafnumForPoint(cl.worldmodel, r_refdef.vieworg);
		else
			i = r_viewleaf - cl.worldmodel->leafs;

		leaf = cl.worldmodel->funcs.LeafnumForPoint(cl.worldmodel, dl->origin);
		lvis = cl.worldmodel->funcs.LeafPVS(cl.worldmodel, leaf, lvisb);
		vvis = cl.worldmodel->funcs.LeafPVS(cl.worldmodel, i, vvisb);

	//	if (!(lvis[i>>3] & (1<<(i&7))))	//light might not be visible, but it's effects probably should be.
	//		return;
		if (!PPL_VisOverlaps(lvis, vvis))	//The two viewing areas do not intersect.
			return false;
	}

	PPL_EnableVertexArrays();

	qglDisable(GL_TEXTURE_2D);
	qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	qglEnable(GL_SCISSOR_TEST);
	if (!((int)r_shadows.value & 4))
	{
		qglDisable(GL_BLEND);
		qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
		qglDepthMask(0);

		if (gldepthfunc==GL_LEQUAL)
			qglDepthFunc(GL_LESS);
		else
			qglDepthFunc(GL_GREATER);
		qglEnable(GL_DEPTH_TEST);
		qglEnable(GL_STENCIL_TEST);

		sincrw = GL_INCR;
		sdecrw = GL_DECR;
		if (gl_config.ext_stencil_wrap)
		{	//minamlise damage...
			sincrw = GL_INCR_WRAP_EXT;
			sdecrw = GL_DECR_WRAP_EXT;
		}
	//our stencil writes.

	#ifdef _DEBUG
		if (r_shadows.value == 666)	//testing (visible shadow volumes)
		{
			if (qglGetError())
				Con_Printf("GL Error on entities\n");
			qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
			qglColor3f(dl->color[0], dl->color[1], dl->color[2]);
			qglDisable(GL_STENCIL_TEST);
			qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			if (qglGetError())
				Con_Printf("GL Error on entities\n");
			PPL_RecursiveWorldNode(dl);
			if (qglGetError())
				Con_Printf("GL Error on entities\n");
			PPL_DrawShadowMeshes(dl);
			if (qglGetError())
				Con_Printf("GL Error on entities\n");
			qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		}
		else
	#endif
			
		if (qglStencilOpSeparateATI && !((int)r_shadows.value & 2))//GL_ATI_separate_stencil
		{
			qglClearStencil(0);
			qglClear(GL_STENCIL_BUFFER_BIT);
			qglDisable(GL_CULL_FACE);

			qglStencilFunc( GL_ALWAYS, 1, ~0 );

			qglStencilOpSeparateATI(GL_BACK, GL_KEEP, sincrw, GL_KEEP);
			qglStencilOpSeparateATI(GL_FRONT, GL_KEEP, sdecrw, GL_KEEP);
			PPL_UpdateNodeShadowFrames(lvis);
			PPL_RecursiveWorldNode(dl);
			PPL_DrawShadowMeshes(dl);
			qglStencilOpSeparateATI(GL_FRONT_AND_BACK, GL_KEEP, GL_KEEP, GL_KEEP);

			qglEnable(GL_CULL_FACE);

			qglStencilFunc( GL_EQUAL, 0, ~0 );
		}
		else if (qglActiveStencilFaceEXT && !((int)r_shadows.value & 2))	//NVidias variation on a theme. (GFFX class)
		{
			qglClearStencil(0);
			qglClear(GL_STENCIL_BUFFER_BIT);
			qglDisable(GL_CULL_FACE);

			qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);

			qglActiveStencilFaceEXT(GL_BACK);
			qglStencilOp(GL_KEEP, sincrw, GL_KEEP);
			qglStencilFunc( GL_ALWAYS, 1, ~0 );

			qglActiveStencilFaceEXT(GL_FRONT);
			qglStencilOp(GL_KEEP, sdecrw, GL_KEEP);
			qglStencilFunc( GL_ALWAYS, 1, ~0 );

			PPL_UpdateNodeShadowFrames(lvis);
			PPL_RecursiveWorldNode(dl);
			PPL_DrawShadowMeshes(dl);

			qglActiveStencilFaceEXT(GL_BACK);
			qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

			qglActiveStencilFaceEXT(GL_FRONT);
			qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

			qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);

			qglEnable(GL_CULL_FACE);

			qglActiveStencilFaceEXT(GL_BACK);
			qglStencilFunc( GL_EQUAL, 0, ~0 );
		}
		else //your graphics card sucks and lacks efficient stencil shadow techniques.
		{	//centered around 0. Will only be increased then decreased less.
			qglClearStencil(0);
			qglClear(GL_STENCIL_BUFFER_BIT);

			qglEnable(GL_CULL_FACE);

			qglStencilFunc( GL_ALWAYS, 0, ~0 );

			shadowsurfcount = 0;
			qglCullFace(GL_BACK);
			qglStencilOp(GL_KEEP, sincrw, GL_KEEP);
			PPL_UpdateNodeShadowFrames(lvis);
			PPL_RecursiveWorldNode(dl);
			PPL_DrawShadowMeshes(dl);

			shadowsurfcount=0;
			qglCullFace(GL_FRONT);
			qglStencilOp(GL_KEEP, sdecrw, GL_KEEP);
			PPL_UpdateNodeShadowFrames(lvis);
			PPL_RecursiveWorldNode(dl);
			PPL_DrawShadowMeshes(dl);

			qglStencilFunc( GL_EQUAL, 0, ~0 );
		}
	//end stencil writing.

		qglEnable(GL_DEPTH_TEST);
		qglDepthMask(0);
		qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
		qglCullFace(GL_FRONT);

#if 0	//draw the stencil stuff to the red channel
/*		{
#pragma comment(lib, "opengl32.lib");
			static char buffer[1024*1024*8];
			glReadPixels(0, 0, vid.width, vid.height, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer);
			glDrawPixels(vid.width, vid.height, GL_GREEN, GL_UNSIGNED_BYTE, buffer);
		}
*/

		qglMatrixMode(GL_PROJECTION);
		qglPushMatrix();
		qglMatrixMode(GL_MODELVIEW);
		qglPushMatrix();
		GL_Set2D();

		qglColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
		qglStencilFunc( GL_GREATER, 1, ~0 );
		Draw_ConsoleBackground(480);

		qglColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
		qglStencilFunc( GL_LESS, 1, ~0 );
		Draw_ConsoleBackground(480);

		qglMatrixMode(GL_PROJECTION);
		qglPopMatrix();
		qglMatrixMode(GL_MODELVIEW);
		qglPopMatrix();
#endif

		qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
	}
	qglColor3f(1,1,1);

	qglEnable(GL_BLEND);
	qglBlendFunc(GL_ONE, GL_ONE);
	qglColor4f(dl->color[0], dl->color[1], dl->color[2], 1);
	qglDepthFunc(GL_EQUAL);

	lightorg[0] = dl->origin[0]+0.5;
	lightorg[1] = dl->origin[1]+0.5;
	lightorg[2] = dl->origin[2]+0.5;

	PPL_DrawEntLighting(dl, colour);
	qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	qglDepthMask(1);
	qglDepthFunc(gldepthfunc);
	qglEnable(GL_DEPTH_TEST);

	qglDisable(GL_STENCIL_TEST);
	qglStencilFunc( GL_ALWAYS, 0, ~0 );

	qglDisable(GL_SCISSOR_TEST);
	qglDisable(GL_BLEND);
	GL_TexEnv(GL_REPLACE);

	return true;
}

#endif

void GL_CheckTMUIs0(void);

void PPL_DrawWorld (void)
{
	RSpeedLocals();
	dlight_t *l;
#if 0
	dlight_t *lc, *furthestprev;
	float furthest;
#endif
	int i;
	int numlights;

	vec3_t mins, maxs;

	int maxshadowlights = gl_maxshadowlights.value;

	if (!r_shadow_realtime_world.value)
		r_lightmapintensity = 1;
	else
		r_lightmapintensity = r_shadow_realtime_world_lightmaps.value;

/*
	if (!lightmap)
	{
		R_PreNewMap();
		R_NewMap();
		return;	// :/
	}
	*/

	if (maxshadowlights < 1)
		maxshadowlights = 1;
//	if (qglGetError())
//		Con_Printf("GL Error before world\n");
//glColorMask(0,0,0,0);

	RSpeedRemark();
	TRACE(("dbg: calling PPL_BaseTextures\n"));
	PPL_BaseTextures(cl.worldmodel);
	RSpeedEnd(RSPEED_WORLD);

//	if (qglGetError())
//		Con_Printf("GL Error during base textures\n");
//glColorMask(1,1,1,1);

	RSpeedRemark();
	TRACE(("dbg: calling PPL_BaseEntTextures\n"));
	PPL_BaseEntTextures();
	RSpeedEnd(RSPEED_DRAWENTITIES);

//	CL_NewDlightRGB(1, r_refdef.vieworg[0], r_refdef.vieworg[1]-16, r_refdef.vieworg[2]-24, 128, 1, 1, 1, 1);

//	if (qglGetError())
//		Con_Printf("GL Error on entities\n");

#ifdef PPL
	numlights = 0;
	RSpeedRemark();
	if (r_shadows.value && qglStencilFunc && gl_canstencil)
	{
		if (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife || cl.worldmodel->fromgame == fg_quake2 /*|| cl.worldmodel->fromgame == fg_quake3*/)
		{
//			lc = NULL;
			for (l = cl_dlights, i=0 ; i<MAX_DLIGHTS ; i++, l++)
			{
				if (!l->radius || l->noppl)
					continue;
				if (l->color[0]<0)
					continue;	//quick check for darklight

				if (l->isstatic)
				{
					if (!r_shadow_realtime_world.value)
						continue;
				}

				mins[0] = l->origin[0] - l->radius;
				mins[1] = l->origin[1] - l->radius;
				mins[2] = l->origin[2] - l->radius;
				maxs[0] = l->origin[0] + l->radius;
				maxs[1] = l->origin[1] + l->radius;
				maxs[2] = l->origin[2] + l->radius;
				if (R_CullBox(mins, maxs))
					continue;
				if (R_CullSphere(l->origin, l->radius))
					continue;

#if 1
				if (maxshadowlights-- <= 0)
					continue;
#else
				VectorSubtract(l->origin, r_refdef.vieworg, mins)
				l->dist = Length(mins);
				VectorNormalize(mins);
				l->dist*=1-sqrt(DotProduct(vpn, mins)*DotProduct(vpn, mins));

				l->next = lc;
				lc = l;
				maxshadowlights--;
			}
			while (maxshadowlights<0)//ooer... we exceeded our quota... strip the furthest ones out.
			{
				furthest = lc->dist;
				furthestprev=NULL;
				for (l = lc; l->next; l = l->next)
				{
					if (l->next->dist > furthest)
					{
						furthest = l->next->dist;
						furthestprev = l;
					}
				}
				if (furthestprev)
					furthestprev->next = furthestprev->next->next;
				else
					lc = lc->next;

				maxshadowlights++;
			}

			for (l = lc; l; l = l->next)	//we now have our quotaed list
			{
#endif
				if(!l->isstatic)
				{
					l->color[0]*=10;
					l->color[1]*=10;
					l->color[2]*=10;
				}
				TRACE(("dbg: calling PPL_AddLight\n"));
				if (PPL_AddLight(l))
					numlights++;
				if(!l->isstatic)
				{
					l->color[0]/=10;
					l->color[1]/=10;
					l->color[2]/=10;
				}
			}
			qglEnable(GL_TEXTURE_2D);
		
		}

		qglDisableClientState(GL_COLOR_ARRAY);
	}
	RSpeedEnd(RSPEED_STENCILSHADOWS);
#endif
//	Con_Printf("%i lights\n", numlights);

//	if (qglGetError())
//		Con_Printf("GL Error on shadow lighting\n");

	RSpeedRemark();

	if (gl_schematics.value)
		PPL_Schematics();

	TRACE(("dbg: calling PPL_DrawEntFullBrights\n"));
	PPL_DrawEntFullBrights();

	RSpeedEnd(RSPEED_FULLBRIGHTS);

//	if (qglGetError())
//		Con_Printf("GL Error on fullbrights/details\n");

//	Con_Printf("%i %i(%i) %i\n", shadowsurfcount, shadowedgecount, shadowemittedeges, shadowlightfaces);

	RQuantAdd(RQUANT_SHADOWFACES, shadowsurfcount);
	RQuantAdd(RQUANT_SHADOWEDGES, shadowedgecount);
	RQuantAdd(RQUANT_LITFACES, shadowlightfaces);

	shadowsurfcount	= 0;
	shadowedgecount = 0;
	shadowlightfaces = 0;
	shadowemittedeges = 0;

	GL_CheckTMUIs0();

	R_IBrokeTheArrays();
}

void PPL_CreateShaderObjects(void)
{
#ifdef PPL
	PPL_CreateLightTexturesProgram();
#endif
	PPL_LoadSpecularFragmentProgram();
}

void PPL_FlushShadowMesh(dlight_t *dl)
{
	int tn;
	shadowmesh_t *sm;
	sm = dl->worldshadowmesh;
	if (sm)
	{
		dl->worldshadowmesh = NULL;
		for (tn = 0; tn < sm->numsurftextures; tn++)
			if (sm->litsurfs[tn].count)
				BZ_Free(sm->litsurfs);
		BZ_Free(sm->indicies);
		BZ_Free(sm->verts);
		BZ_Free(sm);
	}
}

//okay, so this is a bit of a hack...
qboolean buildingmesh;
void (APIENTRY *realBegin) (GLenum);
void (APIENTRY *realEnd) (void);
void (APIENTRY *realVertex3f) (GLfloat x, GLfloat y, GLfloat z);
void (APIENTRY *realVertex3fv) (const GLfloat *v);
void (APIENTRY *realVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
void (APIENTRY *realDrawArrays) (GLenum mode, GLint first, GLsizei count);
void (APIENTRY *realDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);

#define inc 128
int sh_type;
int sh_index[64*64];
int sh_vertnum;		//vertex number (set to 0 at SH_Begin)
int sh_maxverts;
int sh_numverts;	//total emitted
int sh_maxindicies;
int sh_numindicies;
const float *sh_vertexpointer;
int sh_vpstride;
shadowmesh_t *sh_shmesh;
void APIENTRY SH_Begin (GLenum e)
{
	sh_type = e;
}
void APIENTRY SH_End (void)
{
	int i;
	int v1, v2;
	switch(sh_type)
	{
	case GL_POLYGON:
		i = (sh_numindicies+(sh_vertnum-2)*3+inc+5)&~(inc-1);	//and a bit of padding
		if (sh_maxindicies != i)
		{
			sh_maxindicies = i;
			sh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, i * sizeof(*sh_shmesh->indicies));
		}
		//decompose the poly into a triangle fan.
		v1 = sh_index[0];
		v2 = sh_index[1];
		for (i = 2; i < sh_vertnum; i++)
		{
			sh_shmesh->indicies[sh_numindicies++] = v1;
			sh_shmesh->indicies[sh_numindicies++] = v2;
			sh_shmesh->indicies[sh_numindicies++] = v2 = sh_index[i];
		}
		sh_vertnum = 0;
		break;
	case GL_TRIANGLES:
		i = (sh_numindicies+(sh_vertnum)+inc+5)&~(inc-1);	//and a bit of padding
		if (sh_maxindicies != i)
		{
			sh_maxindicies = i;
			sh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, i * sizeof(*sh_shmesh->indicies));
		}
		//add the extra triangles
		for (i = 0; i < sh_vertnum; i+=3)
		{
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+0];
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+1];
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+2];
		}
		sh_vertnum = 0;
		break;
	case GL_QUADS:
		i = (sh_numindicies+(sh_vertnum/4)*6+inc+5)&~(inc-1);	//and a bit of padding
		if (sh_maxindicies != i)
		{
			sh_maxindicies = i;
			sh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, i * sizeof(*sh_shmesh->indicies));
		}
		//add the extra triangles
		for (i = 0; i < sh_vertnum; i+=4)
		{
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+0];
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+1];
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+2];

			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+0];
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+2];
			sh_shmesh->indicies[sh_numindicies++] = sh_index[i+3];
		}
		sh_vertnum = 0;
		break;
	default:
		if (sh_vertnum)
			Sys_Error("SH_End: verticies were left");
	}
}
void APIENTRY SH_Vertex3f (GLfloat x, GLfloat y, GLfloat z)
{
	int i;
	if (sh_vertnum > sizeof(sh_index)/sizeof(sh_index[0]))
		Sys_Error("SH_End: too many verticies");

//add the verts as we go
	i = (sh_numverts+inc+5)&~(inc-1);	//and a bit of padding
	if (sh_maxverts != i)
	{
		sh_maxverts = i;
		sh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, i * sizeof(*sh_shmesh->verts));
	}

	sh_shmesh->verts[sh_numverts][0] = x;
	sh_shmesh->verts[sh_numverts][1] = y;
	sh_shmesh->verts[sh_numverts][2] = z;

	sh_index[sh_vertnum] = sh_numverts;
	sh_vertnum++;
	sh_numverts++;

	switch(sh_type)
	{
	case GL_POLYGON:
		break;
	case GL_TRIANGLES:
		if (sh_vertnum == 3)
			SH_End();
		break;
	case GL_QUADS:
		if (sh_vertnum == 4)
			SH_End();
		break;
	default:
		Sys_Error("SH_Vertex3f: bad type");
	}
}
void APIENTRY SH_Vertex3fv (const GLfloat *v)
{
	SH_Vertex3f(v[0], v[1], v[2]);
}
void APIENTRY SH_VertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
	sh_vertexpointer = pointer;
	sh_vpstride = stride/4;
	if (!sh_vpstride)
		sh_vpstride = 3;
}
void APIENTRY SH_DrawArrays (GLenum mode, GLint first, GLsizei count)
{
	int i;
	SH_Begin(mode);
	count+=first;
	for (i = first; i < count; i++)
		SH_Vertex3fv(sh_vertexpointer + i*sh_vpstride);
	SH_End();
}
void APIENTRY SH_DrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
{
	int i;
	SH_Begin(mode);
	for (i = 0; i < count; i++)
		SH_Vertex3fv(sh_vertexpointer + (((int*)indices)[i])*sh_vpstride);
	SH_End();
}

void PPL_Shadow_Cache_Surface(msurface_t *surf)
{
	int i;
	if (!buildingmesh)
		return;

	for (i = 0; i < cl.worldmodel->numtextures; i++)
		if (surf->texinfo->texture == cl.worldmodel->textures[i])
			break;

	sh_shmesh->litsurfs[i].s = BZ_Realloc(sh_shmesh->litsurfs[i].s, sizeof(void*)*(sh_shmesh->litsurfs[i].count+1));
	sh_shmesh->litsurfs[i].s[sh_shmesh->litsurfs[i].count] = surf;
	sh_shmesh->litsurfs[i].count++;
}

void PPL_Shadow_Cache_Leaf(mleaf_t *leaf)
{
	int i;
	if (!buildingmesh)
		return;

	i = leaf - cl.worldmodel->leafs;
	sh_shmesh->litleaves[i>>3] = 1<<(i&7);
}

void PPL_BeginShadowMesh(dlight_t *dl)
{
	PPL_FlushShadowMesh(dl);

	if (buildingmesh)
		return;
	if (!dl->isstatic)
		return;

	sh_maxverts = 0;
	sh_numverts = 0;
	sh_vertnum = 0;
	sh_maxindicies = 0;
	sh_numindicies = 0;

	buildingmesh = true;
	realBegin			= qglBegin;
	realEnd				= qglEnd;
	realVertex3f		= qglVertex3f;
	realVertex3fv		= qglVertex3fv;
	realVertexPointer	= qglVertexPointer;
	realDrawArrays		= qglDrawArrays;
	realDrawElements	= qglDrawElements;

	qglBegin			= SH_Begin;
	qglEnd				= SH_End;
	qglVertex3f			= SH_Vertex3f;
	qglVertex3fv		= SH_Vertex3fv;
	qglVertexPointer	= SH_VertexPointer;
	qglDrawArrays		= SH_DrawArrays;
	qglDrawElements		= SH_DrawElements;

	sh_shmesh = Z_Malloc(sizeof(*sh_shmesh) + (cl.worldmodel->numleafs+7)/8);
	sh_shmesh->litsurfs = Z_Malloc(sizeof(shadowmeshsurfs_t)*cl.worldmodel->numtextures);
	sh_shmesh->numsurftextures=cl.worldmodel->numtextures;

	sh_shmesh->litleaves = (unsigned char*)(sh_shmesh+1);
}
void PPL_FinishShadowMesh(dlight_t *dl)
{
	if (!buildingmesh)
		return;

	qglBegin			= realBegin;
	qglEnd				= realEnd;
	qglVertex3f			= realVertex3f;
	qglVertex3fv		= realVertex3fv;
	qglVertexPointer	= realVertexPointer;
	qglDrawArrays		= realDrawArrays;
	qglDrawElements		= realDrawElements;
	buildingmesh		= false;

	dl->worldshadowmesh = sh_shmesh;
	sh_shmesh->numindicies = sh_numindicies;
	sh_shmesh->numverts = sh_numverts;

	sh_shmesh = NULL;
}

#endif	//ifdef GLQUAKE