/* -------------------------------------------------------------------------------

   Copyright (C) 1999-2007 id Software, Inc. and contributors.
   For a list of contributors, see the accompanying CONTRIBUTORS file.

   This file is part of GtkRadiant.

   GtkRadiant is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   GtkRadiant is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GtkRadiant; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

   ----------------------------------------------------------------------------------

   This code has been altered significantly from its original form, to support
   several games based on the Quake III Arena engine, in the form of "Q3Map2."

   ------------------------------------------------------------------------------- */



/* marker */
#define LIGHT_YDNAR_C



/* dependencies */
#include "q3map2.h"




/*
   ColorToBytes()
   ydnar: moved to here 2001-02-04
 */

void ColorToBytes( const float *color, byte *colorBytes, float scale ){
	int i;
	float max, gamma;
	vec3_t sample;
	float inv, dif;


	/* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
	if ( scale <= 0.0f ) {
		scale = 1.0f;
	}
	/* globally */
	scale *= lightmapBrightness;

	/* make a local copy */
	VectorScale( color, scale, sample );

	/* muck with it */
	gamma = 1.0f / lightmapGamma;
	for ( i = 0; i < 3; i++ )
	{
		/* handle negative light */
		if ( sample[ i ] < 0.0f ) {
			sample[ i ] = 0.0f;
			continue;
		}

		/* gamma */
		sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
	}

	if ( lightmapExposure == 0 ) {
		/* clamp with color normalization */
		max = sample[ 0 ];
		if ( sample[ 1 ] > max ) {
			max = sample[ 1 ];
		}
		if ( sample[ 2 ] > max ) {
			max = sample[ 2 ];
		}
		if ( max > 255.0f ) {
			VectorScale( sample, ( 255.0f / max ), sample );
		}
	}
	else
	{
		inv = 1.f / lightmapExposure;
		//Exposure

		max = sample[ 0 ];
		if ( sample[ 1 ] > max ) {
			max = sample[ 1 ];
		}
		if ( sample[ 2 ] > max ) {
			max = sample[ 2 ];
		}

		dif = ( 1 -  exp( -max * inv ) )  *  255;

		if ( max > 0 ) {
			dif = dif / max;
		}
		else
		{
			dif = 0;
		}

		for ( i = 0; i < 3; i++ )
		{
			sample[i] *= dif;
		}
	}


	/* compensate for ingame overbrighting/bitshifting */
	VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );

	/* contrast */
	if ( lightmapContrast != 1.0f ){
		for ( i = 0; i < 3; i++ ){
			sample[i] = lightmapContrast * ( sample[i] - 128 ) + 128;
			if ( sample[i] < 0 ){
				sample[i] = 0;
			}
		}
		if ( ( sample[0] > 255 ) || ( sample[1] > 255 ) || ( sample[2] > 255 ) ) {
			max = sample[0] > sample[1] ? sample[0] : sample[1];
			max = max > sample[2] ? max : sample[2];
			sample[0] = sample[0] * 255 / max;
			sample[1] = sample[1] * 255 / max;
			sample[2] = sample[2] * 255 / max;
		}
	}

	/* sRGB lightmaps */
	if ( lightmapsRGB ) {
		sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
		sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
		sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
	}

	/* store it off */
	colorBytes[ 0 ] = sample[ 0 ];
	colorBytes[ 1 ] = sample[ 1 ];
	colorBytes[ 2 ] = sample[ 2 ];
}

/*
 * Same as ColorToBytes, but if the output color will never contain zero
 * components. Used to avoid returning 0 0 0 due to an ioq3 issue. Reason
 * to also map 0 0 1 to 1 1 1 is to ensure monotonicity in the color mapping
 * to prevent banding-like artifacts on lightmaps.
 */
void ColorToBytesNonZero( const float *color, byte *colorBytes, float scale) {
	int i;
	ColorToBytes(color, colorBytes, scale);
	for (i = 0; i < 3; ++i)
		if (colorBytes[i] == 0)
			colorBytes[i] = 1;
}


/* -------------------------------------------------------------------------------

   this section deals with phong shading (normal interpolation across brush faces)

   ------------------------------------------------------------------------------- */

/*
   SmoothNormals()
   smooths together coincident vertex normals across the bsp
 */

#define MAX_SAMPLES             256
#define THETA_EPSILON           0.000001
#define EQUAL_NORMAL_EPSILON    0.01

void SmoothNormals( void ){
	int i, j, k, f, cs, numVerts, numVotes, fOld, start;
	float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
	bspDrawSurface_t    *ds;
	shaderInfo_t        *si;
	float               *shadeAngles;
	byte                *smoothed;
	vec3_t average, diff;
	int indexes[ MAX_SAMPLES ];
	vec3_t votes[ MAX_SAMPLES ];


	/* allocate shade angle table */
	shadeAngles = safe_malloc0( numBSPDrawVerts * sizeof( float ) );

	/* allocate smoothed table */
	cs = ( numBSPDrawVerts / 8 ) + 1;
	smoothed = safe_malloc0( cs );

	/* set default shade angle */
	defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
	maxShadeAngle = 0;

	/* run through every surface and flag verts belonging to non-lightmapped surfaces
	   and set per-vertex smoothing angle */
	for ( i = 0; i < numBSPDrawSurfaces; i++ )
	{
		/* get drawsurf */
		ds = &bspDrawSurfaces[ i ];

		/* get shader for shade angle */
		si = surfaceInfos[ i ].si;
		if ( si->shadeAngleDegrees ) {
			shadeAngle = DEG2RAD( si->shadeAngleDegrees );
		}
		else{
			shadeAngle = defaultShadeAngle;
		}
		if ( shadeAngle > maxShadeAngle ) {
			maxShadeAngle = shadeAngle;
		}

		/* flag its verts */
		for ( j = 0; j < ds->numVerts; j++ )
		{
			f = ds->firstVert + j;
			shadeAngles[ f ] = shadeAngle;
			if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
				smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
			}
		}

		/* ydnar: optional force-to-trisoup */
		if ( trisoup && ds->surfaceType == MST_PLANAR ) {
			ds->surfaceType = MST_TRIANGLE_SOUP;
			ds->lightmapNum[ 0 ] = -3;
		}
	}

	/* bail if no surfaces have a shade angle */
	if ( maxShadeAngle == 0 ) {
		free( shadeAngles );
		free( smoothed );
		return;
	}

	/* init pacifier */
	fOld = -1;
	start = I_FloatTime();

	/* go through the list of vertexes */
	for ( i = 0; i < numBSPDrawVerts; i++ )
	{
		/* print pacifier */
		f = 10 * i / numBSPDrawVerts;
		if ( f != fOld ) {
			fOld = f;
			Sys_Printf( "%i...", f );
		}

		/* already smoothed? */
		if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
			continue;
		}

		/* clear */
		VectorClear( average );
		numVerts = 0;
		numVotes = 0;

		/* build a table of coincident vertexes */
		for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
		{
			/* already smoothed? */
			if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
				continue;
			}

			/* test vertexes */
			if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
				continue;
			}

			/* use smallest shade angle */
			shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );

			/* check shade angle */
			dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
			if ( dot > 1.0 ) {
				dot = 1.0;
			}
			else if ( dot < -1.0 ) {
				dot = -1.0;
			}
			testAngle = acos( dot ) + THETA_EPSILON;
			if ( testAngle >= shadeAngle ) {
				//Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
				continue;
			}
			//Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );

			/* add to the list */
			indexes[ numVerts++ ] = j;

			/* flag vertex */
			smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );

			/* see if this normal has already been voted */
			for ( k = 0; k < numVotes; k++ )
			{
				VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
				if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
					 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
					 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
					break;
				}
			}

			/* add a new vote? */
			if ( k == numVotes && numVotes < MAX_SAMPLES ) {
				VectorAdd( average, bspDrawVerts[ j ].normal, average );
				VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
				numVotes++;
			}
		}

		/* don't average for less than 2 verts */
		if ( numVerts < 2 ) {
			continue;
		}

		/* average normal */
		if ( VectorNormalize( average, average ) > 0 ) {
			/* smooth */
			for ( j = 0; j < numVerts; j++ )
				VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
		}
	}

	/* free the tables */
	free( shadeAngles );
	free( smoothed );

	/* print time */
	Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
}



/* -------------------------------------------------------------------------------

   this section deals with phong shaded lightmap tracing

   ------------------------------------------------------------------------------- */

/* 9th rewrite (recursive subdivision of a lightmap triangle) */

/*
   CalcTangentVectors()
   calculates the st tangent vectors for normalmapping
 */

static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
	int i;
	float bb, s, t;
	vec3_t bary;


	/* calculate barycentric basis for the triangle */
	bb = ( dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ] ) * ( dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ] ) - ( dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ] ) * ( dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ] );
	if ( fabs( bb ) < 0.00000001f ) {
		return qfalse;
	}

	/* do each vertex */
	for ( i = 0; i < numVerts; i++ )
	{
		/* calculate s tangent vector */
		s = dv[ i ]->st[ 0 ] + 10.0f;
		t = dv[ i ]->st[ 1 ];
		bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
		bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
		bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;

		stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
		stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
		stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];

		VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
		VectorNormalize( stv[ i ], stv[ i ] );

		/* calculate t tangent vector */
		s = dv[ i ]->st[ 0 ];
		t = dv[ i ]->st[ 1 ] + 10.0f;
		bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
		bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
		bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;

		ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
		ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
		ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];

		VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
		VectorNormalize( ttv[ i ], ttv[ i ] );

		/* debug code */
		//%	Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
		//%		stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
	}

	/* return to caller */
	return qtrue;
}




/*
   PerturbNormal()
   perterbs the normal by the shader's normalmap in tangent space
 */

static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
	int i;
	vec4_t bump;


	/* passthrough */
	VectorCopy( dv->normal, pNormal );

	/* sample normalmap */
	if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
		return;
	}

	/* remap sampled normal from [0,255] to [-1,-1] */
	for ( i = 0; i < 3; i++ )
		bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );

	/* scale tangent vectors and add to original normal */
	VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
	VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
	VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );

	/* renormalize and return */
	VectorNormalize( pNormal, pNormal );
}



/*
   MapSingleLuxel()
   maps a luxel for triangle bv at
 */

#define NUDGE           0.5f
#define BOGUS_NUDGE     -99999.0f

