1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1159 /* get drawverts and map first triangle */
1160 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1161 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1162 MapTriangle( lm, info, dv, mapNonAxial );
1164 /* get drawverts and map second triangle */
1165 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1166 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1167 MapTriangle( lm, info, dv, mapNonAxial );
1183 /* -----------------------------------------------------------------
1184 average and clean up luxel normals
1185 ----------------------------------------------------------------- */
1187 /* walk the luxels */
1188 for( y = 0; y < lm->sh; y++ )
1190 for( x = 0; x < lm->sw; x++ )
1193 luxel = SUPER_LUXEL( 0, x, y );
1194 normal = SUPER_NORMAL( x, y );
1195 cluster = SUPER_CLUSTER( x, y );
1197 /* only look at mapped luxels */
1201 /* the normal data could be the sum of multiple samples */
1202 if( luxel[ 3 ] > 1.0f )
1203 VectorNormalize( normal, normal );
1205 /* mark this luxel as having only one normal */
1210 /* non-planar surfaces stop here */
1211 if( lm->plane == NULL )
1214 /* -----------------------------------------------------------------
1215 map occluded or unuxed luxels
1216 ----------------------------------------------------------------- */
1218 /* walk the luxels */
1219 radius = floor( superSample / 2 );
1220 radius = radius > 0 ? radius : 1.0f;
1222 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1224 for( y = 0; y < lm->sh; y++ )
1226 for( x = 0; x < lm->sw; x++ )
1229 luxel = SUPER_LUXEL( 0, x, y );
1230 normal = SUPER_NORMAL( x, y );
1231 cluster = SUPER_CLUSTER( x, y );
1233 /* only look at unmapped luxels */
1234 if( *cluster != CLUSTER_UNMAPPED )
1237 /* divine a normal and origin from neighboring luxels */
1238 VectorClear( fake.xyz );
1239 VectorClear( fake.normal );
1240 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1241 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1243 for( sy = (y - 1); sy <= (y + 1); sy++ )
1245 if( sy < 0 || sy >= lm->sh )
1248 for( sx = (x - 1); sx <= (x + 1); sx++ )
1250 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1253 /* get neighboring luxel */
1254 luxel = SUPER_LUXEL( 0, sx, sy );
1255 origin = SUPER_ORIGIN( sx, sy );
1256 normal = SUPER_NORMAL( sx, sy );
1257 cluster = SUPER_CLUSTER( sx, sy );
1259 /* only consider luxels mapped in previous passes */
1260 if( *cluster < 0 || luxel[ 0 ] >= pass )
1263 /* add its distinctiveness to our own */
1264 VectorAdd( fake.xyz, origin, fake.xyz );
1265 VectorAdd( fake.normal, normal, fake.normal );
1266 samples += luxel[ 3 ];
1271 if( samples == 0.0f )
1275 VectorDivide( fake.xyz, samples, fake.xyz );
1276 //% VectorDivide( fake.normal, samples, fake.normal );
1277 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1280 /* map the fake vert */
1281 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1286 /* -----------------------------------------------------------------
1287 average and clean up luxel normals
1288 ----------------------------------------------------------------- */
1290 /* walk the luxels */
1291 for( y = 0; y < lm->sh; y++ )
1293 for( x = 0; x < lm->sw; x++ )
1296 luxel = SUPER_LUXEL( 0, x, y );
1297 normal = SUPER_NORMAL( x, y );
1298 cluster = SUPER_CLUSTER( x, y );
1300 /* only look at mapped luxels */
1304 /* the normal data could be the sum of multiple samples */
1305 if( luxel[ 3 ] > 1.0f )
1306 VectorNormalize( normal, normal );
1308 /* mark this luxel as having only one normal */
1316 for( y = 0; y < lm->sh; y++ )
1318 for( x = 0; x < lm->sw; x++ )
1323 cluster = SUPER_CLUSTER( x, y );
1324 origin = SUPER_ORIGIN( x, y );
1325 normal = SUPER_NORMAL( x, y );
1326 luxel = SUPER_LUXEL( x, y );
1331 /* check if within the bounding boxes of all surfaces referenced */
1332 ClearBounds( mins, maxs );
1333 for( n = 0; n < lm->numLightSurfaces; n++ )
1336 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1337 TOL = info->sampleSize + 2;
1338 AddPointToBounds( info->mins, mins, maxs );
1339 AddPointToBounds( info->maxs, mins, maxs );
1340 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1341 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1342 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1347 if( n < lm->numLightSurfaces )
1350 /* report bogus origin */
1351 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",
1352 rawLightmapNum, x, y, *cluster,
1353 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1354 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1355 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1366 sets up dirtmap (ambient occlusion)
1369 #define DIRT_CONE_ANGLE 88 /* degrees */
1370 #define DIRT_NUM_ANGLE_STEPS 16
1371 #define DIRT_NUM_ELEVATION_STEPS 3
1372 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1374 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1375 static int numDirtVectors = 0;
1377 void SetupDirt( void )
1380 float angle, elevation, angleStep, elevationStep;
1384 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1386 /* calculate angular steps */
1387 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1388 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1392 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1394 /* iterate elevation */
1395 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1397 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1398 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1399 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1404 /* emit some statistics */
1405 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1411 calculates dirt value for a given sample
1414 float DirtForSample( trace_t *trace )
1417 float gatherDirt, outDirt, angle, elevation, ooDepth;
1418 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1424 if( trace == NULL || trace->cluster < 0 )
1429 ooDepth = 1.0f / dirtDepth;
1430 VectorCopy( trace->normal, normal );
1432 /* check if the normal is aligned to the world-up */
1433 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1435 if( normal[ 2 ] == 1.0f )
1437 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1440 else if( normal[ 2 ] == -1.0f )
1442 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1443 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1448 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1449 CrossProduct( normal, worldUp, myRt );
1450 VectorNormalize( myRt, myRt );
1451 CrossProduct( myRt, normal, myUp );
1452 VectorNormalize( myUp, myUp );
1455 /* 1 = random mode, 0 (well everything else) = non-random mode */
1459 for( i = 0; i < numDirtVectors; i++ )
1461 /* get random vector */
1462 angle = Random() * DEG2RAD( 360.0f );
1463 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1464 temp[ 0 ] = cos( angle ) * sin( elevation );
1465 temp[ 1 ] = sin( angle ) * sin( elevation );
1466 temp[ 2 ] = cos( elevation );
1468 /* transform into tangent space */
1469 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1470 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1471 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1474 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1475 SetupTrace( trace );
1479 if( trace->opaque && !(trace->compileFlags & C_SKY) )
1481 VectorSubtract( trace->hit, trace->origin, displacement );
1482 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1488 /* iterate through ordered vectors */
1489 for( i = 0; i < numDirtVectors; i++ )
1491 /* transform vector into tangent space */
1492 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1493 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1494 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1497 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1498 SetupTrace( trace );
1504 VectorSubtract( trace->hit, trace->origin, displacement );
1505 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1511 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1512 SetupTrace( trace );
1518 VectorSubtract( trace->hit, trace->origin, displacement );
1519 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1523 if( gatherDirt <= 0.0f )
1526 /* apply gain (does this even do much? heh) */
1527 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1528 if( outDirt > 1.0f )
1532 outDirt *= dirtScale;
1533 if( outDirt > 1.0f )
1536 /* return to sender */
1537 return 1.0f - outDirt;
1544 calculates dirty fraction for each luxel
1547 void DirtyRawLightmap( int rawLightmapNum )
1549 int i, x, y, sx, sy, *cluster;
1550 float *origin, *normal, *dirt, *dirt2, average, samples;
1552 surfaceInfo_t *info;
1557 /* bail if this number exceeds the number of raw lightmaps */
1558 if( rawLightmapNum >= numRawLightmaps )
1562 lm = &rawLightmaps[ rawLightmapNum ];
1565 trace.testOcclusion = qtrue;
1566 trace.forceSunlight = qfalse;
1567 trace.recvShadows = lm->recvShadows;
1568 trace.numSurfaces = lm->numLightSurfaces;
1569 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1570 trace.inhibitRadius = 0.0f;
1571 trace.testAll = qfalse;
1573 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1574 trace.twoSided = qfalse;
1575 for( i = 0; i < trace.numSurfaces; i++ )
1578 info = &surfaceInfos[ trace.surfaces[ i ] ];
1580 /* check twosidedness */
1581 if( info->si->twoSided )
1583 trace.twoSided = qtrue;
1589 for( i = 0; i < trace.numSurfaces; i++ )
1592 info = &surfaceInfos[ trace.surfaces[ i ] ];
1594 /* check twosidedness */
1595 if( info->si->noDirty )
1603 for( y = 0; y < lm->sh; y++ )
1605 for( x = 0; x < lm->sw; x++ )
1608 cluster = SUPER_CLUSTER( x, y );
1609 origin = SUPER_ORIGIN( x, y );
1610 normal = SUPER_NORMAL( x, y );
1611 dirt = SUPER_DIRT( x, y );
1613 /* set default dirt */
1616 /* only look at mapped luxels */
1620 /* don't apply dirty on this surface */
1628 trace.cluster = *cluster;
1629 VectorCopy( origin, trace.origin );
1630 VectorCopy( normal, trace.normal );
1633 *dirt = DirtForSample( &trace );
1637 /* testing no filtering */
1641 for( y = 0; y < lm->sh; y++ )
1643 for( x = 0; x < lm->sw; x++ )
1646 cluster = SUPER_CLUSTER( x, y );
1647 dirt = SUPER_DIRT( x, y );
1649 /* filter dirt by adjacency to unmapped luxels */
1652 for( sy = (y - 1); sy <= (y + 1); sy++ )
1654 if( sy < 0 || sy >= lm->sh )
1657 for( sx = (x - 1); sx <= (x + 1); sx++ )
1659 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1662 /* get neighboring luxel */
1663 cluster = SUPER_CLUSTER( sx, sy );
1664 dirt2 = SUPER_DIRT( sx, sy );
1665 if( *cluster < 0 || *dirt2 <= 0.0f )
1674 if( samples <= 0.0f )
1679 if( samples <= 0.0f )
1683 *dirt = average / samples;
1692 calculates the pvs cluster, origin, normal of a sub-luxel
1695 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1697 int i, *cluster, *cluster2;
1698 float *origin, *origin2, *normal; //% , *normal2;
1699 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1702 /* calulate x vector */
1703 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1705 cluster = SUPER_CLUSTER( x, y );
1706 origin = SUPER_ORIGIN( x, y );
1707 //% normal = SUPER_NORMAL( x, y );
1708 cluster2 = SUPER_CLUSTER( x + 1, y );
1709 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1710 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1712 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1714 cluster = SUPER_CLUSTER( x - 1, y );
1715 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1716 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1717 cluster2 = SUPER_CLUSTER( x, y );
1718 origin2 = SUPER_ORIGIN( x, y );
1719 //% normal2 = SUPER_NORMAL( x, y );
1723 Error( "Spurious lightmap S vector\n" );
1726 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1727 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1729 /* calulate y vector */
1730 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1732 cluster = SUPER_CLUSTER( x, y );
1733 origin = SUPER_ORIGIN( x, y );
1734 //% normal = SUPER_NORMAL( x, y );
1735 cluster2 = SUPER_CLUSTER( x, y + 1 );
1736 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1737 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1739 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1741 cluster = SUPER_CLUSTER( x, y - 1 );
1742 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1743 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1744 cluster2 = SUPER_CLUSTER( x, y );
1745 origin2 = SUPER_ORIGIN( x, y );
1746 //% normal2 = SUPER_NORMAL( x, y );
1749 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1751 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1752 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1754 /* calculate new origin */
1755 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1756 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1757 for( i = 0; i < 3; i++ )
1758 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1761 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1762 if( *sampleCluster < 0 )
1765 /* calculate new normal */
1766 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1767 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1768 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1770 normal = SUPER_NORMAL( x, y );
1771 VectorCopy( normal, sampleNormal );
1779 SubsampleRawLuxel_r()
1780 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1783 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1785 int b, samples, mapped, lighted;
1788 vec3_t deluxel[ 3 ];
1789 vec3_t origin[ 4 ], normal[ 4 ];
1790 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1791 vec3_t color, direction = { 0, 0, 0 }, total;
1795 if( lightLuxel[ 3 ] >= lightSamples )
1799 VectorClear( total );
1803 /* make 2x2 subsample stamp */
1804 for( b = 0; b < 4; b++ )
1807 VectorCopy( sampleOrigin, origin[ b ] );
1809 /* calculate position */
1810 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1817 /* increment sample count */
1818 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1821 trace->cluster = *cluster;
1822 VectorCopy( origin[ b ], trace->origin );
1823 VectorCopy( normal[ b ], trace->normal );
1827 LightContributionToSample( trace );
1828 if(trace->forceSubsampling > 1.0f)
1830 /* alphashadow: we subsample as deep as we can */
1836 /* add to totals (fixme: make contrast function) */
1837 VectorCopy( trace->color, luxel[ b ] );
1840 VectorCopy( trace->directionContribution, deluxel[ b ] );
1842 VectorAdd( total, trace->color, total );
1843 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1847 /* subsample further? */
1848 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1849 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1850 lighted != 0 && lighted != mapped )
1852 for( b = 0; b < 4; b++ )
1854 if( cluster[ b ] < 0 )
1856 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1861 //% VectorClear( color );
1863 VectorCopy( lightLuxel, color );
1866 VectorCopy( lightDeluxel, direction );
1869 for( b = 0; b < 4; b++ )
1871 if( cluster[ b ] < 0 )
1873 VectorAdd( color, luxel[ b ], color );
1876 VectorAdd( direction, deluxel[ b ], direction );
1885 color[ 0 ] /= samples;
1886 color[ 1 ] /= samples;
1887 color[ 2 ] /= samples;
1890 VectorCopy( color, lightLuxel );
1891 lightLuxel[ 3 ] += 1.0f;
1895 direction[ 0 ] /= samples;
1896 direction[ 1 ] /= samples;
1897 direction[ 2 ] /= samples;
1898 VectorCopy( direction, lightDeluxel );
1903 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1904 static void GaussLikeRandom(float sigma, float *x, float *y)
1907 r = Random() * 2 * Q_PI;
1908 *x = sigma * 2.73861278752581783822 * cos(r);
1909 *y = sigma * 2.73861278752581783822 * sin(r);
1916 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1920 vec3_t origin, normal;
1921 vec3_t total, totaldirection;
1924 VectorClear( total );
1925 VectorClear( totaldirection );
1927 for(b = 0; b < lightSamples; ++b)
1930 VectorCopy( sampleOrigin, origin );
1931 GaussLikeRandom(bias, &dx, &dy);
1933 /* calculate position */
1934 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1941 trace->cluster = cluster;
1942 VectorCopy( origin, trace->origin );
1943 VectorCopy( normal, trace->normal );
1945 LightContributionToSample( trace );
1946 VectorAdd( total, trace->color, total );
1949 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1957 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1958 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1959 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1963 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1964 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1965 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1973 IlluminateRawLightmap()
1974 illuminates the luxels
1977 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1978 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1979 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1981 void IlluminateRawLightmap( int rawLightmapNum )
1983 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1984 int *cluster, *cluster2, mapped, lighted, totalLighted;
1985 size_t llSize, ldSize;
1987 surfaceInfo_t *info;
1988 qboolean filterColor, filterDir;
1990 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1991 unsigned char *flag;
1992 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1993 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1994 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1996 float stackLightLuxels[ STACK_LL_SIZE ];
1999 /* bail if this number exceeds the number of raw lightmaps */
2000 if( rawLightmapNum >= numRawLightmaps )
2004 lm = &rawLightmaps[ rawLightmapNum ];
2007 trace.testOcclusion = !noTrace;
2008 trace.forceSunlight = qfalse;
2009 trace.recvShadows = lm->recvShadows;
2010 trace.numSurfaces = lm->numLightSurfaces;
2011 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2012 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2014 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2015 trace.twoSided = qfalse;
2016 for( i = 0; i < trace.numSurfaces; i++ )
2019 info = &surfaceInfos[ trace.surfaces[ i ] ];
2021 /* check twosidedness */
2022 if( info->si->twoSided )
2024 trace.twoSided = qtrue;
2029 /* create a culled light list for this raw lightmap */
2030 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2032 /* -----------------------------------------------------------------
2034 ----------------------------------------------------------------- */
2037 numLuxelsIlluminated += (lm->sw * lm->sh);
2039 /* test debugging state */
2040 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2042 /* debug fill the luxels */
2043 for( y = 0; y < lm->sh; y++ )
2045 for( x = 0; x < lm->sw; x++ )
2048 cluster = SUPER_CLUSTER( x, y );
2050 /* only fill mapped luxels */
2054 /* get particulars */
2055 luxel = SUPER_LUXEL( 0, x, y );
2056 origin = SUPER_ORIGIN( x, y );
2057 normal = SUPER_NORMAL( x, y );
2059 /* color the luxel with raw lightmap num? */
2061 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2063 /* color the luxel with lightmap axis? */
2064 else if( debugAxis )
2066 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2067 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2068 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2071 /* color the luxel with luxel cluster? */
2072 else if( debugCluster )
2073 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2075 /* color the luxel with luxel origin? */
2076 else if( debugOrigin )
2078 VectorSubtract( lm->maxs, lm->mins, temp );
2079 VectorScale( temp, (1.0f / 255.0f), temp );
2080 VectorSubtract( origin, lm->mins, temp2 );
2081 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2082 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2083 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2086 /* color the luxel with the normal */
2087 else if( normalmap )
2089 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2090 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2091 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2094 /* otherwise clear it */
2096 VectorClear( luxel );
2105 /* allocate temporary per-light luxel storage */
2106 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2107 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2108 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2109 lightLuxels = stackLightLuxels;
2111 lightLuxels = safe_malloc( llSize );
2113 lightDeluxels = safe_malloc( ldSize );
2115 lightDeluxels = NULL;
2118 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2120 /* set ambient color */
2121 for( y = 0; y < lm->sh; y++ )
2123 for( x = 0; x < lm->sw; x++ )
2126 cluster = SUPER_CLUSTER( x, y );
2127 luxel = SUPER_LUXEL( 0, x, y );
2128 normal = SUPER_NORMAL( x, y );
2129 deluxel = SUPER_DELUXEL( x, y );
2131 /* blacken unmapped clusters */
2133 VectorClear( luxel );
2138 VectorCopy( ambientColor, luxel );
2141 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2143 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2144 if(brightness < 0.00390625f)
2145 brightness = 0.00390625f;
2147 VectorScale( normal, brightness, deluxel );
2154 /* clear styled lightmaps */
2155 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2156 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2158 if( lm->superLuxels[ lightmapNum ] != NULL )
2159 memset( lm->superLuxels[ lightmapNum ], 0, size );
2162 /* debugging code */
2163 //% if( trace.numLights <= 0 )
2164 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2166 /* walk light list */
2167 for( i = 0; i < trace.numLights; i++ )
2170 trace.light = trace.lights[ i ];
2173 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2175 if( lm->styles[ lightmapNum ] == trace.light->style ||
2176 lm->styles[ lightmapNum ] == LS_NONE )
2180 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2181 if( lightmapNum >= MAX_LIGHTMAPS )
2183 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2188 memset( lightLuxels, 0, llSize );
2190 memset( lightDeluxels, 0, ldSize );
2193 /* determine filter radius */
2194 filterRadius = lm->filterRadius > trace.light->filterRadius
2196 : trace.light->filterRadius;
2197 if( filterRadius < 0.0f )
2198 filterRadius = 0.0f;
2200 /* set luxel filter radius */
2201 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2202 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2203 luxelFilterRadius = 1;
2205 /* allocate sampling flags storage */
2206 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2208 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2209 if(lm->superFlags == NULL)
2210 lm->superFlags = safe_malloc( size );
2211 memset( (void *) lm->superFlags, 0, size );
2214 /* initial pass, one sample per luxel */
2215 for( y = 0; y < lm->sh; y++ )
2217 for( x = 0; x < lm->sw; x++ )
2220 cluster = SUPER_CLUSTER( x, y );
2224 /* get particulars */
2225 lightLuxel = LIGHT_LUXEL( x, y );
2226 lightDeluxel = LIGHT_DELUXEL( x, y );
2227 origin = SUPER_ORIGIN( x, y );
2228 normal = SUPER_NORMAL( x, y );
2229 flag = SUPER_FLAG( x, y );
2232 ////////// 27's temp hack for testing edge clipping ////
2233 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2235 lightLuxel[ 1 ] = 255;
2236 lightLuxel[ 3 ] = 1.0f;
2242 /* set contribution count */
2243 lightLuxel[ 3 ] = 1.0f;
2246 trace.cluster = *cluster;
2247 VectorCopy( origin, trace.origin );
2248 VectorCopy( normal, trace.normal );
2250 /* get light for this sample */
2251 LightContributionToSample( &trace );
2252 VectorCopy( trace.color, lightLuxel );
2254 /* add the contribution to the deluxemap */
2257 VectorCopy( trace.directionContribution, lightDeluxel );
2260 /* check for evilness */
2261 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2264 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2267 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2273 /* don't even bother with everything else if nothing was lit */
2274 if( totalLighted == 0 )
2277 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2278 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2279 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2282 for( y = 0; y < (lm->sh - 1); y++ )
2284 for( x = 0; x < (lm->sw - 1); x++ )
2289 VectorClear( total );
2291 /* test 2x2 stamp */
2292 for( t = 0; t < 4; t++ )
2294 /* set sample coords */
2295 sx = x + tests[ t ][ 0 ];
2296 sy = y + tests[ t ][ 1 ];
2299 cluster = SUPER_CLUSTER( sx, sy );
2305 flag = SUPER_FLAG( sx, sy );
2306 if(*flag & FLAG_FORCE_SUBSAMPLING)
2308 /* force a lighted/mapped discrepancy so we subsample */
2313 lightLuxel = LIGHT_LUXEL( sx, sy );
2314 VectorAdd( total, lightLuxel, total );
2315 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2319 /* if total color is under a certain amount, then don't bother subsampling */
2320 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2323 /* if all 4 pixels are either in shadow or light, then don't subsample */
2324 if( lighted != 0 && lighted != mapped )
2326 for( t = 0; t < 4; t++ )
2328 /* set sample coords */
2329 sx = x + tests[ t ][ 0 ];
2330 sy = y + tests[ t ][ 1 ];
2333 cluster = SUPER_CLUSTER( sx, sy );
2336 flag = SUPER_FLAG( sx, sy );
2337 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2339 lightLuxel = LIGHT_LUXEL( sx, sy );
2340 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2341 origin = SUPER_ORIGIN( sx, sy );
2343 /* only subsample shadowed luxels */
2344 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2348 if(lightRandomSamples)
2349 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2351 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2353 *flag |= FLAG_ALREADY_SUBSAMPLED;
2355 /* debug code to colorize subsampled areas to yellow */
2356 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2357 //% VectorSet( luxel, 255, 204, 0 );
2364 /* tertiary pass, apply dirt map (ambient occlusion) */
2368 for( y = 0; y < lm->sh; y++ )
2370 for( x = 0; x < lm->sw; x++ )
2373 cluster = SUPER_CLUSTER( x, y );
2377 /* get particulars */
2378 lightLuxel = LIGHT_LUXEL( x, y );
2379 dirt = SUPER_DIRT( x, y );
2381 /* scale light value */
2382 VectorScale( lightLuxel, *dirt, lightLuxel );
2387 /* allocate sampling lightmap storage */
2388 if( lm->superLuxels[ lightmapNum ] == NULL )
2390 /* allocate sampling lightmap storage */
2391 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2392 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2393 memset( lm->superLuxels[ lightmapNum ], 0, size );
2397 if( lightmapNum > 0 )
2399 lm->styles[ lightmapNum ] = trace.light->style;
2400 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2403 /* copy to permanent luxels */
2404 for( y = 0; y < lm->sh; y++ )
2406 for( x = 0; x < lm->sw; x++ )
2408 /* get cluster and origin */
2409 cluster = SUPER_CLUSTER( x, y );
2412 origin = SUPER_ORIGIN( x, y );
2415 if( luxelFilterRadius )
2418 VectorClear( averageColor );
2419 VectorClear( averageDir );
2422 /* cheaper distance-based filtering */
2423 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2425 if( sy < 0 || sy >= lm->sh )
2428 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2430 if( sx < 0 || sx >= lm->sw )
2433 /* get particulars */
2434 cluster = SUPER_CLUSTER( sx, sy );
2437 lightLuxel = LIGHT_LUXEL( sx, sy );
2438 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2441 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2442 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2444 /* scale luxel by filter weight */
2445 VectorScale( lightLuxel, weight, color );
2446 VectorAdd( averageColor, color, averageColor );
2449 VectorScale( lightDeluxel, weight, direction );
2450 VectorAdd( averageDir, direction, averageDir );
2457 if( samples <= 0.0f )
2460 /* scale into luxel */
2461 luxel = SUPER_LUXEL( lightmapNum, x, y );
2464 /* handle negative light */
2465 if( trace.light->flags & LIGHT_NEGATIVE )
2467 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2468 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2469 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2472 /* handle normal light */
2475 luxel[ 0 ] += averageColor[ 0 ] / samples;
2476 luxel[ 1 ] += averageColor[ 1 ] / samples;
2477 luxel[ 2 ] += averageColor[ 2 ] / samples;
2482 /* scale into luxel */
2483 deluxel = SUPER_DELUXEL( x, y );
2484 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2485 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2486 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2493 /* get particulars */
2494 lightLuxel = LIGHT_LUXEL( x, y );
2495 lightDeluxel = LIGHT_DELUXEL( x, y );
2496 luxel = SUPER_LUXEL( lightmapNum, x, y );
2497 deluxel = SUPER_DELUXEL( x, y );
2499 /* handle negative light */
2500 if( trace.light->flags & LIGHT_NEGATIVE )
2501 VectorScale( averageColor, -1.0f, averageColor );
2506 /* handle negative light */
2507 if( trace.light->flags & LIGHT_NEGATIVE )
2508 VectorSubtract( luxel, lightLuxel, luxel );
2510 /* handle normal light */
2512 VectorAdd( luxel, lightLuxel, luxel );
2516 VectorAdd( deluxel, lightDeluxel, deluxel );
2523 /* free temporary luxels */
2524 if( lightLuxels != stackLightLuxels )
2525 free( lightLuxels );
2528 free( lightDeluxels );
2531 /* free light list */
2532 FreeTraceLights( &trace );
2534 /* floodlight pass */
2536 FloodlightIlluminateLightmap(lm);
2540 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2543 if( lm->superLuxels[ lightmapNum ] == NULL )
2546 for( y = 0; y < lm->sh; y++ )
2548 for( x = 0; x < lm->sw; x++ )
2551 cluster = SUPER_CLUSTER( x, y );
2552 //% if( *cluster < 0 )
2555 /* get particulars */
2556 luxel = SUPER_LUXEL( lightmapNum, x, y );
2557 normal = SUPER_NORMAL ( x, y );
2559 luxel[0]=(normal[0]*127)+127;
2560 luxel[1]=(normal[1]*127)+127;
2561 luxel[2]=(normal[2]*127)+127;
2567 /* -----------------------------------------------------------------
2569 ----------------------------------------------------------------- */
2573 /* walk lightmaps */
2574 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2577 if( lm->superLuxels[ lightmapNum ] == NULL )
2580 /* apply dirt to each luxel */
2581 for( y = 0; y < lm->sh; y++ )
2583 for( x = 0; x < lm->sw; x++ )
2586 cluster = SUPER_CLUSTER( x, y );
2587 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2590 /* get particulars */
2591 luxel = SUPER_LUXEL( lightmapNum, x, y );
2592 dirt = SUPER_DIRT( x, y );
2595 VectorScale( luxel, *dirt, luxel );
2599 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2605 /* -----------------------------------------------------------------
2607 ----------------------------------------------------------------- */
2609 /* walk lightmaps */
2610 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2613 if( lm->superLuxels[ lightmapNum ] == NULL )
2616 /* average occluded luxels from neighbors */
2617 for( y = 0; y < lm->sh; y++ )
2619 for( x = 0; x < lm->sw; x++ )
2621 /* get particulars */
2622 cluster = SUPER_CLUSTER( x, y );
2623 luxel = SUPER_LUXEL( lightmapNum, x, y );
2624 deluxel = SUPER_DELUXEL( x, y );
2625 normal = SUPER_NORMAL( x, y );
2627 /* determine if filtering is necessary */
2628 filterColor = qfalse;
2631 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2632 filterColor = qtrue;
2634 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2637 if( !filterColor && !filterDir )
2640 /* choose seed amount */
2641 VectorClear( averageColor );
2642 VectorClear( averageDir );
2645 /* walk 3x3 matrix */
2646 for( sy = (y - 1); sy <= (y + 1); sy++ )
2648 if( sy < 0 || sy >= lm->sh )
2651 for( sx = (x - 1); sx <= (x + 1); sx++ )
2653 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2656 /* get neighbor's particulars */
2657 cluster2 = SUPER_CLUSTER( sx, sy );
2658 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2659 deluxel2 = SUPER_DELUXEL( sx, sy );
2661 /* ignore unmapped/unlit luxels */
2662 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2663 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2666 /* add its distinctiveness to our own */
2667 VectorAdd( averageColor, luxel2, averageColor );
2668 samples += luxel2[ 3 ];
2670 VectorAdd( averageDir, deluxel2, averageDir );
2675 if( samples <= 0.0f )
2678 /* dark lightmap seams */
2681 if( lightmapNum == 0 )
2682 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2689 VectorDivide( averageColor, samples, luxel );
2693 VectorDivide( averageDir, samples, deluxel );
2695 /* set cluster to -3 */
2697 *cluster = CLUSTER_FLOODED;
2705 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2708 if( lm->superLuxels[ lightmapNum ] == NULL )
2710 for( y = 0; y < lm->sh; y++ )
2711 for( x = 0; x < lm->sw; x++ )
2714 cluster = SUPER_CLUSTER( x, y );
2715 luxel = SUPER_LUXEL( lightmapNum, x, y );
2716 deluxel = SUPER_DELUXEL( x, y );
2717 if(!luxel || !deluxel || !cluster)
2719 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2722 else if(*cluster < 0)
2725 // should have neither deluxemap nor lightmap
2727 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2732 // should have both deluxemap and lightmap
2734 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2744 IlluminateVertexes()
2745 light the surface vertexes
2748 #define VERTEX_NUDGE 4.0f
2750 void IlluminateVertexes( int num )
2752 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2753 int lightmapNum, numAvg;
2754 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2755 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2756 bspDrawSurface_t *ds;
2757 surfaceInfo_t *info;
2759 bspDrawVert_t *verts;
2761 float floodLightAmount;
2765 /* get surface, info, and raw lightmap */
2766 ds = &bspDrawSurfaces[ num ];
2767 info = &surfaceInfos[ num ];
2770 /* -----------------------------------------------------------------
2771 illuminate the vertexes
2772 ----------------------------------------------------------------- */
2774 /* calculate vertex lighting for surfaces without lightmaps */
2775 if( lm == NULL || cpmaHack )
2778 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2779 trace.forceSunlight = info->si->forceSunlight;
2780 trace.recvShadows = info->recvShadows;
2781 trace.numSurfaces = 1;
2782 trace.surfaces = #
2783 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2785 /* twosided lighting */
2786 trace.twoSided = info->si->twoSided;
2788 /* make light list for this surface */
2789 CreateTraceLightsForSurface( num, &trace );
2792 verts = yDrawVerts + ds->firstVert;
2794 memset( avgColors, 0, sizeof( avgColors ) );
2796 /* walk the surface verts */
2797 for( i = 0; i < ds->numVerts; i++ )
2799 /* get vertex luxel */
2800 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2802 /* color the luxel with raw lightmap num? */
2804 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2806 /* color the luxel with luxel origin? */
2807 else if( debugOrigin )
2809 VectorSubtract( info->maxs, info->mins, temp );
2810 VectorScale( temp, (1.0f / 255.0f), temp );
2811 VectorSubtract( origin, lm->mins, temp2 );
2812 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2813 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2814 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2817 /* color the luxel with the normal */
2818 else if( normalmap )
2820 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2821 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2822 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2825 /* illuminate the vertex */
2828 /* clear vertex luxel */
2829 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2831 /* try at initial origin */
2832 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2833 if( trace.cluster >= 0 )
2836 VectorCopy( verts[ i ].xyz, trace.origin );
2837 VectorCopy( verts[ i ].normal, trace.normal );
2840 if( dirty && !bouncing )
2841 dirt = DirtForSample( &trace );
2845 /* jal: floodlight */
2846 floodLightAmount = 0.0f;
2847 VectorClear( floodColor );
2848 if( floodlighty && !bouncing )
2850 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2851 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2855 LightingAtSample( &trace, ds->vertexStyles, colors );
2858 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2861 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2863 /* jal: floodlight */
2864 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2867 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2868 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2869 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2873 /* is this sample bright enough? */
2874 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2875 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2876 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2877 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2879 /* nudge the sample point around a bit */
2880 for( x = 0; x < 5; x++ )
2882 /* two's complement 0, 1, -1, 2, -2, etc */
2883 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2885 for( y = 0; y < 5; y++ )
2887 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2889 for( z = 0; z < 5; z++ )
2891 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2894 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2895 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2896 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2898 /* try at nudged origin */
2899 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2900 if( trace.cluster < 0 )
2904 if( dirty && !bouncing )
2905 dirt = DirtForSample( &trace );
2909 /* jal: floodlight */
2910 floodLightAmount = 0.0f;
2911 VectorClear( floodColor );
2912 if( floodlighty && !bouncing )
2914 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2915 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2919 LightingAtSample( &trace, ds->vertexStyles, colors );
2922 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2925 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2927 /* jal: floodlight */
2928 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2931 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2932 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2935 /* bright enough? */
2936 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2937 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2938 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2939 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2946 /* add to average? */
2947 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2948 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2949 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2950 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2953 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2955 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2956 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2961 /* another happy customer */
2962 numVertsIlluminated++;
2965 /* set average color */
2968 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2969 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2973 VectorCopy( ambientColor, avgColors[ 0 ] );
2976 /* clean up and store vertex color */
2977 for( i = 0; i < ds->numVerts; i++ )
2979 /* get vertex luxel */
2980 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2982 /* store average in occluded vertexes */
2983 if( radVertLuxel[ 0 ] < 0.0f )
2985 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2987 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2988 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2991 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2996 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2999 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3000 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3003 if( bouncing || bounce == 0 || !bounceOnly )
3004 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3005 if( !info->si->noVertexLight )
3006 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3010 /* free light list */
3011 FreeTraceLights( &trace );
3013 /* return to sender */
3017 /* -----------------------------------------------------------------
3018 reconstitute vertex lighting from the luxels
3019 ----------------------------------------------------------------- */
3021 /* set styles from lightmap */
3022 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3023 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3025 /* get max search radius */
3027 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3029 /* walk the surface verts */
3030 verts = yDrawVerts + ds->firstVert;
3031 for( i = 0; i < ds->numVerts; i++ )
3033 /* do each lightmap */
3034 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3037 if( lm->superLuxels[ lightmapNum ] == NULL )
3040 /* get luxel coords */
3041 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3042 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3045 else if( x >= lm->sw )
3049 else if( y >= lm->sh )
3052 /* get vertex luxels */
3053 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3054 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3056 /* color the luxel with the normal? */
3059 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3060 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3061 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3064 /* color the luxel with surface num? */
3065 else if( debugSurfaces )
3066 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3068 /* divine color from the superluxels */
3071 /* increasing radius */
3072 VectorClear( radVertLuxel );
3074 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3076 /* sample within radius */
3077 for( sy = (y - radius); sy <= (y + radius); sy++ )
3079 if( sy < 0 || sy >= lm->sh )
3082 for( sx = (x - radius); sx <= (x + radius); sx++ )
3084 if( sx < 0 || sx >= lm->sw )
3087 /* get luxel particulars */
3088 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3089 cluster = SUPER_CLUSTER( sx, sy );
3093 /* testing: must be brigher than ambient color */
3094 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3097 /* add its distinctiveness to our own */
3098 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3099 samples += luxel[ 3 ];
3105 if( samples > 0.0f )
3106 VectorDivide( radVertLuxel, samples, radVertLuxel );
3108 VectorCopy( ambientColor, radVertLuxel );
3111 /* store into floating point storage */
3112 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3113 numVertsIlluminated++;
3115 /* store into bytes (for vertex approximation) */
3116 if( !info->si->noVertexLight )
3117 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3124 /* -------------------------------------------------------------------------------
3126 light optimization (-fast)
3128 creates a list of lights that will affect a surface and stores it in tw
3129 this is to optimize surface lighting by culling out as many of the
3130 lights in the world as possible from further calculation
3132 ------------------------------------------------------------------------------- */
3136 determines opaque brushes in the world and find sky shaders for sunlight calculations
3139 void SetupBrushes( void )
3141 int i, j, b, compileFlags;
3144 bspBrushSide_t *side;
3145 bspShader_t *shader;
3150 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3153 if( opaqueBrushes == NULL )
3154 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3157 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3158 numOpaqueBrushes = 0;
3160 /* walk the list of worldspawn brushes */
3161 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3164 b = bspModels[ 0 ].firstBSPBrush + i;
3165 brush = &bspBrushes[ b ];
3167 /* check all sides */
3170 for( j = 0; j < brush->numSides && inside; j++ )
3172 /* do bsp shader calculations */
3173 side = &bspBrushSides[ brush->firstSide + j ];
3174 shader = &bspShaders[ side->shaderNum ];
3176 /* get shader info */
3177 si = ShaderInfoForShader( shader->shader );
3181 /* or together compile flags */
3182 compileFlags |= si->compileFlags;
3185 /* determine if this brush is opaque to light */
3186 if( !(compileFlags & C_TRANSLUCENT) )
3188 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3194 /* emit some statistics */
3195 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3202 determines if two clusters are visible to each other using the PVS
3205 qboolean ClusterVisible( int a, int b )
3212 if( a < 0 || b < 0 )
3220 if( numBSPVisBytes <=8 )
3224 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3225 leafBytes = ((int*) bspVisBytes)[ 1 ];
3226 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3229 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3238 borrowed from vlight.c
3241 int PointInLeafNum_r( vec3_t point, int nodenum )
3249 while( nodenum >= 0 )
3251 node = &bspNodes[ nodenum ];
3252 plane = &bspPlanes[ node->planeNum ];
3253 dist = DotProduct( point, plane->normal ) - plane->dist;
3255 nodenum = node->children[ 0 ];
3256 else if( dist < -0.1 )
3257 nodenum = node->children[ 1 ];
3260 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3261 if( bspLeafs[ leafnum ].cluster != -1 )
3263 nodenum = node->children[ 1 ];
3267 leafnum = -nodenum - 1;
3275 borrowed from vlight.c
3278 int PointInLeafNum( vec3_t point )
3280 return PointInLeafNum_r( point, 0 );
3286 ClusterVisibleToPoint() - ydnar
3287 returns qtrue if point can "see" cluster
3290 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3295 /* get leafNum for point */
3296 pointCluster = ClusterForPoint( point );
3297 if( pointCluster < 0 )
3301 return ClusterVisible( pointCluster, cluster );
3307 ClusterForPoint() - ydnar
3308 returns the pvs cluster for point
3311 int ClusterForPoint( vec3_t point )
3316 /* get leafNum for point */
3317 leafNum = PointInLeafNum( point );
3321 /* return the cluster */
3322 return bspLeafs[ leafNum ].cluster;
3328 ClusterForPointExt() - ydnar
3329 also takes brushes into account for occlusion testing
3332 int ClusterForPointExt( vec3_t point, float epsilon )
3334 int i, j, b, leafNum, cluster;
3337 int *brushes, numBSPBrushes;
3343 /* get leaf for point */
3344 leafNum = PointInLeafNum( point );
3347 leaf = &bspLeafs[ leafNum ];
3349 /* get the cluster */
3350 cluster = leaf->cluster;
3354 /* transparent leaf, so check point against all brushes in the leaf */
3355 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3356 numBSPBrushes = leaf->numBSPLeafBrushes;
3357 for( i = 0; i < numBSPBrushes; i++ )
3361 if( b > maxOpaqueBrush )
3363 brush = &bspBrushes[ b ];
3364 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3367 /* check point against all planes */
3369 for( j = 0; j < brush->numSides && inside; j++ )
3371 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3372 dot = DotProduct( point, plane->normal );
3378 /* if inside, return bogus cluster */
3383 /* if the point made it this far, it's not inside any opaque brushes */
3390 ClusterForPointExtFilter() - ydnar
3391 adds cluster checking against a list of known valid clusters
3394 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3399 /* get cluster for point */
3400 cluster = ClusterForPointExt( point, epsilon );
3402 /* check if filtering is necessary */
3403 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3407 for( i = 0; i < numClusters; i++ )
3409 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3420 ShaderForPointInLeaf() - ydnar
3421 checks a point against all brushes in a leaf, returning the shader of the brush
3422 also sets the cumulative surface and content flags for the brush hit
3425 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3430 int *brushes, numBSPBrushes;
3433 bspBrushSide_t *side;
3435 bspShader_t *shader;
3436 int allSurfaceFlags, allContentFlags;
3439 /* clear things out first */
3446 leaf = &bspLeafs[ leafNum ];
3448 /* transparent leaf, so check point against all brushes in the leaf */
3449 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3450 numBSPBrushes = leaf->numBSPLeafBrushes;
3451 for( i = 0; i < numBSPBrushes; i++ )
3454 brush = &bspBrushes[ brushes[ i ] ];
3456 /* check point against all planes */
3458 allSurfaceFlags = 0;
3459 allContentFlags = 0;
3460 for( j = 0; j < brush->numSides && inside; j++ )
3462 side = &bspBrushSides[ brush->firstSide + j ];
3463 plane = &bspPlanes[ side->planeNum ];
3464 dot = DotProduct( point, plane->normal );
3470 shader = &bspShaders[ side->shaderNum ];
3471 allSurfaceFlags |= shader->surfaceFlags;
3472 allContentFlags |= shader->contentFlags;
3476 /* handle if inside */
3479 /* if there are desired flags, check for same and continue if they aren't matched */
3480 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3482 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3485 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3486 *surfaceFlags = allSurfaceFlags;
3487 *contentFlags = allContentFlags;
3488 return brush->shaderNum;
3492 /* if the point made it this far, it's not inside any brushes */
3500 chops a bounding box by the plane defined by origin and normal
3501 returns qfalse if the bounds is entirely clipped away
3503 this is not exactly the fastest way to do this...
3506 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3508 /* FIXME: rewrite this so it doesn't use bloody brushes */
3516 calculates each light's effective envelope,
3517 taking into account brightness, type, and pvs.
3520 #define LIGHT_EPSILON 0.125f
3521 #define LIGHT_NUDGE 2.0f
3523 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3525 int i, x, y, z, x1, y1, z1;
3526 light_t *light, *light2, **owner;
3528 vec3_t origin, dir, mins, maxs;
3529 float radius, intensity;
3530 light_t *buckets[ 256 ];
3533 /* early out for weird cases where there are no lights */
3534 if( lights == NULL )
3538 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3542 numCulledLights = 0;
3544 while( *owner != NULL )
3549 /* handle negative lights */
3550 if( light->photons < 0.0f || light->add < 0.0f )
3552 light->photons *= -1.0f;
3553 light->add *= -1.0f;
3554 light->flags |= LIGHT_NEGATIVE;
3558 if( light->type == EMIT_SUN )
3562 light->envelope = MAX_WORLD_COORD * 8.0f;
3563 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3564 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3567 /* everything else */
3570 /* get pvs cluster for light */
3571 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3573 /* invalid cluster? */
3574 if( light->cluster < 0 )
3576 /* nudge the sample point around a bit */
3577 for( x = 0; x < 4; x++ )
3579 /* two's complement 0, 1, -1, 2, -2, etc */
3580 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3582 for( y = 0; y < 4; y++ )
3584 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3586 for( z = 0; z < 4; z++ )
3588 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3591 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3592 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3593 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3595 /* try at nudged origin */
3596 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3597 if( light->cluster < 0 )
3601 VectorCopy( origin, light->origin );
3607 /* only calculate for lights in pvs and outside of opaque brushes */
3608 if( light->cluster >= 0 )
3610 /* set light fast flag */
3612 light->flags |= LIGHT_FAST_TEMP;
3614 light->flags &= ~LIGHT_FAST_TEMP;
3615 if( light->si && light->si->noFast )
3616 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3618 /* clear light envelope */
3619 light->envelope = 0;
3621 /* handle area lights */
3622 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3624 /* ugly hack to calculate extent for area lights, but only done once */
3625 VectorScale( light->normal, -1.0f, dir );
3626 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3630 VectorMA( light->origin, radius, light->normal, origin );
3631 factor = PointToPolygonFormFactor( origin, dir, light->w );
3634 if( (factor * light->add) <= light->falloffTolerance )
3635 light->envelope = radius;
3638 /* check for fast mode */
3639 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3640 light->envelope = MAX_WORLD_COORD * 8.0f;
3641 intensity = light->photons; /* hopefully not used */
3646 intensity = light->photons;
3650 if( light->envelope <= 0.0f )
3652 /* solve distance for non-distance lights */
3653 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3654 light->envelope = MAX_WORLD_COORD * 8.0f;
3656 /* solve distance for linear lights */
3657 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3658 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3659 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3662 add = angle * light->photons * linearScale - (dist * light->fade);
3663 T = (light->photons * linearScale) - (dist * light->fade);
3664 T + (dist * light->fade) = (light->photons * linearScale);
3665 dist * light->fade = (light->photons * linearScale) - T;
3666 dist = ((light->photons * linearScale) - T) / light->fade;
3669 /* solve for inverse square falloff */
3671 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3674 add = light->photons / (dist * dist);
3675 T = light->photons / (dist * dist);
3676 T * (dist * dist) = light->photons;
3677 dist = sqrt( light->photons / T );
3681 /* chop radius against pvs */
3684 ClearBounds( mins, maxs );
3686 /* check all leaves */
3687 for( i = 0; i < numBSPLeafs; i++ )
3690 leaf = &bspLeafs[ i ];
3693 if( leaf->cluster < 0 )
3695 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3698 /* add this leafs bbox to the bounds */
3699 VectorCopy( leaf->mins, origin );
3700 AddPointToBounds( origin, mins, maxs );
3701 VectorCopy( leaf->maxs, origin );
3702 AddPointToBounds( origin, mins, maxs );
3705 /* test to see if bounds encompass light */
3706 for( i = 0; i < 3; i++ )
3708 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3710 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3711 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3712 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3713 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3714 AddPointToBounds( light->origin, mins, maxs );
3718 /* chop the bounds by a plane for area lights and spotlights */
3719 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3720 ChopBounds( mins, maxs, light->origin, light->normal );
3723 VectorCopy( mins, light->mins );
3724 VectorCopy( maxs, light->maxs );
3726 /* reflect bounds around light origin */
3727 //% VectorMA( light->origin, -1.0f, origin, origin );
3728 VectorScale( light->origin, 2, origin );
3729 VectorSubtract( origin, maxs, origin );
3730 AddPointToBounds( origin, mins, maxs );
3731 //% VectorMA( light->origin, -1.0f, mins, origin );
3732 VectorScale( light->origin, 2, origin );
3733 VectorSubtract( origin, mins, origin );
3734 AddPointToBounds( origin, mins, maxs );
3736 /* calculate spherical bounds */
3737 VectorSubtract( maxs, light->origin, dir );
3738 radius = (float) VectorLength( dir );
3740 /* if this radius is smaller than the envelope, then set the envelope to it */
3741 if( radius < light->envelope )
3743 light->envelope = radius;
3744 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3747 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3750 /* add grid/surface only check */
3753 if( !(light->flags & LIGHT_GRID) )
3754 light->envelope = 0.0f;
3758 if( !(light->flags & LIGHT_SURFACES) )
3759 light->envelope = 0.0f;
3764 if( light->cluster < 0 || light->envelope <= 0.0f )
3767 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3769 /* delete the light */
3771 *owner = light->next;
3772 if( light->w != NULL )
3779 /* square envelope */
3780 light->envelope2 = (light->envelope * light->envelope);
3782 /* increment light count */
3785 /* set next light */
3786 owner = &((**owner).next);
3789 /* bucket sort lights by style */
3790 memset( buckets, 0, sizeof( buckets ) );
3792 for( light = lights; light != NULL; light = light2 )
3794 /* get next light */
3795 light2 = light->next;
3797 /* filter into correct bucket */
3798 light->next = buckets[ light->style ];
3799 buckets[ light->style ] = light;
3801 /* if any styled light is present, automatically set nocollapse */
3802 if( light->style != LS_NORMAL )
3806 /* filter back into light list */
3808 for( i = 255; i >= 0; i-- )
3811 for( light = buckets[ i ]; light != NULL; light = light2 )
3813 light2 = light->next;
3814 light->next = lights;
3819 /* emit some statistics */
3820 Sys_Printf( "%9d total lights\n", numLights );
3821 Sys_Printf( "%9d culled lights\n", numCulledLights );
3827 CreateTraceLightsForBounds()
3828 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3831 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3835 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3836 float radius, dist, length;
3839 /* potential pre-setup */
3840 if( numLights == 0 )
3841 SetupEnvelopes( qfalse, fast );
3844 //% 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 ] );
3846 /* allocate the light list */
3847 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3848 trace->numLights = 0;
3850 /* calculate spherical bounds */
3851 VectorAdd( mins, maxs, origin );
3852 VectorScale( origin, 0.5f, origin );
3853 VectorSubtract( maxs, origin, dir );
3854 radius = (float) VectorLength( dir );
3856 /* get length of normal vector */
3857 if( normal != NULL )
3858 length = VectorLength( normal );
3861 normal = nullVector;
3865 /* test each light and see if it reaches the sphere */
3866 /* note: the attenuation code MUST match LightingAtSample() */
3867 for( light = lights; light; light = light->next )
3869 /* check zero sized envelope */
3870 if( light->envelope <= 0 )
3872 lightsEnvelopeCulled++;
3877 if( !(light->flags & flags) )
3880 /* sunlight skips all this nonsense */
3881 if( light->type != EMIT_SUN )
3887 /* check against pvs cluster */
3888 if( numClusters > 0 && clusters != NULL )
3890 for( i = 0; i < numClusters; i++ )
3892 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3897 if( i == numClusters )
3899 lightsClusterCulled++;
3904 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3905 VectorSubtract( light->origin, origin, dir );
3906 dist = VectorLength( dir );
3907 dist -= light->envelope;
3911 lightsEnvelopeCulled++;
3915 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3918 for( i = 0; i < 3; i++ )
3920 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3925 lightsBoundsCulled++;
3931 /* planar surfaces (except twosided surfaces) have a couple more checks */
3932 if( length > 0.0f && trace->twoSided == qfalse )
3934 /* lights coplanar with a surface won't light it */
3935 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3937 lightsPlaneCulled++;
3941 /* check to see if light is behind the plane */
3942 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3944 lightsPlaneCulled++;
3949 /* add this light */
3950 trace->lights[ trace->numLights++ ] = light;
3953 /* make last night null */
3954 trace->lights[ trace->numLights ] = NULL;
3959 void FreeTraceLights( trace_t *trace )
3961 if( trace->lights != NULL )
3962 free( trace->lights );
3968 CreateTraceLightsForSurface()
3969 creates a list of lights that can potentially affect a drawsurface
3972 void CreateTraceLightsForSurface( int num, trace_t *trace )
3975 vec3_t mins, maxs, normal;
3977 bspDrawSurface_t *ds;
3978 surfaceInfo_t *info;
3985 /* get drawsurface and info */
3986 ds = &bspDrawSurfaces[ num ];
3987 info = &surfaceInfos[ num ];
3989 /* get the mins/maxs for the dsurf */
3990 ClearBounds( mins, maxs );
3991 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3992 for( i = 0; i < ds->numVerts; i++ )
3994 dv = &yDrawVerts[ ds->firstVert + i ];
3995 AddPointToBounds( dv->xyz, mins, maxs );
3996 if( !VectorCompare( dv->normal, normal ) )
3997 VectorClear( normal );
4000 /* create the lights for the bounding box */
4001 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4004 /////////////////////////////////////////////////////////////
4006 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4007 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4008 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4009 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4011 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4012 static int numFloodVectors = 0;
4014 void SetupFloodLight( void )
4017 float angle, elevation, angleStep, elevationStep;
4019 double v1,v2,v3,v4,v5,v6;
4022 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4024 /* calculate angular steps */
4025 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4026 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4030 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4032 /* iterate elevation */
4033 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4035 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4036 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4037 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4042 /* emit some statistics */
4043 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4046 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4048 if( value[ 0 ] != '\0' )
4051 v4=floodlightDistance;
4052 v5=floodlightIntensity;
4053 v6=floodlightDirectionScale;
4055 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6);
4057 floodlightRGB[0]=v1;
4058 floodlightRGB[1]=v2;
4059 floodlightRGB[2]=v3;
4061 if (VectorLength(floodlightRGB)==0)
4063 VectorSet(floodlightRGB,240,240,255);
4070 floodlightDistance=v4;
4071 floodlightIntensity=v5;
4072 floodlightDirectionScale=v6;
4074 floodlighty = qtrue;
4075 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4079 VectorSet(floodlightRGB,240,240,255);
4080 //floodlighty = qtrue;
4081 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4083 VectorNormalize(floodlightRGB,floodlightRGB);
4087 FloodLightForSample()
4088 calculates floodlight value for a given sample
4089 once again, kudos to the dirtmapping coder
4092 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4098 float gatherLight, outLight;
4099 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4107 if( trace == NULL || trace->cluster < 0 )
4112 dd = floodLightDistance;
4113 VectorCopy( trace->normal, normal );
4115 /* check if the normal is aligned to the world-up */
4116 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4118 if( normal[ 2 ] == 1.0f )
4120 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4121 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4123 else if( normal[ 2 ] == -1.0f )
4125 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4126 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4131 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4132 CrossProduct( normal, worldUp, myRt );
4133 VectorNormalize( myRt, myRt );
4134 CrossProduct( myRt, normal, myUp );
4135 VectorNormalize( myUp, myUp );
4138 /* vortex: optimise floodLightLowQuality a bit */
4139 if ( floodLightLowQuality == qtrue )
4141 /* iterate through ordered vectors */
4142 for( i = 0; i < numFloodVectors; i++ )
4143 if (rand()%10 != 0 ) continue;
4147 /* iterate through ordered vectors */
4148 for( i = 0; i < numFloodVectors; i++ )
4152 /* transform vector into tangent space */
4153 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4154 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4155 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4158 VectorMA( trace->origin, dd, direction, trace->end );
4160 //VectorMA( trace->origin, 1, direction, trace->origin );
4162 SetupTrace( trace );
4167 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4171 else if ( trace->opaque )
4173 VectorSubtract( trace->hit, trace->origin, displacement );
4174 d=VectorLength( displacement );
4176 // d=trace->distance;
4177 //if (d>256) gatherDirt+=1;
4179 if (contribution>1) contribution=1.0f;
4181 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4184 gatherLight+=contribution;
4189 if( gatherLight <= 0.0f )
4197 outLight=gatherLight;
4198 if( outLight > 1.0f )
4201 /* return to sender */
4206 FloodLightRawLightmap
4207 lighttracer style ambient occlusion light hack.
4208 Kudos to the dirtmapping author for most of this source.
4209 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4210 VorteX: fixed problems with deluxemapping
4213 // floodlight pass on a lightmap
4214 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4216 int i, x, y, *cluster;
4217 float *origin, *normal, *floodlight, floodLightAmount;
4218 surfaceInfo_t *info;
4221 // float samples, average, *floodlight2;
4223 memset(&trace,0,sizeof(trace_t));
4226 trace.testOcclusion = qtrue;
4227 trace.forceSunlight = qfalse;
4228 trace.twoSided = qtrue;
4229 trace.recvShadows = lm->recvShadows;
4230 trace.numSurfaces = lm->numLightSurfaces;
4231 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4232 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4233 trace.testAll = qfalse;
4234 trace.distance = 1024;
4236 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4237 //trace.twoSided = qfalse;
4238 for( i = 0; i < trace.numSurfaces; i++ )
4241 info = &surfaceInfos[ trace.surfaces[ i ] ];
4243 /* check twosidedness */
4244 if( info->si->twoSided )
4246 trace.twoSided = qtrue;
4251 /* gather floodlight */
4252 for( y = 0; y < lm->sh; y++ )
4254 for( x = 0; x < lm->sw; x++ )
4257 cluster = SUPER_CLUSTER( x, y );
4258 origin = SUPER_ORIGIN( x, y );
4259 normal = SUPER_NORMAL( x, y );
4260 floodlight = SUPER_FLOODLIGHT( x, y );
4262 /* set default dirt */
4265 /* only look at mapped luxels */
4270 trace.cluster = *cluster;
4271 VectorCopy( origin, trace.origin );
4272 VectorCopy( normal, trace.normal );
4274 /* get floodlight */
4275 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4277 /* add floodlight */
4278 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4279 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4280 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4281 floodlight[3] += floodlightDirectionScale;
4285 /* testing no filtering */
4291 for( y = 0; y < lm->sh; y++ )
4293 for( x = 0; x < lm->sw; x++ )
4296 cluster = SUPER_CLUSTER( x, y );
4297 floodlight = SUPER_FLOODLIGHT(x, y );
4299 /* filter dirt by adjacency to unmapped luxels */
4300 average = *floodlight;
4302 for( sy = (y - 1); sy <= (y + 1); sy++ )
4304 if( sy < 0 || sy >= lm->sh )
4307 for( sx = (x - 1); sx <= (x + 1); sx++ )
4309 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4312 /* get neighboring luxel */
4313 cluster = SUPER_CLUSTER( sx, sy );
4314 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4315 if( *cluster < 0 || *floodlight2 <= 0.0f )
4319 average += *floodlight2;
4324 if( samples <= 0.0f )
4329 if( samples <= 0.0f )
4333 *floodlight = average / samples;
4339 void FloodLightRawLightmap( int rawLightmapNum )
4343 /* bail if this number exceeds the number of raw lightmaps */
4344 if( rawLightmapNum >= numRawLightmaps )
4347 lm = &rawLightmaps[ rawLightmapNum ];
4350 if (floodlighty && floodlightIntensity)
4351 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
4354 if (lm->floodlightIntensity)
4356 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4357 numSurfacesFloodlighten += 1;
4361 void FloodlightRawLightmaps()
4363 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4364 numSurfacesFloodlighten = 0;
4365 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4366 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4370 FloodLightIlluminate()
4371 illuminate floodlight into lightmap luxels
4374 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4376 float *luxel, *floodlight, *deluxel, *normal;
4379 int x, y, lightmapNum;
4381 /* walk lightmaps */
4382 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4385 if( lm->superLuxels[ lightmapNum ] == NULL )
4388 /* apply floodlight to each luxel */
4389 for( y = 0; y < lm->sh; y++ )
4391 for( x = 0; x < lm->sw; x++ )
4393 /* get floodlight */
4394 floodlight = SUPER_FLOODLIGHT( x, y );
4395 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4399 cluster = SUPER_CLUSTER( x, y );
4401 /* only process mapped luxels */
4405 /* get particulars */
4406 luxel = SUPER_LUXEL( lightmapNum, x, y );
4407 deluxel = SUPER_DELUXEL( x, y );
4409 /* add to lightmap */
4410 luxel[0]+=floodlight[0];
4411 luxel[1]+=floodlight[1];
4412 luxel[2]+=floodlight[2];
4414 if (luxel[3]==0) luxel[3]=1;
4416 /* add to deluxemap */
4417 if (deluxemap && floodlight[3] > 0)
4421 normal = SUPER_NORMAL( x, y );
4422 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4424 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4425 if(brightness < 0.00390625f)
4426 brightness = 0.00390625f;
4428 VectorScale( normal, brightness, lightvector );
4429 VectorAdd( deluxel, lightvector, deluxel );