This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/xtools/bsplib/light.c

2284 lines
60 KiB
C

/* -------------------------------------------------------------------------------
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_C
/* dependencies */
#include "q3map2.h"
#include "stdio.h" // sscanf
/*
CreateSunLight() - ydnar
this creates a sun light
*/
static void CreateSunLight( sun_t *sun )
{
int i;
float photons, d, angle, elevation, da, de;
vec3_t direction;
light_t *light;
/* dummy check */
if( sun == NULL )
return;
/* fixup */
if( sun->numSamples < 1 )
sun->numSamples = 1;
/* set photons */
photons = sun->photons / sun->numSamples;
/* create the right number of suns */
for( i = 0; i < sun->numSamples; i++ )
{
/* calculate sun direction */
if( i == 0 )
VectorCopy( sun->direction, direction );
else
{
/*
sun->direction[ 0 ] = cos( angle ) * cos( elevation );
sun->direction[ 1 ] = sin( angle ) * cos( elevation );
sun->direction[ 2 ] = sin( elevation );
xz_dist = sqrt( x*x + z*z )
latitude = atan2( xz_dist, y ) * RADIANS
longitude = atan2( x, z ) * RADIANS
*/
d = sqrt( sun->direction[ 0 ] * sun->direction[ 0 ] + sun->direction[ 1 ] * sun->direction[ 1 ] );
angle = atan2( sun->direction[ 1 ], sun->direction[ 0 ] );
elevation = atan2( sun->direction[ 2 ], d );
/* jitter the angles (loop to keep random sample within sun->deviance steridians) */
do
{
da = (Random() * 2.0f - 1.0f) * sun->deviance;
de = (Random() * 2.0f - 1.0f) * sun->deviance;
}
while( (da * da + de * de) > (sun->deviance * sun->deviance) );
angle += da;
elevation += de;
/* debug code */
//% Msg( "%d: Angle: %3.4f Elevation: %3.3f\n", sun->numSamples, (angle / Q_PI * 180.0f), (elevation / Q_PI * 180.0f) );
/* create new vector */
direction[ 0 ] = cos( angle ) * cos( elevation );
direction[ 1 ] = sin( angle ) * cos( elevation );
direction[ 2 ] = sin( elevation );
}
/* create a light */
numSunLights++;
light = Malloc( sizeof( *light ) );
light->next = lights;
lights = light;
/* initialize the light */
light->flags = LIGHT_SUN_DEFAULT;
light->type = EMIT_SUN;
light->fade = 1.0f;
light->falloffTolerance = falloffTolerance;
light->filterRadius = sun->filterRadius / sun->numSamples;
light->style = noStyles ? LS_NORMAL : sun->style;
/* set the light's position out to infinity */
VectorMA( vec3_origin, (MAX_WORLD_COORD * 8.0f), direction, light->origin ); /* MAX_WORLD_COORD * 2.0f */
/* set the facing to be the inverse of the sun direction */
VectorScale( direction, -1.0, light->normal );
light->dist = DotProduct( light->origin, light->normal );
/* set color and photons */
VectorCopy( sun->color, light->color );
light->photons = photons * skyScale;
}
/* another sun? */
if( sun->next != NULL )
CreateSunLight( sun->next );
}
/*
CreateSkyLights() - ydnar
simulates sky light with multiple suns
*/
static void CreateSkyLights( vec3_t color, float value, int iterations, float filterRadius, int style )
{
int i, j, numSuns;
int angleSteps, elevationSteps;
float angle, elevation;
float angleStep, elevationStep;
float step, start;
sun_t sun;
/* dummy check */
if( value <= 0.0f || iterations < 2 )
return;
/* calculate some stuff */
step = 2.0f / (iterations - 1);
start = -1.0f;
/* basic sun setup */
VectorCopy( color, sun.color );
sun.deviance = 0.0f;
sun.filterRadius = filterRadius;
sun.numSamples = 1;
sun.style = noStyles ? LS_NORMAL : style;
sun.next = NULL;
/* setup */
elevationSteps = iterations - 1;
angleSteps = elevationSteps * 4;
angle = 0.0f;
elevationStep = DEG2RAD( 90.0f / iterations ); /* skip elevation 0 */
angleStep = DEG2RAD( 360.0f / angleSteps );
/* calc individual sun brightness */
numSuns = angleSteps * elevationSteps + 1;
sun.photons = value / numSuns;
/* iterate elevation */
elevation = elevationStep * 0.5f;
angle = 0.0f;
for( i = 0, elevation = elevationStep * 0.5f; i < elevationSteps; i++ )
{
/* iterate angle */
for( j = 0; j < angleSteps; j++ )
{
/* create sun */
sun.direction[ 0 ] = cos( angle ) * cos( elevation );
sun.direction[ 1 ] = sin( angle ) * cos( elevation );
sun.direction[ 2 ] = sin( elevation );
CreateSunLight( &sun );
/* move */
angle += angleStep;
}
/* move */
elevation += elevationStep;
angle += angleStep / elevationSteps;
}
/* create vertical sun */
VectorSet( sun.direction, 0.0f, 0.0f, 1.0f );
CreateSunLight( &sun );
/* short circuit */
return;
}
/*
CreateEntityLights()
creates lights from light entities
*/
void CreateEntityLights( void )
{
int i, j;
light_t *light, *light2;
entity_t *e, *e2;
const char *name;
const char *target;
vec3_t dest;
const char *value;
float intensity, scale, deviance, filterRadius;
int spawnflags, flags, numSamples;
bool junior, monolight;
double vec[4], col[3];
/* go throught entity list and find lights */
for( i = 0; i < numEntities; i++ )
{
e = &entities[i];
name = ValueForKey( e, "classname" );
// check for lightJunior
if( !com.strnicmp( name, "lightJunior", 11 ))
junior = true;
else if( !com.strnicmp( name, "light", 5 ))
junior = false;
else continue;
// lights with target names (and therefore styles) are only parsed from BSP
target = ValueForKey( e, "targetname" );
if( target[0] != '\0' && i >= numBSPEntities )
continue;
numPointLights++;
light = Malloc( sizeof( *light ));
light->next = lights;
lights = light;
/* handle spawnflags */
spawnflags = IntForKey( e, "spawnflags" );
/* ydnar: quake 3+ light behavior */
if( wolfLight == false )
{
/* set default flags */
flags = LIGHT_Q3A_DEFAULT;
/* linear attenuation? */
if( spawnflags & 1 )
{
flags |= LIGHT_ATTEN_LINEAR;
flags &= ~LIGHT_ATTEN_ANGLE;
}
/* no angle attenuate? */
if( spawnflags & 2 )
flags &= ~LIGHT_ATTEN_ANGLE;
}
/* ydnar: wolf light behavior */
else
{
/* set default flags */
flags = LIGHT_WOLF_DEFAULT;
// spawnflag 1 is reserved for START_OFF
/* angle attenuate? */
if( spawnflags & 2 )
{
flags |= LIGHT_ATTEN_ANGLE;
flags &= ~LIGHT_ATTEN_LINEAR;
}
}
/* other flags (borrowed from wolf) */
// wolf dark light
if(( spawnflags & 4 ) || ( spawnflags & 8 ))
flags |= LIGHT_DARK;
// disable lightgrid for this source
if( spawnflags & 16 ) flags &= ~LIGHT_GRID;
if( junior )
{
flags |= LIGHT_GRID;
flags &= ~LIGHT_SURFACES;
}
light->flags = flags;
// set fade key (from wolf)
light->fade = 1.0f;
if( light->flags & LIGHT_ATTEN_LINEAR )
{
light->fade = FloatForKey( e, "fade" );
if( light->fade == 0.0f ) light->fade = 1.0f;
}
// set angle scaling (from vlight)
light->angleScale = FloatForKey( e, "_anglescale" );
if( light->angleScale != 0.0f ) light->flags |= LIGHT_ATTEN_ANGLE;
GetVectorForKey( e, "origin", light->origin );
light->style = IntForKey( e, "_style" );
if( light->style == LS_NORMAL ) light->style = IntForKey( e, "style" );
if( light->style < LS_NORMAL || light->style >= LS_NONE )
Sys_Break( "Invalid lightstyle (%d) on entity %d", light->style, i );
if( light->style != LS_NORMAL )
MsgDev( D_NOTE, "styled light found targeting %s\n **", target );
value = ValueForKey( e, "light" );
if( !value[0] ) value = ValueForKey( e, "_light" );
// assume default light color
VectorSet( light->color, 1.0f, 1.0f, 1.0f );
intensity = 300.0f;
if( value[0] )
{
switch( sscanf( value, "%lf %lf %lf %lf", &vec[0], &vec[1], &vec[2], &vec[3] ))
{
case 4: // HalfLife light
VectorSet( light->color, vec[0], vec[1], vec[2] );
VectorDivide( light->color, 255.0f, light->color );
intensity = vec[3];
break;
case 3: // Half-Life light_environment
VectorSet( light->intensity, vec[0], vec[1], vec[2] );
VectorDivide( light->color, 255.0f, light->color );
break;
case 1: // Quake light
intensity = vec[0];
monolight = true;
break;
default:
MsgDev( D_WARN, "%s [%i]: '_light' key must be 1 (q1) or 3 or 4 (hl) numbers\n", name, i );
break;
}
}
// set light color
if( monolight )
{
value = ValueForKey( e, "color" );
if( !value[0] ) value = ValueForKey( e, "_color" );
if( value[0] )
{
if(sscanf( value, "%lf %lf %lf", &col[0], &col[1], &col[2] ) != 3 )
{
MsgDev( D_WARN, "light at %.0f %.0f %.0f:\ncolor must be given 3 values\n",
light->origin[0], light->origin[1], light->origin[2] );
}
VectorCopy( col, light->color );
}
else VectorScale( light->color, 0.5f, light->color );
}
ColorNormalize( light->color, light->color );
// set light scale (sof2)
scale = FloatForKey( e, "scale" );
if( scale == 0.0f ) scale = 1.0f;
intensity *= scale;
/* ydnar: get deviance and samples */
deviance = FloatForKey( e, "_deviance" );
if( deviance == 0.0f ) deviance = FloatForKey( e, "_deviation" );
if( deviance == 0.0f ) deviance = FloatForKey( e, "_jitter" );
numSamples = IntForKey( e, "_samples" );
if( deviance < 0.0f || numSamples < 1 )
{
deviance = 0.0f;
numSamples = 1;
}
intensity /= numSamples;
// get filter radius
filterRadius = FloatForKey( e, "_filterradius" );
if( filterRadius == 0.0f ) filterRadius = FloatForKey( e, "_filteradius" );
if( filterRadius == 0.0f ) filterRadius = FloatForKey( e, "_filter" );
if( filterRadius < 0.0f ) filterRadius = 0.0f;
light->filterRadius = filterRadius;
intensity = intensity * pointScale;
light->photons = intensity;
light->type = EMIT_POINT;
/* set falloff threshold */
light->falloffTolerance = falloffTolerance / numSamples;
/* lights with a target will be spotlights */
target = ValueForKey( e, "target" );
if( target[ 0 ] )
{
float radius;
float dist;
sun_t sun;
const char *_sun;
/* get target */
e2 = FindTargetEntity( target );
if( e2 == NULL )
{
MsgDev( D_WARN, "light at (%i %i %i) has missing target\n",
(int)light->origin[0], (int)light->origin[1], (int)light->origin[2] );
}
else
{
/* not a point light */
numPointLights--;
numSpotLights++;
/* make a spotlight */
GetVectorForKey( e2, "origin", dest );
VectorSubtract( dest, light->origin, light->normal );
dist = VectorNormalizeLength( light->normal );
radius = FloatForKey( e, "radius" );
if( !radius ) radius = 64;
if( !dist ) dist = 64;
light->radiusByDist = (radius + 16) / dist;
light->type = EMIT_SPOT;
/* ydnar: wolf mods: spotlights always use nonlinear + angle attenuation */
light->flags &= ~LIGHT_ATTEN_LINEAR;
light->flags |= LIGHT_ATTEN_ANGLE;
light->fade = 1.0f;
/* ydnar: is this a sun? */
_sun = ValueForKey( e, "_sun" );
if( _sun[ 0 ] == '1' )
{
/* not a spot light */
numSpotLights--;
/* unlink this light */
lights = light->next;
/* make a sun */
VectorScale( light->normal, -1.0f, sun.direction );
VectorCopy( light->color, sun.color );
sun.photons = (intensity / pointScale);
sun.deviance = deviance / 180.0f * M_PI;
sun.numSamples = numSamples;
sun.style = noStyles ? LS_NORMAL : light->style;
sun.next = NULL;
/* make a sun light */
CreateSunLight( &sun );
/* free original light */
Mem_Free( light );
light = NULL;
/* skip the rest of this love story */
continue;
}
}
}
/* jitter the light */
for( j = 1; j < numSamples; j++ )
{
/* create a light */
light2 = Malloc( sizeof( *light ) );
memcpy( light2, light, sizeof( *light ) );
light2->next = lights;
lights = light2;
/* add to counts */
if( light->type == EMIT_SPOT )
numSpotLights++;
else
numPointLights++;
/* jitter it */
light2->origin[0] = light->origin[0] + (Random() * 2.0f - 1.0f) * deviance;
light2->origin[1] = light->origin[1] + (Random() * 2.0f - 1.0f) * deviance;
light2->origin[2] = light->origin[2] + (Random() * 2.0f - 1.0f) * deviance;
}
}
}
/*
CreateSurfaceLights() - ydnar
this hijacks the radiosity code to generate surface lights for first pass
*/
#define APPROX_BOUNCE 1.0f
void CreateSurfaceLights( void )
{
int i;
bspDrawSurface_t *ds;
surfaceInfo_t *info;
shaderInfo_t *si;
light_t *light;
float subdivide;
vec3_t origin;
clipWork_t cw;
const char *nss;
/* get sun shader supressor */
nss = ValueForKey( &entities[ 0 ], "_noshadersun" );
/* walk the list of surfaces */
for( i = 0; i < numBSPDrawSurfaces; i++ )
{
/* get surface and other bits */
ds = &bspDrawSurfaces[ i ];
info = &surfaceInfos[ i ];
si = info->si;
/* sunlight? */
if( si->sun != NULL && nss[ 0 ] != '1' )
{
MsgDev( D_NOTE, "Sun: %s\n", si->shader );
CreateSunLight( si->sun );
si->sun = NULL; /* FIXME: leak! */
}
/* sky light? */
if( si->skyLightValue > 0.0f )
{
MsgDev( D_NOTE, "Sky: %s\n", si->shader );
CreateSkyLights( si->color, si->skyLightValue, si->skyLightIterations, si->lightFilterRadius, si->lightStyle );
si->skyLightValue = 0.0f; /* FIXME: hack! */
}
/* try to early out */
if( si->value <= 0 )
continue;
/* autosprite shaders become point lights */
if( si->autosprite )
{
/* create an average xyz */
VectorAdd( info->mins, info->maxs, origin );
VectorScale( origin, 0.5f, origin );
/* create a light */
light = Malloc( sizeof( *light ) );
memset( light, 0, sizeof( *light ) );
light->next = lights;
lights = light;
/* set it up */
light->flags = LIGHT_Q3A_DEFAULT;
light->type = EMIT_POINT;
light->photons = si->value * pointScale;
light->fade = 1.0f;
light->si = si;
VectorCopy( origin, light->origin );
VectorCopy( si->color, light->color );
light->falloffTolerance = falloffTolerance;
light->style = si->lightStyle;
/* add to point light count and continue */
numPointLights++;
continue;
}
/* get subdivision amount */
if( si->lightSubdivide > 0 )
subdivide = si->lightSubdivide;
else
subdivide = defaultLightSubdivide;
/* switch on type */
switch( ds->surfaceType )
{
case MST_PLANAR:
case MST_TRISURF:
RadLightForTriangles( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
break;
case MST_PATCH:
RadLightForPatch( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
break;
default: break;
}
}
}
/*
SetEntityOrigins()
find the offset values for inline models
*/
void SetEntityOrigins( void )
{
int i, j, k, f;
entity_t *e;
vec3_t origin;
const char *key;
int modelnum;
bspModel_t *dm;
bspDrawSurface_t *ds;
/* ydnar: copy drawverts into private storage for nefarious purposes */
yDrawVerts = Malloc( numBSPDrawVerts * sizeof( bspDrawVert_t ) );
memcpy( yDrawVerts, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVert_t ) );
/* set the entity origins */
for( i = 0; i < numEntities; i++ )
{
/* get entity and model */
e = &entities[ i ];
key = ValueForKey( e, "model" );
if( key[ 0 ] != '*' )
continue;
modelnum = atoi( key + 1 );
dm = &bspModels[ modelnum ];
/* get entity origin */
key = ValueForKey( e, "origin" );
if( key[ 0 ] == '\0' )
continue;
GetVectorForKey( e, "origin", origin );
/* set origin for all surfaces for this model */
for( j = 0; j < dm->numBSPSurfaces; j++ )
{
/* get drawsurf */
ds = &bspDrawSurfaces[ dm->firstBSPSurface + j ];
/* set its verts */
for( k = 0; k < ds->numVerts; k++ )
{
f = ds->firstVert + k;
VectorAdd( origin, bspDrawVerts[ f ].xyz, yDrawVerts[ f ].xyz );
}
}
}
}
/*
PointToPolygonFormFactor()
calculates the area over a point/normal hemisphere a winding covers
ydnar: fixme: there has to be a faster way to calculate this
without the expensive per-vert sqrts and transcendental functions
ydnar 2002-09-30: added -faster switch because only 19% deviance > 10%
between this and the approximation
*/
#define ONE_OVER_2PI 0.159154942f //% (1.0f / (2.0f * 3.141592657f))
float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w )
{
vec3_t triVector, triNormal;
int i, j;
vec3_t dirs[ MAX_POINTS_ON_WINDING ];
float total;
float dot, angle, facing;
/* this is expensive */
for( i = 0; i < w->numpoints; i++ )
{
VectorSubtract( w->p[i], point, dirs[i] );
VectorNormalize( dirs[i] );
}
/* duplicate first vertex to avoid mod operation */
VectorCopy( dirs[ 0 ], dirs[ i ] );
/* calculcate relative area */
total = 0.0f;
for( i = 0; i < w->numpoints; i++ )
{
/* get a triangle */
j = i + 1;
dot = DotProduct( dirs[ i ], dirs[ j ] );
/* roundoff can cause slight creep, which gives an IND from acos */
if( dot > 1.0f )
dot = 1.0f;
else if( dot < -1.0f )
dot = -1.0f;
/* get the angle */
angle = acos( dot );
CrossProduct( dirs[ i ], dirs[ j ], triVector );
if( VectorNormalizeLength2( triVector, triNormal ) < 0.0001f )
continue;
facing = DotProduct( normal, triNormal );
total += facing * angle;
/* ydnar: this was throwing too many errors with radiosity + crappy maps. ignoring it. */
if( total > 6.3f || total < -6.3f )
return 0.0f;
}
/* now in the range of 0 to 1 over the entire incoming hemisphere */
//% total /= (2.0f * 3.141592657f);
total *= ONE_OVER_2PI;
return total;
}
/*
LightContributionTosample()
determines the amount of light reaching a sample (luxel or vertex) from a given light
*/
int LightContributionToSample( light_trace_t *trace )
{
light_t *light;
float angle;
float add;
float dist;
/* get light */
light = trace->light;
/* clear color */
VectorClear( trace->color );
/* ydnar: early out */
if( !(light->flags & LIGHT_SURFACES) || light->envelope <= 0.0f )
return 0;
/* do some culling checks */
if( light->type != EMIT_SUN )
{
/* MrE: if the light is behind the surface */
if( trace->twoSided == false )
if( DotProduct( light->origin, trace->normal ) - DotProduct( trace->origin, trace->normal ) < 0.0f )
return 0;
/* ydnar: test pvs */
if( !ClusterVisible( trace->cluster, light->cluster ) )
return 0;
}
/* exact point to polygon form factor */
if( light->type == EMIT_AREA )
{
float factor;
float d;
vec3_t pushedOrigin;
/* project sample point into light plane */
d = DotProduct( trace->origin, light->normal ) - light->dist;
if( d < 3.0f )
{
/* sample point behind plane? */
if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
return 0;
/* sample plane coincident? */
if( d > -3.0f && DotProduct( trace->normal, light->normal ) > 0.9f )
return 0;
}
/* nudge the point so that it is clearly forward of the light */
/* so that surfaces meeting a light emiter don't get black edges */
if( d > -8.0f && d < 8.0f )
VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );
else
VectorCopy( trace->origin, pushedOrigin );
/* get direction and distance */
VectorCopy( light->origin, trace->end );
dist = SetupTrace( trace );
if( dist >= light->envelope )
return 0;
/* ptpff approximation */
if( faster )
{
/* angle attenuation */
angle = DotProduct( trace->normal, trace->direction );
/* twosided lighting */
if( trace->twoSided )
angle = fabs( angle );
/* attenuate */
angle *= -DotProduct( light->normal, trace->direction );
if( angle == 0.0f )
return 0;
else if( angle < 0.0f &&
(trace->twoSided || (light->flags & LIGHT_TWOSIDED)) )
angle = -angle;
add = light->photons / (dist * dist) * angle;
}
else
{
/* calculate the contribution */
factor = PointToPolygonFormFactor( pushedOrigin, trace->normal, light->w );
if( factor == 0.0f )
return 0;
else if( factor < 0.0f )
{
/* twosided lighting */
if( trace->twoSided || (light->flags & LIGHT_TWOSIDED) )
{
factor = -factor;
/* push light origin to other side of the plane */
VectorMA( light->origin, -2.0f, light->normal, trace->end );
dist = SetupTrace( trace );
if( dist >= light->envelope )
return 0;
}
else
return 0;
}
/* ydnar: moved to here */
add = factor * light->add;
}
}
/* point/spot lights */
else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )
{
/* get direction and distance */
VectorCopy( light->origin, trace->end );
dist = SetupTrace( trace );
if( dist >= light->envelope )
return 0;
/* clamp the distance to prevent super hot spots */
if( dist < 16.0f )
dist = 16.0f;
/* angle attenuation */
angle = (light->flags & LIGHT_ATTEN_ANGLE) ? DotProduct( trace->normal, trace->direction ) : 1.0f;
if( light->angleScale != 0.0f )
{
angle /= light->angleScale;
if( angle > 1.0f )
angle = 1.0f;
}
/* twosided lighting */
if( trace->twoSided )
angle = fabs( angle );
/* attenuate */
if( light->flags & LIGHT_ATTEN_LINEAR )
{
add = angle * light->photons * linearScale - (dist * light->fade);
if( add < 0.0f )
add = 0.0f;
}
else
add = light->photons / (dist * dist) * angle;
/* handle spotlights */
if( light->type == EMIT_SPOT )
{
float distByNormal, radiusAtDist, sampleRadius;
vec3_t pointAtDist, distToSample;
/* do cone calculation */
distByNormal = -DotProduct( trace->displacement, light->normal );
if( distByNormal < 0.0f )
return 0;
VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
radiusAtDist = light->radiusByDist * distByNormal;
VectorSubtract( trace->origin, pointAtDist, distToSample );
sampleRadius = VectorLength( distToSample );
/* outside the cone */
if( sampleRadius >= radiusAtDist )
return 0;
/* attenuate */
if( sampleRadius > (radiusAtDist - 32.0f) )
add *= ((radiusAtDist - sampleRadius) / 32.0f);
}
}
/* ydnar: sunlight */
else if( light->type == EMIT_SUN )
{
/* get origin and direction */
VectorAdd( trace->origin, light->origin, trace->end );
dist = SetupTrace( trace );
/* angle attenuation */
angle = (light->flags & LIGHT_ATTEN_ANGLE)
? DotProduct( trace->normal, trace->direction )
: 1.0f;
/* twosided lighting */
if( trace->twoSided )
angle = fabs( angle );
/* attenuate */
add = light->photons * angle;
if( add <= 0.0f )
return 0;
/* setup trace */
trace->testAll = true;
VectorScale( light->color, add, trace->color );
/* trace to point */
if( trace->testOcclusion && !trace->forceSunlight )
{
/* trace */
TraceLine( trace );
if( !(trace->compileFlags & C_SKY) || trace->opaque )
{
VectorClear( trace->color );
return -1;
}
}
/* return to sender */
return 1;
}
/* ydnar: changed to a variable number */
if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )
return 0;
/* setup trace */
trace->testAll = false;
VectorScale( light->color, add, trace->color );
/* raytrace */
TraceLine( trace );
if( trace->passSolid || trace->opaque )
{
VectorClear( trace->color );
return -1;
}
/* return to sender */
return 1;
}
/*
LightingAtSample()
determines the amount of light reaching a sample (luxel or vertex)
*/
void LightingAtSample( light_trace_t *trace, byte styles[ MAX_LIGHTMAPS ], vec3_t colors[ MAX_LIGHTMAPS ] )
{
int i, lightmapNum;
/* clear colors */
for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
VectorClear( colors[ lightmapNum ] );
/* ydnar: normalmap */
if( normalmap )
{
colors[ 0 ][ 0 ] = (trace->normal[ 0 ] + 1.0f) * 127.5f;
colors[ 0 ][ 1 ] = (trace->normal[ 1 ] + 1.0f) * 127.5f;
colors[ 0 ][ 2 ] = (trace->normal[ 2 ] + 1.0f) * 127.5f;
return;
}
/* ydnar: don't bounce ambient all the time */
if( !bouncing )
VectorCopy( ambientColor, colors[ 0 ] );
/* ydnar: trace to all the list of lights pre-stored in tw */
for( i = 0; i < trace->numLights && trace->lights[ i ] != NULL; i++ )
{
/* set light */
trace->light = trace->lights[ i ];
/* style check */
for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
{
if( styles[ lightmapNum ] == trace->light->style ||
styles[ lightmapNum ] == LS_NONE )
break;
}
/* max of MAX_LIGHTMAPS (4) styles allowed to hit a sample */
if( lightmapNum >= MAX_LIGHTMAPS )
continue;
/* sample light */
LightContributionToSample( trace );
if( trace->color[ 0 ] == 0.0f && trace->color[ 1 ] == 0.0f && trace->color[ 2 ] == 0.0f )
continue;
/* handle negative light */
if( trace->light->flags & LIGHT_NEGATIVE )
VectorScale( trace->color, -1.0f, trace->color );
/* set style */
styles[ lightmapNum ] = trace->light->style;
/* add it */
VectorAdd( colors[ lightmapNum ], trace->color, colors[ lightmapNum ] );
/* cheap mode */
if( cheap &&
colors[ 0 ][ 0 ] >= 255.0f &&
colors[ 0 ][ 1 ] >= 255.0f &&
colors[ 0 ][ 2 ] >= 255.0f )
break;
}
}
/*
LightContributionToPoint()
for a given light, how much light/color reaches a given point in space (with no facing)
note: this is similar to LightContributionToSample() but optimized for omnidirectional sampling
*/
int LightContributionToPoint( light_trace_t *trace )
{
light_t *light;
float add, dist;
/* get light */
light = trace->light;
/* clear color */
VectorClear( trace->color );
/* ydnar: early out */
if( !(light->flags & LIGHT_GRID) || light->envelope <= 0.0f )
return false;
/* is this a sun? */
if( light->type != EMIT_SUN )
{
/* sun only? */
if( sunOnly )
return false;
/* test pvs */
if( !ClusterVisible( trace->cluster, light->cluster ) )
return false;
}
/* ydnar: check origin against light's pvs envelope */
if( trace->origin[ 0 ] > light->maxs[ 0 ] || trace->origin[ 0 ] < light->mins[ 0 ] ||
trace->origin[ 1 ] > light->maxs[ 1 ] || trace->origin[ 1 ] < light->mins[ 1 ] ||
trace->origin[ 2 ] > light->maxs[ 2 ] || trace->origin[ 2 ] < light->mins[ 2 ] )
{
gridBoundsCulled++;
return false;
}
/* set light origin */
if( light->type == EMIT_SUN )
VectorAdd( trace->origin, light->origin, trace->end );
else
VectorCopy( light->origin, trace->end );
/* set direction */
dist = SetupTrace( trace );
/* test envelope */
if( dist > light->envelope )
{
gridEnvelopeCulled++;
return false;
}
/* ptpff approximation */
if( light->type == EMIT_AREA && faster )
{
/* clamp the distance to prevent super hot spots */
if( dist < 16.0f )
dist = 16.0f;
/* attenuate */
add = light->photons / (dist * dist);
}
/* exact point to polygon form factor */
else if( light->type == EMIT_AREA )
{
float factor, d;
vec3_t pushedOrigin;
/* see if the point is behind the light */
d = DotProduct( trace->origin, light->normal ) - light->dist;
if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
return false;
/* nudge the point so that it is clearly forward of the light */
/* so that surfaces meeting a light emiter don't get black edges */
if( d > -8.0f && d < 8.0f )
VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );
else
VectorCopy( trace->origin, pushedOrigin );
/* calculate the contribution (ydnar 2002-10-21: [bug 642] bad normal calc) */
factor = PointToPolygonFormFactor( pushedOrigin, trace->direction, light->w );
if( factor == 0.0f )
return false;
else if( factor < 0.0f )
{
if( light->flags & LIGHT_TWOSIDED )
factor = -factor;
else
return false;
}
/* ydnar: moved to here */
add = factor * light->add;
}
/* point/spot lights */
else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )
{
/* clamp the distance to prevent super hot spots */
if( dist < 16.0f )
dist = 16.0f;
/* attenuate */
if( light->flags & LIGHT_ATTEN_LINEAR )
{
add = light->photons * linearScale - (dist * light->fade);
if( add < 0.0f )
add = 0.0f;
}
else
add = light->photons / (dist * dist);
/* handle spotlights */
if( light->type == EMIT_SPOT )
{
float distByNormal, radiusAtDist, sampleRadius;
vec3_t pointAtDist, distToSample;
/* do cone calculation */
distByNormal = -DotProduct( trace->displacement, light->normal );
if( distByNormal < 0.0f )
return false;
VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
radiusAtDist = light->radiusByDist * distByNormal;
VectorSubtract( trace->origin, pointAtDist, distToSample );
sampleRadius = VectorLength( distToSample );
/* outside the cone */
if( sampleRadius >= radiusAtDist )
return false;
/* attenuate */
if( sampleRadius > (radiusAtDist - 32.0f) )
add *= ((radiusAtDist - sampleRadius) / 32.0f);
}
}
/* ydnar: sunlight */
else if( light->type == EMIT_SUN )
{
/* attenuate */
add = light->photons;
if( add <= 0.0f )
return false;
/* setup trace */
trace->testAll = true;
VectorScale( light->color, add, trace->color );
/* trace to point */
if( trace->testOcclusion && !trace->forceSunlight )
{
/* trace */
TraceLine( trace );
if( !(trace->compileFlags & C_SKY) || trace->opaque )
{
VectorClear( trace->color );
return -1;
}
}
/* return to sender */
return true;
}
/* unknown light type */
else
return false;
/* ydnar: changed to a variable number */
if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )
return false;
/* setup trace */
trace->testAll = false;
VectorScale( light->color, add, trace->color );
/* trace */
TraceLine( trace );
if( trace->passSolid )
{
VectorClear( trace->color );
return false;
}
/* we have a valid sample */
return true;
}
/*
TraceGrid()
grid samples are for quickly determining the lighting
of dynamically placed entities in the world
*/
#define MAX_CONTRIBUTIONS 1024
typedef struct
{
vec3_t dir;
vec3_t color;
int style;
}
contribution_t;
void TraceGrid( int num )
{
int i, j, x, y, z, mod, step, numCon, numStyles;
float d;
vec3_t baseOrigin, cheapColor, color;
rawGridPoint_t *gp;
bspGridPoint_t *bgp;
contribution_t contributions[ MAX_CONTRIBUTIONS ];
light_trace_t trace;
/* get grid points */
gp = &rawGridPoints[ num ];
bgp = &bspGridPoints[ num ];
/* get grid origin */
mod = num;
z = mod / (gridBounds[ 0 ] * gridBounds[ 1 ]);
mod -= z * (gridBounds[ 0 ] * gridBounds[ 1 ]);
y = mod / gridBounds[ 0 ];
mod -= y * gridBounds[ 0 ];
x = mod;
trace.origin[ 0 ] = gridMins[ 0 ] + x * gridSize[ 0 ];
trace.origin[ 1 ] = gridMins[ 1 ] + y * gridSize[ 1 ];
trace.origin[ 2 ] = gridMins[ 2 ] + z * gridSize[ 2 ];
/* set inhibit sphere */
if( gridSize[ 0 ] > gridSize[ 1 ] && gridSize[ 0 ] > gridSize[ 2 ] )
trace.inhibitRadius = gridSize[ 0 ] * 0.5f;
else if( gridSize[ 1 ] > gridSize[ 0 ] && gridSize[ 1 ] > gridSize[ 2 ] )
trace.inhibitRadius = gridSize[ 1 ] * 0.5f;
else
trace.inhibitRadius = gridSize[ 2 ] * 0.5f;
/* find point cluster */
trace.cluster = ClusterForPointExt( trace.origin, GRID_EPSILON );
if( trace.cluster < 0 )
{
/* try to nudge the origin around to find a valid point */
VectorCopy( trace.origin, baseOrigin );
for( step = 9; step <= 18; step += 9 )
{
for( i = 0; i < 8; i++ )
{
VectorCopy( baseOrigin, trace.origin );
if( i & 1 )
trace.origin[ 0 ] += step;
else
trace.origin[ 0 ] -= step;
if( i & 2 )
trace.origin[ 1 ] += step;
else
trace.origin[ 1 ] -= step;
if( i & 4 )
trace.origin[ 2 ] += step;
else
trace.origin[ 2 ] -= step;
/* ydnar: changed to find cluster num */
trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );
if( trace.cluster >= 0 )
break;
}
if( i != 8 )
break;
}
/* can't find a valid point at all */
if( step > 18 )
return;
}
/* setup trace */
trace.testOcclusion = !noTrace;
trace.forceSunlight = false;
trace.recvShadows = WORLDSPAWN_RECV_SHADOWS;
trace.numSurfaces = 0;
trace.surfaces = NULL;
trace.numLights = 0;
trace.lights = NULL;
/* clear */
numCon = 0;
VectorClear( cheapColor );
/* trace to all the lights, find the major light direction, and divide the
total light between that along the direction and the remaining in the ambient */
for( trace.light = lights; trace.light != NULL; trace.light = trace.light->next )
{
float addSize;
/* sample light */
if( !LightContributionToPoint( &trace ) )
continue;
/* handle negative light */
if( trace.light->flags & LIGHT_NEGATIVE )
VectorScale( trace.color, -1.0f, trace.color );
/* add a contribution */
VectorCopy( trace.color, contributions[ numCon ].color );
VectorCopy( trace.direction, contributions[ numCon ].dir );
contributions[ numCon ].style = trace.light->style;
numCon++;
/* push average direction around */
addSize = VectorLength( trace.color );
VectorMA( gp->dir, addSize, trace.direction, gp->dir );
/* stop after a while */
if( numCon >= (MAX_CONTRIBUTIONS - 1) )
break;
/* ydnar: cheap mode */
VectorAdd( cheapColor, trace.color, cheapColor );
if( cheapgrid && cheapColor[ 0 ] >= 255.0f && cheapColor[ 1 ] >= 255.0f && cheapColor[ 2 ] >= 255.0f )
break;
}
/* normalize to get primary light direction */
VectorNormalize( gp->dir );
/* now that we have identified the primary light direction,
go back and separate all the light into directed and ambient */
numStyles = 1;
for( i = 0; i < numCon; i++ )
{
/* get relative directed strength */
d = DotProduct( contributions[ i ].dir, gp->dir );
if( d < 0.0f )
d = 0.0f;
/* find appropriate style */
for( j = 0; j < numStyles; j++ )
{
if( gp->styles[ j ] == contributions[ i ].style )
break;
}
/* style not found? */
if( j >= numStyles )
{
/* add a new style */
if( numStyles < MAX_LIGHTMAPS )
{
gp->styles[ numStyles ] = contributions[ i ].style;
bgp->styles[ numStyles ] = contributions[ i ].style;
numStyles++;
//% Msg( "(%d, %d) ", num, contributions[ i ].style );
}
/* fallback */
else
j = 0;
}
/* add the directed color */
VectorMA( gp->directed[ j ], d, contributions[ i ].color, gp->directed[ j ] );
/* ambient light will be at 1/4 the value of directed light */
/* (ydnar: nuke this in favor of more dramatic lighting?) */
d = 0.25f * (1.0f - d);
VectorMA( gp->ambient[ j ], d, contributions[ i ].color, gp->ambient[ j ] );
}
/* store off sample */
for( i = 0; i < MAX_LIGHTMAPS; i++ )
{
/* do some fudging to keep the ambient from being too low (2003-07-05: 0.25 -> 0.125) */
if( !bouncing )
VectorMA( gp->ambient[ i ], 0.125f, gp->directed[ i ], gp->ambient[ i ] );
/* set minimum light and copy off to bytes */
VectorCopy( gp->ambient[ i ], color );
for( j = 0; j < 3; j++ )
if( color[ j ] < minGridLight[ j ] )
color[ j ] = minGridLight[ j ];
ColorToBytes( color, bgp->ambient[ i ], 1.0f );
ColorToBytes( gp->directed[ i ], bgp->directed[ i ], 1.0f );
}
/* debug code */
#if 0
MsgDev( D_NOTE, "%9d Amb: (%03.1f %03.1f %03.1f) Dir: (%03.1f %03.1f %03.1f)\n", num,
gp->ambient[ 0 ][ 0 ], gp->ambient[ 0 ][ 1 ], gp->ambient[ 0 ][ 2 ],
gp->directed[ 0 ][ 0 ], gp->directed[ 0 ][ 1 ], gp->directed[ 0 ][ 2 ] );
#endif
/* store direction */
if( !bouncing ) NormalToLatLong( gp->dir, bgp->latLong );
}
/*
SetupGrid()
calculates the size of the lightgrid and allocates memory
*/
void SetupGrid( void )
{
int i, j;
vec3_t maxs, oldGridSize;
const char *value;
char temp[ 64 ];
/* don't do this if not grid lighting */
if( noGridLighting )
return;
/* ydnar: set grid size */
value = ValueForKey( &entities[ 0 ], "gridsize" );
if( value[ 0 ] != '\0' )
sscanf( value, "%f %f %f", &gridSize[ 0 ], &gridSize[ 1 ], &gridSize[ 2 ] );
/* quantize it */
VectorCopy( gridSize, oldGridSize );
for( i = 0; i < 3; i++ )
gridSize[ i ] = gridSize[ i ] >= 8.0f ? floor( gridSize[ i ] ) : 8.0f;
/* ydnar: increase gridSize until grid count is smaller than max allowed */
numRawGridPoints = MAX_MAP_LIGHTGRID + 1;
j = 0;
while( numRawGridPoints > MAX_MAP_LIGHTGRID )
{
/* get world bounds */
for( i = 0; i < 3; i++ )
{
gridMins[ i ] = gridSize[ i ] * ceil( bspModels[ 0 ].mins[ i ] / gridSize[ i ] );
maxs[ i ] = gridSize[ i ] * floor( bspModels[ 0 ].maxs[ i ] / gridSize[ i ] );
gridBounds[ i ] = (maxs[ i ] - gridMins[ i ]) / gridSize[ i ] + 1;
}
/* set grid size */
numRawGridPoints = gridBounds[ 0 ] * gridBounds[ 1 ] * gridBounds[ 2 ];
/* increase grid size a bit */
if( numRawGridPoints > MAX_MAP_LIGHTGRID )
gridSize[ j++ % 3 ] += 16.0f;
}
/* print it */
MsgDev( D_INFO, "Grid size = { %1.0f, %1.0f, %1.0f }\n", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
/* different? */
if( !VectorCompare( gridSize, oldGridSize ) )
{
sprintf( temp, "%.0f %.0f %.0f", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
SetKeyValue( &entities[ 0 ], "gridsize", (const char*) temp );
MsgDev( D_NOTE, "Storing adjusted grid size\n" );
}
/* 2nd variable. fixme: is this silly? */
numBSPGridPoints = numRawGridPoints;
/* allocate lightgrid */
rawGridPoints = Malloc( numRawGridPoints * sizeof( *rawGridPoints ) );
if( bspGridPoints != NULL ) Mem_Free( bspGridPoints );
bspGridPoints = Malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
/* clear lightgrid */
for( i = 0; i < numRawGridPoints; i++ )
{
VectorCopy( ambientColor, rawGridPoints[ i ].ambient[ j ] );
rawGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
bspGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
for( j = 1; j < MAX_LIGHTMAPS; j++ )
{
rawGridPoints[ i ].styles[ j ] = LS_NONE;
bspGridPoints[ i ].styles[ j ] = LS_NONE;
}
}
/* note it */
MsgDev( D_INFO, "%9d grid points\n", numRawGridPoints );
}
/*
LightWorld()
does what it says...
*/
void LightWorld( void )
{
vec3_t color;
float f;
int b, bt;
bool minVertex, minGrid;
const char *value;
/* ydnar: smooth normals */
if( shade )
{
MsgDev( D_NOTE, "--- SmoothNormals ---\n" );
SmoothNormals();
}
/* determine the number of grid points */
MsgDev( D_NOTE, "--- SetupGrid ---\n" );
SetupGrid();
/* find the optional minimum lighting values */
GetVectorForKey( &entities[ 0 ], "_color", color );
if( VectorLength( color ) == 0.0f ) VectorSet( color, 1.0f, 1.0f, 1.0f );
/* ambient */
f = FloatForKey( &entities[ 0 ], "_ambient" );
if( f == 0.0f )
f = FloatForKey( &entities[0], "ambient" );
VectorScale( color, f, ambientColor );
/* minvertexlight */
minVertex = false;
value = ValueForKey( &entities[ 0 ], "_minvertexlight" );
if( value[ 0 ] != '\0' )
{
minVertex = true;
f = atof( value );
VectorScale( color, f, minVertexLight );
}
/* mingridlight */
minGrid = false;
value = ValueForKey( &entities[ 0 ], "_mingridlight" );
if( value[ 0 ] != '\0' )
{
minGrid = true;
f = atof( value );
VectorScale( color, f, minGridLight );
}
/* minlight */
value = ValueForKey( &entities[ 0 ], "_minlight" );
if( value[ 0 ] != '\0' )
{
f = atof( value );
VectorScale( color, f, minLight );
if( minVertex == false )
VectorScale( color, f, minVertexLight );
if( minGrid == false )
VectorScale( color, f, minGridLight );
}
/* create world lights */
MsgDev( D_NOTE, "--- CreateLights ---\n" );
CreateEntityLights();
CreateSurfaceLights();
MsgDev( D_INFO, "%9d point lights\n", numPointLights );
MsgDev( D_INFO, "%9d spotlights\n", numSpotLights );
MsgDev( D_INFO, "%9d diffuse (area) lights\n", numDiffuseLights );
MsgDev( D_INFO, "%9d sun/sky lights\n", numSunLights );
/* calculate lightgrid */
if( !noGridLighting )
{
/* ydnar: set up light envelopes */
SetupEnvelopes( true, fastgrid );
MsgDev( D_NOTE, "--- TraceGrid ---\n" );
RunThreadsOnIndividual( numRawGridPoints, true, TraceGrid );
MsgDev( D_INFO, "%d x %d x %d = %d grid\n", gridBounds[0], gridBounds[1], gridBounds[2], numBSPGridPoints );
/* ydnar: emit statistics on light culling */
MsgDev( D_INFO, "%9d grid points envelope culled\n", gridEnvelopeCulled );
MsgDev( D_INFO, "%9d grid points bounds culled\n", gridBoundsCulled );
}
/* slight optimization to remove a sqrt */
subdivideThreshold *= subdivideThreshold;
/* map the world luxels */
MsgDev( D_NOTE, "--- MapRawLightmap ---\n" );
RunThreadsOnIndividual( numRawLightmaps, true, MapRawLightmap );
MsgDev( D_INFO, "%9d luxels\n", numLuxels );
MsgDev( D_INFO, "%9d luxels mapped\n", numLuxelsMapped );
MsgDev( D_INFO, "%9d luxels occluded\n", numLuxelsOccluded );
// dirty them up
if( dirty )
{
MsgDev( D_NOTE, "--- DirtyRawLightmap ---\n" );
RunThreadsOnIndividual( numRawLightmaps, true, DirtyRawLightmap );
}
// ydnar: set up light envelopes
SetupEnvelopes( false, fast );
/* light up my world */
lightsPlaneCulled = 0;
lightsEnvelopeCulled = 0;
lightsBoundsCulled = 0;
lightsClusterCulled = 0;
MsgDev( D_NOTE, "--- IlluminateRawLightmap ---\n" );
RunThreadsOnIndividual( numRawLightmaps, true, IlluminateRawLightmap );
MsgDev( D_INFO, "%9d luxels illuminated\n", numLuxelsIlluminated );
StitchSurfaceLightmaps();
MsgDev( D_NOTE, "--- IlluminateVertexes ---\n" );
RunThreadsOnIndividual( numBSPDrawSurfaces, true, IlluminateVertexes );
MsgDev( D_INFO, "%9d vertexes illuminated\n", numVertsIlluminated );
/* ydnar: emit statistics on light culling */
MsgDev( D_INFO, "%9d lights plane culled\n", lightsPlaneCulled );
MsgDev( D_INFO, "%9d lights envelope culled\n", lightsEnvelopeCulled );
MsgDev( D_INFO, "%9d lights bounds culled\n", lightsBoundsCulled );
MsgDev( D_INFO, "%9d lights cluster culled\n", lightsClusterCulled );
// radiosity
b = 1;
bt = bounce;
while( bounce > 0 )
{
/* store off the bsp between bounces */
StoreSurfaceLightmaps();
MsgDev( D_INFO, "Writing %s\n", source );
WriteBSPFile( source );
/* note it */
MsgDev( D_INFO, "\n--- Radiosity (bounce %d of %d) ---\n", b, bt );
/* flag bouncing */
bouncing = true;
VectorClear( ambientColor );
/* generate diffuse lights */
RadFreeLights();
RadCreateDiffuseLights();
/* setup light envelopes */
SetupEnvelopes( false, fastbounce );
if( numLights == 0 )
{
Msg( "No diffuse light to calculate, ending radiosity.\n" );
break;
}
/* add to lightgrid */
if( bouncegrid )
{
gridEnvelopeCulled = 0;
gridBoundsCulled = 0;
MsgDev( D_NOTE, "--- BounceGrid ---\n" );
RunThreadsOnIndividual( numRawGridPoints, true, TraceGrid );
MsgDev( D_INFO, "%9d grid points envelope culled\n", gridEnvelopeCulled );
MsgDev( D_INFO, "%9d grid points bounds culled\n", gridBoundsCulled );
}
// light up my world
lightsPlaneCulled = 0;
lightsEnvelopeCulled = 0;
lightsBoundsCulled = 0;
lightsClusterCulled = 0;
MsgDev( D_NOTE, "--- IlluminateRawLightmap ---\n" );
RunThreadsOnIndividual( numRawLightmaps, true, IlluminateRawLightmap );
MsgDev( D_INFO, "%9d luxels illuminated\n", numLuxelsIlluminated );
MsgDev( D_INFO, "%9d vertexes illuminated\n", numVertsIlluminated );
StitchSurfaceLightmaps();
MsgDev( D_NOTE, "--- IlluminateVertexes ---\n" );
RunThreadsOnIndividual( numBSPDrawSurfaces, true, IlluminateVertexes );
MsgDev( D_INFO, "%9d vertexes illuminated\n", numVertsIlluminated );
/* ydnar: emit statistics on light culling */
MsgDev( D_INFO, "%9d lights plane culled\n", lightsPlaneCulled );
MsgDev( D_INFO, "%9d lights envelope culled\n", lightsEnvelopeCulled );
MsgDev( D_INFO, "%9d lights bounds culled\n", lightsBoundsCulled );
MsgDev( D_INFO, "%9d lights cluster culled\n", lightsClusterCulled );
bounce--;
b++;
}
}
/*
LightMain()
main routine for light processing
*/
int LightMain( int argc, char **argv )
{
int i;
float f;
char mapSource[ 1024 ];
const char *value;
enable_log = true;
Msg( "--- Light ---\n" );
/* set standard game flags */
wolfLight = game->wolfLight;
lmCustomSize = game->lightmapSize;
lightmapGamma = game->lightmapGamma;
lightmapCompensate = game->lightmapCompensate;
/* process commandline arguments */
for( i = 1; i < (argc - 1); i++ )
{
/* lightsource scaling */
if( !strcmp( argv[ i ], "-point" ) || !strcmp( argv[ i ], "-pointscale" ) )
{
f = atof( argv[ i + 1 ] );
pointScale *= f;
MsgDev( D_INFO, "Point (entity) light scaled by %f to %f\n", f, pointScale );
i++;
}
else if( !strcmp( argv[ i ], "-area" ) || !strcmp( argv[ i ], "-areascale" ) )
{
f = atof( argv[ i + 1 ] );
areaScale *= f;
MsgDev( D_INFO, "Area (shader) light scaled by %f to %f\n", f, areaScale );
i++;
}
else if( !strcmp( argv[ i ], "-sky" ) || !strcmp( argv[ i ], "-skyscale" ) )
{
f = atof( argv[ i + 1 ] );
skyScale *= f;
MsgDev( D_INFO, "Sky/sun light scaled by %f to %f\n", f, skyScale );
i++;
}
else if( !strcmp( argv[ i ], "-bouncescale" ) )
{
f = atof( argv[ i + 1 ] );
bounceScale *= f;
MsgDev( D_INFO, "Bounce (radiosity) light scaled by %f to %f\n", f, bounceScale );
i++;
}
else if( !strcmp( argv[ i ], "-scale" ) )
{
f = atof( argv[ i + 1 ] );
pointScale *= f;
areaScale *= f;
skyScale *= f;
bounceScale *= f;
MsgDev( D_INFO, "All light scaled by %f\n", f );
i++;
}
else if( !strcmp( argv[ i ], "-gamma" ) )
{
f = atof( argv[ i + 1 ] );
lightmapGamma = f;
MsgDev( D_INFO, "Lighting gamma set to %f\n", lightmapGamma );
i++;
}
else if( !strcmp( argv[ i ], "-compensate" ) )
{
f = atof( argv[ i + 1 ] );
if( f <= 0.0f )
f = 1.0f;
lightmapCompensate = f;
MsgDev( D_INFO, "Lighting compensation set to 1/%f\n", lightmapCompensate );
i++;
}
/* ydnar switches */
else if( !strcmp( argv[ i ], "-bounce" ) )
{
bounce = atoi( argv[ i + 1 ] );
if( bounce < 0 )
bounce = 0;
else if( bounce > 0 )
MsgDev( D_INFO, "Radiosity enabled with %d bounce(s)\n", bounce );
i++;
}
else if( !strcmp( argv[ i ], "-supersample" ) || !strcmp( argv[ i ], "-super" ) )
{
superSample = atoi( argv[ i + 1 ] );
if( superSample < 1 )
superSample = 1;
else if( superSample > 1 )
MsgDev( D_INFO, "Ordered-grid supersampling enabled with %d sample(s) per lightmap texel\n", (superSample * superSample) );
i++;
}
else if( !strcmp( argv[ i ], "-samples" ) )
{
lightSamples = atoi( argv[ i + 1 ] );
if( lightSamples < 1 )
lightSamples = 1;
else if( lightSamples > 1 )
MsgDev( D_INFO, "Adaptive supersampling enabled with %d sample(s) per lightmap texel\n", lightSamples );
i++;
}
else if( !strcmp( argv[ i ], "-filter" ) )
{
filter = true;
MsgDev( D_INFO, "Lightmap filtering enabled\n" );
}
else if( !strcmp( argv[ i ], "-dark" ) )
{
dark = true;
MsgDev( D_INFO, "Dark lightmap seams enabled\n" );
}
else if( !strcmp( argv[ i ], "-shadeangle" ) )
{
shadeAngleDegrees = atof( argv[ i + 1 ] );
if( shadeAngleDegrees < 0.0f )
shadeAngleDegrees = 0.0f;
else if( shadeAngleDegrees > 0.0f )
{
shade = true;
MsgDev( D_INFO, "Phong shading enabled with a breaking angle of %f degrees\n", shadeAngleDegrees );
}
i++;
}
else if( !strcmp( argv[ i ], "-thresh" ) )
{
subdivideThreshold = atof( argv[ i + 1 ] );
if( subdivideThreshold < 0 )
subdivideThreshold = DEFAULT_SUBDIVIDE_THRESHOLD;
else
MsgDev( D_INFO, "Subdivision threshold set at %.3f\n", subdivideThreshold );
i++;
}
else if( !strcmp( argv[ i ], "-approx" ) )
{
approximateTolerance = atoi( argv[ i + 1 ] );
if( approximateTolerance < 0 )
approximateTolerance = 0;
else if( approximateTolerance > 0 )
MsgDev( D_INFO, "Approximating lightmaps within a byte tolerance of %d\n", approximateTolerance );
i++;
}
else if( !strcmp( argv[ i ], "-deluxe" ) || !strcmp( argv[ i ], "-deluxemap" ) )
{
deluxemap = true;
MsgDev( D_INFO, "Generating deluxemaps for average light direction\n" );
}
else if( !strcmp( argv[ i ], "-external" ) )
{
externalLightmaps = true;
MsgDev( D_INFO, "Storing all lightmaps externally\n" );
}
else if( !strcmp( argv[ i ], "-lightmapsize" ) )
{
lmCustomSize = atoi( argv[ i + 1 ] );
/* must be a power of 2 and greater than 2 */
if( ((lmCustomSize - 1) & lmCustomSize) || lmCustomSize < 2 )
{
MsgDev( D_WARN, "Lightmap size must be a power of 2, greater or equal to 2 pixels.\n" );
lmCustomSize = game->lightmapSize;
}
i++;
MsgDev( D_INFO, "Default lightmap size set to %d x %d pixels\n", lmCustomSize, lmCustomSize );
/* enable external lightmaps */
if( lmCustomSize != game->lightmapSize )
{
externalLightmaps = true;
Msg( "Storing all lightmaps externally\n" );
}
}
/* ydnar: add this to suppress warnings */
else if( !strcmp( argv[ i ], "-custinfoparms") )
{
MsgDev( D_INFO, "Custom info parms enabled\n" );
useCustomInfoParms = true;
}
else if( !strcmp( argv[ i ], "-wolf" ) )
{
/* -game should already be set */
wolfLight = true;
MsgDev( D_INFO, "Enabling Wolf lighting model (linear default)\n" );
}
else if( !strcmp( argv[ i ], "-q3" ) )
{
/* -game should already be set */
wolfLight = false;
MsgDev( D_INFO, "Enabling Quake 3 lighting model (nonlinear default)\n" );
}
else if( !strcmp( argv[ i ], "-sunonly" ) )
{
sunOnly = true;
MsgDev( D_INFO, "Only computing sunlight\n" );
}
else if( !strcmp( argv[ i ], "-bounceonly" ) )
{
bounceOnly = true;
MsgDev( D_INFO, "Storing bounced light (radiosity) only\n" );
}
else if( !strcmp( argv[ i ], "-nocollapse" ) )
{
noCollapse = true;
MsgDev( D_INFO, "Identical lightmap collapsing disabled\n" );
}
else if( !strcmp( argv[ i ], "-shade" ) )
{
shade = true;
MsgDev( D_INFO, "Phong shading enabled\n" );
}
else if( !strcmp( argv[ i ], "-bouncegrid") )
{
bouncegrid = true;
if( bounce > 0 ) MsgDev( D_INFO, "Grid lighting with radiosity enabled\n" );
}
else if( !strcmp( argv[ i ], "-smooth" ) )
{
lightSamples = EXTRA_SCALE;
MsgDev( D_INFO, "The -smooth argument is deprecated, use \"-samples 2\" instead\n" );
}
else if( !strcmp( argv[ i ], "-fast" ) )
{
fast = true;
fastgrid = true;
fastbounce = true;
MsgDev( D_INFO, "Fast mode enabled\n" );
}
else if( !strcmp( argv[ i ], "-faster" ) )
{
faster = true;
fast = true;
fastgrid = true;
fastbounce = true;
MsgDev( D_INFO, "Faster mode enabled\n" );
}
else if( !strcmp( argv[ i ], "-fastgrid" ) )
{
fastgrid = true;
MsgDev( D_INFO, "Fast grid lighting enabled\n" );
}
else if( !strcmp( argv[ i ], "-fastbounce" ) )
{
fastbounce = true;
MsgDev( D_INFO, "Fast bounce mode enabled\n" );
}
else if( !strcmp( argv[ i ], "-cheap" ) )
{
cheap = true;
cheapgrid = true;
MsgDev( D_INFO, "Cheap mode enabled\n" );
}
else if( !strcmp( argv[ i ], "-cheapgrid" ) )
{
cheapgrid = true;
MsgDev( D_INFO, "Cheap grid mode enabled\n" );
}
else if( !strcmp( argv[ i ], "-normalmap" ) )
{
normalmap = true;
MsgDev( D_INFO, "Storing normal map instead of lightmap\n" );
}
else if( !strcmp( argv[ i ], "-trisoup" ) )
{
trisoup = true;
MsgDev( D_INFO, "Converting brush faces to triangle soup\n" );
}
else if( !strcmp( argv[ i ], "-debug" ) )
{
debug = true;
MsgDev( D_INFO, "Lightmap debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-debugsurfaces" ) || !strcmp( argv[ i ], "-debugsurface" ) )
{
debugSurfaces = true;
MsgDev( D_INFO, "Lightmap surface debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-debugunused" ) )
{
debugUnused = true;
MsgDev( D_INFO, "Unused luxel debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-debugaxis" ) )
{
debugAxis = true;
MsgDev( D_INFO, "Lightmap axis debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-debugcluster" ) )
{
debugCluster = true;
MsgDev( D_INFO, "Luxel cluster debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-debugorigin" ) )
{
debugOrigin = true;
MsgDev( D_INFO, "Luxel origin debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-debugdeluxe" ) )
{
deluxemap = true;
debugDeluxemap = true;
MsgDev( D_INFO, "Deluxemap debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-export" ) )
{
exportLightmaps = true;
MsgDev( D_INFO, "Exporting lightmaps\n" );
}
else if( !strcmp(argv[ i ], "-notrace" ))
{
noTrace = true;
MsgDev( D_INFO, "Shadow occlusion disabled\n" );
}
else if( !strcmp(argv[ i ], "-patchshadows" ) )
{
patchShadows = true;
MsgDev( D_INFO, "Patch shadow casting enabled\n" );
}
else if( !strcmp( argv[ i ], "-extra" ) )
{
superSample = EXTRA_SCALE; /* ydnar */
MsgDev( D_INFO, "The -extra argument is deprecated, use \"-super 2\" instead\n" );
}
else if( !strcmp( argv[ i ], "-extrawide" ) )
{
superSample = EXTRAWIDE_SCALE; /* ydnar */
filter = true; /* ydnar */
MsgDev( D_INFO, "The -extrawide argument is deprecated, use \"-filter [-super 2]\" instead\n");
}
else if( !strcmp( argv[ i ], "-samplesize" ) )
{
sampleSize = atoi( argv[ i + 1 ] );
if( sampleSize < 1 )
sampleSize = 1;
i++;
MsgDev( D_INFO, "Default lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
}
else if( !strcmp( argv[ i ], "-novertex" ) )
{
noVertexLighting = true;
MsgDev( D_INFO, "Disabling vertex lighting\n" );
}
else if( !strcmp( argv[ i ], "-nogrid" ) )
{
noGridLighting = true;
MsgDev( D_INFO, "Disabling grid lighting\n" );
}
else if( !strcmp( argv[ i ], "-border" ) )
{
lightmapBorder = true;
MsgDev( D_INFO, "Adding debug border to lightmaps\n" );
}
else if( !strcmp( argv[ i ], "-nosurf" ) )
{
noSurfaces = true;
MsgDev( D_INFO, "Not tracing against surfaces\n" );
}
else if( !strcmp( argv[ i ], "-dump" ) )
{
dump = true;
MsgDev( D_INFO, "Dumping radiosity lights into numbered prefabs\n" );
}
else if( !strcmp( argv[ i ], "-lomem" ) )
{
loMem = true;
MsgDev( D_INFO, "Enabling low-memory (potentially slower) lighting mode\n" );
}
else if( !strcmp( argv[ i ], "-nostyle" ) || !strcmp( argv[ i ], "-nostyles" ) )
{
noStyles = true;
MsgDev( D_INFO, "Disabling lightstyles\n" );
}
else if( !strcmp( argv[ i ], "-cpma" ) )
{
cpmaHack = true;
MsgDev( D_INFO, "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
}
/* r7: dirtmapping */
else if( !strcmp( argv[ i ], "-dirty" ) )
{
dirty = true;
MsgDev( D_INFO, "Dirtmapping enabled\n" );
}
else if( !strcmp( argv[ i ], "-dirtdebug" ) || !strcmp( argv[ i ], "-debugdirt" ) )
{
dirtDebug = true;
MsgDev( D_INFO, "Dirtmap debugging enabled\n" );
}
else if( !strcmp( argv[ i ], "-dirtmode" ) )
{
dirtMode = atoi( argv[ i + 1 ] );
if( dirtMode != 0 && dirtMode != 1 ) dirtMode = 0;
if( dirtMode == 1 ) MsgDev( D_INFO, "Enabling randomized dirtmapping\n" );
else MsgDev( D_INFO, "Enabling ordered dir mapping\n" );
}
else if( !strcmp( argv[ i ], "-dirtdepth" ) )
{
dirtDepth = atof( argv[ i + 1 ] );
if( dirtDepth <= 0.0f )
dirtDepth = 128.0f;
MsgDev( D_INFO, "Dirtmapping depth set to %.1f\n", dirtDepth );
}
else if( !strcmp( argv[ i ], "-dirtscale" ) )
{
dirtScale = atof( argv[ i + 1 ] );
if( dirtScale <= 0.0f ) dirtScale = 1.0f;
MsgDev( D_INFO, "Dirtmapping scale set to %.1f\n", dirtScale );
}
else if( !strcmp( argv[ i ], "-dirtgain" ) )
{
dirtGain = atof( argv[ i + 1 ] );
if( dirtGain <= 0.0f ) dirtGain = 1.0f;
MsgDev( D_INFO, "Dirtmapping gain set to %.1f\n", dirtGain );
}
/* unhandled args */
else MsgDev( D_WARN, "unknown argument \"%s\"\n", argv[i] );
}
/* clean up map name */
com.sprintf( source, "maps/%s", gs_filename );
FS_StripExtension( source );
FS_DefaultExtension( source, ".bsp" );
com.sprintf( mapSource, "maps/%s", gs_filename );
FS_StripExtension( mapSource );
FS_DefaultExtension( mapSource, ".map" );
/* ydnar: set default sample size */
SetDefaultSampleSize( sampleSize );
/* ydnar: handle shaders */
BeginMapShaderFile( source );
LoadShaderInfo();
/* note loading */
MsgDev( D_NOTE, "Loading %s\n", source );
/* ydnar: load surface file */
LoadSurfaceExtraFile( source );
/* load bsp file */
LoadBSPFile( source );
/* parse bsp entities */
ParseEntities();
/* load map file */
value = ValueForKey( &entities[ 0 ], "_keepLights" );
if( value[ 0 ] != '1' )
LoadMapFile( mapSource, true );
/* set the entity/model origins and init yDrawVerts */
SetEntityOrigins();
/* ydnar: set up optimization */
SetupBrushes();
SetupDirt();
SetupSurfaceLightmaps();
/* initialize the surface facet tracing */
SetupTraceNodes();
/* light the world */
LightWorld();
/* ydnar: store off lightmaps */
StoreSurfaceLightmaps();
/* write out the bsp */
UnparseEntities();
Msg( "Writing %s\n", source );
WriteBSPFile( source );
/* ydnar: export lightmaps */
if( exportLightmaps && !externalLightmaps )
ExportLightmaps();
/* return to sender */
return 0;
}