static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] ){
	int i, x, y, numClusters, *clusters, pointCluster, *cluster;
	float           *luxel, *origin, *normal, d, lightmapSampleOffset;
	shaderInfo_t    *si;
	vec3_t pNormal;
	vec3_t vecs[ 3 ];
	vec3_t nudged;
	vec3_t cverts[ 3 ];
	vec3_t temp;
	vec4_t sideplane, hostplane;
	vec3_t origintwo;
	int j, next;
	float e;
	float           *nudge;
	static float nudges[][ 2 ] =
	{
		//%{ 0, 0 },		/* try center first */
		{ -NUDGE, 0 },                      /* left */
		{ NUDGE, 0 },                       /* right */
		{ 0, NUDGE },                       /* up */
		{ 0, -NUDGE },                      /* down */
		{ -NUDGE, NUDGE },                  /* left/up */
		{ NUDGE, -NUDGE },                  /* right/down */
		{ NUDGE, NUDGE },                   /* right/up */
		{ -NUDGE, -NUDGE },                 /* left/down */
		{ BOGUS_NUDGE, BOGUS_NUDGE }
	};


	/* find luxel xy coords (fixme: subtract 0.5?) */
	x = dv->lightmap[ 0 ][ 0 ];
	y = dv->lightmap[ 0 ][ 1 ];
	if ( x < 0 ) {
		x = 0;
	}
	else if ( x >= lm->sw ) {
		x = lm->sw - 1;
	}
	if ( y < 0 ) {
		y = 0;
	}
	else if ( y >= lm->sh ) {
		y = lm->sh - 1;
	}

	/* set shader and cluster list */
	if ( info != NULL ) {
		si = info->si;
		numClusters = info->numSurfaceClusters;
		clusters = &surfaceClusters[ info->firstSurfaceCluster ];
	}
	else
	{
		si = NULL;
		numClusters = 0;
		clusters = NULL;
	}

	/* get luxel, origin, cluster, and normal */
	luxel = SUPER_LUXEL( 0, x, y );
	origin = SUPER_ORIGIN( x, y );
	normal = SUPER_NORMAL( x, y );
	cluster = SUPER_CLUSTER( x, y );

	/* don't attempt to remap occluded luxels for planar surfaces */
	if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
		return ( *cluster );
	}

	/* only average the normal for premapped luxels */
	else if ( ( *cluster ) >= 0 ) {
		/* do bumpmap calculations */
		if ( stv != NULL ) {
			PerturbNormal( dv, si, pNormal, stv, ttv );
		}
		else{
			VectorCopy( dv->normal, pNormal );
		}

		/* add the additional normal data */
		VectorAdd( normal, pNormal, normal );
		luxel[ 3 ] += 1.0f;
		return ( *cluster );
	}

	/* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */

	/* get origin */

	/* axial lightmap projection */
	if ( lm->vecs != NULL ) {
		/* calculate an origin for the sample from the lightmap vectors */
		VectorCopy( lm->origin, origin );
		for ( i = 0; i < 3; i++ )
		{
			/* add unless it's the axis, which is taken care of later */
			if ( i == lm->axisNum ) {
				continue;
			}
			origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
		}

		/* project the origin onto the plane */
		d = DotProduct( origin, plane ) - plane[ 3 ];
		d /= plane[ lm->axisNum ];
		origin[ lm->axisNum ] -= d;
	}

	/* non axial lightmap projection (explicit xyz) */
	else{
		VectorCopy( dv->xyz, origin );
	}

	//////////////////////
	//27's test to make sure samples stay within the triangle boundaries
	//1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
	//2) if it does, nudge it onto the correct side.

	if ( worldverts != NULL && lightmapTriangleCheck ) {
		for ( j = 0; j < 3; j++ )
		{
			VectorCopy( worldverts[j],cverts[j] );
		}
		PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );

		for ( j = 0; j < 3; j++ )
		{
			for ( i = 0; i < 3; i++ )
			{
				//build plane using 2 edges and a normal
				next = ( i + 1 ) % 3;

				VectorCopy( cverts[next],temp );
				VectorAdd( temp,hostplane,temp );
				PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );

				//planetest sample point
				e = DotProduct( origin,sideplane );
				e = e - sideplane[3];
				if ( e > 0 ) {
					//we're bad.
					//VectorClear(origin);
					//Move the sample point back inside triangle bounds
					origin[0] -= sideplane[0] * ( e + 1 );
					origin[1] -= sideplane[1] * ( e + 1 );
					origin[2] -= sideplane[2] * ( e + 1 );
#ifdef DEBUG_27_1
					VectorClear( origin );
#endif
				}
			}
		}
	}

	////////////////////////

	/* planar surfaces have precalculated lightmap vectors for nudging */
	if ( lm->plane != NULL ) {
		VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
		VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
		VectorCopy( lm->plane, vecs[ 2 ] );
	}

	/* non-planar surfaces must calculate them */
	else
	{
		if ( plane != NULL ) {
			VectorCopy( plane, vecs[ 2 ] );
		}
		else{
			VectorCopy( dv->normal, vecs[ 2 ] );
		}
		MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
	}

	/* push the origin off the surface a bit */
	if ( si != NULL ) {
		lightmapSampleOffset = si->lightmapSampleOffset;
	}
	else{
		lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
	}
	if ( lm->axisNum < 0 ) {
		VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
	}
	else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
		origin[ lm->axisNum ] -= lightmapSampleOffset;
	}
	else{
		origin[ lm->axisNum ] += lightmapSampleOffset;
	}

	VectorCopy( origin,origintwo );
	if ( lightmapExtraVisClusterNudge ) {
		origintwo[0] += vecs[2][0];
		origintwo[1] += vecs[2][1];
		origintwo[2] += vecs[2][2];
	}

	/* get cluster */
	pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );

	/* another retarded hack, storing nudge count in luxel[ 1 ] */
	luxel[ 1 ] = 0.0f;

	/* point in solid? (except in dark mode) */
	if ( pointCluster < 0 && dark == qfalse ) {
		/* nudge the the location around */
		nudge = nudges[ 0 ];
		while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
		{
			/* nudge the vector around a bit */
			for ( i = 0; i < 3; i++ )
			{
				/* set nudged point*/
				nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
			}
			nudge += 2;

			/* get pvs cluster */
			pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
			if ( pointCluster >= 0 ) {
				VectorCopy( nudged, origin );
			}
			luxel[ 1 ] += 1.0f;
		}
	}

	/* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
	if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
		VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
		pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
		if ( pointCluster >= 0 ) {
			VectorCopy( nudged, origin );
		}
		luxel[ 1 ] += 1.0f;
	}

	/* valid? */
	if ( pointCluster < 0 ) {
		( *cluster ) = CLUSTER_OCCLUDED;
		VectorClear( origin );
		VectorClear( normal );
		numLuxelsOccluded++;
		return ( *cluster );
	}

	/* debug code */
	//%	Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );

	/* do bumpmap calculations */
	if ( stv ) {
		PerturbNormal( dv, si, pNormal, stv, ttv );
	}
	else{
		VectorCopy( dv->normal, pNormal );
	}

	/* store the cluster and normal */
	( *cluster ) = pointCluster;
	VectorCopy( pNormal, normal );

	/* store explicit mapping pass and implicit mapping pass */
	luxel[ 0 ] = pass;
	luxel[ 3 ] = 1.0f;

	/* add to count */
	numLuxelsMapped++;

	/* return ok */
	return ( *cluster );
}



/*
   MapTriangle_r()
   recursively subdivides a triangle until its edges are shorter
   than the distance between two luxels (thanks jc :)
 */

static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] ){
	bspDrawVert_t mid, *dv2[ 3 ];
	int max;


	/* map the vertexes */
	#if 0
	MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
	MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
	MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
	#endif

	/* subdivide calc */
	{
		int i;
		float       *a, *b, dx, dy, dist, maxDist;


		/* find the longest edge and split it */
		max = -1;
		maxDist = 0;
		for ( i = 0; i < 3; i++ )
		{
			/* get verts */
			a = dv[ i ]->lightmap[ 0 ];
			b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];

			/* get dists */
			dx = a[ 0 ] - b[ 0 ];
			dy = a[ 1 ] - b[ 1 ];
			dist = ( dx * dx ) + ( dy * dy );   //% sqrt( (dx * dx) + (dy * dy) );

			/* longer? */
			if ( dist > maxDist ) {
				maxDist = dist;
				max = i;
			}
		}

		/* try to early out */
		if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
			return;
		}
	}

	/* split the longest edge and map it */
	LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
	MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );

	/* push the point up a little bit to account for fp creep (fixme: revisit this) */
	//%	VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );

	/* recurse to first triangle */
	VectorCopy( dv, dv2 );
	dv2[ max ] = &mid;
	MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );

	/* recurse to second triangle */
	VectorCopy( dv, dv2 );
	dv2[ ( max + 1 ) % 3 ] = &mid;
	MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
}



/*
   MapTriangle()
   seed function for MapTriangle_r()
   requires a cw ordered triangle
 */

static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
	int i;
	vec4_t plane;
	vec3_t          *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
	vec3_t worldverts[ 3 ];


	/* get plane if possible */
	if ( lm->plane != NULL ) {
		VectorCopy( lm->plane, plane );
		plane[ 3 ] = lm->plane[ 3 ];
	}

	/* otherwise make one from the points */
	else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
		return qfalse;
	}

	/* check to see if we need to calculate texture->world tangent vectors */
	if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
		stv = stvStatic;
		ttv = ttvStatic;
	}
	else
	{
		stv = NULL;
		ttv = NULL;
	}

	VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
	VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
	VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );

	/* map the vertexes */
	MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
	MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
	MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );

	/* 2002-11-20: prefer axial triangle edges */
	if ( mapNonAxial ) {
		/* subdivide the triangle */
		MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
		return qtrue;
	}

	for ( i = 0; i < 3; i++ )
	{
		float           *a, *b;
		bspDrawVert_t   *dv2[ 3 ];


		/* get verts */
		a = dv[ i ]->lightmap[ 0 ];
		b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];

		/* make degenerate triangles for mapping edges */
		if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
			dv2[ 0 ] = dv[ i ];
			dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
			dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];

			/* map the degenerate triangle */
			MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
		}
	}

	return qtrue;
}



/*
   MapQuad_r()
   recursively subdivides a quad until its edges are shorter
   than the distance between two luxels
 */

static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] ){
	bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
	int max;


	/* subdivide calc */
	{
		int i;
		float       *a, *b, dx, dy, dist, maxDist;


		/* find the longest edge and split it */
		max = -1;
		maxDist = 0;
		for ( i = 0; i < 4; i++ )
		{
			/* get verts */
			a = dv[ i ]->lightmap[ 0 ];
			b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];

			/* get dists */
			dx = a[ 0 ] - b[ 0 ];
			dy = a[ 1 ] - b[ 1 ];
			dist = ( dx * dx ) + ( dy * dy );   //% sqrt( (dx * dx) + (dy * dy) );

			/* longer? */
			if ( dist > maxDist ) {
				maxDist = dist;
				max = i;
			}
		}

		/* try to early out */
		if ( max < 0 || maxDist <= subdivideThreshold ) {
			return;
		}
	}

	/* we only care about even/odd edges */
	max &= 1;

	/* split the longest edges */
	LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
	LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );

	/* map the vertexes */
	MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
	MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );

	/* 0 and 2 */
	if ( max == 0 ) {
		/* recurse to first quad */
		dv2[ 0 ] = dv[ 0 ];
		dv2[ 1 ] = &mid[ 0 ];
		dv2[ 2 ] = &mid[ 1 ];
		dv2[ 3 ] = dv[ 3 ];
		MapQuad_r( lm, info, dv2, plane, stv, ttv );

		/* recurse to second quad */
		dv2[ 0 ] = &mid[ 0 ];
		dv2[ 1 ] = dv[ 1 ];
		dv2[ 2 ] = dv[ 2 ];
		dv2[ 3 ] = &mid[ 1 ];
		MapQuad_r( lm, info, dv2, plane, stv, ttv );
	}

	/* 1 and 3 */
	else
	{
		/* recurse to first quad */
		dv2[ 0 ] = dv[ 0 ];
		dv2[ 1 ] = dv[ 1 ];
		dv2[ 2 ] = &mid[ 0 ];
		dv2[ 3 ] = &mid[ 1 ];
		MapQuad_r( lm, info, dv2, plane, stv, ttv );

		/* recurse to second quad */
		dv2[ 0 ] = &mid[ 1 ];
		dv2[ 1 ] = &mid[ 0 ];
		dv2[ 2 ] = dv[ 2 ];
		dv2[ 3 ] = dv[ 3 ];
		MapQuad_r( lm, info, dv2, plane, stv, ttv );
	}
}



/*
   MapQuad()
   seed function for MapQuad_r()
   requires a cw ordered triangle quad
 */

#define QUAD_PLANAR_EPSILON     0.5f

static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
	float dist;
	vec4_t plane;
	vec3_t          *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];


	/* get plane if possible */
	if ( lm->plane != NULL ) {
		VectorCopy( lm->plane, plane );
		plane[ 3 ] = lm->plane[ 3 ];
	}

	/* otherwise make one from the points */
	else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
		return qfalse;
	}

	/* 4th point must fall on the plane */
	dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
	if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
		return qfalse;
	}

	/* check to see if we need to calculate texture->world tangent vectors */
	if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
		stv = stvStatic;
		ttv = ttvStatic;
	}
	else
	{
		stv = NULL;
		ttv = NULL;
	}

	/* map the vertexes */
	MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
	MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
	MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
	MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );

	/* subdivide the quad */
	MapQuad_r( lm, info, dv, plane, stv, ttv );
	return qtrue;
}



/*
   MapRawLightmap()
   maps the locations, normals, and pvs clusters for a raw lightmap
 */

#define VectorDivide( in, d, out )  VectorScale( in, ( 1.0f / ( d ) ), out )    //%	(out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)

