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 SetupBrushes( void )
3149 int i, j, b, compileFlags;
3152 bspBrushSide_t *side;
3153 bspShader_t *shader;
3158 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3161 if( opaqueBrushes == NULL )
3162 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3165 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3166 numOpaqueBrushes = 0;
3168 /* walk the list of worldspawn brushes */
3169 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3172 b = bspModels[ 0 ].firstBSPBrush + i;
3173 brush = &bspBrushes[ b ];
3175 /* check all sides */
3178 for( j = 0; j < brush->numSides && inside; j++ )
3180 /* do bsp shader calculations */
3181 side = &bspBrushSides[ brush->firstSide + j ];
3182 shader = &bspShaders[ side->shaderNum ];
3184 /* get shader info */
3185 si = ShaderInfoForShader( shader->shader );
3189 /* or together compile flags */
3190 compileFlags |= si->compileFlags;
3193 /* determine if this brush is opaque to light */
3194 if( !(compileFlags & C_TRANSLUCENT) )
3196 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3202 /* emit some statistics */
3203 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3210 determines if two clusters are visible to each other using the PVS
3213 qboolean ClusterVisible( int a, int b )
3220 if( a < 0 || b < 0 )
3228 if( numBSPVisBytes <=8 )
3232 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3233 leafBytes = ((int*) bspVisBytes)[ 1 ];
3234 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3237 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3246 borrowed from vlight.c
3249 int PointInLeafNum_r( vec3_t point, int nodenum )
3257 while( nodenum >= 0 )
3259 node = &bspNodes[ nodenum ];
3260 plane = &bspPlanes[ node->planeNum ];
3261 dist = DotProduct( point, plane->normal ) - plane->dist;
3263 nodenum = node->children[ 0 ];
3264 else if( dist < -0.1 )
3265 nodenum = node->children[ 1 ];
3268 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3269 if( bspLeafs[ leafnum ].cluster != -1 )
3271 nodenum = node->children[ 1 ];
3275 leafnum = -nodenum - 1;
3283 borrowed from vlight.c
3286 int PointInLeafNum( vec3_t point )
3288 return PointInLeafNum_r( point, 0 );
3294 ClusterVisibleToPoint() - ydnar
3295 returns qtrue if point can "see" cluster
3298 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3303 /* get leafNum for point */
3304 pointCluster = ClusterForPoint( point );
3305 if( pointCluster < 0 )
3309 return ClusterVisible( pointCluster, cluster );
3315 ClusterForPoint() - ydnar
3316 returns the pvs cluster for point
3319 int ClusterForPoint( vec3_t point )
3324 /* get leafNum for point */
3325 leafNum = PointInLeafNum( point );
3329 /* return the cluster */
3330 return bspLeafs[ leafNum ].cluster;
3336 ClusterForPointExt() - ydnar
3337 also takes brushes into account for occlusion testing
3340 int ClusterForPointExt( vec3_t point, float epsilon )
3342 int i, j, b, leafNum, cluster;
3345 int *brushes, numBSPBrushes;
3351 /* get leaf for point */
3352 leafNum = PointInLeafNum( point );
3355 leaf = &bspLeafs[ leafNum ];
3357 /* get the cluster */
3358 cluster = leaf->cluster;
3362 /* transparent leaf, so check point against all brushes in the leaf */
3363 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3364 numBSPBrushes = leaf->numBSPLeafBrushes;
3365 for( i = 0; i < numBSPBrushes; i++ )
3369 if( b > maxOpaqueBrush )
3371 brush = &bspBrushes[ b ];
3372 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3375 /* check point against all planes */
3377 for( j = 0; j < brush->numSides && inside; j++ )
3379 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3380 dot = DotProduct( point, plane->normal );
3386 /* if inside, return bogus cluster */
3391 /* if the point made it this far, it's not inside any opaque brushes */
3398 ClusterForPointExtFilter() - ydnar
3399 adds cluster checking against a list of known valid clusters
3402 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3407 /* get cluster for point */
3408 cluster = ClusterForPointExt( point, epsilon );
3410 /* check if filtering is necessary */
3411 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3415 for( i = 0; i < numClusters; i++ )
3417 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3428 ShaderForPointInLeaf() - ydnar
3429 checks a point against all brushes in a leaf, returning the shader of the brush
3430 also sets the cumulative surface and content flags for the brush hit
3433 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3438 int *brushes, numBSPBrushes;
3441 bspBrushSide_t *side;
3443 bspShader_t *shader;
3444 int allSurfaceFlags, allContentFlags;
3447 /* clear things out first */
3454 leaf = &bspLeafs[ leafNum ];
3456 /* transparent leaf, so check point against all brushes in the leaf */
3457 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3458 numBSPBrushes = leaf->numBSPLeafBrushes;
3459 for( i = 0; i < numBSPBrushes; i++ )
3462 brush = &bspBrushes[ brushes[ i ] ];
3464 /* check point against all planes */
3466 allSurfaceFlags = 0;
3467 allContentFlags = 0;
3468 for( j = 0; j < brush->numSides && inside; j++ )
3470 side = &bspBrushSides[ brush->firstSide + j ];
3471 plane = &bspPlanes[ side->planeNum ];
3472 dot = DotProduct( point, plane->normal );
3478 shader = &bspShaders[ side->shaderNum ];
3479 allSurfaceFlags |= shader->surfaceFlags;
3480 allContentFlags |= shader->contentFlags;
3484 /* handle if inside */
3487 /* if there are desired flags, check for same and continue if they aren't matched */
3488 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3490 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3493 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3494 *surfaceFlags = allSurfaceFlags;
3495 *contentFlags = allContentFlags;
3496 return brush->shaderNum;
3500 /* if the point made it this far, it's not inside any brushes */
3508 chops a bounding box by the plane defined by origin and normal
3509 returns qfalse if the bounds is entirely clipped away
3511 this is not exactly the fastest way to do this...
3514 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3516 /* FIXME: rewrite this so it doesn't use bloody brushes */
3524 calculates each light's effective envelope,
3525 taking into account brightness, type, and pvs.
3528 #define LIGHT_EPSILON 0.125f
3529 #define LIGHT_NUDGE 2.0f
3531 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3533 int i, x, y, z, x1, y1, z1;
3534 light_t *light, *light2, **owner;
3536 vec3_t origin, dir, mins, maxs;
3537 float radius, intensity;
3538 light_t *buckets[ 256 ];
3541 /* early out for weird cases where there are no lights */
3542 if( lights == NULL )
3546 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3550 numCulledLights = 0;
3552 while( *owner != NULL )
3557 /* handle negative lights */
3558 if( light->photons < 0.0f || light->add < 0.0f )
3560 light->photons *= -1.0f;
3561 light->add *= -1.0f;
3562 light->flags |= LIGHT_NEGATIVE;
3566 if( light->type == EMIT_SUN )
3570 light->envelope = MAX_WORLD_COORD * 8.0f;
3571 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3572 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3575 /* everything else */
3578 /* get pvs cluster for light */
3579 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3581 /* invalid cluster? */
3582 if( light->cluster < 0 )
3584 /* nudge the sample point around a bit */
3585 for( x = 0; x < 4; x++ )
3587 /* two's complement 0, 1, -1, 2, -2, etc */
3588 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3590 for( y = 0; y < 4; y++ )
3592 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3594 for( z = 0; z < 4; z++ )
3596 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3599 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3600 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3601 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3603 /* try at nudged origin */
3604 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3605 if( light->cluster < 0 )
3609 VectorCopy( origin, light->origin );
3615 /* only calculate for lights in pvs and outside of opaque brushes */
3616 if( light->cluster >= 0 )
3618 /* set light fast flag */
3620 light->flags |= LIGHT_FAST_TEMP;
3622 light->flags &= ~LIGHT_FAST_TEMP;
3623 if( light->si && light->si->noFast )
3624 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3626 /* clear light envelope */
3627 light->envelope = 0;
3629 /* handle area lights */
3630 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3632 /* ugly hack to calculate extent for area lights, but only done once */
3633 VectorScale( light->normal, -1.0f, dir );
3634 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3638 VectorMA( light->origin, radius, light->normal, origin );
3639 factor = PointToPolygonFormFactor( origin, dir, light->w );
3642 if( (factor * light->add) <= light->falloffTolerance )
3643 light->envelope = radius;
3646 /* check for fast mode */
3647 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3648 light->envelope = MAX_WORLD_COORD * 8.0f;
3649 intensity = light->photons; /* hopefully not used */
3654 intensity = light->photons;
3658 if( light->envelope <= 0.0f )
3660 /* solve distance for non-distance lights */
3661 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3662 light->envelope = MAX_WORLD_COORD * 8.0f;
3664 /* solve distance for linear lights */
3665 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3666 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3667 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3670 add = angle * light->photons * linearScale - (dist * light->fade);
3671 T = (light->photons * linearScale) - (dist * light->fade);
3672 T + (dist * light->fade) = (light->photons * linearScale);
3673 dist * light->fade = (light->photons * linearScale) - T;
3674 dist = ((light->photons * linearScale) - T) / light->fade;
3677 /* solve for inverse square falloff */
3679 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3682 add = light->photons / (dist * dist);
3683 T = light->photons / (dist * dist);
3684 T * (dist * dist) = light->photons;
3685 dist = sqrt( light->photons / T );
3689 /* chop radius against pvs */
3692 ClearBounds( mins, maxs );
3694 /* check all leaves */
3695 for( i = 0; i < numBSPLeafs; i++ )
3698 leaf = &bspLeafs[ i ];
3701 if( leaf->cluster < 0 )
3703 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3706 /* add this leafs bbox to the bounds */
3707 VectorCopy( leaf->mins, origin );
3708 AddPointToBounds( origin, mins, maxs );
3709 VectorCopy( leaf->maxs, origin );
3710 AddPointToBounds( origin, mins, maxs );
3713 /* test to see if bounds encompass light */
3714 for( i = 0; i < 3; i++ )
3716 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3718 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3719 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3720 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3721 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3722 AddPointToBounds( light->origin, mins, maxs );
3726 /* chop the bounds by a plane for area lights and spotlights */
3727 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3728 ChopBounds( mins, maxs, light->origin, light->normal );
3731 VectorCopy( mins, light->mins );
3732 VectorCopy( maxs, light->maxs );
3734 /* reflect bounds around light origin */
3735 //% VectorMA( light->origin, -1.0f, origin, origin );
3736 VectorScale( light->origin, 2, origin );
3737 VectorSubtract( origin, maxs, origin );
3738 AddPointToBounds( origin, mins, maxs );
3739 //% VectorMA( light->origin, -1.0f, mins, origin );
3740 VectorScale( light->origin, 2, origin );
3741 VectorSubtract( origin, mins, origin );
3742 AddPointToBounds( origin, mins, maxs );
3744 /* calculate spherical bounds */
3745 VectorSubtract( maxs, light->origin, dir );
3746 radius = (float) VectorLength( dir );
3748 /* if this radius is smaller than the envelope, then set the envelope to it */
3749 if( radius < light->envelope )
3751 light->envelope = radius;
3752 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3755 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3758 /* add grid/surface only check */
3761 if( !(light->flags & LIGHT_GRID) )
3762 light->envelope = 0.0f;
3766 if( !(light->flags & LIGHT_SURFACES) )
3767 light->envelope = 0.0f;
3772 if( light->cluster < 0 || light->envelope <= 0.0f )
3775 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3777 /* delete the light */
3779 *owner = light->next;
3780 if( light->w != NULL )
3787 /* square envelope */
3788 light->envelope2 = (light->envelope * light->envelope);
3790 /* increment light count */
3793 /* set next light */
3794 owner = &((**owner).next);
3797 /* bucket sort lights by style */
3798 memset( buckets, 0, sizeof( buckets ) );
3800 for( light = lights; light != NULL; light = light2 )
3802 /* get next light */
3803 light2 = light->next;
3805 /* filter into correct bucket */
3806 light->next = buckets[ light->style ];
3807 buckets[ light->style ] = light;
3809 /* if any styled light is present, automatically set nocollapse */
3810 if( light->style != LS_NORMAL )
3814 /* filter back into light list */
3816 for( i = 255; i >= 0; i-- )
3819 for( light = buckets[ i ]; light != NULL; light = light2 )
3821 light2 = light->next;
3822 light->next = lights;
3827 /* emit some statistics */
3828 Sys_Printf( "%9d total lights\n", numLights );
3829 Sys_Printf( "%9d culled lights\n", numCulledLights );
3835 CreateTraceLightsForBounds()
3836 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3839 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3843 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3844 float radius, dist, length;
3847 /* potential pre-setup */
3848 if( numLights == 0 )
3849 SetupEnvelopes( qfalse, fast );
3852 //% 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 ] );
3854 /* allocate the light list */
3855 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3856 trace->numLights = 0;
3858 /* calculate spherical bounds */
3859 VectorAdd( mins, maxs, origin );
3860 VectorScale( origin, 0.5f, origin );
3861 VectorSubtract( maxs, origin, dir );
3862 radius = (float) VectorLength( dir );
3864 /* get length of normal vector */
3865 if( normal != NULL )
3866 length = VectorLength( normal );
3869 normal = nullVector;
3873 /* test each light and see if it reaches the sphere */
3874 /* note: the attenuation code MUST match LightingAtSample() */
3875 for( light = lights; light; light = light->next )
3877 /* check zero sized envelope */
3878 if( light->envelope <= 0 )
3880 lightsEnvelopeCulled++;
3885 if( !(light->flags & flags) )
3888 /* sunlight skips all this nonsense */
3889 if( light->type != EMIT_SUN )
3895 /* check against pvs cluster */
3896 if( numClusters > 0 && clusters != NULL )
3898 for( i = 0; i < numClusters; i++ )
3900 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3905 if( i == numClusters )
3907 lightsClusterCulled++;
3912 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3913 VectorSubtract( light->origin, origin, dir );
3914 dist = VectorLength( dir );
3915 dist -= light->envelope;
3919 lightsEnvelopeCulled++;
3923 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3926 for( i = 0; i < 3; i++ )
3928 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3933 lightsBoundsCulled++;
3939 /* planar surfaces (except twosided surfaces) have a couple more checks */
3940 if( length > 0.0f && trace->twoSided == qfalse )
3942 /* lights coplanar with a surface won't light it */
3943 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3945 lightsPlaneCulled++;
3949 /* check to see if light is behind the plane */
3950 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3952 lightsPlaneCulled++;
3957 /* add this light */
3958 trace->lights[ trace->numLights++ ] = light;
3961 /* make last night null */
3962 trace->lights[ trace->numLights ] = NULL;
3967 void FreeTraceLights( trace_t *trace )
3969 if( trace->lights != NULL )
3970 free( trace->lights );
3976 CreateTraceLightsForSurface()
3977 creates a list of lights that can potentially affect a drawsurface
3980 void CreateTraceLightsForSurface( int num, trace_t *trace )
3983 vec3_t mins, maxs, normal;
3985 bspDrawSurface_t *ds;
3986 surfaceInfo_t *info;
3993 /* get drawsurface and info */
3994 ds = &bspDrawSurfaces[ num ];
3995 info = &surfaceInfos[ num ];
3997 /* get the mins/maxs for the dsurf */
3998 ClearBounds( mins, maxs );
3999 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4000 for( i = 0; i < ds->numVerts; i++ )
4002 dv = &yDrawVerts[ ds->firstVert + i ];
4003 AddPointToBounds( dv->xyz, mins, maxs );
4004 if( !VectorCompare( dv->normal, normal ) )
4005 VectorClear( normal );
4008 /* create the lights for the bounding box */
4009 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4012 /////////////////////////////////////////////////////////////
4014 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4015 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4016 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4017 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4019 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4020 static int numFloodVectors = 0;
4022 void SetupFloodLight( void )
4025 float angle, elevation, angleStep, elevationStep;
4027 double v1,v2,v3,v4,v5,v6;
4030 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4032 /* calculate angular steps */
4033 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4034 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4038 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4040 /* iterate elevation */
4041 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4043 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4044 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4045 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4050 /* emit some statistics */
4051 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4054 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4056 if( value[ 0 ] != '\0' )
4059 v4=floodlightDistance;
4060 v5=floodlightIntensity;
4061 v6=floodlightDirectionScale;
4063 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6);
4065 floodlightRGB[0]=v1;
4066 floodlightRGB[1]=v2;
4067 floodlightRGB[2]=v3;
4069 if (VectorLength(floodlightRGB)==0)
4071 VectorSet(floodlightRGB,240,240,255);
4078 floodlightDistance=v4;
4079 floodlightIntensity=v5;
4080 floodlightDirectionScale=v6;
4082 floodlighty = qtrue;
4083 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4087 VectorSet(floodlightRGB,240,240,255);
4088 //floodlighty = qtrue;
4089 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4091 VectorNormalize(floodlightRGB,floodlightRGB);
4095 FloodLightForSample()
4096 calculates floodlight value for a given sample
4097 once again, kudos to the dirtmapping coder
4100 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4106 float gatherLight, outLight;
4107 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4115 if( trace == NULL || trace->cluster < 0 )
4120 dd = floodLightDistance;
4121 VectorCopy( trace->normal, normal );
4123 /* check if the normal is aligned to the world-up */
4124 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4126 if( normal[ 2 ] == 1.0f )
4128 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4129 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4131 else if( normal[ 2 ] == -1.0f )
4133 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4134 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4139 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4140 CrossProduct( normal, worldUp, myRt );
4141 VectorNormalize( myRt, myRt );
4142 CrossProduct( myRt, normal, myUp );
4143 VectorNormalize( myUp, myUp );
4146 /* vortex: optimise floodLightLowQuality a bit */
4147 if ( floodLightLowQuality == qtrue )
4149 /* iterate through ordered vectors */
4150 for( i = 0; i < numFloodVectors; i++ )
4151 if (rand()%10 != 0 ) continue;
4155 /* iterate through ordered vectors */
4156 for( i = 0; i < numFloodVectors; i++ )
4160 /* transform vector into tangent space */
4161 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4162 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4163 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4166 VectorMA( trace->origin, dd, direction, trace->end );
4168 //VectorMA( trace->origin, 1, direction, trace->origin );
4170 SetupTrace( trace );
4175 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4179 else if ( trace->opaque )
4181 VectorSubtract( trace->hit, trace->origin, displacement );
4182 d=VectorLength( displacement );
4184 // d=trace->distance;
4185 //if (d>256) gatherDirt+=1;
4187 if (contribution>1) contribution=1.0f;
4189 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4192 gatherLight+=contribution;
4197 if( gatherLight <= 0.0f )
4205 outLight=gatherLight;
4206 if( outLight > 1.0f )
4209 /* return to sender */
4214 FloodLightRawLightmap
4215 lighttracer style ambient occlusion light hack.
4216 Kudos to the dirtmapping author for most of this source.
4217 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4218 VorteX: fixed problems with deluxemapping
4221 // floodlight pass on a lightmap
4222 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4224 int i, x, y, *cluster;
4225 float *origin, *normal, *floodlight, floodLightAmount;
4226 surfaceInfo_t *info;
4229 // float samples, average, *floodlight2;
4231 memset(&trace,0,sizeof(trace_t));
4234 trace.testOcclusion = qtrue;
4235 trace.forceSunlight = qfalse;
4236 trace.twoSided = qtrue;
4237 trace.recvShadows = lm->recvShadows;
4238 trace.numSurfaces = lm->numLightSurfaces;
4239 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4240 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4241 trace.testAll = qfalse;
4242 trace.distance = 1024;
4244 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4245 //trace.twoSided = qfalse;
4246 for( i = 0; i < trace.numSurfaces; i++ )
4249 info = &surfaceInfos[ trace.surfaces[ i ] ];
4251 /* check twosidedness */
4252 if( info->si->twoSided )
4254 trace.twoSided = qtrue;
4259 /* gather floodlight */
4260 for( y = 0; y < lm->sh; y++ )
4262 for( x = 0; x < lm->sw; x++ )
4265 cluster = SUPER_CLUSTER( x, y );
4266 origin = SUPER_ORIGIN( x, y );
4267 normal = SUPER_NORMAL( x, y );
4268 floodlight = SUPER_FLOODLIGHT( x, y );
4270 /* set default dirt */
4273 /* only look at mapped luxels */
4278 trace.cluster = *cluster;
4279 VectorCopy( origin, trace.origin );
4280 VectorCopy( normal, trace.normal );
4282 /* get floodlight */
4283 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4285 /* add floodlight */
4286 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4287 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4288 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4289 floodlight[3] += floodlightDirectionScale;
4293 /* testing no filtering */
4299 for( y = 0; y < lm->sh; y++ )
4301 for( x = 0; x < lm->sw; x++ )
4304 cluster = SUPER_CLUSTER( x, y );
4305 floodlight = SUPER_FLOODLIGHT(x, y );
4307 /* filter dirt by adjacency to unmapped luxels */
4308 average = *floodlight;
4310 for( sy = (y - 1); sy <= (y + 1); sy++ )
4312 if( sy < 0 || sy >= lm->sh )
4315 for( sx = (x - 1); sx <= (x + 1); sx++ )
4317 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4320 /* get neighboring luxel */
4321 cluster = SUPER_CLUSTER( sx, sy );
4322 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4323 if( *cluster < 0 || *floodlight2 <= 0.0f )
4327 average += *floodlight2;
4332 if( samples <= 0.0f )
4337 if( samples <= 0.0f )
4341 *floodlight = average / samples;
4347 void FloodLightRawLightmap( int rawLightmapNum )
4351 /* bail if this number exceeds the number of raw lightmaps */
4352 if( rawLightmapNum >= numRawLightmaps )
4355 lm = &rawLightmaps[ rawLightmapNum ];
4358 if (floodlighty && floodlightIntensity)
4359 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
4362 if (lm->floodlightIntensity)
4364 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4365 numSurfacesFloodlighten += 1;
4369 void FloodlightRawLightmaps()
4371 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4372 numSurfacesFloodlighten = 0;
4373 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4374 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4378 FloodLightIlluminate()
4379 illuminate floodlight into lightmap luxels
4382 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4384 float *luxel, *floodlight, *deluxel, *normal;
4387 int x, y, lightmapNum;
4389 /* walk lightmaps */
4390 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4393 if( lm->superLuxels[ lightmapNum ] == NULL )
4396 /* apply floodlight to each luxel */
4397 for( y = 0; y < lm->sh; y++ )
4399 for( x = 0; x < lm->sw; x++ )
4401 /* get floodlight */
4402 floodlight = SUPER_FLOODLIGHT( x, y );
4403 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4407 cluster = SUPER_CLUSTER( x, y );
4409 /* only process mapped luxels */
4413 /* get particulars */
4414 luxel = SUPER_LUXEL( lightmapNum, x, y );
4415 deluxel = SUPER_DELUXEL( x, y );
4417 /* add to lightmap */
4418 luxel[0]+=floodlight[0];
4419 luxel[1]+=floodlight[1];
4420 luxel[2]+=floodlight[2];
4422 if (luxel[3]==0) luxel[3]=1;
4424 /* add to deluxemap */
4425 if (deluxemap && floodlight[3] > 0)
4429 normal = SUPER_NORMAL( x, y );
4430 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4432 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4433 if(brightness < 0.00390625f)
4434 brightness = 0.00390625f;
4436 VectorScale( normal, brightness, lightvector );
4437 VectorAdd( deluxel, lightvector, deluxel );