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 )
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;
1551 /* bail if this number exceeds the number of raw lightmaps */
1552 if( rawLightmapNum >= numRawLightmaps )
1556 lm = &rawLightmaps[ rawLightmapNum ];
1559 trace.testOcclusion = qtrue;
1560 trace.forceSunlight = qfalse;
1561 trace.recvShadows = lm->recvShadows;
1562 trace.numSurfaces = lm->numLightSurfaces;
1563 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1564 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1565 trace.testAll = qtrue;
1567 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1568 trace.twoSided = qfalse;
1569 for( i = 0; i < trace.numSurfaces; i++ )
1572 info = &surfaceInfos[ trace.surfaces[ i ] ];
1574 /* check twosidedness */
1575 if( info->si->twoSided )
1577 trace.twoSided = qtrue;
1583 for( y = 0; y < lm->sh; y++ )
1585 for( x = 0; x < lm->sw; x++ )
1588 cluster = SUPER_CLUSTER( x, y );
1589 origin = SUPER_ORIGIN( x, y );
1590 normal = SUPER_NORMAL( x, y );
1591 dirt = SUPER_DIRT( x, y );
1593 /* set default dirt */
1596 /* only look at mapped luxels */
1601 trace.cluster = *cluster;
1602 VectorCopy( origin, trace.origin );
1603 VectorCopy( normal, trace.normal );
1606 *dirt = DirtForSample( &trace );
1610 /* testing no filtering */
1614 for( y = 0; y < lm->sh; y++ )
1616 for( x = 0; x < lm->sw; x++ )
1619 cluster = SUPER_CLUSTER( x, y );
1620 dirt = SUPER_DIRT( x, y );
1622 /* filter dirt by adjacency to unmapped luxels */
1625 for( sy = (y - 1); sy <= (y + 1); sy++ )
1627 if( sy < 0 || sy >= lm->sh )
1630 for( sx = (x - 1); sx <= (x + 1); sx++ )
1632 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1635 /* get neighboring luxel */
1636 cluster = SUPER_CLUSTER( sx, sy );
1637 dirt2 = SUPER_DIRT( sx, sy );
1638 if( *cluster < 0 || *dirt2 <= 0.0f )
1647 if( samples <= 0.0f )
1652 if( samples <= 0.0f )
1656 *dirt = average / samples;
1665 calculates the pvs cluster, origin, normal of a sub-luxel
1668 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1670 int i, *cluster, *cluster2;
1671 float *origin, *origin2, *normal; //% , *normal2;
1672 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1675 /* calulate x vector */
1676 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1678 cluster = SUPER_CLUSTER( x, y );
1679 origin = SUPER_ORIGIN( x, y );
1680 //% normal = SUPER_NORMAL( x, y );
1681 cluster2 = SUPER_CLUSTER( x + 1, y );
1682 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1683 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1685 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1687 cluster = SUPER_CLUSTER( x - 1, y );
1688 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1689 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1690 cluster2 = SUPER_CLUSTER( x, y );
1691 origin2 = SUPER_ORIGIN( x, y );
1692 //% normal2 = SUPER_NORMAL( x, y );
1695 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1697 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1698 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1700 /* calulate y vector */
1701 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1703 cluster = SUPER_CLUSTER( x, y );
1704 origin = SUPER_ORIGIN( x, y );
1705 //% normal = SUPER_NORMAL( x, y );
1706 cluster2 = SUPER_CLUSTER( x, y + 1 );
1707 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1708 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1710 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1712 cluster = SUPER_CLUSTER( x, y - 1 );
1713 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1714 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1715 cluster2 = SUPER_CLUSTER( x, y );
1716 origin2 = SUPER_ORIGIN( x, y );
1717 //% normal2 = SUPER_NORMAL( x, y );
1720 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1722 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1723 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1725 /* calculate new origin */
1726 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1727 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1728 for( i = 0; i < 3; i++ )
1729 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1732 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1733 if( *sampleCluster < 0 )
1736 /* calculate new normal */
1737 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1738 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1739 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1741 normal = SUPER_NORMAL( x, y );
1742 VectorCopy( normal, sampleNormal );
1750 SubsampleRawLuxel_r()
1751 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1754 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1756 int b, samples, mapped, lighted;
1759 vec3_t origin[ 4 ], normal[ 4 ];
1760 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1761 vec3_t color, total;
1765 if( lightLuxel[ 3 ] >= lightSamples )
1769 VectorClear( total );
1773 /* make 2x2 subsample stamp */
1774 for( b = 0; b < 4; b++ )
1777 VectorCopy( sampleOrigin, origin[ b ] );
1779 /* calculate position */
1780 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1787 /* increment sample count */
1788 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1791 trace->cluster = *cluster;
1792 VectorCopy( origin[ b ], trace->origin );
1793 VectorCopy( normal[ b ], trace->normal );
1797 LightContributionToSample( trace );
1799 /* add to totals (fixme: make contrast function) */
1800 VectorCopy( trace->color, luxel[ b ] );
1801 VectorAdd( total, trace->color, total );
1802 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1806 /* subsample further? */
1807 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1808 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1809 lighted != 0 && lighted != mapped )
1811 for( b = 0; b < 4; b++ )
1813 if( cluster[ b ] < 0 )
1815 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );
1820 //% VectorClear( color );
1822 VectorCopy( lightLuxel, color );
1824 for( b = 0; b < 4; b++ )
1826 if( cluster[ b ] < 0 )
1828 VectorAdd( color, luxel[ b ], color );
1836 color[ 0 ] /= samples;
1837 color[ 1 ] /= samples;
1838 color[ 2 ] /= samples;
1841 VectorCopy( color, lightLuxel );
1842 lightLuxel[ 3 ] += 1.0f;
1849 IlluminateRawLightmap()
1850 illuminates the luxels
1853 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1854 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1856 void IlluminateRawLightmap( int rawLightmapNum )
1858 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1859 int *cluster, *cluster2, mapped, lighted, totalLighted;
1861 surfaceInfo_t *info;
1862 qboolean filterColor, filterDir;
1864 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1865 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1866 vec3_t color, averageColor, averageDir, total, temp, temp2;
1867 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1869 float stackLightLuxels[ STACK_LL_SIZE ];
1872 /* bail if this number exceeds the number of raw lightmaps */
1873 if( rawLightmapNum >= numRawLightmaps )
1877 lm = &rawLightmaps[ rawLightmapNum ];
1880 trace.testOcclusion = !noTrace;
1881 trace.forceSunlight = qfalse;
1882 trace.recvShadows = lm->recvShadows;
1883 trace.numSurfaces = lm->numLightSurfaces;
1884 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1885 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1887 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1888 trace.twoSided = qfalse;
1889 for( i = 0; i < trace.numSurfaces; i++ )
1892 info = &surfaceInfos[ trace.surfaces[ i ] ];
1894 /* check twosidedness */
1895 if( info->si->twoSided )
1897 trace.twoSided = qtrue;
1902 /* create a culled light list for this raw lightmap */
1903 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1905 /* -----------------------------------------------------------------
1907 ----------------------------------------------------------------- */
1910 numLuxelsIlluminated += (lm->sw * lm->sh);
1912 /* test debugging state */
1913 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1915 /* debug fill the luxels */
1916 for( y = 0; y < lm->sh; y++ )
1918 for( x = 0; x < lm->sw; x++ )
1921 cluster = SUPER_CLUSTER( x, y );
1923 /* only fill mapped luxels */
1927 /* get particulars */
1928 luxel = SUPER_LUXEL( 0, x, y );
1929 origin = SUPER_ORIGIN( x, y );
1930 normal = SUPER_NORMAL( x, y );
1932 /* color the luxel with raw lightmap num? */
1934 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1936 /* color the luxel with lightmap axis? */
1937 else if( debugAxis )
1939 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1940 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1941 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1944 /* color the luxel with luxel cluster? */
1945 else if( debugCluster )
1946 VectorCopy( debugColors[ *cluster % 12 ], luxel );
1948 /* color the luxel with luxel origin? */
1949 else if( debugOrigin )
1951 VectorSubtract( lm->maxs, lm->mins, temp );
1952 VectorScale( temp, (1.0f / 255.0f), temp );
1953 VectorSubtract( origin, lm->mins, temp2 );
1954 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
1955 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
1956 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
1959 /* color the luxel with the normal */
1960 else if( normalmap )
1962 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
1963 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
1964 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
1967 /* otherwise clear it */
1969 VectorClear( luxel );
1978 /* allocate temporary per-light luxel storage */
1979 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1980 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
1981 lightLuxels = stackLightLuxels;
1983 lightLuxels = safe_malloc( llSize );
1986 //% memset( lm->superLuxels[ 0 ], 0, llSize );
1988 /* set ambient color */
1989 for( y = 0; y < lm->sh; y++ )
1991 for( x = 0; x < lm->sw; x++ )
1994 cluster = SUPER_CLUSTER( x, y );
1995 luxel = SUPER_LUXEL( 0, x, y );
1996 normal = SUPER_NORMAL( x, y );
1997 deluxel = SUPER_DELUXEL( x, y );
1999 /* blacken unmapped clusters */
2001 VectorClear( luxel );
2006 VectorCopy( ambientColor, luxel );
2009 brightness = ambientColor[ 0 ] * 0.3f + ambientColor[ 1 ] * 0.59f + ambientColor[ 2 ] * 0.11f;
2010 brightness *= (1.0 / 255.0);
2011 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2012 if(brightness < 0.00390625f)
2013 brightness = 0.00390625f;
2014 VectorScale( normal, brightness, deluxel );
2021 /* clear styled lightmaps */
2022 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2023 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2025 if( lm->superLuxels[ lightmapNum ] != NULL )
2026 memset( lm->superLuxels[ lightmapNum ], 0, size );
2029 /* debugging code */
2030 //% if( trace.numLights <= 0 )
2031 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2033 /* walk light list */
2034 for( i = 0; i < trace.numLights; i++ )
2037 trace.light = trace.lights[ i ];
2040 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2042 if( lm->styles[ lightmapNum ] == trace.light->style ||
2043 lm->styles[ lightmapNum ] == LS_NONE )
2047 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2048 if( lightmapNum >= MAX_LIGHTMAPS )
2050 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2055 memset( lightLuxels, 0, llSize );
2058 /* initial pass, one sample per luxel */
2059 for( y = 0; y < lm->sh; y++ )
2061 for( x = 0; x < lm->sw; x++ )
2064 cluster = SUPER_CLUSTER( x, y );
2068 /* get particulars */
2069 lightLuxel = LIGHT_LUXEL( x, y );
2070 deluxel = SUPER_DELUXEL( x, y );
2071 origin = SUPER_ORIGIN( x, y );
2072 normal = SUPER_NORMAL( x, y );
2075 ////////// 27's temp hack for testing edge clipping ////
2076 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2078 lightLuxel[ 1 ] = 255;
2079 lightLuxel[ 3 ] = 1.0f;
2085 /* set contribution count */
2086 lightLuxel[ 3 ] = 1.0f;
2089 trace.cluster = *cluster;
2090 VectorCopy( origin, trace.origin );
2091 VectorCopy( normal, trace.normal );
2093 /* get light for this sample */
2094 LightContributionToSample( &trace );
2095 VectorCopy( trace.color, lightLuxel );
2098 if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2102 /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
2105 if(DotProduct(normal, trace.direction) > 0) // do not take light from the back side
2107 /* color to grayscale (photoshop rgb weighting) */
2108 brightness = trace.colorNoShadow[ 0 ] * 0.3f + trace.colorNoShadow[ 1 ] * 0.59f + trace.colorNoShadow[ 2 ] * 0.11f;
2109 brightness *= (1.0 / 255.0);
2110 VectorScale( trace.direction, brightness, trace.direction );
2111 VectorAdd( deluxel, trace.direction, deluxel );
2117 /* don't even bother with everything else if nothing was lit */
2118 if( totalLighted == 0 )
2121 /* determine filter radius */
2122 filterRadius = lm->filterRadius > trace.light->filterRadius
2124 : trace.light->filterRadius;
2125 if( filterRadius < 0.0f )
2126 filterRadius = 0.0f;
2128 /* set luxel filter radius */
2129 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2130 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2131 luxelFilterRadius = 1;
2133 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2134 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2135 if( lightSamples > 1 && luxelFilterRadius == 0 )
2138 for( y = 0; y < (lm->sh - 1); y++ )
2140 for( x = 0; x < (lm->sw - 1); x++ )
2145 VectorClear( total );
2147 /* test 2x2 stamp */
2148 for( t = 0; t < 4; t++ )
2150 /* set sample coords */
2151 sx = x + tests[ t ][ 0 ];
2152 sy = y + tests[ t ][ 1 ];
2155 cluster = SUPER_CLUSTER( sx, sy );
2161 lightLuxel = LIGHT_LUXEL( sx, sy );
2162 VectorAdd( total, lightLuxel, total );
2163 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2167 /* if total color is under a certain amount, then don't bother subsampling */
2168 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2171 /* if all 4 pixels are either in shadow or light, then don't subsample */
2172 if( lighted != 0 && lighted != mapped )
2174 for( t = 0; t < 4; t++ )
2176 /* set sample coords */
2177 sx = x + tests[ t ][ 0 ];
2178 sy = y + tests[ t ][ 1 ];
2181 cluster = SUPER_CLUSTER( sx, sy );
2184 lightLuxel = LIGHT_LUXEL( sx, sy );
2185 origin = SUPER_ORIGIN( sx, sy );
2187 /* only subsample shadowed luxels */
2188 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2192 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
2194 /* debug code to colorize subsampled areas to yellow */
2195 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2196 //% VectorSet( luxel, 255, 204, 0 );
2203 /* tertiary pass, apply dirt map (ambient occlusion) */
2207 for( y = 0; y < lm->sh; y++ )
2209 for( x = 0; x < lm->sw; x++ )
2212 cluster = SUPER_CLUSTER( x, y );
2216 /* get particulars */
2217 lightLuxel = LIGHT_LUXEL( x, y );
2218 dirt = SUPER_DIRT( x, y );
2220 /* scale light value */
2221 VectorScale( lightLuxel, *dirt, lightLuxel );
2226 /* allocate sampling lightmap storage */
2227 if( lm->superLuxels[ lightmapNum ] == NULL )
2229 /* allocate sampling lightmap storage */
2230 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2231 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2232 memset( lm->superLuxels[ lightmapNum ], 0, size );
2236 if( lightmapNum > 0 )
2238 lm->styles[ lightmapNum ] = trace.light->style;
2239 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2242 /* copy to permanent luxels */
2243 for( y = 0; y < lm->sh; y++ )
2245 for( x = 0; x < lm->sw; x++ )
2247 /* get cluster and origin */
2248 cluster = SUPER_CLUSTER( x, y );
2251 origin = SUPER_ORIGIN( x, y );
2254 if( luxelFilterRadius )
2257 VectorClear( averageColor );
2260 /* cheaper distance-based filtering */
2261 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2263 if( sy < 0 || sy >= lm->sh )
2266 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2268 if( sx < 0 || sx >= lm->sw )
2271 /* get particulars */
2272 cluster = SUPER_CLUSTER( sx, sy );
2275 lightLuxel = LIGHT_LUXEL( sx, sy );
2278 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2279 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2281 /* scale luxel by filter weight */
2282 VectorScale( lightLuxel, weight, color );
2283 VectorAdd( averageColor, color, averageColor );
2289 if( samples <= 0.0f )
2292 /* scale into luxel */
2293 luxel = SUPER_LUXEL( lightmapNum, x, y );
2296 /* handle negative light */
2297 if( trace.light->flags & LIGHT_NEGATIVE )
2299 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2300 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2301 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2304 /* handle normal light */
2307 luxel[ 0 ] += averageColor[ 0 ] / samples;
2308 luxel[ 1 ] += averageColor[ 1 ] / samples;
2309 luxel[ 2 ] += averageColor[ 2 ] / samples;
2316 /* get particulars */
2317 lightLuxel = LIGHT_LUXEL( x, y );
2318 luxel = SUPER_LUXEL( lightmapNum, x, y );
2320 /* handle negative light */
2321 if( trace.light->flags & LIGHT_NEGATIVE )
2322 VectorScale( averageColor, -1.0f, averageColor );
2327 /* handle negative light */
2328 if( trace.light->flags & LIGHT_NEGATIVE )
2329 VectorSubtract( luxel, lightLuxel, luxel );
2331 /* handle normal light */
2333 VectorAdd( luxel, lightLuxel, luxel );
2339 /* free temporary luxels */
2340 if( lightLuxels != stackLightLuxels )
2341 free( lightLuxels );
2344 /* free light list */
2345 FreeTraceLights( &trace );
2347 /* floodlight pass */
2348 FloodlightIlluminateLightmap(lm);
2352 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2355 if( lm->superLuxels[ lightmapNum ] == NULL )
2358 for( y = 0; y < lm->sh; y++ )
2360 for( x = 0; x < lm->sw; x++ )
2363 cluster = SUPER_CLUSTER( x, y );
2364 //% if( *cluster < 0 )
2367 /* get particulars */
2368 luxel = SUPER_LUXEL( lightmapNum, x, y );
2369 normal = SUPER_NORMAL ( x, y );
2371 luxel[0]=(normal[0]*127)+127;
2372 luxel[1]=(normal[1]*127)+127;
2373 luxel[2]=(normal[2]*127)+127;
2379 /* -----------------------------------------------------------------
2381 ----------------------------------------------------------------- */
2385 /* walk lightmaps */
2386 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2389 if( lm->superLuxels[ lightmapNum ] == NULL )
2392 /* apply dirt to each luxel */
2393 for( y = 0; y < lm->sh; y++ )
2395 for( x = 0; x < lm->sw; x++ )
2398 cluster = SUPER_CLUSTER( x, y );
2399 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2402 /* get particulars */
2403 luxel = SUPER_LUXEL( lightmapNum, x, y );
2404 dirt = SUPER_DIRT( x, y );
2407 VectorScale( luxel, *dirt, luxel );
2411 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2417 /* -----------------------------------------------------------------
2419 ----------------------------------------------------------------- */
2421 /* walk lightmaps */
2422 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2425 if( lm->superLuxels[ lightmapNum ] == NULL )
2428 /* average occluded luxels from neighbors */
2429 for( y = 0; y < lm->sh; y++ )
2431 for( x = 0; x < lm->sw; x++ )
2433 /* get particulars */
2434 cluster = SUPER_CLUSTER( x, y );
2435 luxel = SUPER_LUXEL( lightmapNum, x, y );
2436 deluxel = SUPER_DELUXEL( x, y );
2437 normal = SUPER_NORMAL( x, y );
2439 /* determine if filtering is necessary */
2440 filterColor = qfalse;
2443 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2444 filterColor = qtrue;
2445 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2448 if( !filterColor && !filterDir )
2451 /* choose seed amount */
2452 VectorClear( averageColor );
2453 VectorClear( averageDir );
2456 /* walk 3x3 matrix */
2457 for( sy = (y - 1); sy <= (y + 1); sy++ )
2459 if( sy < 0 || sy >= lm->sh )
2462 for( sx = (x - 1); sx <= (x + 1); sx++ )
2464 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2467 /* get neighbor's particulars */
2468 cluster2 = SUPER_CLUSTER( sx, sy );
2469 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2470 deluxel2 = SUPER_DELUXEL( sx, sy );
2472 /* ignore unmapped/unlit luxels */
2473 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2474 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2477 /* add its distinctiveness to our own */
2478 VectorAdd( averageColor, luxel2, averageColor );
2479 samples += luxel2[ 3 ];
2481 VectorAdd( averageDir, deluxel2, averageDir );
2486 if( samples <= 0.0f )
2489 /* dark lightmap seams */
2492 if( lightmapNum == 0 )
2493 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2500 VectorDivide( averageColor, samples, luxel );
2504 VectorDivide( averageDir, samples, deluxel );
2506 /* set cluster to -3 */
2508 *cluster = CLUSTER_FLOODED;
2516 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2519 if( lm->superLuxels[ lightmapNum ] == NULL )
2521 for( y = 0; y < lm->sh; y++ )
2522 for( x = 0; x < lm->sw; x++ )
2525 cluster = SUPER_CLUSTER( x, y );
2526 luxel = SUPER_LUXEL( lightmapNum, x, y );
2527 deluxel = SUPER_DELUXEL( x, y );
2528 if(!luxel || !deluxel || !cluster)
2530 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2533 else if(*cluster < 0)
2536 // should have neither deluxemap nor lightmap
2538 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2543 // should have both deluxemap and lightmap
2545 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2555 IlluminateVertexes()
2556 light the surface vertexes
2559 #define VERTEX_NUDGE 4.0f
2561 void IlluminateVertexes( int num )
2563 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2564 int lightmapNum, numAvg;
2565 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2566 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2567 bspDrawSurface_t *ds;
2568 surfaceInfo_t *info;
2570 bspDrawVert_t *verts;
2574 /* get surface, info, and raw lightmap */
2575 ds = &bspDrawSurfaces[ num ];
2576 info = &surfaceInfos[ num ];
2579 /* -----------------------------------------------------------------
2580 illuminate the vertexes
2581 ----------------------------------------------------------------- */
2583 /* calculate vertex lighting for surfaces without lightmaps */
2584 if( lm == NULL || cpmaHack )
2587 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2588 trace.forceSunlight = info->si->forceSunlight;
2589 trace.recvShadows = info->recvShadows;
2590 trace.numSurfaces = 1;
2591 trace.surfaces = #
2592 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2594 /* twosided lighting */
2595 trace.twoSided = info->si->twoSided;
2597 /* make light list for this surface */
2598 CreateTraceLightsForSurface( num, &trace );
2601 verts = yDrawVerts + ds->firstVert;
2603 memset( avgColors, 0, sizeof( avgColors ) );
2605 /* walk the surface verts */
2606 for( i = 0; i < ds->numVerts; i++ )
2608 /* get vertex luxel */
2609 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2611 /* color the luxel with raw lightmap num? */
2613 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2615 /* color the luxel with luxel origin? */
2616 else if( debugOrigin )
2618 VectorSubtract( info->maxs, info->mins, temp );
2619 VectorScale( temp, (1.0f / 255.0f), temp );
2620 VectorSubtract( origin, lm->mins, temp2 );
2621 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2622 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2623 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2626 /* color the luxel with the normal */
2627 else if( normalmap )
2629 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2630 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2631 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2634 /* illuminate the vertex */
2637 /* clear vertex luxel */
2638 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2640 /* try at initial origin */
2641 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2642 if( trace.cluster >= 0 )
2645 VectorCopy( verts[ i ].xyz, trace.origin );
2646 VectorCopy( verts[ i ].normal, trace.normal );
2650 dirt = DirtForSample( &trace );
2655 LightingAtSample( &trace, ds->vertexStyles, colors );
2658 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2661 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2664 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2665 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2666 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2670 /* is this sample bright enough? */
2671 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2672 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2673 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2674 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2676 /* nudge the sample point around a bit */
2677 for( x = 0; x < 4; x++ )
2679 /* two's complement 0, 1, -1, 2, -2, etc */
2680 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2682 for( y = 0; y < 4; y++ )
2684 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2686 for( z = 0; z < 4; z++ )
2688 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2691 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2692 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2693 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2695 /* try at nudged origin */
2696 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2697 if( trace.cluster < 0 )
2701 LightingAtSample( &trace, ds->vertexStyles, colors );
2704 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2707 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2710 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2711 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2714 /* bright enough? */
2715 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2716 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2717 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2718 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2725 /* add to average? */
2726 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2727 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2728 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2729 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2732 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2734 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2735 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2740 /* another happy customer */
2741 numVertsIlluminated++;
2744 /* set average color */
2747 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2748 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2752 VectorCopy( ambientColor, avgColors[ 0 ] );
2755 /* clean up and store vertex color */
2756 for( i = 0; i < ds->numVerts; i++ )
2758 /* get vertex luxel */
2759 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2761 /* store average in occluded vertexes */
2762 if( radVertLuxel[ 0 ] < 0.0f )
2764 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2766 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2767 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2770 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2775 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2778 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2779 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2782 if( bouncing || bounce == 0 || !bounceOnly )
2783 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2784 if( !info->si->noVertexLight )
2785 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2789 /* free light list */
2790 FreeTraceLights( &trace );
2792 /* return to sender */
2796 /* -----------------------------------------------------------------
2797 reconstitute vertex lighting from the luxels
2798 ----------------------------------------------------------------- */
2800 /* set styles from lightmap */
2801 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2802 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2804 /* get max search radius */
2806 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2808 /* walk the surface verts */
2809 verts = yDrawVerts + ds->firstVert;
2810 for( i = 0; i < ds->numVerts; i++ )
2812 /* do each lightmap */
2813 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2816 if( lm->superLuxels[ lightmapNum ] == NULL )
2819 /* get luxel coords */
2820 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2821 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2824 else if( x >= lm->sw )
2828 else if( y >= lm->sh )
2831 /* get vertex luxels */
2832 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2833 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2835 /* color the luxel with the normal? */
2838 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2839 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2840 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2843 /* color the luxel with surface num? */
2844 else if( debugSurfaces )
2845 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2847 /* divine color from the superluxels */
2850 /* increasing radius */
2851 VectorClear( radVertLuxel );
2853 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2855 /* sample within radius */
2856 for( sy = (y - radius); sy <= (y + radius); sy++ )
2858 if( sy < 0 || sy >= lm->sh )
2861 for( sx = (x - radius); sx <= (x + radius); sx++ )
2863 if( sx < 0 || sx >= lm->sw )
2866 /* get luxel particulars */
2867 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2868 cluster = SUPER_CLUSTER( sx, sy );
2872 /* testing: must be brigher than ambient color */
2873 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2876 /* add its distinctiveness to our own */
2877 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2878 samples += luxel[ 3 ];
2884 if( samples > 0.0f )
2885 VectorDivide( radVertLuxel, samples, radVertLuxel );
2887 VectorCopy( ambientColor, radVertLuxel );
2890 /* store into floating point storage */
2891 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2892 numVertsIlluminated++;
2894 /* store into bytes (for vertex approximation) */
2895 if( !info->si->noVertexLight )
2896 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2903 /* -------------------------------------------------------------------------------
2905 light optimization (-fast)
2907 creates a list of lights that will affect a surface and stores it in tw
2908 this is to optimize surface lighting by culling out as many of the
2909 lights in the world as possible from further calculation
2911 ------------------------------------------------------------------------------- */
2915 determines opaque brushes in the world and find sky shaders for sunlight calculations
2918 void SetupBrushes( void )
2920 int i, j, b, compileFlags;
2923 bspBrushSide_t *side;
2924 bspShader_t *shader;
2929 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
2932 if( opaqueBrushes == NULL )
2933 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
2936 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
2937 numOpaqueBrushes = 0;
2939 /* walk the list of worldspawn brushes */
2940 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
2943 b = bspModels[ 0 ].firstBSPBrush + i;
2944 brush = &bspBrushes[ b ];
2946 /* check all sides */
2949 for( j = 0; j < brush->numSides && inside; j++ )
2951 /* do bsp shader calculations */
2952 side = &bspBrushSides[ brush->firstSide + j ];
2953 shader = &bspShaders[ side->shaderNum ];
2955 /* get shader info */
2956 si = ShaderInfoForShader( shader->shader );
2960 /* or together compile flags */
2961 compileFlags |= si->compileFlags;
2964 /* determine if this brush is opaque to light */
2965 if( !(compileFlags & C_TRANSLUCENT) )
2967 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
2973 /* emit some statistics */
2974 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
2981 determines if two clusters are visible to each other using the PVS
2984 qboolean ClusterVisible( int a, int b )
2986 int portalClusters, leafBytes;
2991 if( a < 0 || b < 0 )
2999 if( numBSPVisBytes <=8 )
3003 portalClusters = ((int *) bspVisBytes)[ 0 ];
3004 leafBytes = ((int*) bspVisBytes)[ 1 ];
3005 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3008 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3017 borrowed from vlight.c
3020 int PointInLeafNum_r( vec3_t point, int nodenum )
3028 while( nodenum >= 0 )
3030 node = &bspNodes[ nodenum ];
3031 plane = &bspPlanes[ node->planeNum ];
3032 dist = DotProduct( point, plane->normal ) - plane->dist;
3034 nodenum = node->children[ 0 ];
3035 else if( dist < -0.1 )
3036 nodenum = node->children[ 1 ];
3039 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3040 if( bspLeafs[ leafnum ].cluster != -1 )
3042 nodenum = node->children[ 1 ];
3046 leafnum = -nodenum - 1;
3054 borrowed from vlight.c
3057 int PointInLeafNum( vec3_t point )
3059 return PointInLeafNum_r( point, 0 );
3065 ClusterVisibleToPoint() - ydnar
3066 returns qtrue if point can "see" cluster
3069 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3074 /* get leafNum for point */
3075 pointCluster = ClusterForPoint( point );
3076 if( pointCluster < 0 )
3080 return ClusterVisible( pointCluster, cluster );
3086 ClusterForPoint() - ydnar
3087 returns the pvs cluster for point
3090 int ClusterForPoint( vec3_t point )
3095 /* get leafNum for point */
3096 leafNum = PointInLeafNum( point );
3100 /* return the cluster */
3101 return bspLeafs[ leafNum ].cluster;
3107 ClusterForPointExt() - ydnar
3108 also takes brushes into account for occlusion testing
3111 int ClusterForPointExt( vec3_t point, float epsilon )
3113 int i, j, b, leafNum, cluster;
3116 int *brushes, numBSPBrushes;
3122 /* get leaf for point */
3123 leafNum = PointInLeafNum( point );
3126 leaf = &bspLeafs[ leafNum ];
3128 /* get the cluster */
3129 cluster = leaf->cluster;
3133 /* transparent leaf, so check point against all brushes in the leaf */
3134 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3135 numBSPBrushes = leaf->numBSPLeafBrushes;
3136 for( i = 0; i < numBSPBrushes; i++ )
3140 if( b > maxOpaqueBrush )
3142 brush = &bspBrushes[ b ];
3143 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3146 /* check point against all planes */
3148 for( j = 0; j < brush->numSides && inside; j++ )
3150 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3151 dot = DotProduct( point, plane->normal );
3157 /* if inside, return bogus cluster */
3162 /* if the point made it this far, it's not inside any opaque brushes */
3169 ClusterForPointExtFilter() - ydnar
3170 adds cluster checking against a list of known valid clusters
3173 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3178 /* get cluster for point */
3179 cluster = ClusterForPointExt( point, epsilon );
3181 /* check if filtering is necessary */
3182 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3186 for( i = 0; i < numClusters; i++ )
3188 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3199 ShaderForPointInLeaf() - ydnar
3200 checks a point against all brushes in a leaf, returning the shader of the brush
3201 also sets the cumulative surface and content flags for the brush hit
3204 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3209 int *brushes, numBSPBrushes;
3212 bspBrushSide_t *side;
3214 bspShader_t *shader;
3215 int allSurfaceFlags, allContentFlags;
3218 /* clear things out first */
3225 leaf = &bspLeafs[ leafNum ];
3227 /* transparent leaf, so check point against all brushes in the leaf */
3228 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3229 numBSPBrushes = leaf->numBSPLeafBrushes;
3230 for( i = 0; i < numBSPBrushes; i++ )
3233 brush = &bspBrushes[ brushes[ i ] ];
3235 /* check point against all planes */
3237 allSurfaceFlags = 0;
3238 allContentFlags = 0;
3239 for( j = 0; j < brush->numSides && inside; j++ )
3241 side = &bspBrushSides[ brush->firstSide + j ];
3242 plane = &bspPlanes[ side->planeNum ];
3243 dot = DotProduct( point, plane->normal );
3249 shader = &bspShaders[ side->shaderNum ];
3250 allSurfaceFlags |= shader->surfaceFlags;
3251 allContentFlags |= shader->contentFlags;
3255 /* handle if inside */
3258 /* if there are desired flags, check for same and continue if they aren't matched */
3259 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3261 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3264 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3265 *surfaceFlags = allSurfaceFlags;
3266 *contentFlags = allContentFlags;
3267 return brush->shaderNum;
3271 /* if the point made it this far, it's not inside any brushes */
3279 chops a bounding box by the plane defined by origin and normal
3280 returns qfalse if the bounds is entirely clipped away
3282 this is not exactly the fastest way to do this...
3285 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3287 /* FIXME: rewrite this so it doesn't use bloody brushes */
3295 calculates each light's effective envelope,
3296 taking into account brightness, type, and pvs.
3299 #define LIGHT_EPSILON 0.125f
3300 #define LIGHT_NUDGE 2.0f
3302 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3304 int i, x, y, z, x1, y1, z1;
3305 light_t *light, *light2, **owner;
3307 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3308 float radius, intensity;
3309 light_t *buckets[ 256 ];
3312 /* early out for weird cases where there are no lights */
3313 if( lights == NULL )
3317 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3321 numCulledLights = 0;
3323 while( *owner != NULL )
3328 /* handle negative lights */
3329 if( light->photons < 0.0f || light->add < 0.0f )
3331 light->photons *= -1.0f;
3332 light->add *= -1.0f;
3333 light->flags |= LIGHT_NEGATIVE;
3337 if( light->type == EMIT_SUN )
3341 light->envelope = MAX_WORLD_COORD * 8.0f;
3342 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3343 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3346 /* everything else */
3349 /* get pvs cluster for light */
3350 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3352 /* invalid cluster? */
3353 if( light->cluster < 0 )
3355 /* nudge the sample point around a bit */
3356 for( x = 0; x < 4; x++ )
3358 /* two's complement 0, 1, -1, 2, -2, etc */
3359 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3361 for( y = 0; y < 4; y++ )
3363 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3365 for( z = 0; z < 4; z++ )
3367 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3370 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3371 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3372 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3374 /* try at nudged origin */
3375 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3376 if( light->cluster < 0 )
3380 VectorCopy( origin, light->origin );
3386 /* only calculate for lights in pvs and outside of opaque brushes */
3387 if( light->cluster >= 0 )
3389 /* set light fast flag */
3391 light->flags |= LIGHT_FAST_TEMP;
3393 light->flags &= ~LIGHT_FAST_TEMP;
3394 if( light->si && light->si->noFast )
3395 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3397 /* clear light envelope */
3398 light->envelope = 0;
3400 /* handle area lights */
3401 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3403 /* ugly hack to calculate extent for area lights, but only done once */
3404 VectorScale( light->normal, -1.0f, dir );
3405 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3409 VectorMA( light->origin, radius, light->normal, origin );
3410 factor = PointToPolygonFormFactor( origin, dir, light->w );
3413 if( (factor * light->add) <= light->falloffTolerance )
3414 light->envelope = radius;
3417 /* check for fast mode */
3418 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3419 light->envelope = MAX_WORLD_COORD * 8.0f;
3424 intensity = light->photons;
3428 if( light->envelope <= 0.0f )
3430 /* solve distance for non-distance lights */
3431 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3432 light->envelope = MAX_WORLD_COORD * 8.0f;
3434 /* solve distance for linear lights */
3435 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3436 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3437 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3440 add = angle * light->photons * linearScale - (dist * light->fade);
3441 T = (light->photons * linearScale) - (dist * light->fade);
3442 T + (dist * light->fade) = (light->photons * linearScale);
3443 dist * light->fade = (light->photons * linearScale) - T;
3444 dist = ((light->photons * linearScale) - T) / light->fade;
3447 /* solve for inverse square falloff */
3449 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3452 add = light->photons / (dist * dist);
3453 T = light->photons / (dist * dist);
3454 T * (dist * dist) = light->photons;
3455 dist = sqrt( light->photons / T );
3459 /* chop radius against pvs */
3462 ClearBounds( mins, maxs );
3464 /* check all leaves */
3465 for( i = 0; i < numBSPLeafs; i++ )
3468 leaf = &bspLeafs[ i ];
3471 if( leaf->cluster < 0 )
3473 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3476 /* add this leafs bbox to the bounds */
3477 VectorCopy( leaf->mins, origin );
3478 AddPointToBounds( origin, mins, maxs );
3479 VectorCopy( leaf->maxs, origin );
3480 AddPointToBounds( origin, mins, maxs );
3483 /* test to see if bounds encompass light */
3484 for( i = 0; i < 3; i++ )
3486 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3488 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3489 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3490 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3491 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3492 AddPointToBounds( light->origin, mins, maxs );
3496 /* chop the bounds by a plane for area lights and spotlights */
3497 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3498 ChopBounds( mins, maxs, light->origin, light->normal );
3501 VectorCopy( mins, light->mins );
3502 VectorCopy( maxs, light->maxs );
3504 /* reflect bounds around light origin */
3505 //% VectorMA( light->origin, -1.0f, origin, origin );
3506 VectorScale( light->origin, 2, origin );
3507 VectorSubtract( origin, maxs, origin );
3508 AddPointToBounds( origin, mins, maxs );
3509 //% VectorMA( light->origin, -1.0f, mins, origin );
3510 VectorScale( light->origin, 2, origin );
3511 VectorSubtract( origin, mins, origin );
3512 AddPointToBounds( origin, mins, maxs );
3514 /* calculate spherical bounds */
3515 VectorSubtract( maxs, light->origin, dir );
3516 radius = (float) VectorLength( dir );
3518 /* if this radius is smaller than the envelope, then set the envelope to it */
3519 if( radius < light->envelope )
3521 light->envelope = radius;
3522 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3525 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3528 /* add grid/surface only check */
3531 if( !(light->flags & LIGHT_GRID) )
3532 light->envelope = 0.0f;
3536 if( !(light->flags & LIGHT_SURFACES) )
3537 light->envelope = 0.0f;
3542 if( light->cluster < 0 || light->envelope <= 0.0f )
3545 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3547 /* delete the light */
3549 *owner = light->next;
3550 if( light->w != NULL )
3557 /* square envelope */
3558 light->envelope2 = (light->envelope * light->envelope);
3560 /* increment light count */
3563 /* set next light */
3564 owner = &((**owner).next);
3567 /* bucket sort lights by style */
3568 memset( buckets, 0, sizeof( buckets ) );
3570 for( light = lights; light != NULL; light = light2 )
3572 /* get next light */
3573 light2 = light->next;
3575 /* filter into correct bucket */
3576 light->next = buckets[ light->style ];
3577 buckets[ light->style ] = light;
3579 /* if any styled light is present, automatically set nocollapse */
3580 if( light->style != LS_NORMAL )
3584 /* filter back into light list */
3586 for( i = 255; i >= 0; i-- )
3589 for( light = buckets[ i ]; light != NULL; light = light2 )
3591 light2 = light->next;
3592 light->next = lights;
3597 /* emit some statistics */
3598 Sys_Printf( "%9d total lights\n", numLights );
3599 Sys_Printf( "%9d culled lights\n", numCulledLights );
3605 CreateTraceLightsForBounds()
3606 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3609 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3613 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3614 float radius, dist, length;
3617 /* potential pre-setup */
3618 if( numLights == 0 )
3619 SetupEnvelopes( qfalse, fast );
3622 //% 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 ] );
3624 /* allocate the light list */
3625 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3626 trace->numLights = 0;
3628 /* calculate spherical bounds */
3629 VectorAdd( mins, maxs, origin );
3630 VectorScale( origin, 0.5f, origin );
3631 VectorSubtract( maxs, origin, dir );
3632 radius = (float) VectorLength( dir );
3634 /* get length of normal vector */
3635 if( normal != NULL )
3636 length = VectorLength( normal );
3639 normal = nullVector;
3643 /* test each light and see if it reaches the sphere */
3644 /* note: the attenuation code MUST match LightingAtSample() */
3645 for( light = lights; light; light = light->next )
3647 /* check zero sized envelope */
3648 if( light->envelope <= 0 )
3650 lightsEnvelopeCulled++;
3655 if( !(light->flags & flags) )
3658 /* sunlight skips all this nonsense */
3659 if( light->type != EMIT_SUN )
3665 /* check against pvs cluster */
3666 if( numClusters > 0 && clusters != NULL )
3668 for( i = 0; i < numClusters; i++ )
3670 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3675 if( i == numClusters )
3677 lightsClusterCulled++;
3682 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3683 VectorSubtract( light->origin, origin, dir );
3684 dist = VectorLength( dir );
3685 dist -= light->envelope;
3689 lightsEnvelopeCulled++;
3693 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3696 for( i = 0; i < 3; i++ )
3698 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3703 lightsBoundsCulled++;
3709 /* planar surfaces (except twosided surfaces) have a couple more checks */
3710 if( length > 0.0f && trace->twoSided == qfalse )
3712 /* lights coplanar with a surface won't light it */
3713 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3715 lightsPlaneCulled++;
3719 /* check to see if light is behind the plane */
3720 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3722 lightsPlaneCulled++;
3727 /* add this light */
3728 trace->lights[ trace->numLights++ ] = light;
3731 /* make last night null */
3732 trace->lights[ trace->numLights ] = NULL;
3737 void FreeTraceLights( trace_t *trace )
3739 if( trace->lights != NULL )
3740 free( trace->lights );
3746 CreateTraceLightsForSurface()
3747 creates a list of lights that can potentially affect a drawsurface
3750 void CreateTraceLightsForSurface( int num, trace_t *trace )
3753 vec3_t mins, maxs, normal;
3755 bspDrawSurface_t *ds;
3756 surfaceInfo_t *info;
3763 /* get drawsurface and info */
3764 ds = &bspDrawSurfaces[ num ];
3765 info = &surfaceInfos[ num ];
3767 /* get the mins/maxs for the dsurf */
3768 ClearBounds( mins, maxs );
3769 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3770 for( i = 0; i < ds->numVerts; i++ )
3772 dv = &yDrawVerts[ ds->firstVert + i ];
3773 AddPointToBounds( dv->xyz, mins, maxs );
3774 if( !VectorCompare( dv->normal, normal ) )
3775 VectorClear( normal );
3778 /* create the lights for the bounding box */
3779 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3782 /////////////////////////////////////////////////////////////
3784 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3785 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3786 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3787 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3789 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3790 static int numFloodVectors = 0;
3792 void SetupFloodLight( void )
3795 float angle, elevation, angleStep, elevationStep;
3797 double v1,v2,v3,v4,v5;
3800 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3802 /* calculate angular steps */
3803 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3804 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3808 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3810 /* iterate elevation */
3811 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3813 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3814 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3815 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3820 /* emit some statistics */
3821 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3824 value = ValueForKey( &entities[ 0 ], "_floodlight" );
3826 if( value[ 0 ] != '\0' )
3829 v4=floodlightDistance;
3830 v5=floodlightIntensity;
3832 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3834 floodlightRGB[0]=v1;
3835 floodlightRGB[1]=v2;
3836 floodlightRGB[2]=v3;
3838 if (VectorLength(floodlightRGB)==0)
3840 VectorSet(floodlightRGB,240,240,255);
3846 floodlightDistance=v4;
3847 floodlightIntensity=v5;
3849 floodlighty = qtrue;
3850 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3854 VectorSet(floodlightRGB,240,240,255);
3855 //floodlighty = qtrue;
3856 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3858 VectorNormalize(floodlightRGB,floodlightRGB);
3862 FloodLightForSample()
3863 calculates floodlight value for a given sample
3864 once again, kudos to the dirtmapping coder
3867 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
3873 float gatherLight, outLight;
3874 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
3882 if( trace == NULL || trace->cluster < 0 )
3887 dd = floodLightDistance;
3888 VectorCopy( trace->normal, normal );
3890 /* check if the normal is aligned to the world-up */
3891 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
3893 if( normal[ 2 ] == 1.0f )
3895 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
3896 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
3898 else if( normal[ 2 ] == -1.0f )
3900 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
3901 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
3906 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
3907 CrossProduct( normal, worldUp, myRt );
3908 VectorNormalize( myRt, myRt );
3909 CrossProduct( myRt, normal, myUp );
3910 VectorNormalize( myUp, myUp );
3913 /* vortex: optimise floodLightLowQuality a bit */
3914 if ( floodLightLowQuality == qtrue )
3916 /* iterate through ordered vectors */
3917 for( i = 0; i < numFloodVectors; i++ )
3918 if (rand()%10 != 0 ) continue;
3922 /* iterate through ordered vectors */
3923 for( i = 0; i < numFloodVectors; i++ )
3927 /* transform vector into tangent space */
3928 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
3929 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
3930 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
3933 VectorMA( trace->origin, dd, direction, trace->end );
3935 //VectorMA( trace->origin, 1, direction, trace->origin );
3937 SetupTrace( trace );
3942 if (trace->compileFlags & C_SKY )
3946 else if ( trace->opaque )
3948 VectorSubtract( trace->hit, trace->origin, displacement );
3949 d=VectorLength( displacement );
3951 // d=trace->distance;
3952 //if (d>256) gatherDirt+=1;
3954 if (contribution>1) contribution=1.0f;
3956 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
3959 gatherLight+=contribution;
3964 if( gatherLight <= 0.0f )
3972 outLight=gatherLight;
3973 if( outLight > 1.0f )
3976 /* return to sender */
3981 FloodLightRawLightmap
3982 lighttracer style ambient occlusion light hack.
3983 Kudos to the dirtmapping author for most of this source.
3984 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
3985 VorteX: fixed problems with deluxemapping
3988 // floodlight pass on a lightmap
3989 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
3991 int i, x, y, *cluster;
3992 float *origin, *normal, *floodlight, floodLightAmount;
3993 surfaceInfo_t *info;
3996 // float samples, average, *floodlight2;
3998 memset(&trace,0,sizeof(trace_t));
4001 trace.testOcclusion = qtrue;
4002 trace.forceSunlight = qfalse;
4003 trace.twoSided = qtrue;
4004 trace.recvShadows = lm->recvShadows;
4005 trace.numSurfaces = lm->numLightSurfaces;
4006 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4007 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4008 trace.testAll = qfalse;
4009 trace.distance = 1024;
4011 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4012 //trace.twoSided = qfalse;
4013 for( i = 0; i < trace.numSurfaces; i++ )
4016 info = &surfaceInfos[ trace.surfaces[ i ] ];
4018 /* check twosidedness */
4019 if( info->si->twoSided )
4021 trace.twoSided = qtrue;
4026 /* gather floodlight */
4027 for( y = 0; y < lm->sh; y++ )
4029 for( x = 0; x < lm->sw; x++ )
4032 cluster = SUPER_CLUSTER( x, y );
4033 origin = SUPER_ORIGIN( x, y );
4034 normal = SUPER_NORMAL( x, y );
4035 floodlight = SUPER_FLOODLIGHT( x, y );
4037 /* set default dirt */
4040 /* only look at mapped luxels */
4045 trace.cluster = *cluster;
4046 VectorCopy( origin, trace.origin );
4047 VectorCopy( normal, trace.normal );
4049 /* get floodlight */
4050 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4052 /* add floodlight */
4053 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4054 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4055 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4056 floodlight[3] += floodlightDirectionScale;
4060 /* testing no filtering */
4066 for( y = 0; y < lm->sh; y++ )
4068 for( x = 0; x < lm->sw; x++ )
4071 cluster = SUPER_CLUSTER( x, y );
4072 floodlight = SUPER_FLOODLIGHT(x, y );
4074 /* filter dirt by adjacency to unmapped luxels */
4075 average = *floodlight;
4077 for( sy = (y - 1); sy <= (y + 1); sy++ )
4079 if( sy < 0 || sy >= lm->sh )
4082 for( sx = (x - 1); sx <= (x + 1); sx++ )
4084 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4087 /* get neighboring luxel */
4088 cluster = SUPER_CLUSTER( sx, sy );
4089 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4090 if( *cluster < 0 || *floodlight2 <= 0.0f )
4094 average += *floodlight2;
4099 if( samples <= 0.0f )
4104 if( samples <= 0.0f )
4108 *floodlight = average / samples;
4114 void FloodLightRawLightmap( int rawLightmapNum )
4118 /* bail if this number exceeds the number of raw lightmaps */
4119 if( rawLightmapNum >= numRawLightmaps )
4122 lm = &rawLightmaps[ rawLightmapNum ];
4125 if (floodlighty && floodlightIntensity)
4126 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 0);
4129 if (lm->floodlightIntensity)
4131 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4132 numSurfacesFloodlighten += 1;
4136 void FloodlightRawLightmaps()
4138 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4139 numSurfacesFloodlighten = 0;
4140 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4141 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4145 FloodLightIlluminate()
4146 illuminate floodlight into lightmap luxels
4149 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4151 float *luxel, *floodlight, *deluxel, *normal;
4155 int x, y, lightmapNum;
4157 /* walk lightmaps */
4158 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4161 if( lm->superLuxels[ lightmapNum ] == NULL )
4164 /* apply floodlight to each luxel */
4165 for( y = 0; y < lm->sh; y++ )
4167 for( x = 0; x < lm->sw; x++ )
4169 /* get floodlight */
4170 floodlight = SUPER_FLOODLIGHT( x, y );
4171 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4175 cluster = SUPER_CLUSTER( x, y );
4177 /* only process mapped luxels */
4181 /* get particulars */
4182 luxel = SUPER_LUXEL( lightmapNum, x, y );
4183 deluxel = SUPER_DELUXEL( x, y );
4185 /* add to lightmap */
4186 luxel[0]+=floodlight[0];
4187 luxel[1]+=floodlight[1];
4188 luxel[2]+=floodlight[2];
4190 if (luxel[3]==0) luxel[3]=1;
4192 /* add to deluxemap */
4193 if (deluxemap && floodlight[3] > 0)
4195 normal = SUPER_NORMAL( x, y );
4196 brightness = floodlight[ 0 ] * 0.3f + floodlight[ 1 ] * 0.59f + floodlight[ 2 ] * 0.11f;
4197 brightness *= ( 1.0f / 255.0f ) * floodlight[3];
4198 VectorScale( normal, brightness, lightvector );
4199 VectorAdd( deluxel, lightvector, deluxel );