1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1159 /* get drawverts and map first triangle */
1160 MapTriangle( lm, info, dv, mapNonAxial );
1162 /* get drawverts and map second triangle */
1163 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1164 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1165 MapTriangle( lm, info, dv, mapNonAxial );
1181 /* -----------------------------------------------------------------
1182 average and clean up luxel normals
1183 ----------------------------------------------------------------- */
1185 /* walk the luxels */
1186 for( y = 0; y < lm->sh; y++ )
1188 for( x = 0; x < lm->sw; x++ )
1191 luxel = SUPER_LUXEL( 0, x, y );
1192 normal = SUPER_NORMAL( x, y );
1193 cluster = SUPER_CLUSTER( x, y );
1195 /* only look at mapped luxels */
1199 /* the normal data could be the sum of multiple samples */
1200 if( luxel[ 3 ] > 1.0f )
1201 VectorNormalize( normal, normal );
1203 /* mark this luxel as having only one normal */
1208 /* non-planar surfaces stop here */
1209 if( lm->plane == NULL )
1212 /* -----------------------------------------------------------------
1213 map occluded or unuxed luxels
1214 ----------------------------------------------------------------- */
1216 /* walk the luxels */
1217 radius = floor( superSample / 2 );
1218 radius = radius > 0 ? radius : 1.0f;
1220 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1222 for( y = 0; y < lm->sh; y++ )
1224 for( x = 0; x < lm->sw; x++ )
1227 luxel = SUPER_LUXEL( 0, x, y );
1228 normal = SUPER_NORMAL( x, y );
1229 cluster = SUPER_CLUSTER( x, y );
1231 /* only look at unmapped luxels */
1232 if( *cluster != CLUSTER_UNMAPPED )
1235 /* divine a normal and origin from neighboring luxels */
1236 VectorClear( fake.xyz );
1237 VectorClear( fake.normal );
1238 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1239 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1241 for( sy = (y - 1); sy <= (y + 1); sy++ )
1243 if( sy < 0 || sy >= lm->sh )
1246 for( sx = (x - 1); sx <= (x + 1); sx++ )
1248 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1251 /* get neighboring luxel */
1252 luxel = SUPER_LUXEL( 0, sx, sy );
1253 origin = SUPER_ORIGIN( sx, sy );
1254 normal = SUPER_NORMAL( sx, sy );
1255 cluster = SUPER_CLUSTER( sx, sy );
1257 /* only consider luxels mapped in previous passes */
1258 if( *cluster < 0 || luxel[ 0 ] >= pass )
1261 /* add its distinctiveness to our own */
1262 VectorAdd( fake.xyz, origin, fake.xyz );
1263 VectorAdd( fake.normal, normal, fake.normal );
1264 samples += luxel[ 3 ];
1269 if( samples == 0.0f )
1273 VectorDivide( fake.xyz, samples, fake.xyz );
1274 //% VectorDivide( fake.normal, samples, fake.normal );
1275 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1278 /* map the fake vert */
1279 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1284 /* -----------------------------------------------------------------
1285 average and clean up luxel normals
1286 ----------------------------------------------------------------- */
1288 /* walk the luxels */
1289 for( y = 0; y < lm->sh; y++ )
1291 for( x = 0; x < lm->sw; x++ )
1294 luxel = SUPER_LUXEL( 0, x, y );
1295 normal = SUPER_NORMAL( x, y );
1296 cluster = SUPER_CLUSTER( x, y );
1298 /* only look at mapped luxels */
1302 /* the normal data could be the sum of multiple samples */
1303 if( luxel[ 3 ] > 1.0f )
1304 VectorNormalize( normal, normal );
1306 /* mark this luxel as having only one normal */
1314 for( y = 0; y < lm->sh; y++ )
1316 for( x = 0; x < lm->sw; x++ )
1321 cluster = SUPER_CLUSTER( x, y );
1322 origin = SUPER_ORIGIN( x, y );
1323 normal = SUPER_NORMAL( x, y );
1324 luxel = SUPER_LUXEL( x, y );
1329 /* check if within the bounding boxes of all surfaces referenced */
1330 ClearBounds( mins, maxs );
1331 for( n = 0; n < lm->numLightSurfaces; n++ )
1334 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1335 TOL = info->sampleSize + 2;
1336 AddPointToBounds( info->mins, mins, maxs );
1337 AddPointToBounds( info->maxs, mins, maxs );
1338 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1339 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1340 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1345 if( n < lm->numLightSurfaces )
1348 /* report bogus origin */
1349 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",
1350 rawLightmapNum, x, y, *cluster,
1351 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1352 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1353 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1364 sets up dirtmap (ambient occlusion)
1367 #define DIRT_CONE_ANGLE 88 /* degrees */
1368 #define DIRT_NUM_ANGLE_STEPS 16
1369 #define DIRT_NUM_ELEVATION_STEPS 3
1370 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1372 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1373 static int numDirtVectors = 0;
1375 void SetupDirt( void )
1378 float angle, elevation, angleStep, elevationStep;
1382 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1384 /* calculate angular steps */
1385 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1386 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1390 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1392 /* iterate elevation */
1393 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1395 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1396 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1397 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1402 /* emit some statistics */
1403 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1409 calculates dirt value for a given sample
1412 float DirtForSample( trace_t *trace )
1415 float gatherDirt, outDirt, angle, elevation, ooDepth;
1416 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1422 if( trace == NULL || trace->cluster < 0 )
1427 ooDepth = 1.0f / dirtDepth;
1428 VectorCopy( trace->normal, normal );
1430 /* check if the normal is aligned to the world-up */
1431 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1433 if( normal[ 2 ] == 1.0f )
1435 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1436 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1438 else if( normal[ 2 ] == -1.0f )
1440 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1441 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1446 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1447 CrossProduct( normal, worldUp, myRt );
1448 VectorNormalize( myRt, myRt );
1449 CrossProduct( myRt, normal, myUp );
1450 VectorNormalize( myUp, myUp );
1453 /* 1 = random mode, 0 (well everything else) = non-random mode */
1457 for( i = 0; i < numDirtVectors; i++ )
1459 /* get random vector */
1460 angle = Random() * DEG2RAD( 360.0f );
1461 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1462 temp[ 0 ] = cos( angle ) * sin( elevation );
1463 temp[ 1 ] = sin( angle ) * sin( elevation );
1464 temp[ 2 ] = cos( elevation );
1466 /* transform into tangent space */
1467 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1468 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1469 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1472 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1473 SetupTrace( trace );
1479 VectorSubtract( trace->hit, trace->origin, displacement );
1480 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1486 /* iterate through ordered vectors */
1487 for( i = 0; i < numDirtVectors; i++ )
1489 /* transform vector into tangent space */
1490 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1491 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1492 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1495 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1496 SetupTrace( trace );
1502 VectorSubtract( trace->hit, trace->origin, displacement );
1503 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1509 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1510 SetupTrace( trace );
1516 VectorSubtract( trace->hit, trace->origin, displacement );
1517 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1521 if( gatherDirt <= 0.0f )
1524 /* apply gain (does this even do much? heh) */
1525 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1526 if( outDirt > 1.0f )
1530 outDirt *= dirtScale;
1531 if( outDirt > 1.0f )
1534 /* return to sender */
1535 return 1.0f - outDirt;
1542 calculates dirty fraction for each luxel
1545 void DirtyRawLightmap( int rawLightmapNum )
1547 int i, x, y, sx, sy, *cluster;
1548 float *origin, *normal, *dirt, *dirt2, average, samples;
1550 surfaceInfo_t *info;
1555 /* bail if this number exceeds the number of raw lightmaps */
1556 if( rawLightmapNum >= numRawLightmaps )
1560 lm = &rawLightmaps[ rawLightmapNum ];
1563 trace.testOcclusion = qtrue;
1564 trace.forceSunlight = qfalse;
1565 trace.recvShadows = lm->recvShadows;
1566 trace.numSurfaces = lm->numLightSurfaces;
1567 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1568 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1569 trace.testAll = qtrue;
1571 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1572 trace.twoSided = qfalse;
1573 for( i = 0; i < trace.numSurfaces; i++ )
1576 info = &surfaceInfos[ trace.surfaces[ i ] ];
1578 /* check twosidedness */
1579 if( info->si->twoSided )
1581 trace.twoSided = qtrue;
1587 for( i = 0; i < trace.numSurfaces; i++ )
1590 info = &surfaceInfos[ trace.surfaces[ i ] ];
1592 /* check twosidedness */
1593 if( info->si->noDirty )
1601 for( y = 0; y < lm->sh; y++ )
1603 for( x = 0; x < lm->sw; x++ )
1606 cluster = SUPER_CLUSTER( x, y );
1607 origin = SUPER_ORIGIN( x, y );
1608 normal = SUPER_NORMAL( x, y );
1609 dirt = SUPER_DIRT( x, y );
1611 /* set default dirt */
1614 /* only look at mapped luxels */
1618 /* don't apply dirty on this surface */
1626 trace.cluster = *cluster;
1627 VectorCopy( origin, trace.origin );
1628 VectorCopy( normal, trace.normal );
1631 *dirt = DirtForSample( &trace );
1635 /* testing no filtering */
1639 for( y = 0; y < lm->sh; y++ )
1641 for( x = 0; x < lm->sw; x++ )
1644 cluster = SUPER_CLUSTER( x, y );
1645 dirt = SUPER_DIRT( x, y );
1647 /* filter dirt by adjacency to unmapped luxels */
1650 for( sy = (y - 1); sy <= (y + 1); sy++ )
1652 if( sy < 0 || sy >= lm->sh )
1655 for( sx = (x - 1); sx <= (x + 1); sx++ )
1657 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1660 /* get neighboring luxel */
1661 cluster = SUPER_CLUSTER( sx, sy );
1662 dirt2 = SUPER_DIRT( sx, sy );
1663 if( *cluster < 0 || *dirt2 <= 0.0f )
1672 if( samples <= 0.0f )
1677 if( samples <= 0.0f )
1681 *dirt = average / samples;
1690 calculates the pvs cluster, origin, normal of a sub-luxel
1693 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1695 int i, *cluster, *cluster2;
1696 float *origin, *origin2, *normal; //% , *normal2;
1697 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1700 /* calulate x vector */
1701 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1703 cluster = SUPER_CLUSTER( x, y );
1704 origin = SUPER_ORIGIN( x, y );
1705 //% normal = SUPER_NORMAL( x, y );
1706 cluster2 = SUPER_CLUSTER( x + 1, y );
1707 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1708 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1710 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1712 cluster = SUPER_CLUSTER( x - 1, y );
1713 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1714 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1715 cluster2 = SUPER_CLUSTER( x, y );
1716 origin2 = SUPER_ORIGIN( x, y );
1717 //% normal2 = SUPER_NORMAL( x, y );
1721 Error( "Spurious lightmap S vector\n" );
1724 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1725 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1727 /* calulate y vector */
1728 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1730 cluster = SUPER_CLUSTER( x, y );
1731 origin = SUPER_ORIGIN( x, y );
1732 //% normal = SUPER_NORMAL( x, y );
1733 cluster2 = SUPER_CLUSTER( x, y + 1 );
1734 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1735 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1737 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1739 cluster = SUPER_CLUSTER( x, y - 1 );
1740 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1741 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1742 cluster2 = SUPER_CLUSTER( x, y );
1743 origin2 = SUPER_ORIGIN( x, y );
1744 //% normal2 = SUPER_NORMAL( x, y );
1747 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1749 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1750 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1752 /* calculate new origin */
1753 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1754 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1755 for( i = 0; i < 3; i++ )
1756 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1759 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1760 if( *sampleCluster < 0 )
1763 /* calculate new normal */
1764 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1765 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1766 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1768 normal = SUPER_NORMAL( x, y );
1769 VectorCopy( normal, sampleNormal );
1777 SubsampleRawLuxel_r()
1778 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1781 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1783 int b, samples, mapped, lighted;
1786 vec3_t deluxel[ 3 ];
1787 vec3_t origin[ 4 ], normal[ 4 ];
1788 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1789 vec3_t color, direction, total;
1793 if( lightLuxel[ 3 ] >= lightSamples )
1797 VectorClear( total );
1801 /* make 2x2 subsample stamp */
1802 for( b = 0; b < 4; b++ )
1805 VectorCopy( sampleOrigin, origin[ b ] );
1807 /* calculate position */
1808 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1815 /* increment sample count */
1816 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1819 trace->cluster = *cluster;
1820 VectorCopy( origin[ b ], trace->origin );
1821 VectorCopy( normal[ b ], trace->normal );
1825 LightContributionToSample( trace );
1826 if(trace->forceSubsampling > 1.0f)
1828 /* alphashadow: we subsample as deep as we can */
1834 /* add to totals (fixme: make contrast function) */
1835 VectorCopy( trace->color, luxel[ b ] );
1838 VectorCopy( trace->directionContribution, deluxel[ b ] );
1840 VectorAdd( total, trace->color, total );
1841 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1845 /* subsample further? */
1846 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1847 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1848 lighted != 0 && lighted != mapped )
1850 for( b = 0; b < 4; b++ )
1852 if( cluster[ b ] < 0 )
1854 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1859 //% VectorClear( color );
1861 VectorCopy( lightLuxel, color );
1862 VectorCopy( lightDeluxel, direction );
1864 for( b = 0; b < 4; b++ )
1866 if( cluster[ b ] < 0 )
1868 VectorAdd( color, luxel[ b ], color );
1871 VectorAdd( direction, deluxel[ b ], direction );
1880 color[ 0 ] /= samples;
1881 color[ 1 ] /= samples;
1882 color[ 2 ] /= samples;
1885 VectorCopy( color, lightLuxel );
1886 lightLuxel[ 3 ] += 1.0f;
1890 direction[ 0 ] /= samples;
1891 direction[ 1 ] /= samples;
1892 direction[ 2 ] /= samples;
1893 VectorCopy( direction, lightDeluxel );
1898 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1899 static void GaussLikeRandom(float sigma, float *x, float *y)
1902 r = Random() * 2 * Q_PI;
1903 *x = sigma * 2.73861278752581783822 * cos(r);
1904 *y = sigma * 2.73861278752581783822 * sin(r);
1911 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1915 vec3_t origin, normal;
1916 vec3_t total, totaldirection;
1919 VectorClear( total );
1920 VectorClear( totaldirection );
1922 for(b = 0; b < lightSamples; ++b)
1925 VectorCopy( sampleOrigin, origin );
1926 GaussLikeRandom(bias, &dx, &dy);
1929 if(dx < -1) dx = -1;
1930 if(dy < -1) dy = -1;
1932 /* calculate position */
1933 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1940 trace->cluster = cluster;
1941 VectorCopy( origin, trace->origin );
1942 VectorCopy( normal, trace->normal );
1944 LightContributionToSample( trace );
1945 VectorAdd( total, trace->color, total );
1948 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1956 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1957 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1958 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1962 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1963 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1964 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1972 IlluminateRawLightmap()
1973 illuminates the luxels
1976 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1977 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1978 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1980 void IlluminateRawLightmap( int rawLightmapNum )
1982 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1983 int *cluster, *cluster2, mapped, lighted, totalLighted;
1984 size_t llSize, ldSize;
1986 surfaceInfo_t *info;
1987 qboolean filterColor, filterDir;
1989 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1990 unsigned char *flag;
1991 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1992 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1993 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1995 float stackLightLuxels[ STACK_LL_SIZE ];
1998 /* bail if this number exceeds the number of raw lightmaps */
1999 if( rawLightmapNum >= numRawLightmaps )
2003 lm = &rawLightmaps[ rawLightmapNum ];
2006 trace.testOcclusion = !noTrace;
2007 trace.forceSunlight = qfalse;
2008 trace.recvShadows = lm->recvShadows;
2009 trace.numSurfaces = lm->numLightSurfaces;
2010 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2011 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2013 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2014 trace.twoSided = qfalse;
2015 for( i = 0; i < trace.numSurfaces; i++ )
2018 info = &surfaceInfos[ trace.surfaces[ i ] ];
2020 /* check twosidedness */
2021 if( info->si->twoSided )
2023 trace.twoSided = qtrue;
2028 /* create a culled light list for this raw lightmap */
2029 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2031 /* -----------------------------------------------------------------
2033 ----------------------------------------------------------------- */
2036 numLuxelsIlluminated += (lm->sw * lm->sh);
2038 /* test debugging state */
2039 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2041 /* debug fill the luxels */
2042 for( y = 0; y < lm->sh; y++ )
2044 for( x = 0; x < lm->sw; x++ )
2047 cluster = SUPER_CLUSTER( x, y );
2049 /* only fill mapped luxels */
2053 /* get particulars */
2054 luxel = SUPER_LUXEL( 0, x, y );
2055 origin = SUPER_ORIGIN( x, y );
2056 normal = SUPER_NORMAL( x, y );
2058 /* color the luxel with raw lightmap num? */
2060 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2062 /* color the luxel with lightmap axis? */
2063 else if( debugAxis )
2065 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2066 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2067 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2070 /* color the luxel with luxel cluster? */
2071 else if( debugCluster )
2072 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2074 /* color the luxel with luxel origin? */
2075 else if( debugOrigin )
2077 VectorSubtract( lm->maxs, lm->mins, temp );
2078 VectorScale( temp, (1.0f / 255.0f), temp );
2079 VectorSubtract( origin, lm->mins, temp2 );
2080 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2081 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2082 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2085 /* color the luxel with the normal */
2086 else if( normalmap )
2088 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2089 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2090 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2093 /* otherwise clear it */
2095 VectorClear( luxel );
2104 /* allocate temporary per-light luxel storage */
2105 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2106 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2107 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2108 lightLuxels = stackLightLuxels;
2110 lightLuxels = safe_malloc( llSize );
2112 lightDeluxels = safe_malloc( ldSize );
2114 lightDeluxels = NULL;
2117 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2119 /* set ambient color */
2120 for( y = 0; y < lm->sh; y++ )
2122 for( x = 0; x < lm->sw; x++ )
2125 cluster = SUPER_CLUSTER( x, y );
2126 luxel = SUPER_LUXEL( 0, x, y );
2127 normal = SUPER_NORMAL( x, y );
2128 deluxel = SUPER_DELUXEL( x, y );
2130 /* blacken unmapped clusters */
2132 VectorClear( luxel );
2137 VectorCopy( ambientColor, luxel );
2140 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2142 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2143 if(brightness < 0.00390625f)
2144 brightness = 0.00390625f;
2146 VectorScale( normal, brightness, deluxel );
2153 /* clear styled lightmaps */
2154 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2155 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2157 if( lm->superLuxels[ lightmapNum ] != NULL )
2158 memset( lm->superLuxels[ lightmapNum ], 0, size );
2161 /* debugging code */
2162 //% if( trace.numLights <= 0 )
2163 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2165 /* walk light list */
2166 for( i = 0; i < trace.numLights; i++ )
2169 trace.light = trace.lights[ i ];
2172 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2174 if( lm->styles[ lightmapNum ] == trace.light->style ||
2175 lm->styles[ lightmapNum ] == LS_NONE )
2179 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2180 if( lightmapNum >= MAX_LIGHTMAPS )
2182 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2187 memset( lightLuxels, 0, llSize );
2189 memset( lightDeluxels, 0, ldSize );
2192 /* determine filter radius */
2193 filterRadius = lm->filterRadius > trace.light->filterRadius
2195 : trace.light->filterRadius;
2196 if( filterRadius < 0.0f )
2197 filterRadius = 0.0f;
2199 /* set luxel filter radius */
2200 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2201 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2202 luxelFilterRadius = 1;
2204 /* allocate sampling flags storage */
2205 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2207 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2208 if(lm->superFlags == NULL)
2209 lm->superFlags = safe_malloc( size );
2210 memset( (void *) lm->superFlags, 0, size );
2213 /* initial pass, one sample per luxel */
2214 for( y = 0; y < lm->sh; y++ )
2216 for( x = 0; x < lm->sw; x++ )
2219 cluster = SUPER_CLUSTER( x, y );
2223 /* get particulars */
2224 lightLuxel = LIGHT_LUXEL( x, y );
2225 lightDeluxel = LIGHT_DELUXEL( x, y );
2226 origin = SUPER_ORIGIN( x, y );
2227 normal = SUPER_NORMAL( x, y );
2228 flag = SUPER_FLAG( x, y );
2231 ////////// 27's temp hack for testing edge clipping ////
2232 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2234 lightLuxel[ 1 ] = 255;
2235 lightLuxel[ 3 ] = 1.0f;
2241 /* set contribution count */
2242 lightLuxel[ 3 ] = 1.0f;
2245 trace.cluster = *cluster;
2246 VectorCopy( origin, trace.origin );
2247 VectorCopy( normal, trace.normal );
2249 /* get light for this sample */
2250 LightContributionToSample( &trace );
2251 VectorCopy( trace.color, lightLuxel );
2253 /* add the contribution to the deluxemap */
2256 VectorCopy( trace.directionContribution, lightDeluxel );
2259 /* check for evilness */
2260 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2263 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2266 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2272 /* don't even bother with everything else if nothing was lit */
2273 if( totalLighted == 0 )
2276 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2277 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2278 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2281 for( y = 0; y < (lm->sh - 1); y++ )
2283 for( x = 0; x < (lm->sw - 1); x++ )
2288 VectorClear( total );
2290 /* test 2x2 stamp */
2291 for( t = 0; t < 4; t++ )
2293 /* set sample coords */
2294 sx = x + tests[ t ][ 0 ];
2295 sy = y + tests[ t ][ 1 ];
2298 cluster = SUPER_CLUSTER( sx, sy );
2304 flag = SUPER_FLAG( sx, sy );
2305 if(*flag & FLAG_FORCE_SUBSAMPLING)
2307 /* force a lighted/mapped discrepancy so we subsample */
2312 lightLuxel = LIGHT_LUXEL( sx, sy );
2313 VectorAdd( total, lightLuxel, total );
2314 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2318 /* if total color is under a certain amount, then don't bother subsampling */
2319 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2322 /* if all 4 pixels are either in shadow or light, then don't subsample */
2323 if( lighted != 0 && lighted != mapped )
2325 for( t = 0; t < 4; t++ )
2327 /* set sample coords */
2328 sx = x + tests[ t ][ 0 ];
2329 sy = y + tests[ t ][ 1 ];
2332 cluster = SUPER_CLUSTER( sx, sy );
2335 flag = SUPER_FLAG( sx, sy );
2336 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2338 lightLuxel = LIGHT_LUXEL( sx, sy );
2339 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2340 origin = SUPER_ORIGIN( sx, sy );
2342 /* only subsample shadowed luxels */
2343 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2347 if(lightRandomSamples)
2348 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f, lightLuxel, deluxemap ? lightDeluxel : NULL );
2350 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2352 *flag |= FLAG_ALREADY_SUBSAMPLED;
2354 /* debug code to colorize subsampled areas to yellow */
2355 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2356 //% VectorSet( luxel, 255, 204, 0 );
2363 /* tertiary pass, apply dirt map (ambient occlusion) */
2367 for( y = 0; y < lm->sh; y++ )
2369 for( x = 0; x < lm->sw; x++ )
2372 cluster = SUPER_CLUSTER( x, y );
2376 /* get particulars */
2377 lightLuxel = LIGHT_LUXEL( x, y );
2378 dirt = SUPER_DIRT( x, y );
2380 /* scale light value */
2381 VectorScale( lightLuxel, *dirt, lightLuxel );
2386 /* allocate sampling lightmap storage */
2387 if( lm->superLuxels[ lightmapNum ] == NULL )
2389 /* allocate sampling lightmap storage */
2390 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2391 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2392 memset( lm->superLuxels[ lightmapNum ], 0, size );
2396 if( lightmapNum > 0 )
2398 lm->styles[ lightmapNum ] = trace.light->style;
2399 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2402 /* copy to permanent luxels */
2403 for( y = 0; y < lm->sh; y++ )
2405 for( x = 0; x < lm->sw; x++ )
2407 /* get cluster and origin */
2408 cluster = SUPER_CLUSTER( x, y );
2411 origin = SUPER_ORIGIN( x, y );
2414 if( luxelFilterRadius )
2417 VectorClear( averageColor );
2418 VectorClear( averageDir );
2421 /* cheaper distance-based filtering */
2422 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2424 if( sy < 0 || sy >= lm->sh )
2427 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2429 if( sx < 0 || sx >= lm->sw )
2432 /* get particulars */
2433 cluster = SUPER_CLUSTER( sx, sy );
2436 lightLuxel = LIGHT_LUXEL( sx, sy );
2437 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2440 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2441 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2443 /* scale luxel by filter weight */
2444 VectorScale( lightLuxel, weight, color );
2445 VectorAdd( averageColor, color, averageColor );
2448 VectorScale( lightDeluxel, weight, direction );
2449 VectorAdd( averageDir, direction, averageDir );
2456 if( samples <= 0.0f )
2459 /* scale into luxel */
2460 luxel = SUPER_LUXEL( lightmapNum, x, y );
2463 /* handle negative light */
2464 if( trace.light->flags & LIGHT_NEGATIVE )
2466 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2467 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2468 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2471 /* handle normal light */
2474 luxel[ 0 ] += averageColor[ 0 ] / samples;
2475 luxel[ 1 ] += averageColor[ 1 ] / samples;
2476 luxel[ 2 ] += averageColor[ 2 ] / samples;
2481 /* scale into luxel */
2482 deluxel = SUPER_DELUXEL( x, y );
2483 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2484 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2485 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2492 /* get particulars */
2493 lightLuxel = LIGHT_LUXEL( x, y );
2494 lightDeluxel = LIGHT_DELUXEL( x, y );
2495 luxel = SUPER_LUXEL( lightmapNum, x, y );
2496 deluxel = SUPER_DELUXEL( x, y );
2498 /* handle negative light */
2499 if( trace.light->flags & LIGHT_NEGATIVE )
2500 VectorScale( averageColor, -1.0f, averageColor );
2505 /* handle negative light */
2506 if( trace.light->flags & LIGHT_NEGATIVE )
2507 VectorSubtract( luxel, lightLuxel, luxel );
2509 /* handle normal light */
2511 VectorAdd( luxel, lightLuxel, luxel );
2515 VectorAdd( deluxel, lightDeluxel, deluxel );
2522 /* free temporary luxels */
2523 if( lightLuxels != stackLightLuxels )
2524 free( lightLuxels );
2527 free( lightDeluxels );
2530 /* free light list */
2531 FreeTraceLights( &trace );
2533 /* floodlight pass */
2535 FloodlightIlluminateLightmap(lm);
2539 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2542 if( lm->superLuxels[ lightmapNum ] == NULL )
2545 for( y = 0; y < lm->sh; y++ )
2547 for( x = 0; x < lm->sw; x++ )
2550 cluster = SUPER_CLUSTER( x, y );
2551 //% if( *cluster < 0 )
2554 /* get particulars */
2555 luxel = SUPER_LUXEL( lightmapNum, x, y );
2556 normal = SUPER_NORMAL ( x, y );
2558 luxel[0]=(normal[0]*127)+127;
2559 luxel[1]=(normal[1]*127)+127;
2560 luxel[2]=(normal[2]*127)+127;
2566 /* -----------------------------------------------------------------
2568 ----------------------------------------------------------------- */
2572 /* walk lightmaps */
2573 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2576 if( lm->superLuxels[ lightmapNum ] == NULL )
2579 /* apply dirt to each luxel */
2580 for( y = 0; y < lm->sh; y++ )
2582 for( x = 0; x < lm->sw; x++ )
2585 cluster = SUPER_CLUSTER( x, y );
2586 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2589 /* get particulars */
2590 luxel = SUPER_LUXEL( lightmapNum, x, y );
2591 dirt = SUPER_DIRT( x, y );
2594 VectorScale( luxel, *dirt, luxel );
2598 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2604 /* -----------------------------------------------------------------
2606 ----------------------------------------------------------------- */
2608 /* walk lightmaps */
2609 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2612 if( lm->superLuxels[ lightmapNum ] == NULL )
2615 /* average occluded luxels from neighbors */
2616 for( y = 0; y < lm->sh; y++ )
2618 for( x = 0; x < lm->sw; x++ )
2620 /* get particulars */
2621 cluster = SUPER_CLUSTER( x, y );
2622 luxel = SUPER_LUXEL( lightmapNum, x, y );
2623 deluxel = SUPER_DELUXEL( x, y );
2624 normal = SUPER_NORMAL( x, y );
2626 /* determine if filtering is necessary */
2627 filterColor = qfalse;
2630 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2631 filterColor = qtrue;
2633 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2636 if( !filterColor && !filterDir )
2639 /* choose seed amount */
2640 VectorClear( averageColor );
2641 VectorClear( averageDir );
2644 /* walk 3x3 matrix */
2645 for( sy = (y - 1); sy <= (y + 1); sy++ )
2647 if( sy < 0 || sy >= lm->sh )
2650 for( sx = (x - 1); sx <= (x + 1); sx++ )
2652 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2655 /* get neighbor's particulars */
2656 cluster2 = SUPER_CLUSTER( sx, sy );
2657 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2658 deluxel2 = SUPER_DELUXEL( sx, sy );
2660 /* ignore unmapped/unlit luxels */
2661 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2662 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2665 /* add its distinctiveness to our own */
2666 VectorAdd( averageColor, luxel2, averageColor );
2667 samples += luxel2[ 3 ];
2669 VectorAdd( averageDir, deluxel2, averageDir );
2674 if( samples <= 0.0f )
2677 /* dark lightmap seams */
2680 if( lightmapNum == 0 )
2681 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2688 VectorDivide( averageColor, samples, luxel );
2692 VectorDivide( averageDir, samples, deluxel );
2694 /* set cluster to -3 */
2696 *cluster = CLUSTER_FLOODED;
2704 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2707 if( lm->superLuxels[ lightmapNum ] == NULL )
2709 for( y = 0; y < lm->sh; y++ )
2710 for( x = 0; x < lm->sw; x++ )
2713 cluster = SUPER_CLUSTER( x, y );
2714 luxel = SUPER_LUXEL( lightmapNum, x, y );
2715 deluxel = SUPER_DELUXEL( x, y );
2716 if(!luxel || !deluxel || !cluster)
2718 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2721 else if(*cluster < 0)
2724 // should have neither deluxemap nor lightmap
2726 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2731 // should have both deluxemap and lightmap
2733 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2743 IlluminateVertexes()
2744 light the surface vertexes
2747 #define VERTEX_NUDGE 4.0f
2749 void IlluminateVertexes( int num )
2751 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2752 int lightmapNum, numAvg;
2753 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2754 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2755 bspDrawSurface_t *ds;
2756 surfaceInfo_t *info;
2758 bspDrawVert_t *verts;
2760 float floodLightAmount;
2764 /* get surface, info, and raw lightmap */
2765 ds = &bspDrawSurfaces[ num ];
2766 info = &surfaceInfos[ num ];
2769 /* -----------------------------------------------------------------
2770 illuminate the vertexes
2771 ----------------------------------------------------------------- */
2773 /* calculate vertex lighting for surfaces without lightmaps */
2774 if( lm == NULL || cpmaHack )
2777 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2778 trace.forceSunlight = info->si->forceSunlight;
2779 trace.recvShadows = info->recvShadows;
2780 trace.numSurfaces = 1;
2781 trace.surfaces = #
2782 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2784 /* twosided lighting */
2785 trace.twoSided = info->si->twoSided;
2787 /* make light list for this surface */
2788 CreateTraceLightsForSurface( num, &trace );
2791 verts = yDrawVerts + ds->firstVert;
2793 memset( avgColors, 0, sizeof( avgColors ) );
2795 /* walk the surface verts */
2796 for( i = 0; i < ds->numVerts; i++ )
2798 /* get vertex luxel */
2799 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2801 /* color the luxel with raw lightmap num? */
2803 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2805 /* color the luxel with luxel origin? */
2806 else if( debugOrigin )
2808 VectorSubtract( info->maxs, info->mins, temp );
2809 VectorScale( temp, (1.0f / 255.0f), temp );
2810 VectorSubtract( origin, lm->mins, temp2 );
2811 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2812 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2813 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2816 /* color the luxel with the normal */
2817 else if( normalmap )
2819 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2820 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2821 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2824 /* illuminate the vertex */
2827 /* clear vertex luxel */
2828 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2830 /* try at initial origin */
2831 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2832 if( trace.cluster >= 0 )
2835 VectorCopy( verts[ i ].xyz, trace.origin );
2836 VectorCopy( verts[ i ].normal, trace.normal );
2839 if( dirty && !bouncing )
2840 dirt = DirtForSample( &trace );
2844 /* jal: floodlight */
2845 floodLightAmount = 0.0f;
2846 VectorClear( floodColor );
2847 if( floodlighty && !bouncing )
2849 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2850 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2854 LightingAtSample( &trace, ds->vertexStyles, colors );
2857 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2860 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2862 /* jal: floodlight */
2863 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2866 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2867 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2868 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2872 /* is this sample bright enough? */
2873 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2874 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2875 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2876 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2878 /* nudge the sample point around a bit */
2879 for( x = 0; x < 5; x++ )
2881 /* two's complement 0, 1, -1, 2, -2, etc */
2882 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2884 for( y = 0; y < 5; y++ )
2886 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2888 for( z = 0; z < 5; z++ )
2890 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2893 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2894 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2895 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2897 /* try at nudged origin */
2898 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2899 if( trace.cluster < 0 )
2903 if( dirty && !bouncing )
2904 dirt = DirtForSample( &trace );
2908 /* jal: floodlight */
2909 floodLightAmount = 0.0f;
2910 VectorClear( floodColor );
2911 if( floodlighty && !bouncing )
2913 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2914 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2918 LightingAtSample( &trace, ds->vertexStyles, colors );
2921 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2924 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2926 /* jal: floodlight */
2927 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2930 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2931 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2934 /* bright enough? */
2935 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2936 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2937 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2938 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2945 /* add to average? */
2946 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2947 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2948 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2949 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2952 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2954 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2955 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2960 /* another happy customer */
2961 numVertsIlluminated++;
2964 /* set average color */
2967 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2968 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2972 VectorCopy( ambientColor, avgColors[ 0 ] );
2975 /* clean up and store vertex color */
2976 for( i = 0; i < ds->numVerts; i++ )
2978 /* get vertex luxel */
2979 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2981 /* store average in occluded vertexes */
2982 if( radVertLuxel[ 0 ] < 0.0f )
2984 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2986 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2987 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2990 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2995 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2998 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2999 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3002 if( bouncing || bounce == 0 || !bounceOnly )
3003 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3004 if( !info->si->noVertexLight )
3005 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3009 /* free light list */
3010 FreeTraceLights( &trace );
3012 /* return to sender */
3016 /* -----------------------------------------------------------------
3017 reconstitute vertex lighting from the luxels
3018 ----------------------------------------------------------------- */
3020 /* set styles from lightmap */
3021 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3022 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3024 /* get max search radius */
3026 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3028 /* walk the surface verts */
3029 verts = yDrawVerts + ds->firstVert;
3030 for( i = 0; i < ds->numVerts; i++ )
3032 /* do each lightmap */
3033 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3036 if( lm->superLuxels[ lightmapNum ] == NULL )
3039 /* get luxel coords */
3040 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3041 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3044 else if( x >= lm->sw )
3048 else if( y >= lm->sh )
3051 /* get vertex luxels */
3052 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3053 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3055 /* color the luxel with the normal? */
3058 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3059 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3060 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3063 /* color the luxel with surface num? */
3064 else if( debugSurfaces )
3065 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3067 /* divine color from the superluxels */
3070 /* increasing radius */
3071 VectorClear( radVertLuxel );
3073 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3075 /* sample within radius */
3076 for( sy = (y - radius); sy <= (y + radius); sy++ )
3078 if( sy < 0 || sy >= lm->sh )
3081 for( sx = (x - radius); sx <= (x + radius); sx++ )
3083 if( sx < 0 || sx >= lm->sw )
3086 /* get luxel particulars */
3087 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3088 cluster = SUPER_CLUSTER( sx, sy );
3092 /* testing: must be brigher than ambient color */
3093 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3096 /* add its distinctiveness to our own */
3097 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3098 samples += luxel[ 3 ];
3104 if( samples > 0.0f )
3105 VectorDivide( radVertLuxel, samples, radVertLuxel );
3107 VectorCopy( ambientColor, radVertLuxel );
3110 /* store into floating point storage */
3111 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3112 numVertsIlluminated++;
3114 /* store into bytes (for vertex approximation) */
3115 if( !info->si->noVertexLight )
3116 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3123 /* -------------------------------------------------------------------------------
3125 light optimization (-fast)
3127 creates a list of lights that will affect a surface and stores it in tw
3128 this is to optimize surface lighting by culling out as many of the
3129 lights in the world as possible from further calculation
3131 ------------------------------------------------------------------------------- */
3135 determines opaque brushes in the world and find sky shaders for sunlight calculations
3138 void SetupBrushes( void )
3140 int i, j, b, compileFlags;
3143 bspBrushSide_t *side;
3144 bspShader_t *shader;
3149 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3152 if( opaqueBrushes == NULL )
3153 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3156 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3157 numOpaqueBrushes = 0;
3159 /* walk the list of worldspawn brushes */
3160 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3163 b = bspModels[ 0 ].firstBSPBrush + i;
3164 brush = &bspBrushes[ b ];
3166 /* check all sides */
3169 for( j = 0; j < brush->numSides && inside; j++ )
3171 /* do bsp shader calculations */
3172 side = &bspBrushSides[ brush->firstSide + j ];
3173 shader = &bspShaders[ side->shaderNum ];
3175 /* get shader info */
3176 si = ShaderInfoForShader( shader->shader );
3180 /* or together compile flags */
3181 compileFlags |= si->compileFlags;
3184 /* determine if this brush is opaque to light */
3185 if( !(compileFlags & C_TRANSLUCENT) )
3187 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3193 /* emit some statistics */
3194 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3201 determines if two clusters are visible to each other using the PVS
3204 qboolean ClusterVisible( int a, int b )
3206 int portalClusters, leafBytes;
3211 if( a < 0 || b < 0 )
3219 if( numBSPVisBytes <=8 )
3223 portalClusters = ((int *) bspVisBytes)[ 0 ];
3224 leafBytes = ((int*) bspVisBytes)[ 1 ];
3225 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3228 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3237 borrowed from vlight.c
3240 int PointInLeafNum_r( vec3_t point, int nodenum )
3248 while( nodenum >= 0 )
3250 node = &bspNodes[ nodenum ];
3251 plane = &bspPlanes[ node->planeNum ];
3252 dist = DotProduct( point, plane->normal ) - plane->dist;
3254 nodenum = node->children[ 0 ];
3255 else if( dist < -0.1 )
3256 nodenum = node->children[ 1 ];
3259 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3260 if( bspLeafs[ leafnum ].cluster != -1 )
3262 nodenum = node->children[ 1 ];
3266 leafnum = -nodenum - 1;
3274 borrowed from vlight.c
3277 int PointInLeafNum( vec3_t point )
3279 return PointInLeafNum_r( point, 0 );
3285 ClusterVisibleToPoint() - ydnar
3286 returns qtrue if point can "see" cluster
3289 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3294 /* get leafNum for point */
3295 pointCluster = ClusterForPoint( point );
3296 if( pointCluster < 0 )
3300 return ClusterVisible( pointCluster, cluster );
3306 ClusterForPoint() - ydnar
3307 returns the pvs cluster for point
3310 int ClusterForPoint( vec3_t point )
3315 /* get leafNum for point */
3316 leafNum = PointInLeafNum( point );
3320 /* return the cluster */
3321 return bspLeafs[ leafNum ].cluster;
3327 ClusterForPointExt() - ydnar
3328 also takes brushes into account for occlusion testing
3331 int ClusterForPointExt( vec3_t point, float epsilon )
3333 int i, j, b, leafNum, cluster;
3336 int *brushes, numBSPBrushes;
3342 /* get leaf for point */
3343 leafNum = PointInLeafNum( point );
3346 leaf = &bspLeafs[ leafNum ];
3348 /* get the cluster */
3349 cluster = leaf->cluster;
3353 /* transparent leaf, so check point against all brushes in the leaf */
3354 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3355 numBSPBrushes = leaf->numBSPLeafBrushes;
3356 for( i = 0; i < numBSPBrushes; i++ )
3360 if( b > maxOpaqueBrush )
3362 brush = &bspBrushes[ b ];
3363 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3366 /* check point against all planes */
3368 for( j = 0; j < brush->numSides && inside; j++ )
3370 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3371 dot = DotProduct( point, plane->normal );
3377 /* if inside, return bogus cluster */
3382 /* if the point made it this far, it's not inside any opaque brushes */
3389 ClusterForPointExtFilter() - ydnar
3390 adds cluster checking against a list of known valid clusters
3393 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3398 /* get cluster for point */
3399 cluster = ClusterForPointExt( point, epsilon );
3401 /* check if filtering is necessary */
3402 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3406 for( i = 0; i < numClusters; i++ )
3408 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3419 ShaderForPointInLeaf() - ydnar
3420 checks a point against all brushes in a leaf, returning the shader of the brush
3421 also sets the cumulative surface and content flags for the brush hit
3424 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3429 int *brushes, numBSPBrushes;
3432 bspBrushSide_t *side;
3434 bspShader_t *shader;
3435 int allSurfaceFlags, allContentFlags;
3438 /* clear things out first */
3445 leaf = &bspLeafs[ leafNum ];
3447 /* transparent leaf, so check point against all brushes in the leaf */
3448 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3449 numBSPBrushes = leaf->numBSPLeafBrushes;
3450 for( i = 0; i < numBSPBrushes; i++ )
3453 brush = &bspBrushes[ brushes[ i ] ];
3455 /* check point against all planes */
3457 allSurfaceFlags = 0;
3458 allContentFlags = 0;
3459 for( j = 0; j < brush->numSides && inside; j++ )
3461 side = &bspBrushSides[ brush->firstSide + j ];
3462 plane = &bspPlanes[ side->planeNum ];
3463 dot = DotProduct( point, plane->normal );
3469 shader = &bspShaders[ side->shaderNum ];
3470 allSurfaceFlags |= shader->surfaceFlags;
3471 allContentFlags |= shader->contentFlags;
3475 /* handle if inside */
3478 /* if there are desired flags, check for same and continue if they aren't matched */
3479 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3481 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3484 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3485 *surfaceFlags = allSurfaceFlags;
3486 *contentFlags = allContentFlags;
3487 return brush->shaderNum;
3491 /* if the point made it this far, it's not inside any brushes */
3499 chops a bounding box by the plane defined by origin and normal
3500 returns qfalse if the bounds is entirely clipped away
3502 this is not exactly the fastest way to do this...
3505 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3507 /* FIXME: rewrite this so it doesn't use bloody brushes */
3515 calculates each light's effective envelope,
3516 taking into account brightness, type, and pvs.
3519 #define LIGHT_EPSILON 0.125f
3520 #define LIGHT_NUDGE 2.0f
3522 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3524 int i, x, y, z, x1, y1, z1;
3525 light_t *light, *light2, **owner;
3527 vec3_t origin, dir, mins, maxs;
3528 float radius, intensity;
3529 light_t *buckets[ 256 ];
3532 /* early out for weird cases where there are no lights */
3533 if( lights == NULL )
3537 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3541 numCulledLights = 0;
3543 while( *owner != NULL )
3548 /* handle negative lights */
3549 if( light->photons < 0.0f || light->add < 0.0f )
3551 light->photons *= -1.0f;
3552 light->add *= -1.0f;
3553 light->flags |= LIGHT_NEGATIVE;
3557 if( light->type == EMIT_SUN )
3561 light->envelope = MAX_WORLD_COORD * 8.0f;
3562 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3563 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3566 /* everything else */
3569 /* get pvs cluster for light */
3570 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3572 /* invalid cluster? */
3573 if( light->cluster < 0 )
3575 /* nudge the sample point around a bit */
3576 for( x = 0; x < 4; x++ )
3578 /* two's complement 0, 1, -1, 2, -2, etc */
3579 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3581 for( y = 0; y < 4; y++ )
3583 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3585 for( z = 0; z < 4; z++ )
3587 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3590 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3591 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3592 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3594 /* try at nudged origin */
3595 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3596 if( light->cluster < 0 )
3600 VectorCopy( origin, light->origin );
3606 /* only calculate for lights in pvs and outside of opaque brushes */
3607 if( light->cluster >= 0 )
3609 /* set light fast flag */
3611 light->flags |= LIGHT_FAST_TEMP;
3613 light->flags &= ~LIGHT_FAST_TEMP;
3614 if( light->si && light->si->noFast )
3615 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3617 /* clear light envelope */
3618 light->envelope = 0;
3620 /* handle area lights */
3621 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3623 /* ugly hack to calculate extent for area lights, but only done once */
3624 VectorScale( light->normal, -1.0f, dir );
3625 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3629 VectorMA( light->origin, radius, light->normal, origin );
3630 factor = PointToPolygonFormFactor( origin, dir, light->w );
3633 if( (factor * light->add) <= light->falloffTolerance )
3634 light->envelope = radius;
3637 /* check for fast mode */
3638 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3639 light->envelope = MAX_WORLD_COORD * 8.0f;
3640 intensity = light->photons; /* hopefully not used */
3645 intensity = light->photons;
3649 if( light->envelope <= 0.0f )
3651 /* solve distance for non-distance lights */
3652 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3653 light->envelope = MAX_WORLD_COORD * 8.0f;
3655 /* solve distance for linear lights */
3656 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3657 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3658 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3661 add = angle * light->photons * linearScale - (dist * light->fade);
3662 T = (light->photons * linearScale) - (dist * light->fade);
3663 T + (dist * light->fade) = (light->photons * linearScale);
3664 dist * light->fade = (light->photons * linearScale) - T;
3665 dist = ((light->photons * linearScale) - T) / light->fade;
3668 /* solve for inverse square falloff */
3670 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3673 add = light->photons / (dist * dist);
3674 T = light->photons / (dist * dist);
3675 T * (dist * dist) = light->photons;
3676 dist = sqrt( light->photons / T );
3680 /* chop radius against pvs */
3683 ClearBounds( mins, maxs );
3685 /* check all leaves */
3686 for( i = 0; i < numBSPLeafs; i++ )
3689 leaf = &bspLeafs[ i ];
3692 if( leaf->cluster < 0 )
3694 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3697 /* add this leafs bbox to the bounds */
3698 VectorCopy( leaf->mins, origin );
3699 AddPointToBounds( origin, mins, maxs );
3700 VectorCopy( leaf->maxs, origin );
3701 AddPointToBounds( origin, mins, maxs );
3704 /* test to see if bounds encompass light */
3705 for( i = 0; i < 3; i++ )
3707 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3709 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3710 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3711 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3712 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3713 AddPointToBounds( light->origin, mins, maxs );
3717 /* chop the bounds by a plane for area lights and spotlights */
3718 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3719 ChopBounds( mins, maxs, light->origin, light->normal );
3722 VectorCopy( mins, light->mins );
3723 VectorCopy( maxs, light->maxs );
3725 /* reflect bounds around light origin */
3726 //% VectorMA( light->origin, -1.0f, origin, origin );
3727 VectorScale( light->origin, 2, origin );
3728 VectorSubtract( origin, maxs, origin );
3729 AddPointToBounds( origin, mins, maxs );
3730 //% VectorMA( light->origin, -1.0f, mins, origin );
3731 VectorScale( light->origin, 2, origin );
3732 VectorSubtract( origin, mins, origin );
3733 AddPointToBounds( origin, mins, maxs );
3735 /* calculate spherical bounds */
3736 VectorSubtract( maxs, light->origin, dir );
3737 radius = (float) VectorLength( dir );
3739 /* if this radius is smaller than the envelope, then set the envelope to it */
3740 if( radius < light->envelope )
3742 light->envelope = radius;
3743 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3746 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3749 /* add grid/surface only check */
3752 if( !(light->flags & LIGHT_GRID) )
3753 light->envelope = 0.0f;
3757 if( !(light->flags & LIGHT_SURFACES) )
3758 light->envelope = 0.0f;
3763 if( light->cluster < 0 || light->envelope <= 0.0f )
3766 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3768 /* delete the light */
3770 *owner = light->next;
3771 if( light->w != NULL )
3778 /* square envelope */
3779 light->envelope2 = (light->envelope * light->envelope);
3781 /* increment light count */
3784 /* set next light */
3785 owner = &((**owner).next);
3788 /* bucket sort lights by style */
3789 memset( buckets, 0, sizeof( buckets ) );
3791 for( light = lights; light != NULL; light = light2 )
3793 /* get next light */
3794 light2 = light->next;
3796 /* filter into correct bucket */
3797 light->next = buckets[ light->style ];
3798 buckets[ light->style ] = light;
3800 /* if any styled light is present, automatically set nocollapse */
3801 if( light->style != LS_NORMAL )
3805 /* filter back into light list */
3807 for( i = 255; i >= 0; i-- )
3810 for( light = buckets[ i ]; light != NULL; light = light2 )
3812 light2 = light->next;
3813 light->next = lights;
3818 /* emit some statistics */
3819 Sys_Printf( "%9d total lights\n", numLights );
3820 Sys_Printf( "%9d culled lights\n", numCulledLights );
3826 CreateTraceLightsForBounds()
3827 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3830 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3834 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3835 float radius, dist, length;
3838 /* potential pre-setup */
3839 if( numLights == 0 )
3840 SetupEnvelopes( qfalse, fast );
3843 //% 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 ] );
3845 /* allocate the light list */
3846 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3847 trace->numLights = 0;
3849 /* calculate spherical bounds */
3850 VectorAdd( mins, maxs, origin );
3851 VectorScale( origin, 0.5f, origin );
3852 VectorSubtract( maxs, origin, dir );
3853 radius = (float) VectorLength( dir );
3855 /* get length of normal vector */
3856 if( normal != NULL )
3857 length = VectorLength( normal );
3860 normal = nullVector;
3864 /* test each light and see if it reaches the sphere */
3865 /* note: the attenuation code MUST match LightingAtSample() */
3866 for( light = lights; light; light = light->next )
3868 /* check zero sized envelope */
3869 if( light->envelope <= 0 )
3871 lightsEnvelopeCulled++;
3876 if( !(light->flags & flags) )
3879 /* sunlight skips all this nonsense */
3880 if( light->type != EMIT_SUN )
3886 /* check against pvs cluster */
3887 if( numClusters > 0 && clusters != NULL )
3889 for( i = 0; i < numClusters; i++ )
3891 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3896 if( i == numClusters )
3898 lightsClusterCulled++;
3903 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3904 VectorSubtract( light->origin, origin, dir );
3905 dist = VectorLength( dir );
3906 dist -= light->envelope;
3910 lightsEnvelopeCulled++;
3914 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3917 for( i = 0; i < 3; i++ )
3919 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3924 lightsBoundsCulled++;
3930 /* planar surfaces (except twosided surfaces) have a couple more checks */
3931 if( length > 0.0f && trace->twoSided == qfalse )
3933 /* lights coplanar with a surface won't light it */
3934 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3936 lightsPlaneCulled++;
3940 /* check to see if light is behind the plane */
3941 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3943 lightsPlaneCulled++;
3948 /* add this light */
3949 trace->lights[ trace->numLights++ ] = light;
3952 /* make last night null */
3953 trace->lights[ trace->numLights ] = NULL;
3958 void FreeTraceLights( trace_t *trace )
3960 if( trace->lights != NULL )
3961 free( trace->lights );
3967 CreateTraceLightsForSurface()
3968 creates a list of lights that can potentially affect a drawsurface
3971 void CreateTraceLightsForSurface( int num, trace_t *trace )
3974 vec3_t mins, maxs, normal;
3976 bspDrawSurface_t *ds;
3977 surfaceInfo_t *info;
3984 /* get drawsurface and info */
3985 ds = &bspDrawSurfaces[ num ];
3986 info = &surfaceInfos[ num ];
3988 /* get the mins/maxs for the dsurf */
3989 ClearBounds( mins, maxs );
3990 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3991 for( i = 0; i < ds->numVerts; i++ )
3993 dv = &yDrawVerts[ ds->firstVert + i ];
3994 AddPointToBounds( dv->xyz, mins, maxs );
3995 if( !VectorCompare( dv->normal, normal ) )
3996 VectorClear( normal );
3999 /* create the lights for the bounding box */
4000 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4003 /////////////////////////////////////////////////////////////
4005 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4006 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4007 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4008 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4010 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4011 static int numFloodVectors = 0;
4013 void SetupFloodLight( void )
4016 float angle, elevation, angleStep, elevationStep;
4018 double v1,v2,v3,v4,v5;
4021 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4023 /* calculate angular steps */
4024 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4025 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4029 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4031 /* iterate elevation */
4032 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4034 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4035 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4036 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4041 /* emit some statistics */
4042 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4045 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4047 if( value[ 0 ] != '\0' )
4050 v4=floodlightDistance;
4051 v5=floodlightIntensity;
4053 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
4055 floodlightRGB[0]=v1;
4056 floodlightRGB[1]=v2;
4057 floodlightRGB[2]=v3;
4059 if (VectorLength(floodlightRGB)==0)
4061 VectorSet(floodlightRGB,240,240,255);
4067 floodlightDistance=v4;
4068 floodlightIntensity=v5;
4070 floodlighty = qtrue;
4071 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4075 VectorSet(floodlightRGB,240,240,255);
4076 //floodlighty = qtrue;
4077 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4079 VectorNormalize(floodlightRGB,floodlightRGB);
4083 FloodLightForSample()
4084 calculates floodlight value for a given sample
4085 once again, kudos to the dirtmapping coder
4088 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4094 float gatherLight, outLight;
4095 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4103 if( trace == NULL || trace->cluster < 0 )
4108 dd = floodLightDistance;
4109 VectorCopy( trace->normal, normal );
4111 /* check if the normal is aligned to the world-up */
4112 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4114 if( normal[ 2 ] == 1.0f )
4116 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4117 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4119 else if( normal[ 2 ] == -1.0f )
4121 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4122 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4127 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4128 CrossProduct( normal, worldUp, myRt );
4129 VectorNormalize( myRt, myRt );
4130 CrossProduct( myRt, normal, myUp );
4131 VectorNormalize( myUp, myUp );
4134 /* vortex: optimise floodLightLowQuality a bit */
4135 if ( floodLightLowQuality == qtrue )
4137 /* iterate through ordered vectors */
4138 for( i = 0; i < numFloodVectors; i++ )
4139 if (rand()%10 != 0 ) continue;
4143 /* iterate through ordered vectors */
4144 for( i = 0; i < numFloodVectors; i++ )
4148 /* transform vector into tangent space */
4149 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4150 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4151 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4154 VectorMA( trace->origin, dd, direction, trace->end );
4156 //VectorMA( trace->origin, 1, direction, trace->origin );
4158 SetupTrace( trace );
4163 if (trace->compileFlags & C_SKY )
4167 else if ( trace->opaque )
4169 VectorSubtract( trace->hit, trace->origin, displacement );
4170 d=VectorLength( displacement );
4172 // d=trace->distance;
4173 //if (d>256) gatherDirt+=1;
4175 if (contribution>1) contribution=1.0f;
4177 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4180 gatherLight+=contribution;
4185 if( gatherLight <= 0.0f )
4193 outLight=gatherLight;
4194 if( outLight > 1.0f )
4197 /* return to sender */
4202 FloodLightRawLightmap
4203 lighttracer style ambient occlusion light hack.
4204 Kudos to the dirtmapping author for most of this source.
4205 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4206 VorteX: fixed problems with deluxemapping
4209 // floodlight pass on a lightmap
4210 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4212 int i, x, y, *cluster;
4213 float *origin, *normal, *floodlight, floodLightAmount;
4214 surfaceInfo_t *info;
4217 // float samples, average, *floodlight2;
4219 memset(&trace,0,sizeof(trace_t));
4222 trace.testOcclusion = qtrue;
4223 trace.forceSunlight = qfalse;
4224 trace.twoSided = qtrue;
4225 trace.recvShadows = lm->recvShadows;
4226 trace.numSurfaces = lm->numLightSurfaces;
4227 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4228 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4229 trace.testAll = qfalse;
4230 trace.distance = 1024;
4232 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4233 //trace.twoSided = qfalse;
4234 for( i = 0; i < trace.numSurfaces; i++ )
4237 info = &surfaceInfos[ trace.surfaces[ i ] ];
4239 /* check twosidedness */
4240 if( info->si->twoSided )
4242 trace.twoSided = qtrue;
4247 /* gather floodlight */
4248 for( y = 0; y < lm->sh; y++ )
4250 for( x = 0; x < lm->sw; x++ )
4253 cluster = SUPER_CLUSTER( x, y );
4254 origin = SUPER_ORIGIN( x, y );
4255 normal = SUPER_NORMAL( x, y );
4256 floodlight = SUPER_FLOODLIGHT( x, y );
4258 /* set default dirt */
4261 /* only look at mapped luxels */
4266 trace.cluster = *cluster;
4267 VectorCopy( origin, trace.origin );
4268 VectorCopy( normal, trace.normal );
4270 /* get floodlight */
4271 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4273 /* add floodlight */
4274 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4275 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4276 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4277 floodlight[3] += floodlightDirectionScale;
4281 /* testing no filtering */
4287 for( y = 0; y < lm->sh; y++ )
4289 for( x = 0; x < lm->sw; x++ )
4292 cluster = SUPER_CLUSTER( x, y );
4293 floodlight = SUPER_FLOODLIGHT(x, y );
4295 /* filter dirt by adjacency to unmapped luxels */
4296 average = *floodlight;
4298 for( sy = (y - 1); sy <= (y + 1); sy++ )
4300 if( sy < 0 || sy >= lm->sh )
4303 for( sx = (x - 1); sx <= (x + 1); sx++ )
4305 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4308 /* get neighboring luxel */
4309 cluster = SUPER_CLUSTER( sx, sy );
4310 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4311 if( *cluster < 0 || *floodlight2 <= 0.0f )
4315 average += *floodlight2;
4320 if( samples <= 0.0f )
4325 if( samples <= 0.0f )
4329 *floodlight = average / samples;
4335 void FloodLightRawLightmap( int rawLightmapNum )
4339 /* bail if this number exceeds the number of raw lightmaps */
4340 if( rawLightmapNum >= numRawLightmaps )
4343 lm = &rawLightmaps[ rawLightmapNum ];
4346 if (floodlighty && floodlightIntensity)
4347 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4350 if (lm->floodlightIntensity)
4352 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4353 numSurfacesFloodlighten += 1;
4357 void FloodlightRawLightmaps()
4359 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4360 numSurfacesFloodlighten = 0;
4361 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4362 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4366 FloodLightIlluminate()
4367 illuminate floodlight into lightmap luxels
4370 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4372 float *luxel, *floodlight, *deluxel, *normal;
4375 int x, y, lightmapNum;
4377 /* walk lightmaps */
4378 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4381 if( lm->superLuxels[ lightmapNum ] == NULL )
4384 /* apply floodlight to each luxel */
4385 for( y = 0; y < lm->sh; y++ )
4387 for( x = 0; x < lm->sw; x++ )
4389 /* get floodlight */
4390 floodlight = SUPER_FLOODLIGHT( x, y );
4391 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4395 cluster = SUPER_CLUSTER( x, y );
4397 /* only process mapped luxels */
4401 /* get particulars */
4402 luxel = SUPER_LUXEL( lightmapNum, x, y );
4403 deluxel = SUPER_DELUXEL( x, y );
4405 /* add to lightmap */
4406 luxel[0]+=floodlight[0];
4407 luxel[1]+=floodlight[1];
4408 luxel[2]+=floodlight[2];
4410 if (luxel[3]==0) luxel[3]=1;
4412 /* add to deluxemap */
4413 if (deluxemap && floodlight[3] > 0)
4417 normal = SUPER_NORMAL( x, y );
4418 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4420 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4421 if(brightness < 0.00390625f)
4422 brightness = 0.00390625f;
4424 VectorScale( normal, brightness, lightvector );
4425 VectorAdd( deluxel, lightvector, deluxel );