1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1159 /* get drawverts and map first triangle */
1160 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1161 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1162 MapTriangle( lm, info, dv, mapNonAxial );
1164 /* get drawverts and map second triangle */
1165 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1166 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1167 MapTriangle( lm, info, dv, mapNonAxial );
1183 /* -----------------------------------------------------------------
1184 average and clean up luxel normals
1185 ----------------------------------------------------------------- */
1187 /* walk the luxels */
1188 for( y = 0; y < lm->sh; y++ )
1190 for( x = 0; x < lm->sw; x++ )
1193 luxel = SUPER_LUXEL( 0, x, y );
1194 normal = SUPER_NORMAL( x, y );
1195 cluster = SUPER_CLUSTER( x, y );
1197 /* only look at mapped luxels */
1201 /* the normal data could be the sum of multiple samples */
1202 if( luxel[ 3 ] > 1.0f )
1203 VectorNormalize( normal, normal );
1205 /* mark this luxel as having only one normal */
1210 /* non-planar surfaces stop here */
1211 if( lm->plane == NULL )
1214 /* -----------------------------------------------------------------
1215 map occluded or unuxed luxels
1216 ----------------------------------------------------------------- */
1218 /* walk the luxels */
1219 radius = floor( superSample / 2 );
1220 radius = radius > 0 ? radius : 1.0f;
1222 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1224 for( y = 0; y < lm->sh; y++ )
1226 for( x = 0; x < lm->sw; x++ )
1229 luxel = SUPER_LUXEL( 0, x, y );
1230 normal = SUPER_NORMAL( x, y );
1231 cluster = SUPER_CLUSTER( x, y );
1233 /* only look at unmapped luxels */
1234 if( *cluster != CLUSTER_UNMAPPED )
1237 /* divine a normal and origin from neighboring luxels */
1238 VectorClear( fake.xyz );
1239 VectorClear( fake.normal );
1240 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1241 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1243 for( sy = (y - 1); sy <= (y + 1); sy++ )
1245 if( sy < 0 || sy >= lm->sh )
1248 for( sx = (x - 1); sx <= (x + 1); sx++ )
1250 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1253 /* get neighboring luxel */
1254 luxel = SUPER_LUXEL( 0, sx, sy );
1255 origin = SUPER_ORIGIN( sx, sy );
1256 normal = SUPER_NORMAL( sx, sy );
1257 cluster = SUPER_CLUSTER( sx, sy );
1259 /* only consider luxels mapped in previous passes */
1260 if( *cluster < 0 || luxel[ 0 ] >= pass )
1263 /* add its distinctiveness to our own */
1264 VectorAdd( fake.xyz, origin, fake.xyz );
1265 VectorAdd( fake.normal, normal, fake.normal );
1266 samples += luxel[ 3 ];
1271 if( samples == 0.0f )
1275 VectorDivide( fake.xyz, samples, fake.xyz );
1276 //% VectorDivide( fake.normal, samples, fake.normal );
1277 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1280 /* map the fake vert */
1281 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1286 /* -----------------------------------------------------------------
1287 average and clean up luxel normals
1288 ----------------------------------------------------------------- */
1290 /* walk the luxels */
1291 for( y = 0; y < lm->sh; y++ )
1293 for( x = 0; x < lm->sw; x++ )
1296 luxel = SUPER_LUXEL( 0, x, y );
1297 normal = SUPER_NORMAL( x, y );
1298 cluster = SUPER_CLUSTER( x, y );
1300 /* only look at mapped luxels */
1304 /* the normal data could be the sum of multiple samples */
1305 if( luxel[ 3 ] > 1.0f )
1306 VectorNormalize( normal, normal );
1308 /* mark this luxel as having only one normal */
1316 for( y = 0; y < lm->sh; y++ )
1318 for( x = 0; x < lm->sw; x++ )
1323 cluster = SUPER_CLUSTER( x, y );
1324 origin = SUPER_ORIGIN( x, y );
1325 normal = SUPER_NORMAL( x, y );
1326 luxel = SUPER_LUXEL( x, y );
1331 /* check if within the bounding boxes of all surfaces referenced */
1332 ClearBounds( mins, maxs );
1333 for( n = 0; n < lm->numLightSurfaces; n++ )
1336 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1337 TOL = info->sampleSize + 2;
1338 AddPointToBounds( info->mins, mins, maxs );
1339 AddPointToBounds( info->maxs, mins, maxs );
1340 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1341 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1342 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1347 if( n < lm->numLightSurfaces )
1350 /* report bogus origin */
1351 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1352 rawLightmapNum, x, y, *cluster,
1353 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1354 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1355 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1366 sets up dirtmap (ambient occlusion)
1369 #define DIRT_CONE_ANGLE 88 /* degrees */
1370 #define DIRT_NUM_ANGLE_STEPS 16
1371 #define DIRT_NUM_ELEVATION_STEPS 3
1372 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1374 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1375 static int numDirtVectors = 0;
1377 void SetupDirt( void )
1380 float angle, elevation, angleStep, elevationStep;
1384 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1386 /* calculate angular steps */
1387 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1388 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1392 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1394 /* iterate elevation */
1395 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1397 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1398 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1399 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1404 /* emit some statistics */
1405 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1411 calculates dirt value for a given sample
1414 float DirtForSample( trace_t *trace )
1417 float gatherDirt, outDirt, angle, elevation, ooDepth;
1418 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1424 if( trace == NULL || trace->cluster < 0 )
1429 ooDepth = 1.0f / dirtDepth;
1430 VectorCopy( trace->normal, normal );
1432 /* check if the normal is aligned to the world-up */
1433 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1435 if( normal[ 2 ] == 1.0f )
1437 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1440 else if( normal[ 2 ] == -1.0f )
1442 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1443 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1448 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1449 CrossProduct( normal, worldUp, myRt );
1450 VectorNormalize( myRt, myRt );
1451 CrossProduct( myRt, normal, myUp );
1452 VectorNormalize( myUp, myUp );
1455 /* 1 = random mode, 0 (well everything else) = non-random mode */
1459 for( i = 0; i < numDirtVectors; i++ )
1461 /* get random vector */
1462 angle = Random() * DEG2RAD( 360.0f );
1463 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1464 temp[ 0 ] = cos( angle ) * sin( elevation );
1465 temp[ 1 ] = sin( angle ) * sin( elevation );
1466 temp[ 2 ] = cos( elevation );
1468 /* transform into tangent space */
1469 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1470 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1471 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1474 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1475 SetupTrace( trace );
1479 if( trace->opaque && !(trace->compileFlags & C_SKY) )
1481 VectorSubtract( trace->hit, trace->origin, displacement );
1482 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1488 /* iterate through ordered vectors */
1489 for( i = 0; i < numDirtVectors; i++ )
1491 /* transform vector into tangent space */
1492 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1493 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1494 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1497 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1498 SetupTrace( trace );
1504 VectorSubtract( trace->hit, trace->origin, displacement );
1505 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1511 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1512 SetupTrace( trace );
1518 VectorSubtract( trace->hit, trace->origin, displacement );
1519 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1523 if( gatherDirt <= 0.0f )
1526 /* apply gain (does this even do much? heh) */
1527 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1528 if( outDirt > 1.0f )
1532 outDirt *= dirtScale;
1533 if( outDirt > 1.0f )
1536 /* return to sender */
1537 return 1.0f - outDirt;
1544 calculates dirty fraction for each luxel
1547 void DirtyRawLightmap( int rawLightmapNum )
1549 int i, x, y, sx, sy, *cluster;
1550 float *origin, *normal, *dirt, *dirt2, average, samples;
1552 surfaceInfo_t *info;
1557 /* bail if this number exceeds the number of raw lightmaps */
1558 if( rawLightmapNum >= numRawLightmaps )
1562 lm = &rawLightmaps[ rawLightmapNum ];
1565 trace.testOcclusion = qtrue;
1566 trace.forceSunlight = qfalse;
1567 trace.recvShadows = lm->recvShadows;
1568 trace.numSurfaces = lm->numLightSurfaces;
1569 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1570 trace.inhibitRadius = 0.0f;
1571 trace.testAll = qfalse;
1573 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1574 trace.twoSided = qfalse;
1575 for( i = 0; i < trace.numSurfaces; i++ )
1578 info = &surfaceInfos[ trace.surfaces[ i ] ];
1580 /* check twosidedness */
1581 if( info->si->twoSided )
1583 trace.twoSided = qtrue;
1589 for( i = 0; i < trace.numSurfaces; i++ )
1592 info = &surfaceInfos[ trace.surfaces[ i ] ];
1594 /* check twosidedness */
1595 if( info->si->noDirty )
1603 for( y = 0; y < lm->sh; y++ )
1605 for( x = 0; x < lm->sw; x++ )
1608 cluster = SUPER_CLUSTER( x, y );
1609 origin = SUPER_ORIGIN( x, y );
1610 normal = SUPER_NORMAL( x, y );
1611 dirt = SUPER_DIRT( x, y );
1613 /* set default dirt */
1616 /* only look at mapped luxels */
1620 /* don't apply dirty on this surface */
1628 trace.cluster = *cluster;
1629 VectorCopy( origin, trace.origin );
1630 VectorCopy( normal, trace.normal );
1633 *dirt = DirtForSample( &trace );
1637 /* testing no filtering */
1641 for( y = 0; y < lm->sh; y++ )
1643 for( x = 0; x < lm->sw; x++ )
1646 cluster = SUPER_CLUSTER( x, y );
1647 dirt = SUPER_DIRT( x, y );
1649 /* filter dirt by adjacency to unmapped luxels */
1652 for( sy = (y - 1); sy <= (y + 1); sy++ )
1654 if( sy < 0 || sy >= lm->sh )
1657 for( sx = (x - 1); sx <= (x + 1); sx++ )
1659 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1662 /* get neighboring luxel */
1663 cluster = SUPER_CLUSTER( sx, sy );
1664 dirt2 = SUPER_DIRT( sx, sy );
1665 if( *cluster < 0 || *dirt2 <= 0.0f )
1674 if( samples <= 0.0f )
1679 if( samples <= 0.0f )
1683 *dirt = average / samples;
1692 calculates the pvs cluster, origin, normal of a sub-luxel
1695 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1697 int i, *cluster, *cluster2;
1698 float *origin, *origin2, *normal; //% , *normal2;
1699 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1702 /* calulate x vector */
1703 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1705 cluster = SUPER_CLUSTER( x, y );
1706 origin = SUPER_ORIGIN( x, y );
1707 //% normal = SUPER_NORMAL( x, y );
1708 cluster2 = SUPER_CLUSTER( x + 1, y );
1709 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1710 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1712 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1714 cluster = SUPER_CLUSTER( x - 1, y );
1715 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1716 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1717 cluster2 = SUPER_CLUSTER( x, y );
1718 origin2 = SUPER_ORIGIN( x, y );
1719 //% normal2 = SUPER_NORMAL( x, y );
1723 Error( "Spurious lightmap S vector\n" );
1726 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1727 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1729 /* calulate y vector */
1730 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1732 cluster = SUPER_CLUSTER( x, y );
1733 origin = SUPER_ORIGIN( x, y );
1734 //% normal = SUPER_NORMAL( x, y );
1735 cluster2 = SUPER_CLUSTER( x, y + 1 );
1736 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1737 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1739 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1741 cluster = SUPER_CLUSTER( x, y - 1 );
1742 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1743 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1744 cluster2 = SUPER_CLUSTER( x, y );
1745 origin2 = SUPER_ORIGIN( x, y );
1746 //% normal2 = SUPER_NORMAL( x, y );
1749 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1751 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1752 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1754 /* calculate new origin */
1755 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1756 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1757 for( i = 0; i < 3; i++ )
1758 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1761 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1762 if( *sampleCluster < 0 )
1765 /* calculate new normal */
1766 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1767 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1768 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1770 normal = SUPER_NORMAL( x, y );
1771 VectorCopy( normal, sampleNormal );
1779 SubsampleRawLuxel_r()
1780 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1783 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1785 int b, samples, mapped, lighted;
1788 vec3_t deluxel[ 3 ];
1789 vec3_t origin[ 4 ], normal[ 4 ];
1790 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1791 vec3_t color, direction, total;
1795 if( lightLuxel[ 3 ] >= lightSamples )
1799 VectorClear( total );
1803 /* make 2x2 subsample stamp */
1804 for( b = 0; b < 4; b++ )
1807 VectorCopy( sampleOrigin, origin[ b ] );
1809 /* calculate position */
1810 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1817 /* increment sample count */
1818 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1821 trace->cluster = *cluster;
1822 VectorCopy( origin[ b ], trace->origin );
1823 VectorCopy( normal[ b ], trace->normal );
1827 LightContributionToSample( trace );
1828 if(trace->forceSubsampling > 1.0f)
1830 /* alphashadow: we subsample as deep as we can */
1836 /* add to totals (fixme: make contrast function) */
1837 VectorCopy( trace->color, luxel[ b ] );
1840 VectorCopy( trace->directionContribution, deluxel[ b ] );
1842 VectorAdd( total, trace->color, total );
1843 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1847 /* subsample further? */
1848 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1849 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1850 lighted != 0 && lighted != mapped )
1852 for( b = 0; b < 4; b++ )
1854 if( cluster[ b ] < 0 )
1856 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1861 //% VectorClear( color );
1863 VectorCopy( lightLuxel, color );
1864 VectorCopy( lightDeluxel, direction );
1866 for( b = 0; b < 4; b++ )
1868 if( cluster[ b ] < 0 )
1870 VectorAdd( color, luxel[ b ], color );
1873 VectorAdd( direction, deluxel[ b ], direction );
1882 color[ 0 ] /= samples;
1883 color[ 1 ] /= samples;
1884 color[ 2 ] /= samples;
1887 VectorCopy( color, lightLuxel );
1888 lightLuxel[ 3 ] += 1.0f;
1892 direction[ 0 ] /= samples;
1893 direction[ 1 ] /= samples;
1894 direction[ 2 ] /= samples;
1895 VectorCopy( direction, lightDeluxel );
1900 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1901 static void GaussLikeRandom(float sigma, float *x, float *y)
1904 r = Random() * 2 * Q_PI;
1905 *x = sigma * 2.73861278752581783822 * cos(r);
1906 *y = sigma * 2.73861278752581783822 * sin(r);
1913 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1917 vec3_t origin, normal;
1918 vec3_t total, totaldirection;
1921 VectorClear( total );
1922 VectorClear( totaldirection );
1924 for(b = 0; b < lightSamples; ++b)
1927 VectorCopy( sampleOrigin, origin );
1928 GaussLikeRandom(bias, &dx, &dy);
1930 /* calculate position */
1931 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1938 trace->cluster = cluster;
1939 VectorCopy( origin, trace->origin );
1940 VectorCopy( normal, trace->normal );
1942 LightContributionToSample( trace );
1943 VectorAdd( total, trace->color, total );
1946 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1954 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1955 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1956 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1960 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1961 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1962 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1970 IlluminateRawLightmap()
1971 illuminates the luxels
1974 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1975 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1976 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1978 void IlluminateRawLightmap( int rawLightmapNum )
1980 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1981 int *cluster, *cluster2, mapped, lighted, totalLighted;
1982 size_t llSize, ldSize;
1984 surfaceInfo_t *info;
1985 qboolean filterColor, filterDir;
1987 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1988 unsigned char *flag;
1989 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1990 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1991 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1993 float stackLightLuxels[ STACK_LL_SIZE ];
1996 /* bail if this number exceeds the number of raw lightmaps */
1997 if( rawLightmapNum >= numRawLightmaps )
2001 lm = &rawLightmaps[ rawLightmapNum ];
2004 trace.testOcclusion = !noTrace;
2005 trace.forceSunlight = qfalse;
2006 trace.recvShadows = lm->recvShadows;
2007 trace.numSurfaces = lm->numLightSurfaces;
2008 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2009 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2011 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2012 trace.twoSided = qfalse;
2013 for( i = 0; i < trace.numSurfaces; i++ )
2016 info = &surfaceInfos[ trace.surfaces[ i ] ];
2018 /* check twosidedness */
2019 if( info->si->twoSided )
2021 trace.twoSided = qtrue;
2026 /* create a culled light list for this raw lightmap */
2027 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2029 /* -----------------------------------------------------------------
2031 ----------------------------------------------------------------- */
2034 numLuxelsIlluminated += (lm->sw * lm->sh);
2036 /* test debugging state */
2037 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2039 /* debug fill the luxels */
2040 for( y = 0; y < lm->sh; y++ )
2042 for( x = 0; x < lm->sw; x++ )
2045 cluster = SUPER_CLUSTER( x, y );
2047 /* only fill mapped luxels */
2051 /* get particulars */
2052 luxel = SUPER_LUXEL( 0, x, y );
2053 origin = SUPER_ORIGIN( x, y );
2054 normal = SUPER_NORMAL( x, y );
2056 /* color the luxel with raw lightmap num? */
2058 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2060 /* color the luxel with lightmap axis? */
2061 else if( debugAxis )
2063 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2064 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2065 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2068 /* color the luxel with luxel cluster? */
2069 else if( debugCluster )
2070 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2072 /* color the luxel with luxel origin? */
2073 else if( debugOrigin )
2075 VectorSubtract( lm->maxs, lm->mins, temp );
2076 VectorScale( temp, (1.0f / 255.0f), temp );
2077 VectorSubtract( origin, lm->mins, temp2 );
2078 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2079 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2080 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2083 /* color the luxel with the normal */
2084 else if( normalmap )
2086 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2087 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2088 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2091 /* otherwise clear it */
2093 VectorClear( luxel );
2102 /* allocate temporary per-light luxel storage */
2103 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2104 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2105 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2106 lightLuxels = stackLightLuxels;
2108 lightLuxels = safe_malloc( llSize );
2110 lightDeluxels = safe_malloc( ldSize );
2112 lightDeluxels = NULL;
2115 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2117 /* set ambient color */
2118 for( y = 0; y < lm->sh; y++ )
2120 for( x = 0; x < lm->sw; x++ )
2123 cluster = SUPER_CLUSTER( x, y );
2124 luxel = SUPER_LUXEL( 0, x, y );
2125 normal = SUPER_NORMAL( x, y );
2126 deluxel = SUPER_DELUXEL( x, y );
2128 /* blacken unmapped clusters */
2130 VectorClear( luxel );
2135 VectorCopy( ambientColor, luxel );
2138 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2140 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2141 if(brightness < 0.00390625f)
2142 brightness = 0.00390625f;
2144 VectorScale( normal, brightness, deluxel );
2151 /* clear styled lightmaps */
2152 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2153 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2155 if( lm->superLuxels[ lightmapNum ] != NULL )
2156 memset( lm->superLuxels[ lightmapNum ], 0, size );
2159 /* debugging code */
2160 //% if( trace.numLights <= 0 )
2161 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2163 /* walk light list */
2164 for( i = 0; i < trace.numLights; i++ )
2167 trace.light = trace.lights[ i ];
2170 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2172 if( lm->styles[ lightmapNum ] == trace.light->style ||
2173 lm->styles[ lightmapNum ] == LS_NONE )
2177 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2178 if( lightmapNum >= MAX_LIGHTMAPS )
2180 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2185 memset( lightLuxels, 0, llSize );
2187 memset( lightDeluxels, 0, ldSize );
2190 /* determine filter radius */
2191 filterRadius = lm->filterRadius > trace.light->filterRadius
2193 : trace.light->filterRadius;
2194 if( filterRadius < 0.0f )
2195 filterRadius = 0.0f;
2197 /* set luxel filter radius */
2198 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2199 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2200 luxelFilterRadius = 1;
2202 /* allocate sampling flags storage */
2203 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2205 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2206 if(lm->superFlags == NULL)
2207 lm->superFlags = safe_malloc( size );
2208 memset( (void *) lm->superFlags, 0, size );
2211 /* initial pass, one sample per luxel */
2212 for( y = 0; y < lm->sh; y++ )
2214 for( x = 0; x < lm->sw; x++ )
2217 cluster = SUPER_CLUSTER( x, y );
2221 /* get particulars */
2222 lightLuxel = LIGHT_LUXEL( x, y );
2223 lightDeluxel = LIGHT_DELUXEL( x, y );
2224 origin = SUPER_ORIGIN( x, y );
2225 normal = SUPER_NORMAL( x, y );
2226 flag = SUPER_FLAG( x, y );
2229 ////////// 27's temp hack for testing edge clipping ////
2230 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2232 lightLuxel[ 1 ] = 255;
2233 lightLuxel[ 3 ] = 1.0f;
2239 /* set contribution count */
2240 lightLuxel[ 3 ] = 1.0f;
2243 trace.cluster = *cluster;
2244 VectorCopy( origin, trace.origin );
2245 VectorCopy( normal, trace.normal );
2247 /* get light for this sample */
2248 LightContributionToSample( &trace );
2249 VectorCopy( trace.color, lightLuxel );
2251 /* add the contribution to the deluxemap */
2254 VectorCopy( trace.directionContribution, lightDeluxel );
2257 /* check for evilness */
2258 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2261 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2264 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2270 /* don't even bother with everything else if nothing was lit */
2271 if( totalLighted == 0 )
2274 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2275 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2276 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2279 for( y = 0; y < (lm->sh - 1); y++ )
2281 for( x = 0; x < (lm->sw - 1); x++ )
2286 VectorClear( total );
2288 /* test 2x2 stamp */
2289 for( t = 0; t < 4; t++ )
2291 /* set sample coords */
2292 sx = x + tests[ t ][ 0 ];
2293 sy = y + tests[ t ][ 1 ];
2296 cluster = SUPER_CLUSTER( sx, sy );
2302 flag = SUPER_FLAG( sx, sy );
2303 if(*flag & FLAG_FORCE_SUBSAMPLING)
2305 /* force a lighted/mapped discrepancy so we subsample */
2310 lightLuxel = LIGHT_LUXEL( sx, sy );
2311 VectorAdd( total, lightLuxel, total );
2312 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2316 /* if total color is under a certain amount, then don't bother subsampling */
2317 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2320 /* if all 4 pixels are either in shadow or light, then don't subsample */
2321 if( lighted != 0 && lighted != mapped )
2323 for( t = 0; t < 4; t++ )
2325 /* set sample coords */
2326 sx = x + tests[ t ][ 0 ];
2327 sy = y + tests[ t ][ 1 ];
2330 cluster = SUPER_CLUSTER( sx, sy );
2333 flag = SUPER_FLAG( sx, sy );
2334 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2336 lightLuxel = LIGHT_LUXEL( sx, sy );
2337 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2338 origin = SUPER_ORIGIN( sx, sy );
2340 /* only subsample shadowed luxels */
2341 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2345 if(lightRandomSamples)
2346 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2348 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2350 *flag |= FLAG_ALREADY_SUBSAMPLED;
2352 /* debug code to colorize subsampled areas to yellow */
2353 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2354 //% VectorSet( luxel, 255, 204, 0 );
2361 /* tertiary pass, apply dirt map (ambient occlusion) */
2365 for( y = 0; y < lm->sh; y++ )
2367 for( x = 0; x < lm->sw; x++ )
2370 cluster = SUPER_CLUSTER( x, y );
2374 /* get particulars */
2375 lightLuxel = LIGHT_LUXEL( x, y );
2376 dirt = SUPER_DIRT( x, y );
2378 /* scale light value */
2379 VectorScale( lightLuxel, *dirt, lightLuxel );
2384 /* allocate sampling lightmap storage */
2385 if( lm->superLuxels[ lightmapNum ] == NULL )
2387 /* allocate sampling lightmap storage */
2388 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2389 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2390 memset( lm->superLuxels[ lightmapNum ], 0, size );
2394 if( lightmapNum > 0 )
2396 lm->styles[ lightmapNum ] = trace.light->style;
2397 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2400 /* copy to permanent luxels */
2401 for( y = 0; y < lm->sh; y++ )
2403 for( x = 0; x < lm->sw; x++ )
2405 /* get cluster and origin */
2406 cluster = SUPER_CLUSTER( x, y );
2409 origin = SUPER_ORIGIN( x, y );
2412 if( luxelFilterRadius )
2415 VectorClear( averageColor );
2416 VectorClear( averageDir );
2419 /* cheaper distance-based filtering */
2420 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2422 if( sy < 0 || sy >= lm->sh )
2425 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2427 if( sx < 0 || sx >= lm->sw )
2430 /* get particulars */
2431 cluster = SUPER_CLUSTER( sx, sy );
2434 lightLuxel = LIGHT_LUXEL( sx, sy );
2435 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2438 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2439 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2441 /* scale luxel by filter weight */
2442 VectorScale( lightLuxel, weight, color );
2443 VectorAdd( averageColor, color, averageColor );
2446 VectorScale( lightDeluxel, weight, direction );
2447 VectorAdd( averageDir, direction, averageDir );
2454 if( samples <= 0.0f )
2457 /* scale into luxel */
2458 luxel = SUPER_LUXEL( lightmapNum, x, y );
2461 /* handle negative light */
2462 if( trace.light->flags & LIGHT_NEGATIVE )
2464 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2465 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2466 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2469 /* handle normal light */
2472 luxel[ 0 ] += averageColor[ 0 ] / samples;
2473 luxel[ 1 ] += averageColor[ 1 ] / samples;
2474 luxel[ 2 ] += averageColor[ 2 ] / samples;
2479 /* scale into luxel */
2480 deluxel = SUPER_DELUXEL( x, y );
2481 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2482 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2483 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2490 /* get particulars */
2491 lightLuxel = LIGHT_LUXEL( x, y );
2492 lightDeluxel = LIGHT_DELUXEL( x, y );
2493 luxel = SUPER_LUXEL( lightmapNum, x, y );
2494 deluxel = SUPER_DELUXEL( x, y );
2496 /* handle negative light */
2497 if( trace.light->flags & LIGHT_NEGATIVE )
2498 VectorScale( averageColor, -1.0f, averageColor );
2503 /* handle negative light */
2504 if( trace.light->flags & LIGHT_NEGATIVE )
2505 VectorSubtract( luxel, lightLuxel, luxel );
2507 /* handle normal light */
2509 VectorAdd( luxel, lightLuxel, luxel );
2513 VectorAdd( deluxel, lightDeluxel, deluxel );
2520 /* free temporary luxels */
2521 if( lightLuxels != stackLightLuxels )
2522 free( lightLuxels );
2525 free( lightDeluxels );
2528 /* free light list */
2529 FreeTraceLights( &trace );
2531 /* floodlight pass */
2533 FloodlightIlluminateLightmap(lm);
2537 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2540 if( lm->superLuxels[ lightmapNum ] == NULL )
2543 for( y = 0; y < lm->sh; y++ )
2545 for( x = 0; x < lm->sw; x++ )
2548 cluster = SUPER_CLUSTER( x, y );
2549 //% if( *cluster < 0 )
2552 /* get particulars */
2553 luxel = SUPER_LUXEL( lightmapNum, x, y );
2554 normal = SUPER_NORMAL ( x, y );
2556 luxel[0]=(normal[0]*127)+127;
2557 luxel[1]=(normal[1]*127)+127;
2558 luxel[2]=(normal[2]*127)+127;
2564 /* -----------------------------------------------------------------
2566 ----------------------------------------------------------------- */
2570 /* walk lightmaps */
2571 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2574 if( lm->superLuxels[ lightmapNum ] == NULL )
2577 /* apply dirt to each luxel */
2578 for( y = 0; y < lm->sh; y++ )
2580 for( x = 0; x < lm->sw; x++ )
2583 cluster = SUPER_CLUSTER( x, y );
2584 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2587 /* get particulars */
2588 luxel = SUPER_LUXEL( lightmapNum, x, y );
2589 dirt = SUPER_DIRT( x, y );
2592 VectorScale( luxel, *dirt, luxel );
2596 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2602 /* -----------------------------------------------------------------
2604 ----------------------------------------------------------------- */
2606 /* walk lightmaps */
2607 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2610 if( lm->superLuxels[ lightmapNum ] == NULL )
2613 /* average occluded luxels from neighbors */
2614 for( y = 0; y < lm->sh; y++ )
2616 for( x = 0; x < lm->sw; x++ )
2618 /* get particulars */
2619 cluster = SUPER_CLUSTER( x, y );
2620 luxel = SUPER_LUXEL( lightmapNum, x, y );
2621 deluxel = SUPER_DELUXEL( x, y );
2622 normal = SUPER_NORMAL( x, y );
2624 /* determine if filtering is necessary */
2625 filterColor = qfalse;
2628 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2629 filterColor = qtrue;
2631 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2634 if( !filterColor && !filterDir )
2637 /* choose seed amount */
2638 VectorClear( averageColor );
2639 VectorClear( averageDir );
2642 /* walk 3x3 matrix */
2643 for( sy = (y - 1); sy <= (y + 1); sy++ )
2645 if( sy < 0 || sy >= lm->sh )
2648 for( sx = (x - 1); sx <= (x + 1); sx++ )
2650 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2653 /* get neighbor's particulars */
2654 cluster2 = SUPER_CLUSTER( sx, sy );
2655 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2656 deluxel2 = SUPER_DELUXEL( sx, sy );
2658 /* ignore unmapped/unlit luxels */
2659 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2660 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2663 /* add its distinctiveness to our own */
2664 VectorAdd( averageColor, luxel2, averageColor );
2665 samples += luxel2[ 3 ];
2667 VectorAdd( averageDir, deluxel2, averageDir );
2672 if( samples <= 0.0f )
2675 /* dark lightmap seams */
2678 if( lightmapNum == 0 )
2679 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2686 VectorDivide( averageColor, samples, luxel );
2690 VectorDivide( averageDir, samples, deluxel );
2692 /* set cluster to -3 */
2694 *cluster = CLUSTER_FLOODED;
2702 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2705 if( lm->superLuxels[ lightmapNum ] == NULL )
2707 for( y = 0; y < lm->sh; y++ )
2708 for( x = 0; x < lm->sw; x++ )
2711 cluster = SUPER_CLUSTER( x, y );
2712 luxel = SUPER_LUXEL( lightmapNum, x, y );
2713 deluxel = SUPER_DELUXEL( x, y );
2714 if(!luxel || !deluxel || !cluster)
2716 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2719 else if(*cluster < 0)
2722 // should have neither deluxemap nor lightmap
2724 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2729 // should have both deluxemap and lightmap
2731 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2741 IlluminateVertexes()
2742 light the surface vertexes
2745 #define VERTEX_NUDGE 4.0f
2747 void IlluminateVertexes( int num )
2749 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2750 int lightmapNum, numAvg;
2751 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2752 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2753 bspDrawSurface_t *ds;
2754 surfaceInfo_t *info;
2756 bspDrawVert_t *verts;
2758 float floodLightAmount;
2762 /* get surface, info, and raw lightmap */
2763 ds = &bspDrawSurfaces[ num ];
2764 info = &surfaceInfos[ num ];
2767 /* -----------------------------------------------------------------
2768 illuminate the vertexes
2769 ----------------------------------------------------------------- */
2771 /* calculate vertex lighting for surfaces without lightmaps */
2772 if( lm == NULL || cpmaHack )
2775 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2776 trace.forceSunlight = info->si->forceSunlight;
2777 trace.recvShadows = info->recvShadows;
2778 trace.numSurfaces = 1;
2779 trace.surfaces = #
2780 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2782 /* twosided lighting */
2783 trace.twoSided = info->si->twoSided;
2785 /* make light list for this surface */
2786 CreateTraceLightsForSurface( num, &trace );
2789 verts = yDrawVerts + ds->firstVert;
2791 memset( avgColors, 0, sizeof( avgColors ) );
2793 /* walk the surface verts */
2794 for( i = 0; i < ds->numVerts; i++ )
2796 /* get vertex luxel */
2797 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2799 /* color the luxel with raw lightmap num? */
2801 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2803 /* color the luxel with luxel origin? */
2804 else if( debugOrigin )
2806 VectorSubtract( info->maxs, info->mins, temp );
2807 VectorScale( temp, (1.0f / 255.0f), temp );
2808 VectorSubtract( origin, lm->mins, temp2 );
2809 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2810 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2811 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2814 /* color the luxel with the normal */
2815 else if( normalmap )
2817 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2818 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2819 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2822 /* illuminate the vertex */
2825 /* clear vertex luxel */
2826 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2828 /* try at initial origin */
2829 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2830 if( trace.cluster >= 0 )
2833 VectorCopy( verts[ i ].xyz, trace.origin );
2834 VectorCopy( verts[ i ].normal, trace.normal );
2837 if( dirty && !bouncing )
2838 dirt = DirtForSample( &trace );
2842 /* jal: floodlight */
2843 floodLightAmount = 0.0f;
2844 VectorClear( floodColor );
2845 if( floodlighty && !bouncing )
2847 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2848 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2852 LightingAtSample( &trace, ds->vertexStyles, colors );
2855 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2858 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2860 /* jal: floodlight */
2861 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2864 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2865 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2866 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2870 /* is this sample bright enough? */
2871 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2872 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2873 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2874 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2876 /* nudge the sample point around a bit */
2877 for( x = 0; x < 5; x++ )
2879 /* two's complement 0, 1, -1, 2, -2, etc */
2880 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2882 for( y = 0; y < 5; y++ )
2884 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2886 for( z = 0; z < 5; z++ )
2888 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2891 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2892 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2893 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2895 /* try at nudged origin */
2896 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2897 if( trace.cluster < 0 )
2901 if( dirty && !bouncing )
2902 dirt = DirtForSample( &trace );
2906 /* jal: floodlight */
2907 floodLightAmount = 0.0f;
2908 VectorClear( floodColor );
2909 if( floodlighty && !bouncing )
2911 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2912 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2916 LightingAtSample( &trace, ds->vertexStyles, colors );
2919 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2922 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2924 /* jal: floodlight */
2925 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2928 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2929 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2932 /* bright enough? */
2933 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2934 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2935 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2936 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2943 /* add to average? */
2944 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2945 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2946 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2947 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2950 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2952 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2953 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2958 /* another happy customer */
2959 numVertsIlluminated++;
2962 /* set average color */
2965 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2966 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2970 VectorCopy( ambientColor, avgColors[ 0 ] );
2973 /* clean up and store vertex color */
2974 for( i = 0; i < ds->numVerts; i++ )
2976 /* get vertex luxel */
2977 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2979 /* store average in occluded vertexes */
2980 if( radVertLuxel[ 0 ] < 0.0f )
2982 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2984 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2985 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2988 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2993 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2996 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2997 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3000 if( bouncing || bounce == 0 || !bounceOnly )
3001 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3002 if( !info->si->noVertexLight )
3003 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3007 /* free light list */
3008 FreeTraceLights( &trace );
3010 /* return to sender */
3014 /* -----------------------------------------------------------------
3015 reconstitute vertex lighting from the luxels
3016 ----------------------------------------------------------------- */
3018 /* set styles from lightmap */
3019 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3020 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3022 /* get max search radius */
3024 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3026 /* walk the surface verts */
3027 verts = yDrawVerts + ds->firstVert;
3028 for( i = 0; i < ds->numVerts; i++ )
3030 /* do each lightmap */
3031 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3034 if( lm->superLuxels[ lightmapNum ] == NULL )
3037 /* get luxel coords */
3038 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3039 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3042 else if( x >= lm->sw )
3046 else if( y >= lm->sh )
3049 /* get vertex luxels */
3050 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3051 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3053 /* color the luxel with the normal? */
3056 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3057 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3058 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3061 /* color the luxel with surface num? */
3062 else if( debugSurfaces )
3063 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3065 /* divine color from the superluxels */
3068 /* increasing radius */
3069 VectorClear( radVertLuxel );
3071 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3073 /* sample within radius */
3074 for( sy = (y - radius); sy <= (y + radius); sy++ )
3076 if( sy < 0 || sy >= lm->sh )
3079 for( sx = (x - radius); sx <= (x + radius); sx++ )
3081 if( sx < 0 || sx >= lm->sw )
3084 /* get luxel particulars */
3085 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3086 cluster = SUPER_CLUSTER( sx, sy );
3090 /* testing: must be brigher than ambient color */
3091 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3094 /* add its distinctiveness to our own */
3095 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3096 samples += luxel[ 3 ];
3102 if( samples > 0.0f )
3103 VectorDivide( radVertLuxel, samples, radVertLuxel );
3105 VectorCopy( ambientColor, radVertLuxel );
3108 /* store into floating point storage */
3109 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3110 numVertsIlluminated++;
3112 /* store into bytes (for vertex approximation) */
3113 if( !info->si->noVertexLight )
3114 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3121 /* -------------------------------------------------------------------------------
3123 light optimization (-fast)
3125 creates a list of lights that will affect a surface and stores it in tw
3126 this is to optimize surface lighting by culling out as many of the
3127 lights in the world as possible from further calculation
3129 ------------------------------------------------------------------------------- */
3133 determines opaque brushes in the world and find sky shaders for sunlight calculations
3136 void SetupBrushes( void )
3138 int i, j, b, compileFlags;
3141 bspBrushSide_t *side;
3142 bspShader_t *shader;
3147 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3150 if( opaqueBrushes == NULL )
3151 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3154 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3155 numOpaqueBrushes = 0;
3157 /* walk the list of worldspawn brushes */
3158 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3161 b = bspModels[ 0 ].firstBSPBrush + i;
3162 brush = &bspBrushes[ b ];
3164 /* check all sides */
3167 for( j = 0; j < brush->numSides && inside; j++ )
3169 /* do bsp shader calculations */
3170 side = &bspBrushSides[ brush->firstSide + j ];
3171 shader = &bspShaders[ side->shaderNum ];
3173 /* get shader info */
3174 si = ShaderInfoForShader( shader->shader );
3178 /* or together compile flags */
3179 compileFlags |= si->compileFlags;
3182 /* determine if this brush is opaque to light */
3183 if( !(compileFlags & C_TRANSLUCENT) )
3185 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3191 /* emit some statistics */
3192 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3199 determines if two clusters are visible to each other using the PVS
3202 qboolean ClusterVisible( int a, int b )
3204 int portalClusters, leafBytes;
3209 if( a < 0 || b < 0 )
3217 if( numBSPVisBytes <=8 )
3221 portalClusters = ((int *) bspVisBytes)[ 0 ];
3222 leafBytes = ((int*) bspVisBytes)[ 1 ];
3223 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3226 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3235 borrowed from vlight.c
3238 int PointInLeafNum_r( vec3_t point, int nodenum )
3246 while( nodenum >= 0 )
3248 node = &bspNodes[ nodenum ];
3249 plane = &bspPlanes[ node->planeNum ];
3250 dist = DotProduct( point, plane->normal ) - plane->dist;
3252 nodenum = node->children[ 0 ];
3253 else if( dist < -0.1 )
3254 nodenum = node->children[ 1 ];
3257 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3258 if( bspLeafs[ leafnum ].cluster != -1 )
3260 nodenum = node->children[ 1 ];
3264 leafnum = -nodenum - 1;
3272 borrowed from vlight.c
3275 int PointInLeafNum( vec3_t point )
3277 return PointInLeafNum_r( point, 0 );
3283 ClusterVisibleToPoint() - ydnar
3284 returns qtrue if point can "see" cluster
3287 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3292 /* get leafNum for point */
3293 pointCluster = ClusterForPoint( point );
3294 if( pointCluster < 0 )
3298 return ClusterVisible( pointCluster, cluster );
3304 ClusterForPoint() - ydnar
3305 returns the pvs cluster for point
3308 int ClusterForPoint( vec3_t point )
3313 /* get leafNum for point */
3314 leafNum = PointInLeafNum( point );
3318 /* return the cluster */
3319 return bspLeafs[ leafNum ].cluster;
3325 ClusterForPointExt() - ydnar
3326 also takes brushes into account for occlusion testing
3329 int ClusterForPointExt( vec3_t point, float epsilon )
3331 int i, j, b, leafNum, cluster;
3334 int *brushes, numBSPBrushes;
3340 /* get leaf for point */
3341 leafNum = PointInLeafNum( point );
3344 leaf = &bspLeafs[ leafNum ];
3346 /* get the cluster */
3347 cluster = leaf->cluster;
3351 /* transparent leaf, so check point against all brushes in the leaf */
3352 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3353 numBSPBrushes = leaf->numBSPLeafBrushes;
3354 for( i = 0; i < numBSPBrushes; i++ )
3358 if( b > maxOpaqueBrush )
3360 brush = &bspBrushes[ b ];
3361 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3364 /* check point against all planes */
3366 for( j = 0; j < brush->numSides && inside; j++ )
3368 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3369 dot = DotProduct( point, plane->normal );
3375 /* if inside, return bogus cluster */
3380 /* if the point made it this far, it's not inside any opaque brushes */
3387 ClusterForPointExtFilter() - ydnar
3388 adds cluster checking against a list of known valid clusters
3391 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3396 /* get cluster for point */
3397 cluster = ClusterForPointExt( point, epsilon );
3399 /* check if filtering is necessary */
3400 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3404 for( i = 0; i < numClusters; i++ )
3406 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3417 ShaderForPointInLeaf() - ydnar
3418 checks a point against all brushes in a leaf, returning the shader of the brush
3419 also sets the cumulative surface and content flags for the brush hit
3422 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3427 int *brushes, numBSPBrushes;
3430 bspBrushSide_t *side;
3432 bspShader_t *shader;
3433 int allSurfaceFlags, allContentFlags;
3436 /* clear things out first */
3443 leaf = &bspLeafs[ leafNum ];
3445 /* transparent leaf, so check point against all brushes in the leaf */
3446 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3447 numBSPBrushes = leaf->numBSPLeafBrushes;
3448 for( i = 0; i < numBSPBrushes; i++ )
3451 brush = &bspBrushes[ brushes[ i ] ];
3453 /* check point against all planes */
3455 allSurfaceFlags = 0;
3456 allContentFlags = 0;
3457 for( j = 0; j < brush->numSides && inside; j++ )
3459 side = &bspBrushSides[ brush->firstSide + j ];
3460 plane = &bspPlanes[ side->planeNum ];
3461 dot = DotProduct( point, plane->normal );
3467 shader = &bspShaders[ side->shaderNum ];
3468 allSurfaceFlags |= shader->surfaceFlags;
3469 allContentFlags |= shader->contentFlags;
3473 /* handle if inside */
3476 /* if there are desired flags, check for same and continue if they aren't matched */
3477 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3479 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3482 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3483 *surfaceFlags = allSurfaceFlags;
3484 *contentFlags = allContentFlags;
3485 return brush->shaderNum;
3489 /* if the point made it this far, it's not inside any brushes */
3497 chops a bounding box by the plane defined by origin and normal
3498 returns qfalse if the bounds is entirely clipped away
3500 this is not exactly the fastest way to do this...
3503 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3505 /* FIXME: rewrite this so it doesn't use bloody brushes */
3513 calculates each light's effective envelope,
3514 taking into account brightness, type, and pvs.
3517 #define LIGHT_EPSILON 0.125f
3518 #define LIGHT_NUDGE 2.0f
3520 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3522 int i, x, y, z, x1, y1, z1;
3523 light_t *light, *light2, **owner;
3525 vec3_t origin, dir, mins, maxs;
3526 float radius, intensity;
3527 light_t *buckets[ 256 ];
3530 /* early out for weird cases where there are no lights */
3531 if( lights == NULL )
3535 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3539 numCulledLights = 0;
3541 while( *owner != NULL )
3546 /* handle negative lights */
3547 if( light->photons < 0.0f || light->add < 0.0f )
3549 light->photons *= -1.0f;
3550 light->add *= -1.0f;
3551 light->flags |= LIGHT_NEGATIVE;
3555 if( light->type == EMIT_SUN )
3559 light->envelope = MAX_WORLD_COORD * 8.0f;
3560 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3561 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3564 /* everything else */
3567 /* get pvs cluster for light */
3568 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3570 /* invalid cluster? */
3571 if( light->cluster < 0 )
3573 /* nudge the sample point around a bit */
3574 for( x = 0; x < 4; x++ )
3576 /* two's complement 0, 1, -1, 2, -2, etc */
3577 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3579 for( y = 0; y < 4; y++ )
3581 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3583 for( z = 0; z < 4; z++ )
3585 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3588 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3589 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3590 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3592 /* try at nudged origin */
3593 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3594 if( light->cluster < 0 )
3598 VectorCopy( origin, light->origin );
3604 /* only calculate for lights in pvs and outside of opaque brushes */
3605 if( light->cluster >= 0 )
3607 /* set light fast flag */
3609 light->flags |= LIGHT_FAST_TEMP;
3611 light->flags &= ~LIGHT_FAST_TEMP;
3612 if( light->si && light->si->noFast )
3613 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3615 /* clear light envelope */
3616 light->envelope = 0;
3618 /* handle area lights */
3619 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3621 /* ugly hack to calculate extent for area lights, but only done once */
3622 VectorScale( light->normal, -1.0f, dir );
3623 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3627 VectorMA( light->origin, radius, light->normal, origin );
3628 factor = PointToPolygonFormFactor( origin, dir, light->w );
3631 if( (factor * light->add) <= light->falloffTolerance )
3632 light->envelope = radius;
3635 /* check for fast mode */
3636 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3637 light->envelope = MAX_WORLD_COORD * 8.0f;
3638 intensity = light->photons; /* hopefully not used */
3643 intensity = light->photons;
3647 if( light->envelope <= 0.0f )
3649 /* solve distance for non-distance lights */
3650 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3651 light->envelope = MAX_WORLD_COORD * 8.0f;
3653 /* solve distance for linear lights */
3654 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3655 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3656 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3659 add = angle * light->photons * linearScale - (dist * light->fade);
3660 T = (light->photons * linearScale) - (dist * light->fade);
3661 T + (dist * light->fade) = (light->photons * linearScale);
3662 dist * light->fade = (light->photons * linearScale) - T;
3663 dist = ((light->photons * linearScale) - T) / light->fade;
3666 /* solve for inverse square falloff */
3668 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3671 add = light->photons / (dist * dist);
3672 T = light->photons / (dist * dist);
3673 T * (dist * dist) = light->photons;
3674 dist = sqrt( light->photons / T );
3678 /* chop radius against pvs */
3681 ClearBounds( mins, maxs );
3683 /* check all leaves */
3684 for( i = 0; i < numBSPLeafs; i++ )
3687 leaf = &bspLeafs[ i ];
3690 if( leaf->cluster < 0 )
3692 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3695 /* add this leafs bbox to the bounds */
3696 VectorCopy( leaf->mins, origin );
3697 AddPointToBounds( origin, mins, maxs );
3698 VectorCopy( leaf->maxs, origin );
3699 AddPointToBounds( origin, mins, maxs );
3702 /* test to see if bounds encompass light */
3703 for( i = 0; i < 3; i++ )
3705 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3707 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3708 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3709 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3710 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3711 AddPointToBounds( light->origin, mins, maxs );
3715 /* chop the bounds by a plane for area lights and spotlights */
3716 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3717 ChopBounds( mins, maxs, light->origin, light->normal );
3720 VectorCopy( mins, light->mins );
3721 VectorCopy( maxs, light->maxs );
3723 /* reflect bounds around light origin */
3724 //% VectorMA( light->origin, -1.0f, origin, origin );
3725 VectorScale( light->origin, 2, origin );
3726 VectorSubtract( origin, maxs, origin );
3727 AddPointToBounds( origin, mins, maxs );
3728 //% VectorMA( light->origin, -1.0f, mins, origin );
3729 VectorScale( light->origin, 2, origin );
3730 VectorSubtract( origin, mins, origin );
3731 AddPointToBounds( origin, mins, maxs );
3733 /* calculate spherical bounds */
3734 VectorSubtract( maxs, light->origin, dir );
3735 radius = (float) VectorLength( dir );
3737 /* if this radius is smaller than the envelope, then set the envelope to it */
3738 if( radius < light->envelope )
3740 light->envelope = radius;
3741 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3744 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3747 /* add grid/surface only check */
3750 if( !(light->flags & LIGHT_GRID) )
3751 light->envelope = 0.0f;
3755 if( !(light->flags & LIGHT_SURFACES) )
3756 light->envelope = 0.0f;
3761 if( light->cluster < 0 || light->envelope <= 0.0f )
3764 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3766 /* delete the light */
3768 *owner = light->next;
3769 if( light->w != NULL )
3776 /* square envelope */
3777 light->envelope2 = (light->envelope * light->envelope);
3779 /* increment light count */
3782 /* set next light */
3783 owner = &((**owner).next);
3786 /* bucket sort lights by style */
3787 memset( buckets, 0, sizeof( buckets ) );
3789 for( light = lights; light != NULL; light = light2 )
3791 /* get next light */
3792 light2 = light->next;
3794 /* filter into correct bucket */
3795 light->next = buckets[ light->style ];
3796 buckets[ light->style ] = light;
3798 /* if any styled light is present, automatically set nocollapse */
3799 if( light->style != LS_NORMAL )
3803 /* filter back into light list */
3805 for( i = 255; i >= 0; i-- )
3808 for( light = buckets[ i ]; light != NULL; light = light2 )
3810 light2 = light->next;
3811 light->next = lights;
3816 /* emit some statistics */
3817 Sys_Printf( "%9d total lights\n", numLights );
3818 Sys_Printf( "%9d culled lights\n", numCulledLights );
3824 CreateTraceLightsForBounds()
3825 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3828 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3832 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3833 float radius, dist, length;
3836 /* potential pre-setup */
3837 if( numLights == 0 )
3838 SetupEnvelopes( qfalse, fast );
3841 //% 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 ] );
3843 /* allocate the light list */
3844 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3845 trace->numLights = 0;
3847 /* calculate spherical bounds */
3848 VectorAdd( mins, maxs, origin );
3849 VectorScale( origin, 0.5f, origin );
3850 VectorSubtract( maxs, origin, dir );
3851 radius = (float) VectorLength( dir );
3853 /* get length of normal vector */
3854 if( normal != NULL )
3855 length = VectorLength( normal );
3858 normal = nullVector;
3862 /* test each light and see if it reaches the sphere */
3863 /* note: the attenuation code MUST match LightingAtSample() */
3864 for( light = lights; light; light = light->next )
3866 /* check zero sized envelope */
3867 if( light->envelope <= 0 )
3869 lightsEnvelopeCulled++;
3874 if( !(light->flags & flags) )
3877 /* sunlight skips all this nonsense */
3878 if( light->type != EMIT_SUN )
3884 /* check against pvs cluster */
3885 if( numClusters > 0 && clusters != NULL )
3887 for( i = 0; i < numClusters; i++ )
3889 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3894 if( i == numClusters )
3896 lightsClusterCulled++;
3901 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3902 VectorSubtract( light->origin, origin, dir );
3903 dist = VectorLength( dir );
3904 dist -= light->envelope;
3908 lightsEnvelopeCulled++;
3912 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3915 for( i = 0; i < 3; i++ )
3917 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3922 lightsBoundsCulled++;
3928 /* planar surfaces (except twosided surfaces) have a couple more checks */
3929 if( length > 0.0f && trace->twoSided == qfalse )
3931 /* lights coplanar with a surface won't light it */
3932 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3934 lightsPlaneCulled++;
3938 /* check to see if light is behind the plane */
3939 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3941 lightsPlaneCulled++;
3946 /* add this light */
3947 trace->lights[ trace->numLights++ ] = light;
3950 /* make last night null */
3951 trace->lights[ trace->numLights ] = NULL;
3956 void FreeTraceLights( trace_t *trace )
3958 if( trace->lights != NULL )
3959 free( trace->lights );
3965 CreateTraceLightsForSurface()
3966 creates a list of lights that can potentially affect a drawsurface
3969 void CreateTraceLightsForSurface( int num, trace_t *trace )
3972 vec3_t mins, maxs, normal;
3974 bspDrawSurface_t *ds;
3975 surfaceInfo_t *info;
3982 /* get drawsurface and info */
3983 ds = &bspDrawSurfaces[ num ];
3984 info = &surfaceInfos[ num ];
3986 /* get the mins/maxs for the dsurf */
3987 ClearBounds( mins, maxs );
3988 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3989 for( i = 0; i < ds->numVerts; i++ )
3991 dv = &yDrawVerts[ ds->firstVert + i ];
3992 AddPointToBounds( dv->xyz, mins, maxs );
3993 if( !VectorCompare( dv->normal, normal ) )
3994 VectorClear( normal );
3997 /* create the lights for the bounding box */
3998 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4001 /////////////////////////////////////////////////////////////
4003 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4004 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4005 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4006 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4008 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4009 static int numFloodVectors = 0;
4011 void SetupFloodLight( void )
4014 float angle, elevation, angleStep, elevationStep;
4016 double v1,v2,v3,v4,v5;
4019 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4021 /* calculate angular steps */
4022 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4023 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4027 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4029 /* iterate elevation */
4030 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4032 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4033 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4034 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4039 /* emit some statistics */
4040 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4043 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4045 if( value[ 0 ] != '\0' )
4048 v4=floodlightDistance;
4049 v5=floodlightIntensity;
4051 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
4053 floodlightRGB[0]=v1;
4054 floodlightRGB[1]=v2;
4055 floodlightRGB[2]=v3;
4057 if (VectorLength(floodlightRGB)==0)
4059 VectorSet(floodlightRGB,240,240,255);
4065 floodlightDistance=v4;
4066 floodlightIntensity=v5;
4068 floodlighty = qtrue;
4069 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4073 VectorSet(floodlightRGB,240,240,255);
4074 //floodlighty = qtrue;
4075 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4077 VectorNormalize(floodlightRGB,floodlightRGB);
4081 FloodLightForSample()
4082 calculates floodlight value for a given sample
4083 once again, kudos to the dirtmapping coder
4086 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4092 float gatherLight, outLight;
4093 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4101 if( trace == NULL || trace->cluster < 0 )
4106 dd = floodLightDistance;
4107 VectorCopy( trace->normal, normal );
4109 /* check if the normal is aligned to the world-up */
4110 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4112 if( normal[ 2 ] == 1.0f )
4114 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4115 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4117 else if( normal[ 2 ] == -1.0f )
4119 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4120 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4125 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4126 CrossProduct( normal, worldUp, myRt );
4127 VectorNormalize( myRt, myRt );
4128 CrossProduct( myRt, normal, myUp );
4129 VectorNormalize( myUp, myUp );
4132 /* vortex: optimise floodLightLowQuality a bit */
4133 if ( floodLightLowQuality == qtrue )
4135 /* iterate through ordered vectors */
4136 for( i = 0; i < numFloodVectors; i++ )
4137 if (rand()%10 != 0 ) continue;
4141 /* iterate through ordered vectors */
4142 for( i = 0; i < numFloodVectors; i++ )
4146 /* transform vector into tangent space */
4147 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4148 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4149 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4152 VectorMA( trace->origin, dd, direction, trace->end );
4154 //VectorMA( trace->origin, 1, direction, trace->origin );
4156 SetupTrace( trace );
4161 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4165 else if ( trace->opaque )
4167 VectorSubtract( trace->hit, trace->origin, displacement );
4168 d=VectorLength( displacement );
4170 // d=trace->distance;
4171 //if (d>256) gatherDirt+=1;
4173 if (contribution>1) contribution=1.0f;
4175 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4178 gatherLight+=contribution;
4183 if( gatherLight <= 0.0f )
4191 outLight=gatherLight;
4192 if( outLight > 1.0f )
4195 /* return to sender */
4200 FloodLightRawLightmap
4201 lighttracer style ambient occlusion light hack.
4202 Kudos to the dirtmapping author for most of this source.
4203 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4204 VorteX: fixed problems with deluxemapping
4207 // floodlight pass on a lightmap
4208 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4210 int i, x, y, *cluster;
4211 float *origin, *normal, *floodlight, floodLightAmount;
4212 surfaceInfo_t *info;
4215 // float samples, average, *floodlight2;
4217 memset(&trace,0,sizeof(trace_t));
4220 trace.testOcclusion = qtrue;
4221 trace.forceSunlight = qfalse;
4222 trace.twoSided = qtrue;
4223 trace.recvShadows = lm->recvShadows;
4224 trace.numSurfaces = lm->numLightSurfaces;
4225 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4226 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4227 trace.testAll = qfalse;
4228 trace.distance = 1024;
4230 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4231 //trace.twoSided = qfalse;
4232 for( i = 0; i < trace.numSurfaces; i++ )
4235 info = &surfaceInfos[ trace.surfaces[ i ] ];
4237 /* check twosidedness */
4238 if( info->si->twoSided )
4240 trace.twoSided = qtrue;
4245 /* gather floodlight */
4246 for( y = 0; y < lm->sh; y++ )
4248 for( x = 0; x < lm->sw; x++ )
4251 cluster = SUPER_CLUSTER( x, y );
4252 origin = SUPER_ORIGIN( x, y );
4253 normal = SUPER_NORMAL( x, y );
4254 floodlight = SUPER_FLOODLIGHT( x, y );
4256 /* set default dirt */
4259 /* only look at mapped luxels */
4264 trace.cluster = *cluster;
4265 VectorCopy( origin, trace.origin );
4266 VectorCopy( normal, trace.normal );
4268 /* get floodlight */
4269 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4271 /* add floodlight */
4272 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4273 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4274 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4275 floodlight[3] += floodlightDirectionScale;
4279 /* testing no filtering */
4285 for( y = 0; y < lm->sh; y++ )
4287 for( x = 0; x < lm->sw; x++ )
4290 cluster = SUPER_CLUSTER( x, y );
4291 floodlight = SUPER_FLOODLIGHT(x, y );
4293 /* filter dirt by adjacency to unmapped luxels */
4294 average = *floodlight;
4296 for( sy = (y - 1); sy <= (y + 1); sy++ )
4298 if( sy < 0 || sy >= lm->sh )
4301 for( sx = (x - 1); sx <= (x + 1); sx++ )
4303 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4306 /* get neighboring luxel */
4307 cluster = SUPER_CLUSTER( sx, sy );
4308 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4309 if( *cluster < 0 || *floodlight2 <= 0.0f )
4313 average += *floodlight2;
4318 if( samples <= 0.0f )
4323 if( samples <= 0.0f )
4327 *floodlight = average / samples;
4333 void FloodLightRawLightmap( int rawLightmapNum )
4337 /* bail if this number exceeds the number of raw lightmaps */
4338 if( rawLightmapNum >= numRawLightmaps )
4341 lm = &rawLightmaps[ rawLightmapNum ];
4344 if (floodlighty && floodlightIntensity)
4345 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4348 if (lm->floodlightIntensity)
4350 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4351 numSurfacesFloodlighten += 1;
4355 void FloodlightRawLightmaps()
4357 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4358 numSurfacesFloodlighten = 0;
4359 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4360 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4364 FloodLightIlluminate()
4365 illuminate floodlight into lightmap luxels
4368 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4370 float *luxel, *floodlight, *deluxel, *normal;
4373 int x, y, lightmapNum;
4375 /* walk lightmaps */
4376 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4379 if( lm->superLuxels[ lightmapNum ] == NULL )
4382 /* apply floodlight to each luxel */
4383 for( y = 0; y < lm->sh; y++ )
4385 for( x = 0; x < lm->sw; x++ )
4387 /* get floodlight */
4388 floodlight = SUPER_FLOODLIGHT( x, y );
4389 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4393 cluster = SUPER_CLUSTER( x, y );
4395 /* only process mapped luxels */
4399 /* get particulars */
4400 luxel = SUPER_LUXEL( lightmapNum, x, y );
4401 deluxel = SUPER_DELUXEL( x, y );
4403 /* add to lightmap */
4404 luxel[0]+=floodlight[0];
4405 luxel[1]+=floodlight[1];
4406 luxel[2]+=floodlight[2];
4408 if (luxel[3]==0) luxel[3]=1;
4410 /* add to deluxemap */
4411 if (deluxemap && floodlight[3] > 0)
4415 normal = SUPER_NORMAL( x, y );
4416 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4418 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4419 if(brightness < 0.00390625f)
4420 brightness = 0.00390625f;
4422 VectorScale( normal, brightness, lightvector );
4423 VectorAdd( deluxel, lightvector, deluxel );