]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/light_bounce.c
set eol-style
[xonotic/netradiant.git] / tools / quake3 / q3map2 / light_bounce.c
index e4d23da32b5fac281f203debdce1ae2b41735ead..4181ce53b70a430c776450cb4212cf6f1b7adb74 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-\r
-----------------------------------------------------------------------------------\r
-\r
-This code has been altered significantly from its original form, to support\r
-several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-\r
-/* marker */\r
-#define LIGHT_BOUNCE_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-/* functions */\r
-\r
-/*\r
-RadFreeLights()\r
-deletes any existing lights, freeing up memory for the next bounce\r
-*/\r
-\r
-void RadFreeLights( void )\r
-{\r
-       light_t         *light, *next;\r
-       \r
-       \r
-       /* delete lights */\r
-       for( light = lights; light; light = next )\r
-       {\r
-               next = light->next;\r
-               if( light->w != NULL )\r
-                       FreeWinding( light->w );\r
-               free( light );\r
-       }\r
-       numLights = 0;\r
-       lights = NULL;\r
-}\r
-\r
-\r
-\r
-/*\r
-RadClipWindingEpsilon()\r
-clips a rad winding by a plane\r
-based off the regular clip winding code\r
-*/\r
-\r
-static void RadClipWindingEpsilon( radWinding_t *in, vec3_t normal, vec_t dist,\r
-       vec_t epsilon, radWinding_t *front, radWinding_t *back, clipWork_t *cw )\r
-{\r
-       vec_t                   *dists;\r
-       int                             *sides;\r
-       int                             counts[ 3 ];\r
-       vec_t                   dot;            /* ydnar: changed from static b/c of threading */ /* VC 4.2 optimizer bug if not static? */\r
-       int                             i, j, k;\r
-       radVert_t               *v1, *v2, mid;\r
-       int                             maxPoints;\r
-       \r
-       \r
-       /* crutch */\r
-       dists = cw->dists;\r
-       sides = cw->sides;\r
-       \r
-       /* clear counts */\r
-       counts[ 0 ] = counts[ 1 ] = counts[ 2 ] = 0;\r
-\r
-       /* determine sides for each point */\r
-       for( i = 0; i < in->numVerts; i++ )\r
-       {\r
-               dot = DotProduct( in->verts[ i ].xyz, normal );\r
-               dot -= dist;\r
-               dists[ i ] = dot;\r
-               if( dot > epsilon )\r
-                       sides[ i ] = SIDE_FRONT;\r
-               else if( dot < -epsilon )\r
-                       sides[ i ] = SIDE_BACK;\r
-               else\r
-                       sides[ i ] = SIDE_ON;\r
-               counts[ sides[ i ] ]++;\r
-       }\r
-       sides[ i ] = sides[ 0 ];\r
-       dists[ i ] = dists[ 0 ];\r
-       \r
-       /* clear front and back */\r
-       front->numVerts = back->numVerts = 0;\r
-       \r
-       /* handle all on one side cases */\r
-       if( counts[ 0 ] == 0 )\r
-       {\r
-               memcpy( back, in, sizeof( radWinding_t ) );\r
-               return;\r
-       }\r
-       if( counts[ 1 ] == 0 )\r
-       {\r
-               memcpy( front, in, sizeof( radWinding_t ) );\r
-               return;\r
-       }\r
-       \r
-       /* setup windings */\r
-       maxPoints = in->numVerts + 4;\r
-       \r
-       /* do individual verts */\r
-       for( i = 0; i < in->numVerts; i++ )\r
-       {\r
-               /* do simple vertex copies first */\r
-               v1 = &in->verts[ i ];\r
-               \r
-               if( sides[ i ] == SIDE_ON )\r
-               {\r
-                       memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );\r
-                       memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );\r
-                       continue;\r
-               }\r
-       \r
-               if( sides[ i ] == SIDE_FRONT )\r
-                       memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );\r
-               \r
-               if( sides[ i ] == SIDE_BACK )\r
-                       memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );\r
-               \r
-               if( sides[ i + 1 ] == SIDE_ON || sides[ i + 1 ] == sides[ i ] )\r
-                       continue;\r
-                       \r
-               /* generate a split vertex */\r
-               v2 = &in->verts[ (i + 1) % in->numVerts ];\r
-               \r
-               dot = dists[ i ] / (dists[ i ] - dists[ i + 1 ]);\r
-\r
-               /* average vertex values */\r
-               for( j = 0; j < 4; j++ )\r
-               {\r
-                       /* color */\r
-                       if( j < 4 )\r
-                       {\r
-                               for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                                       mid.color[ k ][ j ] = v1->color[ k ][ j ] + dot * (v2->color[ k ][ j ] - v1->color[ k ][ j ]);\r
-                       }\r
-                       \r
-                       /* xyz, normal */\r
-                       if( j < 3 )\r
-                       {\r
-                               mid.xyz[ j ] = v1->xyz[ j ] + dot * (v2->xyz[ j ] - v1->xyz[ j ]);\r
-                               mid.normal[ j ] = v1->normal[ j ] + dot * (v2->normal[ j ] - v1->normal[ j ]);\r
-                       }\r
-                       \r
-                       /* st, lightmap */\r
-                       if( j < 2 )\r
-                       {\r
-                               mid.st[ j ] = v1->st[ j ] + dot * (v2->st[ j ] - v1->st[ j ]);\r
-                               for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                                       mid.lightmap[ k ][ j ] = v1->lightmap[ k ][ j ] + dot * (v2->lightmap[ k ][ j ] - v1->lightmap[ k ][ j ]);\r
-                       }\r
-               }\r
-               \r
-               /* normalize the averaged normal */\r
-               VectorNormalize( mid.normal, mid.normal );\r
-\r
-               /* copy the midpoint to both windings */\r
-               memcpy( &front->verts[ front->numVerts++ ], &mid, sizeof( radVert_t ) );\r
-               memcpy( &back->verts[ back->numVerts++ ], &mid, sizeof( radVert_t ) );\r
-       }\r
-       \r
-       /* error check */\r
-       if( front->numVerts > maxPoints || front->numVerts > maxPoints )\r
-               Error( "RadClipWindingEpsilon: points exceeded estimate" );\r
-       if( front->numVerts > MAX_POINTS_ON_WINDING || front->numVerts > MAX_POINTS_ON_WINDING )\r
-               Error( "RadClipWindingEpsilon: MAX_POINTS_ON_WINDING" );\r
-}\r
-\r
-\r
-\r
-\r
-\r
-/*\r
-RadSampleImage()\r
-samples a texture image for a given color\r
-returns qfalse if pixels are bad\r
-*/\r
-\r
-qboolean RadSampleImage( byte *pixels, int width, int height, float st[ 2 ], float color[ 4 ] )\r
-{\r
-       float   sto[ 2 ];\r
-       int             x, y;\r
-       \r
-       \r
-       /* clear color first */\r
-       color[ 0 ] = color[ 1 ] = color[ 2 ] = color[ 3 ] = 255;\r
-       \r
-       /* dummy check */\r
-       if( pixels == NULL || width < 1 || height < 1 )\r
-               return qfalse;\r
-       \r
-       /* bias st */\r
-       sto[ 0 ] = st[ 0 ];\r
-       while( sto[ 0 ] < 0.0f )\r
-               sto[ 0 ] += 1.0f;\r
-       sto[ 1 ] = st[ 1 ];\r
-       while( sto[ 1 ] < 0.0f )\r
-               sto[ 1 ] += 1.0f;\r
-\r
-       /* get offsets */\r
-       x = ((float) width * sto[ 0 ]) + 0.5f;\r
-       x %= width;\r
-       y = ((float) height * sto[ 1 ])  + 0.5f;\r
-       y %= height;\r
-       \r
-       /* get pixel */\r
-       pixels += (y * width * 4) + (x * 4);\r
-       VectorCopy( pixels, color );\r
-       color[ 3 ] = pixels[ 3 ];\r
-       return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-RadSample()\r
-samples a fragment's lightmap or vertex color and returns an\r
-average color and a color gradient for the sample\r
-*/\r
-\r
-#define MAX_SAMPLES                    150\r
-#define SAMPLE_GRANULARITY     6\r
-\r
-static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, radWinding_t *rw, vec3_t average, vec3_t gradient, int *style )\r
-{\r
-       int                     i, j, k, l, v, x, y, samples;\r
-       vec3_t          color, mins, maxs;\r
-       vec4_t          textureColor;\r
-       float           alpha, alphaI, bf;\r
-       vec3_t          blend;\r
-       float           st[ 2 ], lightmap[ 2 ], *radLuxel;\r
-       radVert_t       *rv[ 3 ];\r
-       \r
-       \r
-       /* initial setup */\r
-       ClearBounds( mins, maxs );\r
-       VectorClear( average );\r
-       VectorClear( gradient );\r
-       alpha = 0;\r
-       \r
-       /* dummy check */\r
-       if( rw == NULL || rw->numVerts < 3 )\r
-               return;\r
-       \r
-       /* start sampling */\r
-       samples = 0;\r
-       \r
-       /* sample vertex colors if no lightmap or this is the initial pass */\r
-       if( lm == NULL || lm->radLuxels[ lightmapNum ] == NULL || bouncing == qfalse )\r
-       {\r
-               for( samples = 0; samples < rw->numVerts; samples++ )\r
-               {\r
-                       /* multiply by texture color */\r
-                       if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, rw->verts[ samples ].st, textureColor ) )\r
-                       {\r
-                               VectorCopy( si->averageColor, textureColor );\r
-                               textureColor[ 4 ] = 255.0f;\r
-                       }\r
-                       for( i = 0; i < 3; i++ )\r
-                               color[ i ] = (textureColor[ i ] / 255) * (rw->verts[ samples ].color[ lightmapNum ][ i ] / 255.0f);\r
-                       \r
-                       AddPointToBounds( color, mins, maxs );\r
-                       VectorAdd( average, color, average );\r
-                       \r
-                       /* get alpha */\r
-                       alpha += (textureColor[ 3 ] / 255.0f) * (rw->verts[ samples ].color[ lightmapNum ][ 3 ] / 255.0f);\r
-               }\r
-               \r
-               /* set style */\r
-               *style = ds->vertexStyles[ lightmapNum ];\r
-       }\r
-       \r
-       /* sample lightmap */\r
-       else\r
-       {\r
-               /* fracture the winding into a fan (including degenerate tris) */\r
-               for( v = 1; v < (rw->numVerts - 1) && samples < MAX_SAMPLES; v++ )\r
-               {\r
-                       /* get a triangle */\r
-                       rv[ 0 ] = &rw->verts[ 0 ];\r
-                       rv[ 1 ] = &rw->verts[ v ];\r
-                       rv[ 2 ] = &rw->verts[ v + 1 ];\r
-                       \r
-                       /* this code is embarassing (really should just rasterize the triangle) */\r
-                       for( i = 1; i < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; i++ )\r
-                       {\r
-                               for( j = 1; j < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; j++ )\r
-                               {\r
-                                       for( k = 1; k < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; k++ )\r
-                                       {\r
-                                               /* create a blend vector (barycentric coordinates) */\r
-                                               blend[ 0 ] = i;\r
-                                               blend[ 1 ] = j;\r
-                                               blend[ 2 ] = k;\r
-                                               bf = (1.0 / (blend[ 0 ] + blend[ 1 ] + blend[ 2 ]));\r
-                                               VectorScale( blend, bf, blend );\r
-                                               \r
-                                               /* create a blended sample */\r
-                                               st[ 0 ] = st[ 1 ] = 0.0f;\r
-                                               lightmap[ 0 ] = lightmap[ 1 ] = 0.0f;\r
-                                               alphaI = 0.0f;\r
-                                               for( l = 0; l < 3; l++ )\r
-                                               {\r
-                                                       st[ 0 ] += (rv[ l ]->st[ 0 ] * blend[ l ]);\r
-                                                       st[ 1 ] += (rv[ l ]->st[ 1 ] * blend[ l ]);\r
-                                                       lightmap[ 0 ] += (rv[ l ]->lightmap[ lightmapNum ][ 0 ] * blend[ l ]);\r
-                                                       lightmap[ 1 ] += (rv[ l ]->lightmap[ lightmapNum ][ 1 ] * blend[ l ]);\r
-                                                       alphaI += (rv[ l ]->color[ lightmapNum ][ 3 ] * blend[ l ]);\r
-                                               }\r
-                                               \r
-                                               /* get lightmap xy coords */\r
-                                               x = lightmap[ 0 ] / (float) superSample;\r
-                                               y = lightmap[ 1 ] / (float) superSample;\r
-                                               if( x < 0 )\r
-                                                       x = 0;\r
-                                               else if ( x >= lm->w )\r
-                                                       x = lm->w - 1;\r
-                                               if( y < 0 )\r
-                                                       y = 0;\r
-                                               else if ( y >= lm->h )\r
-                                                       y = lm->h - 1;\r
-                                               \r
-                                               /* get radiosity luxel */\r
-                                               radLuxel = RAD_LUXEL( lightmapNum, x, y );\r
-                                               \r
-                                               /* ignore unlit/unused luxels */\r
-                                               if( radLuxel[ 0 ] < 0.0f )\r
-                                                       continue;\r
-                                               \r
-                                               /* inc samples */\r
-                                               samples++;\r
-                                               \r
-                                               /* multiply by texture color */\r
-                                               if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, st, textureColor ) )\r
-                                               {\r
-                                                       VectorCopy( si->averageColor, textureColor );\r
-                                                       textureColor[ 4 ] = 255;\r
-                                               }\r
-                                               for( i = 0; i < 3; i++ )\r
-                                                       color[ i ] = (textureColor[ i ] / 255) * (radLuxel[ i ] / 255);\r
-                                               \r
-                                               AddPointToBounds( color, mins, maxs );\r
-                                               VectorAdd( average, color, average );\r
-                                               \r
-                                               /* get alpha */\r
-                                               alpha += (textureColor[ 3 ] / 255) * (alphaI / 255);\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               /* set style */\r
-               *style = ds->lightmapStyles[ lightmapNum ];\r
-       }\r
-       \r
-       /* any samples? */\r
-       if( samples <= 0 )\r
-               return;\r
-       \r
-       /* average the color */\r
-       VectorScale( average, (1.0 / samples), average );\r
-       \r
-       /* create the color gradient */\r
-       //%     VectorSubtract( maxs, mins, delta );\r
-       \r
-       /* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */\r
-       //%     gradient[ 0 ] = maxs[ 0 ] > 0.0f ? (maxs[ 0 ] - mins[ 0 ]) / maxs[ 0 ] : 0.0f;\r
-       //%     gradient[ 1 ] = maxs[ 1 ] > 0.0f ? (maxs[ 1 ] - mins[ 1 ]) / maxs[ 1 ] : 0.0f;\r
-       //%     gradient[ 2 ] = maxs[ 2 ] > 0.0f ? (maxs[ 2 ] - mins[ 2 ]) / maxs[ 2 ] : 0.0f;\r
-       \r
-       /* newer: another contrast function */\r
-       for( i = 0; i < 3; i++ )\r
-               gradient[ i ] = (maxs[ i ] - mins[ i ]) * maxs[ i ];\r
-}\r
-\r
-\r
-\r
-/*\r
-RadSubdivideDiffuseLight()\r
-subdivides a radiosity winding until it is smaller than subdivide, then generates an area light\r
-*/\r
-\r
-#define RADIOSITY_MAX_GRADIENT         0.75f   //%     0.25f\r
-#define RADIOSITY_VALUE                                500.0f\r
-#define RADIOSITY_MIN                          0.0001f\r
-#define RADIOSITY_CLIP_EPSILON         0.125f\r
-\r
-static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si,\r
-       float scale, float subdivide, qboolean original, radWinding_t *rw, clipWork_t *cw )\r
-{\r
-       int                             i, style;\r
-       float                   dist, area, value;\r
-       vec3_t                  mins, maxs, normal, d1, d2, cross, color, gradient;\r
-       light_t                 *light, *splash;\r
-       winding_t               *w;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( rw == NULL || rw->numVerts < 3 )\r
-               return;\r
-       \r
-       /* get bounds for winding */\r
-       ClearBounds( mins, maxs );\r
-       for( i = 0; i < rw->numVerts; i++ )\r
-               AddPointToBounds( rw->verts[ i ].xyz, mins, maxs );\r
-       \r
-       /* subdivide if necessary */\r
-       for( i = 0; i < 3; i++ )\r
-       {\r
-               if( maxs[ i ] - mins[ i ] > subdivide )\r
-               {\r
-                       radWinding_t    front, back;\r
-                       \r
-                       \r
-                       /* make axial plane */\r
-                       VectorClear( normal );\r
-                       normal[ i ] = 1;\r
-                       dist = (maxs[ i ] + mins[ i ]) * 0.5f;\r
-                       \r
-                       /* clip the winding */\r
-                       RadClipWindingEpsilon( rw, normal, dist, RADIOSITY_CLIP_EPSILON, &front, &back, cw );\r
-                       \r
-                       /* recurse */\r
-                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &front, cw );\r
-                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &back, cw );\r
-                       return;\r
-               }\r
-       }\r
-       \r
-       /* check area */\r
-       area = 0.0f;\r
-       for( i = 2; i < rw->numVerts; i++ )\r
-       {\r
-               VectorSubtract( rw->verts[ i - 1 ].xyz, rw->verts[ 0 ].xyz, d1 );\r
-               VectorSubtract( rw->verts[ i ].xyz, rw->verts[ 0 ].xyz, d2 );\r
-               CrossProduct( d1, d2, cross );\r
-               area += 0.5f * VectorLength( cross );\r
-       }\r
-       if( area < 1.0f || area > 20000000.0f )\r
-               return;\r
-       \r
-       /* more subdivision may be necessary */\r
-       if( bouncing )\r
-       {\r
-               /* get color sample for the surface fragment */\r
-               RadSample( lightmapNum, ds, lm, si, rw, color, gradient, &style );\r
-               \r
-               /* if color gradient is too high, subdivide again */\r
-               if( subdivide > minDiffuseSubdivide && \r
-                       (gradient[ 0 ] > RADIOSITY_MAX_GRADIENT || gradient[ 1 ] > RADIOSITY_MAX_GRADIENT || gradient[ 2 ] > RADIOSITY_MAX_GRADIENT) )\r
-               {\r
-                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, (subdivide / 2.0f), qfalse, rw, cw );\r
-                       return;\r
-               }\r
-       }\r
-       \r
-       /* create a regular winding and an average normal */\r
-       w = AllocWinding( rw->numVerts );\r
-       w->numpoints = rw->numVerts;\r
-       VectorClear( normal );\r
-       for( i = 0; i < rw->numVerts; i++ )\r
-       {\r
-               VectorCopy( rw->verts[ i ].xyz, w->p[ i ] );\r
-               VectorAdd( normal, rw->verts[ i ].normal, normal );\r
-       }\r
-       VectorScale( normal, (1.0f / rw->numVerts), normal );\r
-       if( VectorNormalize( normal, normal ) == 0.0f )\r
-               return;\r
-       \r
-       /* early out? */\r
-       if( bouncing && VectorLength( color ) < RADIOSITY_MIN )\r
-               return;\r
-       \r
-       /* debug code */\r
-       //%     Sys_Printf( "Size: %d %d %d\n", (int) (maxs[ 0 ] - mins[ 0 ]), (int) (maxs[ 1 ] - mins[ 1 ]), (int) (maxs[ 2 ] - mins[ 2 ]) );\r
-       //%     Sys_Printf( "Grad: %f %f %f\n", gradient[ 0 ], gradient[ 1 ], gradient[ 2 ] );\r
-       \r
-       /* increment counts */\r
-       numDiffuseLights++;\r
-       switch( ds->surfaceType )\r
-       {\r
-               case MST_PLANAR:\r
-                       numBrushDiffuseLights++;\r
-                       break;\r
-               \r
-               case MST_TRIANGLE_SOUP:\r
-                       numTriangleDiffuseLights;\r
-                       break;\r
-               \r
-               case MST_PATCH:\r
-                       numPatchDiffuseLights++;\r
-                       break;\r
-       }\r
-       \r
-       /* create a light */\r
-       light = safe_malloc( sizeof( *light ) );\r
-       memset( light, 0, sizeof( *light ) );\r
-       \r
-       /* attach it */\r
-       ThreadLock();\r
-       light->next = lights;\r
-       lights = light;\r
-       ThreadUnlock();\r
-       \r
-       /* initialize the light */\r
-       light->flags = LIGHT_AREA_DEFAULT;\r
-       light->type = EMIT_AREA;\r
-       light->si = si;\r
-       light->fade = 1.0f;\r
-       light->w = w;\r
-       \r
-       /* set falloff threshold */\r
-       light->falloffTolerance = falloffTolerance;\r
-       \r
-       /* bouncing light? */\r
-       if( bouncing == qfalse )\r
-       {\r
-               /* handle first-pass lights in normal q3a style */\r
-               value = si->value;\r
-               light->photons = value * area * areaScale;\r
-               light->add = value * formFactorValueScale * areaScale;\r
-               VectorCopy( si->color, light->color );\r
-               VectorScale( light->color, light->add, light->emitColor );\r
-               light->style = si->lightStyle;\r
-               if( light->style < 0 || light->style >= LS_NONE )\r
-                       light->style = 0;\r
-               \r
-               /* set origin */\r
-               VectorAdd( mins, maxs, light->origin );\r
-               VectorScale( light->origin, 0.5f, light->origin );\r
-               \r
-               /* nudge it off the plane a bit */\r
-               VectorCopy( normal, light->normal );\r
-               VectorMA( light->origin, 1.0f, light->normal, light->origin );\r
-               light->dist = DotProduct( light->origin, normal );\r
-               \r
-               /* optionally create a point splashsplash light for first pass */\r
-               if( original && si->backsplashFraction > 0 )\r
-               {\r
-                       /* allocate a new point light */\r
-                       splash = safe_malloc( sizeof( *splash ) );\r
-                       memset( splash, 0, sizeof( *splash ) );\r
-                       splash->next = lights;\r
-                       lights = splash;\r
-                       \r
-                       /* set it up */\r
-                       splash->flags = LIGHT_Q3A_DEFAULT;\r
-                       splash->type = EMIT_POINT;\r
-                       splash->photons = light->photons * si->backsplashFraction;\r
-                       splash->fade = 1.0f;\r
-                       splash->si = si;\r
-                       VectorMA( light->origin, si->backsplashDistance, normal, splash->origin );\r
-                       VectorCopy( si->color, splash->color );\r
-                       splash->falloffTolerance = falloffTolerance;\r
-                       splash->style = light->style;\r
-                       \r
-                       /* add to counts */\r
-                       numPointLights++;\r
-               }\r
-       }\r
-       else\r
-       {\r
-               /* handle bounced light (radiosity) a little differently */\r
-               value = RADIOSITY_VALUE * si->bounceScale * 0.375f;\r
-               light->photons = value * area * bounceScale;\r
-               light->add = value * formFactorValueScale * bounceScale;\r
-               VectorCopy( color, light->color );\r
-               VectorScale( light->color, light->add, light->emitColor );\r
-               light->style = style;\r
-               if( light->style < 0 || light->style >= LS_NONE )\r
-                       light->style = 0;\r
-               \r
-               /* set origin */\r
-               WindingCenter( w, light->origin );\r
-               \r
-               /* nudge it off the plane a bit */\r
-               VectorCopy( normal, light->normal );\r
-               VectorMA( light->origin, 1.0f, light->normal, light->origin );\r
-               light->dist = DotProduct( light->origin, normal );\r
-       }\r
-       \r
-       /* emit light from both sides? */\r
-       if( si->compileFlags & C_FOG || si->twoSided )\r
-               light->flags |= LIGHT_TWOSIDED;\r
-       \r
-       //%     Sys_Printf( "\nAL: C: (%6f, %6f, %6f) [%6f] N: (%6f, %6f, %6f) %s\n",\r
-       //%             light->color[ 0 ], light->color[ 1 ], light->color[ 2 ], light->add,\r
-       //%             light->normal[ 0 ], light->normal[ 1 ], light->normal[ 2 ],\r
-       //%             light->si->shader );\r
-}\r
-\r
-\r
-\r
-/*\r
-RadLightForTriangles()\r
-creates unbounced diffuse lights for triangle soup (misc_models, etc)\r
-*/\r
-\r
-void RadLightForTriangles( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )\r
-{\r
-       int                                     i, j, k, v;\r
-       bspDrawSurface_t        *ds;\r
-       surfaceInfo_t           *info;\r
-       float                           *radVertexLuxel;\r
-       radWinding_t            rw;\r
-       \r
-       \r
-       /* get surface */\r
-       ds = &bspDrawSurfaces[ num ];\r
-       info = &surfaceInfos[ num ];\r
-       \r
-       /* each triangle is a potential emitter */\r
-       rw.numVerts = 3;\r
-       for( i = 0; i < ds->numIndexes; i += 3 )\r
-       {\r
-               /* copy each vert */\r
-               for( j = 0; j < 3; j++ )\r
-               {\r
-                       /* get vertex index and rad vertex luxel */\r
-                       v = ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ];\r
-                       \r
-                       /* get most everything */\r
-                       memcpy( &rw.verts[ j ], &yDrawVerts[ v ], sizeof( bspDrawVert_t ) );\r
-                       \r
-                       /* fix colors */\r
-                       for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                       {\r
-                               radVertexLuxel = RAD_VERTEX_LUXEL( k, ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ] );\r
-                               VectorCopy( radVertexLuxel, rw.verts[ j ].color[ k ] );\r
-                               rw.verts[ j ].color[ k ][ 3 ] = yDrawVerts[ v ].color[ k ][ 3 ];\r
-                       }\r
-               }\r
-               \r
-               /* subdivide into area lights */\r
-               RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-RadLightForPatch()\r
-creates unbounced diffuse lights for patches\r
-*/\r
-\r
-#define        PLANAR_EPSILON  0.1f\r
-\r
-void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )\r
-{\r
-       int                                     i, x, y, v, t, pw[ 5 ], r;\r
-       bspDrawSurface_t        *ds;\r
-       surfaceInfo_t           *info;\r
-       bspDrawVert_t           *bogus;\r
-       bspDrawVert_t           *dv[ 4 ];\r
-       mesh_t                          src, *subdivided, *mesh;\r
-       float                           *radVertexLuxel;\r
-       float                           dist;\r
-       vec4_t                          plane;\r
-       qboolean                        planar;\r
-       radWinding_t            rw;\r
-       \r
-       \r
-       /* get surface */\r
-       ds = &bspDrawSurfaces[ num ];\r
-       info = &surfaceInfos[ num ];\r
-       \r
-       /* construct a bogus vert list with color index stuffed into color[ 0 ] */\r
-       bogus = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );\r
-       memcpy( bogus, &yDrawVerts[ ds->firstVert ], ds->numVerts * sizeof( bspDrawVert_t ) );\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-               bogus[ i ].color[ 0 ][ 0 ] = i;\r
-       \r
-       /* build a subdivided mesh identical to shadow facets for this patch */\r
-       /* this MUST MATCH FacetsForPatch() identically! */\r
-       src.width = ds->patchWidth;\r
-       src.height = ds->patchHeight;\r
-       src.verts = bogus;\r
-       //%     subdivided = SubdivideMesh( src, 8, 512 );\r
-       subdivided = SubdivideMesh2( src, info->patchIterations );\r
-       PutMeshOnCurve( *subdivided );\r
-       //%     MakeMeshNormals( *subdivided );\r
-       mesh = RemoveLinearMeshColumnsRows( subdivided );\r
-       FreeMesh( subdivided );\r
-       free( bogus );\r
-       \r
-       /* FIXME: build interpolation table into color[ 1 ] */\r
-       \r
-       /* fix up color indexes */\r
-       for( i = 0; i < (mesh->width * mesh->height); i++ )\r
-       {\r
-               dv[ 0 ] = &mesh->verts[ i ];\r
-               if( dv[ 0 ]->color[ 0 ][ 0 ] >= ds->numVerts )\r
-                       dv[ 0 ]->color[ 0 ][ 0 ] = ds->numVerts - 1;\r
-       }\r
-       \r
-       /* iterate through the mesh quads */\r
-       for( y = 0; y < (mesh->height - 1); y++ )\r
-       {\r
-               for( x = 0; x < (mesh->width - 1); x++ )\r
-               {\r
-                       /* set indexes */\r
-                       pw[ 0 ] = x + (y * mesh->width);\r
-                       pw[ 1 ] = x + ((y + 1) * mesh->width);\r
-                       pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
-                       pw[ 3 ] = x + 1 + (y * mesh->width);\r
-                       pw[ 4 ] = x + (y * mesh->width);        /* same as pw[ 0 ] */\r
-                       \r
-                       /* set radix */\r
-                       r = (x + y) & 1;\r
-                       \r
-                       /* get drawverts */\r
-                       dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];\r
-                       dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];\r
-                       dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];\r
-                       dv[ 3 ] = &mesh->verts[ pw[ r + 3 ] ];\r
-                       \r
-                       /* planar? */\r
-                       planar = PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz );\r
-                       if( planar )\r
-                       {\r
-                               dist = DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ];\r
-                               if( fabs( dist ) > PLANAR_EPSILON )\r
-                                       planar = qfalse;\r
-                       }\r
-                       \r
-                       /* generate a quad */\r
-                       if( planar )\r
-                       {\r
-                               rw.numVerts = 4;\r
-                               for( v = 0; v < 4; v++ )\r
-                               {\r
-                                       /* get most everything */\r
-                                       memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );\r
-                                       \r
-                                       /* fix colors */\r
-                                       for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
-                                       {\r
-                                               radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );\r
-                                               VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );\r
-                                               rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];\r
-                                       }\r
-                               }\r
-                               \r
-                               /* subdivide into area lights */\r
-                               RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );\r
-                       }\r
-                       \r
-                       /* generate 2 tris */\r
-                       else\r
-                       {\r
-                               rw.numVerts = 3;\r
-                               for( t = 0; t < 2; t++ )\r
-                               {\r
-                                       for( v = 0; v < 3 + t; v++ )\r
-                                       {\r
-                                               /* get "other" triangle (stupid hacky logic, but whatevah) */\r
-                                               if( v == 1 && t == 1 )\r
-                                                       v++;\r
-\r
-                                               /* get most everything */\r
-                                               memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );\r
-                                               \r
-                                               /* fix colors */\r
-                                               for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
-                                               {\r
-                                                       radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );\r
-                                                       VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );\r
-                                                       rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];\r
-                                               }\r
-                                       }\r
-                                       \r
-                                       /* subdivide into area lights */\r
-                                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* free the mesh */\r
-       FreeMesh( mesh );\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-RadLight()\r
-creates unbounced diffuse lights for a given surface\r
-*/\r
-\r
-void RadLight( int num )\r
-{\r
-       int                                     lightmapNum;\r
-       float                           scale, subdivide;\r
-       int                                     contentFlags, surfaceFlags, compileFlags;\r
-       bspDrawSurface_t        *ds;\r
-       surfaceInfo_t           *info;\r
-       rawLightmap_t           *lm;\r
-       shaderInfo_t            *si;\r
-       clipWork_t                      cw;\r
-       \r
-       \r
-       /* get drawsurface, lightmap, and shader info */\r
-       ds = &bspDrawSurfaces[ num ];\r
-       info = &surfaceInfos[ num ];\r
-       lm = info->lm;\r
-       si = info->si;\r
-       scale = si->bounceScale;\r
-       \r
-       /* find nodraw bit */\r
-       contentFlags = surfaceFlags = compileFlags = 0;\r
-       ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, &compileFlags );\r
-       \r
-       /* early outs? */\r
-       if( scale <= 0.0f || (si->compileFlags & C_SKY) || si->autosprite ||\r
-               (bspShaders[ ds->shaderNum ].contentFlags & contentFlags) || (bspShaders[ ds->shaderNum ].surfaceFlags & surfaceFlags) ||\r
-               (si->compileFlags & compileFlags) )\r
-               return;\r
-       \r
-       /* determine how much we need to chop up the surface */\r
-       if( si->lightSubdivide )\r
-               subdivide = si->lightSubdivide;\r
-       else\r
-               subdivide = diffuseSubdivide;\r
-       \r
-       /* inc counts */\r
-       numDiffuseSurfaces++;\r
-       \r
-       /* iterate through styles (this could be more efficient, yes) */\r
-       for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
-       {\r
-               /* switch on type */\r
-               if( ds->lightmapStyles[ lightmapNum ] != LS_NONE && ds->lightmapStyles[ lightmapNum ] != LS_UNUSED )\r
-               {\r
-                       switch( ds->surfaceType )\r
-                       {\r
-                               case MST_PLANAR:\r
-                               case MST_TRIANGLE_SOUP:\r
-                                       RadLightForTriangles( num, lightmapNum, lm, si, scale, subdivide, &cw );\r
-                                       break;\r
-                               \r
-                               case MST_PATCH:\r
-                                       RadLightForPatch( num, lightmapNum, lm, si, scale, subdivide, &cw );\r
-                                       break;\r
-                               \r
-                               default:\r
-                                       break;\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-RadCreateDiffuseLights()\r
-creates lights for unbounced light on surfaces in the bsp\r
-*/\r
-\r
-int    iterations = 0;\r
-\r
-void RadCreateDiffuseLights( void )\r
-{\r
-       /* startup */\r
-       Sys_FPrintf( SYS_VRB, "--- RadCreateDiffuseLights ---\n" );\r
-       numDiffuseSurfaces = 0;\r
-       numDiffuseLights = 0;\r
-       numBrushDiffuseLights = 0;\r
-       numTriangleDiffuseLights = 0;\r
-       numPatchDiffuseLights = 0;\r
-       numAreaLights = 0;\r
-       \r
-       /* hit every surface (threaded) */\r
-       RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, RadLight );\r
-       \r
-       /* dump the lights generated to a file */\r
-       if( dump )\r
-       {\r
-               char    dumpName[ 1024 ], ext[ 64 ];\r
-               FILE    *file;\r
-               light_t *light;\r
-               \r
-               strcpy( dumpName, source );\r
-               StripExtension( dumpName );\r
-               sprintf( ext, "_bounce_%03d.map", iterations );\r
-               strcat( dumpName, ext );\r
-               file = fopen( dumpName, "wb" );\r
-               Sys_Printf( "Writing %s...\n", dumpName );\r
-               if( file )\r
-               {\r
-                       for( light = lights; light; light = light->next )\r
-                       {\r
-                               fprintf( file,\r
-                                       "{\n"\r
-                                       "\"classname\" \"light\"\n"\r
-                                       "\"light\" \"%d\"\n"\r
-                                       "\"origin\" \"%.0f %.0f %.0f\"\n"\r
-                                       "\"_color\" \"%.3f %.3f %.3f\"\n"\r
-                                       "}\n",\r
-                                       \r
-                                       (int) light->add,\r
-                                       \r
-                                       light->origin[ 0 ],\r
-                                       light->origin[ 1 ],\r
-                                       light->origin[ 2 ],\r
-                                       \r
-                                       light->color[ 0 ],\r
-                                       light->color[ 1 ],\r
-                                       light->color[ 2 ] );\r
-                       }\r
-                       fclose( file );\r
-               }\r
-       }\r
-       \r
-       /* increment */\r
-       iterations++;\r
-       \r
-       /* print counts */\r
-       Sys_Printf( "%8d diffuse surfaces\n", numDiffuseSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%8d total diffuse lights\n", numDiffuseLights );\r
-       Sys_FPrintf( SYS_VRB, "%8d brush diffuse lights\n", numBrushDiffuseLights );\r
-       Sys_FPrintf( SYS_VRB, "%8d patch diffuse lights\n", numPatchDiffuseLights );\r
-       Sys_FPrintf( SYS_VRB, "%8d triangle diffuse lights\n", numTriangleDiffuseLights );\r
-}\r
-\r
-\r
-\r
-\r
-\r
+/*
+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_BOUNCE_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+/* functions */
+
+/*
+RadFreeLights()
+deletes any existing lights, freeing up memory for the next bounce
+*/
+
+void RadFreeLights( void )
+{
+       light_t         *light, *next;
+       
+       
+       /* delete lights */
+       for( light = lights; light; light = next )
+       {
+               next = light->next;
+               if( light->w != NULL )
+                       FreeWinding( light->w );
+               free( light );
+       }
+       numLights = 0;
+       lights = NULL;
+}
+
+
+
+/*
+RadClipWindingEpsilon()
+clips a rad winding by a plane
+based off the regular clip winding code
+*/
+
+static void RadClipWindingEpsilon( radWinding_t *in, vec3_t normal, vec_t dist,
+       vec_t epsilon, radWinding_t *front, radWinding_t *back, clipWork_t *cw )
+{
+       vec_t                   *dists;
+       int                             *sides;
+       int                             counts[ 3 ];
+       vec_t                   dot;            /* ydnar: changed from static b/c of threading */ /* VC 4.2 optimizer bug if not static? */
+       int                             i, j, k;
+       radVert_t               *v1, *v2, mid;
+       int                             maxPoints;
+       
+       
+       /* crutch */
+       dists = cw->dists;
+       sides = cw->sides;
+       
+       /* clear counts */
+       counts[ 0 ] = counts[ 1 ] = counts[ 2 ] = 0;
+
+       /* determine sides for each point */
+       for( i = 0; i < in->numVerts; i++ )
+       {
+               dot = DotProduct( in->verts[ i ].xyz, normal );
+               dot -= dist;
+               dists[ i ] = dot;
+               if( dot > epsilon )
+                       sides[ i ] = SIDE_FRONT;
+               else if( dot < -epsilon )
+                       sides[ i ] = SIDE_BACK;
+               else
+                       sides[ i ] = SIDE_ON;
+               counts[ sides[ i ] ]++;
+       }
+       sides[ i ] = sides[ 0 ];
+       dists[ i ] = dists[ 0 ];
+       
+       /* clear front and back */
+       front->numVerts = back->numVerts = 0;
+       
+       /* handle all on one side cases */
+       if( counts[ 0 ] == 0 )
+       {
+               memcpy( back, in, sizeof( radWinding_t ) );
+               return;
+       }
+       if( counts[ 1 ] == 0 )
+       {
+               memcpy( front, in, sizeof( radWinding_t ) );
+               return;
+       }
+       
+       /* setup windings */
+       maxPoints = in->numVerts + 4;
+       
+       /* do individual verts */
+       for( i = 0; i < in->numVerts; i++ )
+       {
+               /* do simple vertex copies first */
+               v1 = &in->verts[ i ];
+               
+               if( sides[ i ] == SIDE_ON )
+               {
+                       memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );
+                       memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );
+                       continue;
+               }
+       
+               if( sides[ i ] == SIDE_FRONT )
+                       memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );
+               
+               if( sides[ i ] == SIDE_BACK )
+                       memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );
+               
+               if( sides[ i + 1 ] == SIDE_ON || sides[ i + 1 ] == sides[ i ] )
+                       continue;
+                       
+               /* generate a split vertex */
+               v2 = &in->verts[ (i + 1) % in->numVerts ];
+               
+               dot = dists[ i ] / (dists[ i ] - dists[ i + 1 ]);
+
+               /* average vertex values */
+               for( j = 0; j < 4; j++ )
+               {
+                       /* color */
+                       if( j < 4 )
+                       {
+                               for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                                       mid.color[ k ][ j ] = v1->color[ k ][ j ] + dot * (v2->color[ k ][ j ] - v1->color[ k ][ j ]);
+                       }
+                       
+                       /* xyz, normal */
+                       if( j < 3 )
+                       {
+                               mid.xyz[ j ] = v1->xyz[ j ] + dot * (v2->xyz[ j ] - v1->xyz[ j ]);
+                               mid.normal[ j ] = v1->normal[ j ] + dot * (v2->normal[ j ] - v1->normal[ j ]);
+                       }
+                       
+                       /* st, lightmap */
+                       if( j < 2 )
+                       {
+                               mid.st[ j ] = v1->st[ j ] + dot * (v2->st[ j ] - v1->st[ j ]);
+                               for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                                       mid.lightmap[ k ][ j ] = v1->lightmap[ k ][ j ] + dot * (v2->lightmap[ k ][ j ] - v1->lightmap[ k ][ j ]);
+                       }
+               }
+               
+               /* normalize the averaged normal */
+               VectorNormalize( mid.normal, mid.normal );
+
+               /* copy the midpoint to both windings */
+               memcpy( &front->verts[ front->numVerts++ ], &mid, sizeof( radVert_t ) );
+               memcpy( &back->verts[ back->numVerts++ ], &mid, sizeof( radVert_t ) );
+       }
+       
+       /* error check */
+       if( front->numVerts > maxPoints || front->numVerts > maxPoints )
+               Error( "RadClipWindingEpsilon: points exceeded estimate" );
+       if( front->numVerts > MAX_POINTS_ON_WINDING || front->numVerts > MAX_POINTS_ON_WINDING )
+               Error( "RadClipWindingEpsilon: MAX_POINTS_ON_WINDING" );
+}
+
+
+
+
+
+/*
+RadSampleImage()
+samples a texture image for a given color
+returns qfalse if pixels are bad
+*/
+
+qboolean RadSampleImage( byte *pixels, int width, int height, float st[ 2 ], float color[ 4 ] )
+{
+       float   sto[ 2 ];
+       int             x, y;
+       
+       
+       /* clear color first */
+       color[ 0 ] = color[ 1 ] = color[ 2 ] = color[ 3 ] = 255;
+       
+       /* dummy check */
+       if( pixels == NULL || width < 1 || height < 1 )
+               return qfalse;
+       
+       /* bias st */
+       sto[ 0 ] = st[ 0 ];
+       while( sto[ 0 ] < 0.0f )
+               sto[ 0 ] += 1.0f;
+       sto[ 1 ] = st[ 1 ];
+       while( sto[ 1 ] < 0.0f )
+               sto[ 1 ] += 1.0f;
+
+       /* get offsets */
+       x = ((float) width * sto[ 0 ]) + 0.5f;
+       x %= width;
+       y = ((float) height * sto[ 1 ])  + 0.5f;
+       y %= height;
+       
+       /* get pixel */
+       pixels += (y * width * 4) + (x * 4);
+       VectorCopy( pixels, color );
+       color[ 3 ] = pixels[ 3 ];
+       return qtrue;
+}
+
+
+
+/*
+RadSample()
+samples a fragment's lightmap or vertex color and returns an
+average color and a color gradient for the sample
+*/
+
+#define MAX_SAMPLES                    150
+#define SAMPLE_GRANULARITY     6
+
+static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, radWinding_t *rw, vec3_t average, vec3_t gradient, int *style )
+{
+       int                     i, j, k, l, v, x, y, samples;
+       vec3_t          color, mins, maxs;
+       vec4_t          textureColor;
+       float           alpha, alphaI, bf;
+       vec3_t          blend;
+       float           st[ 2 ], lightmap[ 2 ], *radLuxel;
+       radVert_t       *rv[ 3 ];
+       
+       
+       /* initial setup */
+       ClearBounds( mins, maxs );
+       VectorClear( average );
+       VectorClear( gradient );
+       alpha = 0;
+       
+       /* dummy check */
+       if( rw == NULL || rw->numVerts < 3 )
+               return;
+       
+       /* start sampling */
+       samples = 0;
+       
+       /* sample vertex colors if no lightmap or this is the initial pass */
+       if( lm == NULL || lm->radLuxels[ lightmapNum ] == NULL || bouncing == qfalse )
+       {
+               for( samples = 0; samples < rw->numVerts; samples++ )
+               {
+                       /* multiply by texture color */
+                       if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, rw->verts[ samples ].st, textureColor ) )
+                       {
+                               VectorCopy( si->averageColor, textureColor );
+                               textureColor[ 4 ] = 255.0f;
+                       }
+                       for( i = 0; i < 3; i++ )
+                               color[ i ] = (textureColor[ i ] / 255) * (rw->verts[ samples ].color[ lightmapNum ][ i ] / 255.0f);
+                       
+                       AddPointToBounds( color, mins, maxs );
+                       VectorAdd( average, color, average );
+                       
+                       /* get alpha */
+                       alpha += (textureColor[ 3 ] / 255.0f) * (rw->verts[ samples ].color[ lightmapNum ][ 3 ] / 255.0f);
+               }
+               
+               /* set style */
+               *style = ds->vertexStyles[ lightmapNum ];
+       }
+       
+       /* sample lightmap */
+       else
+       {
+               /* fracture the winding into a fan (including degenerate tris) */
+               for( v = 1; v < (rw->numVerts - 1) && samples < MAX_SAMPLES; v++ )
+               {
+                       /* get a triangle */
+                       rv[ 0 ] = &rw->verts[ 0 ];
+                       rv[ 1 ] = &rw->verts[ v ];
+                       rv[ 2 ] = &rw->verts[ v + 1 ];
+                       
+                       /* this code is embarassing (really should just rasterize the triangle) */
+                       for( i = 1; i < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; i++ )
+                       {
+                               for( j = 1; j < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; j++ )
+                               {
+                                       for( k = 1; k < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; k++ )
+                                       {
+                                               /* create a blend vector (barycentric coordinates) */
+                                               blend[ 0 ] = i;
+                                               blend[ 1 ] = j;
+                                               blend[ 2 ] = k;
+                                               bf = (1.0 / (blend[ 0 ] + blend[ 1 ] + blend[ 2 ]));
+                                               VectorScale( blend, bf, blend );
+                                               
+                                               /* create a blended sample */
+                                               st[ 0 ] = st[ 1 ] = 0.0f;
+                                               lightmap[ 0 ] = lightmap[ 1 ] = 0.0f;
+                                               alphaI = 0.0f;
+                                               for( l = 0; l < 3; l++ )
+                                               {
+                                                       st[ 0 ] += (rv[ l ]->st[ 0 ] * blend[ l ]);
+                                                       st[ 1 ] += (rv[ l ]->st[ 1 ] * blend[ l ]);
+                                                       lightmap[ 0 ] += (rv[ l ]->lightmap[ lightmapNum ][ 0 ] * blend[ l ]);
+                                                       lightmap[ 1 ] += (rv[ l ]->lightmap[ lightmapNum ][ 1 ] * blend[ l ]);
+                                                       alphaI += (rv[ l ]->color[ lightmapNum ][ 3 ] * blend[ l ]);
+                                               }
+                                               
+                                               /* get lightmap xy coords */
+                                               x = lightmap[ 0 ] / (float) superSample;
+                                               y = lightmap[ 1 ] / (float) superSample;
+                                               if( x < 0 )
+                                                       x = 0;
+                                               else if ( x >= lm->w )
+                                                       x = lm->w - 1;
+                                               if( y < 0 )
+                                                       y = 0;
+                                               else if ( y >= lm->h )
+                                                       y = lm->h - 1;
+                                               
+                                               /* get radiosity luxel */
+                                               radLuxel = RAD_LUXEL( lightmapNum, x, y );
+                                               
+                                               /* ignore unlit/unused luxels */
+                                               if( radLuxel[ 0 ] < 0.0f )
+                                                       continue;
+                                               
+                                               /* inc samples */
+                                               samples++;
+                                               
+                                               /* multiply by texture color */
+                                               if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, st, textureColor ) )
+                                               {
+                                                       VectorCopy( si->averageColor, textureColor );
+                                                       textureColor[ 4 ] = 255;
+                                               }
+                                               for( i = 0; i < 3; i++ )
+                                                       color[ i ] = (textureColor[ i ] / 255) * (radLuxel[ i ] / 255);
+                                               
+                                               AddPointToBounds( color, mins, maxs );
+                                               VectorAdd( average, color, average );
+                                               
+                                               /* get alpha */
+                                               alpha += (textureColor[ 3 ] / 255) * (alphaI / 255);
+                                       }
+                               }
+                       }
+               }
+               
+               /* set style */
+               *style = ds->lightmapStyles[ lightmapNum ];
+       }
+       
+       /* any samples? */
+       if( samples <= 0 )
+               return;
+       
+       /* average the color */
+       VectorScale( average, (1.0 / samples), average );
+       
+       /* create the color gradient */
+       //%     VectorSubtract( maxs, mins, delta );
+       
+       /* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */
+       //%     gradient[ 0 ] = maxs[ 0 ] > 0.0f ? (maxs[ 0 ] - mins[ 0 ]) / maxs[ 0 ] : 0.0f;
+       //%     gradient[ 1 ] = maxs[ 1 ] > 0.0f ? (maxs[ 1 ] - mins[ 1 ]) / maxs[ 1 ] : 0.0f;
+       //%     gradient[ 2 ] = maxs[ 2 ] > 0.0f ? (maxs[ 2 ] - mins[ 2 ]) / maxs[ 2 ] : 0.0f;
+       
+       /* newer: another contrast function */
+       for( i = 0; i < 3; i++ )
+               gradient[ i ] = (maxs[ i ] - mins[ i ]) * maxs[ i ];
+}
+
+
+
+/*
+RadSubdivideDiffuseLight()
+subdivides a radiosity winding until it is smaller than subdivide, then generates an area light
+*/
+
+#define RADIOSITY_MAX_GRADIENT         0.75f   //%     0.25f
+#define RADIOSITY_VALUE                                500.0f
+#define RADIOSITY_MIN                          0.0001f
+#define RADIOSITY_CLIP_EPSILON         0.125f
+
+static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si,
+       float scale, float subdivide, qboolean original, radWinding_t *rw, clipWork_t *cw )
+{
+       int                             i, style;
+       float                   dist, area, value;
+       vec3_t                  mins, maxs, normal, d1, d2, cross, color, gradient;
+       light_t                 *light, *splash;
+       winding_t               *w;
+       
+       
+       /* dummy check */
+       if( rw == NULL || rw->numVerts < 3 )
+               return;
+       
+       /* get bounds for winding */
+       ClearBounds( mins, maxs );
+       for( i = 0; i < rw->numVerts; i++ )
+               AddPointToBounds( rw->verts[ i ].xyz, mins, maxs );
+       
+       /* subdivide if necessary */
+       for( i = 0; i < 3; i++ )
+       {
+               if( maxs[ i ] - mins[ i ] > subdivide )
+               {
+                       radWinding_t    front, back;
+                       
+                       
+                       /* make axial plane */
+                       VectorClear( normal );
+                       normal[ i ] = 1;
+                       dist = (maxs[ i ] + mins[ i ]) * 0.5f;
+                       
+                       /* clip the winding */
+                       RadClipWindingEpsilon( rw, normal, dist, RADIOSITY_CLIP_EPSILON, &front, &back, cw );
+                       
+                       /* recurse */
+                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &front, cw );
+                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &back, cw );
+                       return;
+               }
+       }
+       
+       /* check area */
+       area = 0.0f;
+       for( i = 2; i < rw->numVerts; i++ )
+       {
+               VectorSubtract( rw->verts[ i - 1 ].xyz, rw->verts[ 0 ].xyz, d1 );
+               VectorSubtract( rw->verts[ i ].xyz, rw->verts[ 0 ].xyz, d2 );
+               CrossProduct( d1, d2, cross );
+               area += 0.5f * VectorLength( cross );
+       }
+       if( area < 1.0f || area > 20000000.0f )
+               return;
+       
+       /* more subdivision may be necessary */
+       if( bouncing )
+       {
+               /* get color sample for the surface fragment */
+               RadSample( lightmapNum, ds, lm, si, rw, color, gradient, &style );
+               
+               /* if color gradient is too high, subdivide again */
+               if( subdivide > minDiffuseSubdivide && 
+                       (gradient[ 0 ] > RADIOSITY_MAX_GRADIENT || gradient[ 1 ] > RADIOSITY_MAX_GRADIENT || gradient[ 2 ] > RADIOSITY_MAX_GRADIENT) )
+               {
+                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, (subdivide / 2.0f), qfalse, rw, cw );
+                       return;
+               }
+       }
+       
+       /* create a regular winding and an average normal */
+       w = AllocWinding( rw->numVerts );
+       w->numpoints = rw->numVerts;
+       VectorClear( normal );
+       for( i = 0; i < rw->numVerts; i++ )
+       {
+               VectorCopy( rw->verts[ i ].xyz, w->p[ i ] );
+               VectorAdd( normal, rw->verts[ i ].normal, normal );
+       }
+       VectorScale( normal, (1.0f / rw->numVerts), normal );
+       if( VectorNormalize( normal, normal ) == 0.0f )
+               return;
+       
+       /* early out? */
+       if( bouncing && VectorLength( color ) < RADIOSITY_MIN )
+               return;
+       
+       /* debug code */
+       //%     Sys_Printf( "Size: %d %d %d\n", (int) (maxs[ 0 ] - mins[ 0 ]), (int) (maxs[ 1 ] - mins[ 1 ]), (int) (maxs[ 2 ] - mins[ 2 ]) );
+       //%     Sys_Printf( "Grad: %f %f %f\n", gradient[ 0 ], gradient[ 1 ], gradient[ 2 ] );
+       
+       /* increment counts */
+       numDiffuseLights++;
+       switch( ds->surfaceType )
+       {
+               case MST_PLANAR:
+                       numBrushDiffuseLights++;
+                       break;
+               
+               case MST_TRIANGLE_SOUP:
+                       numTriangleDiffuseLights;
+                       break;
+               
+               case MST_PATCH:
+                       numPatchDiffuseLights++;
+                       break;
+       }
+       
+       /* create a light */
+       light = safe_malloc( sizeof( *light ) );
+       memset( light, 0, sizeof( *light ) );
+       
+       /* attach it */
+       ThreadLock();
+       light->next = lights;
+       lights = light;
+       ThreadUnlock();
+       
+       /* initialize the light */
+       light->flags = LIGHT_AREA_DEFAULT;
+       light->type = EMIT_AREA;
+       light->si = si;
+       light->fade = 1.0f;
+       light->w = w;
+       
+       /* set falloff threshold */
+       light->falloffTolerance = falloffTolerance;
+       
+       /* bouncing light? */
+       if( bouncing == qfalse )
+       {
+               /* handle first-pass lights in normal q3a style */
+               value = si->value;
+               light->photons = value * area * areaScale;
+               light->add = value * formFactorValueScale * areaScale;
+               VectorCopy( si->color, light->color );
+               VectorScale( light->color, light->add, light->emitColor );
+               light->style = si->lightStyle;
+               if( light->style < 0 || light->style >= LS_NONE )
+                       light->style = 0;
+               
+               /* set origin */
+               VectorAdd( mins, maxs, light->origin );
+               VectorScale( light->origin, 0.5f, light->origin );
+               
+               /* nudge it off the plane a bit */
+               VectorCopy( normal, light->normal );
+               VectorMA( light->origin, 1.0f, light->normal, light->origin );
+               light->dist = DotProduct( light->origin, normal );
+               
+               /* optionally create a point splashsplash light for first pass */
+               if( original && si->backsplashFraction > 0 )
+               {
+                       /* allocate a new point light */
+                       splash = safe_malloc( sizeof( *splash ) );
+                       memset( splash, 0, sizeof( *splash ) );
+                       splash->next = lights;
+                       lights = splash;
+                       
+                       /* set it up */
+                       splash->flags = LIGHT_Q3A_DEFAULT;
+                       splash->type = EMIT_POINT;
+                       splash->photons = light->photons * si->backsplashFraction;
+                       splash->fade = 1.0f;
+                       splash->si = si;
+                       VectorMA( light->origin, si->backsplashDistance, normal, splash->origin );
+                       VectorCopy( si->color, splash->color );
+                       splash->falloffTolerance = falloffTolerance;
+                       splash->style = light->style;
+                       
+                       /* add to counts */
+                       numPointLights++;
+               }
+       }
+       else
+       {
+               /* handle bounced light (radiosity) a little differently */
+               value = RADIOSITY_VALUE * si->bounceScale * 0.375f;
+               light->photons = value * area * bounceScale;
+               light->add = value * formFactorValueScale * bounceScale;
+               VectorCopy( color, light->color );
+               VectorScale( light->color, light->add, light->emitColor );
+               light->style = style;
+               if( light->style < 0 || light->style >= LS_NONE )
+                       light->style = 0;
+               
+               /* set origin */
+               WindingCenter( w, light->origin );
+               
+               /* nudge it off the plane a bit */
+               VectorCopy( normal, light->normal );
+               VectorMA( light->origin, 1.0f, light->normal, light->origin );
+               light->dist = DotProduct( light->origin, normal );
+       }
+       
+       /* emit light from both sides? */
+       if( si->compileFlags & C_FOG || si->twoSided )
+               light->flags |= LIGHT_TWOSIDED;
+       
+       //%     Sys_Printf( "\nAL: C: (%6f, %6f, %6f) [%6f] N: (%6f, %6f, %6f) %s\n",
+       //%             light->color[ 0 ], light->color[ 1 ], light->color[ 2 ], light->add,
+       //%             light->normal[ 0 ], light->normal[ 1 ], light->normal[ 2 ],
+       //%             light->si->shader );
+}
+
+
+
+/*
+RadLightForTriangles()
+creates unbounced diffuse lights for triangle soup (misc_models, etc)
+*/
+
+void RadLightForTriangles( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )
+{
+       int                                     i, j, k, v;
+       bspDrawSurface_t        *ds;
+       surfaceInfo_t           *info;
+       float                           *radVertexLuxel;
+       radWinding_t            rw;
+       
+       
+       /* get surface */
+       ds = &bspDrawSurfaces[ num ];
+       info = &surfaceInfos[ num ];
+       
+       /* each triangle is a potential emitter */
+       rw.numVerts = 3;
+       for( i = 0; i < ds->numIndexes; i += 3 )
+       {
+               /* copy each vert */
+               for( j = 0; j < 3; j++ )
+               {
+                       /* get vertex index and rad vertex luxel */
+                       v = ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ];
+                       
+                       /* get most everything */
+                       memcpy( &rw.verts[ j ], &yDrawVerts[ v ], sizeof( bspDrawVert_t ) );
+                       
+                       /* fix colors */
+                       for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                       {
+                               radVertexLuxel = RAD_VERTEX_LUXEL( k, ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ] );
+                               VectorCopy( radVertexLuxel, rw.verts[ j ].color[ k ] );
+                               rw.verts[ j ].color[ k ][ 3 ] = yDrawVerts[ v ].color[ k ][ 3 ];
+                       }
+               }
+               
+               /* subdivide into area lights */
+               RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );
+       }
+}
+
+
+
+/*
+RadLightForPatch()
+creates unbounced diffuse lights for patches
+*/
+
+#define        PLANAR_EPSILON  0.1f
+
+void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )
+{
+       int                                     i, x, y, v, t, pw[ 5 ], r;
+       bspDrawSurface_t        *ds;
+       surfaceInfo_t           *info;
+       bspDrawVert_t           *bogus;
+       bspDrawVert_t           *dv[ 4 ];
+       mesh_t                          src, *subdivided, *mesh;
+       float                           *radVertexLuxel;
+       float                           dist;
+       vec4_t                          plane;
+       qboolean                        planar;
+       radWinding_t            rw;
+       
+       
+       /* get surface */
+       ds = &bspDrawSurfaces[ num ];
+       info = &surfaceInfos[ num ];
+       
+       /* construct a bogus vert list with color index stuffed into color[ 0 ] */
+       bogus = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );
+       memcpy( bogus, &yDrawVerts[ ds->firstVert ], ds->numVerts * sizeof( bspDrawVert_t ) );
+       for( i = 0; i < ds->numVerts; i++ )
+               bogus[ i ].color[ 0 ][ 0 ] = i;
+       
+       /* build a subdivided mesh identical to shadow facets for this patch */
+       /* this MUST MATCH FacetsForPatch() identically! */
+       src.width = ds->patchWidth;
+       src.height = ds->patchHeight;
+       src.verts = bogus;
+       //%     subdivided = SubdivideMesh( src, 8, 512 );
+       subdivided = SubdivideMesh2( src, info->patchIterations );
+       PutMeshOnCurve( *subdivided );
+       //%     MakeMeshNormals( *subdivided );
+       mesh = RemoveLinearMeshColumnsRows( subdivided );
+       FreeMesh( subdivided );
+       free( bogus );
+       
+       /* FIXME: build interpolation table into color[ 1 ] */
+       
+       /* fix up color indexes */
+       for( i = 0; i < (mesh->width * mesh->height); i++ )
+       {
+               dv[ 0 ] = &mesh->verts[ i ];
+               if( dv[ 0 ]->color[ 0 ][ 0 ] >= ds->numVerts )
+                       dv[ 0 ]->color[ 0 ][ 0 ] = ds->numVerts - 1;
+       }
+       
+       /* iterate through the mesh quads */
+       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 */
+                       dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
+                       dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];
+                       dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];
+                       dv[ 3 ] = &mesh->verts[ pw[ r + 3 ] ];
+                       
+                       /* planar? */
+                       planar = PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz );
+                       if( planar )
+                       {
+                               dist = DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ];
+                               if( fabs( dist ) > PLANAR_EPSILON )
+                                       planar = qfalse;
+                       }
+                       
+                       /* generate a quad */
+                       if( planar )
+                       {
+                               rw.numVerts = 4;
+                               for( v = 0; v < 4; v++ )
+                               {
+                                       /* get most everything */
+                                       memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );
+                                       
+                                       /* fix colors */
+                                       for( i = 0; i < MAX_LIGHTMAPS; i++ )
+                                       {
+                                               radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );
+                                               VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );
+                                               rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];
+                                       }
+                               }
+                               
+                               /* subdivide into area lights */
+                               RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );
+                       }
+                       
+                       /* generate 2 tris */
+                       else
+                       {
+                               rw.numVerts = 3;
+                               for( t = 0; t < 2; t++ )
+                               {
+                                       for( v = 0; v < 3 + t; v++ )
+                                       {
+                                               /* get "other" triangle (stupid hacky logic, but whatevah) */
+                                               if( v == 1 && t == 1 )
+                                                       v++;
+
+                                               /* get most everything */
+                                               memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );
+                                               
+                                               /* fix colors */
+                                               for( i = 0; i < MAX_LIGHTMAPS; i++ )
+                                               {
+                                                       radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );
+                                                       VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );
+                                                       rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];
+                                               }
+                                       }
+                                       
+                                       /* subdivide into area lights */
+                                       RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );
+                               }
+                       }
+               }
+       }
+       
+       /* free the mesh */
+       FreeMesh( mesh );
+}
+
+
+
+
+/*
+RadLight()
+creates unbounced diffuse lights for a given surface
+*/
+
+void RadLight( int num )
+{
+       int                                     lightmapNum;
+       float                           scale, subdivide;
+       int                                     contentFlags, surfaceFlags, compileFlags;
+       bspDrawSurface_t        *ds;
+       surfaceInfo_t           *info;
+       rawLightmap_t           *lm;
+       shaderInfo_t            *si;
+       clipWork_t                      cw;
+       
+       
+       /* get drawsurface, lightmap, and shader info */
+       ds = &bspDrawSurfaces[ num ];
+       info = &surfaceInfos[ num ];
+       lm = info->lm;
+       si = info->si;
+       scale = si->bounceScale;
+       
+       /* find nodraw bit */
+       contentFlags = surfaceFlags = compileFlags = 0;
+       ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, &compileFlags );
+       
+       /* early outs? */
+       if( scale <= 0.0f || (si->compileFlags & C_SKY) || si->autosprite ||
+               (bspShaders[ ds->shaderNum ].contentFlags & contentFlags) || (bspShaders[ ds->shaderNum ].surfaceFlags & surfaceFlags) ||
+               (si->compileFlags & compileFlags) )
+               return;
+       
+       /* determine how much we need to chop up the surface */
+       if( si->lightSubdivide )
+               subdivide = si->lightSubdivide;
+       else
+               subdivide = diffuseSubdivide;
+       
+       /* inc counts */
+       numDiffuseSurfaces++;
+       
+       /* iterate through styles (this could be more efficient, yes) */
+       for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+       {
+               /* switch on type */
+               if( ds->lightmapStyles[ lightmapNum ] != LS_NONE && ds->lightmapStyles[ lightmapNum ] != LS_UNUSED )
+               {
+                       switch( ds->surfaceType )
+                       {
+                               case MST_PLANAR:
+                               case MST_TRIANGLE_SOUP:
+                                       RadLightForTriangles( num, lightmapNum, lm, si, scale, subdivide, &cw );
+                                       break;
+                               
+                               case MST_PATCH:
+                                       RadLightForPatch( num, lightmapNum, lm, si, scale, subdivide, &cw );
+                                       break;
+                               
+                               default:
+                                       break;
+                       }
+               }
+       }
+}
+
+
+
+/*
+RadCreateDiffuseLights()
+creates lights for unbounced light on surfaces in the bsp
+*/
+
+int    iterations = 0;
+
+void RadCreateDiffuseLights( void )
+{
+       /* startup */
+       Sys_FPrintf( SYS_VRB, "--- RadCreateDiffuseLights ---\n" );
+       numDiffuseSurfaces = 0;
+       numDiffuseLights = 0;
+       numBrushDiffuseLights = 0;
+       numTriangleDiffuseLights = 0;
+       numPatchDiffuseLights = 0;
+       numAreaLights = 0;
+       
+       /* hit every surface (threaded) */
+       RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, RadLight );
+       
+       /* dump the lights generated to a file */
+       if( dump )
+       {
+               char    dumpName[ 1024 ], ext[ 64 ];
+               FILE    *file;
+               light_t *light;
+               
+               strcpy( dumpName, source );
+               StripExtension( dumpName );
+               sprintf( ext, "_bounce_%03d.map", iterations );
+               strcat( dumpName, ext );
+               file = fopen( dumpName, "wb" );
+               Sys_Printf( "Writing %s...\n", dumpName );
+               if( file )
+               {
+                       for( light = lights; light; light = light->next )
+                       {
+                               fprintf( file,
+                                       "{\n"
+                                       "\"classname\" \"light\"\n"
+                                       "\"light\" \"%d\"\n"
+                                       "\"origin\" \"%.0f %.0f %.0f\"\n"
+                                       "\"_color\" \"%.3f %.3f %.3f\"\n"
+                                       "}\n",
+                                       
+                                       (int) light->add,
+                                       
+                                       light->origin[ 0 ],
+                                       light->origin[ 1 ],
+                                       light->origin[ 2 ],
+                                       
+                                       light->color[ 0 ],
+                                       light->color[ 1 ],
+                                       light->color[ 2 ] );
+                       }
+                       fclose( file );
+               }
+       }
+       
+       /* increment */
+       iterations++;
+       
+       /* print counts */
+       Sys_Printf( "%8d diffuse surfaces\n", numDiffuseSurfaces );
+       Sys_FPrintf( SYS_VRB, "%8d total diffuse lights\n", numDiffuseLights );
+       Sys_FPrintf( SYS_VRB, "%8d brush diffuse lights\n", numBrushDiffuseLights );
+       Sys_FPrintf( SYS_VRB, "%8d patch diffuse lights\n", numPatchDiffuseLights );
+       Sys_FPrintf( SYS_VRB, "%8d triangle diffuse lights\n", numTriangleDiffuseLights );
+}
+
+
+
+
+