1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out ) //% (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 /* get drawverts and map first triangle */
1158 MapTriangle( lm, info, dv, mapNonAxial );
1160 /* get drawverts and map second triangle */
1161 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1162 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1163 MapTriangle( lm, info, dv, mapNonAxial );
1178 /* -----------------------------------------------------------------
1179 average and clean up luxel normals
1180 ----------------------------------------------------------------- */
1182 /* walk the luxels */
1183 for( y = 0; y < lm->sh; y++ )
1185 for( x = 0; x < lm->sw; x++ )
1188 luxel = SUPER_LUXEL( 0, x, y );
1189 normal = SUPER_NORMAL( x, y );
1190 cluster = SUPER_CLUSTER( x, y );
1192 /* only look at mapped luxels */
1196 /* the normal data could be the sum of multiple samples */
1197 if( luxel[ 3 ] > 1.0f )
1198 VectorNormalize( normal, normal );
1200 /* mark this luxel as having only one normal */
1205 /* non-planar surfaces stop here */
1206 if( lm->plane == NULL )
1209 /* -----------------------------------------------------------------
1210 map occluded or unuxed luxels
1211 ----------------------------------------------------------------- */
1213 /* walk the luxels */
1214 radius = floor( superSample / 2 );
1215 radius = radius > 0 ? radius : 1.0f;
1217 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1219 for( y = 0; y < lm->sh; y++ )
1221 for( x = 0; x < lm->sw; x++ )
1224 luxel = SUPER_LUXEL( 0, x, y );
1225 normal = SUPER_NORMAL( x, y );
1226 cluster = SUPER_CLUSTER( x, y );
1228 /* only look at unmapped luxels */
1229 if( *cluster != CLUSTER_UNMAPPED )
1232 /* divine a normal and origin from neighboring luxels */
1233 VectorClear( fake.xyz );
1234 VectorClear( fake.normal );
1235 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1236 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1238 for( sy = (y - 1); sy <= (y + 1); sy++ )
1240 if( sy < 0 || sy >= lm->sh )
1243 for( sx = (x - 1); sx <= (x + 1); sx++ )
1245 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1248 /* get neighboring luxel */
1249 luxel = SUPER_LUXEL( 0, sx, sy );
1250 origin = SUPER_ORIGIN( sx, sy );
1251 normal = SUPER_NORMAL( sx, sy );
1252 cluster = SUPER_CLUSTER( sx, sy );
1254 /* only consider luxels mapped in previous passes */
1255 if( *cluster < 0 || luxel[ 0 ] >= pass )
1258 /* add its distinctiveness to our own */
1259 VectorAdd( fake.xyz, origin, fake.xyz );
1260 VectorAdd( fake.normal, normal, fake.normal );
1261 samples += luxel[ 3 ];
1266 if( samples == 0.0f )
1270 VectorDivide( fake.xyz, samples, fake.xyz );
1271 //% VectorDivide( fake.normal, samples, fake.normal );
1272 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1275 /* map the fake vert */
1276 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1281 /* -----------------------------------------------------------------
1282 average and clean up luxel normals
1283 ----------------------------------------------------------------- */
1285 /* walk the luxels */
1286 for( y = 0; y < lm->sh; y++ )
1288 for( x = 0; x < lm->sw; x++ )
1291 luxel = SUPER_LUXEL( 0, x, y );
1292 normal = SUPER_NORMAL( x, y );
1293 cluster = SUPER_CLUSTER( x, y );
1295 /* only look at mapped luxels */
1299 /* the normal data could be the sum of multiple samples */
1300 if( luxel[ 3 ] > 1.0f )
1301 VectorNormalize( normal, normal );
1303 /* mark this luxel as having only one normal */
1311 for( y = 0; y < lm->sh; y++ )
1313 for( x = 0; x < lm->sw; x++ )
1318 cluster = SUPER_CLUSTER( x, y );
1319 origin = SUPER_ORIGIN( x, y );
1320 normal = SUPER_NORMAL( x, y );
1321 luxel = SUPER_LUXEL( x, y );
1326 /* check if within the bounding boxes of all surfaces referenced */
1327 ClearBounds( mins, maxs );
1328 for( n = 0; n < lm->numLightSurfaces; n++ )
1331 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1332 TOL = info->sampleSize + 2;
1333 AddPointToBounds( info->mins, mins, maxs );
1334 AddPointToBounds( info->maxs, mins, maxs );
1335 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1336 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1337 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1342 if( n < lm->numLightSurfaces )
1345 /* report bogus origin */
1346 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1347 rawLightmapNum, x, y, *cluster,
1348 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1349 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1350 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1361 sets up dirtmap (ambient occlusion)
1364 #define DIRT_CONE_ANGLE 88 /* degrees */
1365 #define DIRT_NUM_ANGLE_STEPS 16
1366 #define DIRT_NUM_ELEVATION_STEPS 3
1367 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1369 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1370 static int numDirtVectors = 0;
1372 void SetupDirt( void )
1375 float angle, elevation, angleStep, elevationStep;
1379 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1381 /* calculate angular steps */
1382 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1383 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1387 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1389 /* iterate elevation */
1390 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1392 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1393 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1394 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1399 /* emit some statistics */
1400 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1406 calculates dirt value for a given sample
1409 float DirtForSample( trace_t *trace )
1412 float gatherDirt, outDirt, angle, elevation, ooDepth;
1413 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1419 if( trace == NULL || trace->cluster < 0 )
1424 ooDepth = 1.0f / dirtDepth;
1425 VectorCopy( trace->normal, normal );
1427 /* check if the normal is aligned to the world-up */
1428 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1430 if( normal[ 2 ] == 1.0f )
1432 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1433 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1435 else if( normal[ 2 ] == -1.0f )
1437 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1443 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1444 CrossProduct( normal, worldUp, myRt );
1445 VectorNormalize( myRt, myRt );
1446 CrossProduct( myRt, normal, myUp );
1447 VectorNormalize( myUp, myUp );
1450 /* 1 = random mode, 0 (well everything else) = non-random mode */
1454 for( i = 0; i < numDirtVectors; i++ )
1456 /* get random vector */
1457 angle = Random() * DEG2RAD( 360.0f );
1458 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1459 temp[ 0 ] = cos( angle ) * sin( elevation );
1460 temp[ 1 ] = sin( angle ) * sin( elevation );
1461 temp[ 2 ] = cos( elevation );
1463 /* transform into tangent space */
1464 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1465 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1466 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1469 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1470 SetupTrace( trace );
1476 VectorSubtract( trace->hit, trace->origin, displacement );
1477 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1483 /* iterate through ordered vectors */
1484 for( i = 0; i < numDirtVectors; i++ )
1486 /* transform vector into tangent space */
1487 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1488 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1489 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1492 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1493 SetupTrace( trace );
1499 VectorSubtract( trace->hit, trace->origin, displacement );
1500 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1506 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1507 SetupTrace( trace );
1513 VectorSubtract( trace->hit, trace->origin, displacement );
1514 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1518 if( gatherDirt <= 0.0f )
1521 /* apply gain (does this even do much? heh) */
1522 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1523 if( outDirt > 1.0f )
1527 outDirt *= dirtScale;
1528 if( outDirt > 1.0f )
1531 /* return to sender */
1532 return 1.0f - outDirt;
1539 calculates dirty fraction for each luxel
1542 void DirtyRawLightmap( int rawLightmapNum )
1544 int i, x, y, sx, sy, *cluster;
1545 float *origin, *normal, *dirt, *dirt2, average, samples;
1547 surfaceInfo_t *info;
1552 /* bail if this number exceeds the number of raw lightmaps */
1553 if( rawLightmapNum >= numRawLightmaps )
1557 lm = &rawLightmaps[ rawLightmapNum ];
1560 trace.testOcclusion = qtrue;
1561 trace.forceSunlight = qfalse;
1562 trace.recvShadows = lm->recvShadows;
1563 trace.numSurfaces = lm->numLightSurfaces;
1564 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1565 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1566 trace.testAll = qtrue;
1568 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1569 trace.twoSided = qfalse;
1570 for( i = 0; i < trace.numSurfaces; i++ )
1573 info = &surfaceInfos[ trace.surfaces[ i ] ];
1575 /* check twosidedness */
1576 if( info->si->twoSided )
1578 trace.twoSided = qtrue;
1584 for( i = 0; i < trace.numSurfaces; i++ )
1587 info = &surfaceInfos[ trace.surfaces[ i ] ];
1589 /* check twosidedness */
1590 if( info->si->noDirty )
1598 for( y = 0; y < lm->sh; y++ )
1600 for( x = 0; x < lm->sw; x++ )
1603 cluster = SUPER_CLUSTER( x, y );
1604 origin = SUPER_ORIGIN( x, y );
1605 normal = SUPER_NORMAL( x, y );
1606 dirt = SUPER_DIRT( x, y );
1608 /* set default dirt */
1611 /* only look at mapped luxels */
1615 /* don't apply dirty on this surface */
1623 trace.cluster = *cluster;
1624 VectorCopy( origin, trace.origin );
1625 VectorCopy( normal, trace.normal );
1628 *dirt = DirtForSample( &trace );
1632 /* testing no filtering */
1636 for( y = 0; y < lm->sh; y++ )
1638 for( x = 0; x < lm->sw; x++ )
1641 cluster = SUPER_CLUSTER( x, y );
1642 dirt = SUPER_DIRT( x, y );
1644 /* filter dirt by adjacency to unmapped luxels */
1647 for( sy = (y - 1); sy <= (y + 1); sy++ )
1649 if( sy < 0 || sy >= lm->sh )
1652 for( sx = (x - 1); sx <= (x + 1); sx++ )
1654 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1657 /* get neighboring luxel */
1658 cluster = SUPER_CLUSTER( sx, sy );
1659 dirt2 = SUPER_DIRT( sx, sy );
1660 if( *cluster < 0 || *dirt2 <= 0.0f )
1669 if( samples <= 0.0f )
1674 if( samples <= 0.0f )
1678 *dirt = average / samples;
1687 calculates the pvs cluster, origin, normal of a sub-luxel
1690 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1692 int i, *cluster, *cluster2;
1693 float *origin, *origin2, *normal; //% , *normal2;
1694 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1697 /* calulate x vector */
1698 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1700 cluster = SUPER_CLUSTER( x, y );
1701 origin = SUPER_ORIGIN( x, y );
1702 //% normal = SUPER_NORMAL( x, y );
1703 cluster2 = SUPER_CLUSTER( x + 1, y );
1704 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1705 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1707 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1709 cluster = SUPER_CLUSTER( x - 1, y );
1710 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1711 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1712 cluster2 = SUPER_CLUSTER( x, y );
1713 origin2 = SUPER_ORIGIN( x, y );
1714 //% normal2 = SUPER_NORMAL( x, y );
1717 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1719 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1720 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1722 /* calulate y vector */
1723 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1725 cluster = SUPER_CLUSTER( x, y );
1726 origin = SUPER_ORIGIN( x, y );
1727 //% normal = SUPER_NORMAL( x, y );
1728 cluster2 = SUPER_CLUSTER( x, y + 1 );
1729 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1730 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1732 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1734 cluster = SUPER_CLUSTER( x, y - 1 );
1735 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1736 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1737 cluster2 = SUPER_CLUSTER( x, y );
1738 origin2 = SUPER_ORIGIN( x, y );
1739 //% normal2 = SUPER_NORMAL( x, y );
1742 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1744 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1745 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1747 /* calculate new origin */
1748 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1749 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1750 for( i = 0; i < 3; i++ )
1751 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1754 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1755 if( *sampleCluster < 0 )
1758 /* calculate new normal */
1759 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1760 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1761 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1763 normal = SUPER_NORMAL( x, y );
1764 VectorCopy( normal, sampleNormal );
1772 SubsampleRawLuxel_r()
1773 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1776 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1778 int b, samples, mapped, lighted;
1781 vec3_t deluxel[ 3 ];
1782 vec3_t origin[ 4 ], normal[ 4 ];
1783 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1784 vec3_t color, direction, total;
1788 if( lightLuxel[ 3 ] >= lightSamples )
1792 VectorClear( total );
1796 /* make 2x2 subsample stamp */
1797 for( b = 0; b < 4; b++ )
1800 VectorCopy( sampleOrigin, origin[ b ] );
1802 /* calculate position */
1803 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1810 /* increment sample count */
1811 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1814 trace->cluster = *cluster;
1815 VectorCopy( origin[ b ], trace->origin );
1816 VectorCopy( normal[ b ], trace->normal );
1820 LightContributionToSample( trace );
1821 if(trace->forceSubsampling > 1.0f)
1823 /* alphashadow: we subsample as deep as we can */
1829 /* add to totals (fixme: make contrast function) */
1830 VectorCopy( trace->color, luxel[ b ] );
1833 VectorCopy( trace->directionContribution, deluxel[ b ] );
1835 VectorAdd( total, trace->color, total );
1836 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1840 /* subsample further? */
1841 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1842 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1843 lighted != 0 && lighted != mapped )
1845 for( b = 0; b < 4; b++ )
1847 if( cluster[ b ] < 0 )
1849 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1854 //% VectorClear( color );
1856 VectorCopy( lightLuxel, color );
1857 VectorCopy( lightDeluxel, direction );
1859 for( b = 0; b < 4; b++ )
1861 if( cluster[ b ] < 0 )
1863 VectorAdd( color, luxel[ b ], color );
1866 VectorAdd( direction, deluxel[ b ], direction );
1875 color[ 0 ] /= samples;
1876 color[ 1 ] /= samples;
1877 color[ 2 ] /= samples;
1880 VectorCopy( color, lightLuxel );
1881 lightLuxel[ 3 ] += 1.0f;
1885 direction[ 0 ] /= samples;
1886 direction[ 1 ] /= samples;
1887 direction[ 2 ] /= samples;
1888 VectorCopy( direction, lightDeluxel );
1893 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1894 static void GaussLikeRandom(float sigma, float *x, float *y)
1897 r = Random() * 2 * Q_PI;
1898 *x = sigma * 2.73861278752581783822 * cos(r);
1899 *y = sigma * 2.73861278752581783822 * sin(r);
1906 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1910 vec3_t origin, normal;
1911 vec3_t total, totaldirection;
1914 VectorClear( total );
1915 VectorClear( totaldirection );
1917 for(b = 0; b < lightSamples; ++b)
1920 VectorCopy( sampleOrigin, origin );
1921 GaussLikeRandom(bias, &dx, &dy);
1924 if(dx < -1) dx = -1;
1925 if(dy < -1) dy = -1;
1927 /* calculate position */
1928 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1935 trace->cluster = cluster;
1936 VectorCopy( origin, trace->origin );
1937 VectorCopy( normal, trace->normal );
1939 LightContributionToSample( trace );
1940 VectorAdd( total, trace->color, total );
1943 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1951 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1952 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1953 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1957 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1958 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1959 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1967 IlluminateRawLightmap()
1968 illuminates the luxels
1971 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1972 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1973 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1975 void IlluminateRawLightmap( int rawLightmapNum )
1977 int i, t, x, y, sx, sy, size, llSize, ldSize, luxelFilterRadius, lightmapNum;
1978 int *cluster, *cluster2, mapped, lighted, totalLighted;
1980 surfaceInfo_t *info;
1981 qboolean filterColor, filterDir;
1983 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1984 unsigned char *flag;
1985 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1986 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1987 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1989 float stackLightLuxels[ STACK_LL_SIZE ];
1992 /* bail if this number exceeds the number of raw lightmaps */
1993 if( rawLightmapNum >= numRawLightmaps )
1997 lm = &rawLightmaps[ rawLightmapNum ];
2000 trace.testOcclusion = !noTrace;
2001 trace.forceSunlight = qfalse;
2002 trace.recvShadows = lm->recvShadows;
2003 trace.numSurfaces = lm->numLightSurfaces;
2004 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2005 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2007 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2008 trace.twoSided = qfalse;
2009 for( i = 0; i < trace.numSurfaces; i++ )
2012 info = &surfaceInfos[ trace.surfaces[ i ] ];
2014 /* check twosidedness */
2015 if( info->si->twoSided )
2017 trace.twoSided = qtrue;
2022 /* create a culled light list for this raw lightmap */
2023 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2025 /* -----------------------------------------------------------------
2027 ----------------------------------------------------------------- */
2030 numLuxelsIlluminated += (lm->sw * lm->sh);
2032 /* test debugging state */
2033 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2035 /* debug fill the luxels */
2036 for( y = 0; y < lm->sh; y++ )
2038 for( x = 0; x < lm->sw; x++ )
2041 cluster = SUPER_CLUSTER( x, y );
2043 /* only fill mapped luxels */
2047 /* get particulars */
2048 luxel = SUPER_LUXEL( 0, x, y );
2049 origin = SUPER_ORIGIN( x, y );
2050 normal = SUPER_NORMAL( x, y );
2052 /* color the luxel with raw lightmap num? */
2054 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2056 /* color the luxel with lightmap axis? */
2057 else if( debugAxis )
2059 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2060 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2061 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2064 /* color the luxel with luxel cluster? */
2065 else if( debugCluster )
2066 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2068 /* color the luxel with luxel origin? */
2069 else if( debugOrigin )
2071 VectorSubtract( lm->maxs, lm->mins, temp );
2072 VectorScale( temp, (1.0f / 255.0f), temp );
2073 VectorSubtract( origin, lm->mins, temp2 );
2074 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2075 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2076 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2079 /* color the luxel with the normal */
2080 else if( normalmap )
2082 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2083 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2084 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2087 /* otherwise clear it */
2089 VectorClear( luxel );
2098 /* allocate temporary per-light luxel storage */
2099 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2100 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2101 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2102 lightLuxels = stackLightLuxels;
2104 lightLuxels = safe_malloc( llSize );
2106 lightDeluxels = safe_malloc( ldSize );
2108 lightDeluxels = NULL;
2111 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2113 /* set ambient color */
2114 for( y = 0; y < lm->sh; y++ )
2116 for( x = 0; x < lm->sw; x++ )
2119 cluster = SUPER_CLUSTER( x, y );
2120 luxel = SUPER_LUXEL( 0, x, y );
2121 normal = SUPER_NORMAL( x, y );
2122 deluxel = SUPER_DELUXEL( x, y );
2124 /* blacken unmapped clusters */
2126 VectorClear( luxel );
2131 VectorCopy( ambientColor, luxel );
2134 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2136 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2137 if(brightness < 0.00390625f)
2138 brightness = 0.00390625f;
2140 VectorScale( normal, brightness, deluxel );
2147 /* clear styled lightmaps */
2148 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2149 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2151 if( lm->superLuxels[ lightmapNum ] != NULL )
2152 memset( lm->superLuxels[ lightmapNum ], 0, size );
2155 /* debugging code */
2156 //% if( trace.numLights <= 0 )
2157 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2159 /* walk light list */
2160 for( i = 0; i < trace.numLights; i++ )
2163 trace.light = trace.lights[ i ];
2166 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2168 if( lm->styles[ lightmapNum ] == trace.light->style ||
2169 lm->styles[ lightmapNum ] == LS_NONE )
2173 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2174 if( lightmapNum >= MAX_LIGHTMAPS )
2176 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2181 memset( lightLuxels, 0, llSize );
2183 memset( lightDeluxels, 0, ldSize );
2186 /* determine filter radius */
2187 filterRadius = lm->filterRadius > trace.light->filterRadius
2189 : trace.light->filterRadius;
2190 if( filterRadius < 0.0f )
2191 filterRadius = 0.0f;
2193 /* set luxel filter radius */
2194 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2195 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2196 luxelFilterRadius = 1;
2198 /* allocate sampling flags storage */
2199 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2201 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2202 if(lm->superFlags == NULL)
2203 lm->superFlags = safe_malloc( size );
2204 memset( (void *) lm->superFlags, 0, size );
2207 /* initial pass, one sample per luxel */
2208 for( y = 0; y < lm->sh; y++ )
2210 for( x = 0; x < lm->sw; x++ )
2213 cluster = SUPER_CLUSTER( x, y );
2217 /* get particulars */
2218 lightLuxel = LIGHT_LUXEL( x, y );
2219 lightDeluxel = LIGHT_DELUXEL( x, y );
2220 origin = SUPER_ORIGIN( x, y );
2221 normal = SUPER_NORMAL( x, y );
2222 flag = SUPER_FLAG( x, y );
2225 ////////// 27's temp hack for testing edge clipping ////
2226 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2228 lightLuxel[ 1 ] = 255;
2229 lightLuxel[ 3 ] = 1.0f;
2235 /* set contribution count */
2236 lightLuxel[ 3 ] = 1.0f;
2239 trace.cluster = *cluster;
2240 VectorCopy( origin, trace.origin );
2241 VectorCopy( normal, trace.normal );
2243 /* get light for this sample */
2244 LightContributionToSample( &trace );
2245 VectorCopy( trace.color, lightLuxel );
2247 /* add the contribution to the deluxemap */
2250 VectorCopy( trace.directionContribution, lightDeluxel );
2253 /* check for evilness */
2254 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2257 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2260 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2266 /* don't even bother with everything else if nothing was lit */
2267 if( totalLighted == 0 )
2270 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2271 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2272 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2275 for( y = 0; y < (lm->sh - 1); y++ )
2277 for( x = 0; x < (lm->sw - 1); x++ )
2282 VectorClear( total );
2284 /* test 2x2 stamp */
2285 for( t = 0; t < 4; t++ )
2287 /* set sample coords */
2288 sx = x + tests[ t ][ 0 ];
2289 sy = y + tests[ t ][ 1 ];
2292 cluster = SUPER_CLUSTER( sx, sy );
2298 flag = SUPER_FLAG( sx, sy );
2299 if(*flag & FLAG_FORCE_SUBSAMPLING)
2301 /* force a lighted/mapped discrepancy so we subsample */
2306 lightLuxel = LIGHT_LUXEL( sx, sy );
2307 VectorAdd( total, lightLuxel, total );
2308 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2312 /* if total color is under a certain amount, then don't bother subsampling */
2313 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2316 /* if all 4 pixels are either in shadow or light, then don't subsample */
2317 if( lighted != 0 && lighted != mapped )
2319 for( t = 0; t < 4; t++ )
2321 /* set sample coords */
2322 sx = x + tests[ t ][ 0 ];
2323 sy = y + tests[ t ][ 1 ];
2326 cluster = SUPER_CLUSTER( sx, sy );
2329 flag = SUPER_FLAG( sx, sy );
2330 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2332 lightLuxel = LIGHT_LUXEL( sx, sy );
2333 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2334 origin = SUPER_ORIGIN( sx, sy );
2336 /* only subsample shadowed luxels */
2337 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2341 if(lightRandomSamples)
2342 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f, lightLuxel, deluxemap ? lightDeluxel : NULL );
2344 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2346 *flag |= FLAG_ALREADY_SUBSAMPLED;
2348 /* debug code to colorize subsampled areas to yellow */
2349 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2350 //% VectorSet( luxel, 255, 204, 0 );
2357 /* tertiary pass, apply dirt map (ambient occlusion) */
2361 for( y = 0; y < lm->sh; y++ )
2363 for( x = 0; x < lm->sw; x++ )
2366 cluster = SUPER_CLUSTER( x, y );
2370 /* get particulars */
2371 lightLuxel = LIGHT_LUXEL( x, y );
2372 dirt = SUPER_DIRT( x, y );
2374 /* scale light value */
2375 VectorScale( lightLuxel, *dirt, lightLuxel );
2380 /* allocate sampling lightmap storage */
2381 if( lm->superLuxels[ lightmapNum ] == NULL )
2383 /* allocate sampling lightmap storage */
2384 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2385 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2386 memset( lm->superLuxels[ lightmapNum ], 0, size );
2390 if( lightmapNum > 0 )
2392 lm->styles[ lightmapNum ] = trace.light->style;
2393 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2396 /* copy to permanent luxels */
2397 for( y = 0; y < lm->sh; y++ )
2399 for( x = 0; x < lm->sw; x++ )
2401 /* get cluster and origin */
2402 cluster = SUPER_CLUSTER( x, y );
2405 origin = SUPER_ORIGIN( x, y );
2408 if( luxelFilterRadius )
2411 VectorClear( averageColor );
2412 VectorClear( averageDir );
2415 /* cheaper distance-based filtering */
2416 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2418 if( sy < 0 || sy >= lm->sh )
2421 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2423 if( sx < 0 || sx >= lm->sw )
2426 /* get particulars */
2427 cluster = SUPER_CLUSTER( sx, sy );
2430 lightLuxel = LIGHT_LUXEL( sx, sy );
2431 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2434 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2435 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2437 /* scale luxel by filter weight */
2438 VectorScale( lightLuxel, weight, color );
2439 VectorAdd( averageColor, color, averageColor );
2442 VectorScale( lightDeluxel, weight, direction );
2443 VectorAdd( averageDir, direction, averageDir );
2450 if( samples <= 0.0f )
2453 /* scale into luxel */
2454 luxel = SUPER_LUXEL( lightmapNum, x, y );
2457 /* handle negative light */
2458 if( trace.light->flags & LIGHT_NEGATIVE )
2460 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2461 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2462 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2465 /* handle normal light */
2468 luxel[ 0 ] += averageColor[ 0 ] / samples;
2469 luxel[ 1 ] += averageColor[ 1 ] / samples;
2470 luxel[ 2 ] += averageColor[ 2 ] / samples;
2475 /* scale into luxel */
2476 deluxel = SUPER_DELUXEL( x, y );
2477 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2478 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2479 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2486 /* get particulars */
2487 lightLuxel = LIGHT_LUXEL( x, y );
2488 lightDeluxel = LIGHT_DELUXEL( x, y );
2489 luxel = SUPER_LUXEL( lightmapNum, x, y );
2490 deluxel = SUPER_DELUXEL( x, y );
2492 /* handle negative light */
2493 if( trace.light->flags & LIGHT_NEGATIVE )
2494 VectorScale( averageColor, -1.0f, averageColor );
2499 /* handle negative light */
2500 if( trace.light->flags & LIGHT_NEGATIVE )
2501 VectorSubtract( luxel, lightLuxel, luxel );
2503 /* handle normal light */
2505 VectorAdd( luxel, lightLuxel, luxel );
2509 VectorAdd( deluxel, lightDeluxel, deluxel );
2516 /* free temporary luxels */
2517 if( lightLuxels != stackLightLuxels )
2518 free( lightLuxels );
2521 free( lightDeluxels );
2524 /* free light list */
2525 FreeTraceLights( &trace );
2527 /* floodlight pass */
2529 FloodlightIlluminateLightmap(lm);
2533 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2536 if( lm->superLuxels[ lightmapNum ] == NULL )
2539 for( y = 0; y < lm->sh; y++ )
2541 for( x = 0; x < lm->sw; x++ )
2544 cluster = SUPER_CLUSTER( x, y );
2545 //% if( *cluster < 0 )
2548 /* get particulars */
2549 luxel = SUPER_LUXEL( lightmapNum, x, y );
2550 normal = SUPER_NORMAL ( x, y );
2552 luxel[0]=(normal[0]*127)+127;
2553 luxel[1]=(normal[1]*127)+127;
2554 luxel[2]=(normal[2]*127)+127;
2560 /* -----------------------------------------------------------------
2562 ----------------------------------------------------------------- */
2566 /* walk lightmaps */
2567 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2570 if( lm->superLuxels[ lightmapNum ] == NULL )
2573 /* apply dirt to each luxel */
2574 for( y = 0; y < lm->sh; y++ )
2576 for( x = 0; x < lm->sw; x++ )
2579 cluster = SUPER_CLUSTER( x, y );
2580 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2583 /* get particulars */
2584 luxel = SUPER_LUXEL( lightmapNum, x, y );
2585 dirt = SUPER_DIRT( x, y );
2588 VectorScale( luxel, *dirt, luxel );
2592 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2598 /* -----------------------------------------------------------------
2600 ----------------------------------------------------------------- */
2602 /* walk lightmaps */
2603 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2606 if( lm->superLuxels[ lightmapNum ] == NULL )
2609 /* average occluded luxels from neighbors */
2610 for( y = 0; y < lm->sh; y++ )
2612 for( x = 0; x < lm->sw; x++ )
2614 /* get particulars */
2615 cluster = SUPER_CLUSTER( x, y );
2616 luxel = SUPER_LUXEL( lightmapNum, x, y );
2617 deluxel = SUPER_DELUXEL( x, y );
2618 normal = SUPER_NORMAL( x, y );
2620 /* determine if filtering is necessary */
2621 filterColor = qfalse;
2624 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2625 filterColor = qtrue;
2627 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2630 if( !filterColor && !filterDir )
2633 /* choose seed amount */
2634 VectorClear( averageColor );
2635 VectorClear( averageDir );
2638 /* walk 3x3 matrix */
2639 for( sy = (y - 1); sy <= (y + 1); sy++ )
2641 if( sy < 0 || sy >= lm->sh )
2644 for( sx = (x - 1); sx <= (x + 1); sx++ )
2646 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2649 /* get neighbor's particulars */
2650 cluster2 = SUPER_CLUSTER( sx, sy );
2651 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2652 deluxel2 = SUPER_DELUXEL( sx, sy );
2654 /* ignore unmapped/unlit luxels */
2655 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2656 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2659 /* add its distinctiveness to our own */
2660 VectorAdd( averageColor, luxel2, averageColor );
2661 samples += luxel2[ 3 ];
2663 VectorAdd( averageDir, deluxel2, averageDir );
2668 if( samples <= 0.0f )
2671 /* dark lightmap seams */
2674 if( lightmapNum == 0 )
2675 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2682 VectorDivide( averageColor, samples, luxel );
2686 VectorDivide( averageDir, samples, deluxel );
2688 /* set cluster to -3 */
2690 *cluster = CLUSTER_FLOODED;
2698 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2701 if( lm->superLuxels[ lightmapNum ] == NULL )
2703 for( y = 0; y < lm->sh; y++ )
2704 for( x = 0; x < lm->sw; x++ )
2707 cluster = SUPER_CLUSTER( x, y );
2708 luxel = SUPER_LUXEL( lightmapNum, x, y );
2709 deluxel = SUPER_DELUXEL( x, y );
2710 if(!luxel || !deluxel || !cluster)
2712 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2715 else if(*cluster < 0)
2718 // should have neither deluxemap nor lightmap
2720 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2725 // should have both deluxemap and lightmap
2727 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2737 IlluminateVertexes()
2738 light the surface vertexes
2741 #define VERTEX_NUDGE 4.0f
2743 void IlluminateVertexes( int num )
2745 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2746 int lightmapNum, numAvg;
2747 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2748 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2749 bspDrawSurface_t *ds;
2750 surfaceInfo_t *info;
2752 bspDrawVert_t *verts;
2754 float floodLightAmount;
2758 /* get surface, info, and raw lightmap */
2759 ds = &bspDrawSurfaces[ num ];
2760 info = &surfaceInfos[ num ];
2763 /* -----------------------------------------------------------------
2764 illuminate the vertexes
2765 ----------------------------------------------------------------- */
2767 /* calculate vertex lighting for surfaces without lightmaps */
2768 if( lm == NULL || cpmaHack )
2771 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2772 trace.forceSunlight = info->si->forceSunlight;
2773 trace.recvShadows = info->recvShadows;
2774 trace.numSurfaces = 1;
2775 trace.surfaces = #
2776 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2778 /* twosided lighting */
2779 trace.twoSided = info->si->twoSided;
2781 /* make light list for this surface */
2782 CreateTraceLightsForSurface( num, &trace );
2785 verts = yDrawVerts + ds->firstVert;
2787 memset( avgColors, 0, sizeof( avgColors ) );
2789 /* walk the surface verts */
2790 for( i = 0; i < ds->numVerts; i++ )
2792 /* get vertex luxel */
2793 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2795 /* color the luxel with raw lightmap num? */
2797 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2799 /* color the luxel with luxel origin? */
2800 else if( debugOrigin )
2802 VectorSubtract( info->maxs, info->mins, temp );
2803 VectorScale( temp, (1.0f / 255.0f), temp );
2804 VectorSubtract( origin, lm->mins, temp2 );
2805 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2806 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2807 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2810 /* color the luxel with the normal */
2811 else if( normalmap )
2813 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2814 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2815 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2818 /* illuminate the vertex */
2821 /* clear vertex luxel */
2822 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2824 /* try at initial origin */
2825 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2826 if( trace.cluster >= 0 )
2829 VectorCopy( verts[ i ].xyz, trace.origin );
2830 VectorCopy( verts[ i ].normal, trace.normal );
2833 if( dirty && !bouncing )
2834 dirt = DirtForSample( &trace );
2838 /* jal: floodlight */
2839 floodLightAmount = 0.0f;
2840 VectorClear( floodColor );
2841 if( floodlighty && !bouncing )
2843 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2844 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2848 LightingAtSample( &trace, ds->vertexStyles, colors );
2851 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2854 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2856 /* jal: floodlight */
2857 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2860 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2861 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2862 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2866 /* is this sample bright enough? */
2867 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2868 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2869 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2870 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2872 /* nudge the sample point around a bit */
2873 for( x = 0; x < 4; x++ )
2875 /* two's complement 0, 1, -1, 2, -2, etc */
2876 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2878 for( y = 0; y < 4; y++ )
2880 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2882 for( z = 0; z < 4; z++ )
2884 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2887 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2888 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2889 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2891 /* try at nudged origin */
2892 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2893 if( trace.cluster < 0 )
2897 LightingAtSample( &trace, ds->vertexStyles, colors );
2900 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2903 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2905 /* jal: floodlight */
2906 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2909 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2910 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2913 /* bright enough? */
2914 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2915 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2916 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2917 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2924 /* add to average? */
2925 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2926 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2927 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2928 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2931 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2933 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2934 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2939 /* another happy customer */
2940 numVertsIlluminated++;
2943 /* set average color */
2946 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2947 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2951 VectorCopy( ambientColor, avgColors[ 0 ] );
2954 /* clean up and store vertex color */
2955 for( i = 0; i < ds->numVerts; i++ )
2957 /* get vertex luxel */
2958 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2960 /* store average in occluded vertexes */
2961 if( radVertLuxel[ 0 ] < 0.0f )
2963 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2965 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2966 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2969 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2974 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2977 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2978 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2981 if( bouncing || bounce == 0 || !bounceOnly )
2982 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2983 if( !info->si->noVertexLight )
2984 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2988 /* free light list */
2989 FreeTraceLights( &trace );
2991 /* return to sender */
2995 /* -----------------------------------------------------------------
2996 reconstitute vertex lighting from the luxels
2997 ----------------------------------------------------------------- */
2999 /* set styles from lightmap */
3000 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3001 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3003 /* get max search radius */
3005 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3007 /* walk the surface verts */
3008 verts = yDrawVerts + ds->firstVert;
3009 for( i = 0; i < ds->numVerts; i++ )
3011 /* do each lightmap */
3012 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3015 if( lm->superLuxels[ lightmapNum ] == NULL )
3018 /* get luxel coords */
3019 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3020 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3023 else if( x >= lm->sw )
3027 else if( y >= lm->sh )
3030 /* get vertex luxels */
3031 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3032 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3034 /* color the luxel with the normal? */
3037 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3038 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3039 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3042 /* color the luxel with surface num? */
3043 else if( debugSurfaces )
3044 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3046 /* divine color from the superluxels */
3049 /* increasing radius */
3050 VectorClear( radVertLuxel );
3052 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3054 /* sample within radius */
3055 for( sy = (y - radius); sy <= (y + radius); sy++ )
3057 if( sy < 0 || sy >= lm->sh )
3060 for( sx = (x - radius); sx <= (x + radius); sx++ )
3062 if( sx < 0 || sx >= lm->sw )
3065 /* get luxel particulars */
3066 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3067 cluster = SUPER_CLUSTER( sx, sy );
3071 /* testing: must be brigher than ambient color */
3072 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3075 /* add its distinctiveness to our own */
3076 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3077 samples += luxel[ 3 ];
3083 if( samples > 0.0f )
3084 VectorDivide( radVertLuxel, samples, radVertLuxel );
3086 VectorCopy( ambientColor, radVertLuxel );
3089 /* store into floating point storage */
3090 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3091 numVertsIlluminated++;
3093 /* store into bytes (for vertex approximation) */
3094 if( !info->si->noVertexLight )
3095 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3102 /* -------------------------------------------------------------------------------
3104 light optimization (-fast)
3106 creates a list of lights that will affect a surface and stores it in tw
3107 this is to optimize surface lighting by culling out as many of the
3108 lights in the world as possible from further calculation
3110 ------------------------------------------------------------------------------- */
3114 determines opaque brushes in the world and find sky shaders for sunlight calculations
3117 void SetupBrushes( void )
3119 int i, j, b, compileFlags;
3122 bspBrushSide_t *side;
3123 bspShader_t *shader;
3128 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3131 if( opaqueBrushes == NULL )
3132 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3135 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3136 numOpaqueBrushes = 0;
3138 /* walk the list of worldspawn brushes */
3139 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3142 b = bspModels[ 0 ].firstBSPBrush + i;
3143 brush = &bspBrushes[ b ];
3145 /* check all sides */
3148 for( j = 0; j < brush->numSides && inside; j++ )
3150 /* do bsp shader calculations */
3151 side = &bspBrushSides[ brush->firstSide + j ];
3152 shader = &bspShaders[ side->shaderNum ];
3154 /* get shader info */
3155 si = ShaderInfoForShader( shader->shader );
3159 /* or together compile flags */
3160 compileFlags |= si->compileFlags;
3163 /* determine if this brush is opaque to light */
3164 if( !(compileFlags & C_TRANSLUCENT) )
3166 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3172 /* emit some statistics */
3173 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3180 determines if two clusters are visible to each other using the PVS
3183 qboolean ClusterVisible( int a, int b )
3185 int portalClusters, leafBytes;
3190 if( a < 0 || b < 0 )
3198 if( numBSPVisBytes <=8 )
3202 portalClusters = ((int *) bspVisBytes)[ 0 ];
3203 leafBytes = ((int*) bspVisBytes)[ 1 ];
3204 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3207 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3216 borrowed from vlight.c
3219 int PointInLeafNum_r( vec3_t point, int nodenum )
3227 while( nodenum >= 0 )
3229 node = &bspNodes[ nodenum ];
3230 plane = &bspPlanes[ node->planeNum ];
3231 dist = DotProduct( point, plane->normal ) - plane->dist;
3233 nodenum = node->children[ 0 ];
3234 else if( dist < -0.1 )
3235 nodenum = node->children[ 1 ];
3238 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3239 if( bspLeafs[ leafnum ].cluster != -1 )
3241 nodenum = node->children[ 1 ];
3245 leafnum = -nodenum - 1;
3253 borrowed from vlight.c
3256 int PointInLeafNum( vec3_t point )
3258 return PointInLeafNum_r( point, 0 );
3264 ClusterVisibleToPoint() - ydnar
3265 returns qtrue if point can "see" cluster
3268 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3273 /* get leafNum for point */
3274 pointCluster = ClusterForPoint( point );
3275 if( pointCluster < 0 )
3279 return ClusterVisible( pointCluster, cluster );
3285 ClusterForPoint() - ydnar
3286 returns the pvs cluster for point
3289 int ClusterForPoint( vec3_t point )
3294 /* get leafNum for point */
3295 leafNum = PointInLeafNum( point );
3299 /* return the cluster */
3300 return bspLeafs[ leafNum ].cluster;
3306 ClusterForPointExt() - ydnar
3307 also takes brushes into account for occlusion testing
3310 int ClusterForPointExt( vec3_t point, float epsilon )
3312 int i, j, b, leafNum, cluster;
3315 int *brushes, numBSPBrushes;
3321 /* get leaf for point */
3322 leafNum = PointInLeafNum( point );
3325 leaf = &bspLeafs[ leafNum ];
3327 /* get the cluster */
3328 cluster = leaf->cluster;
3332 /* transparent leaf, so check point against all brushes in the leaf */
3333 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3334 numBSPBrushes = leaf->numBSPLeafBrushes;
3335 for( i = 0; i < numBSPBrushes; i++ )
3339 if( b > maxOpaqueBrush )
3341 brush = &bspBrushes[ b ];
3342 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3345 /* check point against all planes */
3347 for( j = 0; j < brush->numSides && inside; j++ )
3349 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3350 dot = DotProduct( point, plane->normal );
3356 /* if inside, return bogus cluster */
3361 /* if the point made it this far, it's not inside any opaque brushes */
3368 ClusterForPointExtFilter() - ydnar
3369 adds cluster checking against a list of known valid clusters
3372 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3377 /* get cluster for point */
3378 cluster = ClusterForPointExt( point, epsilon );
3380 /* check if filtering is necessary */
3381 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3385 for( i = 0; i < numClusters; i++ )
3387 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3398 ShaderForPointInLeaf() - ydnar
3399 checks a point against all brushes in a leaf, returning the shader of the brush
3400 also sets the cumulative surface and content flags for the brush hit
3403 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3408 int *brushes, numBSPBrushes;
3411 bspBrushSide_t *side;
3413 bspShader_t *shader;
3414 int allSurfaceFlags, allContentFlags;
3417 /* clear things out first */
3424 leaf = &bspLeafs[ leafNum ];
3426 /* transparent leaf, so check point against all brushes in the leaf */
3427 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3428 numBSPBrushes = leaf->numBSPLeafBrushes;
3429 for( i = 0; i < numBSPBrushes; i++ )
3432 brush = &bspBrushes[ brushes[ i ] ];
3434 /* check point against all planes */
3436 allSurfaceFlags = 0;
3437 allContentFlags = 0;
3438 for( j = 0; j < brush->numSides && inside; j++ )
3440 side = &bspBrushSides[ brush->firstSide + j ];
3441 plane = &bspPlanes[ side->planeNum ];
3442 dot = DotProduct( point, plane->normal );
3448 shader = &bspShaders[ side->shaderNum ];
3449 allSurfaceFlags |= shader->surfaceFlags;
3450 allContentFlags |= shader->contentFlags;
3454 /* handle if inside */
3457 /* if there are desired flags, check for same and continue if they aren't matched */
3458 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3460 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3463 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3464 *surfaceFlags = allSurfaceFlags;
3465 *contentFlags = allContentFlags;
3466 return brush->shaderNum;
3470 /* if the point made it this far, it's not inside any brushes */
3478 chops a bounding box by the plane defined by origin and normal
3479 returns qfalse if the bounds is entirely clipped away
3481 this is not exactly the fastest way to do this...
3484 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3486 /* FIXME: rewrite this so it doesn't use bloody brushes */
3494 calculates each light's effective envelope,
3495 taking into account brightness, type, and pvs.
3498 #define LIGHT_EPSILON 0.125f
3499 #define LIGHT_NUDGE 2.0f
3501 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3503 int i, x, y, z, x1, y1, z1;
3504 light_t *light, *light2, **owner;
3506 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3507 float radius, intensity;
3508 light_t *buckets[ 256 ];
3511 /* early out for weird cases where there are no lights */
3512 if( lights == NULL )
3516 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3520 numCulledLights = 0;
3522 while( *owner != NULL )
3527 /* handle negative lights */
3528 if( light->photons < 0.0f || light->add < 0.0f )
3530 light->photons *= -1.0f;
3531 light->add *= -1.0f;
3532 light->flags |= LIGHT_NEGATIVE;
3536 if( light->type == EMIT_SUN )
3540 light->envelope = MAX_WORLD_COORD * 8.0f;
3541 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3542 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3545 /* everything else */
3548 /* get pvs cluster for light */
3549 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3551 /* invalid cluster? */
3552 if( light->cluster < 0 )
3554 /* nudge the sample point around a bit */
3555 for( x = 0; x < 4; x++ )
3557 /* two's complement 0, 1, -1, 2, -2, etc */
3558 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3560 for( y = 0; y < 4; y++ )
3562 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3564 for( z = 0; z < 4; z++ )
3566 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3569 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3570 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3571 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3573 /* try at nudged origin */
3574 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3575 if( light->cluster < 0 )
3579 VectorCopy( origin, light->origin );
3585 /* only calculate for lights in pvs and outside of opaque brushes */
3586 if( light->cluster >= 0 )
3588 /* set light fast flag */
3590 light->flags |= LIGHT_FAST_TEMP;
3592 light->flags &= ~LIGHT_FAST_TEMP;
3593 if( light->si && light->si->noFast )
3594 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3596 /* clear light envelope */
3597 light->envelope = 0;
3599 /* handle area lights */
3600 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3602 /* ugly hack to calculate extent for area lights, but only done once */
3603 VectorScale( light->normal, -1.0f, dir );
3604 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3608 VectorMA( light->origin, radius, light->normal, origin );
3609 factor = PointToPolygonFormFactor( origin, dir, light->w );
3612 if( (factor * light->add) <= light->falloffTolerance )
3613 light->envelope = radius;
3616 /* check for fast mode */
3617 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3618 light->envelope = MAX_WORLD_COORD * 8.0f;
3623 intensity = light->photons;
3627 if( light->envelope <= 0.0f )
3629 /* solve distance for non-distance lights */
3630 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3631 light->envelope = MAX_WORLD_COORD * 8.0f;
3633 /* solve distance for linear lights */
3634 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3635 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3636 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3639 add = angle * light->photons * linearScale - (dist * light->fade);
3640 T = (light->photons * linearScale) - (dist * light->fade);
3641 T + (dist * light->fade) = (light->photons * linearScale);
3642 dist * light->fade = (light->photons * linearScale) - T;
3643 dist = ((light->photons * linearScale) - T) / light->fade;
3646 /* solve for inverse square falloff */
3648 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3651 add = light->photons / (dist * dist);
3652 T = light->photons / (dist * dist);
3653 T * (dist * dist) = light->photons;
3654 dist = sqrt( light->photons / T );
3658 /* chop radius against pvs */
3661 ClearBounds( mins, maxs );
3663 /* check all leaves */
3664 for( i = 0; i < numBSPLeafs; i++ )
3667 leaf = &bspLeafs[ i ];
3670 if( leaf->cluster < 0 )
3672 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3675 /* add this leafs bbox to the bounds */
3676 VectorCopy( leaf->mins, origin );
3677 AddPointToBounds( origin, mins, maxs );
3678 VectorCopy( leaf->maxs, origin );
3679 AddPointToBounds( origin, mins, maxs );
3682 /* test to see if bounds encompass light */
3683 for( i = 0; i < 3; i++ )
3685 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3687 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3688 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3689 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3690 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3691 AddPointToBounds( light->origin, mins, maxs );
3695 /* chop the bounds by a plane for area lights and spotlights */
3696 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3697 ChopBounds( mins, maxs, light->origin, light->normal );
3700 VectorCopy( mins, light->mins );
3701 VectorCopy( maxs, light->maxs );
3703 /* reflect bounds around light origin */
3704 //% VectorMA( light->origin, -1.0f, origin, origin );
3705 VectorScale( light->origin, 2, origin );
3706 VectorSubtract( origin, maxs, origin );
3707 AddPointToBounds( origin, mins, maxs );
3708 //% VectorMA( light->origin, -1.0f, mins, origin );
3709 VectorScale( light->origin, 2, origin );
3710 VectorSubtract( origin, mins, origin );
3711 AddPointToBounds( origin, mins, maxs );
3713 /* calculate spherical bounds */
3714 VectorSubtract( maxs, light->origin, dir );
3715 radius = (float) VectorLength( dir );
3717 /* if this radius is smaller than the envelope, then set the envelope to it */
3718 if( radius < light->envelope )
3720 light->envelope = radius;
3721 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3724 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3727 /* add grid/surface only check */
3730 if( !(light->flags & LIGHT_GRID) )
3731 light->envelope = 0.0f;
3735 if( !(light->flags & LIGHT_SURFACES) )
3736 light->envelope = 0.0f;
3741 if( light->cluster < 0 || light->envelope <= 0.0f )
3744 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3746 /* delete the light */
3748 *owner = light->next;
3749 if( light->w != NULL )
3756 /* square envelope */
3757 light->envelope2 = (light->envelope * light->envelope);
3759 /* increment light count */
3762 /* set next light */
3763 owner = &((**owner).next);
3766 /* bucket sort lights by style */
3767 memset( buckets, 0, sizeof( buckets ) );
3769 for( light = lights; light != NULL; light = light2 )
3771 /* get next light */
3772 light2 = light->next;
3774 /* filter into correct bucket */
3775 light->next = buckets[ light->style ];
3776 buckets[ light->style ] = light;
3778 /* if any styled light is present, automatically set nocollapse */
3779 if( light->style != LS_NORMAL )
3783 /* filter back into light list */
3785 for( i = 255; i >= 0; i-- )
3788 for( light = buckets[ i ]; light != NULL; light = light2 )
3790 light2 = light->next;
3791 light->next = lights;
3796 /* emit some statistics */
3797 Sys_Printf( "%9d total lights\n", numLights );
3798 Sys_Printf( "%9d culled lights\n", numCulledLights );
3804 CreateTraceLightsForBounds()
3805 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3808 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3812 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3813 float radius, dist, length;
3816 /* potential pre-setup */
3817 if( numLights == 0 )
3818 SetupEnvelopes( qfalse, fast );
3821 //% 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 ] );
3823 /* allocate the light list */
3824 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3825 trace->numLights = 0;
3827 /* calculate spherical bounds */
3828 VectorAdd( mins, maxs, origin );
3829 VectorScale( origin, 0.5f, origin );
3830 VectorSubtract( maxs, origin, dir );
3831 radius = (float) VectorLength( dir );
3833 /* get length of normal vector */
3834 if( normal != NULL )
3835 length = VectorLength( normal );
3838 normal = nullVector;
3842 /* test each light and see if it reaches the sphere */
3843 /* note: the attenuation code MUST match LightingAtSample() */
3844 for( light = lights; light; light = light->next )
3846 /* check zero sized envelope */
3847 if( light->envelope <= 0 )
3849 lightsEnvelopeCulled++;
3854 if( !(light->flags & flags) )
3857 /* sunlight skips all this nonsense */
3858 if( light->type != EMIT_SUN )
3864 /* check against pvs cluster */
3865 if( numClusters > 0 && clusters != NULL )
3867 for( i = 0; i < numClusters; i++ )
3869 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3874 if( i == numClusters )
3876 lightsClusterCulled++;
3881 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3882 VectorSubtract( light->origin, origin, dir );
3883 dist = VectorLength( dir );
3884 dist -= light->envelope;
3888 lightsEnvelopeCulled++;
3892 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3895 for( i = 0; i < 3; i++ )
3897 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3902 lightsBoundsCulled++;
3908 /* planar surfaces (except twosided surfaces) have a couple more checks */
3909 if( length > 0.0f && trace->twoSided == qfalse )
3911 /* lights coplanar with a surface won't light it */
3912 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3914 lightsPlaneCulled++;
3918 /* check to see if light is behind the plane */
3919 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3921 lightsPlaneCulled++;
3926 /* add this light */
3927 trace->lights[ trace->numLights++ ] = light;
3930 /* make last night null */
3931 trace->lights[ trace->numLights ] = NULL;
3936 void FreeTraceLights( trace_t *trace )
3938 if( trace->lights != NULL )
3939 free( trace->lights );
3945 CreateTraceLightsForSurface()
3946 creates a list of lights that can potentially affect a drawsurface
3949 void CreateTraceLightsForSurface( int num, trace_t *trace )
3952 vec3_t mins, maxs, normal;
3954 bspDrawSurface_t *ds;
3955 surfaceInfo_t *info;
3962 /* get drawsurface and info */
3963 ds = &bspDrawSurfaces[ num ];
3964 info = &surfaceInfos[ num ];
3966 /* get the mins/maxs for the dsurf */
3967 ClearBounds( mins, maxs );
3968 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3969 for( i = 0; i < ds->numVerts; i++ )
3971 dv = &yDrawVerts[ ds->firstVert + i ];
3972 AddPointToBounds( dv->xyz, mins, maxs );
3973 if( !VectorCompare( dv->normal, normal ) )
3974 VectorClear( normal );
3977 /* create the lights for the bounding box */
3978 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3981 /////////////////////////////////////////////////////////////
3983 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3984 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3985 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3986 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3988 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3989 static int numFloodVectors = 0;
3991 void SetupFloodLight( void )
3994 float angle, elevation, angleStep, elevationStep;
3996 double v1,v2,v3,v4,v5;
3999 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4001 /* calculate angular steps */
4002 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4003 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4007 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4009 /* iterate elevation */
4010 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4012 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4013 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4014 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4019 /* emit some statistics */
4020 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4023 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4025 if( value[ 0 ] != '\0' )
4028 v4=floodlightDistance;
4029 v5=floodlightIntensity;
4031 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
4033 floodlightRGB[0]=v1;
4034 floodlightRGB[1]=v2;
4035 floodlightRGB[2]=v3;
4037 if (VectorLength(floodlightRGB)==0)
4039 VectorSet(floodlightRGB,240,240,255);
4045 floodlightDistance=v4;
4046 floodlightIntensity=v5;
4048 floodlighty = qtrue;
4049 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4053 VectorSet(floodlightRGB,240,240,255);
4054 //floodlighty = qtrue;
4055 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4057 VectorNormalize(floodlightRGB,floodlightRGB);
4061 FloodLightForSample()
4062 calculates floodlight value for a given sample
4063 once again, kudos to the dirtmapping coder
4066 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4072 float gatherLight, outLight;
4073 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4081 if( trace == NULL || trace->cluster < 0 )
4086 dd = floodLightDistance;
4087 VectorCopy( trace->normal, normal );
4089 /* check if the normal is aligned to the world-up */
4090 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4092 if( normal[ 2 ] == 1.0f )
4094 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4095 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4097 else if( normal[ 2 ] == -1.0f )
4099 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4100 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4105 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4106 CrossProduct( normal, worldUp, myRt );
4107 VectorNormalize( myRt, myRt );
4108 CrossProduct( myRt, normal, myUp );
4109 VectorNormalize( myUp, myUp );
4112 /* vortex: optimise floodLightLowQuality a bit */
4113 if ( floodLightLowQuality == qtrue )
4115 /* iterate through ordered vectors */
4116 for( i = 0; i < numFloodVectors; i++ )
4117 if (rand()%10 != 0 ) continue;
4121 /* iterate through ordered vectors */
4122 for( i = 0; i < numFloodVectors; i++ )
4126 /* transform vector into tangent space */
4127 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4128 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4129 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4132 VectorMA( trace->origin, dd, direction, trace->end );
4134 //VectorMA( trace->origin, 1, direction, trace->origin );
4136 SetupTrace( trace );
4141 if (trace->compileFlags & C_SKY )
4145 else if ( trace->opaque )
4147 VectorSubtract( trace->hit, trace->origin, displacement );
4148 d=VectorLength( displacement );
4150 // d=trace->distance;
4151 //if (d>256) gatherDirt+=1;
4153 if (contribution>1) contribution=1.0f;
4155 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4158 gatherLight+=contribution;
4163 if( gatherLight <= 0.0f )
4171 outLight=gatherLight;
4172 if( outLight > 1.0f )
4175 /* return to sender */
4180 FloodLightRawLightmap
4181 lighttracer style ambient occlusion light hack.
4182 Kudos to the dirtmapping author for most of this source.
4183 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4184 VorteX: fixed problems with deluxemapping
4187 // floodlight pass on a lightmap
4188 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4190 int i, x, y, *cluster;
4191 float *origin, *normal, *floodlight, floodLightAmount;
4192 surfaceInfo_t *info;
4195 // float samples, average, *floodlight2;
4197 memset(&trace,0,sizeof(trace_t));
4200 trace.testOcclusion = qtrue;
4201 trace.forceSunlight = qfalse;
4202 trace.twoSided = qtrue;
4203 trace.recvShadows = lm->recvShadows;
4204 trace.numSurfaces = lm->numLightSurfaces;
4205 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4206 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4207 trace.testAll = qfalse;
4208 trace.distance = 1024;
4210 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4211 //trace.twoSided = qfalse;
4212 for( i = 0; i < trace.numSurfaces; i++ )
4215 info = &surfaceInfos[ trace.surfaces[ i ] ];
4217 /* check twosidedness */
4218 if( info->si->twoSided )
4220 trace.twoSided = qtrue;
4225 /* gather floodlight */
4226 for( y = 0; y < lm->sh; y++ )
4228 for( x = 0; x < lm->sw; x++ )
4231 cluster = SUPER_CLUSTER( x, y );
4232 origin = SUPER_ORIGIN( x, y );
4233 normal = SUPER_NORMAL( x, y );
4234 floodlight = SUPER_FLOODLIGHT( x, y );
4236 /* set default dirt */
4239 /* only look at mapped luxels */
4244 trace.cluster = *cluster;
4245 VectorCopy( origin, trace.origin );
4246 VectorCopy( normal, trace.normal );
4248 /* get floodlight */
4249 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4251 /* add floodlight */
4252 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4253 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4254 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4255 floodlight[3] += floodlightDirectionScale;
4259 /* testing no filtering */
4265 for( y = 0; y < lm->sh; y++ )
4267 for( x = 0; x < lm->sw; x++ )
4270 cluster = SUPER_CLUSTER( x, y );
4271 floodlight = SUPER_FLOODLIGHT(x, y );
4273 /* filter dirt by adjacency to unmapped luxels */
4274 average = *floodlight;
4276 for( sy = (y - 1); sy <= (y + 1); sy++ )
4278 if( sy < 0 || sy >= lm->sh )
4281 for( sx = (x - 1); sx <= (x + 1); sx++ )
4283 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4286 /* get neighboring luxel */
4287 cluster = SUPER_CLUSTER( sx, sy );
4288 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4289 if( *cluster < 0 || *floodlight2 <= 0.0f )
4293 average += *floodlight2;
4298 if( samples <= 0.0f )
4303 if( samples <= 0.0f )
4307 *floodlight = average / samples;
4313 void FloodLightRawLightmap( int rawLightmapNum )
4317 /* bail if this number exceeds the number of raw lightmaps */
4318 if( rawLightmapNum >= numRawLightmaps )
4321 lm = &rawLightmaps[ rawLightmapNum ];
4324 if (floodlighty && floodlightIntensity)
4325 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4328 if (lm->floodlightIntensity)
4330 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4331 numSurfacesFloodlighten += 1;
4335 void FloodlightRawLightmaps()
4337 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4338 numSurfacesFloodlighten = 0;
4339 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4340 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4344 FloodLightIlluminate()
4345 illuminate floodlight into lightmap luxels
4348 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4350 float *luxel, *floodlight, *deluxel, *normal;
4353 int x, y, lightmapNum;
4355 /* walk lightmaps */
4356 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4359 if( lm->superLuxels[ lightmapNum ] == NULL )
4362 /* apply floodlight to each luxel */
4363 for( y = 0; y < lm->sh; y++ )
4365 for( x = 0; x < lm->sw; x++ )
4367 /* get floodlight */
4368 floodlight = SUPER_FLOODLIGHT( x, y );
4369 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4373 cluster = SUPER_CLUSTER( x, y );
4375 /* only process mapped luxels */
4379 /* get particulars */
4380 luxel = SUPER_LUXEL( lightmapNum, x, y );
4381 deluxel = SUPER_DELUXEL( x, y );
4383 /* add to lightmap */
4384 luxel[0]+=floodlight[0];
4385 luxel[1]+=floodlight[1];
4386 luxel[2]+=floodlight[2];
4388 if (luxel[3]==0) luxel[3]=1;
4390 /* add to deluxemap */
4391 if (deluxemap && floodlight[3] > 0)
4395 normal = SUPER_NORMAL( x, y );
4396 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4398 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4399 if(brightness < 0.00390625f)
4400 brightness = 0.00390625f;
4402 VectorScale( normal, brightness, lightvector );
4403 VectorAdd( deluxel, lightvector, deluxel );