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 ){
53 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
54 if ( scale <= 0.0f ) {
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 ) {
72 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
75 /* clamp with color normalization */
77 if ( sample[ 1 ] > max ) {
80 if ( sample[ 2 ] > max ) {
84 VectorScale( sample, ( 255.0f / max ), sample );
87 /* compensate for ingame overbrighting/bitshifting */
88 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
91 colorBytes[ 0 ] = sample[ 0 ];
92 colorBytes[ 1 ] = sample[ 1 ];
93 colorBytes[ 2 ] = sample[ 2 ];
98 /* -------------------------------------------------------------------------------
100 this section deals with phong shading (normal interpolation across brush faces)
102 ------------------------------------------------------------------------------- */
106 smooths together coincident vertex normals across the bsp
109 #define MAX_SAMPLES 256
110 #define THETA_EPSILON 0.000001
111 #define EQUAL_NORMAL_EPSILON 0.01
113 void SmoothNormals( void ){
114 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
115 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
116 bspDrawSurface_t *ds;
120 vec3_t average, diff;
121 int indexes[ MAX_SAMPLES ];
122 vec3_t votes[ MAX_SAMPLES ];
125 /* allocate shade angle table */
126 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
127 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
129 /* allocate smoothed table */
130 cs = ( numBSPDrawVerts / 8 ) + 1;
131 smoothed = safe_malloc( cs );
132 memset( smoothed, 0, cs );
134 /* set default shade angle */
135 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
138 /* run through every surface and flag verts belonging to non-lightmapped surfaces
139 and set per-vertex smoothing angle */
140 for ( i = 0; i < numBSPDrawSurfaces; i++ )
143 ds = &bspDrawSurfaces[ i ];
145 /* get shader for shade angle */
146 si = surfaceInfos[ i ].si;
147 if ( si->shadeAngleDegrees ) {
148 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
151 shadeAngle = defaultShadeAngle;
153 if ( shadeAngle > maxShadeAngle ) {
154 maxShadeAngle = shadeAngle;
158 for ( j = 0; j < ds->numVerts; j++ )
160 f = ds->firstVert + j;
161 shadeAngles[ f ] = shadeAngle;
162 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
163 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
167 /* ydnar: optional force-to-trisoup */
168 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
169 ds->surfaceType = MST_TRIANGLE_SOUP;
170 ds->lightmapNum[ 0 ] = -3;
174 /* bail if no surfaces have a shade angle */
175 if ( maxShadeAngle == 0 ) {
183 start = I_FloatTime();
185 /* go through the list of vertexes */
186 for ( i = 0; i < numBSPDrawVerts; i++ )
189 f = 10 * i / numBSPDrawVerts;
192 Sys_Printf( "%i...", f );
195 /* already smoothed? */
196 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
201 VectorClear( average );
205 /* build a table of coincident vertexes */
206 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
208 /* already smoothed? */
209 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
214 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
218 /* use smallest shade angle */
219 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
221 /* check shade angle */
222 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
226 else if ( dot < -1.0 ) {
229 testAngle = acos( dot ) + THETA_EPSILON;
230 if ( testAngle >= shadeAngle ) {
231 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
234 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
236 /* add to the list */
237 indexes[ numVerts++ ] = j;
240 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
242 /* see if this normal has already been voted */
243 for ( k = 0; k < numVotes; k++ )
245 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
246 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
247 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
248 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
253 /* add a new vote? */
254 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
255 VectorAdd( average, bspDrawVerts[ j ].normal, average );
256 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
261 /* don't average for less than 2 verts */
262 if ( numVerts < 2 ) {
267 if ( VectorNormalize( average, average ) > 0 ) {
269 for ( j = 0; j < numVerts; j++ )
270 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
274 /* free the tables */
279 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
284 /* -------------------------------------------------------------------------------
286 this section deals with phong shaded lightmap tracing
288 ------------------------------------------------------------------------------- */
290 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
294 calculates the st tangent vectors for normalmapping
297 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
303 /* calculate barycentric basis for the triangle */
304 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 ] );
305 if ( fabs( bb ) < 0.00000001f ) {
310 for ( i = 0; i < numVerts; i++ )
312 /* calculate s tangent vector */
313 s = dv[ i ]->st[ 0 ] + 10.0f;
314 t = dv[ i ]->st[ 1 ];
315 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
316 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
317 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
319 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
320 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
321 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
323 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
324 VectorNormalize( stv[ i ], stv[ i ] );
326 /* calculate t tangent vector */
327 s = dv[ i ]->st[ 0 ];
328 t = dv[ i ]->st[ 1 ] + 10.0f;
329 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
330 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
331 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
333 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
334 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
335 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
337 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
338 VectorNormalize( ttv[ i ], ttv[ i ] );
341 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
342 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
345 /* return to caller */
354 perterbs the normal by the shader's normalmap in tangent space
357 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
363 VectorCopy( dv->normal, pNormal );
365 /* sample normalmap */
366 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
370 /* remap sampled normal from [0,255] to [-1,-1] */
371 for ( i = 0; i < 3; i++ )
372 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
374 /* scale tangent vectors and add to original normal */
375 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
376 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
377 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
379 /* renormalize and return */
380 VectorNormalize( pNormal, pNormal );
387 maps a luxel for triangle bv at
391 #define BOGUS_NUDGE -99999.0f
393 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 ] ){
394 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
395 float *luxel, *origin, *normal, d, lightmapSampleOffset;
401 static float nudges[][ 2 ] =
403 //%{ 0, 0 }, /* try center first */
404 { -NUDGE, 0 }, /* left */
405 { NUDGE, 0 }, /* right */
406 { 0, NUDGE }, /* up */
407 { 0, -NUDGE }, /* down */
408 { -NUDGE, NUDGE }, /* left/up */
409 { NUDGE, -NUDGE }, /* right/down */
410 { NUDGE, NUDGE }, /* right/up */
411 { -NUDGE, -NUDGE }, /* left/down */
412 { BOGUS_NUDGE, BOGUS_NUDGE }
416 /* find luxel xy coords (fixme: subtract 0.5?) */
417 x = dv->lightmap[ 0 ][ 0 ];
418 y = dv->lightmap[ 0 ][ 1 ];
422 else if ( x >= lm->sw ) {
428 else if ( y >= lm->sh ) {
432 /* set shader and cluster list */
433 if ( info != NULL ) {
435 numClusters = info->numSurfaceClusters;
436 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
445 /* get luxel, origin, cluster, and normal */
446 luxel = SUPER_LUXEL( 0, x, y );
447 origin = SUPER_ORIGIN( x, y );
448 normal = SUPER_NORMAL( x, y );
449 cluster = SUPER_CLUSTER( x, y );
451 /* don't attempt to remap occluded luxels for planar surfaces */
452 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
456 /* only average the normal for premapped luxels */
457 else if ( ( *cluster ) >= 0 ) {
458 /* do bumpmap calculations */
460 PerturbNormal( dv, si, pNormal, stv, ttv );
463 VectorCopy( dv->normal, pNormal );
466 /* add the additional normal data */
467 VectorAdd( normal, pNormal, normal );
472 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
476 /* axial lightmap projection */
477 if ( lm->vecs != NULL ) {
478 /* calculate an origin for the sample from the lightmap vectors */
479 VectorCopy( lm->origin, origin );
480 for ( i = 0; i < 3; i++ )
482 /* add unless it's the axis, which is taken care of later */
483 if ( i == lm->axisNum ) {
486 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
489 /* project the origin onto the plane */
490 d = DotProduct( origin, plane ) - plane[ 3 ];
491 d /= plane[ lm->axisNum ];
492 origin[ lm->axisNum ] -= d;
495 /* non axial lightmap projection (explicit xyz) */
497 VectorCopy( dv->xyz, origin );
500 /* planar surfaces have precalculated lightmap vectors for nudging */
501 if ( lm->plane != NULL ) {
502 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
503 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
504 VectorCopy( lm->plane, vecs[ 2 ] );
507 /* non-planar surfaces must calculate them */
510 if ( plane != NULL ) {
511 VectorCopy( plane, vecs[ 2 ] );
514 VectorCopy( dv->normal, vecs[ 2 ] );
516 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
519 /* push the origin off the surface a bit */
521 lightmapSampleOffset = si->lightmapSampleOffset;
524 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
526 if ( lm->axisNum < 0 ) {
527 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
529 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
530 origin[ lm->axisNum ] -= lightmapSampleOffset;
533 origin[ lm->axisNum ] += lightmapSampleOffset;
537 pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );
539 /* another retarded hack, storing nudge count in luxel[ 1 ] */
542 /* point in solid? (except in dark mode) */
543 if ( pointCluster < 0 && dark == qfalse ) {
544 /* nudge the the location around */
546 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
548 /* nudge the vector around a bit */
549 for ( i = 0; i < 3; i++ )
551 /* set nudged point*/
552 nudged[ i ] = origin[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
556 /* get pvs cluster */
557 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
558 if ( pointCluster >= 0 ) {
559 VectorCopy( nudged, origin );
565 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
566 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
567 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
568 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
569 if ( pointCluster >= 0 ) {
570 VectorCopy( nudged, origin );
576 if ( pointCluster < 0 ) {
577 ( *cluster ) = CLUSTER_OCCLUDED;
578 VectorClear( origin );
579 VectorClear( normal );
585 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
587 /* do bumpmap calculations */
589 PerturbNormal( dv, si, pNormal, stv, ttv );
592 VectorCopy( dv->normal, pNormal );
595 /* store the cluster and normal */
596 ( *cluster ) = pointCluster;
597 VectorCopy( pNormal, normal );
599 /* store explicit mapping pass and implicit mapping pass */
614 recursively subdivides a triangle until its edges are shorter
615 than the distance between two luxels (thanks jc :)
618 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 ] ){
619 bspDrawVert_t mid, *dv2[ 3 ];
623 /* map the vertexes */
625 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
626 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
627 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
633 float *a, *b, dx, dy, dist, maxDist;
636 /* find the longest edge and split it */
639 for ( i = 0; i < 3; i++ )
642 a = dv[ i ]->lightmap[ 0 ];
643 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
646 dx = a[ 0 ] - b[ 0 ];
647 dy = a[ 1 ] - b[ 1 ];
648 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
651 if ( dist > maxDist ) {
657 /* try to early out */
658 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
663 /* split the longest edge and map it */
664 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
665 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );
667 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
668 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
670 /* recurse to first triangle */
671 VectorCopy( dv, dv2 );
673 MapTriangle_r( lm, info, dv2, plane, stv, ttv );
675 /* recurse to second triangle */
676 VectorCopy( dv, dv2 );
677 dv2[ ( max + 1 ) % 3 ] = ∣
678 MapTriangle_r( lm, info, dv2, plane, stv, ttv );
685 seed function for MapTriangle_r()
686 requires a cw ordered triangle
689 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
692 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
695 /* get plane if possible */
696 if ( lm->plane != NULL ) {
697 VectorCopy( lm->plane, plane );
698 plane[ 3 ] = lm->plane[ 3 ];
701 /* otherwise make one from the points */
702 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
706 /* check to see if we need to calculate texture->world tangent vectors */
707 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
717 /* map the vertexes */
718 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
719 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
720 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
722 /* 2002-11-20: prefer axial triangle edges */
724 /* subdivide the triangle */
725 MapTriangle_r( lm, info, dv, plane, stv, ttv );
729 for ( i = 0; i < 3; i++ )
732 bspDrawVert_t *dv2[ 3 ];
736 a = dv[ i ]->lightmap[ 0 ];
737 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
739 /* make degenerate triangles for mapping edges */
740 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
742 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
743 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
745 /* map the degenerate triangle */
746 MapTriangle_r( lm, info, dv2, plane, stv, ttv );
757 recursively subdivides a quad until its edges are shorter
758 than the distance between two luxels
761 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 ] ){
762 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
769 float *a, *b, dx, dy, dist, maxDist;
772 /* find the longest edge and split it */
775 for ( i = 0; i < 4; i++ )
778 a = dv[ i ]->lightmap[ 0 ];
779 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
782 dx = a[ 0 ] - b[ 0 ];
783 dy = a[ 1 ] - b[ 1 ];
784 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
787 if ( dist > maxDist ) {
793 /* try to early out */
794 if ( max < 0 || maxDist <= subdivideThreshold ) {
799 /* we only care about even/odd edges */
802 /* split the longest edges */
803 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
804 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
806 /* map the vertexes */
807 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );
808 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );
812 /* recurse to first quad */
814 dv2[ 1 ] = &mid[ 0 ];
815 dv2[ 2 ] = &mid[ 1 ];
817 MapQuad_r( lm, info, dv2, plane, stv, ttv );
819 /* recurse to second quad */
820 dv2[ 0 ] = &mid[ 0 ];
823 dv2[ 3 ] = &mid[ 1 ];
824 MapQuad_r( lm, info, dv2, plane, stv, ttv );
830 /* recurse to first quad */
833 dv2[ 2 ] = &mid[ 0 ];
834 dv2[ 3 ] = &mid[ 1 ];
835 MapQuad_r( lm, info, dv2, plane, stv, ttv );
837 /* recurse to second quad */
838 dv2[ 0 ] = &mid[ 1 ];
839 dv2[ 1 ] = &mid[ 0 ];
842 MapQuad_r( lm, info, dv2, plane, stv, ttv );
850 seed function for MapQuad_r()
851 requires a cw ordered triangle quad
854 #define QUAD_PLANAR_EPSILON 0.5f
856 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
859 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
862 /* get plane if possible */
863 if ( lm->plane != NULL ) {
864 VectorCopy( lm->plane, plane );
865 plane[ 3 ] = lm->plane[ 3 ];
868 /* otherwise make one from the points */
869 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
873 /* 4th point must fall on the plane */
874 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
875 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
879 /* check to see if we need to calculate texture->world tangent vectors */
880 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
890 /* map the vertexes */
891 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
892 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
893 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
894 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );
896 /* subdivide the quad */
897 MapQuad_r( lm, info, dv, plane, stv, ttv );
905 maps the locations, normals, and pvs clusters for a raw lightmap
908 #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)
910 void MapRawLightmap( int rawLightmapNum ){
911 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
912 float *luxel, *origin, *normal, samples, radius, pass;
914 bspDrawSurface_t *ds;
916 mesh_t src, *subdivided, *mesh;
917 bspDrawVert_t *verts, *dv[ 4 ], fake;
920 /* bail if this number exceeds the number of raw lightmaps */
921 if ( rawLightmapNum >= numRawLightmaps ) {
926 lm = &rawLightmaps[ rawLightmapNum ];
928 /* -----------------------------------------------------------------
929 map referenced surfaces onto the raw lightmap
930 ----------------------------------------------------------------- */
932 /* walk the list of surfaces on this raw lightmap */
933 for ( n = 0; n < lm->numLightSurfaces; n++ )
935 /* with > 1 surface per raw lightmap, clear occluded */
937 for ( y = 0; y < lm->sh; y++ )
939 for ( x = 0; x < lm->sw; x++ )
942 cluster = SUPER_CLUSTER( x, y );
943 if ( *cluster < 0 ) {
944 *cluster = CLUSTER_UNMAPPED;
951 num = lightSurfaces[ lm->firstLightSurface + n ];
952 ds = &bspDrawSurfaces[ num ];
953 info = &surfaceInfos[ num ];
955 /* bail if no lightmap to calculate */
956 if ( info->lm != lm ) {
961 /* map the surface onto the lightmap origin/cluster/normal buffers */
962 switch ( ds->surfaceType )
966 verts = yDrawVerts + ds->firstVert;
968 /* map the triangles */
969 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
971 for ( i = 0; i < ds->numIndexes; i += 3 )
973 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
974 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
975 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
976 MapTriangle( lm, info, dv, mapNonAxial );
982 /* make a mesh from the drawsurf */
983 src.width = ds->patchWidth;
984 src.height = ds->patchHeight;
985 src.verts = &yDrawVerts[ ds->firstVert ];
986 //% subdivided = SubdivideMesh( src, 8, 512 );
987 subdivided = SubdivideMesh2( src, info->patchIterations );
989 /* fit it to the curve and remove colinear verts on rows/columns */
990 PutMeshOnCurve( *subdivided );
991 mesh = RemoveLinearMeshColumnsRows( subdivided );
992 FreeMesh( subdivided );
1000 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1001 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1002 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1003 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1007 /* map the mesh quads */
1010 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1012 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1014 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1017 pw[ 0 ] = x + ( y * mesh->width );
1018 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1019 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1020 pw[ 3 ] = x + 1 + ( y * mesh->width );
1021 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1026 /* get drawverts and map first triangle */
1027 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1028 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1029 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1030 MapTriangle( lm, info, dv, mapNonAxial );
1032 /* get drawverts and map second triangle */
1033 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1034 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1035 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1036 MapTriangle( lm, info, dv, mapNonAxial );
1043 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1045 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1048 pw[ 0 ] = x + ( y * mesh->width );
1049 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1050 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1051 pw[ 3 ] = x + 1 + ( y * mesh->width );
1057 /* attempt to map quad first */
1058 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1059 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1060 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1061 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1062 if ( MapQuad( lm, info, dv ) ) {
1066 /* get drawverts and map first triangle */
1067 MapTriangle( lm, info, dv, mapNonAxial );
1069 /* get drawverts and map second triangle */
1070 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1071 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1072 MapTriangle( lm, info, dv, mapNonAxial );
1087 /* -----------------------------------------------------------------
1088 average and clean up luxel normals
1089 ----------------------------------------------------------------- */
1091 /* walk the luxels */
1092 for ( y = 0; y < lm->sh; y++ )
1094 for ( x = 0; x < lm->sw; x++ )
1097 luxel = SUPER_LUXEL( 0, x, y );
1098 normal = SUPER_NORMAL( x, y );
1099 cluster = SUPER_CLUSTER( x, y );
1101 /* only look at mapped luxels */
1102 if ( *cluster < 0 ) {
1106 /* the normal data could be the sum of multiple samples */
1107 if ( luxel[ 3 ] > 1.0f ) {
1108 VectorNormalize( normal, normal );
1111 /* mark this luxel as having only one normal */
1116 /* non-planar surfaces stop here */
1117 if ( lm->plane == NULL ) {
1121 /* -----------------------------------------------------------------
1122 map occluded or unuxed luxels
1123 ----------------------------------------------------------------- */
1125 /* walk the luxels */
1126 radius = floor( superSample / 2 );
1127 radius = radius > 0 ? radius : 1.0f;
1129 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1131 for ( y = 0; y < lm->sh; y++ )
1133 for ( x = 0; x < lm->sw; x++ )
1136 luxel = SUPER_LUXEL( 0, x, y );
1137 normal = SUPER_NORMAL( x, y );
1138 cluster = SUPER_CLUSTER( x, y );
1140 /* only look at unmapped luxels */
1141 if ( *cluster != CLUSTER_UNMAPPED ) {
1145 /* divine a normal and origin from neighboring luxels */
1146 VectorClear( fake.xyz );
1147 VectorClear( fake.normal );
1148 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1149 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1151 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1153 if ( sy < 0 || sy >= lm->sh ) {
1157 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1159 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1163 /* get neighboring luxel */
1164 luxel = SUPER_LUXEL( 0, sx, sy );
1165 origin = SUPER_ORIGIN( sx, sy );
1166 normal = SUPER_NORMAL( sx, sy );
1167 cluster = SUPER_CLUSTER( sx, sy );
1169 /* only consider luxels mapped in previous passes */
1170 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1174 /* add its distinctiveness to our own */
1175 VectorAdd( fake.xyz, origin, fake.xyz );
1176 VectorAdd( fake.normal, normal, fake.normal );
1177 samples += luxel[ 3 ];
1182 if ( samples == 0.0f ) {
1187 VectorDivide( fake.xyz, samples, fake.xyz );
1188 //% VectorDivide( fake.normal, samples, fake.normal );
1189 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1193 /* map the fake vert */
1194 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
1199 /* -----------------------------------------------------------------
1200 average and clean up luxel normals
1201 ----------------------------------------------------------------- */
1203 /* walk the luxels */
1204 for ( y = 0; y < lm->sh; y++ )
1206 for ( x = 0; x < lm->sw; x++ )
1209 luxel = SUPER_LUXEL( 0, x, y );
1210 normal = SUPER_NORMAL( x, y );
1211 cluster = SUPER_CLUSTER( x, y );
1213 /* only look at mapped luxels */
1214 if ( *cluster < 0 ) {
1218 /* the normal data could be the sum of multiple samples */
1219 if ( luxel[ 3 ] > 1.0f ) {
1220 VectorNormalize( normal, normal );
1223 /* mark this luxel as having only one normal */
1231 for ( y = 0; y < lm->sh; y++ )
1233 for ( x = 0; x < lm->sw; x++ )
1238 cluster = SUPER_CLUSTER( x, y );
1239 origin = SUPER_ORIGIN( x, y );
1240 normal = SUPER_NORMAL( x, y );
1241 luxel = SUPER_LUXEL( x, y );
1243 if ( *cluster < 0 ) {
1247 /* check if within the bounding boxes of all surfaces referenced */
1248 ClearBounds( mins, maxs );
1249 for ( n = 0; n < lm->numLightSurfaces; n++ )
1252 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1253 TOL = info->sampleSize + 2;
1254 AddPointToBounds( info->mins, mins, maxs );
1255 AddPointToBounds( info->maxs, mins, maxs );
1256 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1257 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1258 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1264 if ( n < lm->numLightSurfaces ) {
1268 /* report bogus origin */
1269 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",
1270 rawLightmapNum, x, y, *cluster,
1271 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1272 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1273 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1284 sets up dirtmap (ambient occlusion)
1287 #define DIRT_CONE_ANGLE 88 /* degrees */
1288 #define DIRT_NUM_ANGLE_STEPS 16
1289 #define DIRT_NUM_ELEVATION_STEPS 3
1290 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1292 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1293 static int numDirtVectors = 0;
1295 void SetupDirt( void ){
1297 float angle, elevation, angleStep, elevationStep;
1301 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1303 /* calculate angular steps */
1304 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1305 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1309 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1311 /* iterate elevation */
1312 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1314 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1315 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1316 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1321 /* emit some statistics */
1322 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1328 calculates dirt value for a given sample
1331 float DirtForSample( trace_t *trace ){
1333 float gatherDirt, outDirt, angle, elevation, ooDepth;
1334 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1341 if ( trace == NULL || trace->cluster < 0 ) {
1347 ooDepth = 1.0f / dirtDepth;
1348 VectorCopy( trace->normal, normal );
1350 /* check if the normal is aligned to the world-up */
1351 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f ) {
1352 if ( normal[ 2 ] == 1.0f ) {
1353 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1354 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1356 else if ( normal[ 2 ] == -1.0f ) {
1357 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1358 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1363 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1364 CrossProduct( normal, worldUp, myRt );
1365 VectorNormalize( myRt, myRt );
1366 CrossProduct( myRt, normal, myUp );
1367 VectorNormalize( myUp, myUp );
1370 /* 1 = random mode, 0 (well everything else) = non-random mode */
1371 if ( dirtMode == 1 ) {
1373 for ( i = 0; i < numDirtVectors; i++ )
1375 /* get random vector */
1376 angle = Random() * DEG2RAD( 360.0f );
1377 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1378 temp[ 0 ] = cos( angle ) * sin( elevation );
1379 temp[ 1 ] = sin( angle ) * sin( elevation );
1380 temp[ 2 ] = cos( elevation );
1382 /* transform into tangent space */
1383 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1384 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1385 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1388 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1389 SetupTrace( trace );
1393 if ( trace->opaque ) {
1394 VectorSubtract( trace->hit, trace->origin, displacement );
1395 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1401 /* iterate through ordered vectors */
1402 for ( i = 0; i < numDirtVectors; i++ )
1404 /* transform vector into tangent space */
1405 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1406 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1407 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1410 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1411 SetupTrace( trace );
1415 if ( trace->opaque ) {
1416 VectorSubtract( trace->hit, trace->origin, displacement );
1417 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1423 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1424 SetupTrace( trace );
1428 if ( trace->opaque ) {
1429 VectorSubtract( trace->hit, trace->origin, displacement );
1430 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1434 if ( gatherDirt <= 0.0f ) {
1438 /* apply gain (does this even do much? heh) */
1439 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1440 if ( outDirt > 1.0f ) {
1445 outDirt *= dirtScale;
1446 if ( outDirt > 1.0f ) {
1450 /* return to sender */
1451 return 1.0f - outDirt;
1458 calculates dirty fraction for each luxel
1461 void DirtyRawLightmap( int rawLightmapNum ){
1462 int i, x, y, sx, sy, *cluster;
1463 float *origin, *normal, *dirt, *dirt2, average, samples;
1465 surfaceInfo_t *info;
1469 /* bail if this number exceeds the number of raw lightmaps */
1470 if ( rawLightmapNum >= numRawLightmaps ) {
1475 lm = &rawLightmaps[ rawLightmapNum ];
1478 trace.testOcclusion = qtrue;
1479 trace.forceSunlight = qfalse;
1480 trace.recvShadows = lm->recvShadows;
1481 trace.numSurfaces = lm->numLightSurfaces;
1482 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1483 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1484 trace.testAll = qfalse;
1486 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1487 trace.twoSided = qfalse;
1488 for ( i = 0; i < trace.numSurfaces; i++ )
1491 info = &surfaceInfos[ trace.surfaces[ i ] ];
1493 /* check twosidedness */
1494 if ( info->si->twoSided ) {
1495 trace.twoSided = qtrue;
1501 for ( y = 0; y < lm->sh; y++ )
1503 for ( x = 0; x < lm->sw; x++ )
1506 cluster = SUPER_CLUSTER( x, y );
1507 origin = SUPER_ORIGIN( x, y );
1508 normal = SUPER_NORMAL( x, y );
1509 dirt = SUPER_DIRT( x, y );
1511 /* set default dirt */
1514 /* only look at mapped luxels */
1515 if ( *cluster < 0 ) {
1520 trace.cluster = *cluster;
1521 VectorCopy( origin, trace.origin );
1522 VectorCopy( normal, trace.normal );
1525 *dirt = DirtForSample( &trace );
1529 /* testing no filtering */
1533 for ( y = 0; y < lm->sh; y++ )
1535 for ( x = 0; x < lm->sw; x++ )
1538 cluster = SUPER_CLUSTER( x, y );
1539 dirt = SUPER_DIRT( x, y );
1541 /* filter dirt by adjacency to unmapped luxels */
1544 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1546 if ( sy < 0 || sy >= lm->sh ) {
1550 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1552 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1556 /* get neighboring luxel */
1557 cluster = SUPER_CLUSTER( sx, sy );
1558 dirt2 = SUPER_DIRT( sx, sy );
1559 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1569 if ( samples <= 0.0f ) {
1575 if ( samples <= 0.0f ) {
1580 *dirt = average / samples;
1589 calculates the pvs cluster, origin, normal of a sub-luxel
1592 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1593 int i, *cluster, *cluster2;
1594 float *origin, *origin2, *normal; //% , *normal2;
1595 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1598 /* calulate x vector */
1599 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1600 cluster = SUPER_CLUSTER( x, y );
1601 origin = SUPER_ORIGIN( x, y );
1602 //% normal = SUPER_NORMAL( x, y );
1603 cluster2 = SUPER_CLUSTER( x + 1, y );
1604 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1605 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1607 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1608 cluster = SUPER_CLUSTER( x - 1, y );
1609 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1610 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1611 cluster2 = SUPER_CLUSTER( x, y );
1612 origin2 = SUPER_ORIGIN( x, y );
1613 //% normal2 = SUPER_NORMAL( x, y );
1616 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1619 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1620 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1622 /* calulate y vector */
1623 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1624 cluster = SUPER_CLUSTER( x, y );
1625 origin = SUPER_ORIGIN( x, y );
1626 //% normal = SUPER_NORMAL( x, y );
1627 cluster2 = SUPER_CLUSTER( x, y + 1 );
1628 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1629 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1631 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1632 cluster = SUPER_CLUSTER( x, y - 1 );
1633 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1634 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1635 cluster2 = SUPER_CLUSTER( x, y );
1636 origin2 = SUPER_ORIGIN( x, y );
1637 //% normal2 = SUPER_NORMAL( x, y );
1640 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1643 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1644 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1646 /* calculate new origin */
1647 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1648 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1649 for ( i = 0; i < 3; i++ )
1650 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1653 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1654 if ( *sampleCluster < 0 ) {
1658 /* calculate new normal */
1659 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1660 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1661 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1663 normal = SUPER_NORMAL( x, y );
1664 VectorCopy( normal, sampleNormal );
1672 SubsampleRawLuxel_r()
1673 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1676 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel ){
1677 int b, samples, mapped, lighted;
1680 vec3_t origin[ 4 ], normal[ 4 ];
1681 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1682 vec3_t color, total;
1686 if ( lightLuxel[ 3 ] >= lightSamples ) {
1691 VectorClear( total );
1695 /* make 2x2 subsample stamp */
1696 for ( b = 0; b < 4; b++ )
1699 VectorCopy( sampleOrigin, origin[ b ] );
1701 /* calculate position */
1702 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1708 /* increment sample count */
1709 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1712 trace->cluster = *cluster;
1713 VectorCopy( origin[ b ], trace->origin );
1714 VectorCopy( normal[ b ], trace->normal );
1718 LightContributionToSample( trace );
1720 /* add to totals (fixme: make contrast function) */
1721 VectorCopy( trace->color, luxel[ b ] );
1722 VectorAdd( total, trace->color, total );
1723 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1728 /* subsample further? */
1729 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1730 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1731 lighted != 0 && lighted != mapped ) {
1732 for ( b = 0; b < 4; b++ )
1734 if ( cluster[ b ] < 0 ) {
1737 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.25f ), luxel[ b ] );
1742 //% VectorClear( color );
1744 VectorCopy( lightLuxel, color );
1746 for ( b = 0; b < 4; b++ )
1748 if ( cluster[ b ] < 0 ) {
1751 VectorAdd( color, luxel[ b ], color );
1756 if ( samples > 0 ) {
1758 color[ 0 ] /= samples;
1759 color[ 1 ] /= samples;
1760 color[ 2 ] /= samples;
1763 VectorCopy( color, lightLuxel );
1764 lightLuxel[ 3 ] += 1.0f;
1771 IlluminateRawLightmap()
1772 illuminates the luxels
1775 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1776 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1778 void IlluminateRawLightmap( int rawLightmapNum ){
1779 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1780 int *cluster, *cluster2, mapped, lighted, totalLighted;
1782 surfaceInfo_t *info;
1783 qboolean filterColor, filterDir;
1785 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1786 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1787 vec3_t color, averageColor, averageDir, total, temp, temp2;
1788 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1790 float stackLightLuxels[ STACK_LL_SIZE ];
1793 /* bail if this number exceeds the number of raw lightmaps */
1794 if ( rawLightmapNum >= numRawLightmaps ) {
1799 lm = &rawLightmaps[ rawLightmapNum ];
1802 trace.testOcclusion = !noTrace;
1803 trace.forceSunlight = qfalse;
1804 trace.recvShadows = lm->recvShadows;
1805 trace.numSurfaces = lm->numLightSurfaces;
1806 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1807 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1809 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1810 trace.twoSided = qfalse;
1811 for ( i = 0; i < trace.numSurfaces; i++ )
1814 info = &surfaceInfos[ trace.surfaces[ i ] ];
1816 /* check twosidedness */
1817 if ( info->si->twoSided ) {
1818 trace.twoSided = qtrue;
1823 /* create a culled light list for this raw lightmap */
1824 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1826 /* -----------------------------------------------------------------
1828 ----------------------------------------------------------------- */
1831 numLuxelsIlluminated += ( lm->sw * lm->sh );
1833 /* test debugging state */
1834 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
1835 /* debug fill the luxels */
1836 for ( y = 0; y < lm->sh; y++ )
1838 for ( x = 0; x < lm->sw; x++ )
1841 cluster = SUPER_CLUSTER( x, y );
1843 /* only fill mapped luxels */
1844 if ( *cluster < 0 ) {
1848 /* get particulars */
1849 luxel = SUPER_LUXEL( 0, x, y );
1850 origin = SUPER_ORIGIN( x, y );
1851 normal = SUPER_NORMAL( x, y );
1853 /* color the luxel with raw lightmap num? */
1854 if ( debugSurfaces ) {
1855 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1858 /* color the luxel with lightmap axis? */
1859 else if ( debugAxis ) {
1860 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
1861 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
1862 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
1865 /* color the luxel with luxel cluster? */
1866 else if ( debugCluster ) {
1867 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1870 /* color the luxel with luxel origin? */
1871 else if ( debugOrigin ) {
1872 VectorSubtract( lm->maxs, lm->mins, temp );
1873 VectorScale( temp, ( 1.0f / 255.0f ), temp );
1874 VectorSubtract( origin, lm->mins, temp2 );
1875 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
1876 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
1877 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
1880 /* color the luxel with the normal */
1881 else if ( normalmap ) {
1882 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
1883 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
1884 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
1887 /* otherwise clear it */
1889 VectorClear( luxel );
1899 /* allocate temporary per-light luxel storage */
1900 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1901 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
1902 lightLuxels = stackLightLuxels;
1905 lightLuxels = safe_malloc( llSize );
1909 //% memset( lm->superLuxels[ 0 ], 0, llSize );
1911 /* set ambient color */
1912 for ( y = 0; y < lm->sh; y++ )
1914 for ( x = 0; x < lm->sw; x++ )
1917 cluster = SUPER_CLUSTER( x, y );
1918 luxel = SUPER_LUXEL( 0, x, y );
1919 normal = SUPER_NORMAL( x, y );
1920 deluxel = SUPER_DELUXEL( x, y );
1922 /* blacken unmapped clusters */
1923 if ( *cluster < 0 ) {
1924 VectorClear( luxel );
1930 VectorCopy( ambientColor, luxel );
1932 VectorScale( normal, 0.00390625f, deluxel );
1939 /* clear styled lightmaps */
1940 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1941 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1943 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
1944 memset( lm->superLuxels[ lightmapNum ], 0, size );
1948 /* debugging code */
1949 //% if( trace.numLights <= 0 )
1950 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
1952 /* walk light list */
1953 for ( i = 0; i < trace.numLights; i++ )
1956 trace.light = trace.lights[ i ];
1959 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1961 if ( lm->styles[ lightmapNum ] == trace.light->style ||
1962 lm->styles[ lightmapNum ] == LS_NONE ) {
1967 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
1968 if ( lightmapNum >= MAX_LIGHTMAPS ) {
1969 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
1974 memset( lightLuxels, 0, llSize );
1977 /* initial pass, one sample per luxel */
1978 for ( y = 0; y < lm->sh; y++ )
1980 for ( x = 0; x < lm->sw; x++ )
1983 cluster = SUPER_CLUSTER( x, y );
1984 if ( *cluster < 0 ) {
1988 /* get particulars */
1989 lightLuxel = LIGHT_LUXEL( x, y );
1990 deluxel = SUPER_DELUXEL( x, y );
1991 origin = SUPER_ORIGIN( x, y );
1992 normal = SUPER_NORMAL( x, y );
1994 /* set contribution count */
1995 lightLuxel[ 3 ] = 1.0f;
1998 trace.cluster = *cluster;
1999 VectorCopy( origin, trace.origin );
2000 VectorCopy( normal, trace.normal );
2002 /* get light for this sample */
2003 LightContributionToSample( &trace );
2004 VectorCopy( trace.color, lightLuxel );
2007 if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2011 /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
2013 /* color to grayscale (photoshop rgb weighting) */
2014 brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
2015 brightness *= ( 1.0 / 255.0 );
2016 VectorScale( trace.direction, brightness, trace.direction );
2017 VectorAdd( deluxel, trace.direction, deluxel );
2022 /* don't even bother with everything else if nothing was lit */
2023 if ( totalLighted == 0 ) {
2027 /* determine filter radius */
2028 filterRadius = lm->filterRadius > trace.light->filterRadius
2030 : trace.light->filterRadius;
2031 if ( filterRadius < 0.0f ) {
2032 filterRadius = 0.0f;
2035 /* set luxel filter radius */
2036 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2037 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2038 luxelFilterRadius = 1;
2041 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2042 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2043 if ( lightSamples > 1 && luxelFilterRadius == 0 ) {
2045 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2047 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2052 VectorClear( total );
2054 /* test 2x2 stamp */
2055 for ( t = 0; t < 4; t++ )
2057 /* set sample coords */
2058 sx = x + tests[ t ][ 0 ];
2059 sy = y + tests[ t ][ 1 ];
2062 cluster = SUPER_CLUSTER( sx, sy );
2063 if ( *cluster < 0 ) {
2069 lightLuxel = LIGHT_LUXEL( sx, sy );
2070 VectorAdd( total, lightLuxel, total );
2071 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2076 /* if total color is under a certain amount, then don't bother subsampling */
2077 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2081 /* if all 4 pixels are either in shadow or light, then don't subsample */
2082 if ( lighted != 0 && lighted != mapped ) {
2083 for ( t = 0; t < 4; t++ )
2085 /* set sample coords */
2086 sx = x + tests[ t ][ 0 ];
2087 sy = y + tests[ t ][ 1 ];
2090 cluster = SUPER_CLUSTER( sx, sy );
2091 if ( *cluster < 0 ) {
2094 lightLuxel = LIGHT_LUXEL( sx, sy );
2095 origin = SUPER_ORIGIN( sx, sy );
2097 /* only subsample shadowed luxels */
2098 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2102 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
2104 /* debug code to colorize subsampled areas to yellow */
2105 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2106 //% VectorSet( luxel, 255, 204, 0 );
2113 /* tertiary pass, apply dirt map (ambient occlusion) */
2116 for ( y = 0; y < lm->sh; y++ )
2118 for ( x = 0; x < lm->sw; x++ )
2121 cluster = SUPER_CLUSTER( x, y );
2122 if ( *cluster < 0 ) {
2126 /* get particulars */
2127 lightLuxel = LIGHT_LUXEL( x, y );
2128 dirt = SUPER_DIRT( x, y );
2130 /* scale light value */
2131 VectorScale( lightLuxel, *dirt, lightLuxel );
2136 /* allocate sampling lightmap storage */
2137 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2138 /* allocate sampling lightmap storage */
2139 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2140 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2141 memset( lm->superLuxels[ lightmapNum ], 0, size );
2145 if ( lightmapNum > 0 ) {
2146 lm->styles[ lightmapNum ] = trace.light->style;
2147 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2150 /* copy to permanent luxels */
2151 for ( y = 0; y < lm->sh; y++ )
2153 for ( x = 0; x < lm->sw; x++ )
2155 /* get cluster and origin */
2156 cluster = SUPER_CLUSTER( x, y );
2157 if ( *cluster < 0 ) {
2160 origin = SUPER_ORIGIN( x, y );
2163 if ( luxelFilterRadius ) {
2165 VectorClear( averageColor );
2168 /* cheaper distance-based filtering */
2169 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2171 if ( sy < 0 || sy >= lm->sh ) {
2175 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2177 if ( sx < 0 || sx >= lm->sw ) {
2181 /* get particulars */
2182 cluster = SUPER_CLUSTER( sx, sy );
2183 if ( *cluster < 0 ) {
2186 lightLuxel = LIGHT_LUXEL( sx, sy );
2189 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2190 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2192 /* scale luxel by filter weight */
2193 VectorScale( lightLuxel, weight, color );
2194 VectorAdd( averageColor, color, averageColor );
2200 if ( samples <= 0.0f ) {
2204 /* scale into luxel */
2205 luxel = SUPER_LUXEL( lightmapNum, x, y );
2208 /* handle negative light */
2209 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2210 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2211 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2212 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2215 /* handle normal light */
2218 luxel[ 0 ] += averageColor[ 0 ] / samples;
2219 luxel[ 1 ] += averageColor[ 1 ] / samples;
2220 luxel[ 2 ] += averageColor[ 2 ] / samples;
2227 /* get particulars */
2228 lightLuxel = LIGHT_LUXEL( x, y );
2229 luxel = SUPER_LUXEL( lightmapNum, x, y );
2231 /* handle negative light */
2232 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2233 VectorScale( averageColor, -1.0f, averageColor );
2239 /* handle negative light */
2240 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2241 VectorSubtract( luxel, lightLuxel, luxel );
2244 /* handle normal light */
2246 VectorAdd( luxel, lightLuxel, luxel );
2253 /* free temporary luxels */
2254 if ( lightLuxels != stackLightLuxels ) {
2255 free( lightLuxels );
2259 /* free light list */
2260 FreeTraceLights( &trace );
2262 /* -----------------------------------------------------------------
2264 ----------------------------------------------------------------- */
2267 /* walk lightmaps */
2268 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2271 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2275 /* apply dirt to each luxel */
2276 for ( y = 0; y < lm->sh; y++ )
2278 for ( x = 0; x < lm->sw; x++ )
2281 cluster = SUPER_CLUSTER( x, y );
2282 //% if( *cluster < 0 )
2285 /* get particulars */
2286 luxel = SUPER_LUXEL( lightmapNum, x, y );
2287 dirt = SUPER_DIRT( x, y );
2290 VectorScale( luxel, *dirt, luxel );
2294 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2301 /* -----------------------------------------------------------------
2303 ----------------------------------------------------------------- */
2305 /* walk lightmaps */
2306 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2309 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2313 /* average occluded luxels from neighbors */
2314 for ( y = 0; y < lm->sh; y++ )
2316 for ( x = 0; x < lm->sw; x++ )
2318 /* get particulars */
2319 cluster = SUPER_CLUSTER( x, y );
2320 luxel = SUPER_LUXEL( lightmapNum, x, y );
2321 deluxel = SUPER_DELUXEL( x, y );
2322 normal = SUPER_NORMAL( x, y );
2324 /* determine if filtering is necessary */
2325 filterColor = qfalse;
2327 if ( *cluster < 0 ||
2328 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2329 filterColor = qtrue;
2331 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2335 if ( !filterColor && !filterDir ) {
2339 /* choose seed amount */
2340 VectorClear( averageColor );
2341 VectorClear( averageDir );
2344 /* walk 3x3 matrix */
2345 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2347 if ( sy < 0 || sy >= lm->sh ) {
2351 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2353 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2357 /* get neighbor's particulars */
2358 cluster2 = SUPER_CLUSTER( sx, sy );
2359 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2360 deluxel2 = SUPER_DELUXEL( sx, sy );
2362 /* ignore unmapped/unlit luxels */
2363 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2364 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2368 /* add its distinctiveness to our own */
2369 VectorAdd( averageColor, luxel2, averageColor );
2370 samples += luxel2[ 3 ];
2372 VectorAdd( averageDir, deluxel2, averageDir );
2378 if ( samples <= 0.0f ) {
2382 /* dark lightmap seams */
2384 if ( lightmapNum == 0 ) {
2385 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2391 if ( filterColor ) {
2392 VectorDivide( averageColor, samples, luxel );
2396 VectorDivide( averageDir, samples, deluxel );
2399 /* set cluster to -3 */
2400 if ( *cluster < 0 ) {
2401 *cluster = CLUSTER_FLOODED;
2411 IlluminateVertexes()
2412 light the surface vertexes
2415 #define VERTEX_NUDGE 4.0f
2417 void IlluminateVertexes( int num ){
2418 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2419 int lightmapNum, numAvg;
2420 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2421 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2422 bspDrawSurface_t *ds;
2423 surfaceInfo_t *info;
2425 bspDrawVert_t *verts;
2429 /* get surface, info, and raw lightmap */
2430 ds = &bspDrawSurfaces[ num ];
2431 info = &surfaceInfos[ num ];
2434 /* -----------------------------------------------------------------
2435 illuminate the vertexes
2436 ----------------------------------------------------------------- */
2438 /* calculate vertex lighting for surfaces without lightmaps */
2439 if ( lm == NULL || cpmaHack ) {
2441 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2442 trace.forceSunlight = info->si->forceSunlight;
2443 trace.recvShadows = info->recvShadows;
2444 trace.numSurfaces = 1;
2445 trace.surfaces = #
2446 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2448 /* twosided lighting */
2449 trace.twoSided = info->si->twoSided;
2451 /* make light list for this surface */
2452 CreateTraceLightsForSurface( num, &trace );
2455 verts = yDrawVerts + ds->firstVert;
2457 memset( avgColors, 0, sizeof( avgColors ) );
2459 /* walk the surface verts */
2460 for ( i = 0; i < ds->numVerts; i++ )
2462 /* get vertex luxel */
2463 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2465 /* color the luxel with raw lightmap num? */
2466 if ( debugSurfaces ) {
2467 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2470 /* color the luxel with luxel origin? */
2471 else if ( debugOrigin ) {
2472 VectorSubtract( info->maxs, info->mins, temp );
2473 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2474 VectorSubtract( origin, lm->mins, temp2 );
2475 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2476 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2477 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2480 /* color the luxel with the normal */
2481 else if ( normalmap ) {
2482 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2483 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2484 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2487 /* illuminate the vertex */
2490 /* clear vertex luxel */
2491 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2493 /* try at initial origin */
2494 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2495 if ( trace.cluster >= 0 ) {
2497 VectorCopy( verts[ i ].xyz, trace.origin );
2498 VectorCopy( verts[ i ].normal, trace.normal );
2502 dirt = DirtForSample( &trace );
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 );
2520 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2524 /* is this sample bright enough? */
2525 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2526 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2527 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2528 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2529 /* nudge the sample point around a bit */
2530 for ( x = 0; x < 4; x++ )
2532 /* two's complement 0, 1, -1, 2, -2, etc */
2533 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2535 for ( y = 0; y < 4; y++ )
2537 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2539 for ( z = 0; z < 4; z++ )
2541 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2544 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2545 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2546 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2548 /* try at nudged origin */
2549 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2550 if ( trace.cluster < 0 ) {
2555 LightingAtSample( &trace, ds->vertexStyles, colors );
2558 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2561 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2564 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2565 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2568 /* bright enough? */
2569 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2570 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2571 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2572 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2580 /* add to average? */
2581 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2582 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2583 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2584 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2586 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2588 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2589 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2594 /* another happy customer */
2595 numVertsIlluminated++;
2598 /* set average color */
2600 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2601 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2605 VectorCopy( ambientColor, avgColors[ 0 ] );
2608 /* clean up and store vertex color */
2609 for ( i = 0; i < ds->numVerts; i++ )
2611 /* get vertex luxel */
2612 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2614 /* store average in occluded vertexes */
2615 if ( radVertLuxel[ 0 ] < 0.0f ) {
2616 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2618 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2619 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2622 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2627 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2630 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2631 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2634 if ( bouncing || bounce == 0 || !bounceOnly ) {
2635 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2637 if ( !info->si->noVertexLight ) {
2638 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2643 /* free light list */
2644 FreeTraceLights( &trace );
2646 /* return to sender */
2650 /* -----------------------------------------------------------------
2651 reconstitute vertex lighting from the luxels
2652 ----------------------------------------------------------------- */
2654 /* set styles from lightmap */
2655 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2656 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2658 /* get max search radius */
2660 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2662 /* walk the surface verts */
2663 verts = yDrawVerts + ds->firstVert;
2664 for ( i = 0; i < ds->numVerts; i++ )
2666 /* do each lightmap */
2667 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2670 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2674 /* get luxel coords */
2675 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2676 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2680 else if ( x >= lm->sw ) {
2686 else if ( y >= lm->sh ) {
2690 /* get vertex luxels */
2691 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2692 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2694 /* color the luxel with the normal? */
2696 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2697 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2698 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2701 /* color the luxel with surface num? */
2702 else if ( debugSurfaces ) {
2703 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2706 /* divine color from the superluxels */
2709 /* increasing radius */
2710 VectorClear( radVertLuxel );
2712 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2714 /* sample within radius */
2715 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
2717 if ( sy < 0 || sy >= lm->sh ) {
2721 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
2723 if ( sx < 0 || sx >= lm->sw ) {
2727 /* get luxel particulars */
2728 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2729 cluster = SUPER_CLUSTER( sx, sy );
2730 if ( *cluster < 0 ) {
2734 /* testing: must be brigher than ambient color */
2735 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2738 /* add its distinctiveness to our own */
2739 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2740 samples += luxel[ 3 ];
2746 if ( samples > 0.0f ) {
2747 VectorDivide( radVertLuxel, samples, radVertLuxel );
2750 VectorCopy( ambientColor, radVertLuxel );
2754 /* store into floating point storage */
2755 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2756 numVertsIlluminated++;
2758 /* store into bytes (for vertex approximation) */
2759 if ( !info->si->noVertexLight ) {
2760 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2768 /* -------------------------------------------------------------------------------
2770 light optimization (-fast)
2772 creates a list of lights that will affect a surface and stores it in tw
2773 this is to optimize surface lighting by culling out as many of the
2774 lights in the world as possible from further calculation
2776 ------------------------------------------------------------------------------- */
2780 determines opaque brushes in the world and find sky shaders for sunlight calculations
2783 void SetupBrushes( void ){
2784 int i, j, b, compileFlags;
2787 bspBrushSide_t *side;
2788 bspShader_t *shader;
2793 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
2796 if ( opaqueBrushes == NULL ) {
2797 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
2801 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
2802 numOpaqueBrushes = 0;
2804 /* walk the list of worldspawn brushes */
2805 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
2808 b = bspModels[ 0 ].firstBSPBrush + i;
2809 brush = &bspBrushes[ b ];
2811 /* check all sides */
2814 for ( j = 0; j < brush->numSides && inside; j++ )
2816 /* do bsp shader calculations */
2817 side = &bspBrushSides[ brush->firstSide + j ];
2818 shader = &bspShaders[ side->shaderNum ];
2820 /* get shader info */
2821 si = ShaderInfoForShader( shader->shader );
2826 /* or together compile flags */
2827 compileFlags |= si->compileFlags;
2830 /* determine if this brush is opaque to light */
2831 if ( !( compileFlags & C_TRANSLUCENT ) ) {
2832 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
2838 /* emit some statistics */
2839 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
2846 determines if two clusters are visible to each other using the PVS
2849 qboolean ClusterVisible( int a, int b ){
2850 int portalClusters, leafBytes;
2855 if ( a < 0 || b < 0 ) {
2865 if ( numBSPVisBytes <= 8 ) {
2870 portalClusters = ( (int *) bspVisBytes )[ 0 ];
2871 leafBytes = ( (int*) bspVisBytes )[ 1 ];
2872 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
2875 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
2885 borrowed from vlight.c
2888 int PointInLeafNum_r( vec3_t point, int nodenum ){
2895 while ( nodenum >= 0 )
2897 node = &bspNodes[ nodenum ];
2898 plane = &bspPlanes[ node->planeNum ];
2899 dist = DotProduct( point, plane->normal ) - plane->dist;
2901 nodenum = node->children[ 0 ];
2903 else if ( dist < -0.1 ) {
2904 nodenum = node->children[ 1 ];
2908 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
2909 if ( bspLeafs[ leafnum ].cluster != -1 ) {
2912 nodenum = node->children[ 1 ];
2916 leafnum = -nodenum - 1;
2924 borrowed from vlight.c
2927 int PointInLeafNum( vec3_t point ){
2928 return PointInLeafNum_r( point, 0 );
2934 ClusterVisibleToPoint() - ydnar
2935 returns qtrue if point can "see" cluster
2938 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
2942 /* get leafNum for point */
2943 pointCluster = ClusterForPoint( point );
2944 if ( pointCluster < 0 ) {
2949 return ClusterVisible( pointCluster, cluster );
2955 ClusterForPoint() - ydnar
2956 returns the pvs cluster for point
2959 int ClusterForPoint( vec3_t point ){
2963 /* get leafNum for point */
2964 leafNum = PointInLeafNum( point );
2965 if ( leafNum < 0 ) {
2969 /* return the cluster */
2970 return bspLeafs[ leafNum ].cluster;
2976 ClusterForPointExt() - ydnar
2977 also takes brushes into account for occlusion testing
2980 int ClusterForPointExt( vec3_t point, float epsilon ){
2981 int i, j, b, leafNum, cluster;
2984 int *brushes, numBSPBrushes;
2990 /* get leaf for point */
2991 leafNum = PointInLeafNum( point );
2992 if ( leafNum < 0 ) {
2995 leaf = &bspLeafs[ leafNum ];
2997 /* get the cluster */
2998 cluster = leaf->cluster;
2999 if ( cluster < 0 ) {
3003 /* transparent leaf, so check point against all brushes in the leaf */
3004 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3005 numBSPBrushes = leaf->numBSPLeafBrushes;
3006 for ( i = 0; i < numBSPBrushes; i++ )
3010 if ( b > maxOpaqueBrush ) {
3013 brush = &bspBrushes[ b ];
3014 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3018 /* check point against all planes */
3020 for ( j = 0; j < brush->numSides && inside; j++ )
3022 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3023 dot = DotProduct( point, plane->normal );
3025 if ( dot > epsilon ) {
3030 /* if inside, return bogus cluster */
3036 /* if the point made it this far, it's not inside any opaque brushes */
3043 ClusterForPointExtFilter() - ydnar
3044 adds cluster checking against a list of known valid clusters
3047 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3051 /* get cluster for point */
3052 cluster = ClusterForPointExt( point, epsilon );
3054 /* check if filtering is necessary */
3055 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3060 for ( i = 0; i < numClusters; i++ )
3062 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3074 ShaderForPointInLeaf() - ydnar
3075 checks a point against all brushes in a leaf, returning the shader of the brush
3076 also sets the cumulative surface and content flags for the brush hit
3079 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3083 int *brushes, numBSPBrushes;
3086 bspBrushSide_t *side;
3088 bspShader_t *shader;
3089 int allSurfaceFlags, allContentFlags;
3092 /* clear things out first */
3097 if ( leafNum < 0 ) {
3100 leaf = &bspLeafs[ leafNum ];
3102 /* transparent leaf, so check point against all brushes in the leaf */
3103 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3104 numBSPBrushes = leaf->numBSPLeafBrushes;
3105 for ( i = 0; i < numBSPBrushes; i++ )
3108 brush = &bspBrushes[ brushes[ i ] ];
3110 /* check point against all planes */
3112 allSurfaceFlags = 0;
3113 allContentFlags = 0;
3114 for ( j = 0; j < brush->numSides && inside; j++ )
3116 side = &bspBrushSides[ brush->firstSide + j ];
3117 plane = &bspPlanes[ side->planeNum ];
3118 dot = DotProduct( point, plane->normal );
3120 if ( dot > epsilon ) {
3125 shader = &bspShaders[ side->shaderNum ];
3126 allSurfaceFlags |= shader->surfaceFlags;
3127 allContentFlags |= shader->contentFlags;
3131 /* handle if inside */
3133 /* if there are desired flags, check for same and continue if they aren't matched */
3134 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3137 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3141 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3142 *surfaceFlags = allSurfaceFlags;
3143 *contentFlags = allContentFlags;
3144 return brush->shaderNum;
3148 /* if the point made it this far, it's not inside any brushes */
3156 chops a bounding box by the plane defined by origin and normal
3157 returns qfalse if the bounds is entirely clipped away
3159 this is not exactly the fastest way to do this...
3162 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3163 /* FIXME: rewrite this so it doesn't use bloody brushes */
3171 calculates each light's effective envelope,
3172 taking into account brightness, type, and pvs.
3175 #define LIGHT_EPSILON 0.125f
3176 #define LIGHT_NUDGE 2.0f
3178 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3179 int i, x, y, z, x1, y1, z1;
3180 light_t *light, *light2, **owner;
3182 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3183 float radius, intensity;
3184 light_t *buckets[ 256 ];
3187 /* early out for weird cases where there are no lights */
3188 if ( lights == NULL ) {
3193 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3197 numCulledLights = 0;
3199 while ( *owner != NULL )
3204 /* handle negative lights */
3205 if ( light->photons < 0.0f || light->add < 0.0f ) {
3206 light->photons *= -1.0f;
3207 light->add *= -1.0f;
3208 light->flags |= LIGHT_NEGATIVE;
3212 if ( light->type == EMIT_SUN ) {
3215 light->envelope = MAX_WORLD_COORD * 8.0f;
3216 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3217 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3220 /* everything else */
3223 /* get pvs cluster for light */
3224 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3226 /* invalid cluster? */
3227 if ( light->cluster < 0 ) {
3228 /* nudge the sample point around a bit */
3229 for ( x = 0; x < 4; x++ )
3231 /* two's complement 0, 1, -1, 2, -2, etc */
3232 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3234 for ( y = 0; y < 4; y++ )
3236 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3238 for ( z = 0; z < 4; z++ )
3240 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3243 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3244 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3245 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3247 /* try at nudged origin */
3248 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3249 if ( light->cluster < 0 ) {
3254 VectorCopy( origin, light->origin );
3260 /* only calculate for lights in pvs and outside of opaque brushes */
3261 if ( light->cluster >= 0 ) {
3262 /* set light fast flag */
3264 light->flags |= LIGHT_FAST_TEMP;
3267 light->flags &= ~LIGHT_FAST_TEMP;
3269 if ( light->si && light->si->noFast ) {
3270 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3273 /* clear light envelope */
3274 light->envelope = 0;
3276 /* handle area lights */
3277 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3278 /* ugly hack to calculate extent for area lights, but only done once */
3279 VectorScale( light->normal, -1.0f, dir );
3280 for ( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3284 VectorMA( light->origin, radius, light->normal, origin );
3285 factor = PointToPolygonFormFactor( origin, dir, light->w );
3286 if ( factor < 0.0f ) {
3289 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3290 light->envelope = radius;
3294 /* check for fast mode */
3295 if ( !( light->flags & LIGHT_FAST ) && !( light->flags & LIGHT_FAST_TEMP ) ) {
3296 light->envelope = MAX_WORLD_COORD * 8.0f;
3302 intensity = light->photons;
3306 if ( light->envelope <= 0.0f ) {
3307 /* solve distance for non-distance lights */
3308 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3309 light->envelope = MAX_WORLD_COORD * 8.0f;
3312 /* solve distance for linear lights */
3313 else if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3314 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3315 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3319 add = angle * light->photons * linearScale - (dist * light->fade);
3320 T = (light->photons * linearScale) - (dist * light->fade);
3321 T + (dist * light->fade) = (light->photons * linearScale);
3322 dist * light->fade = (light->photons * linearScale) - T;
3323 dist = ((light->photons * linearScale) - T) / light->fade;
3326 /* solve for inverse square falloff */
3328 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3332 add = light->photons / (dist * dist);
3333 T = light->photons / (dist * dist);
3334 T * (dist * dist) = light->photons;
3335 dist = sqrt( light->photons / T );
3339 /* chop radius against pvs */
3342 ClearBounds( mins, maxs );
3344 /* check all leaves */
3345 for ( i = 0; i < numBSPLeafs; i++ )
3348 leaf = &bspLeafs[ i ];
3351 if ( leaf->cluster < 0 ) {
3354 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3358 /* add this leafs bbox to the bounds */
3359 VectorCopy( leaf->mins, origin );
3360 AddPointToBounds( origin, mins, maxs );
3361 VectorCopy( leaf->maxs, origin );
3362 AddPointToBounds( origin, mins, maxs );
3365 /* test to see if bounds encompass light */
3366 for ( i = 0; i < 3; i++ )
3368 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3369 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3370 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3371 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3372 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3373 AddPointToBounds( light->origin, mins, maxs );
3377 /* chop the bounds by a plane for area lights and spotlights */
3378 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3379 ChopBounds( mins, maxs, light->origin, light->normal );
3383 VectorCopy( mins, light->mins );
3384 VectorCopy( maxs, light->maxs );
3386 /* reflect bounds around light origin */
3387 //% VectorMA( light->origin, -1.0f, origin, origin );
3388 VectorScale( light->origin, 2, origin );
3389 VectorSubtract( origin, maxs, origin );
3390 AddPointToBounds( origin, mins, maxs );
3391 //% VectorMA( light->origin, -1.0f, mins, origin );
3392 VectorScale( light->origin, 2, origin );
3393 VectorSubtract( origin, mins, origin );
3394 AddPointToBounds( origin, mins, maxs );
3396 /* calculate spherical bounds */
3397 VectorSubtract( maxs, light->origin, dir );
3398 radius = (float) VectorLength( dir );
3400 /* if this radius is smaller than the envelope, then set the envelope to it */
3401 if ( radius < light->envelope ) {
3402 light->envelope = radius;
3403 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3406 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3409 /* add grid/surface only check */
3411 if ( !( light->flags & LIGHT_GRID ) ) {
3412 light->envelope = 0.0f;
3417 if ( !( light->flags & LIGHT_SURFACES ) ) {
3418 light->envelope = 0.0f;
3424 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3426 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3428 /* delete the light */
3430 *owner = light->next;
3431 if ( light->w != NULL ) {
3439 /* square envelope */
3440 light->envelope2 = ( light->envelope * light->envelope );
3442 /* increment light count */
3445 /* set next light */
3446 owner = &( ( **owner ).next );
3449 /* bucket sort lights by style */
3450 memset( buckets, 0, sizeof( buckets ) );
3452 for ( light = lights; light != NULL; light = light2 )
3454 /* get next light */
3455 light2 = light->next;
3457 /* filter into correct bucket */
3458 light->next = buckets[ light->style ];
3459 buckets[ light->style ] = light;
3461 /* if any styled light is present, automatically set nocollapse */
3462 if ( light->style != LS_NORMAL ) {
3467 /* filter back into light list */
3469 for ( i = 255; i >= 0; i-- )
3472 for ( light = buckets[ i ]; light != NULL; light = light2 )
3474 light2 = light->next;
3475 light->next = lights;
3480 /* emit some statistics */
3481 Sys_Printf( "%9d total lights\n", numLights );
3482 Sys_Printf( "%9d culled lights\n", numCulledLights );
3488 CreateTraceLightsForBounds()
3489 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3492 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3495 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3496 float radius, dist, length;
3499 /* potential pre-setup */
3500 if ( numLights == 0 ) {
3501 SetupEnvelopes( qfalse, fast );
3505 //% 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 ] );
3507 /* allocate the light list */
3508 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3509 trace->numLights = 0;
3511 /* calculate spherical bounds */
3512 VectorAdd( mins, maxs, origin );
3513 VectorScale( origin, 0.5f, origin );
3514 VectorSubtract( maxs, origin, dir );
3515 radius = (float) VectorLength( dir );
3517 /* get length of normal vector */
3518 if ( normal != NULL ) {
3519 length = VectorLength( normal );
3523 normal = nullVector;
3527 /* test each light and see if it reaches the sphere */
3528 /* note: the attenuation code MUST match LightingAtSample() */
3529 for ( light = lights; light; light = light->next )
3531 /* check zero sized envelope */
3532 if ( light->envelope <= 0 ) {
3533 lightsEnvelopeCulled++;
3538 if ( !( light->flags & flags ) ) {
3542 /* sunlight skips all this nonsense */
3543 if ( light->type != EMIT_SUN ) {
3549 /* check against pvs cluster */
3550 if ( numClusters > 0 && clusters != NULL ) {
3551 for ( i = 0; i < numClusters; i++ )
3553 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3559 if ( i == numClusters ) {
3560 lightsClusterCulled++;
3565 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3566 VectorSubtract( light->origin, origin, dir );
3567 dist = VectorLength( dir );
3568 dist -= light->envelope;
3571 lightsEnvelopeCulled++;
3575 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3578 for ( i = 0; i < 3; i++ )
3580 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3585 lightsBoundsCulled++;
3591 /* planar surfaces (except twosided surfaces) have a couple more checks */
3592 if ( length > 0.0f && trace->twoSided == qfalse ) {
3593 /* lights coplanar with a surface won't light it */
3594 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3595 lightsPlaneCulled++;
3599 /* check to see if light is behind the plane */
3600 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3601 lightsPlaneCulled++;
3606 /* add this light */
3607 trace->lights[ trace->numLights++ ] = light;
3610 /* make last night null */
3611 trace->lights[ trace->numLights ] = NULL;
3616 void FreeTraceLights( trace_t *trace ){
3617 if ( trace->lights != NULL ) {
3618 free( trace->lights );
3625 CreateTraceLightsForSurface()
3626 creates a list of lights that can potentially affect a drawsurface
3629 void CreateTraceLightsForSurface( int num, trace_t *trace ){
3631 vec3_t mins, maxs, normal;
3633 bspDrawSurface_t *ds;
3634 surfaceInfo_t *info;
3642 /* get drawsurface and info */
3643 ds = &bspDrawSurfaces[ num ];
3644 info = &surfaceInfos[ num ];
3646 /* get the mins/maxs for the dsurf */
3647 ClearBounds( mins, maxs );
3648 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3649 for ( i = 0; i < ds->numVerts; i++ )
3651 dv = &yDrawVerts[ ds->firstVert + i ];
3652 AddPointToBounds( dv->xyz, mins, maxs );
3653 if ( !VectorCompare( dv->normal, normal ) ) {
3654 VectorClear( normal );
3658 /* create the lights for the bounding box */
3659 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );