/* ------------------------------------------------------------------------------- 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." ------------------------------------------------------------------------------- */ /* 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, const Vector3& normal, float dist, float epsilon, radWinding_t *front, radWinding_t *back, clipWork_t *cw ){ float *dists; EPlaneSide *sides; int counts[ 3 ]; float dot; /* ydnar: changed from static b/c of threading */ /* VC 4.2 optimizer bug if not static? */ int i, 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++ ) { dists[ i ] = vector3_dot( in->verts[ i ].xyz, normal ) - dist; if ( dists[ i ] > epsilon ) { sides[ i ] = eSideFront; } else if ( dists[ i ] < -epsilon ) { sides[ i ] = eSideBack; } else{ sides[ i ] = eSideOn; } 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 ] == eSideOn ) { memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) ); memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) ); continue; } if ( sides[ i ] == eSideFront ) { memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) ); } if ( sides[ i ] == eSideBack ) { memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) ); } if ( sides[ i + 1 ] == eSideOn || 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 */ /* color */ for ( k = 0; k < MAX_LIGHTMAPS; k++ ){ mid.color[ k ] = v1->color[ k ] + ( v2->color[ k ] - v1->color[ k ] ) * dot; } /* xyz, normal */ mid.xyz = v1->xyz + ( v2->xyz - v1->xyz ) * dot; mid.normal = v1->normal + ( v2->normal - v1->normal ) * dot; /* st, lightmap */ mid.st = v1->st + ( v2->st - v1->st ) * dot; for ( k = 0; k < MAX_LIGHTMAPS; k++ ) mid.lightmap[ k ] = v1->lightmap[ k ] + ( v2->lightmap[ k ] - v1->lightmap[ k ] ) * dot; /* normalize the averaged normal */ VectorNormalize( 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 ) { Error( "RadClipWindingEpsilon: points exceeded estimate" ); } if ( front->numVerts > MAX_POINTS_ON_WINDING ) { Error( "RadClipWindingEpsilon: MAX_POINTS_ON_WINDING" ); } } float Modulo1IfNegative( float f ){ return f < 0.0f ? f - floor( f ) : f; } /* RadSampleImage() samples a texture image for a given color returns false if pixels are bad */ bool RadSampleImage( byte *pixels, int width, int height, const Vector2& st, Color4f& color ){ int x, y; /* clear color first */ color.set( 255 ); /* dummy check */ if ( pixels == NULL || width < 1 || height < 1 ) { return false; } /* get offsets */ x = ( (float) width * Modulo1IfNegative( st[ 0 ] ) ) + 0.5f; x %= width; y = ( (float) height * Modulo1IfNegative( st[ 1 ] ) ) + 0.5f; y %= height; /* get pixel */ pixels += ( y * width * 4 ) + ( x * 4 ); VectorCopy( pixels, color.rgb() ); color.alpha() = 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 true; } /* 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, Vector3& average, Vector3& gradient, int *style ){ int i, j, k, l, v, samples; Vector3 color; MinMax minmax; Color4f textureColor; float alpha, alphaI; radVert_t *rv[ 3 ]; if ( !bouncing ) Sys_Printf( "BUG: RadSample: !bouncing shouldn't happen\n" ); /* initial setup */ average.set( 0 ); gradient.set( 0 ); 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 ) { 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 ) ) { textureColor.rgb() = si->averageColor.rgb(); textureColor.alpha() = 255.0f; } const float avgcolor = ( textureColor[ 0 ] + textureColor[ 1 ] + textureColor[ 2 ] ) / 3; color = ( ( textureColor.rgb() * bounceColorRatio + Vector3( avgcolor * ( 1 - bounceColorRatio ) ) ) / 255 ) * ( rw->verts[ samples ].color[ lightmapNum ].rgb() / 255.0f ); // color = ( textureColor.rgb / 255 ) * ( rw->verts[ samples ].color[ lightmapNum ].rgb / 255.0f ); minmax.extend( color ); average += color; /* get alpha */ alpha += ( textureColor.alpha() / 255.0f ) * ( rw->verts[ samples ].color[ lightmapNum ].alpha() / 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 embarrassing (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) */ DoubleVector3 blend( i, j, k ); blend *= 1.0 / ( blend[ 0 ] + blend[ 1 ] + blend[ 2 ] ); /* create a blended sample */ Vector2 st( 0, 0 ); Vector2 lightmap( 0, 0 ); alphaI = 0.0f; for ( l = 0; l < 3; l++ ) { st += rv[ l ]->st * blend[ l ]; lightmap += rv[ l ]->lightmap[ lightmapNum ] * blend[ l ]; alphaI += rv[ l ]->color[ lightmapNum ].alpha() * blend[ l ]; } /* get lightmap xy coords */ const int x = std::clamp( int( lightmap[ 0 ] / superSample ), 0, lm->w - 1 ); const int y = std::clamp( int( lightmap[ 1 ] / superSample ), 0, lm->h - 1 ); /* get radiosity luxel */ const Vector3& radLuxel = lm->getRadLuxel( 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 ) ) { textureColor.rgb() = si->averageColor.rgb(); textureColor.alpha() = 255; } const float avgcolor = ( textureColor[ 0 ] + textureColor[ 1 ] + textureColor[ 2 ] ) / 3; color = ( ( textureColor.rgb() * bounceColorRatio + Vector3( avgcolor * ( 1 - bounceColorRatio ) ) ) / 255 ) * ( radLuxel / 255 ); //Sys_Printf( "%i %i %i %i %i \n", (int) textureColor.rgb[ 0 ], (int) textureColor.rgb[ 1 ], (int) textureColor.rgb[ 2 ], (int) avgcolor, (int) color[ i ] ); minmax.extend( color ); average += color; /* get alpha */ alpha += ( textureColor.alpha() / 255 ) * ( alphaI / 255 ); } } } } /* set style */ *style = ds->lightmapStyles[ lightmapNum ]; } /* any samples? */ if ( samples <= 0 ) { return; } /* average the color */ average *= ( 1.0 / samples ); /* create the color gradient */ //% VectorSubtract( minmax.maxs, minmax.mins, delta ); /* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */ //% gradient[ 0 ] = minmax.maxs[ 0 ] > 0.0f ? (minmax.maxs[ 0 ] - minmax.mins[ 0 ]) / minmax.maxs[ 0 ] : 0.0f; //% gradient[ 1 ] = minmax.maxs[ 1 ] > 0.0f ? (minmax.maxs[ 1 ] - minmax.mins[ 1 ]) / minmax.maxs[ 1 ] : 0.0f; //% gradient[ 2 ] = minmax.maxs[ 2 ] > 0.0f ? (minmax.maxs[ 2 ] - minmax.mins[ 2 ]) / minmax.maxs[ 2 ] : 0.0f; /* newer: another contrast function */ gradient = ( minmax.maxs - minmax.mins ) * minmax.maxs; } /* 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, radWinding_t *rw, clipWork_t *cw ){ int i, style = 0; float dist, area, value; Vector3 normal, color, gradient; light_t *light, *splash; winding_t *w, *splash_w; /* dummy check */ if ( rw == NULL || rw->numVerts < 3 ) { return; } /* get bounds for winding */ MinMax minmax; for ( i = 0; i < rw->numVerts; i++ ) minmax.extend( rw->verts[ i ].xyz ); /* subdivide if necessary */ for ( i = 0; i < 3; i++ ) { if ( minmax.maxs[ i ] - minmax.mins[ i ] > subdivide ) { auto front = std::make_unique(); auto back = std::make_unique(); /* make axial plane */ dist = ( minmax.maxs[ i ] + minmax.mins[ i ] ) * 0.5f; /* clip the winding */ RadClipWindingEpsilon( rw, g_vector3_axes[i], dist, RADIOSITY_CLIP_EPSILON, front.get(), back.get(), cw ); /* recurse */ RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, front.get(), cw ); RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, back.get(), cw ); return; } } /* check area */ area = 0.0f; for ( i = 2; i < rw->numVerts; i++ ) { area += 0.5f * vector3_length( vector3_cross( rw->verts[ i - 1 ].xyz - rw->verts[ 0 ].xyz, rw->verts[ i ].xyz - rw->verts[ 0 ].xyz ) ); } 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 ), rw, cw ); return; } } /* create a regular winding and an average normal */ w = AllocWinding( rw->numVerts ); w->numpoints = rw->numVerts; normal.set( 0 ); for ( i = 0; i < rw->numVerts; i++ ) { w->p[ i ] = rw->verts[ i ].xyz; normal += rw->verts[ i ].normal; } normal /= rw->numVerts; if ( VectorNormalize( normal ) == 0.0f ) { return; } /* early out? */ if ( bouncing && vector3_length( color ) < RADIOSITY_MIN ) { return; } /* debug code */ //% Sys_Printf( "Size: %d %d %d\n", (int) (minmax.maxs[ 0 ] - minmax.mins[ 0 ]), (int) (minmax.maxs[ 1 ] - minmax.mins[ 1 ]), (int) (minmax.maxs[ 2 ] - minmax.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_calloc( sizeof( *light ) ); /* attach it */ ThreadLock(); light->next = lights; lights = light; ThreadUnlock(); /* initialize the light */ light->flags = LightFlags::DefaultArea; light->type = ELightType::Area; light->si = si; light->fade = 1.0f; light->w = w; /* set falloff threshold */ light->falloffTolerance = falloffTolerance; /* bouncing light? */ if ( !bouncing ) { /* 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; light->color = si->color; light->style = noStyles ? LS_NORMAL : si->lightStyle; if ( light->style < LS_NORMAL || light->style >= LS_NONE ) { light->style = LS_NORMAL; } /* set origin */ light->origin = minmax.origin(); /* nudge it off the plane a bit */ light->normal = normal; light->origin += light->normal; light->dist = vector3_dot( light->origin, normal ); #if 0 /* optionally create a point backsplash light */ if ( si->backsplashFraction > 0 ) { /* allocate a new point light */ splash = safe_calloc( sizeof( *splash ) ); splash->next = lights; lights = splash; /* set it up */ splash->flags = LightFlags::DefaultQ3A; splash->type = ELightType::Point; splash->photons = light->photons * si->backsplashFraction; splash->fade = 1.0f; splash->si = si; splash->origin = normal * si->backsplashDistance + light->origin; splash->color = si->color; splash->falloffTolerance = falloffTolerance; splash->style = noStyles ? LS_NORMAL : light->style; /* add to counts */ numPointLights++; } #endif #if 1 /* optionally create area backsplash light */ //if ( original && si->backsplashFraction > 0 ) { if ( si->backsplashFraction > 0 && !( si->compileFlags & C_SKY ) ) { /* allocate a new area light */ splash = safe_calloc( sizeof( *splash ) ); ThreadLock(); splash->next = lights; lights = splash; ThreadUnlock(); /* set it up */ splash->flags = LightFlags::DefaultArea; splash->type = ELightType::Area; splash->photons = light->photons * 7.0f * si->backsplashFraction; splash->add = light->add * 7.0f * si->backsplashFraction; splash->fade = 1.0f; splash->si = si; splash->color = si->color; splash->falloffTolerance = falloffTolerance; splash->style = noStyles ? LS_NORMAL : si->lightStyle; if ( splash->style < LS_NORMAL || splash->style >= LS_NONE ) { splash->style = LS_NORMAL; } /* create a regular winding */ splash_w = AllocWinding( rw->numVerts ); splash_w->numpoints = rw->numVerts; for ( i = 0; i < rw->numVerts; i++ ) splash_w->p[ i ] = rw->verts[rw->numVerts - 1 - i].xyz + normal * si->backsplashDistance; splash->w = splash_w; splash->origin = normal * si->backsplashDistance + light->origin; splash->normal = -normal; splash->dist = vector3_dot( splash->origin, splash->normal ); // splash->flags |= LightFlags::Twosided; } #endif } 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; light->color = color; light->style = noStyles ? LS_NORMAL : style; if ( light->style < LS_NORMAL || light->style >= LS_NONE ) { light->style = LS_NORMAL; } /* set origin */ light->origin = WindingCenter( w ); /* nudge it off the plane a bit */ light->normal = normal; light->origin += light->normal; light->dist = vector3_dot( 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 |= LightFlags::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; 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++ ) { rw.verts[ j ].color[ k ].rgb() = getRadVertexLuxel( k, ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ] ); rw.verts[ j ].color[ k ].alpha() = yDrawVerts[ v ].color[ k ].alpha(); } } /* subdivide into area lights */ RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, &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; bool 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? */ Plane3f plane; planar = PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ); if ( planar ) { if ( fabs( plane3_distance_to_point( plane, dv[ 1 ]->xyz ) ) > PLANAR_EPSILON ) { planar = false; } } /* 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++ ) { rw.verts[ v ].color[ i ].rgb() = getRadVertexLuxel( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] ); rw.verts[ v ].color[ i ].alpha() = dv[ v ]->color[ i ].alpha(); } } /* subdivide into area lights */ RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, &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++ ) { rw.verts[ v ].color[ i ].rgb() = getRadVertexLuxel( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] ); rw.verts[ v ].color[ i ].alpha() = dv[ v ]->color[ i ].alpha(); } } /* subdivide into area lights */ RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, &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; /* hit every surface (threaded) */ RunThreadsOnIndividual( numBSPDrawSurfaces, true, RadLight ); /* dump the lights generated to a file */ if ( dump ) { char dumpName[ 1024 ], ext[ 64 ]; FILE *file; light_t *light; strcpy( dumpName, source ); sprintf( ext, "_bounce_%03d.map", iterations ); path_set_extension( 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 ); }