--- /dev/null
+/* -------------------------------------------------------------------------------
+
+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 = 0;
+ 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 )
+ {
+ /* 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 = noStyles ? LS_NORMAL : si->lightStyle;
+ if( light->style < LS_NORMAL || light->style >= LS_NONE )
+ light->style = LS_NORMAL;
+
+ /* 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 = noStyles ? LS_NORMAL : 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 = noStyles ? LS_NORMAL : style;
+ if( light->style < LS_NORMAL || light->style >= LS_NONE )
+ light->style = LS_NORMAL;
+
+ /* 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 );
+
+ // jal : avoid bouncing on trans surfaces
+ ApplySurfaceParm( "trans", &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 );
+}
+
+
+
+
+
+