void MapRawLightmap( int rawLightmapNum ){
	int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
	float               *luxel, *origin, *normal, samples, radius, pass;
	rawLightmap_t       *lm;
	bspDrawSurface_t    *ds;
	surfaceInfo_t       *info;
	mesh_t src, *subdivided, *mesh;
	bspDrawVert_t       *verts, *dv[ 4 ], fake;


	/* bail if this number exceeds the number of raw lightmaps */
	if ( rawLightmapNum >= numRawLightmaps ) {
		return;
	}

	/* get lightmap */
	lm = &rawLightmaps[ rawLightmapNum ];

	/* -----------------------------------------------------------------
	   map referenced surfaces onto the raw lightmap
	   ----------------------------------------------------------------- */

	/* walk the list of surfaces on this raw lightmap */
	for ( n = 0; n < lm->numLightSurfaces; n++ )
	{
		/* with > 1 surface per raw lightmap, clear occluded */
		if ( n > 0 ) {
			for ( y = 0; y < lm->sh; y++ )
			{
				for ( x = 0; x < lm->sw; x++ )
				{
					/* get cluster */
					cluster = SUPER_CLUSTER( x, y );
					if ( *cluster < 0 ) {
						*cluster = CLUSTER_UNMAPPED;
					}
				}
			}
		}

		/* get surface */
		num = lightSurfaces[ lm->firstLightSurface + n ];
		ds = &bspDrawSurfaces[ num ];
		info = &surfaceInfos[ num ];

		/* bail if no lightmap to calculate */
		if ( info->lm != lm ) {
			Sys_Printf( "!" );
			continue;
		}

		/* map the surface onto the lightmap origin/cluster/normal buffers */
		switch ( ds->surfaceType )
		{
		case MST_PLANAR:
			/* get verts */
			verts = yDrawVerts + ds->firstVert;

			/* map the triangles */
			for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
			{
				for ( i = 0; i < ds->numIndexes; i += 3 )
				{
					dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
					dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
					dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
					MapTriangle( lm, info, dv, mapNonAxial );
				}
			}
			break;

		case MST_PATCH:
			/* make a mesh from the drawsurf */
			src.width = ds->patchWidth;
			src.height = ds->patchHeight;
			src.verts = &yDrawVerts[ ds->firstVert ];
			//%	subdivided = SubdivideMesh( src, 8, 512 );
			subdivided = SubdivideMesh2( src, info->patchIterations );

			/* fit it to the curve and remove colinear verts on rows/columns */
			PutMeshOnCurve( *subdivided );
			mesh = RemoveLinearMeshColumnsRows( subdivided );
			FreeMesh( subdivided );

			/* get verts */
			verts = mesh->verts;

			/* debug code */
				#if 0
			if ( lm->plane ) {
				Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
							lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
							lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
							lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
			}
				#endif

			/* map the mesh quads */
				#if 0

			for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
			{
				for ( y = 0; y < ( mesh->height - 1 ); y++ )
				{
					for ( x = 0; x < ( mesh->width - 1 ); x++ )
					{
						/* set indexes */
						pw[ 0 ] = x + ( y * mesh->width );
						pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
						pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
						pw[ 3 ] = x + 1 + ( y * mesh->width );
						pw[ 4 ] = x + ( y * mesh->width );      /* same as pw[ 0 ] */

						/* set radix */
						r = ( x + y ) & 1;

						/* get drawverts and map first triangle */
						dv[ 0 ] = &verts[ pw[ r + 0 ] ];
						dv[ 1 ] = &verts[ pw[ r + 1 ] ];
						dv[ 2 ] = &verts[ pw[ r + 2 ] ];
						MapTriangle( lm, info, dv, mapNonAxial );

						/* get drawverts and map second triangle */
						dv[ 0 ] = &verts[ pw[ r + 0 ] ];
						dv[ 1 ] = &verts[ pw[ r + 2 ] ];
						dv[ 2 ] = &verts[ pw[ r + 3 ] ];
						MapTriangle( lm, info, dv, mapNonAxial );
					}
				}
			}

				#else

			for ( y = 0; y < ( mesh->height - 1 ); y++ )
			{
				for ( x = 0; x < ( mesh->width - 1 ); x++ )
				{
					/* set indexes */
					pw[ 0 ] = x + ( y * mesh->width );
					pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
					pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
					pw[ 3 ] = x + 1 + ( y * mesh->width );
					pw[ 4 ] = pw[ 0 ];

					/* set radix */
					r = ( x + y ) & 1;

					/* attempt to map quad first */
					dv[ 0 ] = &verts[ pw[ r + 0 ] ];
					dv[ 1 ] = &verts[ pw[ r + 1 ] ];
					dv[ 2 ] = &verts[ pw[ r + 2 ] ];
					dv[ 3 ] = &verts[ pw[ r + 3 ] ];
					if ( MapQuad( lm, info, dv ) ) {
						continue;
					}

					for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
					{
						/* get drawverts and map first triangle */
						dv[ 1 ] = &verts[ pw[ r + 1 ] ];
						dv[ 2 ] = &verts[ pw[ r + 2 ] ];
						MapTriangle( lm, info, dv, mapNonAxial );

						/* get drawverts and map second triangle */
						dv[ 1 ] = &verts[ pw[ r + 2 ] ];
						dv[ 2 ] = &verts[ pw[ r + 3 ] ];
						MapTriangle( lm, info, dv, mapNonAxial );
					}
				}
			}

				#endif

			/* free the mesh */
			FreeMesh( mesh );
			break;

		default:
			break;
		}
	}

	/* -----------------------------------------------------------------
	   average and clean up luxel normals
	   ----------------------------------------------------------------- */

	/* walk the luxels */
	for ( y = 0; y < lm->sh; y++ )
	{
		for ( x = 0; x < lm->sw; x++ )
		{
			/* get luxel */
			luxel = SUPER_LUXEL( 0, x, y );
			normal = SUPER_NORMAL( x, y );
			cluster = SUPER_CLUSTER( x, y );

			/* only look at mapped luxels */
			if ( *cluster < 0 ) {
				continue;
			}

			/* the normal data could be the sum of multiple samples */
			if ( luxel[ 3 ] > 1.0f ) {
				VectorNormalize( normal, normal );
			}

			/* mark this luxel as having only one normal */
			luxel[ 3 ] = 1.0f;
		}
	}

	/* non-planar surfaces stop here */
	if ( lm->plane == NULL ) {
		return;
	}

	/* -----------------------------------------------------------------
	   map occluded or unuxed luxels
	   ----------------------------------------------------------------- */

	/* walk the luxels */
	/* FIXME: superSample is int, no need in floor() */
	radius = floor( superSample / 2 );
	radius = radius > 0 ? radius : 1.0f;
	radius += 1.0f;
	for ( pass = 2.0f; pass <= radius; pass += 1.0f )
	{
		for ( y = 0; y < lm->sh; y++ )
		{
			for ( x = 0; x < lm->sw; x++ )
			{
				/* get luxel */
				luxel = SUPER_LUXEL( 0, x, y );
				normal = SUPER_NORMAL( x, y );
				cluster = SUPER_CLUSTER( x, y );

				/* only look at unmapped luxels */
				if ( *cluster != CLUSTER_UNMAPPED ) {
					continue;
				}

				/* divine a normal and origin from neighboring luxels */
				VectorClear( fake.xyz );
				VectorClear( fake.normal );
				fake.lightmap[ 0 ][ 0 ] = x;    //% 0.0001 + x;
				fake.lightmap[ 0 ][ 1 ] = y;    //% 0.0001 + y;
				samples = 0.0f;
				for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
				{
					if ( sy < 0 || sy >= lm->sh ) {
						continue;
					}

					for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
					{
						if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
							continue;
						}

						/* get neighboring luxel */
						luxel = SUPER_LUXEL( 0, sx, sy );
						origin = SUPER_ORIGIN( sx, sy );
						normal = SUPER_NORMAL( sx, sy );
						cluster = SUPER_CLUSTER( sx, sy );

						/* only consider luxels mapped in previous passes */
						if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
							continue;
						}

						/* add its distinctiveness to our own */
						VectorAdd( fake.xyz, origin, fake.xyz );
						VectorAdd( fake.normal, normal, fake.normal );
						samples += luxel[ 3 ];
					}
				}

				/* any samples? */
				if ( samples == 0.0f ) {
					continue;
				}

				/* average */
				VectorDivide( fake.xyz, samples, fake.xyz );
				//%	VectorDivide( fake.normal, samples, fake.normal );
				if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
					continue;
				}

				/* map the fake vert */
				MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
			}
		}
	}

	/* -----------------------------------------------------------------
	   average and clean up luxel normals
	   ----------------------------------------------------------------- */

	/* walk the luxels */
	for ( y = 0; y < lm->sh; y++ )
	{
		for ( x = 0; x < lm->sw; x++ )
		{
			/* get luxel */
			luxel = SUPER_LUXEL( 0, x, y );
			normal = SUPER_NORMAL( x, y );
			cluster = SUPER_CLUSTER( x, y );

			/* only look at mapped luxels */
			if ( *cluster < 0 ) {
				continue;
			}

			/* the normal data could be the sum of multiple samples */
			if ( luxel[ 3 ] > 1.0f ) {
				VectorNormalize( normal, normal );
			}

			/* mark this luxel as having only one normal */
			luxel[ 3 ] = 1.0f;
		}
	}

	/* debug code */
	#if 0
	Sys_Printf( "\n" );
	for ( y = 0; y < lm->sh; y++ )
	{
		for ( x = 0; x < lm->sw; x++ )
		{
			vec3_t mins, maxs;


			cluster = SUPER_CLUSTER( x, y );
			origin = SUPER_ORIGIN( x, y );
			normal = SUPER_NORMAL( x, y );
			luxel = SUPER_LUXEL( x, y );

			if ( *cluster < 0 ) {
				continue;
			}

			/* check if within the bounding boxes of all surfaces referenced */
			ClearBounds( mins, maxs );
			for ( n = 0; n < lm->numLightSurfaces; n++ )
			{
				int TOL;
				info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
				TOL = info->sampleSize + 2;
				AddPointToBounds( info->mins, mins, maxs );
				AddPointToBounds( info->maxs, mins, maxs );
				if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
					 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
					 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
					break;
				}
			}

			/* inside? */
			if ( n < lm->numLightSurfaces ) {
				continue;
			}

			/* report bogus origin */
			Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
						rawLightmapNum, x, y, *cluster,
						origin[ 0 ], origin[ 1 ], origin[ 2 ],
						mins[ 0 ], mins[ 1 ], mins[ 2 ],
						maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
						luxel[ 3 ] );
		}
	}
	#endif
}



/*
   SetupDirt()
   sets up dirtmap (ambient occlusion)
 */

#define DIRT_CONE_ANGLE             88  /* degrees */
#define DIRT_NUM_ANGLE_STEPS        16
#define DIRT_NUM_ELEVATION_STEPS    3
#define DIRT_NUM_VECTORS            ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )

static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
static int numDirtVectors = 0;

void SetupDirt( void ){
	int i, j;
	float angle, elevation, angleStep, elevationStep;


	/* note it */
	Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );

	/* calculate angular steps */
	angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
	elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );

	/* iterate angle */
	angle = 0.0f;
	for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
	{
		/* iterate elevation */
		for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
		{
			dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
			dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
			dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
			numDirtVectors++;
		}
	}

	/* emit some statistics */
	Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
}


/*
   DirtForSample()
   calculates dirt value for a given sample
 */

float DirtForSample( trace_t *trace ){
	int i;
	float gatherDirt, outDirt, angle, elevation, ooDepth;
	vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;


	/* dummy check */
	if ( !dirty ) {
		return 1.0f;
	}
	if ( trace == NULL || trace->cluster < 0 ) {
		return 0.0f;
	}

	/* setup */
	gatherDirt = 0.0f;
	ooDepth = 1.0f / dirtDepth;
	VectorCopy( trace->normal, normal );

	/* check if the normal is aligned to the world-up */
	if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
		if ( normal[ 2 ] == 1.0f ) {
			VectorSet( myRt, 1.0f, 0.0f, 0.0f );
			VectorSet( myUp, 0.0f, 1.0f, 0.0f );
		}
		else if ( normal[ 2 ] == -1.0f ) {
			VectorSet( myRt, -1.0f, 0.0f, 0.0f );
			VectorSet( myUp,  0.0f, 1.0f, 0.0f );
		}
	}
	else
	{
		VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
		CrossProduct( normal, worldUp, myRt );
		VectorNormalize( myRt, myRt );
		CrossProduct( myRt, normal, myUp );
		VectorNormalize( myUp, myUp );
	}

	/* 1 = random mode, 0 (well everything else) = non-random mode */
	if ( dirtMode == 1 ) {
		/* iterate */
		for ( i = 0; i < numDirtVectors; i++ )
		{
			/* get random vector */
			angle = Random() * DEG2RAD( 360.0f );
			elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
			temp[ 0 ] = cos( angle ) * sin( elevation );
			temp[ 1 ] = sin( angle ) * sin( elevation );
			temp[ 2 ] = cos( elevation );

			/* transform into tangent space */
			direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
			direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
			direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];

			/* set endpoint */
			VectorMA( trace->origin, dirtDepth, direction, trace->end );
			SetupTrace( trace );
			VectorSet(trace->color, 1.0f, 1.0f, 1.0f);

			/* trace */
			TraceLine( trace );
			if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
				VectorSubtract( trace->hit, trace->origin, displacement );
				gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
			}
		}
	}
	else
	{
		/* iterate through ordered vectors */
		for ( i = 0; i < numDirtVectors; i++ )
		{
			/* transform vector into tangent space */
			direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
			direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
			direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];

			/* set endpoint */
			VectorMA( trace->origin, dirtDepth, direction, trace->end );
			SetupTrace( trace );
			VectorSet(trace->color, 1.0f, 1.0f, 1.0f);

			/* trace */
			TraceLine( trace );
			if ( trace->opaque ) {
				VectorSubtract( trace->hit, trace->origin, displacement );
				gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
			}
		}
	}

	/* direct ray */
	VectorMA( trace->origin, dirtDepth, normal, trace->end );
	SetupTrace( trace );
	VectorSet(trace->color, 1.0f, 1.0f, 1.0f);

	/* trace */
	TraceLine( trace );
	if ( trace->opaque ) {
		VectorSubtract( trace->hit, trace->origin, displacement );
		gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
	}

	/* early out */
	if ( gatherDirt <= 0.0f ) {
		return 1.0f;
	}

	/* apply gain (does this even do much? heh) */
	outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
	if ( outDirt > 1.0f ) {
		outDirt = 1.0f;
	}

	/* apply scale */
	outDirt *= dirtScale;
	if ( outDirt > 1.0f ) {
		outDirt = 1.0f;
	}

	/* return to sender */
	return 1.0f - outDirt;
}



/*
   DirtyRawLightmap()
   calculates dirty fraction for each luxel
 */

