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 )
54 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
58 /* make a local copy */
59 VectorScale( color, scale, sample );
62 gamma = 1.0f / lightmapGamma;
63 for( i = 0; i < 3; i++ )
65 /* handle negative light */
66 if( sample[ i ] < 0.0f )
73 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
76 /* clamp with color normalization */
78 if( sample[ 1 ] > max )
80 if( sample[ 2 ] > max )
83 VectorScale( sample, (255.0f / max), sample );
85 /* compensate for ingame overbrighting/bitshifting */
86 VectorScale( sample, (1.0f / lightmapCompensate), sample );
89 colorBytes[ 0 ] = sample[ 0 ];
90 colorBytes[ 1 ] = sample[ 1 ];
91 colorBytes[ 2 ] = sample[ 2 ];
96 /* -------------------------------------------------------------------------------
98 this section deals with phong shading (normal interpolation across brush faces)
100 ------------------------------------------------------------------------------- */
104 smooths together coincident vertex normals across the bsp
107 #define MAX_SAMPLES 256
108 #define THETA_EPSILON 0.000001
109 #define EQUAL_NORMAL_EPSILON 0.01
111 void SmoothNormals( void )
113 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
114 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
115 bspDrawSurface_t *ds;
119 vec3_t average, diff;
120 int indexes[ MAX_SAMPLES ];
121 vec3_t votes[ MAX_SAMPLES ];
124 /* allocate shade angle table */
125 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
126 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
128 /* allocate smoothed table */
129 cs = (numBSPDrawVerts / 8) + 1;
130 smoothed = safe_malloc( cs );
131 memset( smoothed, 0, cs );
133 /* set default shade angle */
134 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
137 /* run through every surface and flag verts belonging to non-lightmapped surfaces
138 and set per-vertex smoothing angle */
139 for( i = 0; i < numBSPDrawSurfaces; i++ )
142 ds = &bspDrawSurfaces[ i ];
144 /* get shader for shade angle */
145 si = surfaceInfos[ i ].si;
146 if( si->shadeAngleDegrees )
147 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
149 shadeAngle = defaultShadeAngle;
150 if( shadeAngle > maxShadeAngle )
151 maxShadeAngle = shadeAngle;
154 for( j = 0; j < ds->numVerts; j++ )
156 f = ds->firstVert + j;
157 shadeAngles[ f ] = shadeAngle;
158 if( ds->surfaceType == MST_TRIANGLE_SOUP )
159 smoothed[ f >> 3 ] |= (1 << (f & 7));
162 /* ydnar: optional force-to-trisoup */
163 if( trisoup && ds->surfaceType == MST_PLANAR )
165 ds->surfaceType = MST_TRIANGLE_SOUP;
166 ds->lightmapNum[ 0 ] = -3;
170 /* bail if no surfaces have a shade angle */
171 if( maxShadeAngle == 0 )
180 start = I_FloatTime();
182 /* go through the list of vertexes */
183 for( i = 0; i < numBSPDrawVerts; i++ )
186 f = 10 * i / numBSPDrawVerts;
190 Sys_Printf( "%i...", f );
193 /* already smoothed? */
194 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
198 VectorClear( average );
202 /* build a table of coincident vertexes */
203 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
205 /* already smoothed? */
206 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
210 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
213 /* use smallest shade angle */
214 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
216 /* check shade angle */
217 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
220 else if( dot < -1.0 )
222 testAngle = acos( dot ) + THETA_EPSILON;
223 if( testAngle >= shadeAngle )
225 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
228 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
230 /* add to the list */
231 indexes[ numVerts++ ] = j;
234 smoothed[ j >> 3 ] |= (1 << (j & 7));
236 /* see if this normal has already been voted */
237 for( k = 0; k < numVotes; k++ )
239 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
240 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
241 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
242 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
246 /* add a new vote? */
247 if( k == numVotes && numVotes < MAX_SAMPLES )
249 VectorAdd( average, bspDrawVerts[ j ].normal, average );
250 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
255 /* don't average for less than 2 verts */
260 if( VectorNormalize( average, average ) > 0 )
263 for( j = 0; j < numVerts; j++ )
264 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
268 /* free the tables */
273 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
278 /* -------------------------------------------------------------------------------
280 this section deals with phong shaded lightmap tracing
282 ------------------------------------------------------------------------------- */
284 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
288 calculates the st tangent vectors for normalmapping
291 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
298 /* calculate barycentric basis for the triangle */
299 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 ]);
300 if( fabs( bb ) < 0.00000001f )
304 for( i = 0; i < numVerts; i++ )
306 /* calculate s tangent vector */
307 s = dv[ i ]->st[ 0 ] + 10.0f;
308 t = dv[ i ]->st[ 1 ];
309 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
310 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
311 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
313 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
314 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
315 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
317 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
318 VectorNormalize( stv[ i ], stv[ i ] );
320 /* calculate t tangent vector */
321 s = dv[ i ]->st[ 0 ];
322 t = dv[ i ]->st[ 1 ] + 10.0f;
323 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
324 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
325 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
327 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
328 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
329 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
331 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
332 VectorNormalize( ttv[ i ], ttv[ i ] );
335 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
336 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
339 /* return to caller */
348 perterbs the normal by the shader's normalmap in tangent space
351 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
358 VectorCopy( dv->normal, pNormal );
360 /* sample normalmap */
361 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
364 /* remap sampled normal from [0,255] to [-1,-1] */
365 for( i = 0; i < 3; i++ )
366 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
368 /* scale tangent vectors and add to original normal */
369 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
370 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
371 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
373 /* renormalize and return */
374 VectorNormalize( pNormal, pNormal );
381 maps a luxel for triangle bv at
385 #define BOGUS_NUDGE -99999.0f
387 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 ] )
389 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
390 float *luxel, *origin, *normal, d, lightmapSampleOffset;
396 static float nudges[][ 2 ] =
398 //%{ 0, 0 }, /* try center first */
399 { -NUDGE, 0 }, /* left */
400 { NUDGE, 0 }, /* right */
401 { 0, NUDGE }, /* up */
402 { 0, -NUDGE }, /* down */
403 { -NUDGE, NUDGE }, /* left/up */
404 { NUDGE, -NUDGE }, /* right/down */
405 { NUDGE, NUDGE }, /* right/up */
406 { -NUDGE, -NUDGE }, /* left/down */
407 { BOGUS_NUDGE, BOGUS_NUDGE }
411 /* find luxel xy coords (fixme: subtract 0.5?) */
412 x = dv->lightmap[ 0 ][ 0 ];
413 y = dv->lightmap[ 0 ][ 1 ];
416 else if( x >= lm->sw )
420 else if( y >= lm->sh )
423 /* set shader and cluster list */
427 numClusters = info->numSurfaceClusters;
428 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
437 /* get luxel, origin, cluster, and normal */
438 luxel = SUPER_LUXEL( 0, x, y );
439 origin = SUPER_ORIGIN( x, y );
440 normal = SUPER_NORMAL( x, y );
441 cluster = SUPER_CLUSTER( x, y );
443 /* don't attempt to remap occluded luxels for planar surfaces */
444 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
447 /* only average the normal for premapped luxels */
448 else if( (*cluster) >= 0 )
450 /* do bumpmap calculations */
452 PerturbNormal( dv, si, pNormal, stv, ttv );
454 VectorCopy( dv->normal, pNormal );
456 /* add the additional normal data */
457 VectorAdd( normal, pNormal, normal );
462 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
466 /* axial lightmap projection */
467 if( lm->vecs != NULL )
469 /* calculate an origin for the sample from the lightmap vectors */
470 VectorCopy( lm->origin, origin );
471 for( i = 0; i < 3; i++ )
473 /* add unless it's the axis, which is taken care of later */
474 if( i == lm->axisNum )
476 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
479 /* project the origin onto the plane */
480 d = DotProduct( origin, plane ) - plane[ 3 ];
481 d /= plane[ lm->axisNum ];
482 origin[ lm->axisNum ] -= d;
485 /* non axial lightmap projection (explicit xyz) */
487 VectorCopy( dv->xyz, origin );
489 /* planar surfaces have precalculated lightmap vectors for nudging */
490 if( lm->plane != NULL )
492 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
493 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
494 VectorCopy( lm->plane, vecs[ 2 ] );
497 /* non-planar surfaces must calculate them */
501 VectorCopy( plane, vecs[ 2 ] );
503 VectorCopy( dv->normal, vecs[ 2 ] );
504 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
507 /* push the origin off the surface a bit */
509 lightmapSampleOffset = si->lightmapSampleOffset;
511 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
512 if( lm->axisNum < 0 )
513 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
514 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
515 origin[ lm->axisNum ] -= lightmapSampleOffset;
517 origin[ lm->axisNum ] += lightmapSampleOffset;
520 pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );
522 /* another retarded hack, storing nudge count in luxel[ 1 ] */
525 /* point in solid? (except in dark mode) */
526 if( pointCluster < 0 && dark == qfalse )
528 /* nudge the the location around */
530 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
532 /* nudge the vector around a bit */
533 for( i = 0; i < 3; i++ )
535 /* set nudged point*/
536 nudged[ i ] = origin[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
540 /* get pvs cluster */
541 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
542 if( pointCluster >= 0 )
543 VectorCopy( nudged, origin );
548 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
549 if( pointCluster < 0 && si != NULL && dark == qfalse )
551 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
552 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
553 if( pointCluster >= 0 )
554 VectorCopy( nudged, origin );
559 if( pointCluster < 0 )
561 (*cluster) = CLUSTER_OCCLUDED;
562 VectorClear( origin );
563 VectorClear( normal );
569 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
571 /* do bumpmap calculations */
573 PerturbNormal( dv, si, pNormal, stv, ttv );
575 VectorCopy( dv->normal, pNormal );
577 /* store the cluster and normal */
578 (*cluster) = pointCluster;
579 VectorCopy( pNormal, normal );
581 /* store explicit mapping pass and implicit mapping pass */
596 recursively subdivides a triangle until its edges are shorter
597 than the distance between two luxels (thanks jc :)
600 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 ] )
602 bspDrawVert_t mid, *dv2[ 3 ];
606 /* map the vertexes */
608 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
609 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
610 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
616 float *a, *b, dx, dy, dist, maxDist;
619 /* find the longest edge and split it */
622 for( i = 0; i < 3; i++ )
625 a = dv[ i ]->lightmap[ 0 ];
626 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
629 dx = a[ 0 ] - b[ 0 ];
630 dy = a[ 1 ] - b[ 1 ];
631 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
641 /* try to early out */
642 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
646 /* split the longest edge and map it */
647 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
648 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );
650 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
651 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
653 /* recurse to first triangle */
654 VectorCopy( dv, dv2 );
656 MapTriangle_r( lm, info, dv2, plane, stv, ttv );
658 /* recurse to second triangle */
659 VectorCopy( dv, dv2 );
660 dv2[ (max + 1) % 3 ] = ∣
661 MapTriangle_r( lm, info, dv2, plane, stv, ttv );
668 seed function for MapTriangle_r()
669 requires a cw ordered triangle
672 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
676 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
679 /* get plane if possible */
680 if( lm->plane != NULL )
682 VectorCopy( lm->plane, plane );
683 plane[ 3 ] = lm->plane[ 3 ];
686 /* otherwise make one from the points */
687 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
690 /* check to see if we need to calculate texture->world tangent vectors */
691 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
702 /* 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 );
707 /* 2002-11-20: prefer axial triangle edges */
710 /* subdivide the triangle */
711 MapTriangle_r( lm, info, dv, plane, stv, ttv );
715 for( i = 0; i < 3; i++ )
718 bspDrawVert_t *dv2[ 3 ];
722 a = dv[ i ]->lightmap[ 0 ];
723 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
725 /* make degenerate triangles for mapping edges */
726 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
729 dv2[ 1 ] = dv[ (i + 1) % 3 ];
730 dv2[ 2 ] = dv[ (i + 1) % 3 ];
732 /* map the degenerate triangle */
733 MapTriangle_r( lm, info, dv2, plane, stv, ttv );
744 recursively subdivides a quad until its edges are shorter
745 than the distance between two luxels
748 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 ] )
750 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
757 float *a, *b, dx, dy, dist, maxDist;
760 /* find the longest edge and split it */
763 for( i = 0; i < 4; i++ )
766 a = dv[ i ]->lightmap[ 0 ];
767 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
770 dx = a[ 0 ] - b[ 0 ];
771 dy = a[ 1 ] - b[ 1 ];
772 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
782 /* try to early out */
783 if( max < 0 || maxDist <= subdivideThreshold )
787 /* we only care about even/odd edges */
790 /* split the longest edges */
791 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
792 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
794 /* map the vertexes */
795 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );
796 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );
801 /* recurse to first quad */
803 dv2[ 1 ] = &mid[ 0 ];
804 dv2[ 2 ] = &mid[ 1 ];
806 MapQuad_r( lm, info, dv2, plane, stv, ttv );
808 /* recurse to second quad */
809 dv2[ 0 ] = &mid[ 0 ];
812 dv2[ 3 ] = &mid[ 1 ];
813 MapQuad_r( lm, info, dv2, plane, stv, ttv );
819 /* recurse to first quad */
822 dv2[ 2 ] = &mid[ 0 ];
823 dv2[ 3 ] = &mid[ 1 ];
824 MapQuad_r( lm, info, dv2, plane, stv, ttv );
826 /* recurse to second quad */
827 dv2[ 0 ] = &mid[ 1 ];
828 dv2[ 1 ] = &mid[ 0 ];
831 MapQuad_r( lm, info, dv2, plane, stv, ttv );
839 seed function for MapQuad_r()
840 requires a cw ordered triangle quad
843 #define QUAD_PLANAR_EPSILON 0.5f
845 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
849 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
852 /* get plane if possible */
853 if( lm->plane != NULL )
855 VectorCopy( lm->plane, plane );
856 plane[ 3 ] = lm->plane[ 3 ];
859 /* otherwise make one from the points */
860 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
863 /* 4th point must fall on the plane */
864 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
865 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
868 /* check to see if we need to calculate texture->world tangent vectors */
869 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
880 /* map the vertexes */
881 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
882 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
883 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
884 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );
886 /* subdivide the quad */
887 MapQuad_r( lm, info, dv, plane, stv, ttv );
895 maps the locations, normals, and pvs clusters for a raw lightmap
898 #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)
900 void MapRawLightmap( int rawLightmapNum )
902 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
903 float *luxel, *origin, *normal, samples, radius, pass;
905 bspDrawSurface_t *ds;
907 mesh_t src, *subdivided, *mesh;
908 bspDrawVert_t *verts, *dv[ 4 ], fake;
911 /* bail if this number exceeds the number of raw lightmaps */
912 if( rawLightmapNum >= numRawLightmaps )
916 lm = &rawLightmaps[ rawLightmapNum ];
918 /* -----------------------------------------------------------------
919 map referenced surfaces onto the raw lightmap
920 ----------------------------------------------------------------- */
922 /* walk the list of surfaces on this raw lightmap */
923 for( n = 0; n < lm->numLightSurfaces; n++ )
925 /* with > 1 surface per raw lightmap, clear occluded */
928 for( y = 0; y < lm->sh; y++ )
930 for( x = 0; x < lm->sw; x++ )
933 cluster = SUPER_CLUSTER( x, y );
935 *cluster = CLUSTER_UNMAPPED;
941 num = lightSurfaces[ lm->firstLightSurface + n ];
942 ds = &bspDrawSurfaces[ num ];
943 info = &surfaceInfos[ num ];
945 /* bail if no lightmap to calculate */
952 /* map the surface onto the lightmap origin/cluster/normal buffers */
953 switch( ds->surfaceType )
957 verts = yDrawVerts + ds->firstVert;
959 /* map the triangles */
960 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
962 for( i = 0; i < ds->numIndexes; i += 3 )
964 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
965 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
966 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
967 MapTriangle( lm, info, dv, mapNonAxial );
973 /* make a mesh from the drawsurf */
974 src.width = ds->patchWidth;
975 src.height = ds->patchHeight;
976 src.verts = &yDrawVerts[ ds->firstVert ];
977 //% subdivided = SubdivideMesh( src, 8, 512 );
978 subdivided = SubdivideMesh2( src, info->patchIterations );
980 /* fit it to the curve and remove colinear verts on rows/columns */
981 PutMeshOnCurve( *subdivided );
982 mesh = RemoveLinearMeshColumnsRows( subdivided );
983 FreeMesh( subdivided );
992 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
993 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
994 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
995 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
999 /* map the mesh quads */
1002 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1004 for( y = 0; y < (mesh->height - 1); y++ )
1006 for( x = 0; x < (mesh->width - 1); x++ )
1009 pw[ 0 ] = x + (y * mesh->width);
1010 pw[ 1 ] = x + ((y + 1) * mesh->width);
1011 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1012 pw[ 3 ] = x + 1 + (y * mesh->width);
1013 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1018 /* get drawverts and map first triangle */
1019 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1020 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1021 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1022 MapTriangle( lm, info, dv, mapNonAxial );
1024 /* get drawverts and map second triangle */
1025 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1026 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1027 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1028 MapTriangle( lm, info, dv, mapNonAxial );
1035 for( y = 0; y < (mesh->height - 1); y++ )
1037 for( x = 0; x < (mesh->width - 1); x++ )
1040 pw[ 0 ] = x + (y * mesh->width);
1041 pw[ 1 ] = x + ((y + 1) * mesh->width);
1042 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1043 pw[ 3 ] = x + 1 + (y * mesh->width);
1049 /* attempt to map quad first */
1050 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1051 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1052 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1053 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1054 if( MapQuad( lm, info, dv ) )
1057 /* get drawverts and map first triangle */
1058 MapTriangle( lm, info, dv, mapNonAxial );
1060 /* get drawverts and map second triangle */
1061 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1062 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1063 MapTriangle( lm, info, dv, mapNonAxial );
1078 /* -----------------------------------------------------------------
1079 average and clean up luxel normals
1080 ----------------------------------------------------------------- */
1082 /* walk the luxels */
1083 for( y = 0; y < lm->sh; y++ )
1085 for( x = 0; x < lm->sw; x++ )
1088 luxel = SUPER_LUXEL( 0, x, y );
1089 normal = SUPER_NORMAL( x, y );
1090 cluster = SUPER_CLUSTER( x, y );
1092 /* only look at mapped luxels */
1096 /* the normal data could be the sum of multiple samples */
1097 if( luxel[ 3 ] > 1.0f )
1098 VectorNormalize( normal, normal );
1100 /* mark this luxel as having only one normal */
1105 /* non-planar surfaces stop here */
1106 if( lm->plane == NULL )
1109 /* -----------------------------------------------------------------
1110 map occluded or unuxed luxels
1111 ----------------------------------------------------------------- */
1113 /* walk the luxels */
1114 radius = floor( superSample / 2 );
1115 radius = radius > 0 ? radius : 1.0f;
1117 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1119 for( y = 0; y < lm->sh; y++ )
1121 for( x = 0; x < lm->sw; x++ )
1124 luxel = SUPER_LUXEL( 0, x, y );
1125 normal = SUPER_NORMAL( x, y );
1126 cluster = SUPER_CLUSTER( x, y );
1128 /* only look at unmapped luxels */
1129 if( *cluster != CLUSTER_UNMAPPED )
1132 /* divine a normal and origin from neighboring luxels */
1133 VectorClear( fake.xyz );
1134 VectorClear( fake.normal );
1135 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1136 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1138 for( sy = (y - 1); sy <= (y + 1); sy++ )
1140 if( sy < 0 || sy >= lm->sh )
1143 for( sx = (x - 1); sx <= (x + 1); sx++ )
1145 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1148 /* get neighboring luxel */
1149 luxel = SUPER_LUXEL( 0, sx, sy );
1150 origin = SUPER_ORIGIN( sx, sy );
1151 normal = SUPER_NORMAL( sx, sy );
1152 cluster = SUPER_CLUSTER( sx, sy );
1154 /* only consider luxels mapped in previous passes */
1155 if( *cluster < 0 || luxel[ 0 ] >= pass )
1158 /* add its distinctiveness to our own */
1159 VectorAdd( fake.xyz, origin, fake.xyz );
1160 VectorAdd( fake.normal, normal, fake.normal );
1161 samples += luxel[ 3 ];
1166 if( samples == 0.0f )
1170 VectorDivide( fake.xyz, samples, fake.xyz );
1171 //% VectorDivide( fake.normal, samples, fake.normal );
1172 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1175 /* map the fake vert */
1176 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
1181 /* -----------------------------------------------------------------
1182 average and clean up luxel normals
1183 ----------------------------------------------------------------- */
1185 /* walk the luxels */
1186 for( y = 0; y < lm->sh; y++ )
1188 for( x = 0; x < lm->sw; x++ )
1191 luxel = SUPER_LUXEL( 0, x, y );
1192 normal = SUPER_NORMAL( x, y );
1193 cluster = SUPER_CLUSTER( x, y );
1195 /* only look at mapped luxels */
1199 /* the normal data could be the sum of multiple samples */
1200 if( luxel[ 3 ] > 1.0f )
1201 VectorNormalize( normal, normal );
1203 /* mark this luxel as having only one normal */
1211 for( y = 0; y < lm->sh; y++ )
1213 for( x = 0; x < lm->sw; x++ )
1218 cluster = SUPER_CLUSTER( x, y );
1219 origin = SUPER_ORIGIN( x, y );
1220 normal = SUPER_NORMAL( x, y );
1221 luxel = SUPER_LUXEL( x, y );
1226 /* check if within the bounding boxes of all surfaces referenced */
1227 ClearBounds( mins, maxs );
1228 for( n = 0; n < lm->numLightSurfaces; n++ )
1231 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1232 TOL = info->sampleSize + 2;
1233 AddPointToBounds( info->mins, mins, maxs );
1234 AddPointToBounds( info->maxs, mins, maxs );
1235 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1236 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1237 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1242 if( n < lm->numLightSurfaces )
1245 /* report bogus origin */
1246 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",
1247 rawLightmapNum, x, y, *cluster,
1248 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1249 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1250 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1261 sets up dirtmap (ambient occlusion)
1264 #define DIRT_CONE_ANGLE 88 /* degrees */
1265 #define DIRT_NUM_ANGLE_STEPS 16
1266 #define DIRT_NUM_ELEVATION_STEPS 3
1267 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1269 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1270 static int numDirtVectors = 0;
1272 void SetupDirt( void )
1275 float angle, elevation, angleStep, elevationStep;
1279 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1281 /* calculate angular steps */
1282 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1283 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1287 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1289 /* iterate elevation */
1290 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1292 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1293 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1294 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1299 /* emit some statistics */
1300 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1306 calculates dirt value for a given sample
1309 float DirtForSample( trace_t *trace )
1312 float gatherDirt, outDirt, angle, elevation, ooDepth;
1313 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1319 if( trace == NULL || trace->cluster < 0 )
1324 ooDepth = 1.0f / dirtDepth;
1325 VectorCopy( trace->normal, normal );
1327 /* check if the normal is aligned to the world-up */
1328 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
1330 if( normal[ 2 ] == 1.0f )
1332 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1333 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1335 else if( normal[ 2 ] == -1.0f )
1337 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1338 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1343 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1344 CrossProduct( normal, worldUp, myRt );
1345 VectorNormalize( myRt, myRt );
1346 CrossProduct( myRt, normal, myUp );
1347 VectorNormalize( myUp, myUp );
1350 /* 1 = random mode, 0 (well everything else) = non-random mode */
1354 for( i = 0; i < numDirtVectors; i++ )
1356 /* get random vector */
1357 angle = Random() * DEG2RAD( 360.0f );
1358 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1359 temp[ 0 ] = cos( angle ) * sin( elevation );
1360 temp[ 1 ] = sin( angle ) * sin( elevation );
1361 temp[ 2 ] = cos( elevation );
1363 /* transform into tangent space */
1364 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1365 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1366 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1369 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1370 SetupTrace( trace );
1376 VectorSubtract( trace->hit, trace->origin, displacement );
1377 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1383 /* iterate through ordered vectors */
1384 for( i = 0; i < numDirtVectors; i++ )
1386 /* transform vector into tangent space */
1387 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1388 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1389 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1392 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1393 SetupTrace( trace );
1399 VectorSubtract( trace->hit, trace->origin, displacement );
1400 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1406 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1407 SetupTrace( trace );
1413 VectorSubtract( trace->hit, trace->origin, displacement );
1414 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1418 if( gatherDirt <= 0.0f )
1421 /* apply gain (does this even do much? heh) */
1422 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1423 if( outDirt > 1.0f )
1427 outDirt *= dirtScale;
1428 if( outDirt > 1.0f )
1431 /* return to sender */
1432 return 1.0f - outDirt;
1439 calculates dirty fraction for each luxel
1442 void DirtyRawLightmap( int rawLightmapNum )
1444 int i, x, y, sx, sy, *cluster;
1445 float *origin, *normal, *dirt, *dirt2, average, samples;
1447 surfaceInfo_t *info;
1451 /* bail if this number exceeds the number of raw lightmaps */
1452 if( rawLightmapNum >= numRawLightmaps )
1456 lm = &rawLightmaps[ rawLightmapNum ];
1459 trace.testOcclusion = qtrue;
1460 trace.forceSunlight = qfalse;
1461 trace.recvShadows = lm->recvShadows;
1462 trace.numSurfaces = lm->numLightSurfaces;
1463 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1464 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1465 trace.testAll = qfalse;
1467 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1468 trace.twoSided = qfalse;
1469 for( i = 0; i < trace.numSurfaces; i++ )
1472 info = &surfaceInfos[ trace.surfaces[ i ] ];
1474 /* check twosidedness */
1475 if( info->si->twoSided )
1477 trace.twoSided = qtrue;
1483 for( y = 0; y < lm->sh; y++ )
1485 for( x = 0; x < lm->sw; x++ )
1488 cluster = SUPER_CLUSTER( x, y );
1489 origin = SUPER_ORIGIN( x, y );
1490 normal = SUPER_NORMAL( x, y );
1491 dirt = SUPER_DIRT( x, y );
1493 /* set default dirt */
1496 /* only look at mapped luxels */
1501 trace.cluster = *cluster;
1502 VectorCopy( origin, trace.origin );
1503 VectorCopy( normal, trace.normal );
1506 *dirt = DirtForSample( &trace );
1510 /* testing no filtering */
1514 for( y = 0; y < lm->sh; y++ )
1516 for( x = 0; x < lm->sw; x++ )
1519 cluster = SUPER_CLUSTER( x, y );
1520 dirt = SUPER_DIRT( x, y );
1522 /* filter dirt by adjacency to unmapped luxels */
1525 for( sy = (y - 1); sy <= (y + 1); sy++ )
1527 if( sy < 0 || sy >= lm->sh )
1530 for( sx = (x - 1); sx <= (x + 1); sx++ )
1532 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1535 /* get neighboring luxel */
1536 cluster = SUPER_CLUSTER( sx, sy );
1537 dirt2 = SUPER_DIRT( sx, sy );
1538 if( *cluster < 0 || *dirt2 <= 0.0f )
1547 if( samples <= 0.0f )
1552 if( samples <= 0.0f )
1556 *dirt = average / samples;
1565 calculates the pvs cluster, origin, normal of a sub-luxel
1568 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1570 int i, *cluster, *cluster2;
1571 float *origin, *origin2, *normal; //% , *normal2;
1572 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1575 /* calulate x vector */
1576 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1578 cluster = SUPER_CLUSTER( x, y );
1579 origin = SUPER_ORIGIN( x, y );
1580 //% normal = SUPER_NORMAL( x, y );
1581 cluster2 = SUPER_CLUSTER( x + 1, y );
1582 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1583 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1585 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1587 cluster = SUPER_CLUSTER( x - 1, y );
1588 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1589 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1590 cluster2 = SUPER_CLUSTER( x, y );
1591 origin2 = SUPER_ORIGIN( x, y );
1592 //% normal2 = SUPER_NORMAL( x, y );
1595 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1597 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1598 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1600 /* calulate y vector */
1601 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1603 cluster = SUPER_CLUSTER( x, y );
1604 origin = SUPER_ORIGIN( x, y );
1605 //% normal = SUPER_NORMAL( x, y );
1606 cluster2 = SUPER_CLUSTER( x, y + 1 );
1607 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1608 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1610 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1612 cluster = SUPER_CLUSTER( x, y - 1 );
1613 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1614 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1615 cluster2 = SUPER_CLUSTER( x, y );
1616 origin2 = SUPER_ORIGIN( x, y );
1617 //% normal2 = SUPER_NORMAL( x, y );
1620 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1622 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1623 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1625 /* calculate new origin */
1626 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1627 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1628 for( i = 0; i < 3; i++ )
1629 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1632 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1633 if( *sampleCluster < 0 )
1636 /* calculate new normal */
1637 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1638 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1639 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1641 normal = SUPER_NORMAL( x, y );
1642 VectorCopy( normal, sampleNormal );
1650 SubsampleRawLuxel_r()
1651 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1654 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1656 int b, samples, mapped, lighted;
1659 vec3_t origin[ 4 ], normal[ 4 ];
1660 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1661 vec3_t color, total;
1665 if( lightLuxel[ 3 ] >= lightSamples )
1669 VectorClear( total );
1673 /* make 2x2 subsample stamp */
1674 for( b = 0; b < 4; b++ )
1677 VectorCopy( sampleOrigin, origin[ b ] );
1679 /* calculate position */
1680 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1687 /* increment sample count */
1688 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1691 trace->cluster = *cluster;
1692 VectorCopy( origin[ b ], trace->origin );
1693 VectorCopy( normal[ b ], trace->normal );
1697 LightContributionToSample( trace );
1699 /* add to totals (fixme: make contrast function) */
1700 VectorCopy( trace->color, luxel[ b ] );
1701 VectorAdd( total, trace->color, total );
1702 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1706 /* subsample further? */
1707 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1708 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1709 lighted != 0 && lighted != mapped )
1711 for( b = 0; b < 4; b++ )
1713 if( cluster[ b ] < 0 )
1715 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );
1720 //% VectorClear( color );
1722 VectorCopy( lightLuxel, color );
1724 for( b = 0; b < 4; b++ )
1726 if( cluster[ b ] < 0 )
1728 VectorAdd( color, luxel[ b ], color );
1736 color[ 0 ] /= samples;
1737 color[ 1 ] /= samples;
1738 color[ 2 ] /= samples;
1741 VectorCopy( color, lightLuxel );
1742 lightLuxel[ 3 ] += 1.0f;
1749 IlluminateRawLightmap()
1750 illuminates the luxels
1753 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1754 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1756 void IlluminateRawLightmap( int rawLightmapNum )
1758 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1759 int *cluster, *cluster2, mapped, lighted, totalLighted;
1761 surfaceInfo_t *info;
1762 qboolean filterColor, filterDir;
1764 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1765 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1766 vec3_t color, averageColor, averageDir, total, temp, temp2;
1767 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1769 float stackLightLuxels[ STACK_LL_SIZE ];
1772 /* bail if this number exceeds the number of raw lightmaps */
1773 if( rawLightmapNum >= numRawLightmaps )
1777 lm = &rawLightmaps[ rawLightmapNum ];
1780 trace.testOcclusion = !noTrace;
1781 trace.forceSunlight = qfalse;
1782 trace.recvShadows = lm->recvShadows;
1783 trace.numSurfaces = lm->numLightSurfaces;
1784 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1785 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1787 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1788 trace.twoSided = qfalse;
1789 for( i = 0; i < trace.numSurfaces; i++ )
1792 info = &surfaceInfos[ trace.surfaces[ i ] ];
1794 /* check twosidedness */
1795 if( info->si->twoSided )
1797 trace.twoSided = qtrue;
1802 /* create a culled light list for this raw lightmap */
1803 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1805 /* -----------------------------------------------------------------
1807 ----------------------------------------------------------------- */
1810 numLuxelsIlluminated += (lm->sw * lm->sh);
1812 /* test debugging state */
1813 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1815 /* debug fill the luxels */
1816 for( y = 0; y < lm->sh; y++ )
1818 for( x = 0; x < lm->sw; x++ )
1821 cluster = SUPER_CLUSTER( x, y );
1823 /* only fill mapped luxels */
1827 /* get particulars */
1828 luxel = SUPER_LUXEL( 0, x, y );
1829 origin = SUPER_ORIGIN( x, y );
1830 normal = SUPER_NORMAL( x, y );
1832 /* color the luxel with raw lightmap num? */
1834 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1836 /* color the luxel with lightmap axis? */
1837 else if( debugAxis )
1839 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1840 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1841 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1844 /* color the luxel with luxel cluster? */
1845 else if( debugCluster )
1846 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1848 /* color the luxel with luxel origin? */
1849 else if( debugOrigin )
1851 VectorSubtract( lm->maxs, lm->mins, temp );
1852 VectorScale( temp, (1.0f / 255.0f), temp );
1853 VectorSubtract( origin, lm->mins, temp2 );
1854 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
1855 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
1856 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
1859 /* color the luxel with the normal */
1860 else if( normalmap )
1862 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
1863 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
1864 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
1867 /* otherwise clear it */
1869 VectorClear( luxel );
1878 /* allocate temporary per-light luxel storage */
1879 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1880 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
1881 lightLuxels = stackLightLuxels;
1883 lightLuxels = safe_malloc( llSize );
1886 //% memset( lm->superLuxels[ 0 ], 0, llSize );
1888 /* set ambient color */
1889 for( y = 0; y < lm->sh; y++ )
1891 for( x = 0; x < lm->sw; x++ )
1894 cluster = SUPER_CLUSTER( x, y );
1895 luxel = SUPER_LUXEL( 0, x, y );
1896 normal = SUPER_NORMAL( x, y );
1897 deluxel = SUPER_DELUXEL( x, y );
1899 /* blacken unmapped clusters */
1901 VectorClear( luxel );
1906 VectorCopy( ambientColor, luxel );
1908 VectorScale( normal, 0.00390625f, deluxel );
1914 /* clear styled lightmaps */
1915 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1916 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1918 if( lm->superLuxels[ lightmapNum ] != NULL )
1919 memset( lm->superLuxels[ lightmapNum ], 0, size );
1922 /* debugging code */
1923 //% if( trace.numLights <= 0 )
1924 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
1926 /* walk light list */
1927 for( i = 0; i < trace.numLights; i++ )
1930 trace.light = trace.lights[ i ];
1933 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1935 if( lm->styles[ lightmapNum ] == trace.light->style ||
1936 lm->styles[ lightmapNum ] == LS_NONE )
1940 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
1941 if( lightmapNum >= MAX_LIGHTMAPS )
1943 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
1948 memset( lightLuxels, 0, llSize );
1951 /* initial pass, one sample per luxel */
1952 for( y = 0; y < lm->sh; y++ )
1954 for( x = 0; x < lm->sw; x++ )
1957 cluster = SUPER_CLUSTER( x, y );
1961 /* get particulars */
1962 lightLuxel = LIGHT_LUXEL( x, y );
1963 deluxel = SUPER_DELUXEL( x, y );
1964 origin = SUPER_ORIGIN( x, y );
1965 normal = SUPER_NORMAL( x, y );
1967 /* set contribution count */
1968 lightLuxel[ 3 ] = 1.0f;
1971 trace.cluster = *cluster;
1972 VectorCopy( origin, trace.origin );
1973 VectorCopy( normal, trace.normal );
1975 /* get light for this sample */
1976 LightContributionToSample( &trace );
1977 VectorCopy( trace.color, lightLuxel );
1980 if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
1983 /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
1986 /* color to grayscale (photoshop rgb weighting) */
1987 brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
1988 brightness *= (1.0 / 255.0);
1989 VectorScale( trace.direction, brightness, trace.direction );
1990 VectorAdd( deluxel, trace.direction, deluxel );
1995 /* don't even bother with everything else if nothing was lit */
1996 if( totalLighted == 0 )
1999 /* determine filter radius */
2000 filterRadius = lm->filterRadius > trace.light->filterRadius
2002 : trace.light->filterRadius;
2003 if( filterRadius < 0.0f )
2004 filterRadius = 0.0f;
2006 /* set luxel filter radius */
2007 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2008 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2009 luxelFilterRadius = 1;
2011 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2012 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2013 if( lightSamples > 1 && luxelFilterRadius == 0 )
2016 for( y = 0; y < (lm->sh - 1); y++ )
2018 for( x = 0; x < (lm->sw - 1); x++ )
2023 VectorClear( total );
2025 /* test 2x2 stamp */
2026 for( t = 0; t < 4; t++ )
2028 /* set sample coords */
2029 sx = x + tests[ t ][ 0 ];
2030 sy = y + tests[ t ][ 1 ];
2033 cluster = SUPER_CLUSTER( sx, sy );
2039 lightLuxel = LIGHT_LUXEL( sx, sy );
2040 VectorAdd( total, lightLuxel, total );
2041 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2045 /* if total color is under a certain amount, then don't bother subsampling */
2046 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2049 /* if all 4 pixels are either in shadow or light, then don't subsample */
2050 if( lighted != 0 && lighted != mapped )
2052 for( t = 0; t < 4; t++ )
2054 /* set sample coords */
2055 sx = x + tests[ t ][ 0 ];
2056 sy = y + tests[ t ][ 1 ];
2059 cluster = SUPER_CLUSTER( sx, sy );
2062 lightLuxel = LIGHT_LUXEL( sx, sy );
2063 origin = SUPER_ORIGIN( sx, sy );
2065 /* only subsample shadowed luxels */
2066 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2070 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
2072 /* debug code to colorize subsampled areas to yellow */
2073 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2074 //% VectorSet( luxel, 255, 204, 0 );
2081 /* tertiary pass, apply dirt map (ambient occlusion) */
2085 for( y = 0; y < lm->sh; y++ )
2087 for( x = 0; x < lm->sw; x++ )
2090 cluster = SUPER_CLUSTER( x, y );
2094 /* get particulars */
2095 lightLuxel = LIGHT_LUXEL( x, y );
2096 dirt = SUPER_DIRT( x, y );
2098 /* scale light value */
2099 VectorScale( lightLuxel, *dirt, lightLuxel );
2104 /* allocate sampling lightmap storage */
2105 if( lm->superLuxels[ lightmapNum ] == NULL )
2107 /* allocate sampling lightmap storage */
2108 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2109 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2110 memset( lm->superLuxels[ lightmapNum ], 0, size );
2114 if( lightmapNum > 0 )
2116 lm->styles[ lightmapNum ] = trace.light->style;
2117 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2120 /* copy to permanent luxels */
2121 for( y = 0; y < lm->sh; y++ )
2123 for( x = 0; x < lm->sw; x++ )
2125 /* get cluster and origin */
2126 cluster = SUPER_CLUSTER( x, y );
2129 origin = SUPER_ORIGIN( x, y );
2132 if( luxelFilterRadius )
2135 VectorClear( averageColor );
2138 /* cheaper distance-based filtering */
2139 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2141 if( sy < 0 || sy >= lm->sh )
2144 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2146 if( sx < 0 || sx >= lm->sw )
2149 /* get particulars */
2150 cluster = SUPER_CLUSTER( sx, sy );
2153 lightLuxel = LIGHT_LUXEL( sx, sy );
2156 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2157 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2159 /* scale luxel by filter weight */
2160 VectorScale( lightLuxel, weight, color );
2161 VectorAdd( averageColor, color, averageColor );
2167 if( samples <= 0.0f )
2170 /* scale into luxel */
2171 luxel = SUPER_LUXEL( lightmapNum, x, y );
2174 /* handle negative light */
2175 if( trace.light->flags & LIGHT_NEGATIVE )
2177 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2178 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2179 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2182 /* handle normal light */
2185 luxel[ 0 ] += averageColor[ 0 ] / samples;
2186 luxel[ 1 ] += averageColor[ 1 ] / samples;
2187 luxel[ 2 ] += averageColor[ 2 ] / samples;
2194 /* get particulars */
2195 lightLuxel = LIGHT_LUXEL( x, y );
2196 luxel = SUPER_LUXEL( lightmapNum, x, y );
2198 /* handle negative light */
2199 if( trace.light->flags & LIGHT_NEGATIVE )
2200 VectorScale( averageColor, -1.0f, averageColor );
2205 /* handle negative light */
2206 if( trace.light->flags & LIGHT_NEGATIVE )
2207 VectorSubtract( luxel, lightLuxel, luxel );
2209 /* handle normal light */
2211 VectorAdd( luxel, lightLuxel, luxel );
2217 /* free temporary luxels */
2218 if( lightLuxels != stackLightLuxels )
2219 free( lightLuxels );
2222 /* free light list */
2223 FreeTraceLights( &trace );
2225 /* -----------------------------------------------------------------
2227 ----------------------------------------------------------------- */
2231 /* walk lightmaps */
2232 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2235 if( lm->superLuxels[ lightmapNum ] == NULL )
2238 /* apply dirt to each luxel */
2239 for( y = 0; y < lm->sh; y++ )
2241 for( x = 0; x < lm->sw; x++ )
2244 cluster = SUPER_CLUSTER( x, y );
2245 //% if( *cluster < 0 )
2248 /* get particulars */
2249 luxel = SUPER_LUXEL( lightmapNum, x, y );
2250 dirt = SUPER_DIRT( x, y );
2253 VectorScale( luxel, *dirt, luxel );
2257 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2263 /* -----------------------------------------------------------------
2265 ----------------------------------------------------------------- */
2267 /* walk lightmaps */
2268 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2271 if( lm->superLuxels[ lightmapNum ] == NULL )
2274 /* average occluded luxels from neighbors */
2275 for( y = 0; y < lm->sh; y++ )
2277 for( x = 0; x < lm->sw; x++ )
2279 /* get particulars */
2280 cluster = SUPER_CLUSTER( x, y );
2281 luxel = SUPER_LUXEL( lightmapNum, x, y );
2282 deluxel = SUPER_DELUXEL( x, y );
2283 normal = SUPER_NORMAL( x, y );
2285 /* determine if filtering is necessary */
2286 filterColor = qfalse;
2289 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2290 filterColor = qtrue;
2291 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2294 if( !filterColor && !filterDir )
2297 /* choose seed amount */
2298 VectorClear( averageColor );
2299 VectorClear( averageDir );
2302 /* walk 3x3 matrix */
2303 for( sy = (y - 1); sy <= (y + 1); sy++ )
2305 if( sy < 0 || sy >= lm->sh )
2308 for( sx = (x - 1); sx <= (x + 1); sx++ )
2310 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2313 /* get neighbor's particulars */
2314 cluster2 = SUPER_CLUSTER( sx, sy );
2315 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2316 deluxel2 = SUPER_DELUXEL( sx, sy );
2318 /* ignore unmapped/unlit luxels */
2319 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2320 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2323 /* add its distinctiveness to our own */
2324 VectorAdd( averageColor, luxel2, averageColor );
2325 samples += luxel2[ 3 ];
2327 VectorAdd( averageDir, deluxel2, averageDir );
2332 if( samples <= 0.0f )
2335 /* dark lightmap seams */
2338 if( lightmapNum == 0 )
2339 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2346 VectorDivide( averageColor, samples, luxel );
2350 VectorDivide( averageDir, samples, deluxel );
2352 /* set cluster to -3 */
2354 *cluster = CLUSTER_FLOODED;
2363 IlluminateVertexes()
2364 light the surface vertexes
2367 #define VERTEX_NUDGE 4.0f
2369 void IlluminateVertexes( int num )
2371 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2372 int lightmapNum, numAvg;
2373 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2374 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2375 bspDrawSurface_t *ds;
2376 surfaceInfo_t *info;
2378 bspDrawVert_t *verts;
2382 /* get surface, info, and raw lightmap */
2383 ds = &bspDrawSurfaces[ num ];
2384 info = &surfaceInfos[ num ];
2387 /* -----------------------------------------------------------------
2388 illuminate the vertexes
2389 ----------------------------------------------------------------- */
2391 /* calculate vertex lighting for surfaces without lightmaps */
2392 if( lm == NULL || cpmaHack )
2395 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2396 trace.forceSunlight = info->si->forceSunlight;
2397 trace.recvShadows = info->recvShadows;
2398 trace.numSurfaces = 1;
2399 trace.surfaces = #
2400 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2402 /* twosided lighting */
2403 trace.twoSided = info->si->twoSided;
2405 /* make light list for this surface */
2406 CreateTraceLightsForSurface( num, &trace );
2409 verts = yDrawVerts + ds->firstVert;
2411 memset( avgColors, 0, sizeof( avgColors ) );
2413 /* walk the surface verts */
2414 for( i = 0; i < ds->numVerts; i++ )
2416 /* get vertex luxel */
2417 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2419 /* color the luxel with raw lightmap num? */
2421 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2423 /* color the luxel with luxel origin? */
2424 else if( debugOrigin )
2426 VectorSubtract( info->maxs, info->mins, temp );
2427 VectorScale( temp, (1.0f / 255.0f), temp );
2428 VectorSubtract( origin, lm->mins, temp2 );
2429 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2430 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2431 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2434 /* color the luxel with the normal */
2435 else if( normalmap )
2437 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2438 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2439 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2442 /* illuminate the vertex */
2445 /* clear vertex luxel */
2446 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2448 /* try at initial origin */
2449 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2450 if( trace.cluster >= 0 )
2453 VectorCopy( verts[ i ].xyz, trace.origin );
2454 VectorCopy( verts[ i ].normal, trace.normal );
2458 dirt = DirtForSample( &trace );
2463 LightingAtSample( &trace, ds->vertexStyles, colors );
2466 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2469 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2472 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2473 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2474 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2478 /* is this sample bright enough? */
2479 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2480 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2481 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2482 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2484 /* nudge the sample point around a bit */
2485 for( x = 0; x < 4; x++ )
2487 /* two's complement 0, 1, -1, 2, -2, etc */
2488 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2490 for( y = 0; y < 4; y++ )
2492 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2494 for( z = 0; z < 4; z++ )
2496 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2499 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2500 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2501 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2503 /* try at nudged origin */
2504 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2505 if( trace.cluster < 0 )
2509 LightingAtSample( &trace, ds->vertexStyles, colors );
2512 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2515 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2518 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2519 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2522 /* bright enough? */
2523 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2524 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2525 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2526 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2533 /* add to average? */
2534 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2535 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2536 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2537 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2540 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2542 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2543 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2548 /* another happy customer */
2549 numVertsIlluminated++;
2552 /* set average color */
2555 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2556 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2560 VectorCopy( ambientColor, avgColors[ 0 ] );
2563 /* clean up and store vertex color */
2564 for( i = 0; i < ds->numVerts; i++ )
2566 /* get vertex luxel */
2567 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2569 /* store average in occluded vertexes */
2570 if( radVertLuxel[ 0 ] < 0.0f )
2572 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2574 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2575 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2578 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2583 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2586 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2587 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2590 if( bouncing || bounce == 0 || !bounceOnly )
2591 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2592 if( !info->si->noVertexLight )
2593 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2597 /* free light list */
2598 FreeTraceLights( &trace );
2600 /* return to sender */
2604 /* -----------------------------------------------------------------
2605 reconstitute vertex lighting from the luxels
2606 ----------------------------------------------------------------- */
2608 /* set styles from lightmap */
2609 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2610 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2612 /* get max search radius */
2614 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2616 /* walk the surface verts */
2617 verts = yDrawVerts + ds->firstVert;
2618 for( i = 0; i < ds->numVerts; i++ )
2620 /* do each lightmap */
2621 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2624 if( lm->superLuxels[ lightmapNum ] == NULL )
2627 /* get luxel coords */
2628 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2629 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2632 else if( x >= lm->sw )
2636 else if( y >= lm->sh )
2639 /* get vertex luxels */
2640 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2641 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2643 /* color the luxel with the normal? */
2646 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2647 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2648 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2651 /* color the luxel with surface num? */
2652 else if( debugSurfaces )
2653 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2655 /* divine color from the superluxels */
2658 /* increasing radius */
2659 VectorClear( radVertLuxel );
2661 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2663 /* sample within radius */
2664 for( sy = (y - radius); sy <= (y + radius); sy++ )
2666 if( sy < 0 || sy >= lm->sh )
2669 for( sx = (x - radius); sx <= (x + radius); sx++ )
2671 if( sx < 0 || sx >= lm->sw )
2674 /* get luxel particulars */
2675 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2676 cluster = SUPER_CLUSTER( sx, sy );
2680 /* testing: must be brigher than ambient color */
2681 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2684 /* add its distinctiveness to our own */
2685 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2686 samples += luxel[ 3 ];
2692 if( samples > 0.0f )
2693 VectorDivide( radVertLuxel, samples, radVertLuxel );
2695 VectorCopy( ambientColor, radVertLuxel );
2698 /* store into floating point storage */
2699 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2700 numVertsIlluminated++;
2702 /* store into bytes (for vertex approximation) */
2703 if( !info->si->noVertexLight )
2704 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2711 /* -------------------------------------------------------------------------------
2713 light optimization (-fast)
2715 creates a list of lights that will affect a surface and stores it in tw
2716 this is to optimize surface lighting by culling out as many of the
2717 lights in the world as possible from further calculation
2719 ------------------------------------------------------------------------------- */
2723 determines opaque brushes in the world and find sky shaders for sunlight calculations
2726 void SetupBrushes( void )
2728 int i, j, b, compileFlags;
2731 bspBrushSide_t *side;
2732 bspShader_t *shader;
2737 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
2740 if( opaqueBrushes == NULL )
2741 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
2744 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
2745 numOpaqueBrushes = 0;
2747 /* walk the list of worldspawn brushes */
2748 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
2751 b = bspModels[ 0 ].firstBSPBrush + i;
2752 brush = &bspBrushes[ b ];
2754 /* check all sides */
2757 for( j = 0; j < brush->numSides && inside; j++ )
2759 /* do bsp shader calculations */
2760 side = &bspBrushSides[ brush->firstSide + j ];
2761 shader = &bspShaders[ side->shaderNum ];
2763 /* get shader info */
2764 si = ShaderInfoForShader( shader->shader );
2768 /* or together compile flags */
2769 compileFlags |= si->compileFlags;
2772 /* determine if this brush is opaque to light */
2773 if( !(compileFlags & C_TRANSLUCENT) )
2775 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
2781 /* emit some statistics */
2782 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
2789 determines if two clusters are visible to each other using the PVS
2792 qboolean ClusterVisible( int a, int b )
2794 int portalClusters, leafBytes;
2799 if( a < 0 || b < 0 )
2807 if( numBSPVisBytes <=8 )
2811 portalClusters = ((int *) bspVisBytes)[ 0 ];
2812 leafBytes = ((int*) bspVisBytes)[ 1 ];
2813 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
2816 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
2825 borrowed from vlight.c
2828 int PointInLeafNum_r( vec3_t point, int nodenum )
2836 while( nodenum >= 0 )
2838 node = &bspNodes[ nodenum ];
2839 plane = &bspPlanes[ node->planeNum ];
2840 dist = DotProduct( point, plane->normal ) - plane->dist;
2842 nodenum = node->children[ 0 ];
2843 else if( dist < -0.1 )
2844 nodenum = node->children[ 1 ];
2847 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
2848 if( bspLeafs[ leafnum ].cluster != -1 )
2850 nodenum = node->children[ 1 ];
2854 leafnum = -nodenum - 1;
2862 borrowed from vlight.c
2865 int PointInLeafNum( vec3_t point )
2867 return PointInLeafNum_r( point, 0 );
2873 ClusterVisibleToPoint() - ydnar
2874 returns qtrue if point can "see" cluster
2877 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
2882 /* get leafNum for point */
2883 pointCluster = ClusterForPoint( point );
2884 if( pointCluster < 0 )
2888 return ClusterVisible( pointCluster, cluster );
2894 ClusterForPoint() - ydnar
2895 returns the pvs cluster for point
2898 int ClusterForPoint( vec3_t point )
2903 /* get leafNum for point */
2904 leafNum = PointInLeafNum( point );
2908 /* return the cluster */
2909 return bspLeafs[ leafNum ].cluster;
2915 ClusterForPointExt() - ydnar
2916 also takes brushes into account for occlusion testing
2919 int ClusterForPointExt( vec3_t point, float epsilon )
2921 int i, j, b, leafNum, cluster;
2924 int *brushes, numBSPBrushes;
2930 /* get leaf for point */
2931 leafNum = PointInLeafNum( point );
2934 leaf = &bspLeafs[ leafNum ];
2936 /* get the cluster */
2937 cluster = leaf->cluster;
2941 /* transparent leaf, so check point against all brushes in the leaf */
2942 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
2943 numBSPBrushes = leaf->numBSPLeafBrushes;
2944 for( i = 0; i < numBSPBrushes; i++ )
2948 if( b > maxOpaqueBrush )
2950 brush = &bspBrushes[ b ];
2951 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
2954 /* check point against all planes */
2956 for( j = 0; j < brush->numSides && inside; j++ )
2958 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
2959 dot = DotProduct( point, plane->normal );
2965 /* if inside, return bogus cluster */
2970 /* if the point made it this far, it's not inside any opaque brushes */
2977 ClusterForPointExtFilter() - ydnar
2978 adds cluster checking against a list of known valid clusters
2981 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
2986 /* get cluster for point */
2987 cluster = ClusterForPointExt( point, epsilon );
2989 /* check if filtering is necessary */
2990 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
2994 for( i = 0; i < numClusters; i++ )
2996 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3007 ShaderForPointInLeaf() - ydnar
3008 checks a point against all brushes in a leaf, returning the shader of the brush
3009 also sets the cumulative surface and content flags for the brush hit
3012 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3017 int *brushes, numBSPBrushes;
3020 bspBrushSide_t *side;
3022 bspShader_t *shader;
3023 int allSurfaceFlags, allContentFlags;
3026 /* clear things out first */
3033 leaf = &bspLeafs[ leafNum ];
3035 /* transparent leaf, so check point against all brushes in the leaf */
3036 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3037 numBSPBrushes = leaf->numBSPLeafBrushes;
3038 for( i = 0; i < numBSPBrushes; i++ )
3041 brush = &bspBrushes[ brushes[ i ] ];
3043 /* check point against all planes */
3045 allSurfaceFlags = 0;
3046 allContentFlags = 0;
3047 for( j = 0; j < brush->numSides && inside; j++ )
3049 side = &bspBrushSides[ brush->firstSide + j ];
3050 plane = &bspPlanes[ side->planeNum ];
3051 dot = DotProduct( point, plane->normal );
3057 shader = &bspShaders[ side->shaderNum ];
3058 allSurfaceFlags |= shader->surfaceFlags;
3059 allContentFlags |= shader->contentFlags;
3063 /* handle if inside */
3066 /* if there are desired flags, check for same and continue if they aren't matched */
3067 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3069 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3072 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3073 *surfaceFlags = allSurfaceFlags;
3074 *contentFlags = allContentFlags;
3075 return brush->shaderNum;
3079 /* if the point made it this far, it's not inside any brushes */
3087 chops a bounding box by the plane defined by origin and normal
3088 returns qfalse if the bounds is entirely clipped away
3090 this is not exactly the fastest way to do this...
3093 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3095 /* FIXME: rewrite this so it doesn't use bloody brushes */
3103 calculates each light's effective envelope,
3104 taking into account brightness, type, and pvs.
3107 #define LIGHT_EPSILON 0.125f
3108 #define LIGHT_NUDGE 2.0f
3110 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3112 int i, x, y, z, x1, y1, z1;
3113 light_t *light, *light2, **owner;
3115 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3116 float radius, intensity;
3117 light_t *buckets[ 256 ];
3120 /* early out for weird cases where there are no lights */
3121 if( lights == NULL )
3125 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3129 numCulledLights = 0;
3131 while( *owner != NULL )
3136 /* handle negative lights */
3137 if( light->photons < 0.0f || light->add < 0.0f )
3139 light->photons *= -1.0f;
3140 light->add *= -1.0f;
3141 light->flags |= LIGHT_NEGATIVE;
3145 if( light->type == EMIT_SUN )
3149 light->envelope = MAX_WORLD_COORD * 8.0f;
3150 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3151 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3154 /* everything else */
3157 /* get pvs cluster for light */
3158 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3160 /* invalid cluster? */
3161 if( light->cluster < 0 )
3163 /* nudge the sample point around a bit */
3164 for( x = 0; x < 4; x++ )
3166 /* two's complement 0, 1, -1, 2, -2, etc */
3167 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3169 for( y = 0; y < 4; y++ )
3171 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3173 for( z = 0; z < 4; z++ )
3175 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3178 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3179 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3180 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3182 /* try at nudged origin */
3183 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3184 if( light->cluster < 0 )
3188 VectorCopy( origin, light->origin );
3194 /* only calculate for lights in pvs and outside of opaque brushes */
3195 if( light->cluster >= 0 )
3197 /* set light fast flag */
3199 light->flags |= LIGHT_FAST_TEMP;
3201 light->flags &= ~LIGHT_FAST_TEMP;
3202 if( light->si && light->si->noFast )
3203 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3205 /* clear light envelope */
3206 light->envelope = 0;
3208 /* handle area lights */
3209 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3211 /* ugly hack to calculate extent for area lights, but only done once */
3212 VectorScale( light->normal, -1.0f, dir );
3213 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3217 VectorMA( light->origin, radius, light->normal, origin );
3218 factor = PointToPolygonFormFactor( origin, dir, light->w );
3221 if( (factor * light->add) <= light->falloffTolerance )
3222 light->envelope = radius;
3225 /* check for fast mode */
3226 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3227 light->envelope = MAX_WORLD_COORD * 8.0f;
3232 intensity = light->photons;
3236 if( light->envelope <= 0.0f )
3238 /* solve distance for non-distance lights */
3239 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3240 light->envelope = MAX_WORLD_COORD * 8.0f;
3242 /* solve distance for linear lights */
3243 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3244 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3245 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3248 add = angle * light->photons * linearScale - (dist * light->fade);
3249 T = (light->photons * linearScale) - (dist * light->fade);
3250 T + (dist * light->fade) = (light->photons * linearScale);
3251 dist * light->fade = (light->photons * linearScale) - T;
3252 dist = ((light->photons * linearScale) - T) / light->fade;
3255 /* solve for inverse square falloff */
3257 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3260 add = light->photons / (dist * dist);
3261 T = light->photons / (dist * dist);
3262 T * (dist * dist) = light->photons;
3263 dist = sqrt( light->photons / T );
3267 /* chop radius against pvs */
3270 ClearBounds( mins, maxs );
3272 /* check all leaves */
3273 for( i = 0; i < numBSPLeafs; i++ )
3276 leaf = &bspLeafs[ i ];
3279 if( leaf->cluster < 0 )
3281 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3284 /* add this leafs bbox to the bounds */
3285 VectorCopy( leaf->mins, origin );
3286 AddPointToBounds( origin, mins, maxs );
3287 VectorCopy( leaf->maxs, origin );
3288 AddPointToBounds( origin, mins, maxs );
3291 /* test to see if bounds encompass light */
3292 for( i = 0; i < 3; i++ )
3294 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3296 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3297 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3298 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3299 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3300 AddPointToBounds( light->origin, mins, maxs );
3304 /* chop the bounds by a plane for area lights and spotlights */
3305 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3306 ChopBounds( mins, maxs, light->origin, light->normal );
3309 VectorCopy( mins, light->mins );
3310 VectorCopy( maxs, light->maxs );
3312 /* reflect bounds around light origin */
3313 //% VectorMA( light->origin, -1.0f, origin, origin );
3314 VectorScale( light->origin, 2, origin );
3315 VectorSubtract( origin, maxs, origin );
3316 AddPointToBounds( origin, mins, maxs );
3317 //% VectorMA( light->origin, -1.0f, mins, origin );
3318 VectorScale( light->origin, 2, origin );
3319 VectorSubtract( origin, mins, origin );
3320 AddPointToBounds( origin, mins, maxs );
3322 /* calculate spherical bounds */
3323 VectorSubtract( maxs, light->origin, dir );
3324 radius = (float) VectorLength( dir );
3326 /* if this radius is smaller than the envelope, then set the envelope to it */
3327 if( radius < light->envelope )
3329 light->envelope = radius;
3330 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3333 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3336 /* add grid/surface only check */
3339 if( !(light->flags & LIGHT_GRID) )
3340 light->envelope = 0.0f;
3344 if( !(light->flags & LIGHT_SURFACES) )
3345 light->envelope = 0.0f;
3350 if( light->cluster < 0 || light->envelope <= 0.0f )
3353 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3355 /* delete the light */
3357 *owner = light->next;
3358 if( light->w != NULL )
3365 /* square envelope */
3366 light->envelope2 = (light->envelope * light->envelope);
3368 /* increment light count */
3371 /* set next light */
3372 owner = &((**owner).next);
3375 /* bucket sort lights by style */
3376 memset( buckets, 0, sizeof( buckets ) );
3378 for( light = lights; light != NULL; light = light2 )
3380 /* get next light */
3381 light2 = light->next;
3383 /* filter into correct bucket */
3384 light->next = buckets[ light->style ];
3385 buckets[ light->style ] = light;
3387 /* if any styled light is present, automatically set nocollapse */
3388 if( light->style != LS_NORMAL )
3392 /* filter back into light list */
3394 for( i = 255; i >= 0; i-- )
3397 for( light = buckets[ i ]; light != NULL; light = light2 )
3399 light2 = light->next;
3400 light->next = lights;
3405 /* emit some statistics */
3406 Sys_Printf( "%9d total lights\n", numLights );
3407 Sys_Printf( "%9d culled lights\n", numCulledLights );
3413 CreateTraceLightsForBounds()
3414 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3417 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3421 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3422 float radius, dist, length;
3425 /* potential pre-setup */
3426 if( numLights == 0 )
3427 SetupEnvelopes( qfalse, fast );
3430 //% 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 ] );
3432 /* allocate the light list */
3433 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3434 trace->numLights = 0;
3436 /* calculate spherical bounds */
3437 VectorAdd( mins, maxs, origin );
3438 VectorScale( origin, 0.5f, origin );
3439 VectorSubtract( maxs, origin, dir );
3440 radius = (float) VectorLength( dir );
3442 /* get length of normal vector */
3443 if( normal != NULL )
3444 length = VectorLength( normal );
3447 normal = nullVector;
3451 /* test each light and see if it reaches the sphere */
3452 /* note: the attenuation code MUST match LightingAtSample() */
3453 for( light = lights; light; light = light->next )
3455 /* check zero sized envelope */
3456 if( light->envelope <= 0 )
3458 lightsEnvelopeCulled++;
3463 if( !(light->flags & flags) )
3466 /* sunlight skips all this nonsense */
3467 if( light->type != EMIT_SUN )
3473 /* check against pvs cluster */
3474 if( numClusters > 0 && clusters != NULL )
3476 for( i = 0; i < numClusters; i++ )
3478 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3483 if( i == numClusters )
3485 lightsClusterCulled++;
3490 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3491 VectorSubtract( light->origin, origin, dir );
3492 dist = VectorLength( dir );
3493 dist -= light->envelope;
3497 lightsEnvelopeCulled++;
3501 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3504 for( i = 0; i < 3; i++ )
3506 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3511 lightsBoundsCulled++;
3517 /* planar surfaces (except twosided surfaces) have a couple more checks */
3518 if( length > 0.0f && trace->twoSided == qfalse )
3520 /* lights coplanar with a surface won't light it */
3521 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3523 lightsPlaneCulled++;
3527 /* check to see if light is behind the plane */
3528 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3530 lightsPlaneCulled++;
3535 /* add this light */
3536 trace->lights[ trace->numLights++ ] = light;
3539 /* make last night null */
3540 trace->lights[ trace->numLights ] = NULL;
3545 void FreeTraceLights( trace_t *trace )
3547 if( trace->lights != NULL )
3548 free( trace->lights );
3554 CreateTraceLightsForSurface()
3555 creates a list of lights that can potentially affect a drawsurface
3558 void CreateTraceLightsForSurface( int num, trace_t *trace )
3561 vec3_t mins, maxs, normal;
3563 bspDrawSurface_t *ds;
3564 surfaceInfo_t *info;
3571 /* get drawsurface and info */
3572 ds = &bspDrawSurfaces[ num ];
3573 info = &surfaceInfos[ num ];
3575 /* get the mins/maxs for the dsurf */
3576 ClearBounds( mins, maxs );
3577 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3578 for( i = 0; i < ds->numVerts; i++ )
3580 dv = &yDrawVerts[ ds->firstVert + i ];
3581 AddPointToBounds( dv->xyz, mins, maxs );
3582 if( !VectorCompare( dv->normal, normal ) )
3583 VectorClear( normal );
3586 /* create the lights for the bounding box */
3587 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );