1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
42 CreateSunLight() - ydnar
43 this creates a sun light
46 static void CreateSunLight( sun_t *sun ){
48 float photons, d, angle, elevation, da, de;
59 if ( sun->numSamples < 1 ) {
64 photons = sun->photons / sun->numSamples;
66 /* create the right number of suns */
67 for ( i = 0; i < sun->numSamples; i++ )
69 /* calculate sun direction */
71 VectorCopy( sun->direction, direction );
76 sun->direction[ 0 ] = cos( angle ) * cos( elevation );
77 sun->direction[ 1 ] = sin( angle ) * cos( elevation );
78 sun->direction[ 2 ] = sin( elevation );
80 xz_dist = sqrt( x*x + z*z )
81 latitude = atan2( xz_dist, y ) * RADIANS
82 longitude = atan2( x, z ) * RADIANS
85 d = sqrt( sun->direction[ 0 ] * sun->direction[ 0 ] + sun->direction[ 1 ] * sun->direction[ 1 ] );
86 angle = atan2( sun->direction[ 1 ], sun->direction[ 0 ] );
87 elevation = atan2( sun->direction[ 2 ], d );
89 /* jitter the angles (loop to keep random sample within sun->deviance steridians) */
92 da = ( Random() * 2.0f - 1.0f ) * sun->deviance;
93 de = ( Random() * 2.0f - 1.0f ) * sun->deviance;
95 while ( ( da * da + de * de ) > ( sun->deviance * sun->deviance ) );
100 //% Sys_Printf( "%d: Angle: %3.4f Elevation: %3.3f\n", sun->numSamples, (angle / Q_PI * 180.0f), (elevation / Q_PI * 180.0f) );
102 /* create new vector */
103 direction[ 0 ] = cos( angle ) * cos( elevation );
104 direction[ 1 ] = sin( angle ) * cos( elevation );
105 direction[ 2 ] = sin( elevation );
110 light = safe_malloc0( sizeof( *light ) );
111 light->next = lights;
114 /* initialize the light */
115 light->flags = LIGHT_SUN_DEFAULT;
116 light->type = EMIT_SUN;
118 light->falloffTolerance = falloffTolerance;
119 light->filterRadius = sun->filterRadius / sun->numSamples;
120 light->style = noStyles ? LS_NORMAL : sun->style;
122 /* set the light's position out to infinity */
123 VectorMA( vec3_origin, ( MAX_WORLD_COORD * 8.0f ), direction, light->origin ); /* MAX_WORLD_COORD * 2.0f */
125 /* set the facing to be the inverse of the sun direction */
126 VectorScale( direction, -1.0, light->normal );
127 light->dist = DotProduct( light->origin, light->normal );
129 /* set color and photons */
130 VectorCopy( sun->color, light->color );
131 light->photons = photons * skyScale;
135 if ( sun->next != NULL ) {
136 CreateSunLight( sun->next );
143 CreateSkyLights() - ydnar
144 simulates sky light with multiple suns
147 static void CreateSkyLights( vec3_t color, float value, int iterations, float filterRadius, int style ){
149 int angleSteps, elevationSteps;
150 float angle, elevation;
151 float angleStep, elevationStep;
156 if ( value <= 0.0f || iterations < 2 ) {
160 /* basic sun setup */
161 VectorCopy( color, sun.color );
163 sun.filterRadius = filterRadius;
165 sun.style = noStyles ? LS_NORMAL : style;
169 elevationSteps = iterations - 1;
170 angleSteps = elevationSteps * 4;
172 elevationStep = DEG2RAD( 90.0f / iterations ); /* skip elevation 0 */
173 angleStep = DEG2RAD( 360.0f / angleSteps );
175 /* calc individual sun brightness */
176 numSuns = angleSteps * elevationSteps + 1;
177 sun.photons = value / numSuns;
179 /* iterate elevation */
180 elevation = elevationStep * 0.5f;
182 for ( i = 0, elevation = elevationStep * 0.5f; i < elevationSteps; i++ )
185 for ( j = 0; j < angleSteps; j++ )
188 sun.direction[ 0 ] = cos( angle ) * cos( elevation );
189 sun.direction[ 1 ] = sin( angle ) * cos( elevation );
190 sun.direction[ 2 ] = sin( elevation );
191 CreateSunLight( &sun );
198 elevation += elevationStep;
199 angle += angleStep / elevationSteps;
202 /* create vertical sun */
203 VectorSet( sun.direction, 0.0f, 0.0f, 1.0f );
204 CreateSunLight( &sun );
214 creates lights from light entities
217 void CreateEntityLights( void ){
219 light_t *light, *light2;
225 float intensity, scale, deviance, filterRadius;
226 int spawnflags, flags, numSamples;
230 /* go throught entity list and find lights */
231 for ( i = 0; i < numEntities; i++ )
235 name = ValueForKey( e, "classname" );
237 /* ydnar: check for lightJunior */
238 if ( Q_strncasecmp( name, "lightJunior", 11 ) == 0 ) {
241 else if ( Q_strncasecmp( name, "light", 5 ) == 0 ) {
248 /* lights with target names (and therefore styles) are only parsed from BSP */
249 target = ValueForKey( e, "targetname" );
250 if ( target[ 0 ] != '\0' && i >= numBSPEntities ) {
256 light = safe_malloc0( sizeof( *light ) );
257 light->next = lights;
260 /* handle spawnflags */
261 spawnflags = IntForKey( e, "spawnflags" );
263 /* ydnar: quake 3+ light behavior */
264 if ( wolfLight == qfalse ) {
265 /* set default flags */
266 flags = LIGHT_Q3A_DEFAULT;
268 /* linear attenuation? */
269 if ( spawnflags & 1 ) {
270 flags |= LIGHT_ATTEN_LINEAR;
271 flags &= ~LIGHT_ATTEN_ANGLE;
274 /* no angle attenuate? */
275 if ( spawnflags & 2 ) {
276 flags &= ~LIGHT_ATTEN_ANGLE;
280 /* ydnar: wolf light behavior */
283 /* set default flags */
284 flags = LIGHT_WOLF_DEFAULT;
286 /* inverse distance squared attenuation? */
287 if ( spawnflags & 1 ) {
288 flags &= ~LIGHT_ATTEN_LINEAR;
289 flags |= LIGHT_ATTEN_ANGLE;
292 /* angle attenuate? */
293 if ( spawnflags & 2 ) {
294 flags |= LIGHT_ATTEN_ANGLE;
298 /* other flags (borrowed from wolf) */
300 /* wolf dark light? */
301 if ( ( spawnflags & 4 ) || ( spawnflags & 8 ) ) {
306 if ( spawnflags & 16 ) {
307 flags &= ~LIGHT_GRID;
313 flags &= ~LIGHT_SURFACES;
316 /* vortex: unnormalized? */
317 if ( spawnflags & 32 ) {
318 flags |= LIGHT_UNNORMALIZED;
321 /* vortex: distance atten? */
322 if ( spawnflags & 64 ) {
323 flags |= LIGHT_ATTEN_DISTANCE;
326 /* store the flags */
327 light->flags = flags;
329 /* ydnar: set fade key (from wolf) */
331 if ( light->flags & LIGHT_ATTEN_LINEAR ) {
332 light->fade = FloatForKey( e, "fade" );
333 if ( light->fade == 0.0f ) {
338 /* ydnar: set angle scaling (from vlight) */
339 light->angleScale = FloatForKey( e, "_anglescale" );
340 if ( light->angleScale != 0.0f ) {
341 light->flags |= LIGHT_ATTEN_ANGLE;
345 GetVectorForKey( e, "origin", light->origin );
346 light->style = IntForKey( e, "_style" );
347 if ( light->style == LS_NORMAL ) {
348 light->style = IntForKey( e, "style" );
350 if ( light->style < LS_NORMAL || light->style >= LS_NONE ) {
351 Error( "Invalid lightstyle (%d) on entity %d", light->style, i );
354 if ( light->style != LS_NORMAL ) {
355 Sys_FPrintf( SYS_WRN, "WARNING: Styled light found targeting %s\n **", target );
358 /* set light intensity */
359 intensity = FloatForKey( e, "_light" );
360 if ( intensity == 0.0f ) {
361 intensity = FloatForKey( e, "light" );
363 if ( intensity == 0.0f ) {
367 /* ydnar: set light scale (sof2) */
368 scale = FloatForKey( e, "scale" );
369 if ( scale == 0.0f ) {
374 /* ydnar: get deviance and samples */
375 deviance = FloatForKey( e, "_deviance" );
376 if ( deviance == 0.0f ) {
377 deviance = FloatForKey( e, "_deviation" );
379 if ( deviance == 0.0f ) {
380 deviance = FloatForKey( e, "_jitter" );
382 numSamples = IntForKey( e, "_samples" );
383 if ( deviance < 0.0f || numSamples < 1 ) {
387 intensity /= numSamples;
389 /* ydnar: get filter radius */
390 filterRadius = FloatForKey( e, "_filterradius" );
391 if ( filterRadius == 0.0f ) {
392 filterRadius = FloatForKey( e, "_filteradius" );
394 if ( filterRadius == 0.0f ) {
395 filterRadius = FloatForKey( e, "_filter" );
397 if ( filterRadius < 0.0f ) {
400 light->filterRadius = filterRadius;
402 /* set light color */
403 _color = ValueForKey( e, "_color" );
404 if ( _color && _color[ 0 ] ) {
405 sscanf( _color, "%f %f %f", &light->color[ 0 ], &light->color[ 1 ], &light->color[ 2 ] );
407 light->color[0] = Image_LinearFloatFromsRGBFloat( light->color[0] );
408 light->color[1] = Image_LinearFloatFromsRGBFloat( light->color[1] );
409 light->color[2] = Image_LinearFloatFromsRGBFloat( light->color[2] );
411 if ( !( light->flags & LIGHT_UNNORMALIZED ) ) {
412 ColorNormalize( light->color, light->color );
416 light->color[ 0 ] = light->color[ 1 ] = light->color[ 2 ] = 1.0f;
419 light->extraDist = FloatForKey( e, "_extradist" );
420 if ( light->extraDist == 0.0f ) {
421 light->extraDist = extraDist;
424 light->photons = intensity;
426 light->type = EMIT_POINT;
428 /* set falloff threshold */
429 light->falloffTolerance = falloffTolerance / numSamples;
431 /* lights with a target will be spotlights */
432 target = ValueForKey( e, "target" );
441 e2 = FindTargetEntity( target );
443 Sys_FPrintf( SYS_WRN, "WARNING: light at (%i %i %i) has missing target\n",
444 (int) light->origin[ 0 ], (int) light->origin[ 1 ], (int) light->origin[ 2 ] );
445 light->photons *= pointScale;
449 /* not a point light */
453 /* make a spotlight */
454 GetVectorForKey( e2, "origin", dest );
455 VectorSubtract( dest, light->origin, light->normal );
456 dist = VectorNormalize( light->normal, light->normal );
457 radius = FloatForKey( e, "radius" );
464 light->radiusByDist = ( radius + 16 ) / dist;
465 light->type = EMIT_SPOT;
467 /* ydnar: wolf mods: spotlights always use nonlinear + angle attenuation */
468 light->flags &= ~LIGHT_ATTEN_LINEAR;
469 light->flags |= LIGHT_ATTEN_ANGLE;
472 /* ydnar: is this a sun? */
473 _sun = ValueForKey( e, "_sun" );
474 if ( _sun[ 0 ] == '1' ) {
475 /* not a spot light */
478 /* unlink this light */
479 lights = light->next;
482 VectorScale( light->normal, -1.0f, sun.direction );
483 VectorCopy( light->color, sun.color );
484 sun.photons = intensity;
485 sun.deviance = deviance / 180.0f * Q_PI;
486 sun.numSamples = numSamples;
487 sun.style = noStyles ? LS_NORMAL : light->style;
490 /* make a sun light */
491 CreateSunLight( &sun );
493 /* free original light */
497 /* skip the rest of this love story */
502 light->photons *= spotScale;
507 light->photons *= pointScale;
510 /* jitter the light */
511 for ( j = 1; j < numSamples; j++ )
514 light2 = safe_malloc( sizeof( *light ) );
515 memcpy( light2, light, sizeof( *light ) );
516 light2->next = lights;
520 if ( light->type == EMIT_SPOT ) {
528 light2->origin[ 0 ] = light->origin[ 0 ] + ( Random() * 2.0f - 1.0f ) * deviance;
529 light2->origin[ 1 ] = light->origin[ 1 ] + ( Random() * 2.0f - 1.0f ) * deviance;
530 light2->origin[ 2 ] = light->origin[ 2 ] + ( Random() * 2.0f - 1.0f ) * deviance;
538 CreateSurfaceLights() - ydnar
539 this hijacks the radiosity code to generate surface lights for first pass
542 #define APPROX_BOUNCE 1.0f
544 void CreateSurfaceLights( void ){
546 bspDrawSurface_t *ds;
556 /* get sun shader supressor */
557 nss = ValueForKey( &entities[ 0 ], "_noshadersun" );
559 /* walk the list of surfaces */
560 for ( i = 0; i < numBSPDrawSurfaces; i++ )
562 /* get surface and other bits */
563 ds = &bspDrawSurfaces[ i ];
564 info = &surfaceInfos[ i ];
568 if ( si->sun != NULL && nss[ 0 ] != '1' ) {
569 Sys_FPrintf( SYS_VRB, "Sun: %s\n", si->shader );
570 CreateSunLight( si->sun );
571 si->sun = NULL; /* FIXME: leak! */
575 if ( si->skyLightValue > 0.0f ) {
576 Sys_FPrintf( SYS_VRB, "Sky: %s\n", si->shader );
577 CreateSkyLights( si->color, si->skyLightValue, si->skyLightIterations, si->lightFilterRadius, si->lightStyle );
578 si->skyLightValue = 0.0f; /* FIXME: hack! */
581 /* try to early out */
582 if ( si->value <= 0 ) {
586 /* autosprite shaders become point lights */
587 if ( si->autosprite ) {
588 /* create an average xyz */
589 VectorAdd( info->mins, info->maxs, origin );
590 VectorScale( origin, 0.5f, origin );
593 light = safe_malloc0( sizeof( *light ) );
594 light->next = lights;
598 light->flags = LIGHT_Q3A_DEFAULT;
599 light->type = EMIT_POINT;
600 light->photons = si->value * pointScale;
603 VectorCopy( origin, light->origin );
604 VectorCopy( si->color, light->color );
605 light->falloffTolerance = falloffTolerance;
606 light->style = si->lightStyle;
608 /* add to point light count and continue */
613 /* get subdivision amount */
614 if ( si->lightSubdivide > 0 ) {
615 subdivide = si->lightSubdivide;
618 subdivide = defaultLightSubdivide;
622 switch ( ds->surfaceType )
625 case MST_TRIANGLE_SOUP:
626 RadLightForTriangles( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
630 RadLightForPatch( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
643 find the offset values for inline models
646 void SetEntityOrigins( void ){
653 bspDrawSurface_t *ds;
656 /* ydnar: copy drawverts into private storage for nefarious purposes */
657 yDrawVerts = safe_malloc( numBSPDrawVerts * sizeof( bspDrawVert_t ) );
658 memcpy( yDrawVerts, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVert_t ) );
660 /* set the entity origins */
661 for ( i = 0; i < numEntities; i++ )
663 /* get entity and model */
665 key = ValueForKey( e, "model" );
666 if ( key[ 0 ] != '*' ) {
669 modelnum = atoi( key + 1 );
670 dm = &bspModels[ modelnum ];
672 /* get entity origin */
673 key = ValueForKey( e, "origin" );
674 if ( key[ 0 ] == '\0' ) {
677 GetVectorForKey( e, "origin", origin );
679 /* set origin for all surfaces for this model */
680 for ( j = 0; j < dm->numBSPSurfaces; j++ )
683 ds = &bspDrawSurfaces[ dm->firstBSPSurface + j ];
686 for ( k = 0; k < ds->numVerts; k++ )
688 f = ds->firstVert + k;
689 VectorAdd( origin, bspDrawVerts[ f ].xyz, yDrawVerts[ f ].xyz );
698 PointToPolygonFormFactor()
699 calculates the area over a point/normal hemisphere a winding covers
700 ydnar: fixme: there has to be a faster way to calculate this
701 without the expensive per-vert sqrts and transcendental functions
702 ydnar 2002-09-30: added -faster switch because only 19% deviance > 10%
703 between this and the approximation
706 #define ONE_OVER_2PI 0.159154942f //% (1.0f / (2.0f * 3.141592657f))
708 float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w ){
709 vec3_t triVector, triNormal;
711 vec3_t dirs[ MAX_POINTS_ON_WINDING ];
713 float dot, angle, facing;
716 /* this is expensive */
717 for ( i = 0; i < w->numpoints; i++ )
719 VectorSubtract( w->p[ i ], point, dirs[ i ] );
720 VectorFastNormalize( dirs[ i ], dirs[ i ] );
723 /* duplicate first vertex to avoid mod operation */
724 VectorCopy( dirs[ 0 ], dirs[ i ] );
726 /* calculcate relative area */
728 for ( i = 0; i < w->numpoints; i++ )
732 dot = DotProduct( dirs[ i ], dirs[ j ] );
734 /* roundoff can cause slight creep, which gives an IND from acos */
738 else if ( dot < -1.0f ) {
745 CrossProduct( dirs[ i ], dirs[ j ], triVector );
746 if ( VectorFastNormalize( triVector, triNormal ) < 0.0001f ) {
750 facing = DotProduct( normal, triNormal );
751 total += facing * angle;
753 /* ydnar: this was throwing too many errors with radiosity + crappy maps. ignoring it. */
754 if ( total > 6.3f || total < -6.3f ) {
759 /* now in the range of 0 to 1 over the entire incoming hemisphere */
760 //% total /= (2.0f * 3.141592657f);
761 total *= ONE_OVER_2PI;
768 LightContributionTosample()
769 determines the amount of light reaching a sample (luxel or vertex) from a given light
772 int LightContributionToSample( trace_t *trace ){
777 float addDeluxe = 0.0f, addDeluxeBounceScale = 0.25f;
778 qboolean angledDeluxe = qtrue;
779 float colorBrightness;
780 qboolean doAddDeluxe = qtrue;
783 light = trace->light;
786 trace->forceSubsampling = 0.0f; /* to make sure */
787 VectorClear( trace->color );
788 VectorClear( trace->colorNoShadow );
789 VectorClear( trace->directionContribution );
791 colorBrightness = RGBTOGRAY( light->color ) * ( 1.0f / 255.0f );
793 /* ydnar: early out */
794 if ( !( light->flags & LIGHT_SURFACES ) || light->envelope <= 0.0f ) {
798 /* do some culling checks */
799 if ( light->type != EMIT_SUN ) {
800 /* MrE: if the light is behind the surface */
801 if ( trace->twoSided == qfalse ) {
802 if ( DotProduct( light->origin, trace->normal ) - DotProduct( trace->origin, trace->normal ) < 0.0f ) {
807 /* ydnar: test pvs */
808 if ( !ClusterVisible( trace->cluster, light->cluster ) ) {
813 /* exact point to polygon form factor */
814 if ( light->type == EMIT_AREA ) {
819 /* project sample point into light plane */
820 d = DotProduct( trace->origin, light->normal ) - light->dist;
822 /* sample point behind plane? */
823 if ( !( light->flags & LIGHT_TWOSIDED ) && d < -1.0f ) {
827 /* sample plane coincident? */
828 if ( d > -3.0f && DotProduct( trace->normal, light->normal ) > 0.9f ) {
833 /* nudge the point so that it is clearly forward of the light */
834 /* so that surfaces meeting a light emitter don't get black edges */
835 if ( d > -8.0f && d < 8.0f ) {
836 VectorMA( trace->origin, ( 8.0f - d ), light->normal, pushedOrigin );
839 VectorCopy( trace->origin, pushedOrigin );
842 /* get direction and distance */
843 VectorCopy( light->origin, trace->end );
844 dist = SetupTrace( trace );
845 if ( dist >= light->envelope ) {
849 /* ptpff approximation */
851 /* angle attenuation */
852 angle = DotProduct( trace->normal, trace->direction );
854 /* twosided lighting */
855 if ( trace->twoSided && angle < 0 ) {
858 /* no deluxemap contribution from "other side" light */
859 doAddDeluxe = qfalse;
863 angle *= -DotProduct( light->normal, trace->direction );
864 if ( angle == 0.0f ) {
867 else if ( angle < 0.0f &&
868 ( trace->twoSided || ( light->flags & LIGHT_TWOSIDED ) ) ) {
871 /* no deluxemap contribution from "other side" light */
872 doAddDeluxe = qfalse;
875 /* clamp the distance to prevent super hot spots */
876 dist = sqrt( dist * dist + light->extraDist * light->extraDist );
877 if ( dist < 16.0f ) {
881 add = light->photons / ( dist * dist ) * angle;
884 if ( angledDeluxe ) {
885 addDeluxe = light->photons / ( dist * dist ) * angle;
888 addDeluxe = light->photons / ( dist * dist );
894 /* calculate the contribution */
895 factor = PointToPolygonFormFactor( pushedOrigin, trace->normal, light->w );
896 if ( factor == 0.0f ) {
899 else if ( factor < 0.0f ) {
900 /* twosided lighting */
901 if ( trace->twoSided || ( light->flags & LIGHT_TWOSIDED ) ) {
904 /* push light origin to other side of the plane */
905 VectorMA( light->origin, -2.0f, light->normal, trace->end );
906 dist = SetupTrace( trace );
907 if ( dist >= light->envelope ) {
911 /* no deluxemap contribution from "other side" light */
912 doAddDeluxe = qfalse;
919 /* also don't deluxe if the direction is on the wrong side */
920 if ( DotProduct( trace->normal, trace->direction ) < 0 ) {
921 /* no deluxemap contribution from "other side" light */
922 doAddDeluxe = qfalse;
925 /* ydnar: moved to here */
926 add = factor * light->add;
934 /* point/spot lights */
935 else if ( light->type == EMIT_POINT || light->type == EMIT_SPOT ) {
936 /* get direction and distance */
937 VectorCopy( light->origin, trace->end );
938 dist = SetupTrace( trace );
939 if ( dist >= light->envelope ) {
943 /* clamp the distance to prevent super hot spots */
944 dist = sqrt( dist * dist + light->extraDist * light->extraDist );
945 if ( dist < 16.0f ) {
949 /* angle attenuation */
950 if ( light->flags & LIGHT_ATTEN_ANGLE ) {
951 /* standard Lambert attenuation */
952 float dot = DotProduct( trace->normal, trace->direction );
954 /* twosided lighting */
955 if ( trace->twoSided && dot < 0 ) {
958 /* no deluxemap contribution from "other side" light */
959 doAddDeluxe = qfalse;
962 /* jal: optional half Lambert attenuation (http://developer.valvesoftware.com/wiki/Half_Lambert) */
963 if ( lightAngleHL ) {
964 if ( dot > 0.001f ) { // skip coplanar
968 dot = ( dot * 0.5f ) + 0.5f;
982 if ( light->angleScale != 0.0f ) {
983 angle /= light->angleScale;
984 if ( angle > 1.0f ) {
990 if ( light->flags & LIGHT_ATTEN_LINEAR ) {
991 add = angle * light->photons * linearScale - ( dist * light->fade );
997 if ( angledDeluxe ) {
998 addDeluxe = angle * light->photons * linearScale - ( dist * light->fade );
1001 addDeluxe = light->photons * linearScale - ( dist * light->fade );
1004 if ( addDeluxe < 0.0f ) {
1011 add = ( light->photons / ( dist * dist ) ) * angle;
1017 if ( angledDeluxe ) {
1018 addDeluxe = ( light->photons / ( dist * dist ) ) * angle;
1021 addDeluxe = ( light->photons / ( dist * dist ) );
1025 if ( addDeluxe < 0.0f ) {
1030 /* handle spotlights */
1031 if ( light->type == EMIT_SPOT ) {
1032 float distByNormal, radiusAtDist, sampleRadius;
1033 vec3_t pointAtDist, distToSample;
1035 /* do cone calculation */
1036 distByNormal = -DotProduct( trace->displacement, light->normal );
1037 if ( distByNormal < 0.0f ) {
1040 VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
1041 radiusAtDist = light->radiusByDist * distByNormal;
1042 VectorSubtract( trace->origin, pointAtDist, distToSample );
1043 sampleRadius = VectorLength( distToSample );
1045 /* outside the cone */
1046 if ( sampleRadius >= radiusAtDist ) {
1051 if ( sampleRadius > ( radiusAtDist - 32.0f ) ) {
1052 add *= ( ( radiusAtDist - sampleRadius ) / 32.0f );
1057 addDeluxe *= ( ( radiusAtDist - sampleRadius ) / 32.0f );
1059 if ( addDeluxe < 0.0f ) {
1066 /* ydnar: sunlight */
1067 else if ( light->type == EMIT_SUN ) {
1068 /* get origin and direction */
1069 VectorAdd( trace->origin, light->origin, trace->end );
1070 dist = SetupTrace( trace );
1072 /* angle attenuation */
1073 if ( light->flags & LIGHT_ATTEN_ANGLE ) {
1074 /* standard Lambert attenuation */
1075 float dot = DotProduct( trace->normal, trace->direction );
1077 /* twosided lighting */
1078 if ( trace->twoSided && dot < 0 ) {
1081 /* no deluxemap contribution from "other side" light */
1082 doAddDeluxe = qfalse;
1085 /* jal: optional half Lambert attenuation (http://developer.valvesoftware.com/wiki/Half_Lambert) */
1086 if ( lightAngleHL ) {
1087 if ( dot > 0.001f ) { // skip coplanar
1091 dot = ( dot * 0.5f ) + 0.5f;
1106 add = light->photons * angle;
1109 if ( angledDeluxe ) {
1110 addDeluxe = light->photons * angle;
1113 addDeluxe = light->photons;
1116 if ( addDeluxe < 0.0f ) {
1121 if ( add <= 0.0f ) {
1125 /* VorteX: set noShadow color */
1126 VectorScale( light->color, add, trace->colorNoShadow );
1128 addDeluxe *= colorBrightness;
1131 addDeluxe *= addDeluxeBounceScale;
1132 if ( addDeluxe < 0.00390625f ) {
1133 addDeluxe = 0.00390625f;
1137 VectorScale( trace->direction, addDeluxe, trace->directionContribution );
1140 trace->testAll = qtrue;
1141 VectorScale( light->color, add, trace->color );
1143 /* trace to point */
1144 if ( trace->testOcclusion && !trace->forceSunlight ) {
1147 trace->forceSubsampling *= add;
1148 if ( !( trace->compileFlags & C_SKY ) || trace->opaque ) {
1149 VectorClear( trace->color );
1150 VectorClear( trace->directionContribution );
1156 /* return to sender */
1160 Error( "Light of undefined type!" );
1163 /* VorteX: set noShadow color */
1164 VectorScale( light->color, add, trace->colorNoShadow );
1166 /* ydnar: changed to a variable number */
1167 if ( add <= 0.0f || ( add <= light->falloffTolerance && ( light->flags & LIGHT_FAST_ACTUAL ) ) ) {
1171 addDeluxe *= colorBrightness;
1173 /* hack land: scale down the radiosity contribution to light directionality.
1174 Deluxemaps fusion many light directions into one. In a rtl process all lights
1175 would contribute individually to the bump map, so several light sources together
1176 would make it more directional (example: a yellow and red lights received from
1177 opposing sides would light one side in red and the other in blue, adding
1178 the effect of 2 directions applied. In the deluxemapping case, this 2 lights would
1179 neutralize each other making it look like having no direction.
1180 Same thing happens with radiosity. In deluxemapping case the radiosity contribution
1181 is modifying the direction applied from directional lights, making it go closer and closer
1182 to the surface normal the bigger is the amount of radiosity received.
1183 So, for preserving the directional lights contributions, we scale down the radiosity
1184 contribution. It's a hack, but there's a reason behind it */
1186 addDeluxe *= addDeluxeBounceScale;
1187 /* better NOT increase it beyond the original value
1188 if( addDeluxe < 0.00390625f )
1189 addDeluxe = 0.00390625f;
1193 if ( doAddDeluxe ) {
1194 VectorScale( trace->direction, addDeluxe, trace->directionContribution );
1198 trace->testAll = qfalse;
1199 VectorScale( light->color, add, trace->color );
1203 trace->forceSubsampling *= add;
1204 if ( trace->passSolid || trace->opaque ) {
1205 VectorClear( trace->color );
1206 VectorClear( trace->directionContribution );
1211 /* return to sender */
1219 determines the amount of light reaching a sample (luxel or vertex)
1222 void LightingAtSample( trace_t *trace, byte styles[ MAX_LIGHTMAPS ], vec3_t colors[ MAX_LIGHTMAPS ] ){
1227 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1228 VectorClear( colors[ lightmapNum ] );
1230 /* ydnar: normalmap */
1232 colors[ 0 ][ 0 ] = ( trace->normal[ 0 ] + 1.0f ) * 127.5f;
1233 colors[ 0 ][ 1 ] = ( trace->normal[ 1 ] + 1.0f ) * 127.5f;
1234 colors[ 0 ][ 2 ] = ( trace->normal[ 2 ] + 1.0f ) * 127.5f;
1238 /* ydnar: don't bounce ambient all the time */
1240 VectorCopy( ambientColor, colors[ 0 ] );
1243 /* ydnar: trace to all the list of lights pre-stored in tw */
1244 for ( i = 0; i < trace->numLights && trace->lights[ i ] != NULL; i++ )
1247 trace->light = trace->lights[ i ];
1250 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1252 if ( styles[ lightmapNum ] == trace->light->style ||
1253 styles[ lightmapNum ] == LS_NONE ) {
1258 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a sample */
1259 if ( lightmapNum >= MAX_LIGHTMAPS ) {
1264 LightContributionToSample( trace );
1265 if ( trace->color[ 0 ] == 0.0f && trace->color[ 1 ] == 0.0f && trace->color[ 2 ] == 0.0f ) {
1269 /* handle negative light */
1270 if ( trace->light->flags & LIGHT_NEGATIVE ) {
1271 VectorScale( trace->color, -1.0f, trace->color );
1275 styles[ lightmapNum ] = trace->light->style;
1278 VectorAdd( colors[ lightmapNum ], trace->color, colors[ lightmapNum ] );
1282 colors[ 0 ][ 0 ] >= 255.0f &&
1283 colors[ 0 ][ 1 ] >= 255.0f &&
1284 colors[ 0 ][ 2 ] >= 255.0f ) {
1293 LightContributionToPoint()
1294 for a given light, how much light/color reaches a given point in space (with no facing)
1295 note: this is similar to LightContributionToSample() but optimized for omnidirectional sampling
1298 int LightContributionToPoint( trace_t *trace ){
1304 light = trace->light;
1307 VectorClear( trace->color );
1309 /* ydnar: early out */
1310 if ( !( light->flags & LIGHT_GRID ) || light->envelope <= 0.0f ) {
1314 /* is this a sun? */
1315 if ( light->type != EMIT_SUN ) {
1322 if ( !ClusterVisible( trace->cluster, light->cluster ) ) {
1327 /* ydnar: check origin against light's pvs envelope */
1328 if ( trace->origin[ 0 ] > light->maxs[ 0 ] || trace->origin[ 0 ] < light->mins[ 0 ] ||
1329 trace->origin[ 1 ] > light->maxs[ 1 ] || trace->origin[ 1 ] < light->mins[ 1 ] ||
1330 trace->origin[ 2 ] > light->maxs[ 2 ] || trace->origin[ 2 ] < light->mins[ 2 ] ) {
1335 /* set light origin */
1336 if ( light->type == EMIT_SUN ) {
1337 VectorAdd( trace->origin, light->origin, trace->end );
1340 VectorCopy( light->origin, trace->end );
1344 dist = SetupTrace( trace );
1347 if ( dist > light->envelope ) {
1348 gridEnvelopeCulled++;
1352 /* ptpff approximation */
1353 if ( light->type == EMIT_AREA && faster ) {
1354 /* clamp the distance to prevent super hot spots */
1355 dist = sqrt( dist * dist + light->extraDist * light->extraDist );
1356 if ( dist < 16.0f ) {
1361 add = light->photons / ( dist * dist );
1364 /* exact point to polygon form factor */
1365 else if ( light->type == EMIT_AREA ) {
1367 vec3_t pushedOrigin;
1370 /* see if the point is behind the light */
1371 d = DotProduct( trace->origin, light->normal ) - light->dist;
1372 if ( !( light->flags & LIGHT_TWOSIDED ) && d < -1.0f ) {
1376 /* nudge the point so that it is clearly forward of the light */
1377 /* so that surfaces meeting a light emiter don't get black edges */
1378 if ( d > -8.0f && d < 8.0f ) {
1379 VectorMA( trace->origin, ( 8.0f - d ), light->normal, pushedOrigin );
1382 VectorCopy( trace->origin, pushedOrigin );
1385 /* calculate the contribution (ydnar 2002-10-21: [bug 642] bad normal calc) */
1386 factor = PointToPolygonFormFactor( pushedOrigin, trace->direction, light->w );
1387 if ( factor == 0.0f ) {
1390 else if ( factor < 0.0f ) {
1391 if ( light->flags & LIGHT_TWOSIDED ) {
1399 /* ydnar: moved to here */
1400 add = factor * light->add;
1403 /* point/spot lights */
1404 else if ( light->type == EMIT_POINT || light->type == EMIT_SPOT ) {
1405 /* clamp the distance to prevent super hot spots */
1406 dist = sqrt( dist * dist + light->extraDist * light->extraDist );
1407 if ( dist < 16.0f ) {
1412 if ( light->flags & LIGHT_ATTEN_LINEAR ) {
1413 add = light->photons * linearScale - ( dist * light->fade );
1419 add = light->photons / ( dist * dist );
1422 /* handle spotlights */
1423 if ( light->type == EMIT_SPOT ) {
1424 float distByNormal, radiusAtDist, sampleRadius;
1425 vec3_t pointAtDist, distToSample;
1428 /* do cone calculation */
1429 distByNormal = -DotProduct( trace->displacement, light->normal );
1430 if ( distByNormal < 0.0f ) {
1433 VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
1434 radiusAtDist = light->radiusByDist * distByNormal;
1435 VectorSubtract( trace->origin, pointAtDist, distToSample );
1436 sampleRadius = VectorLength( distToSample );
1438 /* outside the cone */
1439 if ( sampleRadius >= radiusAtDist ) {
1444 if ( sampleRadius > ( radiusAtDist - 32.0f ) ) {
1445 add *= ( ( radiusAtDist - sampleRadius ) / 32.0f );
1450 /* ydnar: sunlight */
1451 else if ( light->type == EMIT_SUN ) {
1453 add = light->photons;
1454 if ( add <= 0.0f ) {
1459 trace->testAll = qtrue;
1460 VectorScale( light->color, add, trace->color );
1462 /* trace to point */
1463 if ( trace->testOcclusion && !trace->forceSunlight ) {
1466 if ( !( trace->compileFlags & C_SKY ) || trace->opaque ) {
1467 VectorClear( trace->color );
1472 /* return to sender */
1476 /* unknown light type */
1481 /* ydnar: changed to a variable number */
1482 if ( add <= 0.0f || ( add <= light->falloffTolerance && ( light->flags & LIGHT_FAST_ACTUAL ) ) ) {
1487 trace->testAll = qfalse;
1488 VectorScale( light->color, add, trace->color );
1492 if ( trace->passSolid ) {
1493 VectorClear( trace->color );
1497 /* we have a valid sample */
1505 grid samples are for quickly determining the lighting
1506 of dynamically placed entities in the world
1509 #define MAX_CONTRIBUTIONS 32768
1520 void TraceGrid( int num ){
1521 int i, j, x, y, z, mod, numCon, numStyles;
1523 vec3_t baseOrigin, cheapColor, color, thisdir;
1525 bspGridPoint_t *bgp;
1526 contribution_t contributions[ MAX_CONTRIBUTIONS ];
1529 /* get grid points */
1530 gp = &rawGridPoints[ num ];
1531 bgp = &bspGridPoints[ num ];
1533 /* get grid origin */
1535 z = mod / ( gridBounds[ 0 ] * gridBounds[ 1 ] );
1536 mod -= z * ( gridBounds[ 0 ] * gridBounds[ 1 ] );
1537 y = mod / gridBounds[ 0 ];
1538 mod -= y * gridBounds[ 0 ];
1541 trace.origin[ 0 ] = gridMins[ 0 ] + x * gridSize[ 0 ];
1542 trace.origin[ 1 ] = gridMins[ 1 ] + y * gridSize[ 1 ];
1543 trace.origin[ 2 ] = gridMins[ 2 ] + z * gridSize[ 2 ];
1545 /* set inhibit sphere */
1546 if ( gridSize[ 0 ] > gridSize[ 1 ] && gridSize[ 0 ] > gridSize[ 2 ] ) {
1547 trace.inhibitRadius = gridSize[ 0 ] * 0.5f;
1549 else if ( gridSize[ 1 ] > gridSize[ 0 ] && gridSize[ 1 ] > gridSize[ 2 ] ) {
1550 trace.inhibitRadius = gridSize[ 1 ] * 0.5f;
1553 trace.inhibitRadius = gridSize[ 2 ] * 0.5f;
1556 /* find point cluster */
1557 trace.cluster = ClusterForPointExt( trace.origin, GRID_EPSILON );
1558 if ( trace.cluster < 0 ) {
1559 /* try to nudge the origin around to find a valid point */
1560 VectorCopy( trace.origin, baseOrigin );
1561 for ( step = 0; ( step += 0.005 ) <= 1.0; )
1563 VectorCopy( baseOrigin, trace.origin );
1564 trace.origin[ 0 ] += step * ( Random() - 0.5 ) * gridSize[0];
1565 trace.origin[ 1 ] += step * ( Random() - 0.5 ) * gridSize[1];
1566 trace.origin[ 2 ] += step * ( Random() - 0.5 ) * gridSize[2];
1568 /* ydnar: changed to find cluster num */
1569 trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );
1570 if ( trace.cluster >= 0 ) {
1575 /* can't find a valid point at all */
1582 trace.testOcclusion = !noTrace;
1583 trace.forceSunlight = qfalse;
1584 trace.recvShadows = WORLDSPAWN_RECV_SHADOWS;
1585 trace.numSurfaces = 0;
1586 trace.surfaces = NULL;
1587 trace.numLights = 0;
1588 trace.lights = NULL;
1592 VectorClear( cheapColor );
1594 /* trace to all the lights, find the major light direction, and divide the
1595 total light between that along the direction and the remaining in the ambient */
1596 for ( trace.light = lights; trace.light != NULL; trace.light = trace.light->next )
1602 if ( !LightContributionToPoint( &trace ) ) {
1606 /* handle negative light */
1607 if ( trace.light->flags & LIGHT_NEGATIVE ) {
1608 VectorScale( trace.color, -1.0f, trace.color );
1611 /* add a contribution */
1612 VectorCopy( trace.color, contributions[ numCon ].color );
1613 VectorCopy( trace.direction, contributions[ numCon ].dir );
1614 VectorClear( contributions[ numCon ].ambient );
1615 contributions[ numCon ].style = trace.light->style;
1618 /* push average direction around */
1619 addSize = VectorLength( trace.color );
1620 VectorMA( gp->dir, addSize, trace.direction, gp->dir );
1622 /* stop after a while */
1623 if ( numCon >= ( MAX_CONTRIBUTIONS - 1 ) ) {
1627 /* ydnar: cheap mode */
1628 VectorAdd( cheapColor, trace.color, cheapColor );
1629 if ( cheapgrid && cheapColor[ 0 ] >= 255.0f && cheapColor[ 1 ] >= 255.0f && cheapColor[ 2 ] >= 255.0f ) {
1634 /////// Floodlighting for point //////////////////
1635 //do our floodlight ambient occlusion loop, and add a single contribution based on the brightest dir
1636 if ( floodlighty ) {
1639 vec3_t dir = { 0, 0, 1 };
1640 float ambientFrac = 0.25f;
1642 trace.testOcclusion = qtrue;
1643 trace.forceSunlight = qfalse;
1644 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1645 trace.testAll = qtrue;
1647 for ( k = 0; k < 2; k++ )
1649 if ( k == 0 ) { // upper hemisphere
1650 trace.normal[0] = 0;
1651 trace.normal[1] = 0;
1652 trace.normal[2] = 1;
1654 else //lower hemisphere
1656 trace.normal[0] = 0;
1657 trace.normal[1] = 0;
1658 trace.normal[2] = -1;
1661 f = FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
1663 /* add a fraction as pure ambient, half as top-down direction */
1664 contributions[ numCon ].color[0] = floodlightRGB[0] * floodlightIntensity * f * ( 1.0f - ambientFrac );
1665 contributions[ numCon ].color[1] = floodlightRGB[1] * floodlightIntensity * f * ( 1.0f - ambientFrac );
1666 contributions[ numCon ].color[2] = floodlightRGB[2] * floodlightIntensity * f * ( 1.0f - ambientFrac );
1668 contributions[ numCon ].ambient[0] = floodlightRGB[0] * floodlightIntensity * f * ambientFrac;
1669 contributions[ numCon ].ambient[1] = floodlightRGB[1] * floodlightIntensity * f * ambientFrac;
1670 contributions[ numCon ].ambient[2] = floodlightRGB[2] * floodlightIntensity * f * ambientFrac;
1672 contributions[ numCon ].dir[0] = dir[0];
1673 contributions[ numCon ].dir[1] = dir[1];
1674 contributions[ numCon ].dir[2] = dir[2];
1676 contributions[ numCon ].style = 0;
1678 /* push average direction around */
1679 addSize = VectorLength( contributions[ numCon ].color );
1680 VectorMA( gp->dir, addSize, dir, gp->dir );
1685 /////////////////////
1687 /* normalize to get primary light direction */
1688 VectorNormalize( gp->dir, thisdir );
1690 /* now that we have identified the primary light direction,
1691 go back and separate all the light into directed and ambient */
1694 for ( i = 0; i < numCon; i++ )
1696 /* get relative directed strength */
1697 d = DotProduct( contributions[ i ].dir, thisdir );
1698 /* we map 1 to gridDirectionality, and 0 to gridAmbientDirectionality */
1699 d = gridAmbientDirectionality + d * ( gridDirectionality - gridAmbientDirectionality );
1704 /* find appropriate style */
1705 for ( j = 0; j < numStyles; j++ )
1707 if ( gp->styles[ j ] == contributions[ i ].style ) {
1712 /* style not found? */
1713 if ( j >= numStyles ) {
1714 /* add a new style */
1715 if ( numStyles < MAX_LIGHTMAPS ) {
1716 gp->styles[ numStyles ] = contributions[ i ].style;
1717 bgp->styles[ numStyles ] = contributions[ i ].style;
1719 //% Sys_Printf( "(%d, %d) ", num, contributions[ i ].style );
1728 /* add the directed color */
1729 VectorMA( gp->directed[ j ], d, contributions[ i ].color, gp->directed[ j ] );
1731 /* ambient light will be at 1/4 the value of directed light */
1732 /* (ydnar: nuke this in favor of more dramatic lighting?) */
1733 /* (PM: how about actually making it work? d=1 when it got here for single lights/sun :P */
1735 /* (Hobbes: always setting it to .25 is hardly any better) */
1736 d = 0.25f * ( 1.0f - d );
1737 VectorMA( gp->ambient[ j ], d, contributions[ i ].color, gp->ambient[ j ] );
1739 VectorAdd( gp->ambient[ j ], contributions[ i ].ambient, gp->ambient[ j ] );
1743 * the total light average = ambient value + 0.25 * sum of all directional values
1744 * we can also get the total light average as 0.25 * the sum of all contributions
1746 * 0.25 * sum(contribution_i) == ambient + 0.25 * sum(d_i contribution_i)
1749 * ambient == 0.25 * sum((1 - d_i) contribution_i)
1751 * So, 0.25f * (1.0f - d) IS RIGHT. If you want to tune it, tune d BEFORE.
1756 /* store off sample */
1757 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
1760 /* do some fudging to keep the ambient from being too low (2003-07-05: 0.25 -> 0.125) */
1762 VectorMA( gp->ambient[ i ], 0.125f, gp->directed[ i ], gp->ambient[ i ] );
1766 /* set minimum light and copy off to bytes */
1767 VectorCopy( gp->ambient[ i ], color );
1768 for ( j = 0; j < 3; j++ )
1769 if ( color[ j ] < minGridLight[ j ] ) {
1770 color[ j ] = minGridLight[ j ];
1773 /* vortex: apply gridscale and gridambientscale here */
1774 if (gp->directed[i][0] || gp->directed[i][1] || gp->directed[i][2]) {
1776 * HACK: if there's a non-zero directed component, this
1777 * lightgrid cell is useful. However, ioq3 skips grid
1778 * cells with zero ambient. So let's force ambient to be
1779 * nonzero unless directed is zero too.
1781 ColorToBytesNonZero(color, bgp->ambient[i], gridScale * gridAmbientScale);
1783 ColorToBytes( color, bgp->ambient[ i ], gridScale * gridAmbientScale );
1785 ColorToBytes( gp->directed[ i ], bgp->directed[ i ], gridScale );
1790 //% Sys_FPrintf( SYS_VRB, "%10d %10d %10d ", &gp->ambient[ 0 ][ 0 ], &gp->ambient[ 0 ][ 1 ], &gp->ambient[ 0 ][ 2 ] );
1791 Sys_FPrintf( SYS_VRB, "%9d Amb: (%03.1f %03.1f %03.1f) Dir: (%03.1f %03.1f %03.1f)\n",
1793 gp->ambient[ 0 ][ 0 ], gp->ambient[ 0 ][ 1 ], gp->ambient[ 0 ][ 2 ],
1794 gp->directed[ 0 ][ 0 ], gp->directed[ 0 ][ 1 ], gp->directed[ 0 ][ 2 ] );
1797 /* store direction */
1798 NormalToLatLong( thisdir, bgp->latLong );
1805 calculates the size of the lightgrid and allocates memory
1808 void SetupGrid( void ){
1810 vec3_t maxs, oldGridSize;
1815 /* don't do this if not grid lighting */
1816 if ( noGridLighting ) {
1820 /* ydnar: set grid size */
1821 value = ValueForKey( &entities[ 0 ], "gridsize" );
1822 if ( value[ 0 ] != '\0' ) {
1823 sscanf( value, "%f %f %f", &gridSize[ 0 ], &gridSize[ 1 ], &gridSize[ 2 ] );
1827 VectorCopy( gridSize, oldGridSize );
1828 for ( i = 0; i < 3; i++ )
1829 gridSize[ i ] = gridSize[ i ] >= 8.0f ? floor( gridSize[ i ] ) : 8.0f;
1831 /* ydnar: increase gridSize until grid count is smaller than max allowed */
1832 numRawGridPoints = MAX_MAP_LIGHTGRID + 1;
1834 while ( numRawGridPoints > MAX_MAP_LIGHTGRID )
1836 /* get world bounds */
1837 for ( i = 0; i < 3; i++ )
1839 gridMins[ i ] = gridSize[ i ] * ceil( bspModels[ 0 ].mins[ i ] / gridSize[ i ] );
1840 maxs[ i ] = gridSize[ i ] * floor( bspModels[ 0 ].maxs[ i ] / gridSize[ i ] );
1841 gridBounds[ i ] = ( maxs[ i ] - gridMins[ i ] ) / gridSize[ i ] + 1;
1845 numRawGridPoints = gridBounds[ 0 ] * gridBounds[ 1 ] * gridBounds[ 2 ];
1847 /* increase grid size a bit */
1848 if ( numRawGridPoints > MAX_MAP_LIGHTGRID ) {
1849 gridSize[ j++ % 3 ] += 16.0f;
1854 Sys_Printf( "Grid size = { %1.0f, %1.0f, %1.0f }\n", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
1857 if ( !VectorCompare( gridSize, oldGridSize ) ) {
1858 sprintf( temp, "%.0f %.0f %.0f", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
1859 SetKeyValue( &entities[ 0 ], "gridsize", (const char*) temp );
1860 Sys_FPrintf( SYS_VRB, "Storing adjusted grid size\n" );
1863 /* 2nd variable. fixme: is this silly? */
1864 numBSPGridPoints = numRawGridPoints;
1866 /* allocate lightgrid */
1867 rawGridPoints = safe_malloc0( numRawGridPoints * sizeof( *rawGridPoints ) );
1869 if ( bspGridPoints != NULL ) {
1870 free( bspGridPoints );
1872 bspGridPoints = safe_malloc0( numBSPGridPoints * sizeof( *bspGridPoints ) );
1874 /* clear lightgrid */
1875 for ( i = 0; i < numRawGridPoints; i++ )
1877 VectorCopy( ambientColor, rawGridPoints[ i ].ambient[ 0 ] );
1878 rawGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
1879 bspGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
1880 for ( j = 1; j < MAX_LIGHTMAPS; j++ )
1882 VectorCopy( ambientColor, rawGridPoints[ i ].ambient[ j ] );
1883 rawGridPoints[ i ].styles[ j ] = LS_NONE;
1884 bspGridPoints[ i ].styles[ j ] = LS_NONE;
1889 Sys_Printf( "%9d grid points\n", numRawGridPoints );
1896 does what it says...
1899 void LightWorld( const char *BSPFilePath, qboolean fastAllocate, qboolean noBounceStore ){
1903 qboolean minVertex, minGrid;
1906 /* ydnar: smooth normals */
1908 Sys_Printf( "--- SmoothNormals ---\n" );
1912 /* determine the number of grid points */
1913 Sys_Printf( "--- SetupGrid ---\n" );
1916 /* find the optional minimum lighting values */
1917 GetVectorForKey( &entities[ 0 ], "_color", color );
1918 if ( VectorLength( color ) == 0.0f ) {
1919 VectorSet( color, 1.0, 1.0, 1.0 );
1923 color[0] = Image_LinearFloatFromsRGBFloat( color[0] );
1924 color[1] = Image_LinearFloatFromsRGBFloat( color[1] );
1925 color[2] = Image_LinearFloatFromsRGBFloat( color[2] );
1929 f = FloatForKey( &entities[ 0 ], "_ambient" );
1931 f = FloatForKey( &entities[ 0 ], "ambient" );
1933 VectorScale( color, f, ambientColor );
1935 /* minvertexlight */
1937 value = ValueForKey( &entities[ 0 ], "_minvertexlight" );
1938 if ( value[ 0 ] != '\0' ) {
1941 VectorScale( color, f, minVertexLight );
1946 value = ValueForKey( &entities[ 0 ], "_mingridlight" );
1947 if ( value[ 0 ] != '\0' ) {
1950 VectorScale( color, f, minGridLight );
1954 value = ValueForKey( &entities[ 0 ], "_minlight" );
1955 if ( value[ 0 ] != '\0' ) {
1957 VectorScale( color, f, minLight );
1958 if ( minVertex == qfalse ) {
1959 VectorScale( color, f, minVertexLight );
1961 if ( minGrid == qfalse ) {
1962 VectorScale( color, f, minGridLight );
1966 /* create world lights */
1967 Sys_FPrintf( SYS_VRB, "--- CreateLights ---\n" );
1968 CreateEntityLights();
1969 CreateSurfaceLights();
1970 Sys_Printf( "%9d point lights\n", numPointLights );
1971 Sys_Printf( "%9d spotlights\n", numSpotLights );
1972 Sys_Printf( "%9d diffuse (area) lights\n", numDiffuseLights );
1973 Sys_Printf( "%9d sun/sky lights\n", numSunLights );
1975 /* calculate lightgrid */
1976 if ( !noGridLighting ) {
1977 /* ydnar: set up light envelopes */
1978 SetupEnvelopes( qtrue, fastgrid );
1980 Sys_Printf( "--- TraceGrid ---\n" );
1982 RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
1984 Sys_Printf( "%d x %d x %d = %d grid\n",
1985 gridBounds[ 0 ], gridBounds[ 1 ], gridBounds[ 2 ], numBSPGridPoints );
1987 /* ydnar: emit statistics on light culling */
1988 Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
1989 Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
1992 /* slight optimization to remove a sqrt */
1993 subdivideThreshold *= subdivideThreshold;
1995 /* map the world luxels */
1996 Sys_Printf( "--- MapRawLightmap ---\n" );
1997 RunThreadsOnIndividual( numRawLightmaps, qtrue, MapRawLightmap );
1998 Sys_Printf( "%9d luxels\n", numLuxels );
1999 Sys_Printf( "%9d luxels mapped\n", numLuxelsMapped );
2000 Sys_Printf( "%9d luxels occluded\n", numLuxelsOccluded );
2004 Sys_Printf( "--- DirtyRawLightmap ---\n" );
2005 RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
2008 /* floodlight pass */
2009 FloodlightRawLightmaps();
2011 /* ydnar: set up light envelopes */
2012 SetupEnvelopes( qfalse, fast );
2014 /* light up my world */
2015 lightsPlaneCulled = 0;
2016 lightsEnvelopeCulled = 0;
2017 lightsBoundsCulled = 0;
2018 lightsClusterCulled = 0;
2020 Sys_Printf( "--- IlluminateRawLightmap ---\n" );
2021 RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
2022 Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
2024 StitchSurfaceLightmaps();
2026 Sys_Printf( "--- IlluminateVertexes ---\n" );
2027 RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
2028 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
2030 /* ydnar: emit statistics on light culling */
2031 Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
2032 Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
2033 Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
2034 Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );
2040 while ( bounce > 0 )
2042 qboolean storeForReal = !noBounceStore;
2044 /* store off the bsp between bounces */
2045 StoreSurfaceLightmaps( fastAllocate, storeForReal );
2048 if ( storeForReal ) {
2049 Sys_Printf( "Writing %s\n", BSPFilePath );
2050 WriteBSPFile( BSPFilePath );
2054 Sys_Printf( "\n--- Radiosity (bounce %d of %d) ---\n", b, bt );
2058 VectorClear( ambientColor );
2059 floodlighty = qfalse;
2061 /* generate diffuse lights */
2063 RadCreateDiffuseLights();
2065 /* setup light envelopes */
2066 SetupEnvelopes( qfalse, fastbounce );
2067 if ( numLights == 0 ) {
2068 Sys_Printf( "No diffuse light to calculate, ending radiosity.\n" );
2069 if ( noBounceStore ) {
2075 /* add to lightgrid */
2077 gridEnvelopeCulled = 0;
2078 gridBoundsCulled = 0;
2080 Sys_Printf( "--- BounceGrid ---\n" );
2082 RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
2084 Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
2085 Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
2088 /* light up my world */
2089 lightsPlaneCulled = 0;
2090 lightsEnvelopeCulled = 0;
2091 lightsBoundsCulled = 0;
2092 lightsClusterCulled = 0;
2094 Sys_Printf( "--- IlluminateRawLightmap ---\n" );
2095 RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
2096 Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
2097 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
2099 StitchSurfaceLightmaps();
2101 Sys_Printf( "--- IlluminateVertexes ---\n" );
2102 RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
2103 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
2105 /* ydnar: emit statistics on light culling */
2106 Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
2107 Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
2108 Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
2109 Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );
2116 /* ydnar: store off lightmaps */
2117 StoreSurfaceLightmaps( fastAllocate, qtrue );
2124 added by spoon to get back the changed surfaceflags
2128 void LoadSurfaceFlags( char *filename ) {
2131 for( i = 0; i < numBSPShaders; i++ ) {
2134 si = ShaderInfoForShader( bspShaders[i].shader );
2136 bspShaders[ i ].surfaceFlags = si->surfaceFlags;
2144 main routine for light processing
2147 int LightMain( int argc, char **argv ){
2150 char BSPFilePath[ 1024 ];
2151 char surfaceFilePath[ 1024 ];
2153 surfaceFilePath[0] = 0;
2155 int lightmapMergeSize = 0;
2156 qboolean lightSamplesInsist = qfalse;
2157 qboolean fastAllocate = qtrue;
2158 qboolean noBounceStore = qfalse;
2161 Sys_Printf( "--- Light ---\n" );
2162 Sys_Printf( "--- ProcessGameSpecific ---\n" );
2164 /* set standard game flags */
2165 wolfLight = game->wolfLight;
2166 if ( wolfLight == qtrue ) {
2167 Sys_Printf( " lightning model: wolf\n" );
2170 Sys_Printf( " lightning model: quake3\n" );
2173 lmCustomSize = game->lightmapSize;
2174 Sys_Printf( " lightmap size: %d x %d pixels\n", lmCustomSize, lmCustomSize );
2176 lightmapGamma = game->lightmapGamma;
2177 Sys_Printf( " lightning gamma: %f\n", lightmapGamma );
2179 lightmapsRGB = game->lightmapsRGB;
2180 if ( lightmapsRGB ) {
2181 Sys_Printf( " lightmap colorspace: sRGB\n" );
2184 Sys_Printf( " lightmap colorspace: linear\n" );
2187 texturesRGB = game->texturesRGB;
2188 if ( texturesRGB ) {
2189 Sys_Printf( " texture colorspace: sRGB\n" );
2192 Sys_Printf( " texture colorspace: linear\n" );
2195 colorsRGB = game->colorsRGB;
2197 Sys_Printf( " _color colorspace: sRGB\n" );
2200 Sys_Printf( " _color colorspace: linear\n" );
2203 lightmapCompensate = game->lightmapCompensate;
2204 Sys_Printf( " lightning compensation: %f\n", lightmapCompensate );
2206 lightmapExposure = game->lightmapExposure;
2207 Sys_Printf( " lightning exposure: %f\n", lightmapExposure );
2209 gridScale = game->gridScale;
2210 Sys_Printf( " lightgrid scale: %f\n", gridScale );
2212 gridAmbientScale = game->gridAmbientScale;
2213 Sys_Printf( " lightgrid ambient scale: %f\n", gridAmbientScale );
2215 lightAngleHL = game->lightAngleHL;
2216 if ( lightAngleHL ) {
2217 Sys_Printf( " half lambert light angle attenuation enabled \n" );
2220 noStyles = game->noStyles;
2221 if ( noStyles == qtrue ) {
2222 Sys_Printf( " shader lightstyles hack: disabled\n" );
2225 Sys_Printf( " shader lightstyles hack: enabled\n" );
2228 patchShadows = game->patchShadows;
2229 if ( patchShadows == qtrue ) {
2230 Sys_Printf( " patch shadows: enabled\n" );
2233 Sys_Printf( " patch shadows: disabled\n" );
2236 deluxemap = game->deluxeMap;
2237 deluxemode = game->deluxeMode;
2238 if ( deluxemap == qtrue ) {
2240 Sys_Printf( " deluxemapping: enabled with tangentspace deluxemaps\n" );
2243 Sys_Printf( " deluxemapping: enabled with modelspace deluxemaps\n" );
2247 Sys_Printf( " deluxemapping: disabled\n" );
2250 Sys_Printf( "--- ProcessCommandLine ---\n" );
2252 /* process commandline arguments */
2253 for ( i = 1; i < ( argc - 1 ); i++ )
2255 /* lightsource scaling */
2256 if ( !strcmp( argv[ i ], "-point" ) || !strcmp( argv[ i ], "-pointscale" ) ) {
2257 f = atof( argv[ i + 1 ] );
2260 Sys_Printf( "Spherical point (entity) light scaled by %f to %f\n", f, pointScale );
2261 Sys_Printf( "Spot point (entity) light scaled by %f to %f\n", f, spotScale );
2265 else if ( !strcmp( argv[ i ], "-spherical" ) || !strcmp( argv[ i ], "-sphericalscale" ) ) {
2266 f = atof( argv[ i + 1 ] );
2268 Sys_Printf( "Spherical point (entity) light scaled by %f to %f\n", f, pointScale );
2272 else if ( !strcmp( argv[ i ], "-spot" ) || !strcmp( argv[ i ], "-spotscale" ) ) {
2273 f = atof( argv[ i + 1 ] );
2275 Sys_Printf( "Spot point (entity) light scaled by %f to %f\n", f, spotScale );
2279 else if ( !strcmp( argv[ i ], "-area" ) || !strcmp( argv[ i ], "-areascale" ) ) {
2280 f = atof( argv[ i + 1 ] );
2282 Sys_Printf( "Area (shader) light scaled by %f to %f\n", f, areaScale );
2286 else if ( !strcmp( argv[ i ], "-sky" ) || !strcmp( argv[ i ], "-skyscale" ) ) {
2287 f = atof( argv[ i + 1 ] );
2289 Sys_Printf( "Sky/sun light scaled by %f to %f\n", f, skyScale );
2293 else if ( !strcmp( argv[ i ], "-vertexscale" ) ) {
2294 f = atof( argv[ i + 1 ] );
2295 vertexglobalscale *= f;
2296 Sys_Printf( "Vertexlight scaled by %f to %f\n", f, vertexglobalscale );
2300 else if ( !strcmp( argv[ i ], "-backsplash" ) && i < ( argc - 3 ) ) {
2301 f = atof( argv[ i + 1 ] );
2302 g_backsplashFractionScale = f;
2303 Sys_Printf( "Area lights backsplash fraction scaled by %f\n", f, g_backsplashFractionScale );
2304 f = atof( argv[ i + 2 ] );
2305 if ( f >= -900.0f ){
2306 g_backsplashDistance = f;
2307 Sys_Printf( "Area lights backsplash distance set globally to %f\n", f, g_backsplashDistance );
2312 else if ( !strcmp( argv[ i ], "-nolm" ) ) {
2314 Sys_Printf( "No lightmaps yo\n" );
2317 else if ( !strcmp( argv[ i ], "-bouncecolorratio" ) ) {
2318 f = atof( argv[ i + 1 ] );
2319 bounceColorRatio *= f;
2320 if ( bounceColorRatio > 1 ) {
2321 bounceColorRatio = 1;
2323 if ( bounceColorRatio < 0 ) {
2324 bounceColorRatio = 0;
2326 Sys_Printf( "Bounce color ratio set to %f\n", bounceColorRatio );
2330 else if ( !strcmp( argv[ i ], "-bouncescale" ) ) {
2331 f = atof( argv[ i + 1 ] );
2333 Sys_Printf( "Bounce (radiosity) light scaled by %f to %f\n", f, bounceScale );
2337 else if ( !strcmp( argv[ i ], "-scale" ) ) {
2338 f = atof( argv[ i + 1 ] );
2344 Sys_Printf( "All light scaled by %f\n", f );
2348 else if ( !strcmp( argv[ i ], "-gridscale" ) ) {
2349 f = atof( argv[ i + 1 ] );
2350 Sys_Printf( "Grid lightning scaled by %f\n", f );
2355 else if ( !strcmp( argv[ i ], "-gridambientscale" ) ) {
2356 f = atof( argv[ i + 1 ] );
2357 Sys_Printf( "Grid ambient lightning scaled by %f\n", f );
2358 gridAmbientScale *= f;
2362 else if ( !strcmp( argv[ i ], "-griddirectionality" ) ) {
2363 f = atof( argv[ i + 1 ] );
2367 if ( f < gridAmbientDirectionality ) {
2368 gridAmbientDirectionality = f;
2370 Sys_Printf( "Grid directionality is %f\n", f );
2371 gridDirectionality = f;
2375 else if ( !strcmp( argv[ i ], "-gridambientdirectionality" ) ) {
2376 f = atof( argv[ i + 1 ] );
2380 if ( f > gridDirectionality ) {
2381 gridDirectionality = f;
2383 Sys_Printf( "Grid ambient directionality is %f\n", f );
2384 gridAmbientDirectionality = f;
2388 else if ( !strcmp( argv[ i ], "-gamma" ) ) {
2389 f = atof( argv[ i + 1 ] );
2391 Sys_Printf( "Lighting gamma set to %f\n", lightmapGamma );
2395 else if ( !strcmp( argv[ i ], "-sRGBlight" ) ) {
2396 lightmapsRGB = qtrue;
2397 Sys_Printf( "Lighting is in sRGB\n" );
2400 else if ( !strcmp( argv[ i ], "-nosRGBlight" ) ) {
2401 lightmapsRGB = qfalse;
2402 Sys_Printf( "Lighting is linear\n" );
2405 else if ( !strcmp( argv[ i ], "-sRGBtex" ) ) {
2406 texturesRGB = qtrue;
2407 Sys_Printf( "Textures are in sRGB\n" );
2410 else if ( !strcmp( argv[ i ], "-nosRGBtex" ) ) {
2411 texturesRGB = qfalse;
2412 Sys_Printf( "Textures are linear\n" );
2415 else if ( !strcmp( argv[ i ], "-sRGBcolor" ) ) {
2417 Sys_Printf( "Colors are in sRGB\n" );
2420 else if ( !strcmp( argv[ i ], "-nosRGBcolor" ) ) {
2422 Sys_Printf( "Colors are linear\n" );
2425 else if ( !strcmp( argv[ i ], "-sRGB" ) ) {
2426 lightmapsRGB = qtrue;
2427 Sys_Printf( "Lighting is in sRGB\n" );
2428 texturesRGB = qtrue;
2429 Sys_Printf( "Textures are in sRGB\n" );
2431 Sys_Printf( "Colors are in sRGB\n" );
2434 else if ( !strcmp( argv[ i ], "-nosRGB" ) ) {
2435 lightmapsRGB = qfalse;
2436 Sys_Printf( "Lighting is linear\n" );
2437 texturesRGB = qfalse;
2438 Sys_Printf( "Textures are linear\n" );
2440 Sys_Printf( "Colors are linear\n" );
2443 else if ( !strcmp( argv[ i ], "-exposure" ) ) {
2444 f = atof( argv[ i + 1 ] );
2445 lightmapExposure = f;
2446 Sys_Printf( "Lighting exposure set to %f\n", lightmapExposure );
2450 else if ( !strcmp( argv[ i ], "-compensate" ) ) {
2451 f = atof( argv[ i + 1 ] );
2455 lightmapCompensate = f;
2456 Sys_Printf( "Lighting compensation set to 1/%f\n", lightmapCompensate );
2460 /* Lighting brightness */
2461 else if( !strcmp( argv[ i ], "-brightness" ) ){
2462 f = atof( argv[ i + 1 ] );
2463 lightmapBrightness = f;
2464 Sys_Printf( "Lighting brightness set to %f\n", lightmapBrightness );
2468 /* Lighting contrast */
2469 else if( !strcmp( argv[ i ], "-contrast" ) ){
2470 f = atof( argv[ i + 1 ] );
2471 lightmapContrast = f;
2472 if( lightmapContrast > 255 ){
2473 lightmapContrast = 255;
2475 else if( lightmapContrast < -255 ){
2476 lightmapContrast = -255;
2478 Sys_Printf( "Lighting contrast set to %f\n", lightmapContrast );
2480 /* change to factor in range of 0 to 129.5 */
2481 lightmapContrast = ( 259 * ( lightmapContrast + 255 ) ) / ( 255 * ( 259 - lightmapContrast ) );
2484 /* ydnar switches */
2485 else if ( !strcmp( argv[ i ], "-bounce" ) ) {
2486 bounce = atoi( argv[ i + 1 ] );
2490 else if ( bounce > 0 ) {
2491 Sys_Printf( "Radiosity enabled with %d bounce(s)\n", bounce );
2496 else if ( !strcmp( argv[ i ], "-supersample" ) || !strcmp( argv[ i ], "-super" ) ) {
2497 superSample = atoi( argv[ i + 1 ] );
2498 if ( superSample < 1 ) {
2501 else if ( superSample > 1 ) {
2502 Sys_Printf( "Ordered-grid supersampling enabled with %d sample(s) per lightmap texel\n", ( superSample * superSample ) );
2507 else if ( !strcmp( argv[ i ], "-randomsamples" ) ) {
2508 lightRandomSamples = qtrue;
2509 Sys_Printf( "Random sampling enabled\n", lightRandomSamples );
2512 else if ( !strcmp( argv[ i ], "-samples" ) ) {
2513 if ( *argv[i + 1] == '+' ) {
2514 lightSamplesInsist = qtrue;
2517 lightSamplesInsist = qfalse;
2519 lightSamples = atoi( argv[ i + 1 ] );
2520 if ( lightSamples < 1 ) {
2523 else if ( lightSamples > 1 ) {
2524 Sys_Printf( "Adaptive supersampling enabled with %d sample(s) per lightmap texel\n", lightSamples );
2529 else if ( !strcmp( argv[ i ], "-samplessearchboxsize" ) ) {
2530 lightSamplesSearchBoxSize = atoi( argv[ i + 1 ] );
2531 if ( lightSamplesSearchBoxSize <= 0 ) {
2532 lightSamplesSearchBoxSize = 1;
2534 if ( lightSamplesSearchBoxSize > 4 ) {
2535 lightSamplesSearchBoxSize = 4; /* more makes no sense */
2537 else if ( lightSamplesSearchBoxSize != 1 ) {
2538 Sys_Printf( "Adaptive supersampling uses %f times the normal search box size\n", lightSamplesSearchBoxSize );
2543 else if ( !strcmp( argv[ i ], "-filter" ) ) {
2545 Sys_Printf( "Lightmap filtering enabled\n" );
2548 else if ( !strcmp( argv[ i ], "-dark" ) ) {
2550 Sys_Printf( "Dark lightmap seams enabled\n" );
2553 else if ( !strcmp( argv[ i ], "-shadeangle" ) ) {
2554 shadeAngleDegrees = atof( argv[ i + 1 ] );
2555 if ( shadeAngleDegrees < 0.0f ) {
2556 shadeAngleDegrees = 0.0f;
2558 else if ( shadeAngleDegrees > 0.0f ) {
2560 Sys_Printf( "Phong shading enabled with a breaking angle of %f degrees\n", shadeAngleDegrees );
2565 else if ( !strcmp( argv[ i ], "-thresh" ) ) {
2566 subdivideThreshold = atof( argv[ i + 1 ] );
2567 if ( subdivideThreshold < 0 ) {
2568 subdivideThreshold = DEFAULT_SUBDIVIDE_THRESHOLD;
2571 Sys_Printf( "Subdivision threshold set at %.3f\n", subdivideThreshold );
2576 else if ( !strcmp( argv[ i ], "-approx" ) ) {
2577 approximateTolerance = atoi( argv[ i + 1 ] );
2578 if ( approximateTolerance < 0 ) {
2579 approximateTolerance = 0;
2581 else if ( approximateTolerance > 0 ) {
2582 Sys_Printf( "Approximating lightmaps within a byte tolerance of %d\n", approximateTolerance );
2587 else if ( !strcmp( argv[ i ], "-deluxe" ) || !strcmp( argv[ i ], "-deluxemap" ) ) {
2589 Sys_Printf( "Generating deluxemaps for average light direction\n" );
2591 else if ( !strcmp( argv[ i ], "-deluxemode" ) ) {
2592 deluxemode = atoi( argv[ i + 1 ] );
2593 if ( deluxemode == 0 || deluxemode > 1 || deluxemode < 0 ) {
2594 Sys_Printf( "Generating modelspace deluxemaps\n" );
2598 Sys_Printf( "Generating tangentspace deluxemaps\n" );
2602 else if ( !strcmp( argv[ i ], "-nodeluxe" ) || !strcmp( argv[ i ], "-nodeluxemap" ) ) {
2604 Sys_Printf( "Disabling generating of deluxemaps for average light direction\n" );
2606 else if ( !strcmp( argv[ i ], "-external" ) ) {
2607 externalLightmaps = qtrue;
2608 Sys_Printf( "Storing all lightmaps externally\n" );
2611 else if ( !strcmp( argv[ i ], "-externalnames" ) ) {
2612 externalLightmaps = qtrue;
2613 externalLightmapNames = qtrue;
2614 Sys_Printf( "Writing lightstyle shader using external lightmap names\n" );
2617 else if ( !strcmp( argv[ i ], "-lightmapsize" ) ) {
2618 lmCustomSize = atoi( argv[ i + 1 ] );
2620 /* must be a power of 2 and greater than 2 */
2621 if ( ( ( lmCustomSize - 1 ) & lmCustomSize ) || lmCustomSize < 2 ) {
2622 Sys_FPrintf( SYS_WRN, "WARNING: Lightmap size must be a power of 2, greater or equal to 2 pixels.\n" );
2623 lmCustomSize = game->lightmapSize;
2626 Sys_Printf( "Default lightmap size set to %d x %d pixels\n", lmCustomSize, lmCustomSize );
2628 /* enable external lightmaps */
2629 if ( lmCustomSize != game->lightmapSize ) {
2630 externalLightmaps = qtrue;
2631 Sys_Printf( "Storing all lightmaps externally\n" );
2635 else if ( !strcmp( argv[ i ], "-rawlightmapsizelimit" ) ) {
2636 lmLimitSize = atoi( argv[ i + 1 ] );
2639 Sys_Printf( "Raw lightmap size limit set to %d x %d pixels\n", lmLimitSize, lmLimitSize );
2642 else if ( !strcmp( argv[ i ], "-lightmapdir" ) ) {
2643 lmCustomDir = argv[i + 1];
2647 Sys_Printf( "Lightmap directory set to %s\n", lmCustomDir );
2648 externalLightmaps = qtrue;
2649 Sys_Printf( "Storing all lightmaps externally\n" );
2652 /* ydnar: add this to suppress warnings */
2653 else if ( !strcmp( argv[ i ], "-custinfoparms" ) ) {
2654 Sys_Printf( "Custom info parms enabled\n" );
2655 useCustomInfoParms = qtrue;
2658 else if ( !strcmp( argv[ i ], "-wolf" ) ) {
2659 /* -game should already be set */
2661 Sys_Printf( "Enabling Wolf lighting model (linear default)\n" );
2664 else if ( !strcmp( argv[ i ], "-q3" ) ) {
2665 /* -game should already be set */
2667 Sys_Printf( "Enabling Quake 3 lighting model (nonlinear default)\n" );
2670 else if ( !strcmp( argv[ i ], "-extradist" ) ) {
2671 extraDist = atof( argv[ i + 1 ] );
2672 if ( extraDist < 0 ) {
2676 Sys_Printf( "Default extra radius set to %f units\n", extraDist );
2679 else if ( !strcmp( argv[ i ], "-sunonly" ) ) {
2681 Sys_Printf( "Only computing sunlight\n" );
2684 else if ( !strcmp( argv[ i ], "-bounceonly" ) ) {
2686 Sys_Printf( "Storing bounced light (radiosity) only\n" );
2689 else if ( !strcmp( argv[ i ], "-nobouncestore" ) ) {
2690 noBounceStore = qtrue;
2691 Sys_Printf( "Do not store BSP, lightmap and shader files between bounces\n" );
2694 else if ( !strcmp( argv[ i ], "-nocollapse" ) ) {
2696 Sys_Printf( "Identical lightmap collapsing disabled\n" );
2699 else if ( !strcmp( argv[ i ], "-nolightmapsearch" ) ) {
2700 lightmapSearchBlockSize = 1;
2701 Sys_Printf( "No lightmap searching - all lightmaps will be sequential\n" );
2704 else if ( !strcmp( argv[ i ], "-lightmapsearchpower" ) ) {
2705 lightmapMergeSize = ( game->lightmapSize << atoi( argv[i + 1] ) );
2707 Sys_Printf( "Restricted lightmap searching enabled - optimize for lightmap merge power %d (size %d)\n", atoi( argv[i] ), lightmapMergeSize );
2710 else if ( !strcmp( argv[ i ], "-lightmapsearchblocksize" ) ) {
2711 lightmapSearchBlockSize = atoi( argv[i + 1] );
2713 Sys_Printf( "Restricted lightmap searching enabled - block size set to %d\n", lightmapSearchBlockSize );
2716 else if ( !strcmp( argv[ i ], "-shade" ) ) {
2718 Sys_Printf( "Phong shading enabled\n" );
2721 else if ( !strcmp( argv[ i ], "-bouncegrid" ) ) {
2724 Sys_Printf( "Grid lighting with radiosity enabled\n" );
2728 else if ( !strcmp( argv[ i ], "-smooth" ) ) {
2729 lightSamples = EXTRA_SCALE;
2730 Sys_Printf( "The -smooth argument is deprecated, use \"-samples 2\" instead\n" );
2733 else if ( !strcmp( argv[ i ], "-nofastpoint" ) ) {
2735 Sys_Printf( "Automatic fast mode for point lights disabled\n" );
2738 else if ( !strcmp( argv[ i ], "-fast" ) ) {
2742 Sys_Printf( "Fast mode enabled for all area lights\n" );
2745 else if ( !strcmp( argv[ i ], "-faster" ) ) {
2750 Sys_Printf( "Faster mode enabled\n" );
2753 else if ( !strcmp( argv[ i ], "-fastallocate" ) || !strcmp( argv[ i ], "-fastlightmapsearch" ) ) {
2754 fastAllocate = qtrue;
2756 if ( !strcmp( argv[ i ], "-fastlightmapsearch" ) ) {
2757 Sys_Printf( "The -fastlightmapsearch argument is deprecated, use \"-fastallocate\" instead\n" );
2760 Sys_Printf( "Fast lightmap allocation mode enabled\n" );
2764 else if ( !strcmp( argv[ i ], "-slowallocate" ) ) {
2765 fastAllocate = qfalse;
2766 Sys_Printf( "Slow lightmap allocation mode enabled (default)\n" );
2769 else if ( !strcmp( argv[ i ], "-slowallocate" ) ) {
2770 fastAllocate = qfalse;
2771 Sys_Printf( "Slow allocation mode enabled\n" );
2774 else if ( !strcmp( argv[ i ], "-fastgrid" ) ) {
2776 Sys_Printf( "Fast grid lighting enabled\n" );
2779 else if ( !strcmp( argv[ i ], "-fastbounce" ) ) {
2781 Sys_Printf( "Fast bounce mode enabled\n" );
2784 else if ( !strcmp( argv[ i ], "-cheap" ) ) {
2787 Sys_Printf( "Cheap mode enabled\n" );
2790 else if ( !strcmp( argv[ i ], "-cheapgrid" ) ) {
2792 Sys_Printf( "Cheap grid mode enabled\n" );
2795 else if ( !strcmp( argv[ i ], "-normalmap" ) ) {
2797 Sys_Printf( "Storing normal map instead of lightmap\n" );
2800 else if ( !strcmp( argv[ i ], "-trisoup" ) ) {
2802 Sys_Printf( "Converting brush faces to triangle soup\n" );
2805 else if ( !strcmp( argv[ i ], "-debug" ) ) {
2807 Sys_Printf( "Lightmap debugging enabled\n" );
2810 else if ( !strcmp( argv[ i ], "-debugsurfaces" ) || !strcmp( argv[ i ], "-debugsurface" ) ) {
2811 debugSurfaces = qtrue;
2812 Sys_Printf( "Lightmap surface debugging enabled\n" );
2815 else if ( !strcmp( argv[ i ], "-debugunused" ) ) {
2816 debugUnused = qtrue;
2817 Sys_Printf( "Unused luxel debugging enabled\n" );
2820 else if ( !strcmp( argv[ i ], "-debugaxis" ) ) {
2822 Sys_Printf( "Lightmap axis debugging enabled\n" );
2825 else if ( !strcmp( argv[ i ], "-debugcluster" ) ) {
2826 debugCluster = qtrue;
2827 Sys_Printf( "Luxel cluster debugging enabled\n" );
2830 else if ( !strcmp( argv[ i ], "-debugorigin" ) ) {
2831 debugOrigin = qtrue;
2832 Sys_Printf( "Luxel origin debugging enabled\n" );
2835 else if ( !strcmp( argv[ i ], "-debugdeluxe" ) ) {
2837 debugDeluxemap = qtrue;
2838 Sys_Printf( "Deluxemap debugging enabled\n" );
2841 else if ( !strcmp( argv[ i ], "-export" ) ) {
2842 exportLightmaps = qtrue;
2843 Sys_Printf( "Exporting lightmaps\n" );
2846 else if ( !strcmp( argv[ i ], "-notrace" ) ) {
2848 Sys_Printf( "Shadow occlusion disabled\n" );
2850 else if ( !strcmp( argv[ i ], "-patchshadows" ) ) {
2851 patchShadows = qtrue;
2852 Sys_Printf( "Patch shadow casting enabled\n" );
2854 else if ( !strcmp( argv[ i ], "-extra" ) ) {
2855 superSample = EXTRA_SCALE; /* ydnar */
2856 Sys_Printf( "The -extra argument is deprecated, use \"-super 2\" instead\n" );
2858 else if ( !strcmp( argv[ i ], "-extrawide" ) ) {
2859 superSample = EXTRAWIDE_SCALE; /* ydnar */
2860 filter = qtrue; /* ydnar */
2861 Sys_Printf( "The -extrawide argument is deprecated, use \"-filter [-super 2]\" instead\n" );
2863 else if ( !strcmp( argv[ i ], "-samplesize" ) ) {
2864 sampleSize = atoi( argv[ i + 1 ] );
2865 if ( sampleSize < 1 ) {
2869 Sys_Printf( "Default lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
2871 else if ( !strcmp( argv[ i ], "-minsamplesize" ) ) {
2872 minSampleSize = atoi( argv[ i + 1 ] );
2873 if ( minSampleSize < 1 ) {
2877 Sys_Printf( "Minimum lightmap sample size set to %dx%d units\n", minSampleSize, minSampleSize );
2879 else if ( !strcmp( argv[ i ], "-samplescale" ) ) {
2880 sampleScale = atoi( argv[ i + 1 ] );
2882 Sys_Printf( "Lightmaps sample scale set to %d\n", sampleScale );
2884 else if ( !strcmp( argv[ i ], "-debugsamplesize" ) ) {
2885 debugSampleSize = 1;
2886 Sys_Printf( "debugging Lightmaps SampleSize\n" );
2888 else if ( !strcmp( argv[ i ], "-novertex" ) ) {
2889 noVertexLighting = 1;
2890 if ( ( atof( argv[ i + 1 ] ) != 0 ) && ( atof( argv[ i + 1 ] )) < 1 ) {
2891 noVertexLighting = ( atof( argv[ i + 1 ] ) );
2893 Sys_Printf( "Setting vertex lighting globally to %f\n", noVertexLighting );
2896 Sys_Printf( "Disabling vertex lighting\n" );
2899 else if ( !strcmp( argv[ i ], "-nogrid" ) ) {
2900 noGridLighting = qtrue;
2901 Sys_Printf( "Disabling grid lighting\n" );
2903 else if ( !strcmp( argv[ i ], "-border" ) ) {
2904 lightmapBorder = qtrue;
2905 Sys_Printf( "Adding debug border to lightmaps\n" );
2907 else if ( !strcmp( argv[ i ], "-nosurf" ) ) {
2909 Sys_Printf( "Not tracing against surfaces\n" );
2911 else if ( !strcmp( argv[ i ], "-dump" ) ) {
2913 Sys_Printf( "Dumping radiosity lights into numbered prefabs\n" );
2915 else if ( !strcmp( argv[ i ], "-lomem" ) ) {
2917 Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );
2919 else if ( !strcmp( argv[ i ], "-lightsubdiv" ) ) {
2920 defaultLightSubdivide = atoi( argv[ i + 1 ] );
2921 if ( defaultLightSubdivide < 1 ) {
2922 defaultLightSubdivide = 1;
2925 Sys_Printf( "Default light subdivision set to %d\n", defaultLightSubdivide );
2927 else if ( !strcmp( argv[ i ], "-lightanglehl" ) ) {
2928 if ( ( atoi( argv[ i + 1 ] ) != 0 ) != lightAngleHL ) {
2929 lightAngleHL = ( atoi( argv[ i + 1 ] ) != 0 );
2930 if ( lightAngleHL ) {
2931 Sys_Printf( "Enabling half lambert light angle attenuation\n" );
2934 Sys_Printf( "Disabling half lambert light angle attenuation\n" );
2939 else if ( !strcmp( argv[ i ], "-nostyle" ) || !strcmp( argv[ i ], "-nostyles" ) ) {
2941 Sys_Printf( "Disabling lightstyles\n" );
2943 else if ( !strcmp( argv[ i ], "-style" ) || !strcmp( argv[ i ], "-styles" ) ) {
2945 Sys_Printf( "Enabling lightstyles\n" );
2947 else if ( !strcmp( argv[ i ], "-cpma" ) ) {
2949 Sys_Printf( "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
2951 else if ( !strcmp( argv[ i ], "-floodlight" ) ) {
2952 floodlighty = qtrue;
2953 Sys_Printf( "FloodLighting enabled\n" );
2955 else if ( !strcmp( argv[ i ], "-debugnormals" ) ) {
2956 debugnormals = qtrue;
2957 Sys_Printf( "DebugNormals enabled\n" );
2959 else if ( !strcmp( argv[ i ], "-lowquality" ) ) {
2960 floodlight_lowquality = qtrue;
2961 Sys_Printf( "Low Quality FloodLighting enabled\n" );
2964 /* r7: dirtmapping */
2965 else if ( !strcmp( argv[ i ], "-dirty" ) ) {
2967 Sys_Printf( "Dirtmapping enabled\n" );
2969 else if ( !strcmp( argv[ i ], "-dirtdebug" ) || !strcmp( argv[ i ], "-debugdirt" ) ) {
2971 Sys_Printf( "Dirtmap debugging enabled\n" );
2973 else if ( !strcmp( argv[ i ], "-dirtmode" ) ) {
2974 dirtMode = atoi( argv[ i + 1 ] );
2975 if ( dirtMode != 0 && dirtMode != 1 ) {
2978 if ( dirtMode == 1 ) {
2979 Sys_Printf( "Enabling randomized dirtmapping\n" );
2982 Sys_Printf( "Enabling ordered dirtmapping\n" );
2986 else if ( !strcmp( argv[ i ], "-dirtdepth" ) ) {
2987 dirtDepth = atof( argv[ i + 1 ] );
2988 if ( dirtDepth <= 0.0f ) {
2991 Sys_Printf( "Dirtmapping depth set to %.1f\n", dirtDepth );
2994 else if ( !strcmp( argv[ i ], "-dirtscale" ) ) {
2995 dirtScale = atof( argv[ i + 1 ] );
2996 if ( dirtScale <= 0.0f ) {
2999 Sys_Printf( "Dirtmapping scale set to %.1f\n", dirtScale );
3002 else if ( !strcmp( argv[ i ], "-dirtgain" ) ) {
3003 dirtGain = atof( argv[ i + 1 ] );
3004 if ( dirtGain <= 0.0f ) {
3007 Sys_Printf( "Dirtmapping gain set to %.1f\n", dirtGain );
3010 else if ( !strcmp( argv[ i ], "-trianglecheck" ) ) {
3011 lightmapTriangleCheck = qtrue;
3013 else if ( !strcmp( argv[ i ], "-extravisnudge" ) ) {
3014 lightmapExtraVisClusterNudge = qtrue;
3016 else if ( !strcmp( argv[ i ], "-fill" ) ) {
3017 lightmapFill = qtrue;
3018 Sys_Printf( "Filling lightmap colors from surrounding pixels to improve JPEG compression\n" );
3020 else if ( !strcmp( argv[ i ], "-bspfile" ) )
3022 strcpy( BSPFilePath, argv[i + 1] );
3026 Sys_Printf( "Use %s as bsp file\n", BSPFilePath );
3028 else if ( !strcmp( argv[ i ], "-srffile" ) )
3030 strcpy( surfaceFilePath, argv[i + 1] );
3034 Sys_Printf( "Use %s as surface file\n", surfaceFilePath );
3036 /* unhandled args */
3039 Sys_FPrintf( SYS_WRN, "WARNING: Unknown argument \"%s\"\n", argv[ i ] );
3044 /* fix up falloff tolerance for sRGB */
3045 if ( lightmapsRGB ) {
3046 falloffTolerance = Image_LinearFloatFromsRGBFloat( falloffTolerance * ( 1.0 / 255.0 ) ) * 255.0;
3049 /* fix up samples count */
3050 if ( lightRandomSamples ) {
3051 if ( !lightSamplesInsist ) {
3052 /* approximately match -samples in quality */
3053 switch ( lightSamples )
3059 Sys_Printf( "Adaptive supersampling preset enabled with %d random sample(s) per lightmap texel\n", lightSamples );
3065 Sys_Printf( "Adaptive supersampling preset enabled with %d random sample(s) per lightmap texel\n", lightSamples );
3071 Sys_Printf( "Adaptive supersampling preset enabled with %d random sample(s) per lightmap texel\n", lightSamples );
3079 /* fix up lightmap search power */
3080 if ( lightmapMergeSize ) {
3081 lightmapSearchBlockSize = ( lightmapMergeSize / lmCustomSize ) * ( lightmapMergeSize / lmCustomSize );
3082 if ( lightmapSearchBlockSize < 1 ) {
3083 lightmapSearchBlockSize = 1;
3086 Sys_Printf( "Restricted lightmap searching enabled - block size adjusted to %d\n", lightmapSearchBlockSize );
3090 if ( i != ( argc - 1 ) ) {
3091 Error( "usage: q3map -light [options] <bspfile>" );
3094 strcpy( source, ExpandArg( argv[ i ] ) );
3095 StripExtension( source );
3096 DefaultExtension( source, ".map" );
3098 if (!BSPFilePath[0]) {
3099 strcpy( BSPFilePath, ExpandArg( argv[ i ] ) );
3100 StripExtension( BSPFilePath );
3101 DefaultExtension( BSPFilePath, ".bsp" );
3104 if (!surfaceFilePath[0]) {
3105 strcpy( surfaceFilePath, ExpandArg( argv[ i ] ) );
3106 StripExtension( surfaceFilePath );
3107 DefaultExtension( surfaceFilePath, ".srf" );
3110 /* ydnar: set default sample size */
3111 SetDefaultSampleSize( sampleSize );
3113 /* ydnar: handle shaders */
3114 BeginMapShaderFile( BSPFilePath );
3118 Sys_Printf( "Loading %s\n", source );
3120 /* ydnar: load surface file */
3121 LoadSurfaceExtraFile( surfaceFilePath );
3124 LoadBSPFile( BSPFilePath );
3126 /* parse bsp entities */
3129 /* inject command line parameters */
3130 InjectCommandLine( argv, 0, argc - 1 );
3133 value = ValueForKey( &entities[ 0 ], "_keepLights" );
3134 if ( value[ 0 ] != '1' ) {
3135 LoadMapFile( source, qtrue, qfalse );
3138 /* set the entity/model origins and init yDrawVerts */
3141 /* ydnar: set up optimization */
3145 SetupSurfaceLightmaps();
3147 /* initialize the surface facet tracing */
3150 /* light the world */
3151 LightWorld( BSPFilePath, fastAllocate, noBounceStore );
3153 /* write out the bsp */
3155 Sys_Printf( "Writing %s\n", BSPFilePath );
3156 WriteBSPFile( BSPFilePath );
3158 /* ydnar: export lightmaps */
3159 if ( exportLightmaps && !externalLightmaps ) {
3163 /* return to sender */