void DirtyRawLightmap( int rawLightmapNum ){
	int i, x, y, sx, sy, *cluster;
	float               *origin, *normal, *dirt, *dirt2, average, samples;
	rawLightmap_t       *lm;
	surfaceInfo_t       *info;
	trace_t trace;
	qboolean noDirty;


	/* bail if this number exceeds the number of raw lightmaps */
	if ( rawLightmapNum >= numRawLightmaps ) {
		return;
	}

	/* get lightmap */
	lm = &rawLightmaps[ rawLightmapNum ];

	/* setup trace */
	trace.testOcclusion = qtrue;
	trace.forceSunlight = qfalse;
	trace.recvShadows = lm->recvShadows;
	trace.numSurfaces = lm->numLightSurfaces;
	trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
	trace.inhibitRadius = 0.0f;
	trace.testAll = qfalse;

	/* twosided lighting (may or may not be a good idea for lightmapped stuff) */
	trace.twoSided = qfalse;
	for ( i = 0; i < trace.numSurfaces; i++ )
	{
		/* get surface */
		info = &surfaceInfos[ trace.surfaces[ i ] ];

		/* check twosidedness */
		if ( info->si->twoSided ) {
			trace.twoSided = qtrue;
			break;
		}
	}

	noDirty = qfalse;
	for ( i = 0; i < trace.numSurfaces; i++ )
	{
		/* get surface */
		info = &surfaceInfos[ trace.surfaces[ i ] ];

		/* check twosidedness */
		if ( info->si->noDirty ) {
			noDirty = qtrue;
			break;
		}
	}

	/* gather dirt */
	for ( y = 0; y < lm->sh; y++ )
	{
		for ( x = 0; x < lm->sw; x++ )
		{
			/* get luxel */
			cluster = SUPER_CLUSTER( x, y );
			origin = SUPER_ORIGIN( x, y );
			normal = SUPER_NORMAL( x, y );
			dirt = SUPER_DIRT( x, y );

			/* set default dirt */
			*dirt = 0.0f;

			/* only look at mapped luxels */
			if ( *cluster < 0 ) {
				continue;
			}

			/* don't apply dirty on this surface */
			if ( noDirty ) {
				*dirt = 1.0f;
				continue;
			}

			/* copy to trace */
			trace.cluster = *cluster;
			VectorCopy( origin, trace.origin );
			VectorCopy( normal, trace.normal );

			/* get dirt */
			*dirt = DirtForSample( &trace );
		}
	}

	/* testing no filtering */
	//%	return;

	/* filter dirt */
	for ( y = 0; y < lm->sh; y++ )
	{
		for ( x = 0; x < lm->sw; x++ )
		{
			/* get luxel */
			cluster = SUPER_CLUSTER( x, y );
			dirt = SUPER_DIRT( x, y );

			/* filter dirt by adjacency to unmapped luxels */
			average = *dirt;
			samples = 1.0f;
			for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
			{
				if ( sy < 0 || sy >= lm->sh ) {
					continue;
				}

				for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
				{
					if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
						continue;
					}

					/* get neighboring luxel */
					cluster = SUPER_CLUSTER( sx, sy );
					dirt2 = SUPER_DIRT( sx, sy );
					if ( *cluster < 0 || *dirt2 <= 0.0f ) {
						continue;
					}

					/* add it */
					average += *dirt2;
					samples += 1.0f;
				}

				/* bail */
				if ( samples <= 0.0f ) {
					break;
				}
			}

			/* bail */
			if ( samples <= 0.0f ) {
				continue;
			}

			/* scale dirt */
			*dirt = average / samples;
		}
	}
}



/*
   SubmapRawLuxel()
   calculates the pvs cluster, origin, normal of a sub-luxel
 */

static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
	int i, *cluster, *cluster2;
	float       *origin, *origin2, *normal; //%	, *normal2;
	vec3_t originVecs[ 2 ];                 //%	, normalVecs[ 2 ];

	vec3_t origin_dummy = {};

	/* calulate x vector */
	if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
		cluster = SUPER_CLUSTER( x, y );
		origin = SUPER_ORIGIN( x, y );
		//%	normal = SUPER_NORMAL( x, y );
		cluster2 = SUPER_CLUSTER( x + 1, y );
		origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
		//%	normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
	}
	else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
		cluster = SUPER_CLUSTER( x - 1, y );
		origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
		//%	normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
		cluster2 = SUPER_CLUSTER( x, y );
		origin2 = SUPER_ORIGIN( x, y );
		//%	normal2 = SUPER_NORMAL( x, y );
	}
	else {
		Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
		origin = origin_dummy;
		origin2 = origin_dummy;
	}

	VectorSubtract( origin2, origin, originVecs[ 0 ] );
	//%	VectorSubtract( normal2, normal, normalVecs[ 0 ] );

	/* calulate y vector */
	if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
		cluster = SUPER_CLUSTER( x, y );
		origin = SUPER_ORIGIN( x, y );
		//%	normal = SUPER_NORMAL( x, y );
		cluster2 = SUPER_CLUSTER( x, y + 1 );
		origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
		//%	normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
	}
	else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
		cluster = SUPER_CLUSTER( x, y - 1 );
		origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
		//%	normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
		cluster2 = SUPER_CLUSTER( x, y );
		origin2 = SUPER_ORIGIN( x, y );
		//%	normal2 = SUPER_NORMAL( x, y );
	}
	else{
		Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
	}

	VectorSubtract( origin2, origin, originVecs[ 1 ] );

	/* calculate new origin */
	for ( i = 0; i < 3; i++ )
		sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );

	/* get cluster */
	*sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
	if ( *sampleCluster < 0 ) {
		return qfalse;
	}

	/* calculate new normal */
	normal = SUPER_NORMAL( x, y );
	VectorCopy( normal, sampleNormal );

	/* return ok */
	return qtrue;
}


/*
   SubsampleRawLuxel_r()
   recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
 */

static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
	int b, samples, mapped, lighted;
	int cluster[ 4 ];
	vec4_t luxel[ 4 ];
	vec3_t deluxel[ 4 ];
	vec3_t origin[ 4 ], normal[ 4 ];
	float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
	vec3_t color, direction = { 0, 0, 0 }, total;


	/* limit check */
	if ( lightLuxel[ 3 ] >= lightSamples ) {
		return;
	}

	/* setup */
	VectorClear( total );
	mapped = 0;
	lighted = 0;

	/* make 2x2 subsample stamp */
	for ( b = 0; b < 4; b++ )
	{
		/* set origin */
		VectorCopy( sampleOrigin, origin[ b ] );

		/* calculate position */
		if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
			cluster[ b ] = -1;
			continue;
		}
		mapped++;

		/* increment sample count */
		luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;

		/* setup trace */
		trace->cluster = *cluster;
		VectorCopy( origin[ b ], trace->origin );
		VectorCopy( normal[ b ], trace->normal );

		/* sample light */

		LightContributionToSample( trace );
		if ( trace->forceSubsampling > 1.0f ) {
			/* alphashadow: we subsample as deep as we can */
			++lighted;
			++mapped;
			++mapped;
		}

		/* add to totals (fixme: make contrast function) */
		VectorCopy( trace->color, luxel[ b ] );
		if ( lightDeluxel ) {
			VectorCopy( trace->directionContribution, deluxel[ b ] );
		}
		VectorAdd( total, trace->color, total );
		if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
			lighted++;
		}
	}

	/* subsample further? */
	if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
		 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
		 lighted != 0 && lighted != mapped ) {
		for ( b = 0; b < 4; b++ )
		{
			if ( cluster[ b ] < 0 ) {
				continue;
			}
			SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
		}
	}

	/* average */
	//%	VectorClear( color );
	//%	samples = 0;
	VectorCopy( lightLuxel, color );
	if ( lightDeluxel ) {
		VectorCopy( lightDeluxel, direction );
	}
	samples = 1;
	for ( b = 0; b < 4; b++ )
	{
		if ( cluster[ b ] < 0 ) {
			continue;
		}
		VectorAdd( color, luxel[ b ], color );
		if ( lightDeluxel ) {
			VectorAdd( direction, deluxel[ b ], direction );
		}
		samples++;
	}

	/* add to luxel */
	if ( samples > 0 ) {
		/* average */
		color[ 0 ] /= samples;
		color[ 1 ] /= samples;
		color[ 2 ] /= samples;

		/* add to color */
		VectorCopy( color, lightLuxel );
		lightLuxel[ 3 ] += 1.0f;

		if ( lightDeluxel ) {
			direction[ 0 ] /= samples;
			direction[ 1 ] /= samples;
			direction[ 2 ] /= samples;
			VectorCopy( direction, lightDeluxel );
		}
	}
}

/* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
static void GaussLikeRandom( float sigma, float *x, float *y ){
	float r;
	r = Random() * 2 * Q_PI;
	*x = sigma * 2.73861278752581783822 * cos( r );
	*y = sigma * 2.73861278752581783822 * sin( r );
	r = Random();
	r = 1 - sqrt( r );
	r = 1 - sqrt( r );
	*x *= r;
	*y *= r;
}
static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
	int b, mapped;
	int cluster;
	vec3_t origin, normal;
	vec3_t total, totaldirection;
	float dx, dy;

	VectorClear( total );
	VectorClear( totaldirection );
	mapped = 0;
	for ( b = 0; b < lightSamples; ++b )
	{
		/* set origin */
		VectorCopy( sampleOrigin, origin );
		GaussLikeRandom( bias, &dx, &dy );

		/* calculate position */
		if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
			cluster = -1;
			continue;
		}
		mapped++;

		trace->cluster = cluster;
		VectorCopy( origin, trace->origin );
		VectorCopy( normal, trace->normal );

		LightContributionToSample( trace );
		VectorAdd( total, trace->color, total );
		if ( lightDeluxel ) {
			VectorAdd( totaldirection, trace->directionContribution, totaldirection );
		}
	}

	/* add to luxel */
	if ( mapped > 0 ) {
		/* average */
		lightLuxel[ 0 ] = total[ 0 ] / mapped;
		lightLuxel[ 1 ] = total[ 1 ] / mapped;
		lightLuxel[ 2 ] = total[ 2 ] / mapped;

		if ( lightDeluxel ) {
			lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
			lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
			lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
		}
	}
}



/*
   IlluminateRawLightmap()
   illuminates the luxels
 */

#define STACK_LL_SIZE           ( SUPER_LUXEL_SIZE * 64 * 64 )
#define LIGHT_LUXEL( x, y )     ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
#define LIGHT_DELUXEL( x, y )       ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )

