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