/* ------------------------------------------------------------------------------- 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 "vmap.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 ]; if ( texturesRGB ) { color[0] = Image_LinearFloatFromsRGBFloat( color[0] * ( 1.0 / 255.0 ) ) * 255.0; color[1] = Image_LinearFloatFromsRGBFloat( color[1] * ( 1.0 / 255.0 ) ) * 255.0; color[2] = Image_LinearFloatFromsRGBFloat( color[2] * ( 1.0 / 255.0 ) ) * 255.0; } 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 ]; if (!bouncing) Sys_Printf( "BUG: RadSample: !bouncing shouldn't happen\n" ); /* 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: case MST_PATCHFIXED: 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 ) { /* This is weird. This actually handles surfacelight and not * bounces. */ /* 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; /* we'll absorb too much ambient light */ 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 ); } if (light->photons < 0 || light->add < 0 || light->color[0] < 0 || light->color[1] < 0 || light->color[2] < 0) Sys_Printf( "BUG: RadSubdivideDiffuseLight created a darkbulb\n" ); /* 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; float *radVertexLuxel; radWinding_t rw; /* get surface */ ds = &bspDrawSurfaces[ 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, qboolean fixed ){ 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! */ if (fixed) { src.width = ds->patchWidth&0xffff; src.height = ds->patchHeight&0xffff; src.subdiv_x = ds->patchWidth>>16; src.subdiv_y = ds->patchHeight>>16; } else { src.width = ds->patchWidth; src.height = ds->patchHeight; src.subdiv_x = -1; src.subdiv_y = -1; } 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: case MST_PATCHFIXED: RadLightForPatch( num, lightmapNum, lm, si, scale, subdivide, &cw, (ds->surfaceType==MST_PATCHFIXED)?qtrue:qfalse ); 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 ); }