void IlluminateRawLightmap( int rawLightmapNum ){
	int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
	int                 *cluster, *cluster2, mapped, lighted, totalLighted;
	size_t llSize, ldSize;
	rawLightmap_t       *lm;
	surfaceInfo_t       *info;
	qboolean filterColor, filterDir;
	float brightness;
	float               *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
	unsigned char           *flag;
	float               *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
	vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
	float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
	trace_t trace;
	float stackLightLuxels[ STACK_LL_SIZE ];


	/* bail if this number exceeds the number of raw lightmaps */
	if ( rawLightmapNum >= numRawLightmaps ) {
		return;
	}

	/* get lightmap */
	lm = &rawLightmaps[ rawLightmapNum ];

	/* setup trace */
	trace.testOcclusion = !noTrace;
	trace.forceSunlight = qfalse;
	trace.recvShadows = lm->recvShadows;
	trace.numSurfaces = lm->numLightSurfaces;
	trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
	trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;

	/* twosided lighting (may or may not be a good idea for lightmapped stuff) */
	trace.twoSided = qfalse;
	for ( i = 0; i < trace.numSurfaces; i++ )
	{
		/* get surface */
		info = &surfaceInfos[ trace.surfaces[ i ] ];

		/* check twosidedness */
		if ( info->si->twoSided ) {
			trace.twoSided = qtrue;
			break;
		}
	}

	/* create a culled light list for this raw lightmap */
	CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );

	/* -----------------------------------------------------------------
	   fill pass
	   ----------------------------------------------------------------- */

	/* set counts */
	numLuxelsIlluminated += ( lm->sw * lm->sh );

	/* test debugging state */
	if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
		/* debug fill the luxels */
		for ( y = 0; y < lm->sh; y++ )
		{
			for ( x = 0; x < lm->sw; x++ )
			{
				/* get cluster */
				cluster = SUPER_CLUSTER( x, y );

				/* only fill mapped luxels */
				if ( *cluster < 0 ) {
					continue;
				}

				/* get particulars */
				luxel = SUPER_LUXEL( 0, x, y );
				origin = SUPER_ORIGIN( x, y );
				normal = SUPER_NORMAL( x, y );

				/* color the luxel with raw lightmap num? */
				if ( debugSurfaces ) {
					VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
				}

				/* color the luxel with lightmap axis? */
				else if ( debugAxis ) {
					luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
					luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
					luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
				}

				/* color the luxel with luxel cluster? */
				else if ( debugCluster ) {
					VectorCopy( debugColors[ *cluster % 12 ], luxel );
				}

				/* color the luxel with luxel origin? */
				else if ( debugOrigin ) {
					VectorSubtract( lm->maxs, lm->mins, temp );
					VectorScale( temp, ( 1.0f / 255.0f ), temp );
					VectorSubtract( origin, lm->mins, temp2 );
					luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
					luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
					luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
				}

				/* color the luxel with the normal */
				else if ( normalmap ) {
					luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
					luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
					luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
				}

				/* otherwise clear it */
				else{
					VectorClear( luxel );
				}

				/* add to counts */
				luxel[ 3 ] = 1.0f;
			}
		}
	}
	else
	{
		/* allocate temporary per-light luxel storage */
		llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
		ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
		if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
			lightLuxels = stackLightLuxels;
		}
		else{
			lightLuxels = safe_malloc( llSize );
		}
		if ( deluxemap ) {
			lightDeluxels = safe_malloc( ldSize );
		}
		else{
			lightDeluxels = NULL;
		}

		/* clear luxels */
		//%	memset( lm->superLuxels[ 0 ], 0, llSize );

		/* set ambient color */
		for ( y = 0; y < lm->sh; y++ )
		{
			for ( x = 0; x < lm->sw; x++ )
			{
				/* get cluster */
				cluster = SUPER_CLUSTER( x, y );
				luxel = SUPER_LUXEL( 0, x, y );
				normal = SUPER_NORMAL( x, y );
				deluxel = SUPER_DELUXEL( x, y );

				/* blacken unmapped clusters */
				if ( *cluster < 0 ) {
					VectorClear( luxel );
				}

				/* set ambient */
				else
				{
					VectorCopy( ambientColor, luxel );
					if ( deluxemap ) {
						brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );

						// use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
						if ( brightness < 0.00390625f ) {
							brightness = 0.00390625f;
						}

						VectorScale( normal, brightness, deluxel );
					}
					luxel[ 3 ] = 1.0f;
				}
			}
		}

		/* clear styled lightmaps */
		size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
		for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
		{
			if ( lm->superLuxels[ lightmapNum ] != NULL ) {
				memset( lm->superLuxels[ lightmapNum ], 0, size );
			}
		}

		/* debugging code */
		//%	if( trace.numLights <= 0 )
		//%		Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );

		/* walk light list */
		for ( i = 0; i < trace.numLights; i++ )
		{
			/* setup trace */
			trace.light = trace.lights[ i ];

			/* style check */
			for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
			{
				if ( lm->styles[ lightmapNum ] == trace.light->style ||
					 lm->styles[ lightmapNum ] == LS_NONE ) {
					break;
				}
			}

			/* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
			if ( lightmapNum >= MAX_LIGHTMAPS ) {
				Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
				continue;
			}

			/* setup */
			memset( lightLuxels, 0, llSize );
			if ( deluxemap ) {
				memset( lightDeluxels, 0, ldSize );
			}
			totalLighted = 0;

			/* determine filter radius */
			filterRadius = lm->filterRadius > trace.light->filterRadius
						   ? lm->filterRadius
						   : trace.light->filterRadius;
			if ( filterRadius < 0.0f ) {
				filterRadius = 0.0f;
			}

			/* set luxel filter radius */
			luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
			if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
				luxelFilterRadius = 1;
			}

			/* allocate sampling flags storage */
			if ( lightSamples > 1 || lightRandomSamples ) {
				size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
				if ( lm->superFlags == NULL ) {
					lm->superFlags = safe_malloc( size );
				}
				memset( (void *) lm->superFlags, 0, size );
			}

			/* initial pass, one sample per luxel */
			for ( y = 0; y < lm->sh; y++ )
			{
				for ( x = 0; x < lm->sw; x++ )
				{
					/* get cluster */
					cluster = SUPER_CLUSTER( x, y );
					if ( *cluster < 0 ) {
						continue;
					}

					/* get particulars */
					lightLuxel = LIGHT_LUXEL( x, y );
					lightDeluxel = LIGHT_DELUXEL( x, y );
					origin = SUPER_ORIGIN( x, y );
					normal = SUPER_NORMAL( x, y );
					flag = SUPER_FLAG( x, y );

					/* set contribution count */
					lightLuxel[ 3 ] = 1.0f;

					/* setup trace */
					trace.cluster = *cluster;
					VectorCopy( origin, trace.origin );
					VectorCopy( normal, trace.normal );

					/* get light for this sample */
					LightContributionToSample( &trace );
					VectorCopy( trace.color, lightLuxel );

					/* add the contribution to the deluxemap */
					if ( deluxemap ) {
						VectorCopy( trace.directionContribution, lightDeluxel );
					}

					/* check for evilness */
					if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) ) {
						totalLighted++;
						*flag |= FLAG_FORCE_SUBSAMPLING; /* force */
					}
					/* add to count */
					else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
						totalLighted++;
					}
				}
			}

			/* don't even bother with everything else if nothing was lit */
			if ( totalLighted == 0 ) {
				continue;
			}

			/* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
			/* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
			if ( lightSamples > 1 || lightRandomSamples ) {
				/* walk luxels */
				for ( y = 0; y < ( lm->sh - 1 ); y++ )
				{
					for ( x = 0; x < ( lm->sw - 1 ); x++ )
					{
						/* setup */
						mapped = 0;
						lighted = 0;
						VectorClear( total );

						/* test 2x2 stamp */
						for ( t = 0; t < 4; t++ )
						{
							/* set sample coords */
							sx = x + tests[ t ][ 0 ];
							sy = y + tests[ t ][ 1 ];

							/* get cluster */
							cluster = SUPER_CLUSTER( sx, sy );
							if ( *cluster < 0 ) {
								continue;
							}
							mapped++;

							/* get luxel */
							flag = SUPER_FLAG( sx, sy );
							if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
								/* force a lighted/mapped discrepancy so we subsample */
								++lighted;
								++mapped;
								++mapped;
							}
							lightLuxel = LIGHT_LUXEL( sx, sy );
							VectorAdd( total, lightLuxel, total );
							if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
								lighted++;
							}
						}

						/* if total color is under a certain amount, then don't bother subsampling */
						if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
							continue;
						}

						/* if all 4 pixels are either in shadow or light, then don't subsample */
						if ( lighted != 0 && lighted != mapped ) {
							for ( t = 0; t < 4; t++ )
							{
								/* set sample coords */
								sx = x + tests[ t ][ 0 ];
								sy = y + tests[ t ][ 1 ];

								/* get luxel */
								cluster = SUPER_CLUSTER( sx, sy );
								if ( *cluster < 0 ) {
									continue;
								}
								flag = SUPER_FLAG( sx, sy );
								if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
									continue;
								}
								lightLuxel = LIGHT_LUXEL( sx, sy );
								lightDeluxel = LIGHT_DELUXEL( sx, sy );
								origin = SUPER_ORIGIN( sx, sy );

								/* only subsample shadowed luxels */
								//%	if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
								//%		continue;

								/* subsample it */
								if ( lightRandomSamples ) {
									RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
								}
								else{
									SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
								}

								*flag |= FLAG_ALREADY_SUBSAMPLED;

								/* debug code to colorize subsampled areas to yellow */
								//%	luxel = SUPER_LUXEL( lightmapNum, sx, sy );
								//%	VectorSet( luxel, 255, 204, 0 );
							}
						}
					}
				}
			}

			/* tertiary pass, apply dirt map (ambient occlusion) */
			if ( 0 && dirty ) {
				/* walk luxels */
				for ( y = 0; y < lm->sh; y++ )
				{
					for ( x = 0; x < lm->sw; x++ )
					{
						/* get cluster  */
						cluster = SUPER_CLUSTER( x, y );
						if ( *cluster < 0 ) {
							continue;
						}

						/* get particulars */
						lightLuxel = LIGHT_LUXEL( x, y );
						dirt = SUPER_DIRT( x, y );

						/* scale light value */
						VectorScale( lightLuxel, *dirt, lightLuxel );
					}
				}
			}

			/* allocate sampling lightmap storage */
			if ( lm->superLuxels[ lightmapNum ] == NULL ) {
				/* allocate sampling lightmap storage */
				size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
				lm->superLuxels[ lightmapNum ] = safe_malloc0( size );
			}

			/* set style */
			if ( lightmapNum > 0 ) {
				lm->styles[ lightmapNum ] = trace.light->style;
				//%	Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
			}

			/* copy to permanent luxels */
			for ( y = 0; y < lm->sh; y++ )
			{
				for ( x = 0; x < lm->sw; x++ )
				{
					/* get cluster and origin */
					cluster = SUPER_CLUSTER( x, y );
					if ( *cluster < 0 ) {
						continue;
					}
					origin = SUPER_ORIGIN( x, y );

					/* filter? */
					if ( luxelFilterRadius ) {
						/* setup */
						VectorClear( averageColor );
						VectorClear( averageDir );
						samples = 0.0f;

						/* cheaper distance-based filtering */
						for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
						{
							if ( sy < 0 || sy >= lm->sh ) {
								continue;
							}

							for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
							{
								if ( sx < 0 || sx >= lm->sw ) {
									continue;
								}

								/* get particulars */
								cluster = SUPER_CLUSTER( sx, sy );
								if ( *cluster < 0 ) {
									continue;
								}
								lightLuxel = LIGHT_LUXEL( sx, sy );
								lightDeluxel = LIGHT_DELUXEL( sx, sy );

								/* create weight */
								weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
								weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );

								/* scale luxel by filter weight */
								VectorScale( lightLuxel, weight, color );
								VectorAdd( averageColor, color, averageColor );
								if ( deluxemap ) {
									VectorScale( lightDeluxel, weight, direction );
									VectorAdd( averageDir, direction, averageDir );
								}
								samples += weight;
							}
						}

						/* any samples? */
						if ( samples <= 0.0f ) {
							continue;
						}

						/* scale into luxel */
						luxel = SUPER_LUXEL( lightmapNum, x, y );
						luxel[ 3 ] = 1.0f;

						/* handle negative light */
						if ( trace.light->flags & LIGHT_NEGATIVE ) {
							luxel[ 0 ] -= averageColor[ 0 ] / samples;
							luxel[ 1 ] -= averageColor[ 1 ] / samples;
							luxel[ 2 ] -= averageColor[ 2 ] / samples;
						}

						/* handle normal light */
						else
						{
							luxel[ 0 ] += averageColor[ 0 ] / samples;
							luxel[ 1 ] += averageColor[ 1 ] / samples;
							luxel[ 2 ] += averageColor[ 2 ] / samples;
						}

						if ( deluxemap ) {
							/* scale into luxel */
							deluxel = SUPER_DELUXEL( x, y );
							deluxel[ 0 ] += averageDir[ 0 ] / samples;
							deluxel[ 1 ] += averageDir[ 1 ] / samples;
							deluxel[ 2 ] += averageDir[ 2 ] / samples;
						}
					}

					/* single sample */
					else
					{
						/* get particulars */
						lightLuxel = LIGHT_LUXEL( x, y );
						lightDeluxel = LIGHT_DELUXEL( x, y );
						luxel = SUPER_LUXEL( lightmapNum, x, y );
						deluxel = SUPER_DELUXEL( x, y );

						/* handle negative light */
						if ( trace.light->flags & LIGHT_NEGATIVE ) {
							VectorScale( averageColor, -1.0f, averageColor );
						}

						/* add color */
						luxel[ 3 ] = 1.0f;

						/* handle negative light */
						if ( trace.light->flags & LIGHT_NEGATIVE ) {
							VectorSubtract( luxel, lightLuxel, luxel );
						}

						/* handle normal light */
						else{
							VectorAdd( luxel, lightLuxel, luxel );
						}

						if ( deluxemap ) {
							VectorAdd( deluxel, lightDeluxel, deluxel );
						}
					}
				}
			}
		}

		/* free temporary luxels */
		if ( lightLuxels != stackLightLuxels ) {
			free( lightLuxels );
		}

		if ( deluxemap ) {
			free( lightDeluxels );
		}
	}

	/* free light list */
	FreeTraceLights( &trace );

	/* floodlight pass */
	if ( floodlighty ) {
		FloodlightIlluminateLightmap( lm );
	}

	if ( debugnormals ) {
		for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
		{
			/* early out */
			if ( lm->superLuxels[ lightmapNum ] == NULL ) {
				continue;
			}

			for ( y = 0; y < lm->sh; y++ )
			{
				for ( x = 0; x < lm->sw; x++ )
				{
					/* get cluster */
					cluster = SUPER_CLUSTER( x, y );
					//%	if( *cluster < 0 )
					//%		continue;

					/* get particulars */
					luxel = SUPER_LUXEL( lightmapNum, x, y );
					normal = SUPER_NORMAL(  x, y );

					luxel[0] = ( normal[0] * 127 ) + 127;
					luxel[1] = ( normal[1] * 127 ) + 127;
					luxel[2] = ( normal[2] * 127 ) + 127;
				}
			}
		}
	}

	/*	-----------------------------------------------------------------
	    dirt pass
	    ----------------------------------------------------------------- */

	if ( dirty ) {
		/* walk lightmaps */
		for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
		{
			/* early out */
			if ( lm->superLuxels[ lightmapNum ] == NULL ) {
				continue;
			}

			/* apply dirt to each luxel */
			for ( y = 0; y < lm->sh; y++ )
			{
				for ( x = 0; x < lm->sw; x++ )
				{
					/* get cluster */
					cluster = SUPER_CLUSTER( x, y );

					/* get particulars */
					luxel = SUPER_LUXEL( lightmapNum, x, y );
					dirt = SUPER_DIRT( x, y );

					/* apply dirt */
					VectorScale( luxel, *dirt, luxel );

					/* debugging */
					if ( dirtDebug ) {
						VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
					}
				}
			}
		}
	}

	/* -----------------------------------------------------------------
	   filter pass
	   ----------------------------------------------------------------- */

	/* walk lightmaps */
	for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
	{
		/* early out */
		if ( lm->superLuxels[ lightmapNum ] == NULL ) {
			continue;
		}

		/* average occluded luxels from neighbors */
		for ( y = 0; y < lm->sh; y++ )
		{
			for ( x = 0; x < lm->sw; x++ )
			{
				/* get particulars */
				cluster = SUPER_CLUSTER( x, y );
				luxel = SUPER_LUXEL( lightmapNum, x, y );
				deluxel = SUPER_DELUXEL( x, y );
				normal = SUPER_NORMAL( x, y );

				/* determine if filtering is necessary */
				filterColor = qfalse;
				filterDir = qfalse;
				if ( *cluster < 0 ||
					 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
					filterColor = qtrue;
				}

				if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
					filterDir = qtrue;
				}

				if ( !filterColor && !filterDir ) {
					continue;
				}

				/* choose seed amount */
				VectorClear( averageColor );
				VectorClear( averageDir );
				samples = 0.0f;

				/* walk 3x3 matrix */
				for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
				{
					if ( sy < 0 || sy >= lm->sh ) {
						continue;
					}

					for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
					{
						if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
							continue;
						}

						/* get neighbor's particulars */
						cluster2 = SUPER_CLUSTER( sx, sy );
						luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
						deluxel2 = SUPER_DELUXEL( sx, sy );

						/* ignore unmapped/unlit luxels */
						if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
							 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
							continue;
						}

						/* add its distinctiveness to our own */
						VectorAdd( averageColor, luxel2, averageColor );
						samples += luxel2[ 3 ];
						if ( filterDir ) {
							VectorAdd( averageDir, deluxel2, averageDir );
						}
					}
				}

				/* fall through */
				if ( samples <= 0.0f ) {
					continue;
				}

				/* dark lightmap seams */
				if ( dark ) {
					if ( lightmapNum == 0 ) {
						VectorMA( averageColor, 2.0f, ambientColor, averageColor );
					}
					samples += 2.0f;
				}

				/* average it */
				if ( filterColor ) {
					VectorDivide( averageColor, samples, luxel );
					luxel[ 3 ] = 1.0f;
				}
				if ( filterDir ) {
					VectorDivide( averageDir, samples, deluxel );
				}

				/* set cluster to -3 */
				if ( *cluster < 0 ) {
					*cluster = CLUSTER_FLOODED;
				}
			}
		}
	}
}



