1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 /* get drawverts and map first triangle */
1158 MapTriangle( lm, info, dv, mapNonAxial );
1160 /* get drawverts and map second triangle */
1161 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1162 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1163 MapTriangle( lm, info, dv, mapNonAxial );
1178 /* -----------------------------------------------------------------
1179 average and clean up luxel normals
1180 ----------------------------------------------------------------- */
1182 /* walk the luxels */
1183 for( y = 0; y < lm->sh; y++ )
1185 for( x = 0; x < lm->sw; x++ )
1188 luxel = SUPER_LUXEL( 0, x, y );
1189 normal = SUPER_NORMAL( x, y );
1190 cluster = SUPER_CLUSTER( x, y );
1192 /* only look at mapped luxels */
1196 /* the normal data could be the sum of multiple samples */
1197 if( luxel[ 3 ] > 1.0f )
1198 VectorNormalize( normal, normal );
1200 /* mark this luxel as having only one normal */
1205 /* non-planar surfaces stop here */
1206 if( lm->plane == NULL )
1209 /* -----------------------------------------------------------------
1210 map occluded or unuxed luxels
1211 ----------------------------------------------------------------- */
1213 /* walk the luxels */
1214 radius = floor( superSample / 2 );
1215 radius = radius > 0 ? radius : 1.0f;
1217 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1219 for( y = 0; y < lm->sh; y++ )
1221 for( x = 0; x < lm->sw; x++ )
1224 luxel = SUPER_LUXEL( 0, x, y );
1225 normal = SUPER_NORMAL( x, y );
1226 cluster = SUPER_CLUSTER( x, y );
1228 /* only look at unmapped luxels */
1229 if( *cluster != CLUSTER_UNMAPPED )
1232 /* divine a normal and origin from neighboring luxels */
1233 VectorClear( fake.xyz );
1234 VectorClear( fake.normal );
1235 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1236 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1238 for( sy = (y - 1); sy <= (y + 1); sy++ )
1240 if( sy < 0 || sy >= lm->sh )
1243 for( sx = (x - 1); sx <= (x + 1); sx++ )
1245 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1248 /* get neighboring luxel */
1249 luxel = SUPER_LUXEL( 0, sx, sy );
1250 origin = SUPER_ORIGIN( sx, sy );
1251 normal = SUPER_NORMAL( sx, sy );
1252 cluster = SUPER_CLUSTER( sx, sy );
1254 /* only consider luxels mapped in previous passes */
1255 if( *cluster < 0 || luxel[ 0 ] >= pass )
1258 /* add its distinctiveness to our own */
1259 VectorAdd( fake.xyz, origin, fake.xyz );
1260 VectorAdd( fake.normal, normal, fake.normal );
1261 samples += luxel[ 3 ];
1266 if( samples == 0.0f )
1270 VectorDivide( fake.xyz, samples, fake.xyz );
1271 //% VectorDivide( fake.normal, samples, fake.normal );
1272 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1275 /* map the fake vert */
1276 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1281 /* -----------------------------------------------------------------
1282 average and clean up luxel normals
1283 ----------------------------------------------------------------- */
1285 /* walk the luxels */
1286 for( y = 0; y < lm->sh; y++ )
1288 for( x = 0; x < lm->sw; x++ )
1291 luxel = SUPER_LUXEL( 0, x, y );
1292 normal = SUPER_NORMAL( x, y );
1293 cluster = SUPER_CLUSTER( x, y );
1295 /* only look at mapped luxels */
1299 /* the normal data could be the sum of multiple samples */
1300 if( luxel[ 3 ] > 1.0f )
1301 VectorNormalize( normal, normal );
1303 /* mark this luxel as having only one normal */
1311 for( y = 0; y < lm->sh; y++ )
1313 for( x = 0; x < lm->sw; x++ )
1318 cluster = SUPER_CLUSTER( x, y );
1319 origin = SUPER_ORIGIN( x, y );
1320 normal = SUPER_NORMAL( x, y );
1321 luxel = SUPER_LUXEL( x, y );
1326 /* check if within the bounding boxes of all surfaces referenced */
1327 ClearBounds( mins, maxs );
1328 for( n = 0; n < lm->numLightSurfaces; n++ )
1331 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1332 TOL = info->sampleSize + 2;
1333 AddPointToBounds( info->mins, mins, maxs );
1334 AddPointToBounds( info->maxs, mins, maxs );
1335 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1336 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1337 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1342 if( n < lm->numLightSurfaces )
1345 /* report bogus origin */
1346 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1347 rawLightmapNum, x, y, *cluster,
1348 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1349 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1350 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1361 sets up dirtmap (ambient occlusion)
1364 #define DIRT_CONE_ANGLE 88 /* degrees */
1365 #define DIRT_NUM_ANGLE_STEPS 16
1366 #define DIRT_NUM_ELEVATION_STEPS 3
1367 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1369 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1370 static int numDirtVectors = 0;
1372 void SetupDirt( void )
1375 float angle, elevation, angleStep, elevationStep;
1379 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1381 /* calculate angular steps */
1382 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1383 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1387 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1389 /* iterate elevation */
1390 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1392 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1393 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1394 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1399 /* emit some statistics */
1400 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1406 calculates dirt value for a given sample
1409 float DirtForSample( trace_t *trace )
1412 float gatherDirt, outDirt, angle, elevation, ooDepth;
1413 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1419 if( trace == NULL || trace->cluster < 0 )
1424 ooDepth = 1.0f / dirtDepth;
1425 VectorCopy( trace->normal, normal );
1427 /* check if the normal is aligned to the world-up */
1428 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1430 if( normal[ 2 ] == 1.0f )
1432 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1433 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1435 else if( normal[ 2 ] == -1.0f )
1437 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1443 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1444 CrossProduct( normal, worldUp, myRt );
1445 VectorNormalize( myRt, myRt );
1446 CrossProduct( myRt, normal, myUp );
1447 VectorNormalize( myUp, myUp );
1450 /* 1 = random mode, 0 (well everything else) = non-random mode */
1454 for( i = 0; i < numDirtVectors; i++ )
1456 /* get random vector */
1457 angle = Random() * DEG2RAD( 360.0f );
1458 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1459 temp[ 0 ] = cos( angle ) * sin( elevation );
1460 temp[ 1 ] = sin( angle ) * sin( elevation );
1461 temp[ 2 ] = cos( elevation );
1463 /* transform into tangent space */
1464 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1465 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1466 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1469 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1470 SetupTrace( trace );
1476 VectorSubtract( trace->hit, trace->origin, displacement );
1477 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1483 /* iterate through ordered vectors */
1484 for( i = 0; i < numDirtVectors; i++ )
1486 /* transform vector into tangent space */
1487 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1488 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1489 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1492 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1493 SetupTrace( trace );
1499 VectorSubtract( trace->hit, trace->origin, displacement );
1500 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1506 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1507 SetupTrace( trace );
1513 VectorSubtract( trace->hit, trace->origin, displacement );
1514 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1518 if( gatherDirt <= 0.0f )
1521 /* apply gain (does this even do much? heh) */
1522 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1523 if( outDirt > 1.0f )
1527 outDirt *= dirtScale;
1528 if( outDirt > 1.0f )
1531 /* return to sender */
1532 return 1.0f - outDirt;
1539 calculates dirty fraction for each luxel
1542 void DirtyRawLightmap( int rawLightmapNum )
1544 int i, x, y, sx, sy, *cluster;
1545 float *origin, *normal, *dirt, *dirt2, average, samples;
1547 surfaceInfo_t *info;
1552 /* bail if this number exceeds the number of raw lightmaps */
1553 if( rawLightmapNum >= numRawLightmaps )
1557 lm = &rawLightmaps[ rawLightmapNum ];
1560 trace.testOcclusion = qtrue;
1561 trace.forceSunlight = qfalse;
1562 trace.recvShadows = lm->recvShadows;
1563 trace.numSurfaces = lm->numLightSurfaces;
1564 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1565 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1566 trace.testAll = qtrue;
1568 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1569 trace.twoSided = qfalse;
1570 for( i = 0; i < trace.numSurfaces; i++ )
1573 info = &surfaceInfos[ trace.surfaces[ i ] ];
1575 /* check twosidedness */
1576 if( info->si->twoSided )
1578 trace.twoSided = qtrue;
1584 for( i = 0; i < trace.numSurfaces; i++ )
1587 info = &surfaceInfos[ trace.surfaces[ i ] ];
1589 /* check twosidedness */
1590 if( info->si->noDirty )
1598 for( y = 0; y < lm->sh; y++ )
1600 for( x = 0; x < lm->sw; x++ )
1603 cluster = SUPER_CLUSTER( x, y );
1604 origin = SUPER_ORIGIN( x, y );
1605 normal = SUPER_NORMAL( x, y );
1606 dirt = SUPER_DIRT( x, y );
1608 /* set default dirt */
1611 /* only look at mapped luxels */
1615 /* don't apply dirty on this surface */
1623 trace.cluster = *cluster;
1624 VectorCopy( origin, trace.origin );
1625 VectorCopy( normal, trace.normal );
1628 *dirt = DirtForSample( &trace );
1632 /* testing no filtering */
1636 for( y = 0; y < lm->sh; y++ )
1638 for( x = 0; x < lm->sw; x++ )
1641 cluster = SUPER_CLUSTER( x, y );
1642 dirt = SUPER_DIRT( x, y );
1644 /* filter dirt by adjacency to unmapped luxels */
1647 for( sy = (y - 1); sy <= (y + 1); sy++ )
1649 if( sy < 0 || sy >= lm->sh )
1652 for( sx = (x - 1); sx <= (x + 1); sx++ )
1654 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1657 /* get neighboring luxel */
1658 cluster = SUPER_CLUSTER( sx, sy );
1659 dirt2 = SUPER_DIRT( sx, sy );
1660 if( *cluster < 0 || *dirt2 <= 0.0f )
1669 if( samples <= 0.0f )
1674 if( samples <= 0.0f )
1678 *dirt = average / samples;
1687 calculates the pvs cluster, origin, normal of a sub-luxel
1690 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1692 int i, *cluster, *cluster2;
1693 float *origin, *origin2, *normal; //% , *normal2;
1694 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1697 /* calulate x vector */
1698 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1700 cluster = SUPER_CLUSTER( x, y );
1701 origin = SUPER_ORIGIN( x, y );
1702 //% normal = SUPER_NORMAL( x, y );
1703 cluster2 = SUPER_CLUSTER( x + 1, y );
1704 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1705 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1707 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1709 cluster = SUPER_CLUSTER( x - 1, y );
1710 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1711 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1712 cluster2 = SUPER_CLUSTER( x, y );
1713 origin2 = SUPER_ORIGIN( x, y );
1714 //% normal2 = SUPER_NORMAL( x, y );
1717 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1719 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1720 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1722 /* calulate y vector */
1723 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1725 cluster = SUPER_CLUSTER( x, y );
1726 origin = SUPER_ORIGIN( x, y );
1727 //% normal = SUPER_NORMAL( x, y );
1728 cluster2 = SUPER_CLUSTER( x, y + 1 );
1729 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1730 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1732 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1734 cluster = SUPER_CLUSTER( x, y - 1 );
1735 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1736 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1737 cluster2 = SUPER_CLUSTER( x, y );
1738 origin2 = SUPER_ORIGIN( x, y );
1739 //% normal2 = SUPER_NORMAL( x, y );
1742 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1744 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1745 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1747 /* calculate new origin */
1748 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1749 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1750 for( i = 0; i < 3; i++ )
1751 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1754 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1755 if( *sampleCluster < 0 )
1758 /* calculate new normal */
1759 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1760 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1761 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1763 normal = SUPER_NORMAL( x, y );
1764 VectorCopy( normal, sampleNormal );
1772 SubsampleRawLuxel_r()
1773 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1776 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1778 int b, samples, mapped, lighted;
1781 vec3_t origin[ 4 ], normal[ 4 ];
1782 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1783 vec3_t color, total;
1787 if( lightLuxel[ 3 ] >= lightSamples )
1791 VectorClear( total );
1795 /* make 2x2 subsample stamp */
1796 for( b = 0; b < 4; b++ )
1799 VectorCopy( sampleOrigin, origin[ b ] );
1801 /* calculate position */
1802 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1809 /* increment sample count */
1810 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1813 trace->cluster = *cluster;
1814 VectorCopy( origin[ b ], trace->origin );
1815 VectorCopy( normal[ b ], trace->normal );
1819 LightContributionToSample( trace );
1820 if(trace->forceSubsampling > 1.0f)
1822 /* alphashadow: we subsample as deep as we can */
1828 /* add to totals (fixme: make contrast function) */
1829 VectorCopy( trace->color, luxel[ b ] );
1830 VectorAdd( total, trace->color, total );
1831 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1835 /* subsample further? */
1836 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1837 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1838 lighted != 0 && lighted != mapped )
1840 for( b = 0; b < 4; b++ )
1842 if( cluster[ b ] < 0 )
1844 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ] );
1849 //% VectorClear( color );
1851 VectorCopy( lightLuxel, color );
1853 for( b = 0; b < 4; b++ )
1855 if( cluster[ b ] < 0 )
1857 VectorAdd( color, luxel[ b ], color );
1865 color[ 0 ] /= samples;
1866 color[ 1 ] /= samples;
1867 color[ 2 ] /= samples;
1870 VectorCopy( color, lightLuxel );
1871 lightLuxel[ 3 ] += 1.0f;
1874 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1876 int b, samples, mapped;
1878 vec3_t origin, normal;
1881 VectorClear( total );
1883 for(b = 0; b < lightSamples; ++b)
1886 VectorCopy( sampleOrigin, origin );
1888 /* calculate position */
1889 if( !SubmapRawLuxel( lm, x, y, (bias * (2 * Random() - 1)), (bias * (2 * Random() - 1)), &cluster, origin, normal ) )
1896 trace->cluster = cluster;
1897 VectorCopy( origin, trace->origin );
1898 VectorCopy( normal, trace->normal );
1900 LightContributionToSample( trace );
1901 VectorAdd( total, trace->color, total );
1908 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1909 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1910 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1917 IlluminateRawLightmap()
1918 illuminates the luxels
1921 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1922 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1924 void IlluminateRawLightmap( int rawLightmapNum )
1926 int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1927 int *cluster, *cluster2, mapped, lighted, totalLighted;
1929 surfaceInfo_t *info;
1930 qboolean filterColor, filterDir;
1932 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1933 unsigned char *flag;
1934 float *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1935 vec3_t color, averageColor, averageDir, total, temp, temp2;
1936 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1938 float stackLightLuxels[ STACK_LL_SIZE ];
1941 /* bail if this number exceeds the number of raw lightmaps */
1942 if( rawLightmapNum >= numRawLightmaps )
1946 lm = &rawLightmaps[ rawLightmapNum ];
1949 trace.testOcclusion = !noTrace;
1950 trace.forceSunlight = qfalse;
1951 trace.recvShadows = lm->recvShadows;
1952 trace.numSurfaces = lm->numLightSurfaces;
1953 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1954 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1956 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1957 trace.twoSided = qfalse;
1958 for( i = 0; i < trace.numSurfaces; i++ )
1961 info = &surfaceInfos[ trace.surfaces[ i ] ];
1963 /* check twosidedness */
1964 if( info->si->twoSided )
1966 trace.twoSided = qtrue;
1971 /* create a culled light list for this raw lightmap */
1972 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1974 /* -----------------------------------------------------------------
1976 ----------------------------------------------------------------- */
1979 numLuxelsIlluminated += (lm->sw * lm->sh);
1981 /* test debugging state */
1982 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1984 /* debug fill the luxels */
1985 for( y = 0; y < lm->sh; y++ )
1987 for( x = 0; x < lm->sw; x++ )
1990 cluster = SUPER_CLUSTER( x, y );
1992 /* only fill mapped luxels */
1996 /* get particulars */
1997 luxel = SUPER_LUXEL( 0, x, y );
1998 origin = SUPER_ORIGIN( x, y );
1999 normal = SUPER_NORMAL( x, y );
2001 /* color the luxel with raw lightmap num? */
2003 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2005 /* color the luxel with lightmap axis? */
2006 else if( debugAxis )
2008 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2009 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2010 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2013 /* color the luxel with luxel cluster? */
2014 else if( debugCluster )
2015 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2017 /* color the luxel with luxel origin? */
2018 else if( debugOrigin )
2020 VectorSubtract( lm->maxs, lm->mins, temp );
2021 VectorScale( temp, (1.0f / 255.0f), temp );
2022 VectorSubtract( origin, lm->mins, temp2 );
2023 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2024 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2025 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2028 /* color the luxel with the normal */
2029 else if( normalmap )
2031 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2032 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2033 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2036 /* otherwise clear it */
2038 VectorClear( luxel );
2047 /* allocate temporary per-light luxel storage */
2048 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2049 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2050 lightLuxels = stackLightLuxels;
2052 lightLuxels = safe_malloc( llSize );
2055 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2057 /* set ambient color */
2058 for( y = 0; y < lm->sh; y++ )
2060 for( x = 0; x < lm->sw; x++ )
2063 cluster = SUPER_CLUSTER( x, y );
2064 luxel = SUPER_LUXEL( 0, x, y );
2065 normal = SUPER_NORMAL( x, y );
2066 deluxel = SUPER_DELUXEL( x, y );
2068 /* blacken unmapped clusters */
2070 VectorClear( luxel );
2075 VectorCopy( ambientColor, luxel );
2078 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2080 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2081 if(brightness < 0.00390625f)
2082 brightness = 0.00390625f;
2084 VectorScale( normal, brightness, deluxel );
2091 /* clear styled lightmaps */
2092 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2093 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2095 if( lm->superLuxels[ lightmapNum ] != NULL )
2096 memset( lm->superLuxels[ lightmapNum ], 0, size );
2099 /* debugging code */
2100 //% if( trace.numLights <= 0 )
2101 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2103 /* walk light list */
2104 for( i = 0; i < trace.numLights; i++ )
2107 trace.light = trace.lights[ i ];
2110 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2112 if( lm->styles[ lightmapNum ] == trace.light->style ||
2113 lm->styles[ lightmapNum ] == LS_NONE )
2117 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2118 if( lightmapNum >= MAX_LIGHTMAPS )
2120 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2125 memset( lightLuxels, 0, llSize );
2128 /* determine filter radius */
2129 filterRadius = lm->filterRadius > trace.light->filterRadius
2131 : trace.light->filterRadius;
2132 if( filterRadius < 0.0f )
2133 filterRadius = 0.0f;
2135 /* set luxel filter radius */
2136 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2137 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2138 luxelFilterRadius = 1;
2140 /* allocate sampling flags storage */
2141 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2143 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2144 if(lm->superFlags == NULL)
2145 lm->superFlags = safe_malloc( size );
2146 memset( (void *) lm->superFlags, 0, size );
2149 /* initial pass, one sample per luxel */
2150 for( y = 0; y < lm->sh; y++ )
2152 for( x = 0; x < lm->sw; x++ )
2155 cluster = SUPER_CLUSTER( x, y );
2159 /* get particulars */
2160 lightLuxel = LIGHT_LUXEL( x, y );
2161 deluxel = SUPER_DELUXEL( x, y );
2162 origin = SUPER_ORIGIN( x, y );
2163 normal = SUPER_NORMAL( x, y );
2164 flag = SUPER_FLAG( x, y );
2167 ////////// 27's temp hack for testing edge clipping ////
2168 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2170 lightLuxel[ 1 ] = 255;
2171 lightLuxel[ 3 ] = 1.0f;
2177 /* set contribution count */
2178 lightLuxel[ 3 ] = 1.0f;
2181 trace.cluster = *cluster;
2182 VectorCopy( origin, trace.origin );
2183 VectorCopy( normal, trace.normal );
2185 /* get light for this sample */
2186 LightContributionToSample( &trace );
2187 VectorCopy( trace.color, lightLuxel );
2189 /* add the contribution to the deluxemap */
2191 VectorAdd( deluxel, trace.directionContribution, deluxel );
2193 /* check for evilness */
2194 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2197 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2200 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2206 /* don't even bother with everything else if nothing was lit */
2207 if( totalLighted == 0 )
2210 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2211 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2212 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2215 for( y = 0; y < (lm->sh - 1); y++ )
2217 for( x = 0; x < (lm->sw - 1); x++ )
2222 VectorClear( total );
2224 /* test 2x2 stamp */
2225 for( t = 0; t < 4; t++ )
2227 /* set sample coords */
2228 sx = x + tests[ t ][ 0 ];
2229 sy = y + tests[ t ][ 1 ];
2232 cluster = SUPER_CLUSTER( sx, sy );
2238 flag = SUPER_FLAG( sx, sy );
2239 if(*flag & FLAG_FORCE_SUBSAMPLING)
2241 /* force a lighted/mapped discrepancy so we subsample */
2246 lightLuxel = LIGHT_LUXEL( sx, sy );
2247 VectorAdd( total, lightLuxel, total );
2248 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2252 /* if total color is under a certain amount, then don't bother subsampling */
2253 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2256 /* if all 4 pixels are either in shadow or light, then don't subsample */
2257 if( lighted != 0 && lighted != mapped )
2259 for( t = 0; t < 4; t++ )
2261 /* set sample coords */
2262 sx = x + tests[ t ][ 0 ];
2263 sy = y + tests[ t ][ 1 ];
2266 cluster = SUPER_CLUSTER( sx, sy );
2269 flag = SUPER_FLAG( sx, sy );
2270 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2272 lightLuxel = LIGHT_LUXEL( sx, sy );
2273 origin = SUPER_ORIGIN( sx, sy );
2275 /* only subsample shadowed luxels */
2276 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2280 if(lightRandomSamples)
2281 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f, lightLuxel );
2283 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel );
2285 *flag |= FLAG_ALREADY_SUBSAMPLED;
2287 /* debug code to colorize subsampled areas to yellow */
2288 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2289 //% VectorSet( luxel, 255, 204, 0 );
2296 /* tertiary pass, apply dirt map (ambient occlusion) */
2300 for( y = 0; y < lm->sh; y++ )
2302 for( x = 0; x < lm->sw; x++ )
2305 cluster = SUPER_CLUSTER( x, y );
2309 /* get particulars */
2310 lightLuxel = LIGHT_LUXEL( x, y );
2311 dirt = SUPER_DIRT( x, y );
2313 /* scale light value */
2314 VectorScale( lightLuxel, *dirt, lightLuxel );
2319 /* allocate sampling lightmap storage */
2320 if( lm->superLuxels[ lightmapNum ] == NULL )
2322 /* allocate sampling lightmap storage */
2323 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2324 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2325 memset( lm->superLuxels[ lightmapNum ], 0, size );
2329 if( lightmapNum > 0 )
2331 lm->styles[ lightmapNum ] = trace.light->style;
2332 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2335 /* copy to permanent luxels */
2336 for( y = 0; y < lm->sh; y++ )
2338 for( x = 0; x < lm->sw; x++ )
2340 /* get cluster and origin */
2341 cluster = SUPER_CLUSTER( x, y );
2344 origin = SUPER_ORIGIN( x, y );
2347 if( luxelFilterRadius )
2350 VectorClear( averageColor );
2353 /* cheaper distance-based filtering */
2354 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2356 if( sy < 0 || sy >= lm->sh )
2359 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2361 if( sx < 0 || sx >= lm->sw )
2364 /* get particulars */
2365 cluster = SUPER_CLUSTER( sx, sy );
2368 lightLuxel = LIGHT_LUXEL( sx, sy );
2371 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2372 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2374 /* scale luxel by filter weight */
2375 VectorScale( lightLuxel, weight, color );
2376 VectorAdd( averageColor, color, averageColor );
2382 if( samples <= 0.0f )
2385 /* scale into luxel */
2386 luxel = SUPER_LUXEL( lightmapNum, x, y );
2389 /* handle negative light */
2390 if( trace.light->flags & LIGHT_NEGATIVE )
2392 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2393 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2394 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2397 /* handle normal light */
2400 luxel[ 0 ] += averageColor[ 0 ] / samples;
2401 luxel[ 1 ] += averageColor[ 1 ] / samples;
2402 luxel[ 2 ] += averageColor[ 2 ] / samples;
2409 /* get particulars */
2410 lightLuxel = LIGHT_LUXEL( x, y );
2411 luxel = SUPER_LUXEL( lightmapNum, x, y );
2413 /* handle negative light */
2414 if( trace.light->flags & LIGHT_NEGATIVE )
2415 VectorScale( averageColor, -1.0f, averageColor );
2420 /* handle negative light */
2421 if( trace.light->flags & LIGHT_NEGATIVE )
2422 VectorSubtract( luxel, lightLuxel, luxel );
2424 /* handle normal light */
2426 VectorAdd( luxel, lightLuxel, luxel );
2432 /* free temporary luxels */
2433 if( lightLuxels != stackLightLuxels )
2434 free( lightLuxels );
2437 /* free light list */
2438 FreeTraceLights( &trace );
2440 /* floodlight pass */
2442 FloodlightIlluminateLightmap(lm);
2446 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2449 if( lm->superLuxels[ lightmapNum ] == NULL )
2452 for( y = 0; y < lm->sh; y++ )
2454 for( x = 0; x < lm->sw; x++ )
2457 cluster = SUPER_CLUSTER( x, y );
2458 //% if( *cluster < 0 )
2461 /* get particulars */
2462 luxel = SUPER_LUXEL( lightmapNum, x, y );
2463 normal = SUPER_NORMAL ( x, y );
2465 luxel[0]=(normal[0]*127)+127;
2466 luxel[1]=(normal[1]*127)+127;
2467 luxel[2]=(normal[2]*127)+127;
2473 /* -----------------------------------------------------------------
2475 ----------------------------------------------------------------- */
2479 /* walk lightmaps */
2480 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2483 if( lm->superLuxels[ lightmapNum ] == NULL )
2486 /* apply dirt to each luxel */
2487 for( y = 0; y < lm->sh; y++ )
2489 for( x = 0; x < lm->sw; x++ )
2492 cluster = SUPER_CLUSTER( x, y );
2493 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2496 /* get particulars */
2497 luxel = SUPER_LUXEL( lightmapNum, x, y );
2498 dirt = SUPER_DIRT( x, y );
2501 VectorScale( luxel, *dirt, luxel );
2505 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2511 /* -----------------------------------------------------------------
2513 ----------------------------------------------------------------- */
2515 /* walk lightmaps */
2516 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2519 if( lm->superLuxels[ lightmapNum ] == NULL )
2522 /* average occluded luxels from neighbors */
2523 for( y = 0; y < lm->sh; y++ )
2525 for( x = 0; x < lm->sw; x++ )
2527 /* get particulars */
2528 cluster = SUPER_CLUSTER( x, y );
2529 luxel = SUPER_LUXEL( lightmapNum, x, y );
2530 deluxel = SUPER_DELUXEL( x, y );
2531 normal = SUPER_NORMAL( x, y );
2533 /* determine if filtering is necessary */
2534 filterColor = qfalse;
2537 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2538 filterColor = qtrue;
2540 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2543 if( !filterColor && !filterDir )
2546 /* choose seed amount */
2547 VectorClear( averageColor );
2548 VectorClear( averageDir );
2551 /* walk 3x3 matrix */
2552 for( sy = (y - 1); sy <= (y + 1); sy++ )
2554 if( sy < 0 || sy >= lm->sh )
2557 for( sx = (x - 1); sx <= (x + 1); sx++ )
2559 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2562 /* get neighbor's particulars */
2563 cluster2 = SUPER_CLUSTER( sx, sy );
2564 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2565 deluxel2 = SUPER_DELUXEL( sx, sy );
2567 /* ignore unmapped/unlit luxels */
2568 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2569 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2572 /* add its distinctiveness to our own */
2573 VectorAdd( averageColor, luxel2, averageColor );
2574 samples += luxel2[ 3 ];
2576 VectorAdd( averageDir, deluxel2, averageDir );
2581 if( samples <= 0.0f )
2584 /* dark lightmap seams */
2587 if( lightmapNum == 0 )
2588 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2595 VectorDivide( averageColor, samples, luxel );
2599 VectorDivide( averageDir, samples, deluxel );
2601 /* set cluster to -3 */
2603 *cluster = CLUSTER_FLOODED;
2611 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2614 if( lm->superLuxels[ lightmapNum ] == NULL )
2616 for( y = 0; y < lm->sh; y++ )
2617 for( x = 0; x < lm->sw; x++ )
2620 cluster = SUPER_CLUSTER( x, y );
2621 luxel = SUPER_LUXEL( lightmapNum, x, y );
2622 deluxel = SUPER_DELUXEL( x, y );
2623 if(!luxel || !deluxel || !cluster)
2625 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2628 else if(*cluster < 0)
2631 // should have neither deluxemap nor lightmap
2633 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2638 // should have both deluxemap and lightmap
2640 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2650 IlluminateVertexes()
2651 light the surface vertexes
2654 #define VERTEX_NUDGE 4.0f
2656 void IlluminateVertexes( int num )
2658 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2659 int lightmapNum, numAvg;
2660 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2661 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2662 bspDrawSurface_t *ds;
2663 surfaceInfo_t *info;
2665 bspDrawVert_t *verts;
2667 float floodLightAmount;
2671 /* get surface, info, and raw lightmap */
2672 ds = &bspDrawSurfaces[ num ];
2673 info = &surfaceInfos[ num ];
2676 /* -----------------------------------------------------------------
2677 illuminate the vertexes
2678 ----------------------------------------------------------------- */
2680 /* calculate vertex lighting for surfaces without lightmaps */
2681 if( lm == NULL || cpmaHack )
2684 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2685 trace.forceSunlight = info->si->forceSunlight;
2686 trace.recvShadows = info->recvShadows;
2687 trace.numSurfaces = 1;
2688 trace.surfaces = #
2689 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2691 /* twosided lighting */
2692 trace.twoSided = info->si->twoSided;
2694 /* make light list for this surface */
2695 CreateTraceLightsForSurface( num, &trace );
2698 verts = yDrawVerts + ds->firstVert;
2700 memset( avgColors, 0, sizeof( avgColors ) );
2702 /* walk the surface verts */
2703 for( i = 0; i < ds->numVerts; i++ )
2705 /* get vertex luxel */
2706 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2708 /* color the luxel with raw lightmap num? */
2710 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2712 /* color the luxel with luxel origin? */
2713 else if( debugOrigin )
2715 VectorSubtract( info->maxs, info->mins, temp );
2716 VectorScale( temp, (1.0f / 255.0f), temp );
2717 VectorSubtract( origin, lm->mins, temp2 );
2718 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2719 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2720 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2723 /* color the luxel with the normal */
2724 else if( normalmap )
2726 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2727 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2728 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2731 /* illuminate the vertex */
2734 /* clear vertex luxel */
2735 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2737 /* try at initial origin */
2738 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2739 if( trace.cluster >= 0 )
2742 VectorCopy( verts[ i ].xyz, trace.origin );
2743 VectorCopy( verts[ i ].normal, trace.normal );
2746 if( dirty && !bouncing )
2747 dirt = DirtForSample( &trace );
2751 /* jal: floodlight */
2752 floodLightAmount = 0.0f;
2753 VectorClear( floodColor );
2754 if( floodlighty && !bouncing )
2756 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2757 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2761 LightingAtSample( &trace, ds->vertexStyles, colors );
2764 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2767 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2769 /* jal: floodlight */
2770 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2773 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2774 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2775 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2779 /* is this sample bright enough? */
2780 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2781 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2782 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2783 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2785 /* nudge the sample point around a bit */
2786 for( x = 0; x < 4; x++ )
2788 /* two's complement 0, 1, -1, 2, -2, etc */
2789 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2791 for( y = 0; y < 4; y++ )
2793 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2795 for( z = 0; z < 4; z++ )
2797 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2800 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2801 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2802 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2804 /* try at nudged origin */
2805 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2806 if( trace.cluster < 0 )
2810 LightingAtSample( &trace, ds->vertexStyles, colors );
2813 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2816 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2818 /* jal: floodlight */
2819 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2822 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2823 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2826 /* bright enough? */
2827 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2828 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2829 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2830 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2837 /* add to average? */
2838 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2839 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2840 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2841 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2844 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2846 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2847 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2852 /* another happy customer */
2853 numVertsIlluminated++;
2856 /* set average color */
2859 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2860 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2864 VectorCopy( ambientColor, avgColors[ 0 ] );
2867 /* clean up and store vertex color */
2868 for( i = 0; i < ds->numVerts; i++ )
2870 /* get vertex luxel */
2871 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2873 /* store average in occluded vertexes */
2874 if( radVertLuxel[ 0 ] < 0.0f )
2876 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2878 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2879 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2882 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2887 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2890 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2891 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2894 if( bouncing || bounce == 0 || !bounceOnly )
2895 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2896 if( !info->si->noVertexLight )
2897 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2901 /* free light list */
2902 FreeTraceLights( &trace );
2904 /* return to sender */
2908 /* -----------------------------------------------------------------
2909 reconstitute vertex lighting from the luxels
2910 ----------------------------------------------------------------- */
2912 /* set styles from lightmap */
2913 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2914 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2916 /* get max search radius */
2918 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2920 /* walk the surface verts */
2921 verts = yDrawVerts + ds->firstVert;
2922 for( i = 0; i < ds->numVerts; i++ )
2924 /* do each lightmap */
2925 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2928 if( lm->superLuxels[ lightmapNum ] == NULL )
2931 /* get luxel coords */
2932 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2933 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2936 else if( x >= lm->sw )
2940 else if( y >= lm->sh )
2943 /* get vertex luxels */
2944 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2945 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2947 /* color the luxel with the normal? */
2950 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2951 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2952 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2955 /* color the luxel with surface num? */
2956 else if( debugSurfaces )
2957 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2959 /* divine color from the superluxels */
2962 /* increasing radius */
2963 VectorClear( radVertLuxel );
2965 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2967 /* sample within radius */
2968 for( sy = (y - radius); sy <= (y + radius); sy++ )
2970 if( sy < 0 || sy >= lm->sh )
2973 for( sx = (x - radius); sx <= (x + radius); sx++ )
2975 if( sx < 0 || sx >= lm->sw )
2978 /* get luxel particulars */
2979 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2980 cluster = SUPER_CLUSTER( sx, sy );
2984 /* testing: must be brigher than ambient color */
2985 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2988 /* add its distinctiveness to our own */
2989 VectorAdd( radVertLuxel, luxel, radVertLuxel );
2990 samples += luxel[ 3 ];
2996 if( samples > 0.0f )
2997 VectorDivide( radVertLuxel, samples, radVertLuxel );
2999 VectorCopy( ambientColor, radVertLuxel );
3002 /* store into floating point storage */
3003 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3004 numVertsIlluminated++;
3006 /* store into bytes (for vertex approximation) */
3007 if( !info->si->noVertexLight )
3008 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3015 /* -------------------------------------------------------------------------------
3017 light optimization (-fast)
3019 creates a list of lights that will affect a surface and stores it in tw
3020 this is to optimize surface lighting by culling out as many of the
3021 lights in the world as possible from further calculation
3023 ------------------------------------------------------------------------------- */
3027 determines opaque brushes in the world and find sky shaders for sunlight calculations
3030 void SetupBrushes( void )
3032 int i, j, b, compileFlags;
3035 bspBrushSide_t *side;
3036 bspShader_t *shader;
3041 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3044 if( opaqueBrushes == NULL )
3045 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3048 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3049 numOpaqueBrushes = 0;
3051 /* walk the list of worldspawn brushes */
3052 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3055 b = bspModels[ 0 ].firstBSPBrush + i;
3056 brush = &bspBrushes[ b ];
3058 /* check all sides */
3061 for( j = 0; j < brush->numSides && inside; j++ )
3063 /* do bsp shader calculations */
3064 side = &bspBrushSides[ brush->firstSide + j ];
3065 shader = &bspShaders[ side->shaderNum ];
3067 /* get shader info */
3068 si = ShaderInfoForShader( shader->shader );
3072 /* or together compile flags */
3073 compileFlags |= si->compileFlags;
3076 /* determine if this brush is opaque to light */
3077 if( !(compileFlags & C_TRANSLUCENT) )
3079 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3085 /* emit some statistics */
3086 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3093 determines if two clusters are visible to each other using the PVS
3096 qboolean ClusterVisible( int a, int b )
3098 int portalClusters, leafBytes;
3103 if( a < 0 || b < 0 )
3111 if( numBSPVisBytes <=8 )
3115 portalClusters = ((int *) bspVisBytes)[ 0 ];
3116 leafBytes = ((int*) bspVisBytes)[ 1 ];
3117 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3120 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3129 borrowed from vlight.c
3132 int PointInLeafNum_r( vec3_t point, int nodenum )
3140 while( nodenum >= 0 )
3142 node = &bspNodes[ nodenum ];
3143 plane = &bspPlanes[ node->planeNum ];
3144 dist = DotProduct( point, plane->normal ) - plane->dist;
3146 nodenum = node->children[ 0 ];
3147 else if( dist < -0.1 )
3148 nodenum = node->children[ 1 ];
3151 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3152 if( bspLeafs[ leafnum ].cluster != -1 )
3154 nodenum = node->children[ 1 ];
3158 leafnum = -nodenum - 1;
3166 borrowed from vlight.c
3169 int PointInLeafNum( vec3_t point )
3171 return PointInLeafNum_r( point, 0 );
3177 ClusterVisibleToPoint() - ydnar
3178 returns qtrue if point can "see" cluster
3181 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3186 /* get leafNum for point */
3187 pointCluster = ClusterForPoint( point );
3188 if( pointCluster < 0 )
3192 return ClusterVisible( pointCluster, cluster );
3198 ClusterForPoint() - ydnar
3199 returns the pvs cluster for point
3202 int ClusterForPoint( vec3_t point )
3207 /* get leafNum for point */
3208 leafNum = PointInLeafNum( point );
3212 /* return the cluster */
3213 return bspLeafs[ leafNum ].cluster;
3219 ClusterForPointExt() - ydnar
3220 also takes brushes into account for occlusion testing
3223 int ClusterForPointExt( vec3_t point, float epsilon )
3225 int i, j, b, leafNum, cluster;
3228 int *brushes, numBSPBrushes;
3234 /* get leaf for point */
3235 leafNum = PointInLeafNum( point );
3238 leaf = &bspLeafs[ leafNum ];
3240 /* get the cluster */
3241 cluster = leaf->cluster;
3245 /* transparent leaf, so check point against all brushes in the leaf */
3246 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3247 numBSPBrushes = leaf->numBSPLeafBrushes;
3248 for( i = 0; i < numBSPBrushes; i++ )
3252 if( b > maxOpaqueBrush )
3254 brush = &bspBrushes[ b ];
3255 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3258 /* check point against all planes */
3260 for( j = 0; j < brush->numSides && inside; j++ )
3262 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3263 dot = DotProduct( point, plane->normal );
3269 /* if inside, return bogus cluster */
3274 /* if the point made it this far, it's not inside any opaque brushes */
3281 ClusterForPointExtFilter() - ydnar
3282 adds cluster checking against a list of known valid clusters
3285 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3290 /* get cluster for point */
3291 cluster = ClusterForPointExt( point, epsilon );
3293 /* check if filtering is necessary */
3294 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3298 for( i = 0; i < numClusters; i++ )
3300 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3311 ShaderForPointInLeaf() - ydnar
3312 checks a point against all brushes in a leaf, returning the shader of the brush
3313 also sets the cumulative surface and content flags for the brush hit
3316 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3321 int *brushes, numBSPBrushes;
3324 bspBrushSide_t *side;
3326 bspShader_t *shader;
3327 int allSurfaceFlags, allContentFlags;
3330 /* clear things out first */
3337 leaf = &bspLeafs[ leafNum ];
3339 /* transparent leaf, so check point against all brushes in the leaf */
3340 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3341 numBSPBrushes = leaf->numBSPLeafBrushes;
3342 for( i = 0; i < numBSPBrushes; i++ )
3345 brush = &bspBrushes[ brushes[ i ] ];
3347 /* check point against all planes */
3349 allSurfaceFlags = 0;
3350 allContentFlags = 0;
3351 for( j = 0; j < brush->numSides && inside; j++ )
3353 side = &bspBrushSides[ brush->firstSide + j ];
3354 plane = &bspPlanes[ side->planeNum ];
3355 dot = DotProduct( point, plane->normal );
3361 shader = &bspShaders[ side->shaderNum ];
3362 allSurfaceFlags |= shader->surfaceFlags;
3363 allContentFlags |= shader->contentFlags;
3367 /* handle if inside */
3370 /* if there are desired flags, check for same and continue if they aren't matched */
3371 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3373 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3376 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3377 *surfaceFlags = allSurfaceFlags;
3378 *contentFlags = allContentFlags;
3379 return brush->shaderNum;
3383 /* if the point made it this far, it's not inside any brushes */
3391 chops a bounding box by the plane defined by origin and normal
3392 returns qfalse if the bounds is entirely clipped away
3394 this is not exactly the fastest way to do this...
3397 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3399 /* FIXME: rewrite this so it doesn't use bloody brushes */
3407 calculates each light's effective envelope,
3408 taking into account brightness, type, and pvs.
3411 #define LIGHT_EPSILON 0.125f
3412 #define LIGHT_NUDGE 2.0f
3414 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3416 int i, x, y, z, x1, y1, z1;
3417 light_t *light, *light2, **owner;
3419 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3420 float radius, intensity;
3421 light_t *buckets[ 256 ];
3424 /* early out for weird cases where there are no lights */
3425 if( lights == NULL )
3429 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3433 numCulledLights = 0;
3435 while( *owner != NULL )
3440 /* handle negative lights */
3441 if( light->photons < 0.0f || light->add < 0.0f )
3443 light->photons *= -1.0f;
3444 light->add *= -1.0f;
3445 light->flags |= LIGHT_NEGATIVE;
3449 if( light->type == EMIT_SUN )
3453 light->envelope = MAX_WORLD_COORD * 8.0f;
3454 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3455 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3458 /* everything else */
3461 /* get pvs cluster for light */
3462 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3464 /* invalid cluster? */
3465 if( light->cluster < 0 )
3467 /* nudge the sample point around a bit */
3468 for( x = 0; x < 4; x++ )
3470 /* two's complement 0, 1, -1, 2, -2, etc */
3471 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3473 for( y = 0; y < 4; y++ )
3475 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3477 for( z = 0; z < 4; z++ )
3479 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3482 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3483 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3484 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3486 /* try at nudged origin */
3487 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3488 if( light->cluster < 0 )
3492 VectorCopy( origin, light->origin );
3498 /* only calculate for lights in pvs and outside of opaque brushes */
3499 if( light->cluster >= 0 )
3501 /* set light fast flag */
3503 light->flags |= LIGHT_FAST_TEMP;
3505 light->flags &= ~LIGHT_FAST_TEMP;
3506 if( light->si && light->si->noFast )
3507 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3509 /* clear light envelope */
3510 light->envelope = 0;
3512 /* handle area lights */
3513 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3515 /* ugly hack to calculate extent for area lights, but only done once */
3516 VectorScale( light->normal, -1.0f, dir );
3517 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3521 VectorMA( light->origin, radius, light->normal, origin );
3522 factor = PointToPolygonFormFactor( origin, dir, light->w );
3525 if( (factor * light->add) <= light->falloffTolerance )
3526 light->envelope = radius;
3529 /* check for fast mode */
3530 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3531 light->envelope = MAX_WORLD_COORD * 8.0f;
3536 intensity = light->photons;
3540 if( light->envelope <= 0.0f )
3542 /* solve distance for non-distance lights */
3543 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3544 light->envelope = MAX_WORLD_COORD * 8.0f;
3546 /* solve distance for linear lights */
3547 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3548 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3549 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3552 add = angle * light->photons * linearScale - (dist * light->fade);
3553 T = (light->photons * linearScale) - (dist * light->fade);
3554 T + (dist * light->fade) = (light->photons * linearScale);
3555 dist * light->fade = (light->photons * linearScale) - T;
3556 dist = ((light->photons * linearScale) - T) / light->fade;
3559 /* solve for inverse square falloff */
3561 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3564 add = light->photons / (dist * dist);
3565 T = light->photons / (dist * dist);
3566 T * (dist * dist) = light->photons;
3567 dist = sqrt( light->photons / T );
3571 /* chop radius against pvs */
3574 ClearBounds( mins, maxs );
3576 /* check all leaves */
3577 for( i = 0; i < numBSPLeafs; i++ )
3580 leaf = &bspLeafs[ i ];
3583 if( leaf->cluster < 0 )
3585 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3588 /* add this leafs bbox to the bounds */
3589 VectorCopy( leaf->mins, origin );
3590 AddPointToBounds( origin, mins, maxs );
3591 VectorCopy( leaf->maxs, origin );
3592 AddPointToBounds( origin, mins, maxs );
3595 /* test to see if bounds encompass light */
3596 for( i = 0; i < 3; i++ )
3598 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3600 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3601 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3602 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3603 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3604 AddPointToBounds( light->origin, mins, maxs );
3608 /* chop the bounds by a plane for area lights and spotlights */
3609 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3610 ChopBounds( mins, maxs, light->origin, light->normal );
3613 VectorCopy( mins, light->mins );
3614 VectorCopy( maxs, light->maxs );
3616 /* reflect bounds around light origin */
3617 //% VectorMA( light->origin, -1.0f, origin, origin );
3618 VectorScale( light->origin, 2, origin );
3619 VectorSubtract( origin, maxs, origin );
3620 AddPointToBounds( origin, mins, maxs );
3621 //% VectorMA( light->origin, -1.0f, mins, origin );
3622 VectorScale( light->origin, 2, origin );
3623 VectorSubtract( origin, mins, origin );
3624 AddPointToBounds( origin, mins, maxs );
3626 /* calculate spherical bounds */
3627 VectorSubtract( maxs, light->origin, dir );
3628 radius = (float) VectorLength( dir );
3630 /* if this radius is smaller than the envelope, then set the envelope to it */
3631 if( radius < light->envelope )
3633 light->envelope = radius;
3634 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3637 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3640 /* add grid/surface only check */
3643 if( !(light->flags & LIGHT_GRID) )
3644 light->envelope = 0.0f;
3648 if( !(light->flags & LIGHT_SURFACES) )
3649 light->envelope = 0.0f;
3654 if( light->cluster < 0 || light->envelope <= 0.0f )
3657 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3659 /* delete the light */
3661 *owner = light->next;
3662 if( light->w != NULL )
3669 /* square envelope */
3670 light->envelope2 = (light->envelope * light->envelope);
3672 /* increment light count */
3675 /* set next light */
3676 owner = &((**owner).next);
3679 /* bucket sort lights by style */
3680 memset( buckets, 0, sizeof( buckets ) );
3682 for( light = lights; light != NULL; light = light2 )
3684 /* get next light */
3685 light2 = light->next;
3687 /* filter into correct bucket */
3688 light->next = buckets[ light->style ];
3689 buckets[ light->style ] = light;
3691 /* if any styled light is present, automatically set nocollapse */
3692 if( light->style != LS_NORMAL )
3696 /* filter back into light list */
3698 for( i = 255; i >= 0; i-- )
3701 for( light = buckets[ i ]; light != NULL; light = light2 )
3703 light2 = light->next;
3704 light->next = lights;
3709 /* emit some statistics */
3710 Sys_Printf( "%9d total lights\n", numLights );
3711 Sys_Printf( "%9d culled lights\n", numCulledLights );
3717 CreateTraceLightsForBounds()
3718 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3721 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3725 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3726 float radius, dist, length;
3729 /* potential pre-setup */
3730 if( numLights == 0 )
3731 SetupEnvelopes( qfalse, fast );
3734 //% 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 ] );
3736 /* allocate the light list */
3737 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3738 trace->numLights = 0;
3740 /* calculate spherical bounds */
3741 VectorAdd( mins, maxs, origin );
3742 VectorScale( origin, 0.5f, origin );
3743 VectorSubtract( maxs, origin, dir );
3744 radius = (float) VectorLength( dir );
3746 /* get length of normal vector */
3747 if( normal != NULL )
3748 length = VectorLength( normal );
3751 normal = nullVector;
3755 /* test each light and see if it reaches the sphere */
3756 /* note: the attenuation code MUST match LightingAtSample() */
3757 for( light = lights; light; light = light->next )
3759 /* check zero sized envelope */
3760 if( light->envelope <= 0 )
3762 lightsEnvelopeCulled++;
3767 if( !(light->flags & flags) )
3770 /* sunlight skips all this nonsense */
3771 if( light->type != EMIT_SUN )
3777 /* check against pvs cluster */
3778 if( numClusters > 0 && clusters != NULL )
3780 for( i = 0; i < numClusters; i++ )
3782 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3787 if( i == numClusters )
3789 lightsClusterCulled++;
3794 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3795 VectorSubtract( light->origin, origin, dir );
3796 dist = VectorLength( dir );
3797 dist -= light->envelope;
3801 lightsEnvelopeCulled++;
3805 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3808 for( i = 0; i < 3; i++ )
3810 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3815 lightsBoundsCulled++;
3821 /* planar surfaces (except twosided surfaces) have a couple more checks */
3822 if( length > 0.0f && trace->twoSided == qfalse )
3824 /* lights coplanar with a surface won't light it */
3825 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3827 lightsPlaneCulled++;
3831 /* check to see if light is behind the plane */
3832 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3834 lightsPlaneCulled++;
3839 /* add this light */
3840 trace->lights[ trace->numLights++ ] = light;
3843 /* make last night null */
3844 trace->lights[ trace->numLights ] = NULL;
3849 void FreeTraceLights( trace_t *trace )
3851 if( trace->lights != NULL )
3852 free( trace->lights );
3858 CreateTraceLightsForSurface()
3859 creates a list of lights that can potentially affect a drawsurface
3862 void CreateTraceLightsForSurface( int num, trace_t *trace )
3865 vec3_t mins, maxs, normal;
3867 bspDrawSurface_t *ds;
3868 surfaceInfo_t *info;
3875 /* get drawsurface and info */
3876 ds = &bspDrawSurfaces[ num ];
3877 info = &surfaceInfos[ num ];
3879 /* get the mins/maxs for the dsurf */
3880 ClearBounds( mins, maxs );
3881 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3882 for( i = 0; i < ds->numVerts; i++ )
3884 dv = &yDrawVerts[ ds->firstVert + i ];
3885 AddPointToBounds( dv->xyz, mins, maxs );
3886 if( !VectorCompare( dv->normal, normal ) )
3887 VectorClear( normal );
3890 /* create the lights for the bounding box */
3891 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3894 /////////////////////////////////////////////////////////////
3896 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3897 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3898 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3899 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3901 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3902 static int numFloodVectors = 0;
3904 void SetupFloodLight( void )
3907 float angle, elevation, angleStep, elevationStep;
3909 double v1,v2,v3,v4,v5;
3912 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3914 /* calculate angular steps */
3915 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3916 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3920 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3922 /* iterate elevation */
3923 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3925 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3926 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3927 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3932 /* emit some statistics */
3933 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3936 value = ValueForKey( &entities[ 0 ], "_floodlight" );
3938 if( value[ 0 ] != '\0' )
3941 v4=floodlightDistance;
3942 v5=floodlightIntensity;
3944 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3946 floodlightRGB[0]=v1;
3947 floodlightRGB[1]=v2;
3948 floodlightRGB[2]=v3;
3950 if (VectorLength(floodlightRGB)==0)
3952 VectorSet(floodlightRGB,240,240,255);
3958 floodlightDistance=v4;
3959 floodlightIntensity=v5;
3961 floodlighty = qtrue;
3962 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3966 VectorSet(floodlightRGB,240,240,255);
3967 //floodlighty = qtrue;
3968 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3970 VectorNormalize(floodlightRGB,floodlightRGB);
3974 FloodLightForSample()
3975 calculates floodlight value for a given sample
3976 once again, kudos to the dirtmapping coder
3979 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
3985 float gatherLight, outLight;
3986 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
3994 if( trace == NULL || trace->cluster < 0 )
3999 dd = floodLightDistance;
4000 VectorCopy( trace->normal, normal );
4002 /* check if the normal is aligned to the world-up */
4003 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4005 if( normal[ 2 ] == 1.0f )
4007 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4008 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4010 else if( normal[ 2 ] == -1.0f )
4012 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4013 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4018 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4019 CrossProduct( normal, worldUp, myRt );
4020 VectorNormalize( myRt, myRt );
4021 CrossProduct( myRt, normal, myUp );
4022 VectorNormalize( myUp, myUp );
4025 /* vortex: optimise floodLightLowQuality a bit */
4026 if ( floodLightLowQuality == qtrue )
4028 /* iterate through ordered vectors */
4029 for( i = 0; i < numFloodVectors; i++ )
4030 if (rand()%10 != 0 ) continue;
4034 /* iterate through ordered vectors */
4035 for( i = 0; i < numFloodVectors; i++ )
4039 /* transform vector into tangent space */
4040 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4041 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4042 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4045 VectorMA( trace->origin, dd, direction, trace->end );
4047 //VectorMA( trace->origin, 1, direction, trace->origin );
4049 SetupTrace( trace );
4054 if (trace->compileFlags & C_SKY )
4058 else if ( trace->opaque )
4060 VectorSubtract( trace->hit, trace->origin, displacement );
4061 d=VectorLength( displacement );
4063 // d=trace->distance;
4064 //if (d>256) gatherDirt+=1;
4066 if (contribution>1) contribution=1.0f;
4068 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4071 gatherLight+=contribution;
4076 if( gatherLight <= 0.0f )
4084 outLight=gatherLight;
4085 if( outLight > 1.0f )
4088 /* return to sender */
4093 FloodLightRawLightmap
4094 lighttracer style ambient occlusion light hack.
4095 Kudos to the dirtmapping author for most of this source.
4096 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4097 VorteX: fixed problems with deluxemapping
4100 // floodlight pass on a lightmap
4101 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4103 int i, x, y, *cluster;
4104 float *origin, *normal, *floodlight, floodLightAmount;
4105 surfaceInfo_t *info;
4108 // float samples, average, *floodlight2;
4110 memset(&trace,0,sizeof(trace_t));
4113 trace.testOcclusion = qtrue;
4114 trace.forceSunlight = qfalse;
4115 trace.twoSided = qtrue;
4116 trace.recvShadows = lm->recvShadows;
4117 trace.numSurfaces = lm->numLightSurfaces;
4118 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4119 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4120 trace.testAll = qfalse;
4121 trace.distance = 1024;
4123 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4124 //trace.twoSided = qfalse;
4125 for( i = 0; i < trace.numSurfaces; i++ )
4128 info = &surfaceInfos[ trace.surfaces[ i ] ];
4130 /* check twosidedness */
4131 if( info->si->twoSided )
4133 trace.twoSided = qtrue;
4138 /* gather floodlight */
4139 for( y = 0; y < lm->sh; y++ )
4141 for( x = 0; x < lm->sw; x++ )
4144 cluster = SUPER_CLUSTER( x, y );
4145 origin = SUPER_ORIGIN( x, y );
4146 normal = SUPER_NORMAL( x, y );
4147 floodlight = SUPER_FLOODLIGHT( x, y );
4149 /* set default dirt */
4152 /* only look at mapped luxels */
4157 trace.cluster = *cluster;
4158 VectorCopy( origin, trace.origin );
4159 VectorCopy( normal, trace.normal );
4161 /* get floodlight */
4162 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4164 /* add floodlight */
4165 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4166 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4167 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4168 floodlight[3] += floodlightDirectionScale;
4172 /* testing no filtering */
4178 for( y = 0; y < lm->sh; y++ )
4180 for( x = 0; x < lm->sw; x++ )
4183 cluster = SUPER_CLUSTER( x, y );
4184 floodlight = SUPER_FLOODLIGHT(x, y );
4186 /* filter dirt by adjacency to unmapped luxels */
4187 average = *floodlight;
4189 for( sy = (y - 1); sy <= (y + 1); sy++ )
4191 if( sy < 0 || sy >= lm->sh )
4194 for( sx = (x - 1); sx <= (x + 1); sx++ )
4196 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4199 /* get neighboring luxel */
4200 cluster = SUPER_CLUSTER( sx, sy );
4201 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4202 if( *cluster < 0 || *floodlight2 <= 0.0f )
4206 average += *floodlight2;
4211 if( samples <= 0.0f )
4216 if( samples <= 0.0f )
4220 *floodlight = average / samples;
4226 void FloodLightRawLightmap( int rawLightmapNum )
4230 /* bail if this number exceeds the number of raw lightmaps */
4231 if( rawLightmapNum >= numRawLightmaps )
4234 lm = &rawLightmaps[ rawLightmapNum ];
4237 if (floodlighty && floodlightIntensity)
4238 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4241 if (lm->floodlightIntensity)
4243 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4244 numSurfacesFloodlighten += 1;
4248 void FloodlightRawLightmaps()
4250 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4251 numSurfacesFloodlighten = 0;
4252 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4253 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4257 FloodLightIlluminate()
4258 illuminate floodlight into lightmap luxels
4261 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4263 float *luxel, *floodlight, *deluxel, *normal;
4266 int x, y, lightmapNum;
4268 /* walk lightmaps */
4269 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4272 if( lm->superLuxels[ lightmapNum ] == NULL )
4275 /* apply floodlight to each luxel */
4276 for( y = 0; y < lm->sh; y++ )
4278 for( x = 0; x < lm->sw; x++ )
4280 /* get floodlight */
4281 floodlight = SUPER_FLOODLIGHT( x, y );
4282 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4286 cluster = SUPER_CLUSTER( x, y );
4288 /* only process mapped luxels */
4292 /* get particulars */
4293 luxel = SUPER_LUXEL( lightmapNum, x, y );
4294 deluxel = SUPER_DELUXEL( x, y );
4296 /* add to lightmap */
4297 luxel[0]+=floodlight[0];
4298 luxel[1]+=floodlight[1];
4299 luxel[2]+=floodlight[2];
4301 if (luxel[3]==0) luxel[3]=1;
4303 /* add to deluxemap */
4304 if (deluxemap && floodlight[3] > 0)
4308 normal = SUPER_NORMAL( x, y );
4309 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4311 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4312 if(brightness < 0.00390625f)
4313 brightness = 0.00390625f;
4315 VectorScale( normal, brightness, lightvector );
4316 VectorAdd( deluxel, lightvector, deluxel );