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 );
127 sample[0] = floor(Image_sRGBFloatFromLinearFloat(sample[0] * (1.0 / 255.0)) * 255.0 + 0.5);
128 sample[1] = floor(Image_sRGBFloatFromLinearFloat(sample[1] * (1.0 / 255.0)) * 255.0 + 0.5);
129 sample[2] = floor(Image_sRGBFloatFromLinearFloat(sample[2] * (1.0 / 255.0)) * 255.0 + 0.5);
133 colorBytes[ 0 ] = sample[ 0 ];
134 colorBytes[ 1 ] = sample[ 1 ];
135 colorBytes[ 2 ] = sample[ 2 ];
140 /* -------------------------------------------------------------------------------
142 this section deals with phong shading (normal interpolation across brush faces)
144 ------------------------------------------------------------------------------- */
148 smooths together coincident vertex normals across the bsp
151 #define MAX_SAMPLES 256
152 #define THETA_EPSILON 0.000001
153 #define EQUAL_NORMAL_EPSILON 0.01
155 void SmoothNormals( void )
157 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
158 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
159 bspDrawSurface_t *ds;
163 vec3_t average, diff;
164 int indexes[ MAX_SAMPLES ];
165 vec3_t votes[ MAX_SAMPLES ];
168 /* allocate shade angle table */
169 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
170 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
172 /* allocate smoothed table */
173 cs = (numBSPDrawVerts / 8) + 1;
174 smoothed = safe_malloc( cs );
175 memset( smoothed, 0, cs );
177 /* set default shade angle */
178 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
181 /* run through every surface and flag verts belonging to non-lightmapped surfaces
182 and set per-vertex smoothing angle */
183 for( i = 0; i < numBSPDrawSurfaces; i++ )
186 ds = &bspDrawSurfaces[ i ];
188 /* get shader for shade angle */
189 si = surfaceInfos[ i ].si;
190 if( si->shadeAngleDegrees )
191 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
193 shadeAngle = defaultShadeAngle;
194 if( shadeAngle > maxShadeAngle )
195 maxShadeAngle = shadeAngle;
198 for( j = 0; j < ds->numVerts; j++ )
200 f = ds->firstVert + j;
201 shadeAngles[ f ] = shadeAngle;
202 if( ds->surfaceType == MST_TRIANGLE_SOUP )
203 smoothed[ f >> 3 ] |= (1 << (f & 7));
206 /* ydnar: optional force-to-trisoup */
207 if( trisoup && ds->surfaceType == MST_PLANAR )
209 ds->surfaceType = MST_TRIANGLE_SOUP;
210 ds->lightmapNum[ 0 ] = -3;
214 /* bail if no surfaces have a shade angle */
215 if( maxShadeAngle == 0 )
224 start = I_FloatTime();
226 /* go through the list of vertexes */
227 for( i = 0; i < numBSPDrawVerts; i++ )
230 f = 10 * i / numBSPDrawVerts;
234 Sys_Printf( "%i...", f );
237 /* already smoothed? */
238 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
242 VectorClear( average );
246 /* build a table of coincident vertexes */
247 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
249 /* already smoothed? */
250 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
254 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
257 /* use smallest shade angle */
258 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
260 /* check shade angle */
261 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
264 else if( dot < -1.0 )
266 testAngle = acos( dot ) + THETA_EPSILON;
267 if( testAngle >= shadeAngle )
269 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
272 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
274 /* add to the list */
275 indexes[ numVerts++ ] = j;
278 smoothed[ j >> 3 ] |= (1 << (j & 7));
280 /* see if this normal has already been voted */
281 for( k = 0; k < numVotes; k++ )
283 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
284 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
285 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
286 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
290 /* add a new vote? */
291 if( k == numVotes && numVotes < MAX_SAMPLES )
293 VectorAdd( average, bspDrawVerts[ j ].normal, average );
294 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
299 /* don't average for less than 2 verts */
304 if( VectorNormalize( average, average ) > 0 )
307 for( j = 0; j < numVerts; j++ )
308 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
312 /* free the tables */
317 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
322 /* -------------------------------------------------------------------------------
324 this section deals with phong shaded lightmap tracing
326 ------------------------------------------------------------------------------- */
328 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
332 calculates the st tangent vectors for normalmapping
335 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
342 /* calculate barycentric basis for the triangle */
343 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 ]);
344 if( fabs( bb ) < 0.00000001f )
348 for( i = 0; i < numVerts; i++ )
350 /* calculate s tangent vector */
351 s = dv[ i ]->st[ 0 ] + 10.0f;
352 t = dv[ i ]->st[ 1 ];
353 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
354 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
355 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
357 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
358 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
359 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
361 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
362 VectorNormalize( stv[ i ], stv[ i ] );
364 /* calculate t tangent vector */
365 s = dv[ i ]->st[ 0 ];
366 t = dv[ i ]->st[ 1 ] + 10.0f;
367 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
368 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
369 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
371 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
372 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
373 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
375 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
376 VectorNormalize( ttv[ i ], ttv[ i ] );
379 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
380 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
383 /* return to caller */
392 perterbs the normal by the shader's normalmap in tangent space
395 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
402 VectorCopy( dv->normal, pNormal );
404 /* sample normalmap */
405 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
408 /* remap sampled normal from [0,255] to [-1,-1] */
409 for( i = 0; i < 3; i++ )
410 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
412 /* scale tangent vectors and add to original normal */
413 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
414 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
415 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
417 /* renormalize and return */
418 VectorNormalize( pNormal, pNormal );
425 maps a luxel for triangle bv at
429 #define BOGUS_NUDGE -99999.0f
431 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 ] )
433 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
434 float *luxel, *origin, *normal, d, lightmapSampleOffset;
441 vec4_t sideplane, hostplane;
446 static float nudges[][ 2 ] =
448 //%{ 0, 0 }, /* try center first */
449 { -NUDGE, 0 }, /* left */
450 { NUDGE, 0 }, /* right */
451 { 0, NUDGE }, /* up */
452 { 0, -NUDGE }, /* down */
453 { -NUDGE, NUDGE }, /* left/up */
454 { NUDGE, -NUDGE }, /* right/down */
455 { NUDGE, NUDGE }, /* right/up */
456 { -NUDGE, -NUDGE }, /* left/down */
457 { BOGUS_NUDGE, BOGUS_NUDGE }
461 /* find luxel xy coords (fixme: subtract 0.5?) */
462 x = dv->lightmap[ 0 ][ 0 ];
463 y = dv->lightmap[ 0 ][ 1 ];
466 else if( x >= lm->sw )
470 else if( y >= lm->sh )
473 /* set shader and cluster list */
477 numClusters = info->numSurfaceClusters;
478 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
487 /* get luxel, origin, cluster, and normal */
488 luxel = SUPER_LUXEL( 0, x, y );
489 origin = SUPER_ORIGIN( x, y );
490 normal = SUPER_NORMAL( x, y );
491 cluster = SUPER_CLUSTER( x, y );
493 /* don't attempt to remap occluded luxels for planar surfaces */
494 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
497 /* only average the normal for premapped luxels */
498 else if( (*cluster) >= 0 )
500 /* do bumpmap calculations */
502 PerturbNormal( dv, si, pNormal, stv, ttv );
504 VectorCopy( dv->normal, pNormal );
506 /* add the additional normal data */
507 VectorAdd( normal, pNormal, normal );
512 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
516 /* axial lightmap projection */
517 if( lm->vecs != NULL )
519 /* calculate an origin for the sample from the lightmap vectors */
520 VectorCopy( lm->origin, origin );
521 for( i = 0; i < 3; i++ )
523 /* add unless it's the axis, which is taken care of later */
524 if( i == lm->axisNum )
526 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
529 /* project the origin onto the plane */
530 d = DotProduct( origin, plane ) - plane[ 3 ];
531 d /= plane[ lm->axisNum ];
532 origin[ lm->axisNum ] -= d;
535 /* non axial lightmap projection (explicit xyz) */
537 VectorCopy( dv->xyz, origin );
539 //////////////////////
540 //27's test to make sure samples stay within the triangle boundaries
541 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
542 //2) if it does, nudge it onto the correct side.
544 if (worldverts!=NULL && lightmapTriangleCheck)
548 VectorCopy(worldverts[j],cverts[j]);
550 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
556 //build plane using 2 edges and a normal
559 VectorCopy(cverts[next],temp);
560 VectorAdd(temp,hostplane,temp);
561 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
563 //planetest sample point
564 e=DotProduct(origin,sideplane);
569 //VectorClear(origin);
570 //Move the sample point back inside triangle bounds
571 origin[0]-=sideplane[0]*(e+1);
572 origin[1]-=sideplane[1]*(e+1);
573 origin[2]-=sideplane[2]*(e+1);
582 ////////////////////////
584 /* planar surfaces have precalculated lightmap vectors for nudging */
585 if( lm->plane != NULL )
587 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
588 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
589 VectorCopy( lm->plane, vecs[ 2 ] );
592 /* non-planar surfaces must calculate them */
596 VectorCopy( plane, vecs[ 2 ] );
598 VectorCopy( dv->normal, vecs[ 2 ] );
599 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
602 /* push the origin off the surface a bit */
604 lightmapSampleOffset = si->lightmapSampleOffset;
606 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
607 if( lm->axisNum < 0 )
608 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
609 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
610 origin[ lm->axisNum ] -= lightmapSampleOffset;
612 origin[ lm->axisNum ] += lightmapSampleOffset;
614 VectorCopy(origin,origintwo);
615 if(lightmapExtraVisClusterNudge)
617 origintwo[0]+=vecs[2][0];
618 origintwo[1]+=vecs[2][1];
619 origintwo[2]+=vecs[2][2];
623 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
625 /* another retarded hack, storing nudge count in luxel[ 1 ] */
628 /* point in solid? (except in dark mode) */
629 if( pointCluster < 0 && dark == qfalse )
631 /* nudge the the location around */
633 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
635 /* nudge the vector around a bit */
636 for( i = 0; i < 3; i++ )
638 /* set nudged point*/
639 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
643 /* get pvs cluster */
644 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
645 if( pointCluster >= 0 )
646 VectorCopy( nudged, origin );
651 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
652 if( pointCluster < 0 && si != NULL && dark == qfalse )
654 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
655 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
656 if( pointCluster >= 0 )
657 VectorCopy( nudged, origin );
662 if( pointCluster < 0 )
664 (*cluster) = CLUSTER_OCCLUDED;
665 VectorClear( origin );
666 VectorClear( normal );
672 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
674 /* do bumpmap calculations */
676 PerturbNormal( dv, si, pNormal, stv, ttv );
678 VectorCopy( dv->normal, pNormal );
680 /* store the cluster and normal */
681 (*cluster) = pointCluster;
682 VectorCopy( pNormal, normal );
684 /* store explicit mapping pass and implicit mapping pass */
699 recursively subdivides a triangle until its edges are shorter
700 than the distance between two luxels (thanks jc :)
703 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 ] )
705 bspDrawVert_t mid, *dv2[ 3 ];
709 /* map the vertexes */
711 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
712 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
713 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
719 float *a, *b, dx, dy, dist, maxDist;
722 /* find the longest edge and split it */
725 for( i = 0; i < 3; i++ )
728 a = dv[ i ]->lightmap[ 0 ];
729 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
732 dx = a[ 0 ] - b[ 0 ];
733 dy = a[ 1 ] - b[ 1 ];
734 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
744 /* try to early out */
745 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
749 /* split the longest edge and map it */
750 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
751 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
753 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
754 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
756 /* recurse to first triangle */
757 VectorCopy( dv, dv2 );
759 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
761 /* recurse to second triangle */
762 VectorCopy( dv, dv2 );
763 dv2[ (max + 1) % 3 ] = ∣
764 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
771 seed function for MapTriangle_r()
772 requires a cw ordered triangle
775 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
779 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
780 vec3_t worldverts[ 3 ];
783 /* get plane if possible */
784 if( lm->plane != NULL )
786 VectorCopy( lm->plane, plane );
787 plane[ 3 ] = lm->plane[ 3 ];
790 /* otherwise make one from the points */
791 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
794 /* check to see if we need to calculate texture->world tangent vectors */
795 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
806 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
807 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
808 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
810 /* map the vertexes */
811 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
812 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
813 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
815 /* 2002-11-20: prefer axial triangle edges */
818 /* subdivide the triangle */
819 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
823 for( i = 0; i < 3; i++ )
826 bspDrawVert_t *dv2[ 3 ];
830 a = dv[ i ]->lightmap[ 0 ];
831 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
833 /* make degenerate triangles for mapping edges */
834 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
837 dv2[ 1 ] = dv[ (i + 1) % 3 ];
838 dv2[ 2 ] = dv[ (i + 1) % 3 ];
840 /* map the degenerate triangle */
841 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
852 recursively subdivides a quad until its edges are shorter
853 than the distance between two luxels
856 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 ] )
858 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
865 float *a, *b, dx, dy, dist, maxDist;
868 /* find the longest edge and split it */
871 for( i = 0; i < 4; i++ )
874 a = dv[ i ]->lightmap[ 0 ];
875 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
878 dx = a[ 0 ] - b[ 0 ];
879 dy = a[ 1 ] - b[ 1 ];
880 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
890 /* try to early out */
891 if( max < 0 || maxDist <= subdivideThreshold )
895 /* we only care about even/odd edges */
898 /* split the longest edges */
899 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
900 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
902 /* map the vertexes */
903 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
904 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
909 /* recurse to first quad */
911 dv2[ 1 ] = &mid[ 0 ];
912 dv2[ 2 ] = &mid[ 1 ];
914 MapQuad_r( lm, info, dv2, plane, stv, ttv );
916 /* recurse to second quad */
917 dv2[ 0 ] = &mid[ 0 ];
920 dv2[ 3 ] = &mid[ 1 ];
921 MapQuad_r( lm, info, dv2, plane, stv, ttv );
927 /* recurse to first quad */
930 dv2[ 2 ] = &mid[ 0 ];
931 dv2[ 3 ] = &mid[ 1 ];
932 MapQuad_r( lm, info, dv2, plane, stv, ttv );
934 /* recurse to second quad */
935 dv2[ 0 ] = &mid[ 1 ];
936 dv2[ 1 ] = &mid[ 0 ];
939 MapQuad_r( lm, info, dv2, plane, stv, ttv );
947 seed function for MapQuad_r()
948 requires a cw ordered triangle quad
951 #define QUAD_PLANAR_EPSILON 0.5f
953 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
957 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
960 /* get plane if possible */
961 if( lm->plane != NULL )
963 VectorCopy( lm->plane, plane );
964 plane[ 3 ] = lm->plane[ 3 ];
967 /* otherwise make one from the points */
968 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
971 /* 4th point must fall on the plane */
972 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
973 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
976 /* check to see if we need to calculate texture->world tangent vectors */
977 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
988 /* map the vertexes */
989 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
990 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
991 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
992 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
994 /* subdivide the quad */
995 MapQuad_r( lm, info, dv, plane, stv, ttv );
1003 maps the locations, normals, and pvs clusters for a raw lightmap
1006 #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)
1008 void MapRawLightmap( int rawLightmapNum )
1010 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1011 float *luxel, *origin, *normal, samples, radius, pass;
1013 bspDrawSurface_t *ds;
1014 surfaceInfo_t *info;
1015 mesh_t src, *subdivided, *mesh;
1016 bspDrawVert_t *verts, *dv[ 4 ], fake;
1019 /* bail if this number exceeds the number of raw lightmaps */
1020 if( rawLightmapNum >= numRawLightmaps )
1024 lm = &rawLightmaps[ rawLightmapNum ];
1026 /* -----------------------------------------------------------------
1027 map referenced surfaces onto the raw lightmap
1028 ----------------------------------------------------------------- */
1030 /* walk the list of surfaces on this raw lightmap */
1031 for( n = 0; n < lm->numLightSurfaces; n++ )
1033 /* with > 1 surface per raw lightmap, clear occluded */
1036 for( y = 0; y < lm->sh; y++ )
1038 for( x = 0; x < lm->sw; x++ )
1041 cluster = SUPER_CLUSTER( x, y );
1043 *cluster = CLUSTER_UNMAPPED;
1049 num = lightSurfaces[ lm->firstLightSurface + n ];
1050 ds = &bspDrawSurfaces[ num ];
1051 info = &surfaceInfos[ num ];
1053 /* bail if no lightmap to calculate */
1054 if( info->lm != lm )
1060 /* map the surface onto the lightmap origin/cluster/normal buffers */
1061 switch( ds->surfaceType )
1065 verts = yDrawVerts + ds->firstVert;
1067 /* map the triangles */
1068 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1070 for( i = 0; i < ds->numIndexes; i += 3 )
1072 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1073 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1074 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1075 MapTriangle( lm, info, dv, mapNonAxial );
1081 /* make a mesh from the drawsurf */
1082 src.width = ds->patchWidth;
1083 src.height = ds->patchHeight;
1084 src.verts = &yDrawVerts[ ds->firstVert ];
1085 //% subdivided = SubdivideMesh( src, 8, 512 );
1086 subdivided = SubdivideMesh2( src, info->patchIterations );
1088 /* fit it to the curve and remove colinear verts on rows/columns */
1089 PutMeshOnCurve( *subdivided );
1090 mesh = RemoveLinearMeshColumnsRows( subdivided );
1091 FreeMesh( subdivided );
1094 verts = mesh->verts;
1100 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1101 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1102 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1103 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1107 /* map the mesh quads */
1110 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1112 for( y = 0; y < (mesh->height - 1); y++ )
1114 for( x = 0; x < (mesh->width - 1); x++ )
1117 pw[ 0 ] = x + (y * mesh->width);
1118 pw[ 1 ] = x + ((y + 1) * mesh->width);
1119 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1120 pw[ 3 ] = x + 1 + (y * mesh->width);
1121 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1126 /* get drawverts and map first triangle */
1127 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1128 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1129 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1130 MapTriangle( lm, info, dv, mapNonAxial );
1132 /* get drawverts and map second triangle */
1133 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1134 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1135 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1136 MapTriangle( lm, info, dv, mapNonAxial );
1143 for( y = 0; y < (mesh->height - 1); y++ )
1145 for( x = 0; x < (mesh->width - 1); x++ )
1148 pw[ 0 ] = x + (y * mesh->width);
1149 pw[ 1 ] = x + ((y + 1) * mesh->width);
1150 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1151 pw[ 3 ] = x + 1 + (y * mesh->width);
1157 /* attempt to map quad first */
1158 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1159 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1160 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1161 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1162 if( MapQuad( lm, info, dv ) )
1165 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1167 /* get drawverts and map first triangle */
1168 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1169 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1170 MapTriangle( lm, info, dv, mapNonAxial );
1172 /* get drawverts and map second triangle */
1173 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1174 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1175 MapTriangle( lm, info, dv, mapNonAxial );
1191 /* -----------------------------------------------------------------
1192 average and clean up luxel normals
1193 ----------------------------------------------------------------- */
1195 /* walk the luxels */
1196 for( y = 0; y < lm->sh; y++ )
1198 for( x = 0; x < lm->sw; x++ )
1201 luxel = SUPER_LUXEL( 0, x, y );
1202 normal = SUPER_NORMAL( x, y );
1203 cluster = SUPER_CLUSTER( x, y );
1205 /* only look at mapped luxels */
1209 /* the normal data could be the sum of multiple samples */
1210 if( luxel[ 3 ] > 1.0f )
1211 VectorNormalize( normal, normal );
1213 /* mark this luxel as having only one normal */
1218 /* non-planar surfaces stop here */
1219 if( lm->plane == NULL )
1222 /* -----------------------------------------------------------------
1223 map occluded or unuxed luxels
1224 ----------------------------------------------------------------- */
1226 /* walk the luxels */
1227 radius = floor( superSample / 2 );
1228 radius = radius > 0 ? radius : 1.0f;
1230 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1232 for( y = 0; y < lm->sh; y++ )
1234 for( x = 0; x < lm->sw; x++ )
1237 luxel = SUPER_LUXEL( 0, x, y );
1238 normal = SUPER_NORMAL( x, y );
1239 cluster = SUPER_CLUSTER( x, y );
1241 /* only look at unmapped luxels */
1242 if( *cluster != CLUSTER_UNMAPPED )
1245 /* divine a normal and origin from neighboring luxels */
1246 VectorClear( fake.xyz );
1247 VectorClear( fake.normal );
1248 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1249 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1251 for( sy = (y - 1); sy <= (y + 1); sy++ )
1253 if( sy < 0 || sy >= lm->sh )
1256 for( sx = (x - 1); sx <= (x + 1); sx++ )
1258 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1261 /* get neighboring luxel */
1262 luxel = SUPER_LUXEL( 0, sx, sy );
1263 origin = SUPER_ORIGIN( sx, sy );
1264 normal = SUPER_NORMAL( sx, sy );
1265 cluster = SUPER_CLUSTER( sx, sy );
1267 /* only consider luxels mapped in previous passes */
1268 if( *cluster < 0 || luxel[ 0 ] >= pass )
1271 /* add its distinctiveness to our own */
1272 VectorAdd( fake.xyz, origin, fake.xyz );
1273 VectorAdd( fake.normal, normal, fake.normal );
1274 samples += luxel[ 3 ];
1279 if( samples == 0.0f )
1283 VectorDivide( fake.xyz, samples, fake.xyz );
1284 //% VectorDivide( fake.normal, samples, fake.normal );
1285 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1288 /* map the fake vert */
1289 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1294 /* -----------------------------------------------------------------
1295 average and clean up luxel normals
1296 ----------------------------------------------------------------- */
1298 /* walk the luxels */
1299 for( y = 0; y < lm->sh; y++ )
1301 for( x = 0; x < lm->sw; x++ )
1304 luxel = SUPER_LUXEL( 0, x, y );
1305 normal = SUPER_NORMAL( x, y );
1306 cluster = SUPER_CLUSTER( x, y );
1308 /* only look at mapped luxels */
1312 /* the normal data could be the sum of multiple samples */
1313 if( luxel[ 3 ] > 1.0f )
1314 VectorNormalize( normal, normal );
1316 /* mark this luxel as having only one normal */
1324 for( y = 0; y < lm->sh; y++ )
1326 for( x = 0; x < lm->sw; x++ )
1331 cluster = SUPER_CLUSTER( x, y );
1332 origin = SUPER_ORIGIN( x, y );
1333 normal = SUPER_NORMAL( x, y );
1334 luxel = SUPER_LUXEL( x, y );
1339 /* check if within the bounding boxes of all surfaces referenced */
1340 ClearBounds( mins, maxs );
1341 for( n = 0; n < lm->numLightSurfaces; n++ )
1344 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1345 TOL = info->sampleSize + 2;
1346 AddPointToBounds( info->mins, mins, maxs );
1347 AddPointToBounds( info->maxs, mins, maxs );
1348 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1349 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1350 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1355 if( n < lm->numLightSurfaces )
1358 /* report bogus origin */
1359 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",
1360 rawLightmapNum, x, y, *cluster,
1361 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1362 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1363 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1374 sets up dirtmap (ambient occlusion)
1377 #define DIRT_CONE_ANGLE 88 /* degrees */
1378 #define DIRT_NUM_ANGLE_STEPS 16
1379 #define DIRT_NUM_ELEVATION_STEPS 3
1380 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1382 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1383 static int numDirtVectors = 0;
1385 void SetupDirt( void )
1388 float angle, elevation, angleStep, elevationStep;
1392 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1394 /* calculate angular steps */
1395 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1396 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1400 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1402 /* iterate elevation */
1403 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1405 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1406 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1407 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1412 /* emit some statistics */
1413 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1419 calculates dirt value for a given sample
1422 float DirtForSample( trace_t *trace )
1425 float gatherDirt, outDirt, angle, elevation, ooDepth;
1426 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1432 if( trace == NULL || trace->cluster < 0 )
1437 ooDepth = 1.0f / dirtDepth;
1438 VectorCopy( trace->normal, normal );
1440 /* check if the normal is aligned to the world-up */
1441 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1443 if( normal[ 2 ] == 1.0f )
1445 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1446 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1448 else if( normal[ 2 ] == -1.0f )
1450 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1451 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1456 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1457 CrossProduct( normal, worldUp, myRt );
1458 VectorNormalize( myRt, myRt );
1459 CrossProduct( myRt, normal, myUp );
1460 VectorNormalize( myUp, myUp );
1463 /* 1 = random mode, 0 (well everything else) = non-random mode */
1467 for( i = 0; i < numDirtVectors; i++ )
1469 /* get random vector */
1470 angle = Random() * DEG2RAD( 360.0f );
1471 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1472 temp[ 0 ] = cos( angle ) * sin( elevation );
1473 temp[ 1 ] = sin( angle ) * sin( elevation );
1474 temp[ 2 ] = cos( elevation );
1476 /* transform into tangent space */
1477 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1478 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1479 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1482 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1483 SetupTrace( trace );
1487 if( trace->opaque && !(trace->compileFlags & C_SKY) )
1489 VectorSubtract( trace->hit, trace->origin, displacement );
1490 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1496 /* iterate through ordered vectors */
1497 for( i = 0; i < numDirtVectors; i++ )
1499 /* transform vector into tangent space */
1500 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1501 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1502 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1505 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1506 SetupTrace( trace );
1512 VectorSubtract( trace->hit, trace->origin, displacement );
1513 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1519 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1520 SetupTrace( trace );
1526 VectorSubtract( trace->hit, trace->origin, displacement );
1527 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1531 if( gatherDirt <= 0.0f )
1534 /* apply gain (does this even do much? heh) */
1535 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1536 if( outDirt > 1.0f )
1540 outDirt *= dirtScale;
1541 if( outDirt > 1.0f )
1544 /* return to sender */
1545 return 1.0f - outDirt;
1552 calculates dirty fraction for each luxel
1555 void DirtyRawLightmap( int rawLightmapNum )
1557 int i, x, y, sx, sy, *cluster;
1558 float *origin, *normal, *dirt, *dirt2, average, samples;
1560 surfaceInfo_t *info;
1565 /* bail if this number exceeds the number of raw lightmaps */
1566 if( rawLightmapNum >= numRawLightmaps )
1570 lm = &rawLightmaps[ rawLightmapNum ];
1573 trace.testOcclusion = qtrue;
1574 trace.forceSunlight = qfalse;
1575 trace.recvShadows = lm->recvShadows;
1576 trace.numSurfaces = lm->numLightSurfaces;
1577 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1578 trace.inhibitRadius = 0.0f;
1579 trace.testAll = qfalse;
1581 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1582 trace.twoSided = qfalse;
1583 for( i = 0; i < trace.numSurfaces; i++ )
1586 info = &surfaceInfos[ trace.surfaces[ i ] ];
1588 /* check twosidedness */
1589 if( info->si->twoSided )
1591 trace.twoSided = qtrue;
1597 for( i = 0; i < trace.numSurfaces; i++ )
1600 info = &surfaceInfos[ trace.surfaces[ i ] ];
1602 /* check twosidedness */
1603 if( info->si->noDirty )
1611 for( y = 0; y < lm->sh; y++ )
1613 for( x = 0; x < lm->sw; x++ )
1616 cluster = SUPER_CLUSTER( x, y );
1617 origin = SUPER_ORIGIN( x, y );
1618 normal = SUPER_NORMAL( x, y );
1619 dirt = SUPER_DIRT( x, y );
1621 /* set default dirt */
1624 /* only look at mapped luxels */
1628 /* don't apply dirty on this surface */
1636 trace.cluster = *cluster;
1637 VectorCopy( origin, trace.origin );
1638 VectorCopy( normal, trace.normal );
1641 *dirt = DirtForSample( &trace );
1645 /* testing no filtering */
1649 for( y = 0; y < lm->sh; y++ )
1651 for( x = 0; x < lm->sw; x++ )
1654 cluster = SUPER_CLUSTER( x, y );
1655 dirt = SUPER_DIRT( x, y );
1657 /* filter dirt by adjacency to unmapped luxels */
1660 for( sy = (y - 1); sy <= (y + 1); sy++ )
1662 if( sy < 0 || sy >= lm->sh )
1665 for( sx = (x - 1); sx <= (x + 1); sx++ )
1667 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1670 /* get neighboring luxel */
1671 cluster = SUPER_CLUSTER( sx, sy );
1672 dirt2 = SUPER_DIRT( sx, sy );
1673 if( *cluster < 0 || *dirt2 <= 0.0f )
1682 if( samples <= 0.0f )
1687 if( samples <= 0.0f )
1691 *dirt = average / samples;
1700 calculates the pvs cluster, origin, normal of a sub-luxel
1703 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1705 int i, *cluster, *cluster2;
1706 float *origin, *origin2, *normal; //% , *normal2;
1707 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1710 /* calulate x vector */
1711 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1713 cluster = SUPER_CLUSTER( x, y );
1714 origin = SUPER_ORIGIN( x, y );
1715 //% normal = SUPER_NORMAL( x, y );
1716 cluster2 = SUPER_CLUSTER( x + 1, y );
1717 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1718 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1720 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1722 cluster = SUPER_CLUSTER( x - 1, y );
1723 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1724 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1725 cluster2 = SUPER_CLUSTER( x, y );
1726 origin2 = SUPER_ORIGIN( x, y );
1727 //% normal2 = SUPER_NORMAL( x, y );
1731 Error( "Spurious lightmap S vector\n" );
1734 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1735 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1737 /* calulate y vector */
1738 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1740 cluster = SUPER_CLUSTER( x, y );
1741 origin = SUPER_ORIGIN( x, y );
1742 //% normal = SUPER_NORMAL( x, y );
1743 cluster2 = SUPER_CLUSTER( x, y + 1 );
1744 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1745 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1747 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1749 cluster = SUPER_CLUSTER( x, y - 1 );
1750 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1751 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1752 cluster2 = SUPER_CLUSTER( x, y );
1753 origin2 = SUPER_ORIGIN( x, y );
1754 //% normal2 = SUPER_NORMAL( x, y );
1757 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1759 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1760 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1762 /* calculate new origin */
1763 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1764 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1765 for( i = 0; i < 3; i++ )
1766 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1769 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1770 if( *sampleCluster < 0 )
1773 /* calculate new normal */
1774 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1775 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1776 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1778 normal = SUPER_NORMAL( x, y );
1779 VectorCopy( normal, sampleNormal );
1787 SubsampleRawLuxel_r()
1788 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1791 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1793 int b, samples, mapped, lighted;
1796 vec3_t deluxel[ 3 ];
1797 vec3_t origin[ 4 ], normal[ 4 ];
1798 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1799 vec3_t color, direction = { 0, 0, 0 }, total;
1803 if( lightLuxel[ 3 ] >= lightSamples )
1807 VectorClear( total );
1811 /* make 2x2 subsample stamp */
1812 for( b = 0; b < 4; b++ )
1815 VectorCopy( sampleOrigin, origin[ b ] );
1817 /* calculate position */
1818 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1825 /* increment sample count */
1826 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1829 trace->cluster = *cluster;
1830 VectorCopy( origin[ b ], trace->origin );
1831 VectorCopy( normal[ b ], trace->normal );
1835 LightContributionToSample( trace );
1836 if(trace->forceSubsampling > 1.0f)
1838 /* alphashadow: we subsample as deep as we can */
1844 /* add to totals (fixme: make contrast function) */
1845 VectorCopy( trace->color, luxel[ b ] );
1848 VectorCopy( trace->directionContribution, deluxel[ b ] );
1850 VectorAdd( total, trace->color, total );
1851 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1855 /* subsample further? */
1856 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1857 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1858 lighted != 0 && lighted != mapped )
1860 for( b = 0; b < 4; b++ )
1862 if( cluster[ b ] < 0 )
1864 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1869 //% VectorClear( color );
1871 VectorCopy( lightLuxel, color );
1874 VectorCopy( lightDeluxel, direction );
1877 for( b = 0; b < 4; b++ )
1879 if( cluster[ b ] < 0 )
1881 VectorAdd( color, luxel[ b ], color );
1884 VectorAdd( direction, deluxel[ b ], direction );
1893 color[ 0 ] /= samples;
1894 color[ 1 ] /= samples;
1895 color[ 2 ] /= samples;
1898 VectorCopy( color, lightLuxel );
1899 lightLuxel[ 3 ] += 1.0f;
1903 direction[ 0 ] /= samples;
1904 direction[ 1 ] /= samples;
1905 direction[ 2 ] /= samples;
1906 VectorCopy( direction, lightDeluxel );
1911 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1912 static void GaussLikeRandom(float sigma, float *x, float *y)
1915 r = Random() * 2 * Q_PI;
1916 *x = sigma * 2.73861278752581783822 * cos(r);
1917 *y = sigma * 2.73861278752581783822 * sin(r);
1924 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1928 vec3_t origin, normal;
1929 vec3_t total, totaldirection;
1932 VectorClear( total );
1933 VectorClear( totaldirection );
1935 for(b = 0; b < lightSamples; ++b)
1938 VectorCopy( sampleOrigin, origin );
1939 GaussLikeRandom(bias, &dx, &dy);
1941 /* calculate position */
1942 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1949 trace->cluster = cluster;
1950 VectorCopy( origin, trace->origin );
1951 VectorCopy( normal, trace->normal );
1953 LightContributionToSample( trace );
1954 VectorAdd( total, trace->color, total );
1957 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1965 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1966 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1967 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1971 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1972 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1973 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1981 IlluminateRawLightmap()
1982 illuminates the luxels
1985 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1986 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1987 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1989 void IlluminateRawLightmap( int rawLightmapNum )
1991 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1992 int *cluster, *cluster2, mapped, lighted, totalLighted;
1993 size_t llSize, ldSize;
1995 surfaceInfo_t *info;
1996 qboolean filterColor, filterDir;
1998 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1999 unsigned char *flag;
2000 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2001 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2002 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2004 float stackLightLuxels[ STACK_LL_SIZE ];
2007 /* bail if this number exceeds the number of raw lightmaps */
2008 if( rawLightmapNum >= numRawLightmaps )
2012 lm = &rawLightmaps[ rawLightmapNum ];
2015 trace.testOcclusion = !noTrace;
2016 trace.forceSunlight = qfalse;
2017 trace.recvShadows = lm->recvShadows;
2018 trace.numSurfaces = lm->numLightSurfaces;
2019 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2020 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2022 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2023 trace.twoSided = qfalse;
2024 for( i = 0; i < trace.numSurfaces; i++ )
2027 info = &surfaceInfos[ trace.surfaces[ i ] ];
2029 /* check twosidedness */
2030 if( info->si->twoSided )
2032 trace.twoSided = qtrue;
2037 /* create a culled light list for this raw lightmap */
2038 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2040 /* -----------------------------------------------------------------
2042 ----------------------------------------------------------------- */
2045 numLuxelsIlluminated += (lm->sw * lm->sh);
2047 /* test debugging state */
2048 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2050 /* debug fill the luxels */
2051 for( y = 0; y < lm->sh; y++ )
2053 for( x = 0; x < lm->sw; x++ )
2056 cluster = SUPER_CLUSTER( x, y );
2058 /* only fill mapped luxels */
2062 /* get particulars */
2063 luxel = SUPER_LUXEL( 0, x, y );
2064 origin = SUPER_ORIGIN( x, y );
2065 normal = SUPER_NORMAL( x, y );
2067 /* color the luxel with raw lightmap num? */
2069 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2071 /* color the luxel with lightmap axis? */
2072 else if( debugAxis )
2074 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2075 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2076 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2079 /* color the luxel with luxel cluster? */
2080 else if( debugCluster )
2081 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2083 /* color the luxel with luxel origin? */
2084 else if( debugOrigin )
2086 VectorSubtract( lm->maxs, lm->mins, temp );
2087 VectorScale( temp, (1.0f / 255.0f), temp );
2088 VectorSubtract( origin, lm->mins, temp2 );
2089 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2090 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2091 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2094 /* color the luxel with the normal */
2095 else if( normalmap )
2097 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2098 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2099 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2102 /* otherwise clear it */
2104 VectorClear( luxel );
2113 /* allocate temporary per-light luxel storage */
2114 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2115 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2116 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2117 lightLuxels = stackLightLuxels;
2119 lightLuxels = safe_malloc( llSize );
2121 lightDeluxels = safe_malloc( ldSize );
2123 lightDeluxels = NULL;
2126 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2128 /* set ambient color */
2129 for( y = 0; y < lm->sh; y++ )
2131 for( x = 0; x < lm->sw; x++ )
2134 cluster = SUPER_CLUSTER( x, y );
2135 luxel = SUPER_LUXEL( 0, x, y );
2136 normal = SUPER_NORMAL( x, y );
2137 deluxel = SUPER_DELUXEL( x, y );
2139 /* blacken unmapped clusters */
2141 VectorClear( luxel );
2146 VectorCopy( ambientColor, luxel );
2149 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2151 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2152 if(brightness < 0.00390625f)
2153 brightness = 0.00390625f;
2155 VectorScale( normal, brightness, deluxel );
2162 /* clear styled lightmaps */
2163 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2164 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2166 if( lm->superLuxels[ lightmapNum ] != NULL )
2167 memset( lm->superLuxels[ lightmapNum ], 0, size );
2170 /* debugging code */
2171 //% if( trace.numLights <= 0 )
2172 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2174 /* walk light list */
2175 for( i = 0; i < trace.numLights; i++ )
2178 trace.light = trace.lights[ i ];
2181 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2183 if( lm->styles[ lightmapNum ] == trace.light->style ||
2184 lm->styles[ lightmapNum ] == LS_NONE )
2188 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2189 if( lightmapNum >= MAX_LIGHTMAPS )
2191 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2196 memset( lightLuxels, 0, llSize );
2198 memset( lightDeluxels, 0, ldSize );
2201 /* determine filter radius */
2202 filterRadius = lm->filterRadius > trace.light->filterRadius
2204 : trace.light->filterRadius;
2205 if( filterRadius < 0.0f )
2206 filterRadius = 0.0f;
2208 /* set luxel filter radius */
2209 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2210 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2211 luxelFilterRadius = 1;
2213 /* allocate sampling flags storage */
2214 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2216 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2217 if(lm->superFlags == NULL)
2218 lm->superFlags = safe_malloc( size );
2219 memset( (void *) lm->superFlags, 0, size );
2222 /* initial pass, one sample per luxel */
2223 for( y = 0; y < lm->sh; y++ )
2225 for( x = 0; x < lm->sw; x++ )
2228 cluster = SUPER_CLUSTER( x, y );
2232 /* get particulars */
2233 lightLuxel = LIGHT_LUXEL( x, y );
2234 lightDeluxel = LIGHT_DELUXEL( x, y );
2235 origin = SUPER_ORIGIN( x, y );
2236 normal = SUPER_NORMAL( x, y );
2237 flag = SUPER_FLAG( x, y );
2240 ////////// 27's temp hack for testing edge clipping ////
2241 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2243 lightLuxel[ 1 ] = 255;
2244 lightLuxel[ 3 ] = 1.0f;
2250 /* set contribution count */
2251 lightLuxel[ 3 ] = 1.0f;
2254 trace.cluster = *cluster;
2255 VectorCopy( origin, trace.origin );
2256 VectorCopy( normal, trace.normal );
2258 /* get light for this sample */
2259 LightContributionToSample( &trace );
2260 VectorCopy( trace.color, lightLuxel );
2262 /* add the contribution to the deluxemap */
2265 VectorCopy( trace.directionContribution, lightDeluxel );
2268 /* check for evilness */
2269 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2272 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2275 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2281 /* don't even bother with everything else if nothing was lit */
2282 if( totalLighted == 0 )
2285 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2286 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2287 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2290 for( y = 0; y < (lm->sh - 1); y++ )
2292 for( x = 0; x < (lm->sw - 1); x++ )
2297 VectorClear( total );
2299 /* test 2x2 stamp */
2300 for( t = 0; t < 4; t++ )
2302 /* set sample coords */
2303 sx = x + tests[ t ][ 0 ];
2304 sy = y + tests[ t ][ 1 ];
2307 cluster = SUPER_CLUSTER( sx, sy );
2313 flag = SUPER_FLAG( sx, sy );
2314 if(*flag & FLAG_FORCE_SUBSAMPLING)
2316 /* force a lighted/mapped discrepancy so we subsample */
2321 lightLuxel = LIGHT_LUXEL( sx, sy );
2322 VectorAdd( total, lightLuxel, total );
2323 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2327 /* if total color is under a certain amount, then don't bother subsampling */
2328 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2331 /* if all 4 pixels are either in shadow or light, then don't subsample */
2332 if( lighted != 0 && lighted != mapped )
2334 for( t = 0; t < 4; t++ )
2336 /* set sample coords */
2337 sx = x + tests[ t ][ 0 ];
2338 sy = y + tests[ t ][ 1 ];
2341 cluster = SUPER_CLUSTER( sx, sy );
2344 flag = SUPER_FLAG( sx, sy );
2345 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2347 lightLuxel = LIGHT_LUXEL( sx, sy );
2348 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2349 origin = SUPER_ORIGIN( sx, sy );
2351 /* only subsample shadowed luxels */
2352 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2356 if(lightRandomSamples)
2357 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2359 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2361 *flag |= FLAG_ALREADY_SUBSAMPLED;
2363 /* debug code to colorize subsampled areas to yellow */
2364 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2365 //% VectorSet( luxel, 255, 204, 0 );
2372 /* tertiary pass, apply dirt map (ambient occlusion) */
2376 for( y = 0; y < lm->sh; y++ )
2378 for( x = 0; x < lm->sw; x++ )
2381 cluster = SUPER_CLUSTER( x, y );
2385 /* get particulars */
2386 lightLuxel = LIGHT_LUXEL( x, y );
2387 dirt = SUPER_DIRT( x, y );
2389 /* scale light value */
2390 VectorScale( lightLuxel, *dirt, lightLuxel );
2395 /* allocate sampling lightmap storage */
2396 if( lm->superLuxels[ lightmapNum ] == NULL )
2398 /* allocate sampling lightmap storage */
2399 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2400 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2401 memset( lm->superLuxels[ lightmapNum ], 0, size );
2405 if( lightmapNum > 0 )
2407 lm->styles[ lightmapNum ] = trace.light->style;
2408 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2411 /* copy to permanent luxels */
2412 for( y = 0; y < lm->sh; y++ )
2414 for( x = 0; x < lm->sw; x++ )
2416 /* get cluster and origin */
2417 cluster = SUPER_CLUSTER( x, y );
2420 origin = SUPER_ORIGIN( x, y );
2423 if( luxelFilterRadius )
2426 VectorClear( averageColor );
2427 VectorClear( averageDir );
2430 /* cheaper distance-based filtering */
2431 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2433 if( sy < 0 || sy >= lm->sh )
2436 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2438 if( sx < 0 || sx >= lm->sw )
2441 /* get particulars */
2442 cluster = SUPER_CLUSTER( sx, sy );
2445 lightLuxel = LIGHT_LUXEL( sx, sy );
2446 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2449 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2450 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2452 /* scale luxel by filter weight */
2453 VectorScale( lightLuxel, weight, color );
2454 VectorAdd( averageColor, color, averageColor );
2457 VectorScale( lightDeluxel, weight, direction );
2458 VectorAdd( averageDir, direction, averageDir );
2465 if( samples <= 0.0f )
2468 /* scale into luxel */
2469 luxel = SUPER_LUXEL( lightmapNum, x, y );
2472 /* handle negative light */
2473 if( trace.light->flags & LIGHT_NEGATIVE )
2475 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2476 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2477 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2480 /* handle normal light */
2483 luxel[ 0 ] += averageColor[ 0 ] / samples;
2484 luxel[ 1 ] += averageColor[ 1 ] / samples;
2485 luxel[ 2 ] += averageColor[ 2 ] / samples;
2490 /* scale into luxel */
2491 deluxel = SUPER_DELUXEL( x, y );
2492 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2493 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2494 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2501 /* get particulars */
2502 lightLuxel = LIGHT_LUXEL( x, y );
2503 lightDeluxel = LIGHT_DELUXEL( x, y );
2504 luxel = SUPER_LUXEL( lightmapNum, x, y );
2505 deluxel = SUPER_DELUXEL( x, y );
2507 /* handle negative light */
2508 if( trace.light->flags & LIGHT_NEGATIVE )
2509 VectorScale( averageColor, -1.0f, averageColor );
2514 /* handle negative light */
2515 if( trace.light->flags & LIGHT_NEGATIVE )
2516 VectorSubtract( luxel, lightLuxel, luxel );
2518 /* handle normal light */
2520 VectorAdd( luxel, lightLuxel, luxel );
2524 VectorAdd( deluxel, lightDeluxel, deluxel );
2531 /* free temporary luxels */
2532 if( lightLuxels != stackLightLuxels )
2533 free( lightLuxels );
2536 free( lightDeluxels );
2539 /* free light list */
2540 FreeTraceLights( &trace );
2542 /* floodlight pass */
2544 FloodlightIlluminateLightmap(lm);
2548 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2551 if( lm->superLuxels[ lightmapNum ] == NULL )
2554 for( y = 0; y < lm->sh; y++ )
2556 for( x = 0; x < lm->sw; x++ )
2559 cluster = SUPER_CLUSTER( x, y );
2560 //% if( *cluster < 0 )
2563 /* get particulars */
2564 luxel = SUPER_LUXEL( lightmapNum, x, y );
2565 normal = SUPER_NORMAL ( x, y );
2567 luxel[0]=(normal[0]*127)+127;
2568 luxel[1]=(normal[1]*127)+127;
2569 luxel[2]=(normal[2]*127)+127;
2575 /* -----------------------------------------------------------------
2577 ----------------------------------------------------------------- */
2581 /* walk lightmaps */
2582 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2585 if( lm->superLuxels[ lightmapNum ] == NULL )
2588 /* apply dirt to each luxel */
2589 for( y = 0; y < lm->sh; y++ )
2591 for( x = 0; x < lm->sw; x++ )
2594 cluster = SUPER_CLUSTER( x, y );
2595 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2598 /* get particulars */
2599 luxel = SUPER_LUXEL( lightmapNum, x, y );
2600 dirt = SUPER_DIRT( x, y );
2603 VectorScale( luxel, *dirt, luxel );
2607 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2613 /* -----------------------------------------------------------------
2615 ----------------------------------------------------------------- */
2617 /* walk lightmaps */
2618 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2621 if( lm->superLuxels[ lightmapNum ] == NULL )
2624 /* average occluded luxels from neighbors */
2625 for( y = 0; y < lm->sh; y++ )
2627 for( x = 0; x < lm->sw; x++ )
2629 /* get particulars */
2630 cluster = SUPER_CLUSTER( x, y );
2631 luxel = SUPER_LUXEL( lightmapNum, x, y );
2632 deluxel = SUPER_DELUXEL( x, y );
2633 normal = SUPER_NORMAL( x, y );
2635 /* determine if filtering is necessary */
2636 filterColor = qfalse;
2639 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2640 filterColor = qtrue;
2642 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2645 if( !filterColor && !filterDir )
2648 /* choose seed amount */
2649 VectorClear( averageColor );
2650 VectorClear( averageDir );
2653 /* walk 3x3 matrix */
2654 for( sy = (y - 1); sy <= (y + 1); sy++ )
2656 if( sy < 0 || sy >= lm->sh )
2659 for( sx = (x - 1); sx <= (x + 1); sx++ )
2661 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2664 /* get neighbor's particulars */
2665 cluster2 = SUPER_CLUSTER( sx, sy );
2666 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2667 deluxel2 = SUPER_DELUXEL( sx, sy );
2669 /* ignore unmapped/unlit luxels */
2670 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2671 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2674 /* add its distinctiveness to our own */
2675 VectorAdd( averageColor, luxel2, averageColor );
2676 samples += luxel2[ 3 ];
2678 VectorAdd( averageDir, deluxel2, averageDir );
2683 if( samples <= 0.0f )
2686 /* dark lightmap seams */
2689 if( lightmapNum == 0 )
2690 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2697 VectorDivide( averageColor, samples, luxel );
2701 VectorDivide( averageDir, samples, deluxel );
2703 /* set cluster to -3 */
2705 *cluster = CLUSTER_FLOODED;
2713 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2716 if( lm->superLuxels[ lightmapNum ] == NULL )
2718 for( y = 0; y < lm->sh; y++ )
2719 for( x = 0; x < lm->sw; x++ )
2722 cluster = SUPER_CLUSTER( x, y );
2723 luxel = SUPER_LUXEL( lightmapNum, x, y );
2724 deluxel = SUPER_DELUXEL( x, y );
2725 if(!luxel || !deluxel || !cluster)
2727 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2730 else if(*cluster < 0)
2733 // should have neither deluxemap nor lightmap
2735 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2740 // should have both deluxemap and lightmap
2742 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2752 IlluminateVertexes()
2753 light the surface vertexes
2756 #define VERTEX_NUDGE 4.0f
2758 void IlluminateVertexes( int num )
2760 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2761 int lightmapNum, numAvg;
2762 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2763 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2764 bspDrawSurface_t *ds;
2765 surfaceInfo_t *info;
2767 bspDrawVert_t *verts;
2769 float floodLightAmount;
2773 /* get surface, info, and raw lightmap */
2774 ds = &bspDrawSurfaces[ num ];
2775 info = &surfaceInfos[ num ];
2778 /* -----------------------------------------------------------------
2779 illuminate the vertexes
2780 ----------------------------------------------------------------- */
2782 /* calculate vertex lighting for surfaces without lightmaps */
2783 if( lm == NULL || cpmaHack )
2786 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2787 trace.forceSunlight = info->si->forceSunlight;
2788 trace.recvShadows = info->recvShadows;
2789 trace.numSurfaces = 1;
2790 trace.surfaces = #
2791 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2793 /* twosided lighting */
2794 trace.twoSided = info->si->twoSided;
2796 /* make light list for this surface */
2797 CreateTraceLightsForSurface( num, &trace );
2800 verts = yDrawVerts + ds->firstVert;
2802 memset( avgColors, 0, sizeof( avgColors ) );
2804 /* walk the surface verts */
2805 for( i = 0; i < ds->numVerts; i++ )
2807 /* get vertex luxel */
2808 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2810 /* color the luxel with raw lightmap num? */
2812 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2814 /* color the luxel with luxel origin? */
2815 else if( debugOrigin )
2817 VectorSubtract( info->maxs, info->mins, temp );
2818 VectorScale( temp, (1.0f / 255.0f), temp );
2819 VectorSubtract( origin, lm->mins, temp2 );
2820 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2821 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2822 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2825 /* color the luxel with the normal */
2826 else if( normalmap )
2828 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2829 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2830 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2833 /* illuminate the vertex */
2836 /* clear vertex luxel */
2837 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2839 /* try at initial origin */
2840 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2841 if( trace.cluster >= 0 )
2844 VectorCopy( verts[ i ].xyz, trace.origin );
2845 VectorCopy( verts[ i ].normal, trace.normal );
2848 if( dirty && !bouncing )
2849 dirt = DirtForSample( &trace );
2853 /* jal: floodlight */
2854 floodLightAmount = 0.0f;
2855 VectorClear( floodColor );
2856 if( floodlighty && !bouncing )
2858 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2859 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2863 LightingAtSample( &trace, ds->vertexStyles, colors );
2866 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2869 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2871 /* jal: floodlight */
2872 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2875 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2876 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2877 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2881 /* is this sample bright enough? */
2882 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2883 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2884 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2885 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2887 /* nudge the sample point around a bit */
2888 for( x = 0; x < 5; x++ )
2890 /* two's complement 0, 1, -1, 2, -2, etc */
2891 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2893 for( y = 0; y < 5; y++ )
2895 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2897 for( z = 0; z < 5; z++ )
2899 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2902 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2903 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2904 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2906 /* try at nudged origin */
2907 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2908 if( trace.cluster < 0 )
2912 if( dirty && !bouncing )
2913 dirt = DirtForSample( &trace );
2917 /* jal: floodlight */
2918 floodLightAmount = 0.0f;
2919 VectorClear( floodColor );
2920 if( floodlighty && !bouncing )
2922 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2923 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2927 LightingAtSample( &trace, ds->vertexStyles, colors );
2930 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2933 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2935 /* jal: floodlight */
2936 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2939 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2940 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2943 /* bright enough? */
2944 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2945 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2946 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2947 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2954 /* add to average? */
2955 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2956 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2957 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2958 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2961 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2963 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2964 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2969 /* another happy customer */
2970 numVertsIlluminated++;
2973 /* set average color */
2976 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2977 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2981 VectorCopy( ambientColor, avgColors[ 0 ] );
2984 /* clean up and store vertex color */
2985 for( i = 0; i < ds->numVerts; i++ )
2987 /* get vertex luxel */
2988 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2990 /* store average in occluded vertexes */
2991 if( radVertLuxel[ 0 ] < 0.0f )
2993 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2995 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2996 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2999 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3004 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3007 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3008 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3011 if( bouncing || bounce == 0 || !bounceOnly )
3012 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3013 if( !info->si->noVertexLight )
3014 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3018 /* free light list */
3019 FreeTraceLights( &trace );
3021 /* return to sender */
3025 /* -----------------------------------------------------------------
3026 reconstitute vertex lighting from the luxels
3027 ----------------------------------------------------------------- */
3029 /* set styles from lightmap */
3030 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3031 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3033 /* get max search radius */
3035 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3037 /* walk the surface verts */
3038 verts = yDrawVerts + ds->firstVert;
3039 for( i = 0; i < ds->numVerts; i++ )
3041 /* do each lightmap */
3042 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3045 if( lm->superLuxels[ lightmapNum ] == NULL )
3048 /* get luxel coords */
3049 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3050 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3053 else if( x >= lm->sw )
3057 else if( y >= lm->sh )
3060 /* get vertex luxels */
3061 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3062 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3064 /* color the luxel with the normal? */
3067 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3068 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3069 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3072 /* color the luxel with surface num? */
3073 else if( debugSurfaces )
3074 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3076 /* divine color from the superluxels */
3079 /* increasing radius */
3080 VectorClear( radVertLuxel );
3082 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3084 /* sample within radius */
3085 for( sy = (y - radius); sy <= (y + radius); sy++ )
3087 if( sy < 0 || sy >= lm->sh )
3090 for( sx = (x - radius); sx <= (x + radius); sx++ )
3092 if( sx < 0 || sx >= lm->sw )
3095 /* get luxel particulars */
3096 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3097 cluster = SUPER_CLUSTER( sx, sy );
3101 /* testing: must be brigher than ambient color */
3102 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3105 /* add its distinctiveness to our own */
3106 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3107 samples += luxel[ 3 ];
3113 if( samples > 0.0f )
3114 VectorDivide( radVertLuxel, samples, radVertLuxel );
3116 VectorCopy( ambientColor, radVertLuxel );
3119 /* store into floating point storage */
3120 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3121 numVertsIlluminated++;
3123 /* store into bytes (for vertex approximation) */
3124 if( !info->si->noVertexLight )
3125 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3132 /* -------------------------------------------------------------------------------
3134 light optimization (-fast)
3136 creates a list of lights that will affect a surface and stores it in tw
3137 this is to optimize surface lighting by culling out as many of the
3138 lights in the world as possible from further calculation
3140 ------------------------------------------------------------------------------- */
3144 determines opaque brushes in the world and find sky shaders for sunlight calculations
3147 void SetupBrushesFlags( int mask, int test )
3149 int i, j, b, compileFlags;
3152 bspBrushSide_t *side;
3153 bspShader_t *shader;
3158 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3161 if( opaqueBrushes == NULL )
3162 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3165 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3166 numOpaqueBrushes = 0;
3168 /* walk the list of worldspawn brushes */
3169 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3172 b = bspModels[ 0 ].firstBSPBrush + i;
3173 brush = &bspBrushes[ b ];
3175 /* check all sides */
3178 for( j = 0; j < brush->numSides && inside; j++ )
3180 /* do bsp shader calculations */
3181 side = &bspBrushSides[ brush->firstSide + j ];
3182 shader = &bspShaders[ side->shaderNum ];
3184 /* get shader info */
3185 si = ShaderInfoForShader( shader->shader );
3189 /* or together compile flags */
3190 compileFlags |= si->compileFlags;
3193 /* determine if this brush is opaque to light */
3194 if( (compileFlags & mask) == test )
3196 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3202 /* emit some statistics */
3203 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3205 void SetupBrushes( void )
3207 SetupBrushesFlags(C_TRANSLUCENT, 0);
3214 determines if two clusters are visible to each other using the PVS
3217 qboolean ClusterVisible( int a, int b )
3224 if( a < 0 || b < 0 )
3232 if( numBSPVisBytes <=8 )
3236 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3237 leafBytes = ((int*) bspVisBytes)[ 1 ];
3238 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3241 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3250 borrowed from vlight.c
3253 int PointInLeafNum_r( vec3_t point, int nodenum )
3261 while( nodenum >= 0 )
3263 node = &bspNodes[ nodenum ];
3264 plane = &bspPlanes[ node->planeNum ];
3265 dist = DotProduct( point, plane->normal ) - plane->dist;
3267 nodenum = node->children[ 0 ];
3268 else if( dist < -0.1 )
3269 nodenum = node->children[ 1 ];
3272 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3273 if( bspLeafs[ leafnum ].cluster != -1 )
3275 nodenum = node->children[ 1 ];
3279 leafnum = -nodenum - 1;
3287 borrowed from vlight.c
3290 int PointInLeafNum( vec3_t point )
3292 return PointInLeafNum_r( point, 0 );
3298 ClusterVisibleToPoint() - ydnar
3299 returns qtrue if point can "see" cluster
3302 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3307 /* get leafNum for point */
3308 pointCluster = ClusterForPoint( point );
3309 if( pointCluster < 0 )
3313 return ClusterVisible( pointCluster, cluster );
3319 ClusterForPoint() - ydnar
3320 returns the pvs cluster for point
3323 int ClusterForPoint( vec3_t point )
3328 /* get leafNum for point */
3329 leafNum = PointInLeafNum( point );
3333 /* return the cluster */
3334 return bspLeafs[ leafNum ].cluster;
3340 ClusterForPointExt() - ydnar
3341 also takes brushes into account for occlusion testing
3344 int ClusterForPointExt( vec3_t point, float epsilon )
3346 int i, j, b, leafNum, cluster;
3349 int *brushes, numBSPBrushes;
3355 /* get leaf for point */
3356 leafNum = PointInLeafNum( point );
3359 leaf = &bspLeafs[ leafNum ];
3361 /* get the cluster */
3362 cluster = leaf->cluster;
3366 /* transparent leaf, so check point against all brushes in the leaf */
3367 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3368 numBSPBrushes = leaf->numBSPLeafBrushes;
3369 for( i = 0; i < numBSPBrushes; i++ )
3373 if( b > maxOpaqueBrush )
3375 brush = &bspBrushes[ b ];
3376 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3379 /* check point against all planes */
3381 for( j = 0; j < brush->numSides && inside; j++ )
3383 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3384 dot = DotProduct( point, plane->normal );
3390 /* if inside, return bogus cluster */
3395 /* if the point made it this far, it's not inside any opaque brushes */
3402 ClusterForPointExtFilter() - ydnar
3403 adds cluster checking against a list of known valid clusters
3406 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3411 /* get cluster for point */
3412 cluster = ClusterForPointExt( point, epsilon );
3414 /* check if filtering is necessary */
3415 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3419 for( i = 0; i < numClusters; i++ )
3421 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3432 ShaderForPointInLeaf() - ydnar
3433 checks a point against all brushes in a leaf, returning the shader of the brush
3434 also sets the cumulative surface and content flags for the brush hit
3437 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3442 int *brushes, numBSPBrushes;
3445 bspBrushSide_t *side;
3447 bspShader_t *shader;
3448 int allSurfaceFlags, allContentFlags;
3451 /* clear things out first */
3458 leaf = &bspLeafs[ leafNum ];
3460 /* transparent leaf, so check point against all brushes in the leaf */
3461 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3462 numBSPBrushes = leaf->numBSPLeafBrushes;
3463 for( i = 0; i < numBSPBrushes; i++ )
3466 brush = &bspBrushes[ brushes[ i ] ];
3468 /* check point against all planes */
3470 allSurfaceFlags = 0;
3471 allContentFlags = 0;
3472 for( j = 0; j < brush->numSides && inside; j++ )
3474 side = &bspBrushSides[ brush->firstSide + j ];
3475 plane = &bspPlanes[ side->planeNum ];
3476 dot = DotProduct( point, plane->normal );
3482 shader = &bspShaders[ side->shaderNum ];
3483 allSurfaceFlags |= shader->surfaceFlags;
3484 allContentFlags |= shader->contentFlags;
3488 /* handle if inside */
3491 /* if there are desired flags, check for same and continue if they aren't matched */
3492 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3494 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3497 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3498 *surfaceFlags = allSurfaceFlags;
3499 *contentFlags = allContentFlags;
3500 return brush->shaderNum;
3504 /* if the point made it this far, it's not inside any brushes */
3512 chops a bounding box by the plane defined by origin and normal
3513 returns qfalse if the bounds is entirely clipped away
3515 this is not exactly the fastest way to do this...
3518 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3520 /* FIXME: rewrite this so it doesn't use bloody brushes */
3528 calculates each light's effective envelope,
3529 taking into account brightness, type, and pvs.
3532 #define LIGHT_EPSILON 0.125f
3533 #define LIGHT_NUDGE 2.0f
3535 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3537 int i, x, y, z, x1, y1, z1;
3538 light_t *light, *light2, **owner;
3540 vec3_t origin, dir, mins, maxs;
3541 float radius, intensity;
3542 light_t *buckets[ 256 ];
3545 /* early out for weird cases where there are no lights */
3546 if( lights == NULL )
3550 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3554 numCulledLights = 0;
3556 while( *owner != NULL )
3561 /* handle negative lights */
3562 if( light->photons < 0.0f || light->add < 0.0f )
3564 light->photons *= -1.0f;
3565 light->add *= -1.0f;
3566 light->flags |= LIGHT_NEGATIVE;
3570 if( light->type == EMIT_SUN )
3574 light->envelope = MAX_WORLD_COORD * 8.0f;
3575 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3576 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3579 /* everything else */
3582 /* get pvs cluster for light */
3583 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3585 /* invalid cluster? */
3586 if( light->cluster < 0 )
3588 /* nudge the sample point around a bit */
3589 for( x = 0; x < 4; x++ )
3591 /* two's complement 0, 1, -1, 2, -2, etc */
3592 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3594 for( y = 0; y < 4; y++ )
3596 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3598 for( z = 0; z < 4; z++ )
3600 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3603 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3604 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3605 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3607 /* try at nudged origin */
3608 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3609 if( light->cluster < 0 )
3613 VectorCopy( origin, light->origin );
3619 /* only calculate for lights in pvs and outside of opaque brushes */
3620 if( light->cluster >= 0 )
3622 /* set light fast flag */
3624 light->flags |= LIGHT_FAST_TEMP;
3626 light->flags &= ~LIGHT_FAST_TEMP;
3627 if( light->si && light->si->noFast )
3628 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3630 /* clear light envelope */
3631 light->envelope = 0;
3633 /* handle area lights */
3634 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3636 /* ugly hack to calculate extent for area lights, but only done once */
3637 VectorScale( light->normal, -1.0f, dir );
3638 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3642 VectorMA( light->origin, radius, light->normal, origin );
3643 factor = PointToPolygonFormFactor( origin, dir, light->w );
3646 if( (factor * light->add) <= light->falloffTolerance )
3647 light->envelope = radius;
3650 /* check for fast mode */
3651 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3652 light->envelope = MAX_WORLD_COORD * 8.0f;
3653 intensity = light->photons; /* hopefully not used */
3658 intensity = light->photons;
3662 if( light->envelope <= 0.0f )
3664 /* solve distance for non-distance lights */
3665 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3666 light->envelope = MAX_WORLD_COORD * 8.0f;
3668 /* solve distance for linear lights */
3669 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3670 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3671 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3674 add = angle * light->photons * linearScale - (dist * light->fade);
3675 T = (light->photons * linearScale) - (dist * light->fade);
3676 T + (dist * light->fade) = (light->photons * linearScale);
3677 dist * light->fade = (light->photons * linearScale) - T;
3678 dist = ((light->photons * linearScale) - T) / light->fade;
3681 /* solve for inverse square falloff */
3683 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3686 add = light->photons / (dist * dist);
3687 T = light->photons / (dist * dist);
3688 T * (dist * dist) = light->photons;
3689 dist = sqrt( light->photons / T );
3693 /* chop radius against pvs */
3696 ClearBounds( mins, maxs );
3698 /* check all leaves */
3699 for( i = 0; i < numBSPLeafs; i++ )
3702 leaf = &bspLeafs[ i ];
3705 if( leaf->cluster < 0 )
3707 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3710 /* add this leafs bbox to the bounds */
3711 VectorCopy( leaf->mins, origin );
3712 AddPointToBounds( origin, mins, maxs );
3713 VectorCopy( leaf->maxs, origin );
3714 AddPointToBounds( origin, mins, maxs );
3717 /* test to see if bounds encompass light */
3718 for( i = 0; i < 3; i++ )
3720 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3722 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3723 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3724 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3725 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3726 AddPointToBounds( light->origin, mins, maxs );
3730 /* chop the bounds by a plane for area lights and spotlights */
3731 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3732 ChopBounds( mins, maxs, light->origin, light->normal );
3735 VectorCopy( mins, light->mins );
3736 VectorCopy( maxs, light->maxs );
3738 /* reflect bounds around light origin */
3739 //% VectorMA( light->origin, -1.0f, origin, origin );
3740 VectorScale( light->origin, 2, origin );
3741 VectorSubtract( origin, maxs, origin );
3742 AddPointToBounds( origin, mins, maxs );
3743 //% VectorMA( light->origin, -1.0f, mins, origin );
3744 VectorScale( light->origin, 2, origin );
3745 VectorSubtract( origin, mins, origin );
3746 AddPointToBounds( origin, mins, maxs );
3748 /* calculate spherical bounds */
3749 VectorSubtract( maxs, light->origin, dir );
3750 radius = (float) VectorLength( dir );
3752 /* if this radius is smaller than the envelope, then set the envelope to it */
3753 if( radius < light->envelope )
3755 light->envelope = radius;
3756 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3759 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3762 /* add grid/surface only check */
3765 if( !(light->flags & LIGHT_GRID) )
3766 light->envelope = 0.0f;
3770 if( !(light->flags & LIGHT_SURFACES) )
3771 light->envelope = 0.0f;
3776 if( light->cluster < 0 || light->envelope <= 0.0f )
3779 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3781 /* delete the light */
3783 *owner = light->next;
3784 if( light->w != NULL )
3791 /* square envelope */
3792 light->envelope2 = (light->envelope * light->envelope);
3794 /* increment light count */
3797 /* set next light */
3798 owner = &((**owner).next);
3801 /* bucket sort lights by style */
3802 memset( buckets, 0, sizeof( buckets ) );
3804 for( light = lights; light != NULL; light = light2 )
3806 /* get next light */
3807 light2 = light->next;
3809 /* filter into correct bucket */
3810 light->next = buckets[ light->style ];
3811 buckets[ light->style ] = light;
3813 /* if any styled light is present, automatically set nocollapse */
3814 if( light->style != LS_NORMAL )
3818 /* filter back into light list */
3820 for( i = 255; i >= 0; i-- )
3823 for( light = buckets[ i ]; light != NULL; light = light2 )
3825 light2 = light->next;
3826 light->next = lights;
3831 /* emit some statistics */
3832 Sys_Printf( "%9d total lights\n", numLights );
3833 Sys_Printf( "%9d culled lights\n", numCulledLights );
3839 CreateTraceLightsForBounds()
3840 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3843 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3847 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3848 float radius, dist, length;
3851 /* potential pre-setup */
3852 if( numLights == 0 )
3853 SetupEnvelopes( qfalse, fast );
3856 //% 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 ] );
3858 /* allocate the light list */
3859 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3860 trace->numLights = 0;
3862 /* calculate spherical bounds */
3863 VectorAdd( mins, maxs, origin );
3864 VectorScale( origin, 0.5f, origin );
3865 VectorSubtract( maxs, origin, dir );
3866 radius = (float) VectorLength( dir );
3868 /* get length of normal vector */
3869 if( normal != NULL )
3870 length = VectorLength( normal );
3873 normal = nullVector;
3877 /* test each light and see if it reaches the sphere */
3878 /* note: the attenuation code MUST match LightingAtSample() */
3879 for( light = lights; light; light = light->next )
3881 /* check zero sized envelope */
3882 if( light->envelope <= 0 )
3884 lightsEnvelopeCulled++;
3889 if( !(light->flags & flags) )
3892 /* sunlight skips all this nonsense */
3893 if( light->type != EMIT_SUN )
3899 /* check against pvs cluster */
3900 if( numClusters > 0 && clusters != NULL )
3902 for( i = 0; i < numClusters; i++ )
3904 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3909 if( i == numClusters )
3911 lightsClusterCulled++;
3916 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3917 VectorSubtract( light->origin, origin, dir );
3918 dist = VectorLength( dir );
3919 dist -= light->envelope;
3923 lightsEnvelopeCulled++;
3927 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3930 for( i = 0; i < 3; i++ )
3932 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3937 lightsBoundsCulled++;
3943 /* planar surfaces (except twosided surfaces) have a couple more checks */
3944 if( length > 0.0f && trace->twoSided == qfalse )
3946 /* lights coplanar with a surface won't light it */
3947 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3949 lightsPlaneCulled++;
3953 /* check to see if light is behind the plane */
3954 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3956 lightsPlaneCulled++;
3961 /* add this light */
3962 trace->lights[ trace->numLights++ ] = light;
3965 /* make last night null */
3966 trace->lights[ trace->numLights ] = NULL;
3971 void FreeTraceLights( trace_t *trace )
3973 if( trace->lights != NULL )
3974 free( trace->lights );
3980 CreateTraceLightsForSurface()
3981 creates a list of lights that can potentially affect a drawsurface
3984 void CreateTraceLightsForSurface( int num, trace_t *trace )
3987 vec3_t mins, maxs, normal;
3989 bspDrawSurface_t *ds;
3990 surfaceInfo_t *info;
3997 /* get drawsurface and info */
3998 ds = &bspDrawSurfaces[ num ];
3999 info = &surfaceInfos[ num ];
4001 /* get the mins/maxs for the dsurf */
4002 ClearBounds( mins, maxs );
4003 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4004 for( i = 0; i < ds->numVerts; i++ )
4006 dv = &yDrawVerts[ ds->firstVert + i ];
4007 AddPointToBounds( dv->xyz, mins, maxs );
4008 if( !VectorCompare( dv->normal, normal ) )
4009 VectorClear( normal );
4012 /* create the lights for the bounding box */
4013 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4016 /////////////////////////////////////////////////////////////
4018 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4019 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4020 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4021 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4023 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4024 static int numFloodVectors = 0;
4026 void SetupFloodLight( void )
4029 float angle, elevation, angleStep, elevationStep;
4031 double v1,v2,v3,v4,v5,v6;
4034 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4036 /* calculate angular steps */
4037 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4038 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4042 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4044 /* iterate elevation */
4045 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4047 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4048 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4049 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4054 /* emit some statistics */
4055 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4058 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4060 if( value[ 0 ] != '\0' )
4063 v4=floodlightDistance;
4064 v5=floodlightIntensity;
4065 v6=floodlightDirectionScale;
4067 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6);
4069 floodlightRGB[0]=v1;
4070 floodlightRGB[1]=v2;
4071 floodlightRGB[2]=v3;
4073 if (VectorLength(floodlightRGB)==0)
4075 VectorSet(floodlightRGB,240,240,255);
4082 floodlightDistance=v4;
4083 floodlightIntensity=v5;
4084 floodlightDirectionScale=v6;
4086 floodlighty = qtrue;
4087 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4091 VectorSet(floodlightRGB,240,240,255);
4092 //floodlighty = qtrue;
4093 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4095 VectorNormalize(floodlightRGB,floodlightRGB);
4099 FloodLightForSample()
4100 calculates floodlight value for a given sample
4101 once again, kudos to the dirtmapping coder
4104 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4110 float gatherLight, outLight;
4111 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4119 if( trace == NULL || trace->cluster < 0 )
4124 dd = floodLightDistance;
4125 VectorCopy( trace->normal, normal );
4127 /* check if the normal is aligned to the world-up */
4128 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4130 if( normal[ 2 ] == 1.0f )
4132 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4133 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4135 else if( normal[ 2 ] == -1.0f )
4137 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4138 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4143 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4144 CrossProduct( normal, worldUp, myRt );
4145 VectorNormalize( myRt, myRt );
4146 CrossProduct( myRt, normal, myUp );
4147 VectorNormalize( myUp, myUp );
4150 /* vortex: optimise floodLightLowQuality a bit */
4151 if ( floodLightLowQuality == qtrue )
4153 /* iterate through ordered vectors */
4154 for( i = 0; i < numFloodVectors; i++ )
4155 if (rand()%10 != 0 ) continue;
4159 /* iterate through ordered vectors */
4160 for( i = 0; i < numFloodVectors; i++ )
4164 /* transform vector into tangent space */
4165 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4166 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4167 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4170 VectorMA( trace->origin, dd, direction, trace->end );
4172 //VectorMA( trace->origin, 1, direction, trace->origin );
4174 SetupTrace( trace );
4179 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4183 else if ( trace->opaque )
4185 VectorSubtract( trace->hit, trace->origin, displacement );
4186 d=VectorLength( displacement );
4188 // d=trace->distance;
4189 //if (d>256) gatherDirt+=1;
4191 if (contribution>1) contribution=1.0f;
4193 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4196 gatherLight+=contribution;
4201 if( gatherLight <= 0.0f )
4209 outLight=gatherLight;
4210 if( outLight > 1.0f )
4213 /* return to sender */
4218 FloodLightRawLightmap
4219 lighttracer style ambient occlusion light hack.
4220 Kudos to the dirtmapping author for most of this source.
4221 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4222 VorteX: fixed problems with deluxemapping
4225 // floodlight pass on a lightmap
4226 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4228 int i, x, y, *cluster;
4229 float *origin, *normal, *floodlight, floodLightAmount;
4230 surfaceInfo_t *info;
4233 // float samples, average, *floodlight2;
4235 memset(&trace,0,sizeof(trace_t));
4238 trace.testOcclusion = qtrue;
4239 trace.forceSunlight = qfalse;
4240 trace.twoSided = qtrue;
4241 trace.recvShadows = lm->recvShadows;
4242 trace.numSurfaces = lm->numLightSurfaces;
4243 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4244 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4245 trace.testAll = qfalse;
4246 trace.distance = 1024;
4248 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4249 //trace.twoSided = qfalse;
4250 for( i = 0; i < trace.numSurfaces; i++ )
4253 info = &surfaceInfos[ trace.surfaces[ i ] ];
4255 /* check twosidedness */
4256 if( info->si->twoSided )
4258 trace.twoSided = qtrue;
4263 /* gather floodlight */
4264 for( y = 0; y < lm->sh; y++ )
4266 for( x = 0; x < lm->sw; x++ )
4269 cluster = SUPER_CLUSTER( x, y );
4270 origin = SUPER_ORIGIN( x, y );
4271 normal = SUPER_NORMAL( x, y );
4272 floodlight = SUPER_FLOODLIGHT( x, y );
4274 /* set default dirt */
4277 /* only look at mapped luxels */
4282 trace.cluster = *cluster;
4283 VectorCopy( origin, trace.origin );
4284 VectorCopy( normal, trace.normal );
4286 /* get floodlight */
4287 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4289 /* add floodlight */
4290 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4291 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4292 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4293 floodlight[3] += floodlightDirectionScale;
4297 /* testing no filtering */
4303 for( y = 0; y < lm->sh; y++ )
4305 for( x = 0; x < lm->sw; x++ )
4308 cluster = SUPER_CLUSTER( x, y );
4309 floodlight = SUPER_FLOODLIGHT(x, y );
4311 /* filter dirt by adjacency to unmapped luxels */
4312 average = *floodlight;
4314 for( sy = (y - 1); sy <= (y + 1); sy++ )
4316 if( sy < 0 || sy >= lm->sh )
4319 for( sx = (x - 1); sx <= (x + 1); sx++ )
4321 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4324 /* get neighboring luxel */
4325 cluster = SUPER_CLUSTER( sx, sy );
4326 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4327 if( *cluster < 0 || *floodlight2 <= 0.0f )
4331 average += *floodlight2;
4336 if( samples <= 0.0f )
4341 if( samples <= 0.0f )
4345 *floodlight = average / samples;
4351 void FloodLightRawLightmap( int rawLightmapNum )
4355 /* bail if this number exceeds the number of raw lightmaps */
4356 if( rawLightmapNum >= numRawLightmaps )
4359 lm = &rawLightmaps[ rawLightmapNum ];
4362 if (floodlighty && floodlightIntensity)
4363 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
4366 if (lm->floodlightIntensity)
4368 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4369 numSurfacesFloodlighten += 1;
4373 void FloodlightRawLightmaps()
4375 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4376 numSurfacesFloodlighten = 0;
4377 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4378 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4382 FloodLightIlluminate()
4383 illuminate floodlight into lightmap luxels
4386 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4388 float *luxel, *floodlight, *deluxel, *normal;
4391 int x, y, lightmapNum;
4393 /* walk lightmaps */
4394 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4397 if( lm->superLuxels[ lightmapNum ] == NULL )
4400 /* apply floodlight to each luxel */
4401 for( y = 0; y < lm->sh; y++ )
4403 for( x = 0; x < lm->sw; x++ )
4405 /* get floodlight */
4406 floodlight = SUPER_FLOODLIGHT( x, y );
4407 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4411 cluster = SUPER_CLUSTER( x, y );
4413 /* only process mapped luxels */
4417 /* get particulars */
4418 luxel = SUPER_LUXEL( lightmapNum, x, y );
4419 deluxel = SUPER_DELUXEL( x, y );
4421 /* add to lightmap */
4422 luxel[0]+=floodlight[0];
4423 luxel[1]+=floodlight[1];
4424 luxel[2]+=floodlight[2];
4426 if (luxel[3]==0) luxel[3]=1;
4428 /* add to deluxemap */
4429 if (deluxemap && floodlight[3] > 0)
4433 normal = SUPER_NORMAL( x, y );
4434 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4436 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4437 if(brightness < 0.00390625f)
4438 brightness = 0.00390625f;
4440 VectorScale( normal, brightness, lightvector );
4441 VectorAdd( deluxel, lightvector, deluxel );