2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
21 ----------------------------------------------------------------------------------
\r
23 This code has been altered significantly from its original form, to support
\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
\r
26 ------------------------------------------------------------------------------- */
\r
41 CreateSunLight() - ydnar
\r
42 this creates a sun light
\r
45 static void CreateSunLight( sun_t *sun )
\r
48 float photons, d, angle, elevation, da, de;
\r
58 if( sun->numSamples < 1 )
\r
59 sun->numSamples = 1;
\r
62 photons = sun->photons / sun->numSamples;
\r
64 /* create the right number of suns */
\r
65 for( i = 0; i < sun->numSamples; i++ )
\r
67 /* calculate sun direction */
\r
69 VectorCopy( sun->direction, direction );
\r
73 sun->direction[ 0 ] = cos( angle ) * cos( elevation );
\r
74 sun->direction[ 1 ] = sin( angle ) * cos( elevation );
\r
75 sun->direction[ 2 ] = sin( elevation );
\r
77 xz_dist = sqrt( x*x + z*z )
\r
78 latitude = atan2( xz_dist, y ) * RADIANS
\r
79 longitude = atan2( x, z ) * RADIANS
\r
82 d = sqrt( sun->direction[ 0 ] * sun->direction[ 0 ] + sun->direction[ 1 ] * sun->direction[ 1 ] );
\r
83 angle = atan2( sun->direction[ 1 ], sun->direction[ 0 ] );
\r
84 elevation = atan2( sun->direction[ 2 ], d );
\r
86 /* jitter the angles (loop to keep random sample within sun->deviance steridians) */
\r
89 da = (Random() * 2.0f - 1.0f) * sun->deviance;
\r
90 de = (Random() * 2.0f - 1.0f) * sun->deviance;
\r
92 while( (da * da + de * de) > (sun->deviance * sun->deviance) );
\r
97 //% Sys_Printf( "%d: Angle: %3.4f Elevation: %3.3f\n", sun->numSamples, (angle / Q_PI * 180.0f), (elevation / Q_PI * 180.0f) );
\r
99 /* create new vector */
\r
100 direction[ 0 ] = cos( angle ) * cos( elevation );
\r
101 direction[ 1 ] = sin( angle ) * cos( elevation );
\r
102 direction[ 2 ] = sin( elevation );
\r
105 /* create a light */
\r
107 light = safe_malloc( sizeof( *light ) );
\r
108 memset( light, 0, sizeof( *light ) );
\r
109 light->next = lights;
\r
112 /* initialize the light */
\r
113 light->flags = LIGHT_SUN_DEFAULT;
\r
114 light->type = EMIT_SUN;
\r
115 light->fade = 1.0f;
\r
116 light->falloffTolerance = falloffTolerance;
\r
117 light->filterRadius = sun->filterRadius / sun->numSamples;
\r
119 /* set the light's position out to infinity */
\r
120 VectorMA( vec3_origin, (MAX_WORLD_COORD * 8.0f), direction, light->origin ); /* MAX_WORLD_COORD * 2.0f */
\r
122 /* set the facing to be the inverse of the sun direction */
\r
123 VectorScale( direction, -1.0, light->normal );
\r
124 light->dist = DotProduct( light->origin, light->normal );
\r
126 /* set color and photons */
\r
127 VectorCopy( sun->color, light->color );
\r
128 light->photons = photons * skyScale;
\r
132 if( sun->next != NULL )
\r
133 CreateSunLight( sun->next );
\r
139 CreateSkyLights() - ydnar
\r
140 simulates sky light with multiple suns
\r
143 static void CreateSkyLights( vec3_t color, float value, int iterations, float filterRadius )
\r
145 int c, i, j, k, numSuns;
\r
152 if( value <= 0.0f || iterations < 2 )
\r
155 /* calculate some stuff */
\r
156 step = 2.0f / (iterations - 1);
\r
159 /* basic sun setup */
\r
160 VectorCopy( color, sun.color );
\r
161 sun.deviance = 0.0f;
\r
162 sun.filterRadius = filterRadius;
\r
163 sun.numSamples = 1;
\r
168 for( c = 0; c < 2; c++ )
\r
170 for( k = 0, in[ 2 ] = start; k < iterations; k++, in[ 2 ] += step )
\r
172 /* don't create sky light below the horizon */
\r
173 if( in[ 2 ] <= 0.0f )
\r
176 for( j = 0, in[ 1 ] = start; j < iterations; j++, in[ 1 ] += step )
\r
178 for( i = 0, in[ 0 ] = start; i < iterations; i++, in[ 0 ] += step )
\r
180 if( VectorNormalize( in, sun.direction ) )
\r
182 if( c > 0 && numSuns > 0 )
\r
184 sun.photons = value / numSuns;
\r
185 CreateSunLight( &sun );
\r
199 CreateEntityLights()
\r
200 creates lights from light entities
\r
203 void CreateEntityLights( void )
\r
206 light_t *light, *light2;
\r
209 const char *target;
\r
211 const char *_color;
\r
212 float intensity, scale, deviance, filterRadius;
\r
213 int spawnflags, flags, numSamples;
\r
217 /* go throught entity list and find lights */
\r
218 for( i = 0; i < numEntities; i++ )
\r
221 e = &entities[ i ];
\r
222 name = ValueForKey( e, "classname" );
\r
224 /* ydnar: check for lightJunior */
\r
225 if( Q_strncasecmp( name, "lightJunior", 11 ) == 0 )
\r
227 else if( Q_strncasecmp( name, "light", 5 ) == 0 )
\r
232 /* lights with target names (and therefore styles) are only parsed from BSP */
\r
233 target = ValueForKey( e, "targetname" );
\r
234 if( target[ 0 ] != '\0' && i >= numBSPEntities )
\r
237 /* create a light */
\r
239 light = safe_malloc( sizeof( *light ) );
\r
240 memset( light, 0, sizeof( *light ) );
\r
241 light->next = lights;
\r
244 /* handle spawnflags */
\r
245 spawnflags = IntForKey( e, "spawnflags" );
\r
247 /* ydnar: quake 3+ light behavior */
\r
248 if( game->wolfLight == qfalse )
\r
250 /* set default flags */
\r
251 flags = LIGHT_Q3A_DEFAULT;
\r
253 /* linear attenuation? */
\r
254 if( spawnflags & 1 )
\r
256 flags |= LIGHT_ATTEN_LINEAR;
\r
257 flags &= ~LIGHT_ATTEN_ANGLE;
\r
260 /* no angle attenuate? */
\r
261 if( spawnflags & 2 )
\r
262 flags &= ~LIGHT_ATTEN_ANGLE;
\r
265 /* ydnar: wolf light behavior */
\r
268 /* set default flags */
\r
269 flags = LIGHT_WOLF_DEFAULT;
\r
271 /* inverse distance squared attenuation? */
\r
272 if( spawnflags & 1 )
\r
274 flags &= ~LIGHT_ATTEN_LINEAR;
\r
275 flags |= LIGHT_ATTEN_ANGLE;
\r
278 /* angle attenuate? */
\r
279 if( spawnflags & 2 )
\r
280 flags |= LIGHT_ATTEN_ANGLE;
\r
283 /* other flags (borrowed from wolf) */
\r
285 /* wolf dark light? */
\r
286 if( (spawnflags & 4) || (spawnflags & 8) )
\r
287 flags |= LIGHT_DARK;
\r
290 if( spawnflags & 16 )
\r
291 flags &= ~LIGHT_GRID;
\r
296 flags |= LIGHT_GRID;
\r
297 flags &= ~LIGHT_SURFACES;
\r
300 /* store the flags */
\r
301 light->flags = flags;
\r
303 /* ydnar: set fade key (from wolf) */
\r
304 light->fade = 1.0f;
\r
305 if( light->flags & LIGHT_ATTEN_LINEAR )
\r
307 light->fade = FloatForKey( e, "fade" );
\r
308 if( light->fade == 0.0f )
\r
309 light->fade = 1.0f;
\r
312 /* ydnar: set angle scaling (from vlight) */
\r
313 light->angleScale = FloatForKey( e, "_anglescale" );
\r
314 if( light->angleScale != 0.0f )
\r
315 light->flags |= LIGHT_ATTEN_ANGLE;
\r
318 GetVectorForKey( e, "origin", light->origin);
\r
319 light->style = IntForKey( e, "_style" );
\r
320 if( light->style == 0 )
\r
321 light->style = IntForKey( e, "style" );
\r
322 if( light->style < LS_NORMAL || light->style >= LS_NONE )
\r
323 Error( "Invalid lightstyle (%d) on entity %d", light->style, i );
\r
325 /* set light intensity */
\r
326 intensity = FloatForKey( e, "_light" );
\r
327 if( intensity == 0.0f )
\r
328 intensity = FloatForKey( e, "light" );
\r
329 if( intensity == 0.0f)
\r
330 intensity = 300.0f;
\r
332 /* ydnar: set light scale (sof2) */
\r
333 scale = FloatForKey( e, "scale" );
\r
334 if( scale == 0.0f )
\r
336 intensity *= scale;
\r
338 /* ydnar: get deviance and samples */
\r
339 deviance = FloatForKey( e, "_deviance" );
\r
340 if( deviance == 0.0f )
\r
341 deviance = FloatForKey( e, "_deviation" );
\r
342 if( deviance == 0.0f )
\r
343 deviance = FloatForKey( e, "_jitter" );
\r
344 numSamples = IntForKey( e, "_samples" );
\r
345 if( deviance < 0.0f || numSamples < 1 )
\r
350 intensity /= numSamples;
\r
352 /* ydnar: get filter radius */
\r
353 filterRadius = FloatForKey( e, "_filterradius" );
\r
354 if( filterRadius == 0.0f )
\r
355 filterRadius = FloatForKey( e, "_filteradius" );
\r
356 if( filterRadius == 0.0f )
\r
357 filterRadius = FloatForKey( e, "_filter" );
\r
358 if( filterRadius < 0.0f )
\r
359 filterRadius = 0.0f;
\r
360 light->filterRadius = filterRadius;
\r
362 /* set light color */
\r
363 _color = ValueForKey( e, "_color" );
\r
364 if( _color && _color[ 0 ] )
\r
366 sscanf( _color, "%f %f %f", &light->color[ 0 ], &light->color[ 1 ], &light->color[ 2 ] );
\r
367 ColorNormalize( light->color, light->color );
\r
370 light->color[ 0 ] = light->color[ 1 ] = light->color[ 2 ] = 1.0f;
\r
372 intensity = intensity * pointScale;
\r
373 light->photons = intensity;
\r
375 light->type = EMIT_POINT;
\r
377 /* set falloff threshold */
\r
378 light->falloffTolerance = falloffTolerance / numSamples;
\r
380 /* lights with a target will be spotlights */
\r
381 target = ValueForKey( e, "target" );
\r
391 e2 = FindTargetEntity( target );
\r
394 Sys_Printf( "WARNING: light at (%i %i %i) has missing target\n",
\r
395 (int) light->origin[ 0 ], (int) light->origin[ 1 ], (int) light->origin[ 2 ] );
\r
399 /* not a point light */
\r
403 /* make a spotlight */
\r
404 GetVectorForKey( e2, "origin", dest );
\r
405 VectorSubtract( dest, light->origin, light->normal );
\r
406 dist = VectorNormalize( light->normal, light->normal );
\r
407 radius = FloatForKey( e, "radius" );
\r
412 light->radiusByDist = (radius + 16) / dist;
\r
413 light->type = EMIT_SPOT;
\r
415 /* ydnar: wolf mods: spotlights always use nonlinear + angle attenuation */
\r
416 light->flags &= ~LIGHT_ATTEN_LINEAR;
\r
417 light->flags |= LIGHT_ATTEN_ANGLE;
\r
418 light->fade = 1.0f;
\r
420 /* ydnar: is this a sun? */
\r
421 _sun = ValueForKey( e, "_sun" );
\r
422 if( _sun[ 0 ] == '1' )
\r
424 /* not a spot light */
\r
427 /* unlink this light */
\r
428 lights = light->next;
\r
431 VectorScale( light->normal, -1.0f, sun.direction );
\r
432 VectorCopy( light->color, sun.color );
\r
433 sun.photons = (intensity / pointScale);
\r
434 sun.deviance = deviance / 180.0f * Q_PI;
\r
435 sun.numSamples = numSamples;
\r
438 /* make a sun light */
\r
439 CreateSunLight( &sun );
\r
441 /* free original light */
\r
445 /* skip the rest of this love story */
\r
451 /* jitter the light */
\r
452 for( j = 1; j < numSamples; j++ )
\r
454 /* create a light */
\r
455 light2 = safe_malloc( sizeof( *light ) );
\r
456 memcpy( light2, light, sizeof( *light ) );
\r
457 light2->next = lights;
\r
460 /* add to counts */
\r
461 if( light->type == EMIT_SPOT )
\r
467 light2->origin[ 0 ] = light->origin[ 0 ] + (Random() * 2.0f - 1.0f) * deviance;
\r
468 light2->origin[ 1 ] = light->origin[ 1 ] + (Random() * 2.0f - 1.0f) * deviance;
\r
469 light2->origin[ 2 ] = light->origin[ 2 ] + (Random() * 2.0f - 1.0f) * deviance;
\r
477 CreateSurfaceLights() - ydnar
\r
478 this hijacks the radiosity code to generate surface lights for first pass
\r
481 #define APPROX_BOUNCE 1.0f
\r
483 void CreateSurfaceLights( void )
\r
486 bspDrawSurface_t *ds;
\r
487 surfaceInfo_t *info;
\r
496 /* get sun shader supressor */
\r
497 nss = ValueForKey( &entities[ 0 ], "_noshadersun" );
\r
499 /* walk the list of surfaces */
\r
500 for( i = 0; i < numBSPDrawSurfaces; i++ )
\r
502 /* get surface and other bits */
\r
503 ds = &bspDrawSurfaces[ i ];
\r
504 info = &surfaceInfos[ i ];
\r
508 if( si->sun != NULL && nss[ 0 ] != '1' )
\r
510 Sys_FPrintf( SYS_VRB, "Sun: %s\n", si->shader );
\r
511 CreateSunLight( si->sun );
\r
512 si->sun = NULL; /* FIXME: leak! */
\r
516 if( si->skyLightValue > 0.0f )
\r
518 Sys_FPrintf( SYS_VRB, "Sky: %s\n", si->shader );
\r
519 CreateSkyLights( si->color, si->skyLightValue, si->skyLightIterations, si->lightFilterRadius );
\r
520 si->skyLightValue = 0.0f; /* FIXME: hack! */
\r
523 /* try to early out */
\r
524 if( si->value <= 0 )
\r
527 /* autosprite shaders become point lights */
\r
528 if( si->autosprite )
\r
530 /* create an average xyz */
\r
531 VectorAdd( info->mins, info->maxs, origin );
\r
532 VectorScale( origin, 0.5f, origin );
\r
534 /* create a light */
\r
535 light = safe_malloc( sizeof( *light ) );
\r
536 memset( light, 0, sizeof( *light ) );
\r
537 light->next = lights;
\r
541 light->flags = LIGHT_Q3A_DEFAULT;
\r
542 light->type = EMIT_POINT;
\r
543 light->photons = si->value * pointScale;
\r
544 light->fade = 1.0f;
\r
546 VectorCopy( origin, light->origin );
\r
547 VectorCopy( si->color, light->color );
\r
548 light->falloffTolerance = falloffTolerance;
\r
549 light->style = light->style;
\r
551 /* add to point light count and continue */
\r
556 /* get subdivision amount */
\r
557 if( si->lightSubdivide > 0 )
\r
558 subdivide = si->lightSubdivide;
\r
560 subdivide = defaultLightSubdivide;
\r
562 /* switch on type */
\r
563 switch( ds->surfaceType )
\r
566 case MST_TRIANGLE_SOUP:
\r
567 RadLightForTriangles( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
\r
571 RadLightForPatch( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
\r
584 find the offset values for inline models
\r
587 void SetEntityOrigins( void )
\r
595 bspDrawSurface_t *ds;
\r
598 /* ydnar: copy drawverts into private storage for nefarious purposes */
\r
599 yDrawVerts = safe_malloc( numBSPDrawVerts * sizeof( bspDrawVert_t ) );
\r
600 memcpy( yDrawVerts, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVert_t ) );
\r
602 /* set the entity origins */
\r
603 for( i = 0; i < numEntities; i++ )
\r
605 /* get entity and model */
\r
606 e = &entities[ i ];
\r
607 key = ValueForKey( e, "model" );
\r
608 if( key[ 0 ] != '*' )
\r
610 modelnum = atoi( key + 1 );
\r
611 dm = &bspModels[ modelnum ];
\r
613 /* get entity origin */
\r
614 key = ValueForKey( e, "origin" );
\r
615 if( key[ 0 ] == '\0' )
\r
617 GetVectorForKey( e, "origin", origin );
\r
619 /* set origin for all surfaces for this model */
\r
620 for( j = 0; j < dm->numBSPSurfaces; j++ )
\r
623 ds = &bspDrawSurfaces[ dm->firstBSPSurface + j ];
\r
625 /* set its verts */
\r
626 for( k = 0; k < ds->numVerts; k++ )
\r
628 f = ds->firstVert + k;
\r
629 VectorAdd( origin, bspDrawVerts[ f ].xyz, yDrawVerts[ f ].xyz );
\r
638 PointToPolygonFormFactor()
\r
639 calculates the area over a point/normal hemisphere a winding covers
\r
640 ydnar: fixme: there has to be a faster way to calculate this
\r
641 without the expensive per-vert sqrts and transcendental functions
\r
642 ydnar 2002-09-30: added -faster switch because only 19% deviance > 10%
\r
643 between this and the approximation
\r
646 #define ONE_OVER_2PI 0.159154942f //% (1.0f / (2.0f * 3.141592657f))
\r
648 float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w )
\r
650 vec3_t triVector, triNormal;
\r
652 vec3_t dirs[ MAX_POINTS_ON_WINDING ];
\r
654 float dot, angle, facing;
\r
657 /* this is expensive */
\r
658 for( i = 0; i < w->numpoints; i++ )
\r
660 VectorSubtract( w->p[ i ], point, dirs[ i ] );
\r
661 VectorNormalize( dirs[ i ], dirs[ i ] );
\r
664 /* duplicate first vertex to avoid mod operation */
\r
665 VectorCopy( dirs[ 0 ], dirs[ i ] );
\r
667 /* calculcate relative area */
\r
669 for( i = 0; i < w->numpoints; i++ )
\r
671 /* get a triangle */
\r
673 dot = DotProduct( dirs[ i ], dirs[ j ] );
\r
675 /* roundoff can cause slight creep, which gives an IND from acos */
\r
678 else if( dot < -1.0f )
\r
681 /* get the angle */
\r
682 angle = acos( dot );
\r
684 CrossProduct( dirs[ i ], dirs[ j ], triVector );
\r
685 if( VectorNormalize( triVector, triNormal ) < 0.0001f )
\r
688 facing = DotProduct( normal, triNormal );
\r
689 total += facing * angle;
\r
691 /* ydnar: this was throwing too many errors with radiosity + crappy maps. ignoring it. */
\r
692 if( total > 6.3f || total < -6.3f )
\r
696 /* now in the range of 0 to 1 over the entire incoming hemisphere */
\r
697 //% total /= (2.0f * 3.141592657f);
\r
698 total *= ONE_OVER_2PI;
\r
705 LightContributionTosample()
\r
706 determines the amount of light reaching a sample (luxel or vertex) from a given light
\r
709 int LightContributionToSample( trace_t *trace )
\r
718 light = trace->light;
\r
721 VectorClear( trace->color );
\r
723 /* ydnar: early out */
\r
724 if( !(light->flags & LIGHT_SURFACES) || light->envelope <= 0.0f )
\r
727 /* do some culling checks */
\r
728 if( light->type != EMIT_SUN )
\r
730 /* MrE: if the light is behind the surface */
\r
731 if( trace->twoSided == qfalse )
\r
732 if( DotProduct( light->origin, trace->normal ) - DotProduct( trace->origin, trace->normal ) < 0.0f )
\r
735 /* ydnar: test pvs */
\r
736 if( !ClusterVisible( trace->cluster, light->cluster ) )
\r
740 /* ptpff approximation */
\r
741 if( light->type == EMIT_AREA && faster )
\r
743 /* get direction and distance */
\r
744 VectorCopy( light->origin, trace->end );
\r
745 dist = SetupTrace( trace );
\r
746 if( dist >= light->envelope )
\r
749 /* clamp the distance to prevent super hot spots */
\r
753 /* angle attenuation */
\r
754 angle = DotProduct( trace->normal, trace->direction );
\r
756 /* twosided lighting */
\r
757 if( trace->twoSided )
\r
758 angle = fabs( angle );
\r
761 angle *= -DotProduct( light->normal, trace->direction );
\r
762 if( angle <= 0.0f )
\r
764 add = light->photons / (dist * dist) * angle;
\r
767 /* exact point to polygon form factor */
\r
768 else if( light->type == EMIT_AREA )
\r
772 vec3_t pushedOrigin;
\r
775 /* project sample point into light plane */
\r
776 d = DotProduct( trace->origin, light->normal ) - light->dist;
\r
777 //% if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
\r
781 /* sample point behind plane? */
\r
782 if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
\r
785 /* sample plane coincident? */
\r
786 if( d > -3.0f && DotProduct( trace->normal, light->normal ) > 0.9f )
\r
790 /* nudge the point so that it is clearly forward of the light */
\r
791 /* so that surfaces meeting a light emiter don't get black edges */
\r
792 if( d > -8.0f && d < 8.0f )
\r
793 VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );
\r
795 VectorCopy( trace->origin, pushedOrigin );
\r
797 /* get direction and distance */
\r
798 VectorCopy( light->origin, trace->end );
\r
799 dist = SetupTrace( trace );
\r
800 if( dist >= light->envelope )
\r
803 /* calculate the contribution */
\r
804 factor = PointToPolygonFormFactor( pushedOrigin, trace->normal, light->w );
\r
805 if( factor == 0.0f )
\r
807 else if( factor < 0.0f )
\r
809 /* twosided lighting */
\r
810 if( trace->twoSided || (light->flags & LIGHT_TWOSIDED) )
\r
814 /* push light origin to other side of the plane */
\r
815 VectorMA( light->origin, -2.0f, light->normal, trace->end );
\r
816 dist = SetupTrace( trace );
\r
817 if( dist >= light->envelope )
\r
824 /* ydnar: moved to here */
\r
825 add = factor * light->add;
\r
828 /* point/spot lights */
\r
829 else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )
\r
831 /* get direction and distance */
\r
832 VectorCopy( light->origin, trace->end );
\r
833 dist = SetupTrace( trace );
\r
834 if( dist >= light->envelope )
\r
837 /* clamp the distance to prevent super hot spots */
\r
841 /* angle attenuation */
\r
842 angle = (light->flags & LIGHT_ATTEN_ANGLE) ? DotProduct( trace->normal, trace->direction ) : 1.0f;
\r
843 if( light->angleScale != 0.0f )
\r
845 angle /= light->angleScale;
\r
850 /* twosided lighting */
\r
851 if( trace->twoSided )
\r
852 angle = fabs( angle );
\r
855 if( light->flags & LIGHT_ATTEN_LINEAR )
\r
857 add = angle * light->photons * linearScale - (dist * light->fade);
\r
862 add = light->photons / (dist * dist) * angle;
\r
864 /* handle spotlights */
\r
865 if( light->type == EMIT_SPOT )
\r
867 float distByNormal, radiusAtDist, sampleRadius;
\r
868 vec3_t pointAtDist, distToSample;
\r
871 /* do cone calculation */
\r
872 distByNormal = -DotProduct( trace->displacement, light->normal );
\r
873 if( distByNormal < 0.0f )
\r
875 VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
\r
876 radiusAtDist = light->radiusByDist * distByNormal;
\r
877 VectorSubtract( trace->origin, pointAtDist, distToSample );
\r
878 sampleRadius = VectorLength( distToSample );
\r
880 /* outside the cone */
\r
881 if( sampleRadius >= radiusAtDist )
\r
885 if( sampleRadius > (radiusAtDist - 32.0f) )
\r
886 add *= ((radiusAtDist - sampleRadius) / 32.0f);
\r
890 /* ydnar: sunlight */
\r
891 else if( light->type == EMIT_SUN )
\r
893 /* get origin and direction */
\r
894 VectorAdd( trace->origin, light->origin, trace->end );
\r
895 dist = SetupTrace( trace );
\r
897 /* angle attenuation */
\r
898 angle = (light->flags & LIGHT_ATTEN_ANGLE)
\r
899 ? DotProduct( trace->normal, trace->direction )
\r
902 /* twosided lighting */
\r
903 if( trace->twoSided )
\r
904 angle = fabs( angle );
\r
907 add = light->photons * angle;
\r
912 trace->testAll = qtrue;
\r
913 VectorScale( light->color, add, trace->color );
\r
915 /* trace to point */
\r
916 if( trace->testOcclusion && !trace->forceSunlight )
\r
919 TraceLine( trace );
\r
920 if( !(trace->compileFlags & C_SKY) || trace->opaque )
\r
922 VectorClear( trace->color );
\r
927 /* return to sender */
\r
931 /* ydnar: changed to a variable number */
\r
932 if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )
\r
936 trace->testAll = qfalse;
\r
937 VectorScale( light->color, add, trace->color );
\r
940 TraceLine( trace );
\r
941 if( trace->passSolid || trace->opaque )
\r
943 VectorClear( trace->color );
\r
947 /* return to sender */
\r
955 determines the amount of light reaching a sample (luxel or vertex)
\r
958 void LightingAtSample( trace_t *trace, byte styles[ MAX_LIGHTMAPS ], vec3_t colors[ MAX_LIGHTMAPS ] )
\r
960 int i, lightmapNum;
\r
964 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
965 VectorClear( colors[ lightmapNum ] );
\r
967 /* ydnar: normalmap */
\r
970 colors[ 0 ][ 0 ] = (trace->normal[ 0 ] + 1.0f) * 127.5f;
\r
971 colors[ 0 ][ 1 ] = (trace->normal[ 1 ] + 1.0f) * 127.5f;
\r
972 colors[ 0 ][ 2 ] = (trace->normal[ 2 ] + 1.0f) * 127.5f;
\r
976 /* ydnar: don't bounce ambient all the time */
\r
978 VectorCopy( ambientColor, colors[ 0 ] );
\r
980 /* ydnar: trace to all the list of lights pre-stored in tw */
\r
981 for( i = 0; i < trace->numLights && trace->lights[ i ] != NULL; i++ )
\r
984 trace->light = trace->lights[ i ];
\r
987 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
989 if( styles[ lightmapNum ] == trace->light->style ||
\r
990 styles[ lightmapNum ] == LS_NONE )
\r
994 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a sample */
\r
995 if( lightmapNum >= MAX_LIGHTMAPS )
\r
999 LightContributionToSample( trace );
\r
1000 if( trace->color[ 0 ] == 0.0f && trace->color[ 1 ] == 0.0f && trace->color[ 2 ] == 0.0f )
\r
1003 /* handle negative light */
\r
1004 if( trace->light->flags & LIGHT_NEGATIVE )
\r
1005 VectorScale( trace->color, -1.0f, trace->color );
\r
1008 styles[ lightmapNum ] = trace->light->style;
\r
1011 VectorAdd( colors[ lightmapNum ], trace->color, colors[ lightmapNum ] );
\r
1015 colors[ 0 ][ 0 ] >= 255.0f &&
\r
1016 colors[ 0 ][ 1 ] >= 255.0f &&
\r
1017 colors[ 0 ][ 2 ] >= 255.0f )
\r
1025 LightContributionToPoint()
\r
1026 for a given light, how much light/color reaches a given point in space (with no facing)
\r
1027 note: this is similar to LightContributionToSample() but optimized for omnidirectional sampling
\r
1030 int LightContributionToPoint( trace_t *trace )
\r
1037 light = trace->light;
\r
1040 VectorClear( trace->color );
\r
1042 /* ydnar: early out */
\r
1043 if( !(light->flags & LIGHT_GRID) || light->envelope <= 0.0f )
\r
1046 /* is this a sun? */
\r
1047 if( light->type != EMIT_SUN )
\r
1054 if( !ClusterVisible( trace->cluster, light->cluster ) )
\r
1058 /* ydnar: check origin against light's pvs envelope */
\r
1059 if( trace->origin[ 0 ] > light->maxs[ 0 ] || trace->origin[ 0 ] < light->mins[ 0 ] ||
\r
1060 trace->origin[ 1 ] > light->maxs[ 1 ] || trace->origin[ 1 ] < light->mins[ 1 ] ||
\r
1061 trace->origin[ 2 ] > light->maxs[ 2 ] || trace->origin[ 2 ] < light->mins[ 2 ] )
\r
1063 gridBoundsCulled++;
\r
1067 /* set light origin */
\r
1068 if( light->type == EMIT_SUN )
\r
1069 VectorAdd( trace->origin, light->origin, trace->end );
\r
1071 VectorCopy( light->origin, trace->end );
\r
1073 /* set direction */
\r
1074 dist = SetupTrace( trace );
\r
1076 /* test envelope */
\r
1077 if( dist > light->envelope )
\r
1079 gridEnvelopeCulled++;
\r
1083 /* ptpff approximation */
\r
1084 if( light->type == EMIT_AREA && faster )
\r
1086 /* clamp the distance to prevent super hot spots */
\r
1087 if( dist < 16.0f )
\r
1091 add = light->photons / (dist * dist);
\r
1094 /* exact point to polygon form factor */
\r
1095 else if( light->type == EMIT_AREA )
\r
1098 vec3_t pushedOrigin;
\r
1101 /* see if the point is behind the light */
\r
1102 d = DotProduct( trace->origin, light->normal ) - light->dist;
\r
1103 if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )
\r
1106 /* nudge the point so that it is clearly forward of the light */
\r
1107 /* so that surfaces meeting a light emiter don't get black edges */
\r
1108 if( d > -8.0f && d < 8.0f )
\r
1109 VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );
\r
1111 VectorCopy( trace->origin, pushedOrigin );
\r
1113 /* calculate the contribution (ydnar 2002-10-21: [bug 642] bad normal calc) */
\r
1114 factor = PointToPolygonFormFactor( pushedOrigin, trace->direction, light->w );
\r
1115 if( factor == 0.0f )
\r
1117 else if( factor < 0.0f )
\r
1119 if( light->flags & LIGHT_TWOSIDED )
\r
1125 /* ydnar: moved to here */
\r
1126 add = factor * light->add;
\r
1129 /* point/spot lights */
\r
1130 else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )
\r
1132 /* clamp the distance to prevent super hot spots */
\r
1133 if( dist < 16.0f )
\r
1137 if( light->flags & LIGHT_ATTEN_LINEAR )
\r
1139 add = light->photons * linearScale - (dist * light->fade);
\r
1144 add = light->photons / (dist * dist);
\r
1146 /* handle spotlights */
\r
1147 if( light->type == EMIT_SPOT )
\r
1149 float distByNormal, radiusAtDist, sampleRadius;
\r
1150 vec3_t pointAtDist, distToSample;
\r
1153 /* do cone calculation */
\r
1154 distByNormal = -DotProduct( trace->displacement, light->normal );
\r
1155 if( distByNormal < 0.0f )
\r
1157 VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
\r
1158 radiusAtDist = light->radiusByDist * distByNormal;
\r
1159 VectorSubtract( trace->origin, pointAtDist, distToSample );
\r
1160 sampleRadius = VectorLength( distToSample );
\r
1162 /* outside the cone */
\r
1163 if( sampleRadius >= radiusAtDist )
\r
1167 if( sampleRadius > (radiusAtDist - 32.0f) )
\r
1168 add *= ((radiusAtDist - sampleRadius) / 32.0f);
\r
1172 /* ydnar: sunlight */
\r
1173 else if( light->type == EMIT_SUN )
\r
1176 add = light->photons;
\r
1181 trace->testAll = qtrue;
\r
1182 VectorScale( light->color, add, trace->color );
\r
1184 /* trace to point */
\r
1185 if( trace->testOcclusion && !trace->forceSunlight )
\r
1188 TraceLine( trace );
\r
1189 if( !(trace->compileFlags & C_SKY) || trace->opaque )
\r
1191 VectorClear( trace->color );
\r
1196 /* return to sender */
\r
1200 /* unknown light type */
\r
1204 /* ydnar: changed to a variable number */
\r
1205 if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )
\r
1209 trace->testAll = qfalse;
\r
1210 VectorScale( light->color, add, trace->color );
\r
1213 TraceLine( trace );
\r
1214 if( trace->passSolid )
\r
1216 VectorClear( trace->color );
\r
1220 /* we have a valid sample */
\r
1228 grid samples are for quickly determining the lighting
\r
1229 of dynamically placed entities in the world
\r
1232 #define MAX_CONTRIBUTIONS 1024
\r
1242 void TraceGrid( int num )
\r
1244 int i, j, x, y, z, mod, step, numCon, numStyles;
\r
1246 vec3_t baseOrigin, cheapColor, color;
\r
1247 rawGridPoint_t *gp;
\r
1248 bspGridPoint_t *bgp;
\r
1249 contribution_t contributions[ MAX_CONTRIBUTIONS ];
\r
1253 /* get grid points */
\r
1254 gp = &rawGridPoints[ num ];
\r
1255 bgp = &bspGridPoints[ num ];
\r
1257 /* get grid origin */
\r
1259 z = mod / (gridBounds[ 0 ] * gridBounds[ 1 ]);
\r
1260 mod -= z * (gridBounds[ 0 ] * gridBounds[ 1 ]);
\r
1261 y = mod / gridBounds[ 0 ];
\r
1262 mod -= y * gridBounds[ 0 ];
\r
1265 trace.origin[ 0 ] = gridMins[ 0 ] + x * gridSize[ 0 ];
\r
1266 trace.origin[ 1 ] = gridMins[ 1 ] + y * gridSize[ 1 ];
\r
1267 trace.origin[ 2 ] = gridMins[ 2 ] + z * gridSize[ 2 ];
\r
1269 /* set inhibit sphere */
\r
1270 if( gridSize[ 0 ] > gridSize[ 1 ] && gridSize[ 0 ] > gridSize[ 2 ] )
\r
1271 trace.inhibitRadius = gridSize[ 0 ] * 0.5f;
\r
1272 else if( gridSize[ 1 ] > gridSize[ 0 ] && gridSize[ 1 ] > gridSize[ 2 ] )
\r
1273 trace.inhibitRadius = gridSize[ 1 ] * 0.5f;
\r
1275 trace.inhibitRadius = gridSize[ 2 ] * 0.5f;
\r
1277 /* find point cluster */
\r
1278 trace.cluster = ClusterForPointExt( trace.origin, GRID_EPSILON );
\r
1279 if( trace.cluster < 0 )
\r
1281 /* try to nudge the origin around to find a valid point */
\r
1282 VectorCopy( trace.origin, baseOrigin );
\r
1283 for( step = 9; step <= 18; step += 9 )
\r
1285 for( i = 0; i < 8; i++ )
\r
1287 VectorCopy( baseOrigin, trace.origin );
\r
1289 trace.origin[ 0 ] += step;
\r
1291 trace.origin[ 0 ] -= step;
\r
1294 trace.origin[ 1 ] += step;
\r
1296 trace.origin[ 1 ] -= step;
\r
1299 trace.origin[ 2 ] += step;
\r
1301 trace.origin[ 2 ] -= step;
\r
1303 /* ydnar: changed to find cluster num */
\r
1304 trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );
\r
1305 if( trace.cluster >= 0 )
\r
1313 /* can't find a valid point at all */
\r
1319 trace.testOcclusion = !noTrace;
\r
1320 trace.forceSunlight = qfalse;
\r
1321 trace.recvShadows = WORLDSPAWN_RECV_SHADOWS;
\r
1322 trace.numSurfaces = 0;
\r
1323 trace.surfaces = NULL;
\r
1324 trace.numLights = 0;
\r
1325 trace.lights = NULL;
\r
1329 VectorClear( cheapColor );
\r
1331 /* trace to all the lights, find the major light direction, and divide the
\r
1332 total light between that along the direction and the remaining in the ambient */
\r
1333 for( trace.light = lights; trace.light != NULL; trace.light = trace.light->next )
\r
1338 /* sample light */
\r
1339 if( !LightContributionToPoint( &trace ) )
\r
1342 /* handle negative light */
\r
1343 if( trace.light->flags & LIGHT_NEGATIVE )
\r
1344 VectorScale( trace.color, -1.0f, trace.color );
\r
1346 /* add a contribution */
\r
1347 VectorCopy( trace.color, contributions[ numCon ].color );
\r
1348 VectorCopy( trace.direction, contributions[ numCon ].dir );
\r
1349 contributions[ numCon ].style = trace.light->style;
\r
1352 /* push average direction around */
\r
1353 addSize = VectorLength( trace.color );
\r
1354 VectorMA( gp->dir, addSize, trace.direction, gp->dir );
\r
1356 /* stop after a while */
\r
1357 if( numCon >= (MAX_CONTRIBUTIONS - 1) )
\r
1360 /* ydnar: cheap mode */
\r
1361 VectorAdd( cheapColor, trace.color, cheapColor );
\r
1362 if( cheapgrid && cheapColor[ 0 ] >= 255.0f && cheapColor[ 1 ] >= 255.0f && cheapColor[ 2 ] >= 255.0f )
\r
1366 /* normalize to get primary light direction */
\r
1367 VectorNormalize( gp->dir, gp->dir );
\r
1369 /* now that we have identified the primary light direction,
\r
1370 go back and separate all the light into directed and ambient */
\r
1372 for( i = 0; i < numCon; i++ )
\r
1374 /* get relative directed strength */
\r
1375 d = DotProduct( contributions[ i ].dir, gp->dir );
\r
1379 /* find appropriate style */
\r
1380 for( j = 0; j < numStyles; j++ )
\r
1382 if( gp->styles[ j ] == contributions[ i ].style )
\r
1386 /* style not found? */
\r
1387 if( j >= numStyles )
\r
1389 /* add a new style */
\r
1390 if( numStyles < MAX_LIGHTMAPS )
\r
1392 gp->styles[ numStyles ] = contributions[ i ].style;
\r
1393 bgp->styles[ numStyles ] = contributions[ i ].style;
\r
1395 //% Sys_Printf( "(%d, %d) ", num, contributions[ i ].style );
\r
1403 /* add the directed color */
\r
1404 VectorMA( gp->directed[ j ], d, contributions[ i ].color, gp->directed[ j ] );
\r
1406 /* ambient light will be at 1/4 the value of directed light */
\r
1407 /* (ydnar: nuke this in favor of more dramatic lighting?) */
\r
1408 d = 0.25f * (1.0f - d);
\r
1409 VectorMA( gp->ambient[ j ], d, contributions[ i ].color, gp->ambient[ j ] );
\r
1413 /* store off sample */
\r
1414 for( i = 0; i < MAX_LIGHTMAPS; i++ )
\r
1416 /* do some fudging to keep the ambient from being too low (2003-07-05: 0.25 -> 0.125) */
\r
1418 VectorMA( gp->ambient[ i ], 0.125f, gp->directed[ i ], gp->ambient[ i ] );
\r
1420 /* set minimum light and copy off to bytes */
\r
1421 VectorCopy( gp->ambient[ i ], color );
\r
1422 for( j = 0; j < 3; j++ )
\r
1423 if( color[ j ] < minGridLight[ j ] )
\r
1424 color[ j ] = minGridLight[ j ];
\r
1425 ColorToBytes( color, bgp->ambient[ i ], 1.0f );
\r
1426 ColorToBytes( gp->directed[ i ], bgp->directed[ i ], 1.0f );
\r
1431 //% Sys_FPrintf( SYS_VRB, "%10d %10d %10d ", &gp->ambient[ 0 ][ 0 ], &gp->ambient[ 0 ][ 1 ], &gp->ambient[ 0 ][ 2 ] );
\r
1432 Sys_FPrintf( SYS_VRB, "%9d Amb: (%03.1f %03.1f %03.1f) Dir: (%03.1f %03.1f %03.1f)\n",
\r
1434 gp->ambient[ 0 ][ 0 ], gp->ambient[ 0 ][ 1 ], gp->ambient[ 0 ][ 2 ],
\r
1435 gp->directed[ 0 ][ 0 ], gp->directed[ 0 ][ 1 ], gp->directed[ 0 ][ 2 ] );
\r
1438 /* store direction */
\r
1439 NormalToLatLong( gp->dir, bgp->latLong );
\r
1446 calculates the size of the lightgrid and allocates memory
\r
1449 void SetupGrid( void )
\r
1452 vec3_t maxs, oldGridSize;
\r
1453 const char *value;
\r
1457 /* don't do this if not grid lighting */
\r
1458 if( noGridLighting )
\r
1461 /* ydnar: set grid size */
\r
1462 value = ValueForKey( &entities[ 0 ], "gridsize" );
\r
1463 if( value[ 0 ] != '\0' )
\r
1464 sscanf( value, "%f %f %f", &gridSize[ 0 ], &gridSize[ 1 ], &gridSize[ 2 ] );
\r
1467 VectorCopy( gridSize, oldGridSize );
\r
1468 for( i = 0; i < 3; i++ )
\r
1469 gridSize[ i ] = gridSize[ i ] >= 8.0f ? floor( gridSize[ i ] ) : 8.0f;
\r
1471 /* ydnar: increase gridSize until grid count is smaller than max allowed */
\r
1472 numRawGridPoints = MAX_MAP_LIGHTGRID + 1;
\r
1474 while( numRawGridPoints > MAX_MAP_LIGHTGRID )
\r
1476 /* get world bounds */
\r
1477 for( i = 0; i < 3; i++ )
\r
1479 gridMins[ i ] = gridSize[ i ] * ceil( bspModels[ 0 ].mins[ i ] / gridSize[ i ] );
\r
1480 maxs[ i ] = gridSize[ i ] * floor( bspModels[ 0 ].maxs[ i ] / gridSize[ i ] );
\r
1481 gridBounds[ i ] = (maxs[ i ] - gridMins[ i ]) / gridSize[ i ] + 1;
\r
1484 /* set grid size */
\r
1485 numRawGridPoints = gridBounds[ 0 ] * gridBounds[ 1 ] * gridBounds[ 2 ];
\r
1487 /* increase grid size a bit */
\r
1488 if( numRawGridPoints > MAX_MAP_LIGHTGRID )
\r
1489 gridSize[ j++ % 3 ] += 16.0f;
\r
1493 Sys_Printf( "Grid size = { %1.0f, %1.0f, %1.0f }\n", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
\r
1496 if( !VectorCompare( gridSize, oldGridSize ) )
\r
1498 sprintf( temp, "%.0f %.0f %.0f", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
\r
1499 SetKeyValue( &entities[ 0 ], "gridsize", (const char*) temp );
\r
1500 Sys_FPrintf( SYS_VRB, "Storing adjusted grid size\n" );
\r
1503 /* 2nd variable. fixme: is this silly? */
\r
1504 numBSPGridPoints = numRawGridPoints;
\r
1506 /* allocate lightgrid */
\r
1507 rawGridPoints = safe_malloc( numRawGridPoints * sizeof( *rawGridPoints ) );
\r
1508 memset( rawGridPoints, 0, numRawGridPoints * sizeof( *rawGridPoints ) );
\r
1510 if( bspGridPoints != NULL )
\r
1511 free( bspGridPoints );
\r
1512 bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
\r
1513 memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );
\r
1515 /* clear lightgrid */
\r
1516 for( i = 0; i < numRawGridPoints; i++ )
\r
1518 VectorCopy( ambientColor, rawGridPoints[ i ].ambient[ j ] );
\r
1519 rawGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
\r
1520 bspGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
\r
1521 for( j = 1; j < MAX_LIGHTMAPS; j++ )
\r
1523 rawGridPoints[ i ].styles[ j ] = LS_NONE;
\r
1524 bspGridPoints[ i ].styles[ j ] = LS_NONE;
\r
1529 Sys_Printf( "%9d grid points\n", numRawGridPoints );
\r
1536 does what it says...
\r
1539 void LightWorld( void )
\r
1544 qboolean minVertex, minGrid;
\r
1545 const char *value;
\r
1548 /* ydnar: smooth normals */
\r
1551 Sys_Printf( "--- SmoothNormals ---\n" );
\r
1555 /* determine the number of grid points */
\r
1556 Sys_Printf( "--- SetupGrid ---\n" );
\r
1559 /* find the optional minimum lighting values */
\r
1560 GetVectorForKey( &entities[ 0 ], "_color", color );
\r
1561 if( VectorLength( color ) == 0.0f )
\r
1562 VectorSet( color, 1.0, 1.0, 1.0 );
\r
1565 f = FloatForKey( &entities[ 0 ], "_ambient" );
\r
1567 f = FloatForKey( &entities[ 0 ], "ambient" );
\r
1568 VectorScale( color, f, ambientColor );
\r
1570 /* minvertexlight */
\r
1571 minVertex = qfalse;
\r
1572 value = ValueForKey( &entities[ 0 ], "_minvertexlight" );
\r
1573 if( value[ 0 ] != '\0' )
\r
1575 minVertex = qtrue;
\r
1576 f = atof( value );
\r
1577 VectorScale( color, f, minVertexLight );
\r
1580 /* mingridlight */
\r
1582 value = ValueForKey( &entities[ 0 ], "_mingridlight" );
\r
1583 if( value[ 0 ] != '\0' )
\r
1586 f = atof( value );
\r
1587 VectorScale( color, f, minGridLight );
\r
1591 value = ValueForKey( &entities[ 0 ], "_minlight" );
\r
1592 if( value[ 0 ] != '\0' )
\r
1594 f = atof( value );
\r
1595 VectorScale( color, f, minLight );
\r
1596 if( minVertex == qfalse )
\r
1597 VectorScale( color, f, minVertexLight );
\r
1598 if( minGrid == qfalse )
\r
1599 VectorScale( color, f, minGridLight );
\r
1602 /* create world lights */
\r
1603 Sys_FPrintf( SYS_VRB, "--- CreateLights ---\n" );
\r
1604 CreateEntityLights();
\r
1605 CreateSurfaceLights();
\r
1606 Sys_Printf( "%9d point lights\n", numPointLights );
\r
1607 Sys_Printf( "%9d spotlights\n", numSpotLights );
\r
1608 Sys_Printf( "%9d diffuse (area) lights\n", numDiffuseLights );
\r
1609 Sys_Printf( "%9d sun/sky lights\n", numSunLights );
\r
1611 /* calculate lightgrid */
\r
1612 if( !noGridLighting )
\r
1614 /* ydnar: set up light envelopes */
\r
1615 SetupEnvelopes( qtrue, fastgrid );
\r
1617 Sys_Printf( "--- TraceGrid ---\n" );
\r
1618 RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
\r
1619 Sys_Printf( "%d x %d x %d = %d grid\n",
\r
1620 gridBounds[ 0 ], gridBounds[ 1 ], gridBounds[ 2 ], numBSPGridPoints );
\r
1622 /* ydnar: emit statistics on light culling */
\r
1623 Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
\r
1624 Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
\r
1627 /* slight optimization to remove a sqrt */
\r
1628 subdivideThreshold *= subdivideThreshold;
\r
1630 /* map the world luxels */
\r
1631 Sys_Printf( "--- MapRawLightmap ---\n" );
\r
1632 RunThreadsOnIndividual( numRawLightmaps, qtrue, MapRawLightmap );
\r
1633 Sys_Printf( "%9d luxels\n", numLuxels );
\r
1634 Sys_Printf( "%9d luxels mapped\n", numLuxelsMapped );
\r
1635 Sys_Printf( "%9d luxels occluded\n", numLuxelsOccluded );
\r
1637 /* ydnar: set up light envelopes */
\r
1638 SetupEnvelopes( qfalse, fast );
\r
1640 /* light up my world */
\r
1641 lightsPlaneCulled = 0;
\r
1642 lightsEnvelopeCulled = 0;
\r
1643 lightsBoundsCulled = 0;
\r
1644 lightsClusterCulled = 0;
\r
1646 Sys_Printf( "--- IlluminateRawLightmap ---\n" );
\r
1647 RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
\r
1648 Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
\r
1650 StitchSurfaceLightmaps();
\r
1652 Sys_Printf( "--- IlluminateVertexes ---\n" );
\r
1653 RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
\r
1654 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
\r
1656 /* ydnar: emit statistics on light culling */
\r
1657 Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
\r
1658 Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
\r
1659 Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
\r
1660 Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );
\r
1665 while( bounce > 0 )
\r
1667 /* store off the bsp between bounces */
\r
1668 StoreSurfaceLightmaps();
\r
1669 Sys_Printf( "Writing %s\n", source );
\r
1670 WriteBSPFile( source );
\r
1673 Sys_Printf( "\n--- Radiosity (bounce %d of %d) ---\n", b, bt );
\r
1675 /* flag bouncing */
\r
1677 VectorClear( ambientColor );
\r
1679 /* generate diffuse lights */
\r
1681 RadCreateDiffuseLights();
\r
1683 /* setup light envelopes */
\r
1684 SetupEnvelopes( qfalse, fastbounce );
\r
1685 if( numLights == 0 )
\r
1687 Sys_Printf( "No diffuse light to calculate, ending radiosity.\n" );
\r
1691 /* add to lightgrid */
\r
1694 gridEnvelopeCulled = 0;
\r
1695 gridBoundsCulled = 0;
\r
1697 Sys_Printf( "--- BounceGrid ---\n" );
\r
1698 RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
\r
1699 Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
\r
1700 Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
\r
1703 /* light up my world */
\r
1704 lightsPlaneCulled = 0;
\r
1705 lightsEnvelopeCulled = 0;
\r
1706 lightsBoundsCulled = 0;
\r
1707 lightsClusterCulled = 0;
\r
1709 Sys_Printf( "--- IlluminateRawLightmap ---\n" );
\r
1710 RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
\r
1711 Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
\r
1712 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
\r
1714 StitchSurfaceLightmaps();
\r
1716 Sys_Printf( "--- IlluminateVertexes ---\n" );
\r
1717 RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
\r
1718 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
\r
1720 /* ydnar: emit statistics on light culling */
\r
1721 Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
\r
1722 Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
\r
1723 Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
\r
1724 Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );
\r
1736 main routine for light processing
\r
1739 int LightMain( int argc, char **argv )
\r
1743 char mapSource[ 1024 ];
\r
1744 const char *value;
\r
1748 Sys_Printf( "--- Light ---\n" );
\r
1750 /* process commandline arguments */
\r
1751 for( i = 1; i < (argc - 1); i++ )
\r
1753 /* lightsource scaling */
\r
1754 if( !strcmp( argv[ i ], "-point" ) || !strcmp( argv[ i ], "-pointscale" ) )
\r
1756 f = atof( argv[ i + 1 ] );
\r
1758 Sys_Printf( "Point (entity) light scaled by %f to %f\n", f, pointScale );
\r
1762 else if( !strcmp( argv[ i ], "-area" ) || !strcmp( argv[ i ], "-areascale" ) )
\r
1764 f = atof( argv[ i + 1 ] );
\r
1766 Sys_Printf( "Area (shader) light scaled by %f to %f\n", f, areaScale );
\r
1770 else if( !strcmp( argv[ i ], "-sky" ) || !strcmp( argv[ i ], "-skyscale" ) )
\r
1772 f = atof( argv[ i + 1 ] );
\r
1774 Sys_Printf( "Sky/sun light scaled by %f to %f\n", f, skyScale );
\r
1778 else if( !strcmp( argv[ i ], "-bouncescale" ) )
\r
1780 f = atof( argv[ i + 1 ] );
\r
1782 Sys_Printf( "Bounce (radiosity) light scaled by %f to %f\n", f, bounceScale );
\r
1786 else if( !strcmp( argv[ i ], "-scale" ) )
\r
1788 f = atof( argv[ i + 1 ] );
\r
1793 Sys_Printf( "All light scaled by %f\n", f );
\r
1797 /* ydnar switches */
\r
1798 else if( !strcmp( argv[ i ], "-bounce" ) )
\r
1800 bounce = atoi( argv[ i + 1 ] );
\r
1803 else if( bounce > 0 )
\r
1804 Sys_Printf( "Radiosity enabled with %d bounce(s)\n", bounce );
\r
1808 else if( !strcmp( argv[ i ], "-supersample" ) || !strcmp( argv[ i ], "-super" ) )
\r
1810 superSample = atoi( argv[ i + 1 ] );
\r
1811 if( superSample < 1 )
\r
1813 else if( superSample > 1 )
\r
1814 Sys_Printf( "Ordered-grid supersampling enabled with %d sample(s) per lightmap texel\n", (superSample * superSample) );
\r
1818 else if( !strcmp( argv[ i ], "-samples" ) )
\r
1820 lightSamples = atoi( argv[ i + 1 ] );
\r
1821 if( lightSamples < 1 )
\r
1823 else if( lightSamples > 1 )
\r
1824 Sys_Printf( "Adaptive supersampling enabled with %d sample(s) per lightmap texel\n", lightSamples );
\r
1828 else if( !strcmp( argv[ i ], "-filter" ) )
\r
1831 Sys_Printf( "Lightmap filtering enabled\n" );
\r
1834 else if( !strcmp( argv[ i ], "-shadeangle" ) )
\r
1836 shadeAngleDegrees = atof( argv[ i + 1 ] );
\r
1837 if( shadeAngleDegrees < 0.0f )
\r
1838 shadeAngleDegrees = 0.0f;
\r
1839 else if( shadeAngleDegrees > 0.0f )
\r
1842 Sys_Printf( "Phong shading enabled with a breaking angle of %f degrees\n", shadeAngleDegrees );
\r
1847 else if( !strcmp( argv[ i ], "-thresh" ) )
\r
1849 subdivideThreshold = atof( argv[ i + 1 ] );
\r
1850 if( subdivideThreshold < 0 )
\r
1851 subdivideThreshold = DEFAULT_SUBDIVIDE_THRESHOLD;
\r
1853 Sys_Printf( "Subdivision threshold set at %.3f\n", subdivideThreshold );
\r
1857 else if( !strcmp( argv[ i ], "-approx" ) )
\r
1859 approximateTolerance = atoi( argv[ i + 1 ] );
\r
1860 if( approximateTolerance < 0 )
\r
1861 approximateTolerance = 0;
\r
1862 else if( approximateTolerance > 0 )
\r
1863 Sys_Printf( "Approximating lightmaps within a byte tolerance of %d\n", approximateTolerance );
\r
1867 else if( !strcmp( argv[ i ], "-deluxe" ) || !strcmp( argv[ i ], "-deluxemap" ) )
\r
1869 deluxemap = qtrue;
\r
1870 Sys_Printf( "Generating deluxemaps for average light direction\n" );
\r
1873 else if( !strcmp( argv[ i ], "-external" ) )
\r
1875 externalLightmaps = qtrue;
\r
1876 Sys_Printf( "Storing all lightmaps externally\n" );
\r
1879 else if( !strcmp( argv[ i ], "-lightmapsize" ) )
\r
1881 lmCustomSize = atoi( argv[ i + 1 ] );
\r
1883 /* must be a power of 2 and greater than 2 */
\r
1884 if( ((lmCustomSize - 1) & lmCustomSize) || lmCustomSize < 2 )
\r
1886 Sys_Printf( "WARNING: Lightmap size must be a power of 2, greater or equal to 2 pixels.\n" );
\r
1887 lmCustomSize = LIGHTMAP_WIDTH;
\r
1890 Sys_Printf( "Default lightmap size set to %d x %d pixels\n", lmCustomSize, lmCustomSize );
\r
1892 /* enable external lightmaps */
\r
1893 if( lmCustomSize != LIGHTMAP_WIDTH )
\r
1895 externalLightmaps = qtrue;
\r
1896 Sys_Printf( "Storing all lightmaps externally\n" );
\r
1900 /* ydnar: add this to suppress warnings */
\r
1901 else if( !strcmp( argv[ i ], "-custinfoparms") )
\r
1903 Sys_Printf( "Custom info parms enabled\n" );
\r
1904 useCustomInfoParms = qtrue;
\r
1907 else if( !strcmp( argv[ i ], "-wolf" ) )
\r
1909 /* -game should already be set */
\r
1910 game->wolfLight = qtrue;
\r
1911 Sys_Printf( "Enabling Wolf lighting model\n" );
\r
1914 else if( !strcmp( argv[ i ], "-sunonly" ) )
\r
1917 Sys_Printf( "Only computing sunlight\n" );
\r
1920 else if( !strcmp( argv[ i ], "-bounceonly" ) )
\r
1922 bounceOnly = qtrue;
\r
1923 Sys_Printf( "Storing bounced light (radiosity) only\n" );
\r
1926 else if( !strcmp( argv[ i ], "-nocollapse" ) )
\r
1928 noCollapse = qtrue;
\r
1929 Sys_Printf( "Identical lightmap collapsing disabled\n" );
\r
1932 else if( !strcmp( argv[ i ], "-shade" ) )
\r
1935 Sys_Printf( "Phong shading enabled\n" );
\r
1938 else if( !strcmp( argv[ i ], "-bouncegrid") )
\r
1940 bouncegrid = qtrue;
\r
1942 Sys_Printf( "Grid lighting with radiosity enabled\n" );
\r
1945 else if( !strcmp( argv[ i ], "-smooth" ) )
\r
1948 lightSamples = EXTRA_SCALE;
\r
1949 Sys_Printf( "The -smooth argument is deprecated, use \"-samples 2\" instead\n" );
\r
1952 else if( !strcmp( argv[ i ], "-fast" ) )
\r
1956 fastbounce = qtrue;
\r
1957 Sys_Printf( "Fast mode enabled\n" );
\r
1960 else if( !strcmp( argv[ i ], "-faster" ) )
\r
1965 fastbounce = qtrue;
\r
1966 Sys_Printf( "Faster mode enabled\n" );
\r
1969 else if( !strcmp( argv[ i ], "-fastgrid" ) )
\r
1972 Sys_Printf( "Fast grid lighting enabled\n" );
\r
1975 else if( !strcmp( argv[ i ], "-fastbounce" ) )
\r
1977 fastbounce = qtrue;
\r
1978 Sys_Printf( "Fast bounce mode enabled\n" );
\r
1981 else if( !strcmp( argv[ i ], "-cheap" ) )
\r
1984 cheapgrid = qtrue;
\r
1985 Sys_Printf( "Cheap mode enabled\n" );
\r
1988 else if( !strcmp( argv[ i ], "-cheapgrid" ) )
\r
1990 cheapgrid = qtrue;
\r
1991 Sys_Printf( "Cheap grid mode enabled\n" );
\r
1994 else if( !strcmp( argv[ i ], "-normalmap" ) )
\r
1996 normalmap = qtrue;
\r
1997 Sys_Printf( "Storing normal map instead of lightmap\n" );
\r
2000 else if( !strcmp( argv[ i ], "-trisoup" ) )
\r
2003 Sys_Printf( "Converting brush faces to triangle soup\n" );
\r
2006 else if( !strcmp( argv[ i ], "-debug" ) )
\r
2009 Sys_Printf( "Lightmap debugging enabled\n" );
\r
2012 else if( !strcmp( argv[ i ], "-debugsurfaces" ) || !strcmp( argv[ i ], "-debugsurface" ) )
\r
2014 debugSurfaces = qtrue;
\r
2015 Sys_Printf( "Lightmap surface debugging enabled\n" );
\r
2018 else if( !strcmp( argv[ i ], "-debugunused" ) )
\r
2020 debugUnused = qtrue;
\r
2021 Sys_Printf( "Unused luxel debugging enabled\n" );
\r
2024 else if( !strcmp( argv[ i ], "-debugaxis" ) )
\r
2026 debugAxis = qtrue;
\r
2027 Sys_Printf( "Lightmap axis debugging enabled\n" );
\r
2030 else if( !strcmp( argv[ i ], "-debugcluster" ) )
\r
2032 debugCluster = qtrue;
\r
2033 Sys_Printf( "Luxel cluster debugging enabled\n" );
\r
2036 else if( !strcmp( argv[ i ], "-debugorigin" ) )
\r
2038 debugOrigin = qtrue;
\r
2039 Sys_Printf( "Luxel origin debugging enabled\n" );
\r
2042 else if( !strcmp( argv[ i ], "-debugdeluxe" ) )
\r
2044 deluxemap = qtrue;
\r
2045 debugDeluxemap = qtrue;
\r
2046 Sys_Printf( "Deluxemap debugging enabled\n" );
\r
2049 else if( !strcmp( argv[ i ], "-export" ) )
\r
2051 exportLightmaps = qtrue;
\r
2052 Sys_Printf( "Exporting lightmaps\n" );
\r
2055 else if( !strcmp(argv[ i ], "-notrace" ))
\r
2058 Sys_Printf( "Shadow occlusion disabled\n" );
\r
2060 else if( !strcmp(argv[ i ], "-patchshadows" ) )
\r
2062 patchShadows = qtrue;
\r
2063 Sys_Printf( "Patch shadow casting enabled\n" );
\r
2065 else if( !strcmp( argv[ i ], "-extra" ) )
\r
2068 superSample = EXTRA_SCALE; /* ydnar */
\r
2069 Sys_Printf( "The -extra argument is deprecated, use \"-super 2\" instead\n" );
\r
2071 else if( !strcmp( argv[ i ], "-extrawide" ) )
\r
2074 extraWide = qtrue;
\r
2075 superSample = EXTRAWIDE_SCALE; /* ydnar */
\r
2076 filter = qtrue; /* ydnar */
\r
2077 Sys_Printf( "The -extrawide argument is deprecated, use \"-filter [-super 2]\" instead\n");
\r
2079 else if( !strcmp( argv[ i ], "-samplesize" ) )
\r
2081 sampleSize = atoi( argv[ i + 1 ] );
\r
2082 if( sampleSize < 1 )
\r
2085 Sys_Printf( "Default lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
\r
2087 else if( !strcmp( argv[ i ], "-novertex" ) )
\r
2089 noVertexLighting = qtrue;
\r
2090 Sys_Printf( "Disabling vertex lighting\n" );
\r
2092 else if( !strcmp( argv[ i ], "-nogrid" ) )
\r
2094 noGridLighting = qtrue;
\r
2095 Sys_Printf( "Disabling grid lighting\n" );
\r
2097 else if( !strcmp( argv[ i ], "-border" ) )
\r
2099 lightmapBorder = qtrue;
\r
2100 Sys_Printf( "Adding debug border to lightmaps\n" );
\r
2102 else if( !strcmp( argv[ i ], "-nosurf" ) )
\r
2104 noSurfaces = qtrue;
\r
2105 Sys_Printf( "Not tracing against surfaces\n" );
\r
2107 else if( !strcmp( argv[ i ], "-dump" ) )
\r
2110 Sys_Printf( "Dumping radiosity lights into numbered prefabs\n" );
\r
2112 else if( !strcmp( argv[ i ], "-lomem" ) )
\r
2115 Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );
\r
2119 Sys_Printf( "WARNING: Unknown option \"%s\"\n", argv[ i ] );
\r
2122 /* clean up map name */
\r
2123 strcpy( source, ExpandArg( argv[ i ] ) );
\r
2124 StripExtension( source );
\r
2125 DefaultExtension( source, ".bsp" );
\r
2126 strcpy( mapSource, ExpandArg( argv[ i ] ) );
\r
2127 StripExtension( mapSource );
\r
2128 DefaultExtension( mapSource, ".map" );
\r
2130 /* ydnar: set default sample size */
\r
2131 SetDefaultSampleSize( sampleSize );
\r
2133 /* ydnar: handle shaders */
\r
2134 BeginMapShaderFile( source );
\r
2137 /* note loading */
\r
2138 Sys_Printf( "Loading %s\n", source );
\r
2140 /* ydnar: load surface file */
\r
2141 LoadSurfaceExtraFile( source );
\r
2143 /* load bsp file */
\r
2144 LoadBSPFile( source );
\r
2146 /* parse bsp entities */
\r
2149 /* load map file */
\r
2150 value = ValueForKey( &entities[ 0 ], "_keepLights" );
\r
2151 if( value[ 0 ] != '1' )
\r
2152 LoadMapFile( mapSource, qtrue );
\r
2154 /* set the entity/model origins and init yDrawVerts */
\r
2155 SetEntityOrigins();
\r
2157 /* ydnar: set up optimization */
\r
2159 SetupSurfaceLightmaps();
\r
2161 /* initialize the surface facet tracing */
\r
2162 SetupTraceNodes();
\r
2164 /* light the world */
\r
2167 /* ydnar: store off lightmaps */
\r
2168 StoreSurfaceLightmaps();
\r
2170 /* write out the bsp */
\r
2171 UnparseEntities();
\r
2172 Sys_Printf( "Writing %s\n", source );
\r
2173 WriteBSPFile( source );
\r
2175 /* ydnar: export lightmaps */
\r
2176 if( exportLightmaps && !externalLightmaps )
\r
2177 ExportLightmaps();
\r
2179 /* return to sender */
\r