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_any, int test_any, int mask_all, int test_all )
3150 unsigned int compileFlags, allCompileFlags;
3153 bspBrushSide_t *side;
3154 bspShader_t *shader;
3159 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3162 if( opaqueBrushes == NULL )
3163 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3166 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3167 numOpaqueBrushes = 0;
3169 /* walk the list of worldspawn brushes */
3170 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3173 b = bspModels[ 0 ].firstBSPBrush + i;
3174 brush = &bspBrushes[ b ];
3176 /* check all sides */
3179 allCompileFlags = ~(0u);
3180 for( j = 0; j < brush->numSides && inside; j++ )
3182 /* do bsp shader calculations */
3183 side = &bspBrushSides[ brush->firstSide + j ];
3184 shader = &bspShaders[ side->shaderNum ];
3186 /* get shader info */
3187 si = ShaderInfoForShaderNull( shader->shader );
3191 /* or together compile flags */
3192 compileFlags |= si->compileFlags;
3193 allCompileFlags &= si->compileFlags;
3196 Sys_FPrintf( SYS_VRB, "flags: %d (all: %d)\n", compileFlags, allCompileFlags );
3198 /* determine if this brush is opaque to light */
3199 if( (compileFlags & mask_any) == test_any && (allCompileFlags & mask_all) == test_all )
3201 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3207 /* emit some statistics */
3208 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3210 void SetupBrushes( void )
3212 SetupBrushesFlags(C_TRANSLUCENT, 0, 0, 0);
3219 determines if two clusters are visible to each other using the PVS
3222 qboolean ClusterVisible( int a, int b )
3229 if( a < 0 || b < 0 )
3237 if( numBSPVisBytes <=8 )
3241 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3242 leafBytes = ((int*) bspVisBytes)[ 1 ];
3243 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3246 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3255 borrowed from vlight.c
3258 int PointInLeafNum_r( vec3_t point, int nodenum )
3266 while( nodenum >= 0 )
3268 node = &bspNodes[ nodenum ];
3269 plane = &bspPlanes[ node->planeNum ];
3270 dist = DotProduct( point, plane->normal ) - plane->dist;
3272 nodenum = node->children[ 0 ];
3273 else if( dist < -0.1 )
3274 nodenum = node->children[ 1 ];
3277 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3278 if( bspLeafs[ leafnum ].cluster != -1 )
3280 nodenum = node->children[ 1 ];
3284 leafnum = -nodenum - 1;
3292 borrowed from vlight.c
3295 int PointInLeafNum( vec3_t point )
3297 return PointInLeafNum_r( point, 0 );
3303 ClusterVisibleToPoint() - ydnar
3304 returns qtrue if point can "see" cluster
3307 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3312 /* get leafNum for point */
3313 pointCluster = ClusterForPoint( point );
3314 if( pointCluster < 0 )
3318 return ClusterVisible( pointCluster, cluster );
3324 ClusterForPoint() - ydnar
3325 returns the pvs cluster for point
3328 int ClusterForPoint( vec3_t point )
3333 /* get leafNum for point */
3334 leafNum = PointInLeafNum( point );
3338 /* return the cluster */
3339 return bspLeafs[ leafNum ].cluster;
3345 ClusterForPointExt() - ydnar
3346 also takes brushes into account for occlusion testing
3349 int ClusterForPointExt( vec3_t point, float epsilon )
3351 int i, j, b, leafNum, cluster;
3354 int *brushes, numBSPBrushes;
3360 /* get leaf for point */
3361 leafNum = PointInLeafNum( point );
3364 leaf = &bspLeafs[ leafNum ];
3366 /* get the cluster */
3367 cluster = leaf->cluster;
3371 /* transparent leaf, so check point against all brushes in the leaf */
3372 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3373 numBSPBrushes = leaf->numBSPLeafBrushes;
3374 for( i = 0; i < numBSPBrushes; i++ )
3378 if( b > maxOpaqueBrush )
3380 brush = &bspBrushes[ b ];
3381 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3384 /* check point against all planes */
3386 for( j = 0; j < brush->numSides && inside; j++ )
3388 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3389 dot = DotProduct( point, plane->normal );
3395 /* if inside, return bogus cluster */
3400 /* if the point made it this far, it's not inside any opaque brushes */
3407 ClusterForPointExtFilter() - ydnar
3408 adds cluster checking against a list of known valid clusters
3411 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3416 /* get cluster for point */
3417 cluster = ClusterForPointExt( point, epsilon );
3419 /* check if filtering is necessary */
3420 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3424 for( i = 0; i < numClusters; i++ )
3426 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3437 ShaderForPointInLeaf() - ydnar
3438 checks a point against all brushes in a leaf, returning the shader of the brush
3439 also sets the cumulative surface and content flags for the brush hit
3442 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3447 int *brushes, numBSPBrushes;
3450 bspBrushSide_t *side;
3452 bspShader_t *shader;
3453 int allSurfaceFlags, allContentFlags;
3456 /* clear things out first */
3463 leaf = &bspLeafs[ leafNum ];
3465 /* transparent leaf, so check point against all brushes in the leaf */
3466 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3467 numBSPBrushes = leaf->numBSPLeafBrushes;
3468 for( i = 0; i < numBSPBrushes; i++ )
3471 brush = &bspBrushes[ brushes[ i ] ];
3473 /* check point against all planes */
3475 allSurfaceFlags = 0;
3476 allContentFlags = 0;
3477 for( j = 0; j < brush->numSides && inside; j++ )
3479 side = &bspBrushSides[ brush->firstSide + j ];
3480 plane = &bspPlanes[ side->planeNum ];
3481 dot = DotProduct( point, plane->normal );
3487 shader = &bspShaders[ side->shaderNum ];
3488 allSurfaceFlags |= shader->surfaceFlags;
3489 allContentFlags |= shader->contentFlags;
3493 /* handle if inside */
3496 /* if there are desired flags, check for same and continue if they aren't matched */
3497 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3499 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3502 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3503 *surfaceFlags = allSurfaceFlags;
3504 *contentFlags = allContentFlags;
3505 return brush->shaderNum;
3509 /* if the point made it this far, it's not inside any brushes */
3517 chops a bounding box by the plane defined by origin and normal
3518 returns qfalse if the bounds is entirely clipped away
3520 this is not exactly the fastest way to do this...
3523 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3525 /* FIXME: rewrite this so it doesn't use bloody brushes */
3533 calculates each light's effective envelope,
3534 taking into account brightness, type, and pvs.
3537 #define LIGHT_EPSILON 0.125f
3538 #define LIGHT_NUDGE 2.0f
3540 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3542 int i, x, y, z, x1, y1, z1;
3543 light_t *light, *light2, **owner;
3545 vec3_t origin, dir, mins, maxs;
3546 float radius, intensity;
3547 light_t *buckets[ 256 ];
3550 /* early out for weird cases where there are no lights */
3551 if( lights == NULL )
3555 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3559 numCulledLights = 0;
3561 while( *owner != NULL )
3566 /* handle negative lights */
3567 if( light->photons < 0.0f || light->add < 0.0f )
3569 light->photons *= -1.0f;
3570 light->add *= -1.0f;
3571 light->flags |= LIGHT_NEGATIVE;
3575 if( light->type == EMIT_SUN )
3579 light->envelope = MAX_WORLD_COORD * 8.0f;
3580 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3581 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3584 /* everything else */
3587 /* get pvs cluster for light */
3588 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3590 /* invalid cluster? */
3591 if( light->cluster < 0 )
3593 /* nudge the sample point around a bit */
3594 for( x = 0; x < 4; x++ )
3596 /* two's complement 0, 1, -1, 2, -2, etc */
3597 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3599 for( y = 0; y < 4; y++ )
3601 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3603 for( z = 0; z < 4; z++ )
3605 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3608 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3609 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3610 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3612 /* try at nudged origin */
3613 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3614 if( light->cluster < 0 )
3618 VectorCopy( origin, light->origin );
3624 /* only calculate for lights in pvs and outside of opaque brushes */
3625 if( light->cluster >= 0 )
3627 /* set light fast flag */
3629 light->flags |= LIGHT_FAST_TEMP;
3631 light->flags &= ~LIGHT_FAST_TEMP;
3632 if( light->si && light->si->noFast )
3633 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3635 /* clear light envelope */
3636 light->envelope = 0;
3638 /* handle area lights */
3639 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3641 /* ugly hack to calculate extent for area lights, but only done once */
3642 VectorScale( light->normal, -1.0f, dir );
3643 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3647 VectorMA( light->origin, radius, light->normal, origin );
3648 factor = PointToPolygonFormFactor( origin, dir, light->w );
3651 if( (factor * light->add) <= light->falloffTolerance )
3652 light->envelope = radius;
3655 /* check for fast mode */
3656 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3657 light->envelope = MAX_WORLD_COORD * 8.0f;
3658 intensity = light->photons; /* hopefully not used */
3663 intensity = light->photons;
3667 if( light->envelope <= 0.0f )
3669 /* solve distance for non-distance lights */
3670 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3671 light->envelope = MAX_WORLD_COORD * 8.0f;
3673 /* solve distance for linear lights */
3674 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3675 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3676 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3679 add = angle * light->photons * linearScale - (dist * light->fade);
3680 T = (light->photons * linearScale) - (dist * light->fade);
3681 T + (dist * light->fade) = (light->photons * linearScale);
3682 dist * light->fade = (light->photons * linearScale) - T;
3683 dist = ((light->photons * linearScale) - T) / light->fade;
3686 /* solve for inverse square falloff */
3688 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3691 add = light->photons / (dist * dist);
3692 T = light->photons / (dist * dist);
3693 T * (dist * dist) = light->photons;
3694 dist = sqrt( light->photons / T );
3698 /* chop radius against pvs */
3701 ClearBounds( mins, maxs );
3703 /* check all leaves */
3704 for( i = 0; i < numBSPLeafs; i++ )
3707 leaf = &bspLeafs[ i ];
3710 if( leaf->cluster < 0 )
3712 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3715 /* add this leafs bbox to the bounds */
3716 VectorCopy( leaf->mins, origin );
3717 AddPointToBounds( origin, mins, maxs );
3718 VectorCopy( leaf->maxs, origin );
3719 AddPointToBounds( origin, mins, maxs );
3722 /* test to see if bounds encompass light */
3723 for( i = 0; i < 3; i++ )
3725 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3727 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3728 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3729 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3730 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3731 AddPointToBounds( light->origin, mins, maxs );
3735 /* chop the bounds by a plane for area lights and spotlights */
3736 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3737 ChopBounds( mins, maxs, light->origin, light->normal );
3740 VectorCopy( mins, light->mins );
3741 VectorCopy( maxs, light->maxs );
3743 /* reflect bounds around light origin */
3744 //% VectorMA( light->origin, -1.0f, origin, origin );
3745 VectorScale( light->origin, 2, origin );
3746 VectorSubtract( origin, maxs, origin );
3747 AddPointToBounds( origin, mins, maxs );
3748 //% VectorMA( light->origin, -1.0f, mins, origin );
3749 VectorScale( light->origin, 2, origin );
3750 VectorSubtract( origin, mins, origin );
3751 AddPointToBounds( origin, mins, maxs );
3753 /* calculate spherical bounds */
3754 VectorSubtract( maxs, light->origin, dir );
3755 radius = (float) VectorLength( dir );
3757 /* if this radius is smaller than the envelope, then set the envelope to it */
3758 if( radius < light->envelope )
3760 light->envelope = radius;
3761 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3764 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3767 /* add grid/surface only check */
3770 if( !(light->flags & LIGHT_GRID) )
3771 light->envelope = 0.0f;
3775 if( !(light->flags & LIGHT_SURFACES) )
3776 light->envelope = 0.0f;
3781 if( light->cluster < 0 || light->envelope <= 0.0f )
3784 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3786 /* delete the light */
3788 *owner = light->next;
3789 if( light->w != NULL )
3796 /* square envelope */
3797 light->envelope2 = (light->envelope * light->envelope);
3799 /* increment light count */
3802 /* set next light */
3803 owner = &((**owner).next);
3806 /* bucket sort lights by style */
3807 memset( buckets, 0, sizeof( buckets ) );
3809 for( light = lights; light != NULL; light = light2 )
3811 /* get next light */
3812 light2 = light->next;
3814 /* filter into correct bucket */
3815 light->next = buckets[ light->style ];
3816 buckets[ light->style ] = light;
3818 /* if any styled light is present, automatically set nocollapse */
3819 if( light->style != LS_NORMAL )
3823 /* filter back into light list */
3825 for( i = 255; i >= 0; i-- )
3828 for( light = buckets[ i ]; light != NULL; light = light2 )
3830 light2 = light->next;
3831 light->next = lights;
3836 /* emit some statistics */
3837 Sys_Printf( "%9d total lights\n", numLights );
3838 Sys_Printf( "%9d culled lights\n", numCulledLights );
3844 CreateTraceLightsForBounds()
3845 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3848 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3852 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3853 float radius, dist, length;
3856 /* potential pre-setup */
3857 if( numLights == 0 )
3858 SetupEnvelopes( qfalse, fast );
3861 //% 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 ] );
3863 /* allocate the light list */
3864 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3865 trace->numLights = 0;
3867 /* calculate spherical bounds */
3868 VectorAdd( mins, maxs, origin );
3869 VectorScale( origin, 0.5f, origin );
3870 VectorSubtract( maxs, origin, dir );
3871 radius = (float) VectorLength( dir );
3873 /* get length of normal vector */
3874 if( normal != NULL )
3875 length = VectorLength( normal );
3878 normal = nullVector;
3882 /* test each light and see if it reaches the sphere */
3883 /* note: the attenuation code MUST match LightingAtSample() */
3884 for( light = lights; light; light = light->next )
3886 /* check zero sized envelope */
3887 if( light->envelope <= 0 )
3889 lightsEnvelopeCulled++;
3894 if( !(light->flags & flags) )
3897 /* sunlight skips all this nonsense */
3898 if( light->type != EMIT_SUN )
3904 /* check against pvs cluster */
3905 if( numClusters > 0 && clusters != NULL )
3907 for( i = 0; i < numClusters; i++ )
3909 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3914 if( i == numClusters )
3916 lightsClusterCulled++;
3921 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3922 VectorSubtract( light->origin, origin, dir );
3923 dist = VectorLength( dir );
3924 dist -= light->envelope;
3928 lightsEnvelopeCulled++;
3932 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3935 for( i = 0; i < 3; i++ )
3937 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3942 lightsBoundsCulled++;
3948 /* planar surfaces (except twosided surfaces) have a couple more checks */
3949 if( length > 0.0f && trace->twoSided == qfalse )
3951 /* lights coplanar with a surface won't light it */
3952 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3954 lightsPlaneCulled++;
3958 /* check to see if light is behind the plane */
3959 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3961 lightsPlaneCulled++;
3966 /* add this light */
3967 trace->lights[ trace->numLights++ ] = light;
3970 /* make last night null */
3971 trace->lights[ trace->numLights ] = NULL;
3976 void FreeTraceLights( trace_t *trace )
3978 if( trace->lights != NULL )
3979 free( trace->lights );
3985 CreateTraceLightsForSurface()
3986 creates a list of lights that can potentially affect a drawsurface
3989 void CreateTraceLightsForSurface( int num, trace_t *trace )
3992 vec3_t mins, maxs, normal;
3994 bspDrawSurface_t *ds;
3995 surfaceInfo_t *info;
4002 /* get drawsurface and info */
4003 ds = &bspDrawSurfaces[ num ];
4004 info = &surfaceInfos[ num ];
4006 /* get the mins/maxs for the dsurf */
4007 ClearBounds( mins, maxs );
4008 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4009 for( i = 0; i < ds->numVerts; i++ )
4011 dv = &yDrawVerts[ ds->firstVert + i ];
4012 AddPointToBounds( dv->xyz, mins, maxs );
4013 if( !VectorCompare( dv->normal, normal ) )
4014 VectorClear( normal );
4017 /* create the lights for the bounding box */
4018 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4021 /////////////////////////////////////////////////////////////
4023 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4024 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4025 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4026 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4028 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4029 static int numFloodVectors = 0;
4031 void SetupFloodLight( void )
4034 float angle, elevation, angleStep, elevationStep;
4036 double v1,v2,v3,v4,v5,v6;
4039 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4041 /* calculate angular steps */
4042 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4043 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4047 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4049 /* iterate elevation */
4050 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4052 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4053 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4054 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4059 /* emit some statistics */
4060 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4063 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4065 if( value[ 0 ] != '\0' )
4068 v4=floodlightDistance;
4069 v5=floodlightIntensity;
4070 v6=floodlightDirectionScale;
4072 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6);
4074 floodlightRGB[0]=v1;
4075 floodlightRGB[1]=v2;
4076 floodlightRGB[2]=v3;
4078 if (VectorLength(floodlightRGB)==0)
4080 VectorSet(floodlightRGB,240,240,255);
4087 floodlightDistance=v4;
4088 floodlightIntensity=v5;
4089 floodlightDirectionScale=v6;
4091 floodlighty = qtrue;
4092 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4096 VectorSet(floodlightRGB,240,240,255);
4097 //floodlighty = qtrue;
4098 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4100 VectorNormalize(floodlightRGB,floodlightRGB);
4104 FloodLightForSample()
4105 calculates floodlight value for a given sample
4106 once again, kudos to the dirtmapping coder
4109 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4115 float gatherLight, outLight;
4116 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4124 if( trace == NULL || trace->cluster < 0 )
4129 dd = floodLightDistance;
4130 VectorCopy( trace->normal, normal );
4132 /* check if the normal is aligned to the world-up */
4133 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4135 if( normal[ 2 ] == 1.0f )
4137 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4138 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4140 else if( normal[ 2 ] == -1.0f )
4142 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4143 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4148 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4149 CrossProduct( normal, worldUp, myRt );
4150 VectorNormalize( myRt, myRt );
4151 CrossProduct( myRt, normal, myUp );
4152 VectorNormalize( myUp, myUp );
4155 /* vortex: optimise floodLightLowQuality a bit */
4156 if ( floodLightLowQuality == qtrue )
4158 /* iterate through ordered vectors */
4159 for( i = 0; i < numFloodVectors; i++ )
4160 if (rand()%10 != 0 ) continue;
4164 /* iterate through ordered vectors */
4165 for( i = 0; i < numFloodVectors; i++ )
4169 /* transform vector into tangent space */
4170 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4171 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4172 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4175 VectorMA( trace->origin, dd, direction, trace->end );
4177 //VectorMA( trace->origin, 1, direction, trace->origin );
4179 SetupTrace( trace );
4184 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4188 else if ( trace->opaque )
4190 VectorSubtract( trace->hit, trace->origin, displacement );
4191 d=VectorLength( displacement );
4193 // d=trace->distance;
4194 //if (d>256) gatherDirt+=1;
4196 if (contribution>1) contribution=1.0f;
4198 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4201 gatherLight+=contribution;
4206 if( gatherLight <= 0.0f )
4214 outLight=gatherLight;
4215 if( outLight > 1.0f )
4218 /* return to sender */
4223 FloodLightRawLightmap
4224 lighttracer style ambient occlusion light hack.
4225 Kudos to the dirtmapping author for most of this source.
4226 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4227 VorteX: fixed problems with deluxemapping
4230 // floodlight pass on a lightmap
4231 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4233 int i, x, y, *cluster;
4234 float *origin, *normal, *floodlight, floodLightAmount;
4235 surfaceInfo_t *info;
4238 // float samples, average, *floodlight2;
4240 memset(&trace,0,sizeof(trace_t));
4243 trace.testOcclusion = qtrue;
4244 trace.forceSunlight = qfalse;
4245 trace.twoSided = qtrue;
4246 trace.recvShadows = lm->recvShadows;
4247 trace.numSurfaces = lm->numLightSurfaces;
4248 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4249 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4250 trace.testAll = qfalse;
4251 trace.distance = 1024;
4253 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4254 //trace.twoSided = qfalse;
4255 for( i = 0; i < trace.numSurfaces; i++ )
4258 info = &surfaceInfos[ trace.surfaces[ i ] ];
4260 /* check twosidedness */
4261 if( info->si->twoSided )
4263 trace.twoSided = qtrue;
4268 /* gather floodlight */
4269 for( y = 0; y < lm->sh; y++ )
4271 for( x = 0; x < lm->sw; x++ )
4274 cluster = SUPER_CLUSTER( x, y );
4275 origin = SUPER_ORIGIN( x, y );
4276 normal = SUPER_NORMAL( x, y );
4277 floodlight = SUPER_FLOODLIGHT( x, y );
4279 /* set default dirt */
4282 /* only look at mapped luxels */
4287 trace.cluster = *cluster;
4288 VectorCopy( origin, trace.origin );
4289 VectorCopy( normal, trace.normal );
4291 /* get floodlight */
4292 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4294 /* add floodlight */
4295 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4296 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4297 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4298 floodlight[3] += floodlightDirectionScale;
4302 /* testing no filtering */
4308 for( y = 0; y < lm->sh; y++ )
4310 for( x = 0; x < lm->sw; x++ )
4313 cluster = SUPER_CLUSTER( x, y );
4314 floodlight = SUPER_FLOODLIGHT(x, y );
4316 /* filter dirt by adjacency to unmapped luxels */
4317 average = *floodlight;
4319 for( sy = (y - 1); sy <= (y + 1); sy++ )
4321 if( sy < 0 || sy >= lm->sh )
4324 for( sx = (x - 1); sx <= (x + 1); sx++ )
4326 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4329 /* get neighboring luxel */
4330 cluster = SUPER_CLUSTER( sx, sy );
4331 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4332 if( *cluster < 0 || *floodlight2 <= 0.0f )
4336 average += *floodlight2;
4341 if( samples <= 0.0f )
4346 if( samples <= 0.0f )
4350 *floodlight = average / samples;
4356 void FloodLightRawLightmap( int rawLightmapNum )
4360 /* bail if this number exceeds the number of raw lightmaps */
4361 if( rawLightmapNum >= numRawLightmaps )
4364 lm = &rawLightmaps[ rawLightmapNum ];
4367 if (floodlighty && floodlightIntensity)
4368 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
4371 if (lm->floodlightIntensity)
4373 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4374 numSurfacesFloodlighten += 1;
4378 void FloodlightRawLightmaps()
4380 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4381 numSurfacesFloodlighten = 0;
4382 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4383 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4387 FloodLightIlluminate()
4388 illuminate floodlight into lightmap luxels
4391 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4393 float *luxel, *floodlight, *deluxel, *normal;
4396 int x, y, lightmapNum;
4398 /* walk lightmaps */
4399 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4402 if( lm->superLuxels[ lightmapNum ] == NULL )
4405 /* apply floodlight to each luxel */
4406 for( y = 0; y < lm->sh; y++ )
4408 for( x = 0; x < lm->sw; x++ )
4410 /* get floodlight */
4411 floodlight = SUPER_FLOODLIGHT( x, y );
4412 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4416 cluster = SUPER_CLUSTER( x, y );
4418 /* only process mapped luxels */
4422 /* get particulars */
4423 luxel = SUPER_LUXEL( lightmapNum, x, y );
4424 deluxel = SUPER_DELUXEL( x, y );
4426 /* add to lightmap */
4427 luxel[0]+=floodlight[0];
4428 luxel[1]+=floodlight[1];
4429 luxel[2]+=floodlight[2];
4431 if (luxel[3]==0) luxel[3]=1;
4433 /* add to deluxemap */
4434 if (deluxemap && floodlight[3] > 0)
4438 normal = SUPER_NORMAL( x, y );
4439 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4441 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4442 if(brightness < 0.00390625f)
4443 brightness = 0.00390625f;
4445 VectorScale( normal, brightness, lightvector );
4446 VectorAdd( deluxel, lightvector, deluxel );