1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 /* get drawverts and map first triangle */
1158 MapTriangle( lm, info, dv, mapNonAxial );
1160 /* get drawverts and map second triangle */
1161 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1162 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1163 MapTriangle( lm, info, dv, mapNonAxial );
1178 /* -----------------------------------------------------------------
1179 average and clean up luxel normals
1180 ----------------------------------------------------------------- */
1182 /* walk the luxels */
1183 for( y = 0; y < lm->sh; y++ )
1185 for( x = 0; x < lm->sw; x++ )
1188 luxel = SUPER_LUXEL( 0, x, y );
1189 normal = SUPER_NORMAL( x, y );
1190 cluster = SUPER_CLUSTER( x, y );
1192 /* only look at mapped luxels */
1196 /* the normal data could be the sum of multiple samples */
1197 if( luxel[ 3 ] > 1.0f )
1198 VectorNormalize( normal, normal );
1200 /* mark this luxel as having only one normal */
1205 /* non-planar surfaces stop here */
1206 if( lm->plane == NULL )
1209 /* -----------------------------------------------------------------
1210 map occluded or unuxed luxels
1211 ----------------------------------------------------------------- */
1213 /* walk the luxels */
1214 radius = floor( superSample / 2 );
1215 radius = radius > 0 ? radius : 1.0f;
1217 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1219 for( y = 0; y < lm->sh; y++ )
1221 for( x = 0; x < lm->sw; x++ )
1224 luxel = SUPER_LUXEL( 0, x, y );
1225 normal = SUPER_NORMAL( x, y );
1226 cluster = SUPER_CLUSTER( x, y );
1228 /* only look at unmapped luxels */
1229 if( *cluster != CLUSTER_UNMAPPED )
1232 /* divine a normal and origin from neighboring luxels */
1233 VectorClear( fake.xyz );
1234 VectorClear( fake.normal );
1235 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1236 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1238 for( sy = (y - 1); sy <= (y + 1); sy++ )
1240 if( sy < 0 || sy >= lm->sh )
1243 for( sx = (x - 1); sx <= (x + 1); sx++ )
1245 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1248 /* get neighboring luxel */
1249 luxel = SUPER_LUXEL( 0, sx, sy );
1250 origin = SUPER_ORIGIN( sx, sy );
1251 normal = SUPER_NORMAL( sx, sy );
1252 cluster = SUPER_CLUSTER( sx, sy );
1254 /* only consider luxels mapped in previous passes */
1255 if( *cluster < 0 || luxel[ 0 ] >= pass )
1258 /* add its distinctiveness to our own */
1259 VectorAdd( fake.xyz, origin, fake.xyz );
1260 VectorAdd( fake.normal, normal, fake.normal );
1261 samples += luxel[ 3 ];
1266 if( samples == 0.0f )
1270 VectorDivide( fake.xyz, samples, fake.xyz );
1271 //% VectorDivide( fake.normal, samples, fake.normal );
1272 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1275 /* map the fake vert */
1276 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1281 /* -----------------------------------------------------------------
1282 average and clean up luxel normals
1283 ----------------------------------------------------------------- */
1285 /* walk the luxels */
1286 for( y = 0; y < lm->sh; y++ )
1288 for( x = 0; x < lm->sw; x++ )
1291 luxel = SUPER_LUXEL( 0, x, y );
1292 normal = SUPER_NORMAL( x, y );
1293 cluster = SUPER_CLUSTER( x, y );
1295 /* only look at mapped luxels */
1299 /* the normal data could be the sum of multiple samples */
1300 if( luxel[ 3 ] > 1.0f )
1301 VectorNormalize( normal, normal );
1303 /* mark this luxel as having only one normal */
1311 for( y = 0; y < lm->sh; y++ )
1313 for( x = 0; x < lm->sw; x++ )
1318 cluster = SUPER_CLUSTER( x, y );
1319 origin = SUPER_ORIGIN( x, y );
1320 normal = SUPER_NORMAL( x, y );
1321 luxel = SUPER_LUXEL( x, y );
1326 /* check if within the bounding boxes of all surfaces referenced */
1327 ClearBounds( mins, maxs );
1328 for( n = 0; n < lm->numLightSurfaces; n++ )
1331 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1332 TOL = info->sampleSize + 2;
1333 AddPointToBounds( info->mins, mins, maxs );
1334 AddPointToBounds( info->maxs, mins, maxs );
1335 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1336 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1337 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1342 if( n < lm->numLightSurfaces )
1345 /* report bogus origin */
1346 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1347 rawLightmapNum, x, y, *cluster,
1348 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1349 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1350 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1361 sets up dirtmap (ambient occlusion)
1364 #define DIRT_CONE_ANGLE 88 /* degrees */
1365 #define DIRT_NUM_ANGLE_STEPS 16
1366 #define DIRT_NUM_ELEVATION_STEPS 3
1367 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1369 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1370 static int numDirtVectors = 0;
1372 void SetupDirt( void )
1375 float angle, elevation, angleStep, elevationStep;
1379 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1381 /* calculate angular steps */
1382 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1383 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1387 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1389 /* iterate elevation */
1390 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1392 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1393 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1394 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1399 /* emit some statistics */
1400 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1406 calculates dirt value for a given sample
1409 float DirtForSample( trace_t *trace )
1412 float gatherDirt, outDirt, angle, elevation, ooDepth;
1413 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1419 if( trace == NULL || trace->cluster < 0 )
1424 ooDepth = 1.0f / dirtDepth;
1425 VectorCopy( trace->normal, normal );
1427 /* check if the normal is aligned to the world-up */
1428 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1430 if( normal[ 2 ] == 1.0f )
1432 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1433 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1435 else if( normal[ 2 ] == -1.0f )
1437 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1443 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1444 CrossProduct( normal, worldUp, myRt );
1445 VectorNormalize( myRt, myRt );
1446 CrossProduct( myRt, normal, myUp );
1447 VectorNormalize( myUp, myUp );
1450 /* 1 = random mode, 0 (well everything else) = non-random mode */
1454 for( i = 0; i < numDirtVectors; i++ )
1456 /* get random vector */
1457 angle = Random() * DEG2RAD( 360.0f );
1458 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1459 temp[ 0 ] = cos( angle ) * sin( elevation );
1460 temp[ 1 ] = sin( angle ) * sin( elevation );
1461 temp[ 2 ] = cos( elevation );
1463 /* transform into tangent space */
1464 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1465 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1466 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1469 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1470 SetupTrace( trace );
1476 VectorSubtract( trace->hit, trace->origin, displacement );
1477 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1483 /* iterate through ordered vectors */
1484 for( i = 0; i < numDirtVectors; i++ )
1486 /* transform vector into tangent space */
1487 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1488 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1489 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1492 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1493 SetupTrace( trace );
1499 VectorSubtract( trace->hit, trace->origin, displacement );
1500 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1506 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1507 SetupTrace( trace );
1513 VectorSubtract( trace->hit, trace->origin, displacement );
1514 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1518 if( gatherDirt <= 0.0f )
1521 /* apply gain (does this even do much? heh) */
1522 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1523 if( outDirt > 1.0f )
1527 outDirt *= dirtScale;
1528 if( outDirt > 1.0f )
1531 /* return to sender */
1532 return 1.0f - outDirt;
1539 calculates dirty fraction for each luxel
1542 void DirtyRawLightmap( int rawLightmapNum )
1544 int i, x, y, sx, sy, *cluster;
1545 float *origin, *normal, *dirt, *dirt2, average, samples;
1547 surfaceInfo_t *info;
1552 /* bail if this number exceeds the number of raw lightmaps */
1553 if( rawLightmapNum >= numRawLightmaps )
1557 lm = &rawLightmaps[ rawLightmapNum ];
1560 trace.testOcclusion = qtrue;
1561 trace.forceSunlight = qfalse;
1562 trace.recvShadows = lm->recvShadows;
1563 trace.numSurfaces = lm->numLightSurfaces;
1564 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1565 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1566 trace.testAll = qtrue;
1568 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1569 trace.twoSided = qfalse;
1570 for( i = 0; i < trace.numSurfaces; i++ )
1573 info = &surfaceInfos[ trace.surfaces[ i ] ];
1575 /* check twosidedness */
1576 if( info->si->twoSided )
1578 trace.twoSided = qtrue;
1584 for( i = 0; i < trace.numSurfaces; i++ )
1587 info = &surfaceInfos[ trace.surfaces[ i ] ];
1589 /* check twosidedness */
1590 if( info->si->noDirty )
1598 for( y = 0; y < lm->sh; y++ )
1600 for( x = 0; x < lm->sw; x++ )
1603 cluster = SUPER_CLUSTER( x, y );
1604 origin = SUPER_ORIGIN( x, y );
1605 normal = SUPER_NORMAL( x, y );
1606 dirt = SUPER_DIRT( x, y );
1608 /* set default dirt */
1611 /* only look at mapped luxels */
1615 /* don't apply dirty on this surface */
1623 trace.cluster = *cluster;
1624 VectorCopy( origin, trace.origin );
1625 VectorCopy( normal, trace.normal );
1628 *dirt = DirtForSample( &trace );
1632 /* testing no filtering */
1636 for( y = 0; y < lm->sh; y++ )
1638 for( x = 0; x < lm->sw; x++ )
1641 cluster = SUPER_CLUSTER( x, y );
1642 dirt = SUPER_DIRT( x, y );
1644 /* filter dirt by adjacency to unmapped luxels */
1647 for( sy = (y - 1); sy <= (y + 1); sy++ )
1649 if( sy < 0 || sy >= lm->sh )
1652 for( sx = (x - 1); sx <= (x + 1); sx++ )
1654 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1657 /* get neighboring luxel */
1658 cluster = SUPER_CLUSTER( sx, sy );
1659 dirt2 = SUPER_DIRT( sx, sy );
1660 if( *cluster < 0 || *dirt2 <= 0.0f )
1669 if( samples <= 0.0f )
1674 if( samples <= 0.0f )
1678 *dirt = average / samples;
1687 calculates the pvs cluster, origin, normal of a sub-luxel
1690 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1692 int i, *cluster, *cluster2;
1693 float *origin, *origin2, *normal; //% , *normal2;
1694 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1697 /* calulate x vector */
1698 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1700 cluster = SUPER_CLUSTER( x, y );
1701 origin = SUPER_ORIGIN( x, y );
1702 //% normal = SUPER_NORMAL( x, y );
1703 cluster2 = SUPER_CLUSTER( x + 1, y );
1704 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1705 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1707 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1709 cluster = SUPER_CLUSTER( x - 1, y );
1710 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1711 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1712 cluster2 = SUPER_CLUSTER( x, y );
1713 origin2 = SUPER_ORIGIN( x, y );
1714 //% normal2 = SUPER_NORMAL( x, y );
1717 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1719 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1720 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1722 /* calulate y vector */
1723 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1725 cluster = SUPER_CLUSTER( x, y );
1726 origin = SUPER_ORIGIN( x, y );
1727 //% normal = SUPER_NORMAL( x, y );
1728 cluster2 = SUPER_CLUSTER( x, y + 1 );
1729 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1730 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1732 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1734 cluster = SUPER_CLUSTER( x, y - 1 );
1735 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1736 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1737 cluster2 = SUPER_CLUSTER( x, y );
1738 origin2 = SUPER_ORIGIN( x, y );
1739 //% normal2 = SUPER_NORMAL( x, y );
1742 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1744 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1745 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1747 /* calculate new origin */
1748 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1749 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1750 for( i = 0; i < 3; i++ )
1751 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1754 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1755 if( *sampleCluster < 0 )
1758 /* calculate new normal */
1759 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1760 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1761 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1763 normal = SUPER_NORMAL( x, y );
1764 VectorCopy( normal, sampleNormal );
1772 SubsampleRawLuxel_r()
1773 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1776 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1778 int b, samples, mapped, lighted;
1781 vec3_t origin[ 4 ], normal[ 4 ];
1782 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1783 vec3_t color, total;
1787 if( lightLuxel[ 3 ] >= lightSamples )
1791 VectorClear( total );
1795 /* make 2x2 subsample stamp */
1796 for( b = 0; b < 4; b++ )
1799 VectorCopy( sampleOrigin, origin[ b ] );
1801 /* calculate position */
1802 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1809 /* increment sample count */
1810 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1813 trace->cluster = *cluster;
1814 VectorCopy( origin[ b ], trace->origin );
1815 VectorCopy( normal[ b ], trace->normal );
1819 LightContributionToSample( trace );
1820 if(trace->forceSubsampling > 1.0f)
1822 /* alphashadow: we subsample as deep as we can */
1828 /* add to totals (fixme: make contrast function) */
1829 VectorCopy( trace->color, luxel[ b ] );
1830 VectorAdd( total, trace->color, total );
1831 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1835 /* subsample further? */
1836 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1837 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1838 lighted != 0 && lighted != mapped )
1840 for( b = 0; b < 4; b++ )
1842 if( cluster[ b ] < 0 )
1844 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ] );
1849 //% VectorClear( color );
1851 VectorCopy( lightLuxel, color );
1853 for( b = 0; b < 4; b++ )
1855 if( cluster[ b ] < 0 )
1857 VectorAdd( color, luxel[ b ], color );
1865 color[ 0 ] /= samples;
1866 color[ 1 ] /= samples;
1867 color[ 2 ] /= samples;
1870 VectorCopy( color, lightLuxel );
1871 lightLuxel[ 3 ] += 1.0f;
1878 IlluminateRawLightmap()
1879 illuminates the luxels
1882 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1883 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1885 void IlluminateRawLightmap( int rawLightmapNum )
1887 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1888 int *cluster, *cluster2, mapped, lighted, totalLighted;
1890 surfaceInfo_t *info;
1891 qboolean filterColor, filterDir;
1893 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1894 unsigned char *flag;
1895 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1896 vec3_t color, averageColor, averageDir, total, temp, temp2;
1897 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1899 float stackLightLuxels[ STACK_LL_SIZE ];
1902 /* bail if this number exceeds the number of raw lightmaps */
1903 if( rawLightmapNum >= numRawLightmaps )
1907 lm = &rawLightmaps[ rawLightmapNum ];
1910 trace.testOcclusion = !noTrace;
1911 trace.forceSunlight = qfalse;
1912 trace.recvShadows = lm->recvShadows;
1913 trace.numSurfaces = lm->numLightSurfaces;
1914 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1915 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1917 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1918 trace.twoSided = qfalse;
1919 for( i = 0; i < trace.numSurfaces; i++ )
1922 info = &surfaceInfos[ trace.surfaces[ i ] ];
1924 /* check twosidedness */
1925 if( info->si->twoSided )
1927 trace.twoSided = qtrue;
1932 /* create a culled light list for this raw lightmap */
1933 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1935 /* -----------------------------------------------------------------
1937 ----------------------------------------------------------------- */
1940 numLuxelsIlluminated += (lm->sw * lm->sh);
1942 /* test debugging state */
1943 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1945 /* debug fill the luxels */
1946 for( y = 0; y < lm->sh; y++ )
1948 for( x = 0; x < lm->sw; x++ )
1951 cluster = SUPER_CLUSTER( x, y );
1953 /* only fill mapped luxels */
1957 /* get particulars */
1958 luxel = SUPER_LUXEL( 0, x, y );
1959 origin = SUPER_ORIGIN( x, y );
1960 normal = SUPER_NORMAL( x, y );
1962 /* color the luxel with raw lightmap num? */
1964 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1966 /* color the luxel with lightmap axis? */
1967 else if( debugAxis )
1969 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1970 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1971 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1974 /* color the luxel with luxel cluster? */
1975 else if( debugCluster )
1976 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1978 /* color the luxel with luxel origin? */
1979 else if( debugOrigin )
1981 VectorSubtract( lm->maxs, lm->mins, temp );
1982 VectorScale( temp, (1.0f / 255.0f), temp );
1983 VectorSubtract( origin, lm->mins, temp2 );
1984 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
1985 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
1986 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
1989 /* color the luxel with the normal */
1990 else if( normalmap )
1992 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
1993 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
1994 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
1997 /* otherwise clear it */
1999 VectorClear( luxel );
2008 /* allocate temporary per-light luxel storage */
2009 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2010 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2011 lightLuxels = stackLightLuxels;
2013 lightLuxels = safe_malloc( llSize );
2016 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2018 /* set ambient color */
2019 for( y = 0; y < lm->sh; y++ )
2021 for( x = 0; x < lm->sw; x++ )
2024 cluster = SUPER_CLUSTER( x, y );
2025 luxel = SUPER_LUXEL( 0, x, y );
2026 normal = SUPER_NORMAL( x, y );
2027 deluxel = SUPER_DELUXEL( x, y );
2029 /* blacken unmapped clusters */
2031 VectorClear( luxel );
2036 VectorCopy( ambientColor, luxel );
2039 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2041 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2042 if(brightness < 0.00390625f)
2043 brightness = 0.00390625f;
2045 VectorScale( normal, brightness, deluxel );
2052 /* clear styled lightmaps */
2053 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2054 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2056 if( lm->superLuxels[ lightmapNum ] != NULL )
2057 memset( lm->superLuxels[ lightmapNum ], 0, size );
2060 /* debugging code */
2061 //% if( trace.numLights <= 0 )
2062 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2064 /* walk light list */
2065 for( i = 0; i < trace.numLights; i++ )
2068 trace.light = trace.lights[ i ];
2071 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2073 if( lm->styles[ lightmapNum ] == trace.light->style ||
2074 lm->styles[ lightmapNum ] == LS_NONE )
2078 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2079 if( lightmapNum >= MAX_LIGHTMAPS )
2081 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2086 memset( lightLuxels, 0, llSize );
2089 /* determine filter radius */
2090 filterRadius = lm->filterRadius > trace.light->filterRadius
2092 : trace.light->filterRadius;
2093 if( filterRadius < 0.0f )
2094 filterRadius = 0.0f;
2096 /* set luxel filter radius */
2097 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2098 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2099 luxelFilterRadius = 1;
2101 /* allocate sampling flags storage */
2102 if(lightSamples > 1 && luxelFilterRadius == 0)
2104 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2105 if(lm->superFlags == NULL)
2106 lm->superFlags = safe_malloc( size );
2107 memset( (void *) lm->superFlags, 0, size );
2110 /* initial pass, one sample per luxel */
2111 for( y = 0; y < lm->sh; y++ )
2113 for( x = 0; x < lm->sw; x++ )
2116 cluster = SUPER_CLUSTER( x, y );
2120 /* get particulars */
2121 lightLuxel = LIGHT_LUXEL( x, y );
2122 deluxel = SUPER_DELUXEL( x, y );
2123 origin = SUPER_ORIGIN( x, y );
2124 normal = SUPER_NORMAL( x, y );
2125 flag = SUPER_FLAG( x, y );
2128 ////////// 27's temp hack for testing edge clipping ////
2129 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2131 lightLuxel[ 1 ] = 255;
2132 lightLuxel[ 3 ] = 1.0f;
2138 /* set contribution count */
2139 lightLuxel[ 3 ] = 1.0f;
2142 trace.cluster = *cluster;
2143 VectorCopy( origin, trace.origin );
2144 VectorCopy( normal, trace.normal );
2146 /* get light for this sample */
2147 LightContributionToSample( &trace );
2148 VectorCopy( trace.color, lightLuxel );
2150 /* add the contribution to the deluxemap */
2152 VectorAdd( deluxel, trace.directionContribution, deluxel );
2154 /* check for evilness */
2155 if(trace.forceSubsampling > 1.0f && lightSamples > 1 && luxelFilterRadius == 0)
2158 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2161 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2167 /* don't even bother with everything else if nothing was lit */
2168 if( totalLighted == 0 )
2171 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2172 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2173 if( lightSamples > 1 && luxelFilterRadius == 0 )
2176 for( y = 0; y < (lm->sh - 1); y++ )
2178 for( x = 0; x < (lm->sw - 1); x++ )
2183 VectorClear( total );
2185 /* test 2x2 stamp */
2186 for( t = 0; t < 4; t++ )
2188 /* set sample coords */
2189 sx = x + tests[ t ][ 0 ];
2190 sy = y + tests[ t ][ 1 ];
2193 cluster = SUPER_CLUSTER( sx, sy );
2199 flag = SUPER_FLAG( sx, sy );
2200 if(*flag & FLAG_FORCE_SUBSAMPLING)
2202 /* force a lighted/mapped discrepancy so we subsample */
2207 lightLuxel = LIGHT_LUXEL( sx, sy );
2208 VectorAdd( total, lightLuxel, total );
2209 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2213 /* if total color is under a certain amount, then don't bother subsampling */
2214 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2217 /* if all 4 pixels are either in shadow or light, then don't subsample */
2218 if( lighted != 0 && lighted != mapped )
2220 for( t = 0; t < 4; t++ )
2222 /* set sample coords */
2223 sx = x + tests[ t ][ 0 ];
2224 sy = y + tests[ t ][ 1 ];
2227 cluster = SUPER_CLUSTER( sx, sy );
2230 flag = SUPER_FLAG( sx, sy );
2231 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2233 lightLuxel = LIGHT_LUXEL( sx, sy );
2234 origin = SUPER_ORIGIN( sx, sy );
2236 /* only subsample shadowed luxels */
2237 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2241 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel );
2243 *flag |= FLAG_ALREADY_SUBSAMPLED;
2245 /* debug code to colorize subsampled areas to yellow */
2246 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2247 //% VectorSet( luxel, 255, 204, 0 );
2254 /* tertiary pass, apply dirt map (ambient occlusion) */
2258 for( y = 0; y < lm->sh; y++ )
2260 for( x = 0; x < lm->sw; x++ )
2263 cluster = SUPER_CLUSTER( x, y );
2267 /* get particulars */
2268 lightLuxel = LIGHT_LUXEL( x, y );
2269 dirt = SUPER_DIRT( x, y );
2271 /* scale light value */
2272 VectorScale( lightLuxel, *dirt, lightLuxel );
2277 /* allocate sampling lightmap storage */
2278 if( lm->superLuxels[ lightmapNum ] == NULL )
2280 /* allocate sampling lightmap storage */
2281 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2282 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2283 memset( lm->superLuxels[ lightmapNum ], 0, size );
2287 if( lightmapNum > 0 )
2289 lm->styles[ lightmapNum ] = trace.light->style;
2290 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2293 /* copy to permanent luxels */
2294 for( y = 0; y < lm->sh; y++ )
2296 for( x = 0; x < lm->sw; x++ )
2298 /* get cluster and origin */
2299 cluster = SUPER_CLUSTER( x, y );
2302 origin = SUPER_ORIGIN( x, y );
2305 if( luxelFilterRadius )
2308 VectorClear( averageColor );
2311 /* cheaper distance-based filtering */
2312 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2314 if( sy < 0 || sy >= lm->sh )
2317 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2319 if( sx < 0 || sx >= lm->sw )
2322 /* get particulars */
2323 cluster = SUPER_CLUSTER( sx, sy );
2326 lightLuxel = LIGHT_LUXEL( sx, sy );
2329 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2330 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2332 /* scale luxel by filter weight */
2333 VectorScale( lightLuxel, weight, color );
2334 VectorAdd( averageColor, color, averageColor );
2340 if( samples <= 0.0f )
2343 /* scale into luxel */
2344 luxel = SUPER_LUXEL( lightmapNum, x, y );
2347 /* handle negative light */
2348 if( trace.light->flags & LIGHT_NEGATIVE )
2350 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2351 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2352 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2355 /* handle normal light */
2358 luxel[ 0 ] += averageColor[ 0 ] / samples;
2359 luxel[ 1 ] += averageColor[ 1 ] / samples;
2360 luxel[ 2 ] += averageColor[ 2 ] / samples;
2367 /* get particulars */
2368 lightLuxel = LIGHT_LUXEL( x, y );
2369 luxel = SUPER_LUXEL( lightmapNum, x, y );
2371 /* handle negative light */
2372 if( trace.light->flags & LIGHT_NEGATIVE )
2373 VectorScale( averageColor, -1.0f, averageColor );
2378 /* handle negative light */
2379 if( trace.light->flags & LIGHT_NEGATIVE )
2380 VectorSubtract( luxel, lightLuxel, luxel );
2382 /* handle normal light */
2384 VectorAdd( luxel, lightLuxel, luxel );
2390 /* free temporary luxels */
2391 if( lightLuxels != stackLightLuxels )
2392 free( lightLuxels );
2395 /* free light list */
2396 FreeTraceLights( &trace );
2398 /* floodlight pass */
2400 FloodlightIlluminateLightmap(lm);
2404 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2407 if( lm->superLuxels[ lightmapNum ] == NULL )
2410 for( y = 0; y < lm->sh; y++ )
2412 for( x = 0; x < lm->sw; x++ )
2415 cluster = SUPER_CLUSTER( x, y );
2416 //% if( *cluster < 0 )
2419 /* get particulars */
2420 luxel = SUPER_LUXEL( lightmapNum, x, y );
2421 normal = SUPER_NORMAL ( x, y );
2423 luxel[0]=(normal[0]*127)+127;
2424 luxel[1]=(normal[1]*127)+127;
2425 luxel[2]=(normal[2]*127)+127;
2431 /* -----------------------------------------------------------------
2433 ----------------------------------------------------------------- */
2437 /* walk lightmaps */
2438 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2441 if( lm->superLuxels[ lightmapNum ] == NULL )
2444 /* apply dirt to each luxel */
2445 for( y = 0; y < lm->sh; y++ )
2447 for( x = 0; x < lm->sw; x++ )
2450 cluster = SUPER_CLUSTER( x, y );
2451 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2454 /* get particulars */
2455 luxel = SUPER_LUXEL( lightmapNum, x, y );
2456 dirt = SUPER_DIRT( x, y );
2459 VectorScale( luxel, *dirt, luxel );
2463 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2469 /* -----------------------------------------------------------------
2471 ----------------------------------------------------------------- */
2473 /* walk lightmaps */
2474 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2477 if( lm->superLuxels[ lightmapNum ] == NULL )
2480 /* average occluded luxels from neighbors */
2481 for( y = 0; y < lm->sh; y++ )
2483 for( x = 0; x < lm->sw; x++ )
2485 /* get particulars */
2486 cluster = SUPER_CLUSTER( x, y );
2487 luxel = SUPER_LUXEL( lightmapNum, x, y );
2488 deluxel = SUPER_DELUXEL( x, y );
2489 normal = SUPER_NORMAL( x, y );
2491 /* determine if filtering is necessary */
2492 filterColor = qfalse;
2495 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2496 filterColor = qtrue;
2498 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2501 if( !filterColor && !filterDir )
2504 /* choose seed amount */
2505 VectorClear( averageColor );
2506 VectorClear( averageDir );
2509 /* walk 3x3 matrix */
2510 for( sy = (y - 1); sy <= (y + 1); sy++ )
2512 if( sy < 0 || sy >= lm->sh )
2515 for( sx = (x - 1); sx <= (x + 1); sx++ )
2517 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2520 /* get neighbor's particulars */
2521 cluster2 = SUPER_CLUSTER( sx, sy );
2522 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2523 deluxel2 = SUPER_DELUXEL( sx, sy );
2525 /* ignore unmapped/unlit luxels */
2526 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2527 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2530 /* add its distinctiveness to our own */
2531 VectorAdd( averageColor, luxel2, averageColor );
2532 samples += luxel2[ 3 ];
2534 VectorAdd( averageDir, deluxel2, averageDir );
2539 if( samples <= 0.0f )
2542 /* dark lightmap seams */
2545 if( lightmapNum == 0 )
2546 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2553 VectorDivide( averageColor, samples, luxel );
2557 VectorDivide( averageDir, samples, deluxel );
2559 /* set cluster to -3 */
2561 *cluster = CLUSTER_FLOODED;
2569 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2572 if( lm->superLuxels[ lightmapNum ] == NULL )
2574 for( y = 0; y < lm->sh; y++ )
2575 for( x = 0; x < lm->sw; x++ )
2578 cluster = SUPER_CLUSTER( x, y );
2579 luxel = SUPER_LUXEL( lightmapNum, x, y );
2580 deluxel = SUPER_DELUXEL( x, y );
2581 if(!luxel || !deluxel || !cluster)
2583 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2586 else if(*cluster < 0)
2589 // should have neither deluxemap nor lightmap
2591 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2596 // should have both deluxemap and lightmap
2598 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2608 IlluminateVertexes()
2609 light the surface vertexes
2612 #define VERTEX_NUDGE 4.0f
2614 void IlluminateVertexes( int num )
2616 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2617 int lightmapNum, numAvg;
2618 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2619 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2620 bspDrawSurface_t *ds;
2621 surfaceInfo_t *info;
2623 bspDrawVert_t *verts;
2625 float floodLightAmount;
2629 /* get surface, info, and raw lightmap */
2630 ds = &bspDrawSurfaces[ num ];
2631 info = &surfaceInfos[ num ];
2634 /* -----------------------------------------------------------------
2635 illuminate the vertexes
2636 ----------------------------------------------------------------- */
2638 /* calculate vertex lighting for surfaces without lightmaps */
2639 if( lm == NULL || cpmaHack )
2642 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2643 trace.forceSunlight = info->si->forceSunlight;
2644 trace.recvShadows = info->recvShadows;
2645 trace.numSurfaces = 1;
2646 trace.surfaces = #
2647 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2649 /* twosided lighting */
2650 trace.twoSided = info->si->twoSided;
2652 /* make light list for this surface */
2653 CreateTraceLightsForSurface( num, &trace );
2656 verts = yDrawVerts + ds->firstVert;
2658 memset( avgColors, 0, sizeof( avgColors ) );
2660 /* walk the surface verts */
2661 for( i = 0; i < ds->numVerts; i++ )
2663 /* get vertex luxel */
2664 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2666 /* color the luxel with raw lightmap num? */
2668 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2670 /* color the luxel with luxel origin? */
2671 else if( debugOrigin )
2673 VectorSubtract( info->maxs, info->mins, temp );
2674 VectorScale( temp, (1.0f / 255.0f), temp );
2675 VectorSubtract( origin, lm->mins, temp2 );
2676 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2677 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2678 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2681 /* color the luxel with the normal */
2682 else if( normalmap )
2684 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2685 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2686 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2689 /* illuminate the vertex */
2692 /* clear vertex luxel */
2693 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2695 /* try at initial origin */
2696 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2697 if( trace.cluster >= 0 )
2700 VectorCopy( verts[ i ].xyz, trace.origin );
2701 VectorCopy( verts[ i ].normal, trace.normal );
2704 if( dirty && !bouncing )
2705 dirt = DirtForSample( &trace );
2709 /* jal: floodlight */
2710 floodLightAmount = 0.0f;
2711 VectorClear( floodColor );
2712 if( floodlighty && !bouncing )
2714 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2715 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2719 LightingAtSample( &trace, ds->vertexStyles, colors );
2722 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2725 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2727 /* jal: floodlight */
2728 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2731 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2732 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2733 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2737 /* is this sample bright enough? */
2738 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2739 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2740 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2741 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2743 /* nudge the sample point around a bit */
2744 for( x = 0; x < 4; x++ )
2746 /* two's complement 0, 1, -1, 2, -2, etc */
2747 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2749 for( y = 0; y < 4; y++ )
2751 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2753 for( z = 0; z < 4; z++ )
2755 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2758 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2759 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2760 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2762 /* try at nudged origin */
2763 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2764 if( trace.cluster < 0 )
2768 LightingAtSample( &trace, ds->vertexStyles, colors );
2771 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2774 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2776 /* jal: floodlight */
2777 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2780 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2781 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2784 /* bright enough? */
2785 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2786 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2787 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2788 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2795 /* add to average? */
2796 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2797 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2798 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2799 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2802 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2804 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2805 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2810 /* another happy customer */
2811 numVertsIlluminated++;
2814 /* set average color */
2817 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2818 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2822 VectorCopy( ambientColor, avgColors[ 0 ] );
2825 /* clean up and store vertex color */
2826 for( i = 0; i < ds->numVerts; i++ )
2828 /* get vertex luxel */
2829 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2831 /* store average in occluded vertexes */
2832 if( radVertLuxel[ 0 ] < 0.0f )
2834 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2836 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2837 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2840 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2845 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2848 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2849 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2852 if( bouncing || bounce == 0 || !bounceOnly )
2853 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2854 if( !info->si->noVertexLight )
2855 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2859 /* free light list */
2860 FreeTraceLights( &trace );
2862 /* return to sender */
2866 /* -----------------------------------------------------------------
2867 reconstitute vertex lighting from the luxels
2868 ----------------------------------------------------------------- */
2870 /* set styles from lightmap */
2871 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2872 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2874 /* get max search radius */
2876 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2878 /* walk the surface verts */
2879 verts = yDrawVerts + ds->firstVert;
2880 for( i = 0; i < ds->numVerts; i++ )
2882 /* do each lightmap */
2883 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2886 if( lm->superLuxels[ lightmapNum ] == NULL )
2889 /* get luxel coords */
2890 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2891 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2894 else if( x >= lm->sw )
2898 else if( y >= lm->sh )
2901 /* get vertex luxels */
2902 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2903 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2905 /* color the luxel with the normal? */
2908 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2909 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2910 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2913 /* color the luxel with surface num? */
2914 else if( debugSurfaces )
2915 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2917 /* divine color from the superluxels */
2920 /* increasing radius */
2921 VectorClear( radVertLuxel );
2923 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2925 /* sample within radius */
2926 for( sy = (y - radius); sy <= (y + radius); sy++ )
2928 if( sy < 0 || sy >= lm->sh )
2931 for( sx = (x - radius); sx <= (x + radius); sx++ )
2933 if( sx < 0 || sx >= lm->sw )
2936 /* get luxel particulars */
2937 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2938 cluster = SUPER_CLUSTER( sx, sy );
2942 /* testing: must be brigher than ambient color */
2943 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2946 /* add its distinctiveness to our own */
2947 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2948 samples += luxel[ 3 ];
2954 if( samples > 0.0f )
2955 VectorDivide( radVertLuxel, samples, radVertLuxel );
2957 VectorCopy( ambientColor, radVertLuxel );
2960 /* store into floating point storage */
2961 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2962 numVertsIlluminated++;
2964 /* store into bytes (for vertex approximation) */
2965 if( !info->si->noVertexLight )
2966 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2973 /* -------------------------------------------------------------------------------
2975 light optimization (-fast)
2977 creates a list of lights that will affect a surface and stores it in tw
2978 this is to optimize surface lighting by culling out as many of the
2979 lights in the world as possible from further calculation
2981 ------------------------------------------------------------------------------- */
2985 determines opaque brushes in the world and find sky shaders for sunlight calculations
2988 void SetupBrushes( void )
2990 int i, j, b, compileFlags;
2993 bspBrushSide_t *side;
2994 bspShader_t *shader;
2999 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3002 if( opaqueBrushes == NULL )
3003 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3006 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3007 numOpaqueBrushes = 0;
3009 /* walk the list of worldspawn brushes */
3010 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3013 b = bspModels[ 0 ].firstBSPBrush + i;
3014 brush = &bspBrushes[ b ];
3016 /* check all sides */
3019 for( j = 0; j < brush->numSides && inside; j++ )
3021 /* do bsp shader calculations */
3022 side = &bspBrushSides[ brush->firstSide + j ];
3023 shader = &bspShaders[ side->shaderNum ];
3025 /* get shader info */
3026 si = ShaderInfoForShader( shader->shader );
3030 /* or together compile flags */
3031 compileFlags |= si->compileFlags;
3034 /* determine if this brush is opaque to light */
3035 if( !(compileFlags & C_TRANSLUCENT) )
3037 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3043 /* emit some statistics */
3044 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3051 determines if two clusters are visible to each other using the PVS
3054 qboolean ClusterVisible( int a, int b )
3056 int portalClusters, leafBytes;
3061 if( a < 0 || b < 0 )
3069 if( numBSPVisBytes <=8 )
3073 portalClusters = ((int *) bspVisBytes)[ 0 ];
3074 leafBytes = ((int*) bspVisBytes)[ 1 ];
3075 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3078 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3087 borrowed from vlight.c
3090 int PointInLeafNum_r( vec3_t point, int nodenum )
3098 while( nodenum >= 0 )
3100 node = &bspNodes[ nodenum ];
3101 plane = &bspPlanes[ node->planeNum ];
3102 dist = DotProduct( point, plane->normal ) - plane->dist;
3104 nodenum = node->children[ 0 ];
3105 else if( dist < -0.1 )
3106 nodenum = node->children[ 1 ];
3109 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3110 if( bspLeafs[ leafnum ].cluster != -1 )
3112 nodenum = node->children[ 1 ];
3116 leafnum = -nodenum - 1;
3124 borrowed from vlight.c
3127 int PointInLeafNum( vec3_t point )
3129 return PointInLeafNum_r( point, 0 );
3135 ClusterVisibleToPoint() - ydnar
3136 returns qtrue if point can "see" cluster
3139 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3144 /* get leafNum for point */
3145 pointCluster = ClusterForPoint( point );
3146 if( pointCluster < 0 )
3150 return ClusterVisible( pointCluster, cluster );
3156 ClusterForPoint() - ydnar
3157 returns the pvs cluster for point
3160 int ClusterForPoint( vec3_t point )
3165 /* get leafNum for point */
3166 leafNum = PointInLeafNum( point );
3170 /* return the cluster */
3171 return bspLeafs[ leafNum ].cluster;
3177 ClusterForPointExt() - ydnar
3178 also takes brushes into account for occlusion testing
3181 int ClusterForPointExt( vec3_t point, float epsilon )
3183 int i, j, b, leafNum, cluster;
3186 int *brushes, numBSPBrushes;
3192 /* get leaf for point */
3193 leafNum = PointInLeafNum( point );
3196 leaf = &bspLeafs[ leafNum ];
3198 /* get the cluster */
3199 cluster = leaf->cluster;
3203 /* transparent leaf, so check point against all brushes in the leaf */
3204 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3205 numBSPBrushes = leaf->numBSPLeafBrushes;
3206 for( i = 0; i < numBSPBrushes; i++ )
3210 if( b > maxOpaqueBrush )
3212 brush = &bspBrushes[ b ];
3213 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3216 /* check point against all planes */
3218 for( j = 0; j < brush->numSides && inside; j++ )
3220 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3221 dot = DotProduct( point, plane->normal );
3227 /* if inside, return bogus cluster */
3232 /* if the point made it this far, it's not inside any opaque brushes */
3239 ClusterForPointExtFilter() - ydnar
3240 adds cluster checking against a list of known valid clusters
3243 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3248 /* get cluster for point */
3249 cluster = ClusterForPointExt( point, epsilon );
3251 /* check if filtering is necessary */
3252 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3256 for( i = 0; i < numClusters; i++ )
3258 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3269 ShaderForPointInLeaf() - ydnar
3270 checks a point against all brushes in a leaf, returning the shader of the brush
3271 also sets the cumulative surface and content flags for the brush hit
3274 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3279 int *brushes, numBSPBrushes;
3282 bspBrushSide_t *side;
3284 bspShader_t *shader;
3285 int allSurfaceFlags, allContentFlags;
3288 /* clear things out first */
3295 leaf = &bspLeafs[ leafNum ];
3297 /* transparent leaf, so check point against all brushes in the leaf */
3298 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3299 numBSPBrushes = leaf->numBSPLeafBrushes;
3300 for( i = 0; i < numBSPBrushes; i++ )
3303 brush = &bspBrushes[ brushes[ i ] ];
3305 /* check point against all planes */
3307 allSurfaceFlags = 0;
3308 allContentFlags = 0;
3309 for( j = 0; j < brush->numSides && inside; j++ )
3311 side = &bspBrushSides[ brush->firstSide + j ];
3312 plane = &bspPlanes[ side->planeNum ];
3313 dot = DotProduct( point, plane->normal );
3319 shader = &bspShaders[ side->shaderNum ];
3320 allSurfaceFlags |= shader->surfaceFlags;
3321 allContentFlags |= shader->contentFlags;
3325 /* handle if inside */
3328 /* if there are desired flags, check for same and continue if they aren't matched */
3329 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3331 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3334 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3335 *surfaceFlags = allSurfaceFlags;
3336 *contentFlags = allContentFlags;
3337 return brush->shaderNum;
3341 /* if the point made it this far, it's not inside any brushes */
3349 chops a bounding box by the plane defined by origin and normal
3350 returns qfalse if the bounds is entirely clipped away
3352 this is not exactly the fastest way to do this...
3355 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3357 /* FIXME: rewrite this so it doesn't use bloody brushes */
3365 calculates each light's effective envelope,
3366 taking into account brightness, type, and pvs.
3369 #define LIGHT_EPSILON 0.125f
3370 #define LIGHT_NUDGE 2.0f
3372 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3374 int i, x, y, z, x1, y1, z1;
3375 light_t *light, *light2, **owner;
3377 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3378 float radius, intensity;
3379 light_t *buckets[ 256 ];
3382 /* early out for weird cases where there are no lights */
3383 if( lights == NULL )
3387 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3391 numCulledLights = 0;
3393 while( *owner != NULL )
3398 /* handle negative lights */
3399 if( light->photons < 0.0f || light->add < 0.0f )
3401 light->photons *= -1.0f;
3402 light->add *= -1.0f;
3403 light->flags |= LIGHT_NEGATIVE;
3407 if( light->type == EMIT_SUN )
3411 light->envelope = MAX_WORLD_COORD * 8.0f;
3412 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3413 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3416 /* everything else */
3419 /* get pvs cluster for light */
3420 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3422 /* invalid cluster? */
3423 if( light->cluster < 0 )
3425 /* nudge the sample point around a bit */
3426 for( x = 0; x < 4; x++ )
3428 /* two's complement 0, 1, -1, 2, -2, etc */
3429 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3431 for( y = 0; y < 4; y++ )
3433 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3435 for( z = 0; z < 4; z++ )
3437 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3440 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3441 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3442 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3444 /* try at nudged origin */
3445 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3446 if( light->cluster < 0 )
3450 VectorCopy( origin, light->origin );
3456 /* only calculate for lights in pvs and outside of opaque brushes */
3457 if( light->cluster >= 0 )
3459 /* set light fast flag */
3461 light->flags |= LIGHT_FAST_TEMP;
3463 light->flags &= ~LIGHT_FAST_TEMP;
3464 if( light->si && light->si->noFast )
3465 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3467 /* clear light envelope */
3468 light->envelope = 0;
3470 /* handle area lights */
3471 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3473 /* ugly hack to calculate extent for area lights, but only done once */
3474 VectorScale( light->normal, -1.0f, dir );
3475 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3479 VectorMA( light->origin, radius, light->normal, origin );
3480 factor = PointToPolygonFormFactor( origin, dir, light->w );
3483 if( (factor * light->add) <= light->falloffTolerance )
3484 light->envelope = radius;
3487 /* check for fast mode */
3488 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3489 light->envelope = MAX_WORLD_COORD * 8.0f;
3494 intensity = light->photons;
3498 if( light->envelope <= 0.0f )
3500 /* solve distance for non-distance lights */
3501 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3502 light->envelope = MAX_WORLD_COORD * 8.0f;
3504 /* solve distance for linear lights */
3505 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3506 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3507 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3510 add = angle * light->photons * linearScale - (dist * light->fade);
3511 T = (light->photons * linearScale) - (dist * light->fade);
3512 T + (dist * light->fade) = (light->photons * linearScale);
3513 dist * light->fade = (light->photons * linearScale) - T;
3514 dist = ((light->photons * linearScale) - T) / light->fade;
3517 /* solve for inverse square falloff */
3519 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3522 add = light->photons / (dist * dist);
3523 T = light->photons / (dist * dist);
3524 T * (dist * dist) = light->photons;
3525 dist = sqrt( light->photons / T );
3529 /* chop radius against pvs */
3532 ClearBounds( mins, maxs );
3534 /* check all leaves */
3535 for( i = 0; i < numBSPLeafs; i++ )
3538 leaf = &bspLeafs[ i ];
3541 if( leaf->cluster < 0 )
3543 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3546 /* add this leafs bbox to the bounds */
3547 VectorCopy( leaf->mins, origin );
3548 AddPointToBounds( origin, mins, maxs );
3549 VectorCopy( leaf->maxs, origin );
3550 AddPointToBounds( origin, mins, maxs );
3553 /* test to see if bounds encompass light */
3554 for( i = 0; i < 3; i++ )
3556 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3558 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3559 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3560 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3561 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3562 AddPointToBounds( light->origin, mins, maxs );
3566 /* chop the bounds by a plane for area lights and spotlights */
3567 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3568 ChopBounds( mins, maxs, light->origin, light->normal );
3571 VectorCopy( mins, light->mins );
3572 VectorCopy( maxs, light->maxs );
3574 /* reflect bounds around light origin */
3575 //% VectorMA( light->origin, -1.0f, origin, origin );
3576 VectorScale( light->origin, 2, origin );
3577 VectorSubtract( origin, maxs, origin );
3578 AddPointToBounds( origin, mins, maxs );
3579 //% VectorMA( light->origin, -1.0f, mins, origin );
3580 VectorScale( light->origin, 2, origin );
3581 VectorSubtract( origin, mins, origin );
3582 AddPointToBounds( origin, mins, maxs );
3584 /* calculate spherical bounds */
3585 VectorSubtract( maxs, light->origin, dir );
3586 radius = (float) VectorLength( dir );
3588 /* if this radius is smaller than the envelope, then set the envelope to it */
3589 if( radius < light->envelope )
3591 light->envelope = radius;
3592 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3595 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3598 /* add grid/surface only check */
3601 if( !(light->flags & LIGHT_GRID) )
3602 light->envelope = 0.0f;
3606 if( !(light->flags & LIGHT_SURFACES) )
3607 light->envelope = 0.0f;
3612 if( light->cluster < 0 || light->envelope <= 0.0f )
3615 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3617 /* delete the light */
3619 *owner = light->next;
3620 if( light->w != NULL )
3627 /* square envelope */
3628 light->envelope2 = (light->envelope * light->envelope);
3630 /* increment light count */
3633 /* set next light */
3634 owner = &((**owner).next);
3637 /* bucket sort lights by style */
3638 memset( buckets, 0, sizeof( buckets ) );
3640 for( light = lights; light != NULL; light = light2 )
3642 /* get next light */
3643 light2 = light->next;
3645 /* filter into correct bucket */
3646 light->next = buckets[ light->style ];
3647 buckets[ light->style ] = light;
3649 /* if any styled light is present, automatically set nocollapse */
3650 if( light->style != LS_NORMAL )
3654 /* filter back into light list */
3656 for( i = 255; i >= 0; i-- )
3659 for( light = buckets[ i ]; light != NULL; light = light2 )
3661 light2 = light->next;
3662 light->next = lights;
3667 /* emit some statistics */
3668 Sys_Printf( "%9d total lights\n", numLights );
3669 Sys_Printf( "%9d culled lights\n", numCulledLights );
3675 CreateTraceLightsForBounds()
3676 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3679 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3683 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3684 float radius, dist, length;
3687 /* potential pre-setup */
3688 if( numLights == 0 )
3689 SetupEnvelopes( qfalse, fast );
3692 //% 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 ] );
3694 /* allocate the light list */
3695 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3696 trace->numLights = 0;
3698 /* calculate spherical bounds */
3699 VectorAdd( mins, maxs, origin );
3700 VectorScale( origin, 0.5f, origin );
3701 VectorSubtract( maxs, origin, dir );
3702 radius = (float) VectorLength( dir );
3704 /* get length of normal vector */
3705 if( normal != NULL )
3706 length = VectorLength( normal );
3709 normal = nullVector;
3713 /* test each light and see if it reaches the sphere */
3714 /* note: the attenuation code MUST match LightingAtSample() */
3715 for( light = lights; light; light = light->next )
3717 /* check zero sized envelope */
3718 if( light->envelope <= 0 )
3720 lightsEnvelopeCulled++;
3725 if( !(light->flags & flags) )
3728 /* sunlight skips all this nonsense */
3729 if( light->type != EMIT_SUN )
3735 /* check against pvs cluster */
3736 if( numClusters > 0 && clusters != NULL )
3738 for( i = 0; i < numClusters; i++ )
3740 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3745 if( i == numClusters )
3747 lightsClusterCulled++;
3752 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3753 VectorSubtract( light->origin, origin, dir );
3754 dist = VectorLength( dir );
3755 dist -= light->envelope;
3759 lightsEnvelopeCulled++;
3763 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3766 for( i = 0; i < 3; i++ )
3768 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3773 lightsBoundsCulled++;
3779 /* planar surfaces (except twosided surfaces) have a couple more checks */
3780 if( length > 0.0f && trace->twoSided == qfalse )
3782 /* lights coplanar with a surface won't light it */
3783 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3785 lightsPlaneCulled++;
3789 /* check to see if light is behind the plane */
3790 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3792 lightsPlaneCulled++;
3797 /* add this light */
3798 trace->lights[ trace->numLights++ ] = light;
3801 /* make last night null */
3802 trace->lights[ trace->numLights ] = NULL;
3807 void FreeTraceLights( trace_t *trace )
3809 if( trace->lights != NULL )
3810 free( trace->lights );
3816 CreateTraceLightsForSurface()
3817 creates a list of lights that can potentially affect a drawsurface
3820 void CreateTraceLightsForSurface( int num, trace_t *trace )
3823 vec3_t mins, maxs, normal;
3825 bspDrawSurface_t *ds;
3826 surfaceInfo_t *info;
3833 /* get drawsurface and info */
3834 ds = &bspDrawSurfaces[ num ];
3835 info = &surfaceInfos[ num ];
3837 /* get the mins/maxs for the dsurf */
3838 ClearBounds( mins, maxs );
3839 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3840 for( i = 0; i < ds->numVerts; i++ )
3842 dv = &yDrawVerts[ ds->firstVert + i ];
3843 AddPointToBounds( dv->xyz, mins, maxs );
3844 if( !VectorCompare( dv->normal, normal ) )
3845 VectorClear( normal );
3848 /* create the lights for the bounding box */
3849 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3852 /////////////////////////////////////////////////////////////
3854 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3855 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3856 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3857 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3859 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3860 static int numFloodVectors = 0;
3862 void SetupFloodLight( void )
3865 float angle, elevation, angleStep, elevationStep;
3867 double v1,v2,v3,v4,v5;
3870 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3872 /* calculate angular steps */
3873 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3874 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3878 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3880 /* iterate elevation */
3881 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3883 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3884 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3885 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3890 /* emit some statistics */
3891 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3894 value = ValueForKey( &entities[ 0 ], "_floodlight" );
3896 if( value[ 0 ] != '\0' )
3899 v4=floodlightDistance;
3900 v5=floodlightIntensity;
3902 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3904 floodlightRGB[0]=v1;
3905 floodlightRGB[1]=v2;
3906 floodlightRGB[2]=v3;
3908 if (VectorLength(floodlightRGB)==0)
3910 VectorSet(floodlightRGB,240,240,255);
3916 floodlightDistance=v4;
3917 floodlightIntensity=v5;
3919 floodlighty = qtrue;
3920 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3924 VectorSet(floodlightRGB,240,240,255);
3925 //floodlighty = qtrue;
3926 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3928 VectorNormalize(floodlightRGB,floodlightRGB);
3932 FloodLightForSample()
3933 calculates floodlight value for a given sample
3934 once again, kudos to the dirtmapping coder
3937 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
3943 float gatherLight, outLight;
3944 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
3952 if( trace == NULL || trace->cluster < 0 )
3957 dd = floodLightDistance;
3958 VectorCopy( trace->normal, normal );
3960 /* check if the normal is aligned to the world-up */
3961 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
3963 if( normal[ 2 ] == 1.0f )
3965 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
3966 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
3968 else if( normal[ 2 ] == -1.0f )
3970 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
3971 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
3976 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
3977 CrossProduct( normal, worldUp, myRt );
3978 VectorNormalize( myRt, myRt );
3979 CrossProduct( myRt, normal, myUp );
3980 VectorNormalize( myUp, myUp );
3983 /* vortex: optimise floodLightLowQuality a bit */
3984 if ( floodLightLowQuality == qtrue )
3986 /* iterate through ordered vectors */
3987 for( i = 0; i < numFloodVectors; i++ )
3988 if (rand()%10 != 0 ) continue;
3992 /* iterate through ordered vectors */
3993 for( i = 0; i < numFloodVectors; i++ )
3997 /* transform vector into tangent space */
3998 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
3999 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4000 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4003 VectorMA( trace->origin, dd, direction, trace->end );
4005 //VectorMA( trace->origin, 1, direction, trace->origin );
4007 SetupTrace( trace );
4012 if (trace->compileFlags & C_SKY )
4016 else if ( trace->opaque )
4018 VectorSubtract( trace->hit, trace->origin, displacement );
4019 d=VectorLength( displacement );
4021 // d=trace->distance;
4022 //if (d>256) gatherDirt+=1;
4024 if (contribution>1) contribution=1.0f;
4026 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4029 gatherLight+=contribution;
4034 if( gatherLight <= 0.0f )
4042 outLight=gatherLight;
4043 if( outLight > 1.0f )
4046 /* return to sender */
4051 FloodLightRawLightmap
4052 lighttracer style ambient occlusion light hack.
4053 Kudos to the dirtmapping author for most of this source.
4054 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4055 VorteX: fixed problems with deluxemapping
4058 // floodlight pass on a lightmap
4059 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4061 int i, x, y, *cluster;
4062 float *origin, *normal, *floodlight, floodLightAmount;
4063 surfaceInfo_t *info;
4066 // float samples, average, *floodlight2;
4068 memset(&trace,0,sizeof(trace_t));
4071 trace.testOcclusion = qtrue;
4072 trace.forceSunlight = qfalse;
4073 trace.twoSided = qtrue;
4074 trace.recvShadows = lm->recvShadows;
4075 trace.numSurfaces = lm->numLightSurfaces;
4076 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4077 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4078 trace.testAll = qfalse;
4079 trace.distance = 1024;
4081 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4082 //trace.twoSided = qfalse;
4083 for( i = 0; i < trace.numSurfaces; i++ )
4086 info = &surfaceInfos[ trace.surfaces[ i ] ];
4088 /* check twosidedness */
4089 if( info->si->twoSided )
4091 trace.twoSided = qtrue;
4096 /* gather floodlight */
4097 for( y = 0; y < lm->sh; y++ )
4099 for( x = 0; x < lm->sw; x++ )
4102 cluster = SUPER_CLUSTER( x, y );
4103 origin = SUPER_ORIGIN( x, y );
4104 normal = SUPER_NORMAL( x, y );
4105 floodlight = SUPER_FLOODLIGHT( x, y );
4107 /* set default dirt */
4110 /* only look at mapped luxels */
4115 trace.cluster = *cluster;
4116 VectorCopy( origin, trace.origin );
4117 VectorCopy( normal, trace.normal );
4119 /* get floodlight */
4120 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4122 /* add floodlight */
4123 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4124 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4125 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4126 floodlight[3] += floodlightDirectionScale;
4130 /* testing no filtering */
4136 for( y = 0; y < lm->sh; y++ )
4138 for( x = 0; x < lm->sw; x++ )
4141 cluster = SUPER_CLUSTER( x, y );
4142 floodlight = SUPER_FLOODLIGHT(x, y );
4144 /* filter dirt by adjacency to unmapped luxels */
4145 average = *floodlight;
4147 for( sy = (y - 1); sy <= (y + 1); sy++ )
4149 if( sy < 0 || sy >= lm->sh )
4152 for( sx = (x - 1); sx <= (x + 1); sx++ )
4154 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4157 /* get neighboring luxel */
4158 cluster = SUPER_CLUSTER( sx, sy );
4159 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4160 if( *cluster < 0 || *floodlight2 <= 0.0f )
4164 average += *floodlight2;
4169 if( samples <= 0.0f )
4174 if( samples <= 0.0f )
4178 *floodlight = average / samples;
4184 void FloodLightRawLightmap( int rawLightmapNum )
4188 /* bail if this number exceeds the number of raw lightmaps */
4189 if( rawLightmapNum >= numRawLightmaps )
4192 lm = &rawLightmaps[ rawLightmapNum ];
4195 if (floodlighty && floodlightIntensity)
4196 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4199 if (lm->floodlightIntensity)
4201 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4202 numSurfacesFloodlighten += 1;
4206 void FloodlightRawLightmaps()
4208 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4209 numSurfacesFloodlighten = 0;
4210 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4211 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4215 FloodLightIlluminate()
4216 illuminate floodlight into lightmap luxels
4219 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4221 float *luxel, *floodlight, *deluxel, *normal;
4224 int x, y, lightmapNum;
4226 /* walk lightmaps */
4227 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4230 if( lm->superLuxels[ lightmapNum ] == NULL )
4233 /* apply floodlight to each luxel */
4234 for( y = 0; y < lm->sh; y++ )
4236 for( x = 0; x < lm->sw; x++ )
4238 /* get floodlight */
4239 floodlight = SUPER_FLOODLIGHT( x, y );
4240 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4244 cluster = SUPER_CLUSTER( x, y );
4246 /* only process mapped luxels */
4250 /* get particulars */
4251 luxel = SUPER_LUXEL( lightmapNum, x, y );
4252 deluxel = SUPER_DELUXEL( x, y );
4254 /* add to lightmap */
4255 luxel[0]+=floodlight[0];
4256 luxel[1]+=floodlight[1];
4257 luxel[2]+=floodlight[2];
4259 if (luxel[3]==0) luxel[3]=1;
4261 /* add to deluxemap */
4262 if (deluxemap && floodlight[3] > 0)
4266 normal = SUPER_NORMAL( x, y );
4267 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4269 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4270 if(brightness < 0.00390625f)
4271 brightness = 0.00390625f;
4273 VectorScale( normal, brightness, lightvector );
4274 VectorAdd( deluxel, lightvector, deluxel );