/*
   IlluminateVertexes()
   light the surface vertexes
 */

#define VERTEX_NUDGE    4.0f

void IlluminateVertexes( int num ){
	int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
	int lightmapNum, numAvg;
	float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
	vec3_t temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
	bspDrawSurface_t    *ds;
	surfaceInfo_t       *info;
	rawLightmap_t       *lm;
	bspDrawVert_t       *verts;
	trace_t trace;
	float floodLightAmount;
	vec3_t floodColor;


	/* get surface, info, and raw lightmap */
	ds = &bspDrawSurfaces[ num ];
	info = &surfaceInfos[ num ];
	lm = info->lm;

	/* -----------------------------------------------------------------
	   illuminate the vertexes
	   ----------------------------------------------------------------- */

	/* calculate vertex lighting for surfaces without lightmaps */
	if ( lm == NULL || cpmaHack ) {
		/* setup trace */
		trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
		trace.forceSunlight = info->si->forceSunlight;
		trace.recvShadows = info->recvShadows;
		trace.numSurfaces = 1;
		trace.surfaces = &num;
		trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;

		/* twosided lighting */
		trace.twoSided = info->si->twoSided;

		/* make light list for this surface */
		CreateTraceLightsForSurface( num, &trace );

		/* setup */
		verts = yDrawVerts + ds->firstVert;
		numAvg = 0;
		memset( avgColors, 0, sizeof( avgColors ) );

		/* walk the surface verts */
		for ( i = 0; i < ds->numVerts; i++ )
		{
			/* get vertex luxel */
			radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );

			/* color the luxel with raw lightmap num? */
			if ( debugSurfaces ) {
				VectorCopy( debugColors[ num % 12 ], radVertLuxel );
			}

			/* color the luxel with luxel origin? */
			else if ( debugOrigin ) {
				VectorSubtract( info->maxs, info->mins, temp );
				VectorScale( temp, ( 1.0f / 255.0f ), temp );
				VectorSubtract( verts[ i ].xyz, info->mins, temp2 );
				radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
				radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
				radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
			}

			/* color the luxel with the normal */
			else if ( normalmap ) {
				radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
				radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
				radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
			}

			else if ( info->si->noVertexLight ) {
				VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
			}

			else if ( noVertexLighting > 0 ) {
				VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
			}

			/* illuminate the vertex */
			else
			{
				/* clear vertex luxel */
				VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );

				/* try at initial origin */
				trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
				if ( trace.cluster >= 0 ) {
					/* setup trace */
					VectorCopy( verts[ i ].xyz, trace.origin );
					VectorCopy( verts[ i ].normal, trace.normal );

					/* r7 dirt */
					if ( dirty && !bouncing ) {
						dirt = DirtForSample( &trace );
					}
					else{
						dirt = 1.0f;
					}

					/* jal: floodlight */
					floodLightAmount = 0.0f;
					VectorClear( floodColor );
					if ( floodlighty && !bouncing ) {
						floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
						VectorScale( floodlightRGB, floodLightAmount, floodColor );
					}

					/* trace */
					LightingAtSample( &trace, ds->vertexStyles, colors );

					/* store */
					for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
					{
						/* r7 dirt */
						VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );

						/* jal: floodlight */
						VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );

						/* store */
						radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
						VectorCopy( colors[ lightmapNum ], radVertLuxel );
						VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
					}
				}

				/* is this sample bright enough? */
				radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
				if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
					 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
					 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
					/* nudge the sample point around a bit */
					for ( x = 0; x < 5; x++ )
					{
						/* two's complement 0, 1, -1, 2, -2, etc */
						x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );

						for ( y = 0; y < 5; y++ )
						{
							y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );

							for ( z = 0; z < 5; z++ )
							{
								z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );

								/* nudge origin */
								trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
								trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
								trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );

								/* try at nudged origin */
								trace.cluster = ClusterForPointExtFilter( trace.origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
								if ( trace.cluster < 0 ) {
									continue;
								}

								/* r7 dirt */
								if ( dirty && !bouncing ) {
									dirt = DirtForSample( &trace );
								}
								else{
									dirt = 1.0f;
								}

								/* jal: floodlight */
								floodLightAmount = 0.0f;
								VectorClear( floodColor );
								if ( floodlighty && !bouncing ) {
									floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
									VectorScale( floodlightRGB, floodLightAmount, floodColor );
								}

								/* trace */
								LightingAtSample( &trace, ds->vertexStyles, colors );

								/* store */
								for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
								{
									/* r7 dirt */
									VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );

									/* jal: floodlight */
									VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );

									/* store */
									radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
									VectorCopy( colors[ lightmapNum ], radVertLuxel );
								}

								/* bright enough? */
								radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
								if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
									 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
									 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
									x = y = z = 1000;
								}
							}
						}
					}
				}

				/* add to average? */
				radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
				if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
					 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
					 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
					numAvg++;
					for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
					{
						radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
						VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
					}
				}
			}

			/* another happy customer */
			numVertsIlluminated++;
		}

		/* set average color */
		if ( numAvg > 0 ) {
			for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
				VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
		}
		else
		{
			VectorCopy( ambientColor, avgColors[ 0 ] );
		}

		/* clean up and store vertex color */
		for ( i = 0; i < ds->numVerts; i++ )
		{
			/* get vertex luxel */
			radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );

			/* store average in occluded vertexes */
			if ( radVertLuxel[ 0 ] < 0.0f ) {
				for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
				{
					radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
					VectorCopy( avgColors[ lightmapNum ], radVertLuxel );

					/* debug code */
					//%	VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
				}
			}

			/* store it */
			for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
			{
				/* get luxels */
				vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
				radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );

				/* store */
				if ( bouncing || bounce == 0 || !bounceOnly ) {
					VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
				}
				if ( !info->si->noVertexLight ) {
					ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
				}
			}
		}

		/* free light list */
		FreeTraceLights( &trace );

		/* return to sender */
		return;
	}

	/* -----------------------------------------------------------------
	   reconstitute vertex lighting from the luxels
	   ----------------------------------------------------------------- */

	/* set styles from lightmap */
	for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
		ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];

	/* get max search radius */
	maxRadius = lm->sw;
	maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;

	/* walk the surface verts */
	verts = yDrawVerts + ds->firstVert;
	for ( i = 0; i < ds->numVerts; i++ )
	{
		/* do each lightmap */
		for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
		{
			/* early out */
			if ( lm->superLuxels[ lightmapNum ] == NULL ) {
				continue;
			}

			/* get luxel coords */
			x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
			y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
			if ( x < 0 ) {
				x = 0;
			}
			else if ( x >= lm->sw ) {
				x = lm->sw - 1;
			}
			if ( y < 0 ) {
				y = 0;
			}
			else if ( y >= lm->sh ) {
				y = lm->sh - 1;
			}

			/* get vertex luxels */
			vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
			radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );

			/* color the luxel with the normal? */
			if ( normalmap ) {
				radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
				radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
				radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
			}

			/* color the luxel with surface num? */
			else if ( debugSurfaces ) {
				VectorCopy( debugColors[ num % 12 ], radVertLuxel );
			}

			else if ( info->si->noVertexLight ) {
				VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
			}

			else if ( noVertexLighting > 0 ) {
				VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
			}

			/* divine color from the superluxels */
			else
			{
				/* increasing radius */
				VectorClear( radVertLuxel );
				samples = 0.0f;
				for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
				{
					/* sample within radius */
					for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
					{
						if ( sy < 0 || sy >= lm->sh ) {
							continue;
						}

						for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
						{
							if ( sx < 0 || sx >= lm->sw ) {
								continue;
							}

							/* get luxel particulars */
							luxel = SUPER_LUXEL( lightmapNum, sx, sy );
							cluster = SUPER_CLUSTER( sx, sy );
							if ( *cluster < 0 ) {
								continue;
							}

							/* testing: must be brigher than ambient color */
							//%	if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
							//%		continue;

							/* add its distinctiveness to our own */
							VectorAdd( radVertLuxel, luxel, radVertLuxel );
							samples += luxel[ 3 ];
						}
					}
				}

				/* any color? */
				if ( samples > 0.0f ) {
					VectorDivide( radVertLuxel, samples, radVertLuxel );
				}
				else{
					VectorCopy( ambientColor, radVertLuxel );
				}
			}

			/* store into floating point storage */
			VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
			numVertsIlluminated++;

			/* store into bytes (for vertex approximation) */
			if ( !info->si->noVertexLight ) {
				ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
			}
		}
	}
}



/* -------------------------------------------------------------------------------

   light optimization (-fast)

   creates a list of lights that will affect a surface and stores it in tw
   this is to optimize surface lighting by culling out as many of the
   lights in the world as possible from further calculation

   ------------------------------------------------------------------------------- */

/*
   SetupBrushes()
   determines opaque brushes in the world and find sky shaders for sunlight calculations
 */

void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
	int i, j, b;
	unsigned int compileFlags, allCompileFlags;
	bspBrush_t      *brush;
	bspBrushSide_t  *side;
	bspShader_t     *shader;
	shaderInfo_t    *si;


	/* note it */
	Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );

	/* allocate */
	if ( opaqueBrushes == NULL ) {
		opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
	}

	/* clear */
	memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
	numOpaqueBrushes = 0;

	/* walk the list of worldspawn brushes */
	for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
	{
		/* get brush */
		b = bspModels[ 0 ].firstBSPBrush + i;
		brush = &bspBrushes[ b ];

		/* check all sides */
		compileFlags = 0;
		allCompileFlags = ~( 0u );
		for ( j = 0; j < brush->numSides; j++ )
		{
			/* do bsp shader calculations */
			side = &bspBrushSides[ brush->firstSide + j ];
			shader = &bspShaders[ side->shaderNum ];

			/* get shader info */
			si = ShaderInfoForShaderNull( shader->shader );
			if ( si == NULL ) {
				continue;
			}

			/* or together compile flags */
			compileFlags |= si->compileFlags;
			allCompileFlags &= si->compileFlags;
		}

		/* determine if this brush is opaque to light */
		if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
			opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
			numOpaqueBrushes++;
			maxOpaqueBrush = i;
		}
	}

	/* emit some statistics */
	Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
}
void SetupBrushes( void ){
	SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
}



/*
   ClusterVisible()
   determines if two clusters are visible to each other using the PVS
 */

qboolean ClusterVisible( int a, int b ){
	int leafBytes;
	byte        *pvs;


	/* dummy check */
	if ( a < 0 || b < 0 ) {
		return qfalse;
	}

	/* early out */
	if ( a == b ) {
		return qtrue;
	}

	/* not vised? */
	if ( numBSPVisBytes <= 8 ) {
		return qtrue;
	}

	/* get pvs data */
	/* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
	leafBytes = ( (int*) bspVisBytes )[ 1 ];
	pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );

	/* check */
	if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
		return qtrue;
	}
	return qfalse;
}



/*
   PointInLeafNum_r()
   borrowed from vlight.c
 */

int PointInLeafNum_r( vec3_t point, int nodenum ){
	int leafnum;
	vec_t dist;
	bspNode_t       *node;
	bspPlane_t  *plane;


	while ( nodenum >= 0 )
	{
		node = &bspNodes[ nodenum ];
		plane = &bspPlanes[ node->planeNum ];
		dist = DotProduct( point, plane->normal ) - plane->dist;
		if ( dist > 0.1 ) {
			nodenum = node->children[ 0 ];
		}
		else if ( dist < -0.1 ) {
			nodenum = node->children[ 1 ];
		}
		else
		{
			leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
			if ( bspLeafs[ leafnum ].cluster != -1 ) {
				return leafnum;
			}
			nodenum = node->children[ 1 ];
		}
	}

	leafnum = -nodenum - 1;
	return leafnum;
}



/*
   PointInLeafnum()
   borrowed from vlight.c
 */

int PointInLeafNum( vec3_t point ){
	return PointInLeafNum_r( point, 0 );
}



/*
   ClusterVisibleToPoint() - ydnar
   returns qtrue if point can "see" cluster
 */

qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
	int pointCluster;


	/* get leafNum for point */
	pointCluster = ClusterForPoint( point );
	if ( pointCluster < 0 ) {
		return qfalse;
	}

	/* check pvs */
	return ClusterVisible( pointCluster, cluster );
}



/*
   ClusterForPoint() - ydnar
   returns the pvs cluster for point
 */

int ClusterForPoint( vec3_t point ){
	int leafNum;


	/* get leafNum for point */
	leafNum = PointInLeafNum( point );
	if ( leafNum < 0 ) {
		return -1;
	}

	/* return the cluster */
	return bspLeafs[ leafNum ].cluster;
}



