1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1159 /* get drawverts and map first triangle */
1160 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1161 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1162 MapTriangle( lm, info, dv, mapNonAxial );
1164 /* get drawverts and map second triangle */
1165 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1166 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1167 MapTriangle( lm, info, dv, mapNonAxial );
1183 /* -----------------------------------------------------------------
1184 average and clean up luxel normals
1185 ----------------------------------------------------------------- */
1187 /* walk the luxels */
1188 for( y = 0; y < lm->sh; y++ )
1190 for( x = 0; x < lm->sw; x++ )
1193 luxel = SUPER_LUXEL( 0, x, y );
1194 normal = SUPER_NORMAL( x, y );
1195 cluster = SUPER_CLUSTER( x, y );
1197 /* only look at mapped luxels */
1201 /* the normal data could be the sum of multiple samples */
1202 if( luxel[ 3 ] > 1.0f )
1203 VectorNormalize( normal, normal );
1205 /* mark this luxel as having only one normal */
1210 /* non-planar surfaces stop here */
1211 if( lm->plane == NULL )
1214 /* -----------------------------------------------------------------
1215 map occluded or unuxed luxels
1216 ----------------------------------------------------------------- */
1218 /* walk the luxels */
1219 radius = floor( superSample / 2 );
1220 radius = radius > 0 ? radius : 1.0f;
1222 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1224 for( y = 0; y < lm->sh; y++ )
1226 for( x = 0; x < lm->sw; x++ )
1229 luxel = SUPER_LUXEL( 0, x, y );
1230 normal = SUPER_NORMAL( x, y );
1231 cluster = SUPER_CLUSTER( x, y );
1233 /* only look at unmapped luxels */
1234 if( *cluster != CLUSTER_UNMAPPED )
1237 /* divine a normal and origin from neighboring luxels */
1238 VectorClear( fake.xyz );
1239 VectorClear( fake.normal );
1240 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1241 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1243 for( sy = (y - 1); sy <= (y + 1); sy++ )
1245 if( sy < 0 || sy >= lm->sh )
1248 for( sx = (x - 1); sx <= (x + 1); sx++ )
1250 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1253 /* get neighboring luxel */
1254 luxel = SUPER_LUXEL( 0, sx, sy );
1255 origin = SUPER_ORIGIN( sx, sy );
1256 normal = SUPER_NORMAL( sx, sy );
1257 cluster = SUPER_CLUSTER( sx, sy );
1259 /* only consider luxels mapped in previous passes */
1260 if( *cluster < 0 || luxel[ 0 ] >= pass )
1263 /* add its distinctiveness to our own */
1264 VectorAdd( fake.xyz, origin, fake.xyz );
1265 VectorAdd( fake.normal, normal, fake.normal );
1266 samples += luxel[ 3 ];
1271 if( samples == 0.0f )
1275 VectorDivide( fake.xyz, samples, fake.xyz );
1276 //% VectorDivide( fake.normal, samples, fake.normal );
1277 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1280 /* map the fake vert */
1281 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1286 /* -----------------------------------------------------------------
1287 average and clean up luxel normals
1288 ----------------------------------------------------------------- */
1290 /* walk the luxels */
1291 for( y = 0; y < lm->sh; y++ )
1293 for( x = 0; x < lm->sw; x++ )
1296 luxel = SUPER_LUXEL( 0, x, y );
1297 normal = SUPER_NORMAL( x, y );
1298 cluster = SUPER_CLUSTER( x, y );
1300 /* only look at mapped luxels */
1304 /* the normal data could be the sum of multiple samples */
1305 if( luxel[ 3 ] > 1.0f )
1306 VectorNormalize( normal, normal );
1308 /* mark this luxel as having only one normal */
1316 for( y = 0; y < lm->sh; y++ )
1318 for( x = 0; x < lm->sw; x++ )
1323 cluster = SUPER_CLUSTER( x, y );
1324 origin = SUPER_ORIGIN( x, y );
1325 normal = SUPER_NORMAL( x, y );
1326 luxel = SUPER_LUXEL( x, y );
1331 /* check if within the bounding boxes of all surfaces referenced */
1332 ClearBounds( mins, maxs );
1333 for( n = 0; n < lm->numLightSurfaces; n++ )
1336 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1337 TOL = info->sampleSize + 2;
1338 AddPointToBounds( info->mins, mins, maxs );
1339 AddPointToBounds( info->maxs, mins, maxs );
1340 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1341 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1342 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1347 if( n < lm->numLightSurfaces )
1350 /* report bogus origin */
1351 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1352 rawLightmapNum, x, y, *cluster,
1353 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1354 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1355 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1366 sets up dirtmap (ambient occlusion)
1369 #define DIRT_CONE_ANGLE 88 /* degrees */
1370 #define DIRT_NUM_ANGLE_STEPS 16
1371 #define DIRT_NUM_ELEVATION_STEPS 3
1372 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1374 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1375 static int numDirtVectors = 0;
1377 void SetupDirt( void )
1380 float angle, elevation, angleStep, elevationStep;
1384 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1386 /* calculate angular steps */
1387 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1388 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1392 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1394 /* iterate elevation */
1395 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1397 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1398 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1399 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1404 /* emit some statistics */
1405 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1411 calculates dirt value for a given sample
1414 float DirtForSample( trace_t *trace )
1417 float gatherDirt, outDirt, angle, elevation, ooDepth;
1418 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1424 if( trace == NULL || trace->cluster < 0 )
1429 ooDepth = 1.0f / dirtDepth;
1430 VectorCopy( trace->normal, normal );
1432 /* check if the normal is aligned to the world-up */
1433 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1435 if( normal[ 2 ] == 1.0f )
1437 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1440 else if( normal[ 2 ] == -1.0f )
1442 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1443 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1448 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1449 CrossProduct( normal, worldUp, myRt );
1450 VectorNormalize( myRt, myRt );
1451 CrossProduct( myRt, normal, myUp );
1452 VectorNormalize( myUp, myUp );
1455 /* 1 = random mode, 0 (well everything else) = non-random mode */
1459 for( i = 0; i < numDirtVectors; i++ )
1461 /* get random vector */
1462 angle = Random() * DEG2RAD( 360.0f );
1463 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1464 temp[ 0 ] = cos( angle ) * sin( elevation );
1465 temp[ 1 ] = sin( angle ) * sin( elevation );
1466 temp[ 2 ] = cos( elevation );
1468 /* transform into tangent space */
1469 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1470 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1471 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1474 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1475 SetupTrace( trace );
1481 VectorSubtract( trace->hit, trace->origin, displacement );
1482 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1488 /* iterate through ordered vectors */
1489 for( i = 0; i < numDirtVectors; i++ )
1491 /* transform vector into tangent space */
1492 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1493 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1494 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1497 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1498 SetupTrace( trace );
1504 VectorSubtract( trace->hit, trace->origin, displacement );
1505 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1511 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1512 SetupTrace( trace );
1518 VectorSubtract( trace->hit, trace->origin, displacement );
1519 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1523 if( gatherDirt <= 0.0f )
1526 /* apply gain (does this even do much? heh) */
1527 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1528 if( outDirt > 1.0f )
1532 outDirt *= dirtScale;
1533 if( outDirt > 1.0f )
1536 /* return to sender */
1537 return 1.0f - outDirt;
1544 calculates dirty fraction for each luxel
1547 void DirtyRawLightmap( int rawLightmapNum )
1549 int i, x, y, sx, sy, *cluster;
1550 float *origin, *normal, *dirt, *dirt2, average, samples;
1552 surfaceInfo_t *info;
1557 /* bail if this number exceeds the number of raw lightmaps */
1558 if( rawLightmapNum >= numRawLightmaps )
1562 lm = &rawLightmaps[ rawLightmapNum ];
1565 trace.testOcclusion = qtrue;
1566 trace.forceSunlight = qfalse;
1567 trace.recvShadows = lm->recvShadows;
1568 trace.numSurfaces = lm->numLightSurfaces;
1569 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1570 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1571 trace.testAll = qtrue;
1573 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1574 trace.twoSided = qfalse;
1575 for( i = 0; i < trace.numSurfaces; i++ )
1578 info = &surfaceInfos[ trace.surfaces[ i ] ];
1580 /* check twosidedness */
1581 if( info->si->twoSided )
1583 trace.twoSided = qtrue;
1589 for( i = 0; i < trace.numSurfaces; i++ )
1592 info = &surfaceInfos[ trace.surfaces[ i ] ];
1594 /* check twosidedness */
1595 if( info->si->noDirty )
1603 for( y = 0; y < lm->sh; y++ )
1605 for( x = 0; x < lm->sw; x++ )
1608 cluster = SUPER_CLUSTER( x, y );
1609 origin = SUPER_ORIGIN( x, y );
1610 normal = SUPER_NORMAL( x, y );
1611 dirt = SUPER_DIRT( x, y );
1613 /* set default dirt */
1616 /* only look at mapped luxels */
1620 /* don't apply dirty on this surface */
1628 trace.cluster = *cluster;
1629 VectorCopy( origin, trace.origin );
1630 VectorCopy( normal, trace.normal );
1633 *dirt = DirtForSample( &trace );
1637 /* testing no filtering */
1641 for( y = 0; y < lm->sh; y++ )
1643 for( x = 0; x < lm->sw; x++ )
1646 cluster = SUPER_CLUSTER( x, y );
1647 dirt = SUPER_DIRT( x, y );
1649 /* filter dirt by adjacency to unmapped luxels */
1652 for( sy = (y - 1); sy <= (y + 1); sy++ )
1654 if( sy < 0 || sy >= lm->sh )
1657 for( sx = (x - 1); sx <= (x + 1); sx++ )
1659 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1662 /* get neighboring luxel */
1663 cluster = SUPER_CLUSTER( sx, sy );
1664 dirt2 = SUPER_DIRT( sx, sy );
1665 if( *cluster < 0 || *dirt2 <= 0.0f )
1674 if( samples <= 0.0f )
1679 if( samples <= 0.0f )
1683 *dirt = average / samples;
1692 calculates the pvs cluster, origin, normal of a sub-luxel
1695 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1697 int i, *cluster, *cluster2;
1698 float *origin, *origin2, *normal; //% , *normal2;
1699 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1702 /* calulate x vector */
1703 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1705 cluster = SUPER_CLUSTER( x, y );
1706 origin = SUPER_ORIGIN( x, y );
1707 //% normal = SUPER_NORMAL( x, y );
1708 cluster2 = SUPER_CLUSTER( x + 1, y );
1709 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1710 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1712 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1714 cluster = SUPER_CLUSTER( x - 1, y );
1715 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1716 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1717 cluster2 = SUPER_CLUSTER( x, y );
1718 origin2 = SUPER_ORIGIN( x, y );
1719 //% normal2 = SUPER_NORMAL( x, y );
1723 Error( "Spurious lightmap S vector\n" );
1726 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1727 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1729 /* calulate y vector */
1730 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1732 cluster = SUPER_CLUSTER( x, y );
1733 origin = SUPER_ORIGIN( x, y );
1734 //% normal = SUPER_NORMAL( x, y );
1735 cluster2 = SUPER_CLUSTER( x, y + 1 );
1736 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1737 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1739 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1741 cluster = SUPER_CLUSTER( x, y - 1 );
1742 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1743 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1744 cluster2 = SUPER_CLUSTER( x, y );
1745 origin2 = SUPER_ORIGIN( x, y );
1746 //% normal2 = SUPER_NORMAL( x, y );
1749 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1751 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1752 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1754 /* calculate new origin */
1755 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1756 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1757 for( i = 0; i < 3; i++ )
1758 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1761 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1762 if( *sampleCluster < 0 )
1765 /* calculate new normal */
1766 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1767 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1768 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1770 normal = SUPER_NORMAL( x, y );
1771 VectorCopy( normal, sampleNormal );
1779 SubsampleRawLuxel_r()
1780 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1783 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1785 int b, samples, mapped, lighted;
1788 vec3_t deluxel[ 3 ];
1789 vec3_t origin[ 4 ], normal[ 4 ];
1790 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1791 vec3_t color, direction, total;
1795 if( lightLuxel[ 3 ] >= lightSamples )
1799 VectorClear( total );
1803 /* make 2x2 subsample stamp */
1804 for( b = 0; b < 4; b++ )
1807 VectorCopy( sampleOrigin, origin[ b ] );
1809 /* calculate position */
1810 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1817 /* increment sample count */
1818 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1821 trace->cluster = *cluster;
1822 VectorCopy( origin[ b ], trace->origin );
1823 VectorCopy( normal[ b ], trace->normal );
1827 LightContributionToSample( trace );
1828 if(trace->forceSubsampling > 1.0f)
1830 /* alphashadow: we subsample as deep as we can */
1836 /* add to totals (fixme: make contrast function) */
1837 VectorCopy( trace->color, luxel[ b ] );
1840 VectorCopy( trace->directionContribution, deluxel[ b ] );
1842 VectorAdd( total, trace->color, total );
1843 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1847 /* subsample further? */
1848 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1849 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1850 lighted != 0 && lighted != mapped )
1852 for( b = 0; b < 4; b++ )
1854 if( cluster[ b ] < 0 )
1856 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1861 //% VectorClear( color );
1863 VectorCopy( lightLuxel, color );
1864 VectorCopy( lightDeluxel, direction );
1866 for( b = 0; b < 4; b++ )
1868 if( cluster[ b ] < 0 )
1870 VectorAdd( color, luxel[ b ], color );
1873 VectorAdd( direction, deluxel[ b ], direction );
1882 color[ 0 ] /= samples;
1883 color[ 1 ] /= samples;
1884 color[ 2 ] /= samples;
1887 VectorCopy( color, lightLuxel );
1888 lightLuxel[ 3 ] += 1.0f;
1892 direction[ 0 ] /= samples;
1893 direction[ 1 ] /= samples;
1894 direction[ 2 ] /= samples;
1895 VectorCopy( direction, lightDeluxel );
1900 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1901 static void GaussLikeRandom(float sigma, float *x, float *y)
1904 r = Random() * 2 * Q_PI;
1905 *x = sigma * 2.73861278752581783822 * cos(r);
1906 *y = sigma * 2.73861278752581783822 * sin(r);
1913 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1917 vec3_t origin, normal;
1918 vec3_t total, totaldirection;
1921 VectorClear( total );
1922 VectorClear( totaldirection );
1924 for(b = 0; b < lightSamples; ++b)
1927 VectorCopy( sampleOrigin, origin );
1928 GaussLikeRandom(bias, &dx, &dy);
1931 if(dx < -1) dx = -1;
1932 if(dy < -1) dy = -1;
1934 /* calculate position */
1935 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1942 trace->cluster = cluster;
1943 VectorCopy( origin, trace->origin );
1944 VectorCopy( normal, trace->normal );
1946 LightContributionToSample( trace );
1947 VectorAdd( total, trace->color, total );
1950 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1958 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1959 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1960 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1964 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1965 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1966 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1974 IlluminateRawLightmap()
1975 illuminates the luxels
1978 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1979 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1980 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1982 void IlluminateRawLightmap( int rawLightmapNum )
1984 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1985 int *cluster, *cluster2, mapped, lighted, totalLighted;
1986 size_t llSize, ldSize;
1988 surfaceInfo_t *info;
1989 qboolean filterColor, filterDir;
1991 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1992 unsigned char *flag;
1993 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1994 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1995 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1997 float stackLightLuxels[ STACK_LL_SIZE ];
2000 /* bail if this number exceeds the number of raw lightmaps */
2001 if( rawLightmapNum >= numRawLightmaps )
2005 lm = &rawLightmaps[ rawLightmapNum ];
2008 trace.testOcclusion = !noTrace;
2009 trace.forceSunlight = qfalse;
2010 trace.recvShadows = lm->recvShadows;
2011 trace.numSurfaces = lm->numLightSurfaces;
2012 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2013 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2015 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2016 trace.twoSided = qfalse;
2017 for( i = 0; i < trace.numSurfaces; i++ )
2020 info = &surfaceInfos[ trace.surfaces[ i ] ];
2022 /* check twosidedness */
2023 if( info->si->twoSided )
2025 trace.twoSided = qtrue;
2030 /* create a culled light list for this raw lightmap */
2031 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2033 /* -----------------------------------------------------------------
2035 ----------------------------------------------------------------- */
2038 numLuxelsIlluminated += (lm->sw * lm->sh);
2040 /* test debugging state */
2041 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2043 /* debug fill the luxels */
2044 for( y = 0; y < lm->sh; y++ )
2046 for( x = 0; x < lm->sw; x++ )
2049 cluster = SUPER_CLUSTER( x, y );
2051 /* only fill mapped luxels */
2055 /* get particulars */
2056 luxel = SUPER_LUXEL( 0, x, y );
2057 origin = SUPER_ORIGIN( x, y );
2058 normal = SUPER_NORMAL( x, y );
2060 /* color the luxel with raw lightmap num? */
2062 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2064 /* color the luxel with lightmap axis? */
2065 else if( debugAxis )
2067 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2068 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2069 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2072 /* color the luxel with luxel cluster? */
2073 else if( debugCluster )
2074 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2076 /* color the luxel with luxel origin? */
2077 else if( debugOrigin )
2079 VectorSubtract( lm->maxs, lm->mins, temp );
2080 VectorScale( temp, (1.0f / 255.0f), temp );
2081 VectorSubtract( origin, lm->mins, temp2 );
2082 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2083 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2084 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2087 /* color the luxel with the normal */
2088 else if( normalmap )
2090 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2091 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2092 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2095 /* otherwise clear it */
2097 VectorClear( luxel );
2106 /* allocate temporary per-light luxel storage */
2107 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2108 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2109 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2110 lightLuxels = stackLightLuxels;
2112 lightLuxels = safe_malloc( llSize );
2114 lightDeluxels = safe_malloc( ldSize );
2116 lightDeluxels = NULL;
2119 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2121 /* set ambient color */
2122 for( y = 0; y < lm->sh; y++ )
2124 for( x = 0; x < lm->sw; x++ )
2127 cluster = SUPER_CLUSTER( x, y );
2128 luxel = SUPER_LUXEL( 0, x, y );
2129 normal = SUPER_NORMAL( x, y );
2130 deluxel = SUPER_DELUXEL( x, y );
2132 /* blacken unmapped clusters */
2134 VectorClear( luxel );
2139 VectorCopy( ambientColor, luxel );
2142 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2144 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2145 if(brightness < 0.00390625f)
2146 brightness = 0.00390625f;
2148 VectorScale( normal, brightness, deluxel );
2155 /* clear styled lightmaps */
2156 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2157 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2159 if( lm->superLuxels[ lightmapNum ] != NULL )
2160 memset( lm->superLuxels[ lightmapNum ], 0, size );
2163 /* debugging code */
2164 //% if( trace.numLights <= 0 )
2165 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2167 /* walk light list */
2168 for( i = 0; i < trace.numLights; i++ )
2171 trace.light = trace.lights[ i ];
2174 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2176 if( lm->styles[ lightmapNum ] == trace.light->style ||
2177 lm->styles[ lightmapNum ] == LS_NONE )
2181 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2182 if( lightmapNum >= MAX_LIGHTMAPS )
2184 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2189 memset( lightLuxels, 0, llSize );
2191 memset( lightDeluxels, 0, ldSize );
2194 /* determine filter radius */
2195 filterRadius = lm->filterRadius > trace.light->filterRadius
2197 : trace.light->filterRadius;
2198 if( filterRadius < 0.0f )
2199 filterRadius = 0.0f;
2201 /* set luxel filter radius */
2202 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2203 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2204 luxelFilterRadius = 1;
2206 /* allocate sampling flags storage */
2207 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2209 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2210 if(lm->superFlags == NULL)
2211 lm->superFlags = safe_malloc( size );
2212 memset( (void *) lm->superFlags, 0, size );
2215 /* initial pass, one sample per luxel */
2216 for( y = 0; y < lm->sh; y++ )
2218 for( x = 0; x < lm->sw; x++ )
2221 cluster = SUPER_CLUSTER( x, y );
2225 /* get particulars */
2226 lightLuxel = LIGHT_LUXEL( x, y );
2227 lightDeluxel = LIGHT_DELUXEL( x, y );
2228 origin = SUPER_ORIGIN( x, y );
2229 normal = SUPER_NORMAL( x, y );
2230 flag = SUPER_FLAG( x, y );
2233 ////////// 27's temp hack for testing edge clipping ////
2234 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2236 lightLuxel[ 1 ] = 255;
2237 lightLuxel[ 3 ] = 1.0f;
2243 /* set contribution count */
2244 lightLuxel[ 3 ] = 1.0f;
2247 trace.cluster = *cluster;
2248 VectorCopy( origin, trace.origin );
2249 VectorCopy( normal, trace.normal );
2251 /* get light for this sample */
2252 LightContributionToSample( &trace );
2253 VectorCopy( trace.color, lightLuxel );
2255 /* add the contribution to the deluxemap */
2258 VectorCopy( trace.directionContribution, lightDeluxel );
2261 /* check for evilness */
2262 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2265 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2268 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2274 /* don't even bother with everything else if nothing was lit */
2275 if( totalLighted == 0 )
2278 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2279 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2280 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2283 for( y = 0; y < (lm->sh - 1); y++ )
2285 for( x = 0; x < (lm->sw - 1); x++ )
2290 VectorClear( total );
2292 /* test 2x2 stamp */
2293 for( t = 0; t < 4; t++ )
2295 /* set sample coords */
2296 sx = x + tests[ t ][ 0 ];
2297 sy = y + tests[ t ][ 1 ];
2300 cluster = SUPER_CLUSTER( sx, sy );
2306 flag = SUPER_FLAG( sx, sy );
2307 if(*flag & FLAG_FORCE_SUBSAMPLING)
2309 /* force a lighted/mapped discrepancy so we subsample */
2314 lightLuxel = LIGHT_LUXEL( sx, sy );
2315 VectorAdd( total, lightLuxel, total );
2316 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2320 /* if total color is under a certain amount, then don't bother subsampling */
2321 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2324 /* if all 4 pixels are either in shadow or light, then don't subsample */
2325 if( lighted != 0 && lighted != mapped )
2327 for( t = 0; t < 4; t++ )
2329 /* set sample coords */
2330 sx = x + tests[ t ][ 0 ];
2331 sy = y + tests[ t ][ 1 ];
2334 cluster = SUPER_CLUSTER( sx, sy );
2337 flag = SUPER_FLAG( sx, sy );
2338 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2340 lightLuxel = LIGHT_LUXEL( sx, sy );
2341 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2342 origin = SUPER_ORIGIN( sx, sy );
2344 /* only subsample shadowed luxels */
2345 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2349 if(lightRandomSamples)
2350 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f, lightLuxel, deluxemap ? lightDeluxel : NULL );
2352 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2354 *flag |= FLAG_ALREADY_SUBSAMPLED;
2356 /* debug code to colorize subsampled areas to yellow */
2357 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2358 //% VectorSet( luxel, 255, 204, 0 );
2365 /* tertiary pass, apply dirt map (ambient occlusion) */
2369 for( y = 0; y < lm->sh; y++ )
2371 for( x = 0; x < lm->sw; x++ )
2374 cluster = SUPER_CLUSTER( x, y );
2378 /* get particulars */
2379 lightLuxel = LIGHT_LUXEL( x, y );
2380 dirt = SUPER_DIRT( x, y );
2382 /* scale light value */
2383 VectorScale( lightLuxel, *dirt, lightLuxel );
2388 /* allocate sampling lightmap storage */
2389 if( lm->superLuxels[ lightmapNum ] == NULL )
2391 /* allocate sampling lightmap storage */
2392 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2393 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2394 memset( lm->superLuxels[ lightmapNum ], 0, size );
2398 if( lightmapNum > 0 )
2400 lm->styles[ lightmapNum ] = trace.light->style;
2401 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2404 /* copy to permanent luxels */
2405 for( y = 0; y < lm->sh; y++ )
2407 for( x = 0; x < lm->sw; x++ )
2409 /* get cluster and origin */
2410 cluster = SUPER_CLUSTER( x, y );
2413 origin = SUPER_ORIGIN( x, y );
2416 if( luxelFilterRadius )
2419 VectorClear( averageColor );
2420 VectorClear( averageDir );
2423 /* cheaper distance-based filtering */
2424 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2426 if( sy < 0 || sy >= lm->sh )
2429 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2431 if( sx < 0 || sx >= lm->sw )
2434 /* get particulars */
2435 cluster = SUPER_CLUSTER( sx, sy );
2438 lightLuxel = LIGHT_LUXEL( sx, sy );
2439 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2442 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2443 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2445 /* scale luxel by filter weight */
2446 VectorScale( lightLuxel, weight, color );
2447 VectorAdd( averageColor, color, averageColor );
2450 VectorScale( lightDeluxel, weight, direction );
2451 VectorAdd( averageDir, direction, averageDir );
2458 if( samples <= 0.0f )
2461 /* scale into luxel */
2462 luxel = SUPER_LUXEL( lightmapNum, x, y );
2465 /* handle negative light */
2466 if( trace.light->flags & LIGHT_NEGATIVE )
2468 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2469 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2470 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2473 /* handle normal light */
2476 luxel[ 0 ] += averageColor[ 0 ] / samples;
2477 luxel[ 1 ] += averageColor[ 1 ] / samples;
2478 luxel[ 2 ] += averageColor[ 2 ] / samples;
2483 /* scale into luxel */
2484 deluxel = SUPER_DELUXEL( x, y );
2485 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2486 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2487 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2494 /* get particulars */
2495 lightLuxel = LIGHT_LUXEL( x, y );
2496 lightDeluxel = LIGHT_DELUXEL( x, y );
2497 luxel = SUPER_LUXEL( lightmapNum, x, y );
2498 deluxel = SUPER_DELUXEL( x, y );
2500 /* handle negative light */
2501 if( trace.light->flags & LIGHT_NEGATIVE )
2502 VectorScale( averageColor, -1.0f, averageColor );
2507 /* handle negative light */
2508 if( trace.light->flags & LIGHT_NEGATIVE )
2509 VectorSubtract( luxel, lightLuxel, luxel );
2511 /* handle normal light */
2513 VectorAdd( luxel, lightLuxel, luxel );
2517 VectorAdd( deluxel, lightDeluxel, deluxel );
2524 /* free temporary luxels */
2525 if( lightLuxels != stackLightLuxels )
2526 free( lightLuxels );
2529 free( lightDeluxels );
2532 /* free light list */
2533 FreeTraceLights( &trace );
2535 /* floodlight pass */
2537 FloodlightIlluminateLightmap(lm);
2541 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2544 if( lm->superLuxels[ lightmapNum ] == NULL )
2547 for( y = 0; y < lm->sh; y++ )
2549 for( x = 0; x < lm->sw; x++ )
2552 cluster = SUPER_CLUSTER( x, y );
2553 //% if( *cluster < 0 )
2556 /* get particulars */
2557 luxel = SUPER_LUXEL( lightmapNum, x, y );
2558 normal = SUPER_NORMAL ( x, y );
2560 luxel[0]=(normal[0]*127)+127;
2561 luxel[1]=(normal[1]*127)+127;
2562 luxel[2]=(normal[2]*127)+127;
2568 /* -----------------------------------------------------------------
2570 ----------------------------------------------------------------- */
2574 /* walk lightmaps */
2575 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2578 if( lm->superLuxels[ lightmapNum ] == NULL )
2581 /* apply dirt to each luxel */
2582 for( y = 0; y < lm->sh; y++ )
2584 for( x = 0; x < lm->sw; x++ )
2587 cluster = SUPER_CLUSTER( x, y );
2588 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2591 /* get particulars */
2592 luxel = SUPER_LUXEL( lightmapNum, x, y );
2593 dirt = SUPER_DIRT( x, y );
2596 VectorScale( luxel, *dirt, luxel );
2600 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2606 /* -----------------------------------------------------------------
2608 ----------------------------------------------------------------- */
2610 /* walk lightmaps */
2611 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2614 if( lm->superLuxels[ lightmapNum ] == NULL )
2617 /* average occluded luxels from neighbors */
2618 for( y = 0; y < lm->sh; y++ )
2620 for( x = 0; x < lm->sw; x++ )
2622 /* get particulars */
2623 cluster = SUPER_CLUSTER( x, y );
2624 luxel = SUPER_LUXEL( lightmapNum, x, y );
2625 deluxel = SUPER_DELUXEL( x, y );
2626 normal = SUPER_NORMAL( x, y );
2628 /* determine if filtering is necessary */
2629 filterColor = qfalse;
2632 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2633 filterColor = qtrue;
2635 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2638 if( !filterColor && !filterDir )
2641 /* choose seed amount */
2642 VectorClear( averageColor );
2643 VectorClear( averageDir );
2646 /* walk 3x3 matrix */
2647 for( sy = (y - 1); sy <= (y + 1); sy++ )
2649 if( sy < 0 || sy >= lm->sh )
2652 for( sx = (x - 1); sx <= (x + 1); sx++ )
2654 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2657 /* get neighbor's particulars */
2658 cluster2 = SUPER_CLUSTER( sx, sy );
2659 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2660 deluxel2 = SUPER_DELUXEL( sx, sy );
2662 /* ignore unmapped/unlit luxels */
2663 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2664 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2667 /* add its distinctiveness to our own */
2668 VectorAdd( averageColor, luxel2, averageColor );
2669 samples += luxel2[ 3 ];
2671 VectorAdd( averageDir, deluxel2, averageDir );
2676 if( samples <= 0.0f )
2679 /* dark lightmap seams */
2682 if( lightmapNum == 0 )
2683 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2690 VectorDivide( averageColor, samples, luxel );
2694 VectorDivide( averageDir, samples, deluxel );
2696 /* set cluster to -3 */
2698 *cluster = CLUSTER_FLOODED;
2706 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2709 if( lm->superLuxels[ lightmapNum ] == NULL )
2711 for( y = 0; y < lm->sh; y++ )
2712 for( x = 0; x < lm->sw; x++ )
2715 cluster = SUPER_CLUSTER( x, y );
2716 luxel = SUPER_LUXEL( lightmapNum, x, y );
2717 deluxel = SUPER_DELUXEL( x, y );
2718 if(!luxel || !deluxel || !cluster)
2720 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2723 else if(*cluster < 0)
2726 // should have neither deluxemap nor lightmap
2728 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2733 // should have both deluxemap and lightmap
2735 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2745 IlluminateVertexes()
2746 light the surface vertexes
2749 #define VERTEX_NUDGE 4.0f
2751 void IlluminateVertexes( int num )
2753 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2754 int lightmapNum, numAvg;
2755 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2756 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2757 bspDrawSurface_t *ds;
2758 surfaceInfo_t *info;
2760 bspDrawVert_t *verts;
2762 float floodLightAmount;
2766 /* get surface, info, and raw lightmap */
2767 ds = &bspDrawSurfaces[ num ];
2768 info = &surfaceInfos[ num ];
2771 /* -----------------------------------------------------------------
2772 illuminate the vertexes
2773 ----------------------------------------------------------------- */
2775 /* calculate vertex lighting for surfaces without lightmaps */
2776 if( lm == NULL || cpmaHack )
2779 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2780 trace.forceSunlight = info->si->forceSunlight;
2781 trace.recvShadows = info->recvShadows;
2782 trace.numSurfaces = 1;
2783 trace.surfaces = #
2784 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2786 /* twosided lighting */
2787 trace.twoSided = info->si->twoSided;
2789 /* make light list for this surface */
2790 CreateTraceLightsForSurface( num, &trace );
2793 verts = yDrawVerts + ds->firstVert;
2795 memset( avgColors, 0, sizeof( avgColors ) );
2797 /* walk the surface verts */
2798 for( i = 0; i < ds->numVerts; i++ )
2800 /* get vertex luxel */
2801 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2803 /* color the luxel with raw lightmap num? */
2805 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2807 /* color the luxel with luxel origin? */
2808 else if( debugOrigin )
2810 VectorSubtract( info->maxs, info->mins, temp );
2811 VectorScale( temp, (1.0f / 255.0f), temp );
2812 VectorSubtract( origin, lm->mins, temp2 );
2813 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2814 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2815 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2818 /* color the luxel with the normal */
2819 else if( normalmap )
2821 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2822 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2823 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2826 /* illuminate the vertex */
2829 /* clear vertex luxel */
2830 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2832 /* try at initial origin */
2833 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2834 if( trace.cluster >= 0 )
2837 VectorCopy( verts[ i ].xyz, trace.origin );
2838 VectorCopy( verts[ i ].normal, trace.normal );
2841 if( dirty && !bouncing )
2842 dirt = DirtForSample( &trace );
2846 /* jal: floodlight */
2847 floodLightAmount = 0.0f;
2848 VectorClear( floodColor );
2849 if( floodlighty && !bouncing )
2851 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2852 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2856 LightingAtSample( &trace, ds->vertexStyles, colors );
2859 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2862 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2864 /* jal: floodlight */
2865 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2868 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2869 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2870 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2874 /* is this sample bright enough? */
2875 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2876 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2877 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2878 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2880 /* nudge the sample point around a bit */
2881 for( x = 0; x < 5; x++ )
2883 /* two's complement 0, 1, -1, 2, -2, etc */
2884 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2886 for( y = 0; y < 5; y++ )
2888 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2890 for( z = 0; z < 5; z++ )
2892 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2895 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2896 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2897 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2899 /* try at nudged origin */
2900 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2901 if( trace.cluster < 0 )
2905 if( dirty && !bouncing )
2906 dirt = DirtForSample( &trace );
2910 /* jal: floodlight */
2911 floodLightAmount = 0.0f;
2912 VectorClear( floodColor );
2913 if( floodlighty && !bouncing )
2915 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2916 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2920 LightingAtSample( &trace, ds->vertexStyles, colors );
2923 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2926 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2928 /* jal: floodlight */
2929 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2932 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2933 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2936 /* bright enough? */
2937 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2938 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2939 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2940 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2947 /* add to average? */
2948 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2949 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2950 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2951 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2954 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2956 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2957 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2962 /* another happy customer */
2963 numVertsIlluminated++;
2966 /* set average color */
2969 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2970 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2974 VectorCopy( ambientColor, avgColors[ 0 ] );
2977 /* clean up and store vertex color */
2978 for( i = 0; i < ds->numVerts; i++ )
2980 /* get vertex luxel */
2981 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2983 /* store average in occluded vertexes */
2984 if( radVertLuxel[ 0 ] < 0.0f )
2986 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2988 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2989 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2992 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2997 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3000 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3001 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3004 if( bouncing || bounce == 0 || !bounceOnly )
3005 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3006 if( !info->si->noVertexLight )
3007 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3011 /* free light list */
3012 FreeTraceLights( &trace );
3014 /* return to sender */
3018 /* -----------------------------------------------------------------
3019 reconstitute vertex lighting from the luxels
3020 ----------------------------------------------------------------- */
3022 /* set styles from lightmap */
3023 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3024 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3026 /* get max search radius */
3028 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3030 /* walk the surface verts */
3031 verts = yDrawVerts + ds->firstVert;
3032 for( i = 0; i < ds->numVerts; i++ )
3034 /* do each lightmap */
3035 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3038 if( lm->superLuxels[ lightmapNum ] == NULL )
3041 /* get luxel coords */
3042 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3043 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3046 else if( x >= lm->sw )
3050 else if( y >= lm->sh )
3053 /* get vertex luxels */
3054 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3055 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3057 /* color the luxel with the normal? */
3060 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3061 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3062 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3065 /* color the luxel with surface num? */
3066 else if( debugSurfaces )
3067 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3069 /* divine color from the superluxels */
3072 /* increasing radius */
3073 VectorClear( radVertLuxel );
3075 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3077 /* sample within radius */
3078 for( sy = (y - radius); sy <= (y + radius); sy++ )
3080 if( sy < 0 || sy >= lm->sh )
3083 for( sx = (x - radius); sx <= (x + radius); sx++ )
3085 if( sx < 0 || sx >= lm->sw )
3088 /* get luxel particulars */
3089 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3090 cluster = SUPER_CLUSTER( sx, sy );
3094 /* testing: must be brigher than ambient color */
3095 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3098 /* add its distinctiveness to our own */
3099 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3100 samples += luxel[ 3 ];
3106 if( samples > 0.0f )
3107 VectorDivide( radVertLuxel, samples, radVertLuxel );
3109 VectorCopy( ambientColor, radVertLuxel );
3112 /* store into floating point storage */
3113 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3114 numVertsIlluminated++;
3116 /* store into bytes (for vertex approximation) */
3117 if( !info->si->noVertexLight )
3118 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3125 /* -------------------------------------------------------------------------------
3127 light optimization (-fast)
3129 creates a list of lights that will affect a surface and stores it in tw
3130 this is to optimize surface lighting by culling out as many of the
3131 lights in the world as possible from further calculation
3133 ------------------------------------------------------------------------------- */
3137 determines opaque brushes in the world and find sky shaders for sunlight calculations
3140 void SetupBrushes( void )
3142 int i, j, b, compileFlags;
3145 bspBrushSide_t *side;
3146 bspShader_t *shader;
3151 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3154 if( opaqueBrushes == NULL )
3155 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3158 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3159 numOpaqueBrushes = 0;
3161 /* walk the list of worldspawn brushes */
3162 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3165 b = bspModels[ 0 ].firstBSPBrush + i;
3166 brush = &bspBrushes[ b ];
3168 /* check all sides */
3171 for( j = 0; j < brush->numSides && inside; j++ )
3173 /* do bsp shader calculations */
3174 side = &bspBrushSides[ brush->firstSide + j ];
3175 shader = &bspShaders[ side->shaderNum ];
3177 /* get shader info */
3178 si = ShaderInfoForShader( shader->shader );
3182 /* or together compile flags */
3183 compileFlags |= si->compileFlags;
3186 /* determine if this brush is opaque to light */
3187 if( !(compileFlags & C_TRANSLUCENT) )
3189 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3195 /* emit some statistics */
3196 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3203 determines if two clusters are visible to each other using the PVS
3206 qboolean ClusterVisible( int a, int b )
3208 int portalClusters, leafBytes;
3213 if( a < 0 || b < 0 )
3221 if( numBSPVisBytes <=8 )
3225 portalClusters = ((int *) bspVisBytes)[ 0 ];
3226 leafBytes = ((int*) bspVisBytes)[ 1 ];
3227 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3230 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3239 borrowed from vlight.c
3242 int PointInLeafNum_r( vec3_t point, int nodenum )
3250 while( nodenum >= 0 )
3252 node = &bspNodes[ nodenum ];
3253 plane = &bspPlanes[ node->planeNum ];
3254 dist = DotProduct( point, plane->normal ) - plane->dist;
3256 nodenum = node->children[ 0 ];
3257 else if( dist < -0.1 )
3258 nodenum = node->children[ 1 ];
3261 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3262 if( bspLeafs[ leafnum ].cluster != -1 )
3264 nodenum = node->children[ 1 ];
3268 leafnum = -nodenum - 1;
3276 borrowed from vlight.c
3279 int PointInLeafNum( vec3_t point )
3281 return PointInLeafNum_r( point, 0 );
3287 ClusterVisibleToPoint() - ydnar
3288 returns qtrue if point can "see" cluster
3291 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3296 /* get leafNum for point */
3297 pointCluster = ClusterForPoint( point );
3298 if( pointCluster < 0 )
3302 return ClusterVisible( pointCluster, cluster );
3308 ClusterForPoint() - ydnar
3309 returns the pvs cluster for point
3312 int ClusterForPoint( vec3_t point )
3317 /* get leafNum for point */
3318 leafNum = PointInLeafNum( point );
3322 /* return the cluster */
3323 return bspLeafs[ leafNum ].cluster;
3329 ClusterForPointExt() - ydnar
3330 also takes brushes into account for occlusion testing
3333 int ClusterForPointExt( vec3_t point, float epsilon )
3335 int i, j, b, leafNum, cluster;
3338 int *brushes, numBSPBrushes;
3344 /* get leaf for point */
3345 leafNum = PointInLeafNum( point );
3348 leaf = &bspLeafs[ leafNum ];
3350 /* get the cluster */
3351 cluster = leaf->cluster;
3355 /* transparent leaf, so check point against all brushes in the leaf */
3356 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3357 numBSPBrushes = leaf->numBSPLeafBrushes;
3358 for( i = 0; i < numBSPBrushes; i++ )
3362 if( b > maxOpaqueBrush )
3364 brush = &bspBrushes[ b ];
3365 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3368 /* check point against all planes */
3370 for( j = 0; j < brush->numSides && inside; j++ )
3372 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3373 dot = DotProduct( point, plane->normal );
3379 /* if inside, return bogus cluster */
3384 /* if the point made it this far, it's not inside any opaque brushes */
3391 ClusterForPointExtFilter() - ydnar
3392 adds cluster checking against a list of known valid clusters
3395 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3400 /* get cluster for point */
3401 cluster = ClusterForPointExt( point, epsilon );
3403 /* check if filtering is necessary */
3404 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3408 for( i = 0; i < numClusters; i++ )
3410 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3421 ShaderForPointInLeaf() - ydnar
3422 checks a point against all brushes in a leaf, returning the shader of the brush
3423 also sets the cumulative surface and content flags for the brush hit
3426 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3431 int *brushes, numBSPBrushes;
3434 bspBrushSide_t *side;
3436 bspShader_t *shader;
3437 int allSurfaceFlags, allContentFlags;
3440 /* clear things out first */
3447 leaf = &bspLeafs[ leafNum ];
3449 /* transparent leaf, so check point against all brushes in the leaf */
3450 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3451 numBSPBrushes = leaf->numBSPLeafBrushes;
3452 for( i = 0; i < numBSPBrushes; i++ )
3455 brush = &bspBrushes[ brushes[ i ] ];
3457 /* check point against all planes */
3459 allSurfaceFlags = 0;
3460 allContentFlags = 0;
3461 for( j = 0; j < brush->numSides && inside; j++ )
3463 side = &bspBrushSides[ brush->firstSide + j ];
3464 plane = &bspPlanes[ side->planeNum ];
3465 dot = DotProduct( point, plane->normal );
3471 shader = &bspShaders[ side->shaderNum ];
3472 allSurfaceFlags |= shader->surfaceFlags;
3473 allContentFlags |= shader->contentFlags;
3477 /* handle if inside */
3480 /* if there are desired flags, check for same and continue if they aren't matched */
3481 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3483 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3486 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3487 *surfaceFlags = allSurfaceFlags;
3488 *contentFlags = allContentFlags;
3489 return brush->shaderNum;
3493 /* if the point made it this far, it's not inside any brushes */
3501 chops a bounding box by the plane defined by origin and normal
3502 returns qfalse if the bounds is entirely clipped away
3504 this is not exactly the fastest way to do this...
3507 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3509 /* FIXME: rewrite this so it doesn't use bloody brushes */
3517 calculates each light's effective envelope,
3518 taking into account brightness, type, and pvs.
3521 #define LIGHT_EPSILON 0.125f
3522 #define LIGHT_NUDGE 2.0f
3524 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3526 int i, x, y, z, x1, y1, z1;
3527 light_t *light, *light2, **owner;
3529 vec3_t origin, dir, mins, maxs;
3530 float radius, intensity;
3531 light_t *buckets[ 256 ];
3534 /* early out for weird cases where there are no lights */
3535 if( lights == NULL )
3539 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3543 numCulledLights = 0;
3545 while( *owner != NULL )
3550 /* handle negative lights */
3551 if( light->photons < 0.0f || light->add < 0.0f )
3553 light->photons *= -1.0f;
3554 light->add *= -1.0f;
3555 light->flags |= LIGHT_NEGATIVE;
3559 if( light->type == EMIT_SUN )
3563 light->envelope = MAX_WORLD_COORD * 8.0f;
3564 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3565 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3568 /* everything else */
3571 /* get pvs cluster for light */
3572 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3574 /* invalid cluster? */
3575 if( light->cluster < 0 )
3577 /* nudge the sample point around a bit */
3578 for( x = 0; x < 4; x++ )
3580 /* two's complement 0, 1, -1, 2, -2, etc */
3581 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3583 for( y = 0; y < 4; y++ )
3585 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3587 for( z = 0; z < 4; z++ )
3589 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3592 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3593 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3594 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3596 /* try at nudged origin */
3597 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3598 if( light->cluster < 0 )
3602 VectorCopy( origin, light->origin );
3608 /* only calculate for lights in pvs and outside of opaque brushes */
3609 if( light->cluster >= 0 )
3611 /* set light fast flag */
3613 light->flags |= LIGHT_FAST_TEMP;
3615 light->flags &= ~LIGHT_FAST_TEMP;
3616 if( light->si && light->si->noFast )
3617 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3619 /* clear light envelope */
3620 light->envelope = 0;
3622 /* handle area lights */
3623 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3625 /* ugly hack to calculate extent for area lights, but only done once */
3626 VectorScale( light->normal, -1.0f, dir );
3627 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3631 VectorMA( light->origin, radius, light->normal, origin );
3632 factor = PointToPolygonFormFactor( origin, dir, light->w );
3635 if( (factor * light->add) <= light->falloffTolerance )
3636 light->envelope = radius;
3639 /* check for fast mode */
3640 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3641 light->envelope = MAX_WORLD_COORD * 8.0f;
3642 intensity = light->photons; /* hopefully not used */
3647 intensity = light->photons;
3651 if( light->envelope <= 0.0f )
3653 /* solve distance for non-distance lights */
3654 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3655 light->envelope = MAX_WORLD_COORD * 8.0f;
3657 /* solve distance for linear lights */
3658 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3659 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3660 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3663 add = angle * light->photons * linearScale - (dist * light->fade);
3664 T = (light->photons * linearScale) - (dist * light->fade);
3665 T + (dist * light->fade) = (light->photons * linearScale);
3666 dist * light->fade = (light->photons * linearScale) - T;
3667 dist = ((light->photons * linearScale) - T) / light->fade;
3670 /* solve for inverse square falloff */
3672 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3675 add = light->photons / (dist * dist);
3676 T = light->photons / (dist * dist);
3677 T * (dist * dist) = light->photons;
3678 dist = sqrt( light->photons / T );
3682 /* chop radius against pvs */
3685 ClearBounds( mins, maxs );
3687 /* check all leaves */
3688 for( i = 0; i < numBSPLeafs; i++ )
3691 leaf = &bspLeafs[ i ];
3694 if( leaf->cluster < 0 )
3696 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3699 /* add this leafs bbox to the bounds */
3700 VectorCopy( leaf->mins, origin );
3701 AddPointToBounds( origin, mins, maxs );
3702 VectorCopy( leaf->maxs, origin );
3703 AddPointToBounds( origin, mins, maxs );
3706 /* test to see if bounds encompass light */
3707 for( i = 0; i < 3; i++ )
3709 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3711 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3712 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3713 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3714 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3715 AddPointToBounds( light->origin, mins, maxs );
3719 /* chop the bounds by a plane for area lights and spotlights */
3720 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3721 ChopBounds( mins, maxs, light->origin, light->normal );
3724 VectorCopy( mins, light->mins );
3725 VectorCopy( maxs, light->maxs );
3727 /* reflect bounds around light origin */
3728 //% VectorMA( light->origin, -1.0f, origin, origin );
3729 VectorScale( light->origin, 2, origin );
3730 VectorSubtract( origin, maxs, origin );
3731 AddPointToBounds( origin, mins, maxs );
3732 //% VectorMA( light->origin, -1.0f, mins, origin );
3733 VectorScale( light->origin, 2, origin );
3734 VectorSubtract( origin, mins, origin );
3735 AddPointToBounds( origin, mins, maxs );
3737 /* calculate spherical bounds */
3738 VectorSubtract( maxs, light->origin, dir );
3739 radius = (float) VectorLength( dir );
3741 /* if this radius is smaller than the envelope, then set the envelope to it */
3742 if( radius < light->envelope )
3744 light->envelope = radius;
3745 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3748 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3751 /* add grid/surface only check */
3754 if( !(light->flags & LIGHT_GRID) )
3755 light->envelope = 0.0f;
3759 if( !(light->flags & LIGHT_SURFACES) )
3760 light->envelope = 0.0f;
3765 if( light->cluster < 0 || light->envelope <= 0.0f )
3768 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3770 /* delete the light */
3772 *owner = light->next;
3773 if( light->w != NULL )
3780 /* square envelope */
3781 light->envelope2 = (light->envelope * light->envelope);
3783 /* increment light count */
3786 /* set next light */
3787 owner = &((**owner).next);
3790 /* bucket sort lights by style */
3791 memset( buckets, 0, sizeof( buckets ) );
3793 for( light = lights; light != NULL; light = light2 )
3795 /* get next light */
3796 light2 = light->next;
3798 /* filter into correct bucket */
3799 light->next = buckets[ light->style ];
3800 buckets[ light->style ] = light;
3802 /* if any styled light is present, automatically set nocollapse */
3803 if( light->style != LS_NORMAL )
3807 /* filter back into light list */
3809 for( i = 255; i >= 0; i-- )
3812 for( light = buckets[ i ]; light != NULL; light = light2 )
3814 light2 = light->next;
3815 light->next = lights;
3820 /* emit some statistics */
3821 Sys_Printf( "%9d total lights\n", numLights );
3822 Sys_Printf( "%9d culled lights\n", numCulledLights );
3828 CreateTraceLightsForBounds()
3829 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3832 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3836 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3837 float radius, dist, length;
3840 /* potential pre-setup */
3841 if( numLights == 0 )
3842 SetupEnvelopes( qfalse, fast );
3845 //% 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 ] );
3847 /* allocate the light list */
3848 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3849 trace->numLights = 0;
3851 /* calculate spherical bounds */
3852 VectorAdd( mins, maxs, origin );
3853 VectorScale( origin, 0.5f, origin );
3854 VectorSubtract( maxs, origin, dir );
3855 radius = (float) VectorLength( dir );
3857 /* get length of normal vector */
3858 if( normal != NULL )
3859 length = VectorLength( normal );
3862 normal = nullVector;
3866 /* test each light and see if it reaches the sphere */
3867 /* note: the attenuation code MUST match LightingAtSample() */
3868 for( light = lights; light; light = light->next )
3870 /* check zero sized envelope */
3871 if( light->envelope <= 0 )
3873 lightsEnvelopeCulled++;
3878 if( !(light->flags & flags) )
3881 /* sunlight skips all this nonsense */
3882 if( light->type != EMIT_SUN )
3888 /* check against pvs cluster */
3889 if( numClusters > 0 && clusters != NULL )
3891 for( i = 0; i < numClusters; i++ )
3893 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3898 if( i == numClusters )
3900 lightsClusterCulled++;
3905 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3906 VectorSubtract( light->origin, origin, dir );
3907 dist = VectorLength( dir );
3908 dist -= light->envelope;
3912 lightsEnvelopeCulled++;
3916 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3919 for( i = 0; i < 3; i++ )
3921 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3926 lightsBoundsCulled++;
3932 /* planar surfaces (except twosided surfaces) have a couple more checks */
3933 if( length > 0.0f && trace->twoSided == qfalse )
3935 /* lights coplanar with a surface won't light it */
3936 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3938 lightsPlaneCulled++;
3942 /* check to see if light is behind the plane */
3943 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3945 lightsPlaneCulled++;
3950 /* add this light */
3951 trace->lights[ trace->numLights++ ] = light;
3954 /* make last night null */
3955 trace->lights[ trace->numLights ] = NULL;
3960 void FreeTraceLights( trace_t *trace )
3962 if( trace->lights != NULL )
3963 free( trace->lights );
3969 CreateTraceLightsForSurface()
3970 creates a list of lights that can potentially affect a drawsurface
3973 void CreateTraceLightsForSurface( int num, trace_t *trace )
3976 vec3_t mins, maxs, normal;
3978 bspDrawSurface_t *ds;
3979 surfaceInfo_t *info;
3986 /* get drawsurface and info */
3987 ds = &bspDrawSurfaces[ num ];
3988 info = &surfaceInfos[ num ];
3990 /* get the mins/maxs for the dsurf */
3991 ClearBounds( mins, maxs );
3992 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3993 for( i = 0; i < ds->numVerts; i++ )
3995 dv = &yDrawVerts[ ds->firstVert + i ];
3996 AddPointToBounds( dv->xyz, mins, maxs );
3997 if( !VectorCompare( dv->normal, normal ) )
3998 VectorClear( normal );
4001 /* create the lights for the bounding box */
4002 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4005 /////////////////////////////////////////////////////////////
4007 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4008 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4009 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4010 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4012 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4013 static int numFloodVectors = 0;
4015 void SetupFloodLight( void )
4018 float angle, elevation, angleStep, elevationStep;
4020 double v1,v2,v3,v4,v5;
4023 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4025 /* calculate angular steps */
4026 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4027 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4031 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4033 /* iterate elevation */
4034 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4036 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4037 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4038 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4043 /* emit some statistics */
4044 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4047 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4049 if( value[ 0 ] != '\0' )
4052 v4=floodlightDistance;
4053 v5=floodlightIntensity;
4055 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
4057 floodlightRGB[0]=v1;
4058 floodlightRGB[1]=v2;
4059 floodlightRGB[2]=v3;
4061 if (VectorLength(floodlightRGB)==0)
4063 VectorSet(floodlightRGB,240,240,255);
4069 floodlightDistance=v4;
4070 floodlightIntensity=v5;
4072 floodlighty = qtrue;
4073 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4077 VectorSet(floodlightRGB,240,240,255);
4078 //floodlighty = qtrue;
4079 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4081 VectorNormalize(floodlightRGB,floodlightRGB);
4085 FloodLightForSample()
4086 calculates floodlight value for a given sample
4087 once again, kudos to the dirtmapping coder
4090 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4096 float gatherLight, outLight;
4097 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4105 if( trace == NULL || trace->cluster < 0 )
4110 dd = floodLightDistance;
4111 VectorCopy( trace->normal, normal );
4113 /* check if the normal is aligned to the world-up */
4114 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4116 if( normal[ 2 ] == 1.0f )
4118 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4119 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4121 else if( normal[ 2 ] == -1.0f )
4123 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4124 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4129 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4130 CrossProduct( normal, worldUp, myRt );
4131 VectorNormalize( myRt, myRt );
4132 CrossProduct( myRt, normal, myUp );
4133 VectorNormalize( myUp, myUp );
4136 /* vortex: optimise floodLightLowQuality a bit */
4137 if ( floodLightLowQuality == qtrue )
4139 /* iterate through ordered vectors */
4140 for( i = 0; i < numFloodVectors; i++ )
4141 if (rand()%10 != 0 ) continue;
4145 /* iterate through ordered vectors */
4146 for( i = 0; i < numFloodVectors; i++ )
4150 /* transform vector into tangent space */
4151 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4152 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4153 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4156 VectorMA( trace->origin, dd, direction, trace->end );
4158 //VectorMA( trace->origin, 1, direction, trace->origin );
4160 SetupTrace( trace );
4165 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4169 else if ( trace->opaque )
4171 VectorSubtract( trace->hit, trace->origin, displacement );
4172 d=VectorLength( displacement );
4174 // d=trace->distance;
4175 //if (d>256) gatherDirt+=1;
4177 if (contribution>1) contribution=1.0f;
4179 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4182 gatherLight+=contribution;
4187 if( gatherLight <= 0.0f )
4195 outLight=gatherLight;
4196 if( outLight > 1.0f )
4199 /* return to sender */
4204 FloodLightRawLightmap
4205 lighttracer style ambient occlusion light hack.
4206 Kudos to the dirtmapping author for most of this source.
4207 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4208 VorteX: fixed problems with deluxemapping
4211 // floodlight pass on a lightmap
4212 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4214 int i, x, y, *cluster;
4215 float *origin, *normal, *floodlight, floodLightAmount;
4216 surfaceInfo_t *info;
4219 // float samples, average, *floodlight2;
4221 memset(&trace,0,sizeof(trace_t));
4224 trace.testOcclusion = qtrue;
4225 trace.forceSunlight = qfalse;
4226 trace.twoSided = qtrue;
4227 trace.recvShadows = lm->recvShadows;
4228 trace.numSurfaces = lm->numLightSurfaces;
4229 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4230 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4231 trace.testAll = qfalse;
4232 trace.distance = 1024;
4234 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4235 //trace.twoSided = qfalse;
4236 for( i = 0; i < trace.numSurfaces; i++ )
4239 info = &surfaceInfos[ trace.surfaces[ i ] ];
4241 /* check twosidedness */
4242 if( info->si->twoSided )
4244 trace.twoSided = qtrue;
4249 /* gather floodlight */
4250 for( y = 0; y < lm->sh; y++ )
4252 for( x = 0; x < lm->sw; x++ )
4255 cluster = SUPER_CLUSTER( x, y );
4256 origin = SUPER_ORIGIN( x, y );
4257 normal = SUPER_NORMAL( x, y );
4258 floodlight = SUPER_FLOODLIGHT( x, y );
4260 /* set default dirt */
4263 /* only look at mapped luxels */
4268 trace.cluster = *cluster;
4269 VectorCopy( origin, trace.origin );
4270 VectorCopy( normal, trace.normal );
4272 /* get floodlight */
4273 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4275 /* add floodlight */
4276 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4277 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4278 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4279 floodlight[3] += floodlightDirectionScale;
4283 /* testing no filtering */
4289 for( y = 0; y < lm->sh; y++ )
4291 for( x = 0; x < lm->sw; x++ )
4294 cluster = SUPER_CLUSTER( x, y );
4295 floodlight = SUPER_FLOODLIGHT(x, y );
4297 /* filter dirt by adjacency to unmapped luxels */
4298 average = *floodlight;
4300 for( sy = (y - 1); sy <= (y + 1); sy++ )
4302 if( sy < 0 || sy >= lm->sh )
4305 for( sx = (x - 1); sx <= (x + 1); sx++ )
4307 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4310 /* get neighboring luxel */
4311 cluster = SUPER_CLUSTER( sx, sy );
4312 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4313 if( *cluster < 0 || *floodlight2 <= 0.0f )
4317 average += *floodlight2;
4322 if( samples <= 0.0f )
4327 if( samples <= 0.0f )
4331 *floodlight = average / samples;
4337 void FloodLightRawLightmap( int rawLightmapNum )
4341 /* bail if this number exceeds the number of raw lightmaps */
4342 if( rawLightmapNum >= numRawLightmaps )
4345 lm = &rawLightmaps[ rawLightmapNum ];
4348 if (floodlighty && floodlightIntensity)
4349 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4352 if (lm->floodlightIntensity)
4354 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4355 numSurfacesFloodlighten += 1;
4359 void FloodlightRawLightmaps()
4361 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4362 numSurfacesFloodlighten = 0;
4363 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4364 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4368 FloodLightIlluminate()
4369 illuminate floodlight into lightmap luxels
4372 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4374 float *luxel, *floodlight, *deluxel, *normal;
4377 int x, y, lightmapNum;
4379 /* walk lightmaps */
4380 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4383 if( lm->superLuxels[ lightmapNum ] == NULL )
4386 /* apply floodlight to each luxel */
4387 for( y = 0; y < lm->sh; y++ )
4389 for( x = 0; x < lm->sw; x++ )
4391 /* get floodlight */
4392 floodlight = SUPER_FLOODLIGHT( x, y );
4393 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4397 cluster = SUPER_CLUSTER( x, y );
4399 /* only process mapped luxels */
4403 /* get particulars */
4404 luxel = SUPER_LUXEL( lightmapNum, x, y );
4405 deluxel = SUPER_DELUXEL( x, y );
4407 /* add to lightmap */
4408 luxel[0]+=floodlight[0];
4409 luxel[1]+=floodlight[1];
4410 luxel[2]+=floodlight[2];
4412 if (luxel[3]==0) luxel[3]=1;
4414 /* add to deluxemap */
4415 if (deluxemap && floodlight[3] > 0)
4419 normal = SUPER_NORMAL( x, y );
4420 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4422 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4423 if(brightness < 0.00390625f)
4424 brightness = 0.00390625f;
4426 VectorScale( normal, brightness, lightvector );
4427 VectorAdd( deluxel, lightvector, deluxel );