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 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 /* get drawverts and map first triangle */
1158 MapTriangle( lm, info, dv, mapNonAxial );
1160 /* get drawverts and map second triangle */
1161 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1162 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1163 MapTriangle( lm, info, dv, mapNonAxial );
1178 /* -----------------------------------------------------------------
1179 average and clean up luxel normals
1180 ----------------------------------------------------------------- */
1182 /* walk the luxels */
1183 for( y = 0; y < lm->sh; y++ )
1185 for( x = 0; x < lm->sw; x++ )
1188 luxel = SUPER_LUXEL( 0, x, y );
1189 normal = SUPER_NORMAL( x, y );
1190 cluster = SUPER_CLUSTER( x, y );
1192 /* only look at mapped luxels */
1196 /* the normal data could be the sum of multiple samples */
1197 if( luxel[ 3 ] > 1.0f )
1198 VectorNormalize( normal, normal );
1200 /* mark this luxel as having only one normal */
1205 /* non-planar surfaces stop here */
1206 if( lm->plane == NULL )
1209 /* -----------------------------------------------------------------
1210 map occluded or unuxed luxels
1211 ----------------------------------------------------------------- */
1213 /* walk the luxels */
1214 radius = floor( superSample / 2 );
1215 radius = radius > 0 ? radius : 1.0f;
1217 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1219 for( y = 0; y < lm->sh; y++ )
1221 for( x = 0; x < lm->sw; x++ )
1224 luxel = SUPER_LUXEL( 0, x, y );
1225 normal = SUPER_NORMAL( x, y );
1226 cluster = SUPER_CLUSTER( x, y );
1228 /* only look at unmapped luxels */
1229 if( *cluster != CLUSTER_UNMAPPED )
1232 /* divine a normal and origin from neighboring luxels */
1233 VectorClear( fake.xyz );
1234 VectorClear( fake.normal );
1235 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1236 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1238 for( sy = (y - 1); sy <= (y + 1); sy++ )
1240 if( sy < 0 || sy >= lm->sh )
1243 for( sx = (x - 1); sx <= (x + 1); sx++ )
1245 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1248 /* get neighboring luxel */
1249 luxel = SUPER_LUXEL( 0, sx, sy );
1250 origin = SUPER_ORIGIN( sx, sy );
1251 normal = SUPER_NORMAL( sx, sy );
1252 cluster = SUPER_CLUSTER( sx, sy );
1254 /* only consider luxels mapped in previous passes */
1255 if( *cluster < 0 || luxel[ 0 ] >= pass )
1258 /* add its distinctiveness to our own */
1259 VectorAdd( fake.xyz, origin, fake.xyz );
1260 VectorAdd( fake.normal, normal, fake.normal );
1261 samples += luxel[ 3 ];
1266 if( samples == 0.0f )
1270 VectorDivide( fake.xyz, samples, fake.xyz );
1271 //% VectorDivide( fake.normal, samples, fake.normal );
1272 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1275 /* map the fake vert */
1276 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1281 /* -----------------------------------------------------------------
1282 average and clean up luxel normals
1283 ----------------------------------------------------------------- */
1285 /* walk the luxels */
1286 for( y = 0; y < lm->sh; y++ )
1288 for( x = 0; x < lm->sw; x++ )
1291 luxel = SUPER_LUXEL( 0, x, y );
1292 normal = SUPER_NORMAL( x, y );
1293 cluster = SUPER_CLUSTER( x, y );
1295 /* only look at mapped luxels */
1299 /* the normal data could be the sum of multiple samples */
1300 if( luxel[ 3 ] > 1.0f )
1301 VectorNormalize( normal, normal );
1303 /* mark this luxel as having only one normal */
1311 for( y = 0; y < lm->sh; y++ )
1313 for( x = 0; x < lm->sw; x++ )
1318 cluster = SUPER_CLUSTER( x, y );
1319 origin = SUPER_ORIGIN( x, y );
1320 normal = SUPER_NORMAL( x, y );
1321 luxel = SUPER_LUXEL( x, y );
1326 /* check if within the bounding boxes of all surfaces referenced */
1327 ClearBounds( mins, maxs );
1328 for( n = 0; n < lm->numLightSurfaces; n++ )
1331 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1332 TOL = info->sampleSize + 2;
1333 AddPointToBounds( info->mins, mins, maxs );
1334 AddPointToBounds( info->maxs, mins, maxs );
1335 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1336 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1337 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1342 if( n < lm->numLightSurfaces )
1345 /* report bogus origin */
1346 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1347 rawLightmapNum, x, y, *cluster,
1348 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1349 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1350 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1361 sets up dirtmap (ambient occlusion)
1364 #define DIRT_CONE_ANGLE 88 /* degrees */
1365 #define DIRT_NUM_ANGLE_STEPS 16
1366 #define DIRT_NUM_ELEVATION_STEPS 3
1367 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1369 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1370 static int numDirtVectors = 0;
1372 void SetupDirt( void )
1375 float angle, elevation, angleStep, elevationStep;
1379 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1381 /* calculate angular steps */
1382 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1383 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1387 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1389 /* iterate elevation */
1390 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1392 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1393 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1394 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1399 /* emit some statistics */
1400 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1406 calculates dirt value for a given sample
1409 float DirtForSample( trace_t *trace )
1412 float gatherDirt, outDirt, angle, elevation, ooDepth;
1413 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1419 if( trace == NULL || trace->cluster < 0 )
1424 ooDepth = 1.0f / dirtDepth;
1425 VectorCopy( trace->normal, normal );
1427 /* check if the normal is aligned to the world-up */
1428 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1430 if( normal[ 2 ] == 1.0f )
1432 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1433 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1435 else if( normal[ 2 ] == -1.0f )
1437 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1443 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1444 CrossProduct( normal, worldUp, myRt );
1445 VectorNormalize( myRt, myRt );
1446 CrossProduct( myRt, normal, myUp );
1447 VectorNormalize( myUp, myUp );
1450 /* 1 = random mode, 0 (well everything else) = non-random mode */
1454 for( i = 0; i < numDirtVectors; i++ )
1456 /* get random vector */
1457 angle = Random() * DEG2RAD( 360.0f );
1458 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1459 temp[ 0 ] = cos( angle ) * sin( elevation );
1460 temp[ 1 ] = sin( angle ) * sin( elevation );
1461 temp[ 2 ] = cos( elevation );
1463 /* transform into tangent space */
1464 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1465 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1466 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1469 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1470 SetupTrace( trace );
1476 VectorSubtract( trace->hit, trace->origin, displacement );
1477 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1483 /* iterate through ordered vectors */
1484 for( i = 0; i < numDirtVectors; i++ )
1486 /* transform vector into tangent space */
1487 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1488 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1489 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1492 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1493 SetupTrace( trace );
1499 VectorSubtract( trace->hit, trace->origin, displacement );
1500 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1506 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1507 SetupTrace( trace );
1513 VectorSubtract( trace->hit, trace->origin, displacement );
1514 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1518 if( gatherDirt <= 0.0f )
1521 /* apply gain (does this even do much? heh) */
1522 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1523 if( outDirt > 1.0f )
1527 outDirt *= dirtScale;
1528 if( outDirt > 1.0f )
1531 /* return to sender */
1532 return 1.0f - outDirt;
1539 calculates dirty fraction for each luxel
1542 void DirtyRawLightmap( int rawLightmapNum )
1544 int i, x, y, sx, sy, *cluster;
1545 float *origin, *normal, *dirt, *dirt2, average, samples;
1547 surfaceInfo_t *info;
1552 /* bail if this number exceeds the number of raw lightmaps */
1553 if( rawLightmapNum >= numRawLightmaps )
1557 lm = &rawLightmaps[ rawLightmapNum ];
1560 trace.testOcclusion = qtrue;
1561 trace.forceSunlight = qfalse;
1562 trace.recvShadows = lm->recvShadows;
1563 trace.numSurfaces = lm->numLightSurfaces;
1564 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1565 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1566 trace.testAll = qtrue;
1568 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1569 trace.twoSided = qfalse;
1570 for( i = 0; i < trace.numSurfaces; i++ )
1573 info = &surfaceInfos[ trace.surfaces[ i ] ];
1575 /* check twosidedness */
1576 if( info->si->twoSided )
1578 trace.twoSided = qtrue;
1584 for( i = 0; i < trace.numSurfaces; i++ )
1587 info = &surfaceInfos[ trace.surfaces[ i ] ];
1589 /* check twosidedness */
1590 if( info->si->noDirty )
1598 for( y = 0; y < lm->sh; y++ )
1600 for( x = 0; x < lm->sw; x++ )
1603 cluster = SUPER_CLUSTER( x, y );
1604 origin = SUPER_ORIGIN( x, y );
1605 normal = SUPER_NORMAL( x, y );
1606 dirt = SUPER_DIRT( x, y );
1608 /* set default dirt */
1611 /* only look at mapped luxels */
1615 /* don't apply dirty on this surface */
1623 trace.cluster = *cluster;
1624 VectorCopy( origin, trace.origin );
1625 VectorCopy( normal, trace.normal );
1628 *dirt = DirtForSample( &trace );
1632 /* testing no filtering */
1636 for( y = 0; y < lm->sh; y++ )
1638 for( x = 0; x < lm->sw; x++ )
1641 cluster = SUPER_CLUSTER( x, y );
1642 dirt = SUPER_DIRT( x, y );
1644 /* filter dirt by adjacency to unmapped luxels */
1647 for( sy = (y - 1); sy <= (y + 1); sy++ )
1649 if( sy < 0 || sy >= lm->sh )
1652 for( sx = (x - 1); sx <= (x + 1); sx++ )
1654 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1657 /* get neighboring luxel */
1658 cluster = SUPER_CLUSTER( sx, sy );
1659 dirt2 = SUPER_DIRT( sx, sy );
1660 if( *cluster < 0 || *dirt2 <= 0.0f )
1669 if( samples <= 0.0f )
1674 if( samples <= 0.0f )
1678 *dirt = average / samples;
1687 calculates the pvs cluster, origin, normal of a sub-luxel
1690 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1692 int i, *cluster, *cluster2;
1693 float *origin, *origin2, *normal; //% , *normal2;
1694 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1697 /* calulate x vector */
1698 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1700 cluster = SUPER_CLUSTER( x, y );
1701 origin = SUPER_ORIGIN( x, y );
1702 //% normal = SUPER_NORMAL( x, y );
1703 cluster2 = SUPER_CLUSTER( x + 1, y );
1704 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1705 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1707 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1709 cluster = SUPER_CLUSTER( x - 1, y );
1710 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1711 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1712 cluster2 = SUPER_CLUSTER( x, y );
1713 origin2 = SUPER_ORIGIN( x, y );
1714 //% normal2 = SUPER_NORMAL( x, y );
1717 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1719 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1720 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1722 /* calulate y vector */
1723 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1725 cluster = SUPER_CLUSTER( x, y );
1726 origin = SUPER_ORIGIN( x, y );
1727 //% normal = SUPER_NORMAL( x, y );
1728 cluster2 = SUPER_CLUSTER( x, y + 1 );
1729 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1730 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1732 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1734 cluster = SUPER_CLUSTER( x, y - 1 );
1735 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1736 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1737 cluster2 = SUPER_CLUSTER( x, y );
1738 origin2 = SUPER_ORIGIN( x, y );
1739 //% normal2 = SUPER_NORMAL( x, y );
1742 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1744 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1745 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1747 /* calculate new origin */
1748 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1749 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1750 for( i = 0; i < 3; i++ )
1751 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1754 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1755 if( *sampleCluster < 0 )
1758 /* calculate new normal */
1759 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1760 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1761 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1763 normal = SUPER_NORMAL( x, y );
1764 VectorCopy( normal, sampleNormal );
1772 SubsampleRawLuxel_r()
1773 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1776 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1778 int b, samples, mapped, lighted;
1781 vec3_t origin[ 4 ], normal[ 4 ];
1782 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1783 vec3_t color, total;
1787 if( lightLuxel[ 3 ] >= lightSamples )
1791 VectorClear( total );
1795 /* make 2x2 subsample stamp */
1796 for( b = 0; b < 4; b++ )
1799 VectorCopy( sampleOrigin, origin[ b ] );
1801 /* calculate position */
1802 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1809 /* increment sample count */
1810 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1813 trace->cluster = *cluster;
1814 VectorCopy( origin[ b ], trace->origin );
1815 VectorCopy( normal[ b ], trace->normal );
1819 LightContributionToSample( trace );
1821 /* add to totals (fixme: make contrast function) */
1822 VectorCopy( trace->color, luxel[ b ] );
1823 VectorAdd( total, trace->color, total );
1824 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1828 /* subsample further? */
1829 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1830 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1831 lighted != 0 && lighted != mapped )
1833 for( b = 0; b < 4; b++ )
1835 if( cluster[ b ] < 0 )
1837 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ] );
1842 //% VectorClear( color );
1844 VectorCopy( lightLuxel, color );
1846 for( b = 0; b < 4; b++ )
1848 if( cluster[ b ] < 0 )
1850 VectorAdd( color, luxel[ b ], color );
1858 color[ 0 ] /= samples;
1859 color[ 1 ] /= samples;
1860 color[ 2 ] /= samples;
1863 VectorCopy( color, lightLuxel );
1864 lightLuxel[ 3 ] += 1.0f;
1871 IlluminateRawLightmap()
1872 illuminates the luxels
1875 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1876 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1878 void IlluminateRawLightmap( int rawLightmapNum )
1880 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1881 int *cluster, *cluster2, mapped, lighted, totalLighted;
1883 surfaceInfo_t *info;
1884 qboolean filterColor, filterDir;
1886 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1887 unsigned char *flag;
1888 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1889 vec3_t color, averageColor, averageDir, total, temp, temp2;
1890 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1892 float stackLightLuxels[ STACK_LL_SIZE ];
1895 /* bail if this number exceeds the number of raw lightmaps */
1896 if( rawLightmapNum >= numRawLightmaps )
1900 lm = &rawLightmaps[ rawLightmapNum ];
1903 trace.testOcclusion = !noTrace;
1904 trace.forceSunlight = qfalse;
1905 trace.recvShadows = lm->recvShadows;
1906 trace.numSurfaces = lm->numLightSurfaces;
1907 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1908 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1910 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1911 trace.twoSided = qfalse;
1912 for( i = 0; i < trace.numSurfaces; i++ )
1915 info = &surfaceInfos[ trace.surfaces[ i ] ];
1917 /* check twosidedness */
1918 if( info->si->twoSided )
1920 trace.twoSided = qtrue;
1925 /* create a culled light list for this raw lightmap */
1926 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1928 /* -----------------------------------------------------------------
1930 ----------------------------------------------------------------- */
1933 numLuxelsIlluminated += (lm->sw * lm->sh);
1935 /* test debugging state */
1936 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1938 /* debug fill the luxels */
1939 for( y = 0; y < lm->sh; y++ )
1941 for( x = 0; x < lm->sw; x++ )
1944 cluster = SUPER_CLUSTER( x, y );
1946 /* only fill mapped luxels */
1950 /* get particulars */
1951 luxel = SUPER_LUXEL( 0, x, y );
1952 origin = SUPER_ORIGIN( x, y );
1953 normal = SUPER_NORMAL( x, y );
1955 /* color the luxel with raw lightmap num? */
1957 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1959 /* color the luxel with lightmap axis? */
1960 else if( debugAxis )
1962 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1963 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1964 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1967 /* color the luxel with luxel cluster? */
1968 else if( debugCluster )
1969 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1971 /* color the luxel with luxel origin? */
1972 else if( debugOrigin )
1974 VectorSubtract( lm->maxs, lm->mins, temp );
1975 VectorScale( temp, (1.0f / 255.0f), temp );
1976 VectorSubtract( origin, lm->mins, temp2 );
1977 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
1978 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
1979 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
1982 /* color the luxel with the normal */
1983 else if( normalmap )
1985 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
1986 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
1987 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
1990 /* otherwise clear it */
1992 VectorClear( luxel );
2001 /* allocate temporary per-light luxel storage */
2002 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2003 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2004 lightLuxels = stackLightLuxels;
2006 lightLuxels = safe_malloc( llSize );
2009 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2011 /* set ambient color */
2012 for( y = 0; y < lm->sh; y++ )
2014 for( x = 0; x < lm->sw; x++ )
2017 cluster = SUPER_CLUSTER( x, y );
2018 luxel = SUPER_LUXEL( 0, x, y );
2019 normal = SUPER_NORMAL( x, y );
2020 deluxel = SUPER_DELUXEL( x, y );
2022 /* blacken unmapped clusters */
2024 VectorClear( luxel );
2029 VectorCopy( ambientColor, luxel );
2032 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2034 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2035 if(brightness < 0.00390625f)
2036 brightness = 0.00390625f;
2038 VectorScale( normal, brightness, deluxel );
2045 /* clear styled lightmaps */
2046 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2047 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2049 if( lm->superLuxels[ lightmapNum ] != NULL )
2050 memset( lm->superLuxels[ lightmapNum ], 0, size );
2053 /* debugging code */
2054 //% if( trace.numLights <= 0 )
2055 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2057 /* walk light list */
2058 for( i = 0; i < trace.numLights; i++ )
2061 trace.light = trace.lights[ i ];
2064 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2066 if( lm->styles[ lightmapNum ] == trace.light->style ||
2067 lm->styles[ lightmapNum ] == LS_NONE )
2071 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2072 if( lightmapNum >= MAX_LIGHTMAPS )
2074 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2079 memset( lightLuxels, 0, llSize );
2082 /* determine filter radius */
2083 filterRadius = lm->filterRadius > trace.light->filterRadius
2085 : trace.light->filterRadius;
2086 if( filterRadius < 0.0f )
2087 filterRadius = 0.0f;
2089 /* set luxel filter radius */
2090 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2091 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2092 luxelFilterRadius = 1;
2094 /* allocate sampling flags storage */
2095 if(lightSamples > 1 && luxelFilterRadius == 0)
2097 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2098 if(lm->superFlags == NULL)
2099 lm->superFlags = safe_malloc( size );
2100 memset( (void *) lm->superFlags, 0, size );
2103 /* initial pass, one sample per luxel */
2104 for( y = 0; y < lm->sh; y++ )
2106 for( x = 0; x < lm->sw; x++ )
2109 cluster = SUPER_CLUSTER( x, y );
2113 /* get particulars */
2114 lightLuxel = LIGHT_LUXEL( x, y );
2115 deluxel = SUPER_DELUXEL( x, y );
2116 origin = SUPER_ORIGIN( x, y );
2117 normal = SUPER_NORMAL( x, y );
2118 flag = SUPER_FLAG( x, y );
2121 ////////// 27's temp hack for testing edge clipping ////
2122 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2124 lightLuxel[ 1 ] = 255;
2125 lightLuxel[ 3 ] = 1.0f;
2131 /* set contribution count */
2132 lightLuxel[ 3 ] = 1.0f;
2135 trace.cluster = *cluster;
2136 VectorCopy( origin, trace.origin );
2137 VectorCopy( normal, trace.normal );
2139 /* get light for this sample */
2140 LightContributionToSample( &trace );
2141 VectorCopy( trace.color, lightLuxel );
2143 /* add the contribution to the deluxemap */
2145 VectorAdd( deluxel, trace.directionContribution, deluxel );
2147 /* check for evilness */
2148 if(trace.forceSubsampling && lightSamples > 1 && luxelFilterRadius == 0)
2151 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2154 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2160 /* don't even bother with everything else if nothing was lit */
2161 if( totalLighted == 0 )
2164 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2165 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2166 if( lightSamples > 1 && luxelFilterRadius == 0 )
2169 for( y = 0; y < (lm->sh - 1); y++ )
2171 for( x = 0; x < (lm->sw - 1); x++ )
2176 VectorClear( total );
2178 /* test 2x2 stamp */
2179 for( t = 0; t < 4; t++ )
2181 /* set sample coords */
2182 sx = x + tests[ t ][ 0 ];
2183 sy = y + tests[ t ][ 1 ];
2186 cluster = SUPER_CLUSTER( sx, sy );
2192 flag = SUPER_FLAG( sx, sy );
2193 if(*flag & FLAG_FORCE_SUBSAMPLING)
2195 /* force a lighted/mapped discrepancy so we subsample */
2200 lightLuxel = LIGHT_LUXEL( sx, sy );
2201 VectorAdd( total, lightLuxel, total );
2202 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2206 /* if total color is under a certain amount, then don't bother subsampling */
2207 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2210 /* if all 4 pixels are either in shadow or light, then don't subsample */
2211 if( lighted != 0 && lighted != mapped )
2213 for( t = 0; t < 4; t++ )
2215 /* set sample coords */
2216 sx = x + tests[ t ][ 0 ];
2217 sy = y + tests[ t ][ 1 ];
2220 cluster = SUPER_CLUSTER( sx, sy );
2223 flag = SUPER_FLAG( sx, sy );
2224 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2226 lightLuxel = LIGHT_LUXEL( sx, sy );
2227 origin = SUPER_ORIGIN( sx, sy );
2229 /* only subsample shadowed luxels */
2230 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2234 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel );
2236 *flag |= FLAG_ALREADY_SUBSAMPLED;
2238 /* debug code to colorize subsampled areas to yellow */
2239 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2240 //% VectorSet( luxel, 255, 204, 0 );
2247 /* tertiary pass, apply dirt map (ambient occlusion) */
2251 for( y = 0; y < lm->sh; y++ )
2253 for( x = 0; x < lm->sw; x++ )
2256 cluster = SUPER_CLUSTER( x, y );
2260 /* get particulars */
2261 lightLuxel = LIGHT_LUXEL( x, y );
2262 dirt = SUPER_DIRT( x, y );
2264 /* scale light value */
2265 VectorScale( lightLuxel, *dirt, lightLuxel );
2270 /* allocate sampling lightmap storage */
2271 if( lm->superLuxels[ lightmapNum ] == NULL )
2273 /* allocate sampling lightmap storage */
2274 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2275 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2276 memset( lm->superLuxels[ lightmapNum ], 0, size );
2280 if( lightmapNum > 0 )
2282 lm->styles[ lightmapNum ] = trace.light->style;
2283 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2286 /* copy to permanent luxels */
2287 for( y = 0; y < lm->sh; y++ )
2289 for( x = 0; x < lm->sw; x++ )
2291 /* get cluster and origin */
2292 cluster = SUPER_CLUSTER( x, y );
2295 origin = SUPER_ORIGIN( x, y );
2298 if( luxelFilterRadius )
2301 VectorClear( averageColor );
2304 /* cheaper distance-based filtering */
2305 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2307 if( sy < 0 || sy >= lm->sh )
2310 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2312 if( sx < 0 || sx >= lm->sw )
2315 /* get particulars */
2316 cluster = SUPER_CLUSTER( sx, sy );
2319 lightLuxel = LIGHT_LUXEL( sx, sy );
2322 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2323 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2325 /* scale luxel by filter weight */
2326 VectorScale( lightLuxel, weight, color );
2327 VectorAdd( averageColor, color, averageColor );
2333 if( samples <= 0.0f )
2336 /* scale into luxel */
2337 luxel = SUPER_LUXEL( lightmapNum, x, y );
2340 /* handle negative light */
2341 if( trace.light->flags & LIGHT_NEGATIVE )
2343 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2344 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2345 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2348 /* handle normal light */
2351 luxel[ 0 ] += averageColor[ 0 ] / samples;
2352 luxel[ 1 ] += averageColor[ 1 ] / samples;
2353 luxel[ 2 ] += averageColor[ 2 ] / samples;
2360 /* get particulars */
2361 lightLuxel = LIGHT_LUXEL( x, y );
2362 luxel = SUPER_LUXEL( lightmapNum, x, y );
2364 /* handle negative light */
2365 if( trace.light->flags & LIGHT_NEGATIVE )
2366 VectorScale( averageColor, -1.0f, averageColor );
2371 /* handle negative light */
2372 if( trace.light->flags & LIGHT_NEGATIVE )
2373 VectorSubtract( luxel, lightLuxel, luxel );
2375 /* handle normal light */
2377 VectorAdd( luxel, lightLuxel, luxel );
2383 /* free temporary luxels */
2384 if( lightLuxels != stackLightLuxels )
2385 free( lightLuxels );
2388 /* free light list */
2389 FreeTraceLights( &trace );
2391 /* floodlight pass */
2393 FloodlightIlluminateLightmap(lm);
2397 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2400 if( lm->superLuxels[ lightmapNum ] == NULL )
2403 for( y = 0; y < lm->sh; y++ )
2405 for( x = 0; x < lm->sw; x++ )
2408 cluster = SUPER_CLUSTER( x, y );
2409 //% if( *cluster < 0 )
2412 /* get particulars */
2413 luxel = SUPER_LUXEL( lightmapNum, x, y );
2414 normal = SUPER_NORMAL ( x, y );
2416 luxel[0]=(normal[0]*127)+127;
2417 luxel[1]=(normal[1]*127)+127;
2418 luxel[2]=(normal[2]*127)+127;
2424 /* -----------------------------------------------------------------
2426 ----------------------------------------------------------------- */
2430 /* walk lightmaps */
2431 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2434 if( lm->superLuxels[ lightmapNum ] == NULL )
2437 /* apply dirt to each luxel */
2438 for( y = 0; y < lm->sh; y++ )
2440 for( x = 0; x < lm->sw; x++ )
2443 cluster = SUPER_CLUSTER( x, y );
2444 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2447 /* get particulars */
2448 luxel = SUPER_LUXEL( lightmapNum, x, y );
2449 dirt = SUPER_DIRT( x, y );
2452 VectorScale( luxel, *dirt, luxel );
2456 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2462 /* -----------------------------------------------------------------
2464 ----------------------------------------------------------------- */
2466 /* walk lightmaps */
2467 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2470 if( lm->superLuxels[ lightmapNum ] == NULL )
2473 /* average occluded luxels from neighbors */
2474 for( y = 0; y < lm->sh; y++ )
2476 for( x = 0; x < lm->sw; x++ )
2478 /* get particulars */
2479 cluster = SUPER_CLUSTER( x, y );
2480 luxel = SUPER_LUXEL( lightmapNum, x, y );
2481 deluxel = SUPER_DELUXEL( x, y );
2482 normal = SUPER_NORMAL( x, y );
2484 /* determine if filtering is necessary */
2485 filterColor = qfalse;
2488 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2489 filterColor = qtrue;
2491 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2494 if( !filterColor && !filterDir )
2497 /* choose seed amount */
2498 VectorClear( averageColor );
2499 VectorClear( averageDir );
2502 /* walk 3x3 matrix */
2503 for( sy = (y - 1); sy <= (y + 1); sy++ )
2505 if( sy < 0 || sy >= lm->sh )
2508 for( sx = (x - 1); sx <= (x + 1); sx++ )
2510 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2513 /* get neighbor's particulars */
2514 cluster2 = SUPER_CLUSTER( sx, sy );
2515 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2516 deluxel2 = SUPER_DELUXEL( sx, sy );
2518 /* ignore unmapped/unlit luxels */
2519 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2520 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2523 /* add its distinctiveness to our own */
2524 VectorAdd( averageColor, luxel2, averageColor );
2525 samples += luxel2[ 3 ];
2527 VectorAdd( averageDir, deluxel2, averageDir );
2532 if( samples <= 0.0f )
2535 /* dark lightmap seams */
2538 if( lightmapNum == 0 )
2539 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2546 VectorDivide( averageColor, samples, luxel );
2550 VectorDivide( averageDir, samples, deluxel );
2552 /* set cluster to -3 */
2554 *cluster = CLUSTER_FLOODED;
2562 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2565 if( lm->superLuxels[ lightmapNum ] == NULL )
2567 for( y = 0; y < lm->sh; y++ )
2568 for( x = 0; x < lm->sw; x++ )
2571 cluster = SUPER_CLUSTER( x, y );
2572 luxel = SUPER_LUXEL( lightmapNum, x, y );
2573 deluxel = SUPER_DELUXEL( x, y );
2574 if(!luxel || !deluxel || !cluster)
2576 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2579 else if(*cluster < 0)
2582 // should have neither deluxemap nor lightmap
2584 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2589 // should have both deluxemap and lightmap
2591 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2601 IlluminateVertexes()
2602 light the surface vertexes
2605 #define VERTEX_NUDGE 4.0f
2607 void IlluminateVertexes( int num )
2609 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2610 int lightmapNum, numAvg;
2611 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2612 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2613 bspDrawSurface_t *ds;
2614 surfaceInfo_t *info;
2616 bspDrawVert_t *verts;
2618 float floodLightAmount;
2622 /* get surface, info, and raw lightmap */
2623 ds = &bspDrawSurfaces[ num ];
2624 info = &surfaceInfos[ num ];
2627 /* -----------------------------------------------------------------
2628 illuminate the vertexes
2629 ----------------------------------------------------------------- */
2631 /* calculate vertex lighting for surfaces without lightmaps */
2632 if( lm == NULL || cpmaHack )
2635 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2636 trace.forceSunlight = info->si->forceSunlight;
2637 trace.recvShadows = info->recvShadows;
2638 trace.numSurfaces = 1;
2639 trace.surfaces = #
2640 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2642 /* twosided lighting */
2643 trace.twoSided = info->si->twoSided;
2645 /* make light list for this surface */
2646 CreateTraceLightsForSurface( num, &trace );
2649 verts = yDrawVerts + ds->firstVert;
2651 memset( avgColors, 0, sizeof( avgColors ) );
2653 /* walk the surface verts */
2654 for( i = 0; i < ds->numVerts; i++ )
2656 /* get vertex luxel */
2657 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2659 /* color the luxel with raw lightmap num? */
2661 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2663 /* color the luxel with luxel origin? */
2664 else if( debugOrigin )
2666 VectorSubtract( info->maxs, info->mins, temp );
2667 VectorScale( temp, (1.0f / 255.0f), temp );
2668 VectorSubtract( origin, lm->mins, temp2 );
2669 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2670 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2671 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2674 /* color the luxel with the normal */
2675 else if( normalmap )
2677 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2678 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2679 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2682 /* illuminate the vertex */
2685 /* clear vertex luxel */
2686 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2688 /* try at initial origin */
2689 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2690 if( trace.cluster >= 0 )
2693 VectorCopy( verts[ i ].xyz, trace.origin );
2694 VectorCopy( verts[ i ].normal, trace.normal );
2697 if( dirty && !bouncing )
2698 dirt = DirtForSample( &trace );
2702 /* jal: floodlight */
2703 floodLightAmount = 0.0f;
2704 VectorClear( floodColor );
2705 if( floodlighty && !bouncing )
2707 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2708 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2712 LightingAtSample( &trace, ds->vertexStyles, colors );
2715 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2718 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2720 /* jal: floodlight */
2721 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2724 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2725 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2726 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2730 /* is this sample bright enough? */
2731 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2732 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2733 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2734 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2736 /* nudge the sample point around a bit */
2737 for( x = 0; x < 4; x++ )
2739 /* two's complement 0, 1, -1, 2, -2, etc */
2740 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2742 for( y = 0; y < 4; y++ )
2744 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2746 for( z = 0; z < 4; z++ )
2748 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2751 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2752 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2753 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2755 /* try at nudged origin */
2756 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2757 if( trace.cluster < 0 )
2761 LightingAtSample( &trace, ds->vertexStyles, colors );
2764 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2767 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2769 /* jal: floodlight */
2770 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2773 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2774 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2777 /* bright enough? */
2778 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2779 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2780 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2781 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2788 /* add to average? */
2789 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2790 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2791 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2792 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2795 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2797 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2798 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2803 /* another happy customer */
2804 numVertsIlluminated++;
2807 /* set average color */
2810 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2811 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2815 VectorCopy( ambientColor, avgColors[ 0 ] );
2818 /* clean up and store vertex color */
2819 for( i = 0; i < ds->numVerts; i++ )
2821 /* get vertex luxel */
2822 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2824 /* store average in occluded vertexes */
2825 if( radVertLuxel[ 0 ] < 0.0f )
2827 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2829 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2830 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2833 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2838 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2841 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2842 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2845 if( bouncing || bounce == 0 || !bounceOnly )
2846 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2847 if( !info->si->noVertexLight )
2848 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2852 /* free light list */
2853 FreeTraceLights( &trace );
2855 /* return to sender */
2859 /* -----------------------------------------------------------------
2860 reconstitute vertex lighting from the luxels
2861 ----------------------------------------------------------------- */
2863 /* set styles from lightmap */
2864 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2865 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2867 /* get max search radius */
2869 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2871 /* walk the surface verts */
2872 verts = yDrawVerts + ds->firstVert;
2873 for( i = 0; i < ds->numVerts; i++ )
2875 /* do each lightmap */
2876 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2879 if( lm->superLuxels[ lightmapNum ] == NULL )
2882 /* get luxel coords */
2883 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2884 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2887 else if( x >= lm->sw )
2891 else if( y >= lm->sh )
2894 /* get vertex luxels */
2895 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2896 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2898 /* color the luxel with the normal? */
2901 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2902 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2903 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2906 /* color the luxel with surface num? */
2907 else if( debugSurfaces )
2908 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2910 /* divine color from the superluxels */
2913 /* increasing radius */
2914 VectorClear( radVertLuxel );
2916 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2918 /* sample within radius */
2919 for( sy = (y - radius); sy <= (y + radius); sy++ )
2921 if( sy < 0 || sy >= lm->sh )
2924 for( sx = (x - radius); sx <= (x + radius); sx++ )
2926 if( sx < 0 || sx >= lm->sw )
2929 /* get luxel particulars */
2930 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2931 cluster = SUPER_CLUSTER( sx, sy );
2935 /* testing: must be brigher than ambient color */
2936 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2939 /* add its distinctiveness to our own */
2940 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2941 samples += luxel[ 3 ];
2947 if( samples > 0.0f )
2948 VectorDivide( radVertLuxel, samples, radVertLuxel );
2950 VectorCopy( ambientColor, radVertLuxel );
2953 /* store into floating point storage */
2954 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2955 numVertsIlluminated++;
2957 /* store into bytes (for vertex approximation) */
2958 if( !info->si->noVertexLight )
2959 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2966 /* -------------------------------------------------------------------------------
2968 light optimization (-fast)
2970 creates a list of lights that will affect a surface and stores it in tw
2971 this is to optimize surface lighting by culling out as many of the
2972 lights in the world as possible from further calculation
2974 ------------------------------------------------------------------------------- */
2978 determines opaque brushes in the world and find sky shaders for sunlight calculations
2981 void SetupBrushes( void )
2983 int i, j, b, compileFlags;
2986 bspBrushSide_t *side;
2987 bspShader_t *shader;
2992 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
2995 if( opaqueBrushes == NULL )
2996 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
2999 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3000 numOpaqueBrushes = 0;
3002 /* walk the list of worldspawn brushes */
3003 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3006 b = bspModels[ 0 ].firstBSPBrush + i;
3007 brush = &bspBrushes[ b ];
3009 /* check all sides */
3012 for( j = 0; j < brush->numSides && inside; j++ )
3014 /* do bsp shader calculations */
3015 side = &bspBrushSides[ brush->firstSide + j ];
3016 shader = &bspShaders[ side->shaderNum ];
3018 /* get shader info */
3019 si = ShaderInfoForShader( shader->shader );
3023 /* or together compile flags */
3024 compileFlags |= si->compileFlags;
3027 /* determine if this brush is opaque to light */
3028 if( !(compileFlags & C_TRANSLUCENT) )
3030 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3036 /* emit some statistics */
3037 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3044 determines if two clusters are visible to each other using the PVS
3047 qboolean ClusterVisible( int a, int b )
3049 int portalClusters, leafBytes;
3054 if( a < 0 || b < 0 )
3062 if( numBSPVisBytes <=8 )
3066 portalClusters = ((int *) bspVisBytes)[ 0 ];
3067 leafBytes = ((int*) bspVisBytes)[ 1 ];
3068 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3071 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3080 borrowed from vlight.c
3083 int PointInLeafNum_r( vec3_t point, int nodenum )
3091 while( nodenum >= 0 )
3093 node = &bspNodes[ nodenum ];
3094 plane = &bspPlanes[ node->planeNum ];
3095 dist = DotProduct( point, plane->normal ) - plane->dist;
3097 nodenum = node->children[ 0 ];
3098 else if( dist < -0.1 )
3099 nodenum = node->children[ 1 ];
3102 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3103 if( bspLeafs[ leafnum ].cluster != -1 )
3105 nodenum = node->children[ 1 ];
3109 leafnum = -nodenum - 1;
3117 borrowed from vlight.c
3120 int PointInLeafNum( vec3_t point )
3122 return PointInLeafNum_r( point, 0 );
3128 ClusterVisibleToPoint() - ydnar
3129 returns qtrue if point can "see" cluster
3132 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3137 /* get leafNum for point */
3138 pointCluster = ClusterForPoint( point );
3139 if( pointCluster < 0 )
3143 return ClusterVisible( pointCluster, cluster );
3149 ClusterForPoint() - ydnar
3150 returns the pvs cluster for point
3153 int ClusterForPoint( vec3_t point )
3158 /* get leafNum for point */
3159 leafNum = PointInLeafNum( point );
3163 /* return the cluster */
3164 return bspLeafs[ leafNum ].cluster;
3170 ClusterForPointExt() - ydnar
3171 also takes brushes into account for occlusion testing
3174 int ClusterForPointExt( vec3_t point, float epsilon )
3176 int i, j, b, leafNum, cluster;
3179 int *brushes, numBSPBrushes;
3185 /* get leaf for point */
3186 leafNum = PointInLeafNum( point );
3189 leaf = &bspLeafs[ leafNum ];
3191 /* get the cluster */
3192 cluster = leaf->cluster;
3196 /* transparent leaf, so check point against all brushes in the leaf */
3197 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3198 numBSPBrushes = leaf->numBSPLeafBrushes;
3199 for( i = 0; i < numBSPBrushes; i++ )
3203 if( b > maxOpaqueBrush )
3205 brush = &bspBrushes[ b ];
3206 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3209 /* check point against all planes */
3211 for( j = 0; j < brush->numSides && inside; j++ )
3213 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3214 dot = DotProduct( point, plane->normal );
3220 /* if inside, return bogus cluster */
3225 /* if the point made it this far, it's not inside any opaque brushes */
3232 ClusterForPointExtFilter() - ydnar
3233 adds cluster checking against a list of known valid clusters
3236 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3241 /* get cluster for point */
3242 cluster = ClusterForPointExt( point, epsilon );
3244 /* check if filtering is necessary */
3245 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3249 for( i = 0; i < numClusters; i++ )
3251 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3262 ShaderForPointInLeaf() - ydnar
3263 checks a point against all brushes in a leaf, returning the shader of the brush
3264 also sets the cumulative surface and content flags for the brush hit
3267 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3272 int *brushes, numBSPBrushes;
3275 bspBrushSide_t *side;
3277 bspShader_t *shader;
3278 int allSurfaceFlags, allContentFlags;
3281 /* clear things out first */
3288 leaf = &bspLeafs[ leafNum ];
3290 /* transparent leaf, so check point against all brushes in the leaf */
3291 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3292 numBSPBrushes = leaf->numBSPLeafBrushes;
3293 for( i = 0; i < numBSPBrushes; i++ )
3296 brush = &bspBrushes[ brushes[ i ] ];
3298 /* check point against all planes */
3300 allSurfaceFlags = 0;
3301 allContentFlags = 0;
3302 for( j = 0; j < brush->numSides && inside; j++ )
3304 side = &bspBrushSides[ brush->firstSide + j ];
3305 plane = &bspPlanes[ side->planeNum ];
3306 dot = DotProduct( point, plane->normal );
3312 shader = &bspShaders[ side->shaderNum ];
3313 allSurfaceFlags |= shader->surfaceFlags;
3314 allContentFlags |= shader->contentFlags;
3318 /* handle if inside */
3321 /* if there are desired flags, check for same and continue if they aren't matched */
3322 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3324 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3327 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3328 *surfaceFlags = allSurfaceFlags;
3329 *contentFlags = allContentFlags;
3330 return brush->shaderNum;
3334 /* if the point made it this far, it's not inside any brushes */
3342 chops a bounding box by the plane defined by origin and normal
3343 returns qfalse if the bounds is entirely clipped away
3345 this is not exactly the fastest way to do this...
3348 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3350 /* FIXME: rewrite this so it doesn't use bloody brushes */
3358 calculates each light's effective envelope,
3359 taking into account brightness, type, and pvs.
3362 #define LIGHT_EPSILON 0.125f
3363 #define LIGHT_NUDGE 2.0f
3365 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3367 int i, x, y, z, x1, y1, z1;
3368 light_t *light, *light2, **owner;
3370 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3371 float radius, intensity;
3372 light_t *buckets[ 256 ];
3375 /* early out for weird cases where there are no lights */
3376 if( lights == NULL )
3380 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3384 numCulledLights = 0;
3386 while( *owner != NULL )
3391 /* handle negative lights */
3392 if( light->photons < 0.0f || light->add < 0.0f )
3394 light->photons *= -1.0f;
3395 light->add *= -1.0f;
3396 light->flags |= LIGHT_NEGATIVE;
3400 if( light->type == EMIT_SUN )
3404 light->envelope = MAX_WORLD_COORD * 8.0f;
3405 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3406 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3409 /* everything else */
3412 /* get pvs cluster for light */
3413 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3415 /* invalid cluster? */
3416 if( light->cluster < 0 )
3418 /* nudge the sample point around a bit */
3419 for( x = 0; x < 4; x++ )
3421 /* two's complement 0, 1, -1, 2, -2, etc */
3422 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3424 for( y = 0; y < 4; y++ )
3426 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3428 for( z = 0; z < 4; z++ )
3430 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3433 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3434 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3435 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3437 /* try at nudged origin */
3438 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3439 if( light->cluster < 0 )
3443 VectorCopy( origin, light->origin );
3449 /* only calculate for lights in pvs and outside of opaque brushes */
3450 if( light->cluster >= 0 )
3452 /* set light fast flag */
3454 light->flags |= LIGHT_FAST_TEMP;
3456 light->flags &= ~LIGHT_FAST_TEMP;
3457 if( light->si && light->si->noFast )
3458 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3460 /* clear light envelope */
3461 light->envelope = 0;
3463 /* handle area lights */
3464 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3466 /* ugly hack to calculate extent for area lights, but only done once */
3467 VectorScale( light->normal, -1.0f, dir );
3468 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3472 VectorMA( light->origin, radius, light->normal, origin );
3473 factor = PointToPolygonFormFactor( origin, dir, light->w );
3476 if( (factor * light->add) <= light->falloffTolerance )
3477 light->envelope = radius;
3480 /* check for fast mode */
3481 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3482 light->envelope = MAX_WORLD_COORD * 8.0f;
3487 intensity = light->photons;
3491 if( light->envelope <= 0.0f )
3493 /* solve distance for non-distance lights */
3494 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3495 light->envelope = MAX_WORLD_COORD * 8.0f;
3497 /* solve distance for linear lights */
3498 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3499 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3500 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3503 add = angle * light->photons * linearScale - (dist * light->fade);
3504 T = (light->photons * linearScale) - (dist * light->fade);
3505 T + (dist * light->fade) = (light->photons * linearScale);
3506 dist * light->fade = (light->photons * linearScale) - T;
3507 dist = ((light->photons * linearScale) - T) / light->fade;
3510 /* solve for inverse square falloff */
3512 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3515 add = light->photons / (dist * dist);
3516 T = light->photons / (dist * dist);
3517 T * (dist * dist) = light->photons;
3518 dist = sqrt( light->photons / T );
3522 /* chop radius against pvs */
3525 ClearBounds( mins, maxs );
3527 /* check all leaves */
3528 for( i = 0; i < numBSPLeafs; i++ )
3531 leaf = &bspLeafs[ i ];
3534 if( leaf->cluster < 0 )
3536 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3539 /* add this leafs bbox to the bounds */
3540 VectorCopy( leaf->mins, origin );
3541 AddPointToBounds( origin, mins, maxs );
3542 VectorCopy( leaf->maxs, origin );
3543 AddPointToBounds( origin, mins, maxs );
3546 /* test to see if bounds encompass light */
3547 for( i = 0; i < 3; i++ )
3549 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3551 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3552 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3553 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3554 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3555 AddPointToBounds( light->origin, mins, maxs );
3559 /* chop the bounds by a plane for area lights and spotlights */
3560 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3561 ChopBounds( mins, maxs, light->origin, light->normal );
3564 VectorCopy( mins, light->mins );
3565 VectorCopy( maxs, light->maxs );
3567 /* reflect bounds around light origin */
3568 //% VectorMA( light->origin, -1.0f, origin, origin );
3569 VectorScale( light->origin, 2, origin );
3570 VectorSubtract( origin, maxs, origin );
3571 AddPointToBounds( origin, mins, maxs );
3572 //% VectorMA( light->origin, -1.0f, mins, origin );
3573 VectorScale( light->origin, 2, origin );
3574 VectorSubtract( origin, mins, origin );
3575 AddPointToBounds( origin, mins, maxs );
3577 /* calculate spherical bounds */
3578 VectorSubtract( maxs, light->origin, dir );
3579 radius = (float) VectorLength( dir );
3581 /* if this radius is smaller than the envelope, then set the envelope to it */
3582 if( radius < light->envelope )
3584 light->envelope = radius;
3585 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3588 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3591 /* add grid/surface only check */
3594 if( !(light->flags & LIGHT_GRID) )
3595 light->envelope = 0.0f;
3599 if( !(light->flags & LIGHT_SURFACES) )
3600 light->envelope = 0.0f;
3605 if( light->cluster < 0 || light->envelope <= 0.0f )
3608 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3610 /* delete the light */
3612 *owner = light->next;
3613 if( light->w != NULL )
3620 /* square envelope */
3621 light->envelope2 = (light->envelope * light->envelope);
3623 /* increment light count */
3626 /* set next light */
3627 owner = &((**owner).next);
3630 /* bucket sort lights by style */
3631 memset( buckets, 0, sizeof( buckets ) );
3633 for( light = lights; light != NULL; light = light2 )
3635 /* get next light */
3636 light2 = light->next;
3638 /* filter into correct bucket */
3639 light->next = buckets[ light->style ];
3640 buckets[ light->style ] = light;
3642 /* if any styled light is present, automatically set nocollapse */
3643 if( light->style != LS_NORMAL )
3647 /* filter back into light list */
3649 for( i = 255; i >= 0; i-- )
3652 for( light = buckets[ i ]; light != NULL; light = light2 )
3654 light2 = light->next;
3655 light->next = lights;
3660 /* emit some statistics */
3661 Sys_Printf( "%9d total lights\n", numLights );
3662 Sys_Printf( "%9d culled lights\n", numCulledLights );
3668 CreateTraceLightsForBounds()
3669 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3672 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3676 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3677 float radius, dist, length;
3680 /* potential pre-setup */
3681 if( numLights == 0 )
3682 SetupEnvelopes( qfalse, fast );
3685 //% Sys_Printf( "CTWLFB: (%4.1f %4.1f %4.1f) (%4.1f %4.1f %4.1f)\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );
3687 /* allocate the light list */
3688 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3689 trace->numLights = 0;
3691 /* calculate spherical bounds */
3692 VectorAdd( mins, maxs, origin );
3693 VectorScale( origin, 0.5f, origin );
3694 VectorSubtract( maxs, origin, dir );
3695 radius = (float) VectorLength( dir );
3697 /* get length of normal vector */
3698 if( normal != NULL )
3699 length = VectorLength( normal );
3702 normal = nullVector;
3706 /* test each light and see if it reaches the sphere */
3707 /* note: the attenuation code MUST match LightingAtSample() */
3708 for( light = lights; light; light = light->next )
3710 /* check zero sized envelope */
3711 if( light->envelope <= 0 )
3713 lightsEnvelopeCulled++;
3718 if( !(light->flags & flags) )
3721 /* sunlight skips all this nonsense */
3722 if( light->type != EMIT_SUN )
3728 /* check against pvs cluster */
3729 if( numClusters > 0 && clusters != NULL )
3731 for( i = 0; i < numClusters; i++ )
3733 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3738 if( i == numClusters )
3740 lightsClusterCulled++;
3745 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3746 VectorSubtract( light->origin, origin, dir );
3747 dist = VectorLength( dir );
3748 dist -= light->envelope;
3752 lightsEnvelopeCulled++;
3756 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3759 for( i = 0; i < 3; i++ )
3761 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3766 lightsBoundsCulled++;
3772 /* planar surfaces (except twosided surfaces) have a couple more checks */
3773 if( length > 0.0f && trace->twoSided == qfalse )
3775 /* lights coplanar with a surface won't light it */
3776 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3778 lightsPlaneCulled++;
3782 /* check to see if light is behind the plane */
3783 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3785 lightsPlaneCulled++;
3790 /* add this light */
3791 trace->lights[ trace->numLights++ ] = light;
3794 /* make last night null */
3795 trace->lights[ trace->numLights ] = NULL;
3800 void FreeTraceLights( trace_t *trace )
3802 if( trace->lights != NULL )
3803 free( trace->lights );
3809 CreateTraceLightsForSurface()
3810 creates a list of lights that can potentially affect a drawsurface
3813 void CreateTraceLightsForSurface( int num, trace_t *trace )
3816 vec3_t mins, maxs, normal;
3818 bspDrawSurface_t *ds;
3819 surfaceInfo_t *info;
3826 /* get drawsurface and info */
3827 ds = &bspDrawSurfaces[ num ];
3828 info = &surfaceInfos[ num ];
3830 /* get the mins/maxs for the dsurf */
3831 ClearBounds( mins, maxs );
3832 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3833 for( i = 0; i < ds->numVerts; i++ )
3835 dv = &yDrawVerts[ ds->firstVert + i ];
3836 AddPointToBounds( dv->xyz, mins, maxs );
3837 if( !VectorCompare( dv->normal, normal ) )
3838 VectorClear( normal );
3841 /* create the lights for the bounding box */
3842 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3845 /////////////////////////////////////////////////////////////
3847 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3848 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3849 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3850 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3852 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3853 static int numFloodVectors = 0;
3855 void SetupFloodLight( void )
3858 float angle, elevation, angleStep, elevationStep;
3860 double v1,v2,v3,v4,v5;
3863 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3865 /* calculate angular steps */
3866 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3867 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3871 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3873 /* iterate elevation */
3874 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3876 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3877 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3878 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3883 /* emit some statistics */
3884 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3887 value = ValueForKey( &entities[ 0 ], "_floodlight" );
3889 if( value[ 0 ] != '\0' )
3892 v4=floodlightDistance;
3893 v5=floodlightIntensity;
3895 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3897 floodlightRGB[0]=v1;
3898 floodlightRGB[1]=v2;
3899 floodlightRGB[2]=v3;
3901 if (VectorLength(floodlightRGB)==0)
3903 VectorSet(floodlightRGB,240,240,255);
3909 floodlightDistance=v4;
3910 floodlightIntensity=v5;
3912 floodlighty = qtrue;
3913 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3917 VectorSet(floodlightRGB,240,240,255);
3918 //floodlighty = qtrue;
3919 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3921 VectorNormalize(floodlightRGB,floodlightRGB);
3925 FloodLightForSample()
3926 calculates floodlight value for a given sample
3927 once again, kudos to the dirtmapping coder
3930 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
3936 float gatherLight, outLight;
3937 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
3945 if( trace == NULL || trace->cluster < 0 )
3950 dd = floodLightDistance;
3951 VectorCopy( trace->normal, normal );
3953 /* check if the normal is aligned to the world-up */
3954 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
3956 if( normal[ 2 ] == 1.0f )
3958 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
3959 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
3961 else if( normal[ 2 ] == -1.0f )
3963 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
3964 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
3969 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
3970 CrossProduct( normal, worldUp, myRt );
3971 VectorNormalize( myRt, myRt );
3972 CrossProduct( myRt, normal, myUp );
3973 VectorNormalize( myUp, myUp );
3976 /* vortex: optimise floodLightLowQuality a bit */
3977 if ( floodLightLowQuality == qtrue )
3979 /* iterate through ordered vectors */
3980 for( i = 0; i < numFloodVectors; i++ )
3981 if (rand()%10 != 0 ) continue;
3985 /* iterate through ordered vectors */
3986 for( i = 0; i < numFloodVectors; i++ )
3990 /* transform vector into tangent space */
3991 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
3992 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
3993 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
3996 VectorMA( trace->origin, dd, direction, trace->end );
3998 //VectorMA( trace->origin, 1, direction, trace->origin );
4000 SetupTrace( trace );
4005 if (trace->compileFlags & C_SKY )
4009 else if ( trace->opaque )
4011 VectorSubtract( trace->hit, trace->origin, displacement );
4012 d=VectorLength( displacement );
4014 // d=trace->distance;
4015 //if (d>256) gatherDirt+=1;
4017 if (contribution>1) contribution=1.0f;
4019 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4022 gatherLight+=contribution;
4027 if( gatherLight <= 0.0f )
4035 outLight=gatherLight;
4036 if( outLight > 1.0f )
4039 /* return to sender */
4044 FloodLightRawLightmap
4045 lighttracer style ambient occlusion light hack.
4046 Kudos to the dirtmapping author for most of this source.
4047 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4048 VorteX: fixed problems with deluxemapping
4051 // floodlight pass on a lightmap
4052 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4054 int i, x, y, *cluster;
4055 float *origin, *normal, *floodlight, floodLightAmount;
4056 surfaceInfo_t *info;
4059 // float samples, average, *floodlight2;
4061 memset(&trace,0,sizeof(trace_t));
4064 trace.testOcclusion = qtrue;
4065 trace.forceSunlight = qfalse;
4066 trace.twoSided = qtrue;
4067 trace.recvShadows = lm->recvShadows;
4068 trace.numSurfaces = lm->numLightSurfaces;
4069 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4070 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4071 trace.testAll = qfalse;
4072 trace.distance = 1024;
4074 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4075 //trace.twoSided = qfalse;
4076 for( i = 0; i < trace.numSurfaces; i++ )
4079 info = &surfaceInfos[ trace.surfaces[ i ] ];
4081 /* check twosidedness */
4082 if( info->si->twoSided )
4084 trace.twoSided = qtrue;
4089 /* gather floodlight */
4090 for( y = 0; y < lm->sh; y++ )
4092 for( x = 0; x < lm->sw; x++ )
4095 cluster = SUPER_CLUSTER( x, y );
4096 origin = SUPER_ORIGIN( x, y );
4097 normal = SUPER_NORMAL( x, y );
4098 floodlight = SUPER_FLOODLIGHT( x, y );
4100 /* set default dirt */
4103 /* only look at mapped luxels */
4108 trace.cluster = *cluster;
4109 VectorCopy( origin, trace.origin );
4110 VectorCopy( normal, trace.normal );
4112 /* get floodlight */
4113 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4115 /* add floodlight */
4116 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4117 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4118 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4119 floodlight[3] += floodlightDirectionScale;
4123 /* testing no filtering */
4129 for( y = 0; y < lm->sh; y++ )
4131 for( x = 0; x < lm->sw; x++ )
4134 cluster = SUPER_CLUSTER( x, y );
4135 floodlight = SUPER_FLOODLIGHT(x, y );
4137 /* filter dirt by adjacency to unmapped luxels */
4138 average = *floodlight;
4140 for( sy = (y - 1); sy <= (y + 1); sy++ )
4142 if( sy < 0 || sy >= lm->sh )
4145 for( sx = (x - 1); sx <= (x + 1); sx++ )
4147 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4150 /* get neighboring luxel */
4151 cluster = SUPER_CLUSTER( sx, sy );
4152 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4153 if( *cluster < 0 || *floodlight2 <= 0.0f )
4157 average += *floodlight2;
4162 if( samples <= 0.0f )
4167 if( samples <= 0.0f )
4171 *floodlight = average / samples;
4177 void FloodLightRawLightmap( int rawLightmapNum )
4181 /* bail if this number exceeds the number of raw lightmaps */
4182 if( rawLightmapNum >= numRawLightmaps )
4185 lm = &rawLightmaps[ rawLightmapNum ];
4188 if (floodlighty && floodlightIntensity)
4189 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4192 if (lm->floodlightIntensity)
4194 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4195 numSurfacesFloodlighten += 1;
4199 void FloodlightRawLightmaps()
4201 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4202 numSurfacesFloodlighten = 0;
4203 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4204 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4208 FloodLightIlluminate()
4209 illuminate floodlight into lightmap luxels
4212 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4214 float *luxel, *floodlight, *deluxel, *normal;
4217 int x, y, lightmapNum;
4219 /* walk lightmaps */
4220 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4223 if( lm->superLuxels[ lightmapNum ] == NULL )
4226 /* apply floodlight to each luxel */
4227 for( y = 0; y < lm->sh; y++ )
4229 for( x = 0; x < lm->sw; x++ )
4231 /* get floodlight */
4232 floodlight = SUPER_FLOODLIGHT( x, y );
4233 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4237 cluster = SUPER_CLUSTER( x, y );
4239 /* only process mapped luxels */
4243 /* get particulars */
4244 luxel = SUPER_LUXEL( lightmapNum, x, y );
4245 deluxel = SUPER_DELUXEL( x, y );
4247 /* add to lightmap */
4248 luxel[0]+=floodlight[0];
4249 luxel[1]+=floodlight[1];
4250 luxel[2]+=floodlight[2];
4252 if (luxel[3]==0) luxel[3]=1;
4254 /* add to deluxemap */
4255 if (deluxemap && floodlight[3] > 0)
4259 normal = SUPER_NORMAL( x, y );
4260 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4262 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4263 if(brightness < 0.00390625f)
4264 brightness = 0.00390625f;
4266 VectorScale( normal, brightness, lightvector );
4267 VectorAdd( deluxel, lightvector, deluxel );