/*
   ClusterForPointExt() - ydnar
   also takes brushes into account for occlusion testing
 */

int ClusterForPointExt( vec3_t point, float epsilon ){
	int i, j, b, leafNum, cluster;
	float dot;
	qboolean inside;
	int             *brushes, numBSPBrushes;
	bspLeaf_t       *leaf;
	bspBrush_t      *brush;
	bspPlane_t      *plane;


	/* get leaf for point */
	leafNum = PointInLeafNum( point );
	if ( leafNum < 0 ) {
		return -1;
	}
	leaf = &bspLeafs[ leafNum ];

	/* get the cluster */
	cluster = leaf->cluster;
	if ( cluster < 0 ) {
		return -1;
	}

	/* transparent leaf, so check point against all brushes in the leaf */
	brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
	numBSPBrushes = leaf->numBSPLeafBrushes;
	for ( i = 0; i < numBSPBrushes; i++ )
	{
		/* get parts */
		b = brushes[ i ];
		if ( b > maxOpaqueBrush ) {
			continue;
		}
		brush = &bspBrushes[ b ];
		if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
			continue;
		}

		/* check point against all planes */
		inside = qtrue;
		for ( j = 0; j < brush->numSides && inside; j++ )
		{
			plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
			dot = DotProduct( point, plane->normal );
			dot -= plane->dist;
			if ( dot > epsilon ) {
				inside = qfalse;
			}
		}

		/* if inside, return bogus cluster */
		if ( inside ) {
			return -1 - b;
		}
	}

	/* if the point made it this far, it's not inside any opaque brushes */
	return cluster;
}



/*
   ClusterForPointExtFilter() - ydnar
   adds cluster checking against a list of known valid clusters
 */

int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
	int i, cluster;


	/* get cluster for point */
	cluster = ClusterForPointExt( point, epsilon );

	/* check if filtering is necessary */
	if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
		return cluster;
	}

	/* filter */
	for ( i = 0; i < numClusters; i++ )
	{
		if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
			return cluster;
		}
	}

	/* failed */
	return -1;
}



/*
   ShaderForPointInLeaf() - ydnar
   checks a point against all brushes in a leaf, returning the shader of the brush
   also sets the cumulative surface and content flags for the brush hit
 */

int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
	int i, j;
	float dot;
	qboolean inside;
	int             *brushes, numBSPBrushes;
	bspLeaf_t           *leaf;
	bspBrush_t      *brush;
	bspBrushSide_t  *side;
	bspPlane_t      *plane;
	bspShader_t     *shader;
	int allSurfaceFlags, allContentFlags;


	/* clear things out first */
	*surfaceFlags = 0;
	*contentFlags = 0;

	/* get leaf */
	if ( leafNum < 0 ) {
		return -1;
	}
	leaf = &bspLeafs[ leafNum ];

	/* transparent leaf, so check point against all brushes in the leaf */
	brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
	numBSPBrushes = leaf->numBSPLeafBrushes;
	for ( i = 0; i < numBSPBrushes; i++ )
	{
		/* get parts */
		brush = &bspBrushes[ brushes[ i ] ];

		/* check point against all planes */
		inside = qtrue;
		allSurfaceFlags = 0;
		allContentFlags = 0;
		for ( j = 0; j < brush->numSides && inside; j++ )
		{
			side = &bspBrushSides[ brush->firstSide + j ];
			plane = &bspPlanes[ side->planeNum ];
			dot = DotProduct( point, plane->normal );
			dot -= plane->dist;
			if ( dot > epsilon ) {
				inside = qfalse;
			}
			else
			{
				shader = &bspShaders[ side->shaderNum ];
				allSurfaceFlags |= shader->surfaceFlags;
				allContentFlags |= shader->contentFlags;
			}
		}

		/* handle if inside */
		if ( inside ) {
			/* if there are desired flags, check for same and continue if they aren't matched */
			if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
				continue;
			}
			if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
				continue;
			}

			/* store the cumulative flags and return the brush shader (which is mostly useless) */
			*surfaceFlags = allSurfaceFlags;
			*contentFlags = allContentFlags;
			return brush->shaderNum;
		}
	}

	/* if the point made it this far, it's not inside any brushes */
	return -1;
}



/*
   ChopBounds()
   chops a bounding box by the plane defined by origin and normal
   returns qfalse if the bounds is entirely clipped away

   this is not exactly the fastest way to do this...
 */

qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
	/* FIXME: rewrite this so it doesn't use bloody brushes */
	return qtrue;
}



/*
   SetupEnvelopes()
   calculates each light's effective envelope,
   taking into account brightness, type, and pvs.
 */

#define LIGHT_EPSILON   0.125f
#define LIGHT_NUDGE     2.0f

void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
	int i, x, y, z, x1, y1, z1;
	light_t     *light, *light2, **owner;
	bspLeaf_t   *leaf;
	vec3_t origin, dir, mins, maxs;
	float radius, intensity;
	light_t     *buckets[ 256 ];


	/* early out for weird cases where there are no lights */
	if ( lights == NULL ) {
		return;
	}

	/* note it */
	Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );

	/* count lights */
	numLights = 0;
	numCulledLights = 0;
	owner = &lights;
	while ( *owner != NULL )
	{
		/* get light */
		light = *owner;

		/* handle negative lights */
		if ( light->photons < 0.0f || light->add < 0.0f ) {
			light->photons *= -1.0f;
			light->add *= -1.0f;
			light->flags |= LIGHT_NEGATIVE;
		}

		/* sunlight? */
		if ( light->type == EMIT_SUN ) {
			/* special cased */
			light->cluster = 0;
			light->envelope = MAX_WORLD_COORD * 8.0f;
			VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
			VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
		}

		/* everything else */
		else
		{
			/* get pvs cluster for light */
			light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );

			/* invalid cluster? */
			if ( light->cluster < 0 ) {
				/* nudge the sample point around a bit */
				for ( x = 0; x < 4; x++ )
				{
					/* two's complement 0, 1, -1, 2, -2, etc */
					x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );

					for ( y = 0; y < 4; y++ )
					{
						y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );

						for ( z = 0; z < 4; z++ )
						{
							z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );

							/* nudge origin */
							origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
							origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
							origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );

							/* try at nudged origin */
							light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
							if ( light->cluster < 0 ) {
								continue;
							}

							/* set origin */
							VectorCopy( origin, light->origin );
						}
					}
				}
			}

			/* only calculate for lights in pvs and outside of opaque brushes */
			if ( light->cluster >= 0 ) {
				/* set light fast flag */
				if ( fastFlag ) {
					light->flags |= LIGHT_FAST_TEMP;
				}
				else{
					light->flags &= ~LIGHT_FAST_TEMP;
				}
				if ( fastpoint && ( light->type != EMIT_AREA ) ) {
					light->flags |= LIGHT_FAST_TEMP;
				}
				if ( light->si && light->si->noFast ) {
					light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
				}

				/* clear light envelope */
				light->envelope = 0;

				/* handle area lights */
				if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
					light->envelope = MAX_WORLD_COORD * 8.0f;

					/* check for fast mode */
					if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
						/* ugly hack to calculate extent for area lights, but only done once */
						VectorScale( light->normal, -1.0f, dir );
						for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
						{
							float factor;

							VectorMA( light->origin, radius, light->normal, origin );
							factor = PointToPolygonFormFactor( origin, dir, light->w );
							if ( factor < 0.0f ) {
								factor *= -1.0f;
							}
							if ( ( factor * light->add ) <= light->falloffTolerance ) {
								light->envelope = radius;
								break;
							}
						}
					}

					intensity = light->photons; /* hopefully not used */
				}
				else
				{
					radius = 0.0f;
					intensity = light->photons;
				}

				/* other calcs */
				if ( light->envelope <= 0.0f ) {
					/* solve distance for non-distance lights */
					if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
						light->envelope = MAX_WORLD_COORD * 8.0f;
					}

					else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
						/* solve distance for linear lights */
						if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
							light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
						}

						/*
						   add = angle * light->photons * linearScale - (dist * light->fade);
						   T = (light->photons * linearScale) - (dist * light->fade);
						   T + (dist * light->fade) = (light->photons * linearScale);
						   dist * light->fade = (light->photons * linearScale) - T;
						   dist = ((light->photons * linearScale) - T) / light->fade;
						 */

						/* solve for inverse square falloff */
						else{
							light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
						}

						/*
						   add = light->photons / (dist * dist);
						   T = light->photons / (dist * dist);
						   T * (dist * dist) = light->photons;
						   dist = sqrt( light->photons / T );
						 */
					}
					else
					{
						/* solve distance for linear lights */
						if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
							light->envelope = ( intensity * linearScale ) / light->fade;
						}

						/* can't cull these */
						else{
							light->envelope = MAX_WORLD_COORD * 8.0f;
						}
					}
				}

				/* chop radius against pvs */
				{
					/* clear bounds */
					ClearBounds( mins, maxs );

					/* check all leaves */
					for ( i = 0; i < numBSPLeafs; i++ )
					{
						/* get test leaf */
						leaf = &bspLeafs[ i ];

						/* in pvs? */
						if ( leaf->cluster < 0 ) {
							continue;
						}
						if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
							continue;
						}

						/* add this leafs bbox to the bounds */
						VectorCopy( leaf->mins, origin );
						AddPointToBounds( origin, mins, maxs );
						VectorCopy( leaf->maxs, origin );
						AddPointToBounds( origin, mins, maxs );
					}

					/* test to see if bounds encompass light */
					for ( i = 0; i < 3; i++ )
					{
						if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
							//% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
							//%     mins[ 0 ], mins[ 1 ], mins[ 2 ],
							//%     maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
							//%     numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
							AddPointToBounds( light->origin, mins, maxs );
						}
					}

					/* chop the bounds by a plane for area lights and spotlights */
					if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
						ChopBounds( mins, maxs, light->origin, light->normal );
					}

					/* copy bounds */
					VectorCopy( mins, light->mins );
					VectorCopy( maxs, light->maxs );

					/* reflect bounds around light origin */
					//%	VectorMA( light->origin, -1.0f, origin, origin );
					VectorScale( light->origin, 2, origin );
					VectorSubtract( origin, maxs, origin );
					AddPointToBounds( origin, mins, maxs );
					//%	VectorMA( light->origin, -1.0f, mins, origin );
					VectorScale( light->origin, 2, origin );
					VectorSubtract( origin, mins, origin );
					AddPointToBounds( origin, mins, maxs );

					/* calculate spherical bounds */
					VectorSubtract( maxs, light->origin, dir );
					radius = (float) VectorLength( dir );

					/* if this radius is smaller than the envelope, then set the envelope to it */
					if ( radius < light->envelope ) {
						light->envelope = radius;
						//%	Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
					}
					//%	else
					//%		Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
				}

				/* add grid/surface only check */
				if ( forGrid ) {
					if ( !( light->flags & LIGHT_GRID ) ) {
						light->envelope = 0.0f;
					}
				}
				else
				{
					if ( !( light->flags & LIGHT_SURFACES ) ) {
						light->envelope = 0.0f;
					}
				}
			}

			/* culled? */
			if ( light->cluster < 0 || light->envelope <= 0.0f ) {
				/* debug code */
				//%	Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );

				/* delete the light */
				numCulledLights++;
				*owner = light->next;
				if ( light->w != NULL ) {
					free( light->w );
				}
				free( light );
				continue;
			}
		}

		/* square envelope */
		light->envelope2 = ( light->envelope * light->envelope );

		/* increment light count */
		numLights++;

		/* set next light */
		owner = &( ( **owner ).next );
	}

	/* bucket sort lights by style */
	memset( buckets, 0, sizeof( buckets ) );
	light2 = NULL;
	for ( light = lights; light != NULL; light = light2 )
	{
		/* get next light */
		light2 = light->next;

		/* filter into correct bucket */
		light->next = buckets[ light->style ];
		buckets[ light->style ] = light;

		/* if any styled light is present, automatically set nocollapse */
		if ( light->style != LS_NORMAL ) {
			noCollapse = qtrue;
		}
	}

	/* filter back into light list */
	lights = NULL;
	for ( i = 255; i >= 0; i-- )
	{
		light2 = NULL;
		for ( light = buckets[ i ]; light != NULL; light = light2 )
		{
			light2 = light->next;
			light->next = lights;
			lights = light;
		}
	}

	/* emit some statistics */
	Sys_Printf( "%9d total lights\n", numLights );
	Sys_Printf( "%9d culled lights\n", numCulledLights );
}



/*
   CreateTraceLightsForBounds()
   creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
 */

