+
+/////////////////////////////////////////////////////////////
+
+#define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
+#define FLOODLIGHT_NUM_ANGLE_STEPS 16
+#define FLOODLIGHT_NUM_ELEVATION_STEPS 4
+#define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
+
+static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
+static int numFloodVectors = 0;
+
+void SetupFloodLight( void ){
+ int i, j;
+ float angle, elevation, angleStep, elevationStep;
+ const char *value;
+ double v1,v2,v3,v4,v5,v6;
+
+ /* note it */
+ Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
+
+ /* calculate angular steps */
+ angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
+ elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
+
+ /* iterate angle */
+ angle = 0.0f;
+ for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
+ {
+ /* iterate elevation */
+ for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
+ {
+ floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
+ floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
+ floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
+ numFloodVectors++;
+ }
+ }
+
+ /* emit some statistics */
+ Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
+
+ /* floodlight */
+ value = ValueForKey( &entities[ 0 ], "_floodlight" );
+
+ if ( value[ 0 ] != '\0' ) {
+ v1 = v2 = v3 = 0;
+ v4 = floodlightDistance;
+ v5 = floodlightIntensity;
+ v6 = floodlightDirectionScale;
+
+ sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
+
+ floodlightRGB[0] = v1;
+ floodlightRGB[1] = v2;
+ floodlightRGB[2] = v3;
+
+ if ( VectorLength( floodlightRGB ) == 0 ) {
+ VectorSet( floodlightRGB,0.94,0.94,1.0 );
+ }
+
+ if ( v4 < 1 ) {
+ v4 = 1024;
+ }
+ if ( v5 < 1 ) {
+ v5 = 128;
+ }
+ if ( v6 < 0 ) {
+ v6 = 1;
+ }
+
+ floodlightDistance = v4;
+ floodlightIntensity = v5;
+ floodlightDirectionScale = v6;
+
+ floodlighty = qtrue;
+ Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
+ }
+ else
+ {
+ VectorSet( floodlightRGB,0.94,0.94,1.0 );
+ }
+ if ( colorsRGB ) {
+ floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
+ floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
+ floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
+ }
+ ColorNormalize( floodlightRGB,floodlightRGB );
+}
+
+/*
+ FloodLightForSample()
+ calculates floodlight value for a given sample
+ once again, kudos to the dirtmapping coder
+ */
+
+float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
+ int i;
+ float d;
+ float contribution;
+ int sub = 0;
+ float gatherLight, outLight;
+ vec3_t normal, worldUp, myUp, myRt, direction, displacement;
+ float dd;
+ int vecs = 0;
+
+ gatherLight = 0;
+ /* dummy check */
+ //if( !dirty )
+ // return 1.0f;
+ if ( trace == NULL || trace->cluster < 0 ) {
+ return 0.0f;
+ }
+
+
+ /* setup */
+ dd = floodLightDistance;
+ VectorCopy( trace->normal, normal );
+
+ /* check if the normal is aligned to the world-up */
+ if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
+ if ( normal[ 2 ] == 1.0f ) {
+ VectorSet( myRt, 1.0f, 0.0f, 0.0f );
+ VectorSet( myUp, 0.0f, 1.0f, 0.0f );
+ }
+ else if ( normal[ 2 ] == -1.0f ) {
+ VectorSet( myRt, -1.0f, 0.0f, 0.0f );
+ VectorSet( myUp, 0.0f, 1.0f, 0.0f );
+ }
+ }
+ else
+ {
+ VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
+ CrossProduct( normal, worldUp, myRt );
+ VectorNormalize( myRt, myRt );
+ CrossProduct( myRt, normal, myUp );
+ VectorNormalize( myUp, myUp );
+ }
+
+ /* vortex: optimise floodLightLowQuality a bit */
+ if ( floodLightLowQuality == qtrue ) {
+ /* iterate through ordered vectors */
+ for ( i = 0; i < numFloodVectors; i++ )
+ if ( rand() % 10 != 0 ) {
+ continue;
+ }
+ }
+ else
+ {
+ /* iterate through ordered vectors */
+ for ( i = 0; i < numFloodVectors; i++ )
+ {
+ vecs++;
+
+ /* transform vector into tangent space */
+ direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
+ direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
+ direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
+
+ /* set endpoint */
+ VectorMA( trace->origin, dd, direction, trace->end );
+
+ //VectorMA( trace->origin, 1, direction, trace->origin );
+
+ SetupTrace( trace );
+ VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
+ /* trace */
+ TraceLine( trace );
+ contribution = 1;
+
+ if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
+ contribution = 1.0f;
+ }
+ else if ( trace->opaque ) {
+ VectorSubtract( trace->hit, trace->origin, displacement );
+ d = VectorLength( displacement );
+
+ // d=trace->distance;
+ //if (d>256) gatherDirt+=1;
+ contribution = d / dd;
+ if ( contribution > 1 ) {
+ contribution = 1.0f;
+ }
+
+ //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
+ }
+
+ gatherLight += contribution;
+ }
+ }
+
+ /* early out */
+ if ( gatherLight <= 0.0f ) {
+ return 0.0f;
+ }
+
+ sub = vecs;
+
+ if ( sub < 1 ) {
+ sub = 1;
+ }
+ gatherLight /= ( sub );
+
+ outLight = gatherLight;
+ if ( outLight > 1.0f ) {
+ outLight = 1.0f;
+ }
+
+ /* return to sender */
+ return outLight;
+}
+
+/*
+ FloodLightRawLightmap
+ lighttracer style ambient occlusion light hack.
+ Kudos to the dirtmapping author for most of this source.
+ VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
+ VorteX: fixed problems with deluxemapping
+ */
+
+// floodlight pass on a lightmap
+void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
+ int i, x, y, *cluster;
+ float *origin, *normal, *floodlight, floodLightAmount;
+ surfaceInfo_t *info;
+ trace_t trace;
+ // int sx, sy;
+ // float samples, average, *floodlight2;
+
+ memset( &trace,0,sizeof( trace_t ) );
+
+ /* setup trace */
+ trace.testOcclusion = qtrue;
+ trace.forceSunlight = qfalse;
+ trace.twoSided = qtrue;
+ trace.recvShadows = lm->recvShadows;
+ trace.numSurfaces = lm->numLightSurfaces;
+ trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
+ trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
+ trace.testAll = qfalse;
+ trace.distance = 1024;
+
+ /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
+ //trace.twoSided = qfalse;
+ for ( i = 0; i < trace.numSurfaces; i++ )
+ {
+ /* get surface */
+ info = &surfaceInfos[ trace.surfaces[ i ] ];
+
+ /* check twosidedness */
+ if ( info->si->twoSided ) {
+ trace.twoSided = qtrue;
+ break;
+ }
+ }
+
+ /* gather floodlight */
+ for ( y = 0; y < lm->sh; y++ )
+ {
+ for ( x = 0; x < lm->sw; x++ )
+ {
+ /* get luxel */
+ cluster = SUPER_CLUSTER( x, y );
+ origin = SUPER_ORIGIN( x, y );
+ normal = SUPER_NORMAL( x, y );
+ floodlight = SUPER_FLOODLIGHT( x, y );
+
+ /* set default dirt */
+ *floodlight = 0.0f;
+
+ /* only look at mapped luxels */
+ if ( *cluster < 0 ) {
+ continue;
+ }
+
+ /* copy to trace */
+ trace.cluster = *cluster;
+ VectorCopy( origin, trace.origin );
+ VectorCopy( normal, trace.normal );
+
+ /* get floodlight */
+ floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
+
+ /* add floodlight */
+ floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
+ floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
+ floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
+ floodlight[3] += floodlightDirectionScale;
+ }
+ }
+
+ /* testing no filtering */
+ return;
+
+#if 0
+
+ /* filter "dirt" */
+ for ( y = 0; y < lm->sh; y++ )
+ {
+ for ( x = 0; x < lm->sw; x++ )
+ {
+ /* get luxel */
+ cluster = SUPER_CLUSTER( x, y );
+ floodlight = SUPER_FLOODLIGHT( x, y );
+
+ /* filter dirt by adjacency to unmapped luxels */
+ average = *floodlight;
+ samples = 1.0f;
+ for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
+ {
+ if ( sy < 0 || sy >= lm->sh ) {
+ continue;
+ }
+
+ for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
+ {
+ if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
+ continue;
+ }
+
+ /* get neighboring luxel */
+ cluster = SUPER_CLUSTER( sx, sy );
+ floodlight2 = SUPER_FLOODLIGHT( sx, sy );
+ if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
+ continue;
+ }
+
+ /* add it */
+ average += *floodlight2;
+ samples += 1.0f;
+ }
+
+ /* bail */
+ if ( samples <= 0.0f ) {
+ break;
+ }
+ }
+
+ /* bail */
+ if ( samples <= 0.0f ) {
+ continue;
+ }
+
+ /* scale dirt */
+ *floodlight = average / samples;
+ }
+ }
+#endif
+}
+
+void FloodLightRawLightmap( int rawLightmapNum ){
+ rawLightmap_t *lm;
+
+ /* bail if this number exceeds the number of raw lightmaps */
+ if ( rawLightmapNum >= numRawLightmaps ) {
+ return;
+ }
+ /* get lightmap */
+ lm = &rawLightmaps[ rawLightmapNum ];
+
+ /* global pass */
+ if ( floodlighty && floodlightIntensity ) {
+ FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
+ }
+
+ /* custom pass */
+ if ( lm->floodlightIntensity ) {
+ FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
+ numSurfacesFloodlighten += 1;
+ }
+}
+
+void FloodlightRawLightmaps(){
+ Sys_Printf( "--- FloodlightRawLightmap ---\n" );
+ numSurfacesFloodlighten = 0;
+ RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
+ Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
+}
+
+/*
+ FloodLightIlluminate()
+ illuminate floodlight into lightmap luxels
+ */
+
+void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
+ float *luxel, *floodlight, *deluxel, *normal;
+ int *cluster;
+ float brightness;
+ int x, y, lightmapNum;
+
+ /* walk lightmaps */
+ for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+ {
+ /* early out */
+ if ( lm->superLuxels[ lightmapNum ] == NULL ) {
+ continue;
+ }
+
+ /* apply floodlight to each luxel */
+ for ( y = 0; y < lm->sh; y++ )
+ {
+ for ( x = 0; x < lm->sw; x++ )
+ {
+ /* get floodlight */
+ floodlight = SUPER_FLOODLIGHT( x, y );
+ if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
+ continue;
+ }
+
+ /* get cluster */
+ cluster = SUPER_CLUSTER( x, y );
+
+ /* only process mapped luxels */
+ if ( *cluster < 0 ) {
+ continue;
+ }
+
+ /* get particulars */
+ luxel = SUPER_LUXEL( lightmapNum, x, y );
+ deluxel = SUPER_DELUXEL( x, y );
+
+ /* add to lightmap */
+ luxel[0] += floodlight[0];
+ luxel[1] += floodlight[1];
+ luxel[2] += floodlight[2];
+
+ if ( luxel[3] == 0 ) {
+ luxel[3] = 1;
+ }
+
+ /* add to deluxemap */
+ if ( deluxemap && floodlight[3] > 0 ) {
+ vec3_t lightvector;
+
+ normal = SUPER_NORMAL( x, y );
+ brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
+
+ // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
+ if ( brightness < 0.00390625f ) {
+ brightness = 0.00390625f;
+ }
+
+ VectorScale( normal, brightness, lightvector );
+ VectorAdd( deluxel, lightvector, deluxel );
+ }
+ }
+ }
+ }
+}