void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
	int i;
	light_t     *light;
	vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
	float radius, dist, length;


	/* potential pre-setup  */
	if ( numLights == 0 ) {
		SetupEnvelopes( qfalse, fast );
	}

	/* debug code */
	//% Sys_Printf( "CTWLFB: (%4.1f %4.1f %4.1f) (%4.1f %4.1f %4.1f)\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );

	/* allocate the light list */
	trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
	trace->numLights = 0;

	/* calculate spherical bounds */
	VectorAdd( mins, maxs, origin );
	VectorScale( origin, 0.5f, origin );
	VectorSubtract( maxs, origin, dir );
	radius = (float) VectorLength( dir );

	/* get length of normal vector */
	if ( normal != NULL ) {
		length = VectorLength( normal );
	}
	else
	{
		normal = nullVector;
		length = 0;
	}

	/* test each light and see if it reaches the sphere */
	/* note: the attenuation code MUST match LightingAtSample() */
	for ( light = lights; light; light = light->next )
	{
		/* check zero sized envelope */
		if ( light->envelope <= 0 ) {
			lightsEnvelopeCulled++;
			continue;
		}

		/* check flags */
		if ( !( light->flags & flags ) ) {
			continue;
		}

		/* sunlight skips all this nonsense */
		if ( light->type != EMIT_SUN ) {
			/* sun only? */
			if ( sunOnly ) {
				continue;
			}

			/* check against pvs cluster */
			if ( numClusters > 0 && clusters != NULL ) {
				for ( i = 0; i < numClusters; i++ )
				{
					if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
						break;
					}
				}

				/* fixme! */
				if ( i == numClusters ) {
					lightsClusterCulled++;
					continue;
				}
			}

			/* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
			VectorSubtract( light->origin, origin, dir );
			dist = VectorLength( dir );
			dist -= light->envelope;
			dist -= radius;
			if ( dist > 0 ) {
				lightsEnvelopeCulled++;
				continue;
			}

			/* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
			#if 0
			skip = qfalse;
			for ( i = 0; i < 3; i++ )
			{
				if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
					skip = qtrue;
				}
			}
			if ( skip ) {
				lightsBoundsCulled++;
				continue;
			}
			#endif
		}

		/* planar surfaces (except twosided surfaces) have a couple more checks */
		if ( length > 0.0f && trace->twoSided == qfalse ) {
			/* lights coplanar with a surface won't light it */
			if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
				lightsPlaneCulled++;
				continue;
			}

			/* check to see if light is behind the plane */
			if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
				lightsPlaneCulled++;
				continue;
			}
		}

		/* add this light */
		trace->lights[ trace->numLights++ ] = light;
	}

	/* make last night null */
	trace->lights[ trace->numLights ] = NULL;
}



void FreeTraceLights( trace_t *trace ){
	if ( trace->lights != NULL ) {
		free( trace->lights );
	}
}



/*
   CreateTraceLightsForSurface()
   creates a list of lights that can potentially affect a drawsurface
 */

void CreateTraceLightsForSurface( int num, trace_t *trace ){
	int i;
	vec3_t mins, maxs, normal;
	bspDrawVert_t       *dv;
	bspDrawSurface_t    *ds;
	surfaceInfo_t       *info;


	/* dummy check */
	if ( num < 0 ) {
		return;
	}

	/* get drawsurface and info */
	ds = &bspDrawSurfaces[ num ];
	info = &surfaceInfos[ num ];

	/* get the mins/maxs for the dsurf */
	ClearBounds( mins, maxs );
	VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
	for ( i = 0; i < ds->numVerts; i++ )
	{
		dv = &yDrawVerts[ ds->firstVert + i ];
		AddPointToBounds( dv->xyz, mins, maxs );
		if ( !VectorCompare( dv->normal, normal ) ) {
			VectorClear( normal );
		}
	}

	/* create the lights for the bounding box */
	CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
}

/////////////////////////////////////////////////////////////

#define FLOODLIGHT_CONE_ANGLE           88  /* degrees */
#define FLOODLIGHT_NUM_ANGLE_STEPS      16
#define FLOODLIGHT_NUM_ELEVATION_STEPS  4
#define FLOODLIGHT_NUM_VECTORS          ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )

static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
static int numFloodVectors = 0;

void SetupFloodLight( void ){
	int i, j;
	float angle, elevation, angleStep, elevationStep;
	const char  *value;
	double v1,v2,v3,v4,v5,v6;

	/* note it */
	Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );

	/* calculate angular steps */
	angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
	elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );

	/* iterate angle */
	angle = 0.0f;
	for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
	{
		/* iterate elevation */
		for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
		{
			floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
			floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
			floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
			numFloodVectors++;
		}
	}

	/* emit some statistics */
	Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );

	/* floodlight */
	value = ValueForKey( &entities[ 0 ], "_floodlight" );

	if ( value[ 0 ] != '\0' ) {
		v1 = v2 = v3 = 0;
		v4 = floodlightDistance;
		v5 = floodlightIntensity;
		v6 = floodlightDirectionScale;

		sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );

		floodlightRGB[0] = v1;
		floodlightRGB[1] = v2;
		floodlightRGB[2] = v3;

		if ( VectorLength( floodlightRGB ) == 0 ) {
			VectorSet( floodlightRGB,0.94,0.94,1.0 );
		}

		if ( v4 < 1 ) {
			v4 = 1024;
		}
		if ( v5 < 1 ) {
			v5 = 128;
		}
		if ( v6 < 0 ) {
			v6 = 1;
		}

		floodlightDistance = v4;
		floodlightIntensity = v5;
		floodlightDirectionScale = v6;

		floodlighty = qtrue;
		Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
	}
	else
	{
		VectorSet( floodlightRGB,0.94,0.94,1.0 );
	}
	if ( colorsRGB ) {
		floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
		floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
		floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
	}
	ColorNormalize( floodlightRGB,floodlightRGB );
}

/*
   FloodLightForSample()
   calculates floodlight value for a given sample
   once again, kudos to the dirtmapping coder
 */

float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
	int i;
	float d;
	float contribution;
	int sub = 0;
	float gatherLight, outLight;
	vec3_t normal, worldUp, myUp, myRt, direction, displacement;
	float dd;
	int vecs = 0;

	gatherLight = 0;
	/* dummy check */
	//if( !dirty )
	//	return 1.0f;
	if ( trace == NULL || trace->cluster < 0 ) {
		return 0.0f;
	}


	/* setup */
	dd = floodLightDistance;
	VectorCopy( trace->normal, normal );

	/* check if the normal is aligned to the world-up */
	if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
		if ( normal[ 2 ] == 1.0f ) {
			VectorSet( myRt, 1.0f, 0.0f, 0.0f );
			VectorSet( myUp, 0.0f, 1.0f, 0.0f );
		}
		else if ( normal[ 2 ] == -1.0f ) {
			VectorSet( myRt, -1.0f, 0.0f, 0.0f );
			VectorSet( myUp,  0.0f, 1.0f, 0.0f );
		}
	}
	else
	{
		VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
		CrossProduct( normal, worldUp, myRt );
		VectorNormalize( myRt, myRt );
		CrossProduct( myRt, normal, myUp );
		VectorNormalize( myUp, myUp );
	}

	/* vortex: optimise floodLightLowQuality a bit */
	if ( floodLightLowQuality == qtrue ) {
		/* iterate through ordered vectors */
		for ( i = 0; i < numFloodVectors; i++ )
			if ( rand() % 10 != 0 ) {
				continue;
			}
	}
	else
	{
		/* iterate through ordered vectors */
		for ( i = 0; i < numFloodVectors; i++ )
		{
			vecs++;

			/* transform vector into tangent space */
			direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
			direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
			direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];

			/* set endpoint */
			VectorMA( trace->origin, dd, direction, trace->end );

			//VectorMA( trace->origin, 1, direction, trace->origin );

			SetupTrace( trace );
			VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
			/* trace */
			TraceLine( trace );
			contribution = 1;

			if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
				contribution = 1.0f;
			}
			else if ( trace->opaque ) {
				VectorSubtract( trace->hit, trace->origin, displacement );
				d = VectorLength( displacement );

				// d=trace->distance;
				//if (d>256) gatherDirt+=1;
				contribution = d / dd;
				if ( contribution > 1 ) {
					contribution = 1.0f;
				}

				//gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
			}

			gatherLight += contribution;
		}
	}

	/* early out */
	if ( gatherLight <= 0.0f ) {
		return 0.0f;
	}

	sub = vecs;

	if ( sub < 1 ) {
		sub = 1;
	}
	gatherLight /= ( sub );

	outLight = gatherLight;
	if ( outLight > 1.0f ) {
		outLight = 1.0f;
	}

	/* return to sender */
	return outLight;
}

/*
   FloodLightRawLightmap
   lighttracer style ambient occlusion light hack.
   Kudos to the dirtmapping author for most of this source.
   VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
   VorteX: fixed problems with deluxemapping
 */

// floodlight pass on a lightmap
void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
	int i, x, y, *cluster;
	float               *origin, *normal, *floodlight, floodLightAmount;
	surfaceInfo_t       *info;
	trace_t trace;
	// int sx, sy;
	// float samples, average, *floodlight2;

	memset( &trace,0,sizeof( trace_t ) );

	/* setup trace */
	trace.testOcclusion = qtrue;
	trace.forceSunlight = qfalse;
	trace.twoSided = qtrue;
	trace.recvShadows = lm->recvShadows;
	trace.numSurfaces = lm->numLightSurfaces;
	trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
	trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
	trace.testAll = qfalse;
	trace.distance = 1024;

	/* twosided lighting (may or may not be a good idea for lightmapped stuff) */
	//trace.twoSided = qfalse;
	for ( i = 0; i < trace.numSurfaces; i++ )
	{
		/* get surface */
		info = &surfaceInfos[ trace.surfaces[ i ] ];

		/* check twosidedness */
		if ( info->si->twoSided ) {
			trace.twoSided = qtrue;
			break;
		}
	}

	/* gather floodlight */
	for ( y = 0; y < lm->sh; y++ )
	{
		for ( x = 0; x < lm->sw; x++ )
		{
			/* get luxel */
			cluster = SUPER_CLUSTER( x, y );
			origin = SUPER_ORIGIN( x, y );
			normal = SUPER_NORMAL( x, y );
			floodlight = SUPER_FLOODLIGHT( x, y );

			/* set default dirt */
			*floodlight = 0.0f;

			/* only look at mapped luxels */
			if ( *cluster < 0 ) {
				continue;
			}

			/* copy to trace */
			trace.cluster = *cluster;
			VectorCopy( origin, trace.origin );
			VectorCopy( normal, trace.normal );

			/* get floodlight */
			floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;

			/* add floodlight */
			floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
			floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
			floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
			floodlight[3] += floodlightDirectionScale;
		}
	}

	/* testing no filtering */
	return;

#if 0

	/* filter "dirt" */
	for ( y = 0; y < lm->sh; y++ )
	{
		for ( x = 0; x < lm->sw; x++ )
		{
			/* get luxel */
			cluster = SUPER_CLUSTER( x, y );
			floodlight = SUPER_FLOODLIGHT( x, y );

			/* filter dirt by adjacency to unmapped luxels */
			average = *floodlight;
			samples = 1.0f;
			for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
			{
				if ( sy < 0 || sy >= lm->sh ) {
					continue;
				}

				for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
				{
					if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
						continue;
					}

					/* get neighboring luxel */
					cluster = SUPER_CLUSTER( sx, sy );
					floodlight2 = SUPER_FLOODLIGHT( sx, sy );
					if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
						continue;
					}

					/* add it */
					average += *floodlight2;
					samples += 1.0f;
				}

				/* bail */
				if ( samples <= 0.0f ) {
					break;
				}
			}

			/* bail */
			if ( samples <= 0.0f ) {
				continue;
			}

			/* scale dirt */
			*floodlight = average / samples;
		}
	}
#endif
}

void FloodLightRawLightmap( int rawLightmapNum ){
	rawLightmap_t       *lm;

	/* bail if this number exceeds the number of raw lightmaps */
	if ( rawLightmapNum >= numRawLightmaps ) {
		return;
	}
	/* get lightmap */
	lm = &rawLightmaps[ rawLightmapNum ];

	/* global pass */
	if ( floodlighty && floodlightIntensity ) {
		FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
	}

	/* custom pass */
	if ( lm->floodlightIntensity ) {
		FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
		numSurfacesFloodlighten += 1;
	}
}

void FloodlightRawLightmaps(){
	Sys_Printf( "--- FloodlightRawLightmap ---\n" );
	numSurfacesFloodlighten = 0;
	RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
	Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
}

/*
   FloodLightIlluminate()
   illuminate floodlight into lightmap luxels
 */

void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
	float               *luxel, *floodlight, *deluxel, *normal;
	int                 *cluster;
	float brightness;
	int x, y, lightmapNum;

	/* walk lightmaps */
	for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
	{
		/* early out */
		if ( lm->superLuxels[ lightmapNum ] == NULL ) {
			continue;
		}

		if( lm->styles[lightmapNum] != LS_NORMAL && lm->styles[lightmapNum] != LS_NONE ) // isStyleLight
			continue;

		/* apply floodlight to each luxel */
		for ( y = 0; y < lm->sh; y++ )
		{
			for ( x = 0; x < lm->sw; x++ )
			{
				/* get floodlight */
				floodlight = SUPER_FLOODLIGHT( x, y );
				if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
					continue;
				}

				/* get cluster */
				cluster = SUPER_CLUSTER( x, y );

				/* only process mapped luxels */
				if ( *cluster < 0 ) {
					continue;
				}

				/* get particulars */
				luxel = SUPER_LUXEL( lightmapNum, x, y );
				deluxel = SUPER_DELUXEL( x, y );

				/* add to lightmap */
				luxel[0] += floodlight[0];
				luxel[1] += floodlight[1];
				luxel[2] += floodlight[2];

				if ( luxel[3] == 0 ) {
					luxel[3] = 1;
				}

				/* add to deluxemap */
				if ( deluxemap && floodlight[3] > 0 ) {
					vec3_t lightvector;

					normal = SUPER_NORMAL( x, y );
					brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];

					// use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
					if ( brightness < 0.00390625f ) {
						brightness = 0.00390625f;
					}

					VectorScale( normal, brightness, lightvector );
					VectorAdd( deluxel, lightvector, deluxel );
				}
			}
		}
	}
}
