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 ){
54 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
55 if ( scale <= 0.0f ) {
59 scale *= lightmapBrightness;
61 /* make a local copy */
62 VectorScale( color, scale, sample );
65 gamma = 1.0f / lightmapGamma;
66 for ( i = 0; i < 3; i++ )
68 /* handle negative light */
69 if ( sample[ i ] < 0.0f ) {
75 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
78 if ( lightmapExposure == 0 ) {
79 /* clamp with color normalization */
81 if ( sample[ 1 ] > max ) {
84 if ( sample[ 2 ] > max ) {
88 VectorScale( sample, ( 255.0f / max ), sample );
93 inv = 1.f / lightmapExposure;
97 if ( sample[ 1 ] > max ) {
100 if ( sample[ 2 ] > max ) {
104 dif = ( 1 - exp( -max * inv ) ) * 255;
114 for ( i = 0; i < 3; i++ )
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
125 if ( lightmapContrast != 1.0f ){
126 for ( i = 0; i < 3; i++ ){
127 sample[i] = lightmapContrast * ( sample[i] - 128 ) + 128;
128 if ( sample[i] < 0 ){
132 if ( ( sample[0] > 255 ) || ( sample[1] > 255 ) || ( sample[2] > 255 ) ) {
133 max = sample[0] > sample[1] ? sample[0] : sample[1];
134 max = max > sample[2] ? max : sample[2];
135 sample[0] = sample[0] * 255 / max;
136 sample[1] = sample[1] * 255 / max;
137 sample[2] = sample[2] * 255 / max;
142 if ( lightmapsRGB ) {
143 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
144 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
145 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
149 colorBytes[ 0 ] = sample[ 0 ];
150 colorBytes[ 1 ] = sample[ 1 ];
151 colorBytes[ 2 ] = sample[ 2 ];
155 * Same as ColorToBytes, but if the output color will never contain zero
156 * components. Used to avoid returning 0 0 0 due to an ioq3 issue. Reason
157 * to also map 0 0 1 to 1 1 1 is to ensure monotonicity in the color mapping
158 * to prevent banding-like artifacts on lightmaps.
160 void ColorToBytesNonZero( const float *color, byte *colorBytes, float scale) {
162 ColorToBytes(color, colorBytes, scale);
163 for (i = 0; i < 3; ++i)
164 if (colorBytes[i] == 0)
169 /* -------------------------------------------------------------------------------
171 this section deals with phong shading (normal interpolation across brush faces)
173 ------------------------------------------------------------------------------- */
177 smooths together coincident vertex normals across the bsp
180 #define MAX_SAMPLES 256
181 #define THETA_EPSILON 0.000001
182 #define EQUAL_NORMAL_EPSILON 0.01
184 void SmoothNormals( void ){
185 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
186 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
187 bspDrawSurface_t *ds;
191 vec3_t average, diff;
192 int indexes[ MAX_SAMPLES ];
193 vec3_t votes[ MAX_SAMPLES ];
196 /* allocate shade angle table */
197 shadeAngles = safe_malloc0( numBSPDrawVerts * sizeof( float ) );
199 /* allocate smoothed table */
200 cs = ( numBSPDrawVerts / 8 ) + 1;
201 smoothed = safe_malloc0( cs );
203 /* set default shade angle */
204 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
207 /* run through every surface and flag verts belonging to non-lightmapped surfaces
208 and set per-vertex smoothing angle */
209 for ( i = 0; i < numBSPDrawSurfaces; i++ )
212 ds = &bspDrawSurfaces[ i ];
214 /* get shader for shade angle */
215 si = surfaceInfos[ i ].si;
216 if ( si->shadeAngleDegrees ) {
217 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
220 shadeAngle = defaultShadeAngle;
222 if ( shadeAngle > maxShadeAngle ) {
223 maxShadeAngle = shadeAngle;
227 for ( j = 0; j < ds->numVerts; j++ )
229 f = ds->firstVert + j;
230 shadeAngles[ f ] = shadeAngle;
231 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
232 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
236 /* ydnar: optional force-to-trisoup */
237 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
238 ds->surfaceType = MST_TRIANGLE_SOUP;
239 ds->lightmapNum[ 0 ] = -3;
243 /* bail if no surfaces have a shade angle */
244 if ( maxShadeAngle == 0 ) {
252 start = I_FloatTime();
254 /* go through the list of vertexes */
255 for ( i = 0; i < numBSPDrawVerts; i++ )
258 f = 10 * i / numBSPDrawVerts;
261 Sys_Printf( "%i...", f );
264 /* already smoothed? */
265 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
270 VectorClear( average );
274 /* build a table of coincident vertexes */
275 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
277 /* already smoothed? */
278 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
283 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
287 /* use smallest shade angle */
288 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
290 /* check shade angle */
291 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
295 else if ( dot < -1.0 ) {
298 testAngle = acos( dot ) + THETA_EPSILON;
299 if ( testAngle >= shadeAngle ) {
300 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
303 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
305 /* add to the list */
306 indexes[ numVerts++ ] = j;
309 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
311 /* see if this normal has already been voted */
312 for ( k = 0; k < numVotes; k++ )
314 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
315 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
316 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
317 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
322 /* add a new vote? */
323 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
324 VectorAdd( average, bspDrawVerts[ j ].normal, average );
325 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
330 /* don't average for less than 2 verts */
331 if ( numVerts < 2 ) {
336 if ( VectorNormalize( average, average ) > 0 ) {
338 for ( j = 0; j < numVerts; j++ )
339 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
343 /* free the tables */
348 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
353 /* -------------------------------------------------------------------------------
355 this section deals with phong shaded lightmap tracing
357 ------------------------------------------------------------------------------- */
359 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
363 calculates the st tangent vectors for normalmapping
366 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
372 /* calculate barycentric basis for the triangle */
373 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 ] );
374 if ( fabs( bb ) < 0.00000001f ) {
379 for ( i = 0; i < numVerts; i++ )
381 /* calculate s tangent vector */
382 s = dv[ i ]->st[ 0 ] + 10.0f;
383 t = dv[ i ]->st[ 1 ];
384 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
385 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
386 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
388 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
389 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
390 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
392 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
393 VectorNormalize( stv[ i ], stv[ i ] );
395 /* calculate t tangent vector */
396 s = dv[ i ]->st[ 0 ];
397 t = dv[ i ]->st[ 1 ] + 10.0f;
398 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
399 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
400 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
402 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
403 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
404 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
406 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
407 VectorNormalize( ttv[ i ], ttv[ i ] );
410 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
411 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
414 /* return to caller */
423 perterbs the normal by the shader's normalmap in tangent space
426 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
432 VectorCopy( dv->normal, pNormal );
434 /* sample normalmap */
435 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
439 /* remap sampled normal from [0,255] to [-1,-1] */
440 for ( i = 0; i < 3; i++ )
441 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
443 /* scale tangent vectors and add to original normal */
444 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
445 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
446 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
448 /* renormalize and return */
449 VectorNormalize( pNormal, pNormal );
456 maps a luxel for triangle bv at
460 #define BOGUS_NUDGE -99999.0f
462 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 ] ){
463 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
464 float *luxel, *origin, *normal, d, lightmapSampleOffset;
471 vec4_t sideplane, hostplane;
476 static float nudges[][ 2 ] =
478 //%{ 0, 0 }, /* try center first */
479 { -NUDGE, 0 }, /* left */
480 { NUDGE, 0 }, /* right */
481 { 0, NUDGE }, /* up */
482 { 0, -NUDGE }, /* down */
483 { -NUDGE, NUDGE }, /* left/up */
484 { NUDGE, -NUDGE }, /* right/down */
485 { NUDGE, NUDGE }, /* right/up */
486 { -NUDGE, -NUDGE }, /* left/down */
487 { BOGUS_NUDGE, BOGUS_NUDGE }
491 /* find luxel xy coords (fixme: subtract 0.5?) */
492 x = dv->lightmap[ 0 ][ 0 ];
493 y = dv->lightmap[ 0 ][ 1 ];
497 else if ( x >= lm->sw ) {
503 else if ( y >= lm->sh ) {
507 /* set shader and cluster list */
508 if ( info != NULL ) {
510 numClusters = info->numSurfaceClusters;
511 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
520 /* get luxel, origin, cluster, and normal */
521 luxel = SUPER_LUXEL( 0, x, y );
522 origin = SUPER_ORIGIN( x, y );
523 normal = SUPER_NORMAL( x, y );
524 cluster = SUPER_CLUSTER( x, y );
526 /* don't attempt to remap occluded luxels for planar surfaces */
527 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
531 /* only average the normal for premapped luxels */
532 else if ( ( *cluster ) >= 0 ) {
533 /* do bumpmap calculations */
535 PerturbNormal( dv, si, pNormal, stv, ttv );
538 VectorCopy( dv->normal, pNormal );
541 /* add the additional normal data */
542 VectorAdd( normal, pNormal, normal );
547 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
551 /* axial lightmap projection */
552 if ( lm->vecs != NULL ) {
553 /* calculate an origin for the sample from the lightmap vectors */
554 VectorCopy( lm->origin, origin );
555 for ( i = 0; i < 3; i++ )
557 /* add unless it's the axis, which is taken care of later */
558 if ( i == lm->axisNum ) {
561 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
564 /* project the origin onto the plane */
565 d = DotProduct( origin, plane ) - plane[ 3 ];
566 d /= plane[ lm->axisNum ];
567 origin[ lm->axisNum ] -= d;
570 /* non axial lightmap projection (explicit xyz) */
572 VectorCopy( dv->xyz, origin );
575 //////////////////////
576 //27's test to make sure samples stay within the triangle boundaries
577 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
578 //2) if it does, nudge it onto the correct side.
580 if ( worldverts != NULL && lightmapTriangleCheck ) {
581 for ( j = 0; j < 3; j++ )
583 VectorCopy( worldverts[j],cverts[j] );
585 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
587 for ( j = 0; j < 3; j++ )
589 for ( i = 0; i < 3; i++ )
591 //build plane using 2 edges and a normal
592 next = ( i + 1 ) % 3;
594 VectorCopy( cverts[next],temp );
595 VectorAdd( temp,hostplane,temp );
596 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
598 //planetest sample point
599 e = DotProduct( origin,sideplane );
600 e = e - sideplane[3];
603 //VectorClear(origin);
604 //Move the sample point back inside triangle bounds
605 origin[0] -= sideplane[0] * ( e + 1 );
606 origin[1] -= sideplane[1] * ( e + 1 );
607 origin[2] -= sideplane[2] * ( e + 1 );
609 VectorClear( origin );
616 ////////////////////////
618 /* planar surfaces have precalculated lightmap vectors for nudging */
619 if ( lm->plane != NULL ) {
620 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
621 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
622 VectorCopy( lm->plane, vecs[ 2 ] );
625 /* non-planar surfaces must calculate them */
628 if ( plane != NULL ) {
629 VectorCopy( plane, vecs[ 2 ] );
632 VectorCopy( dv->normal, vecs[ 2 ] );
634 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
637 /* push the origin off the surface a bit */
639 lightmapSampleOffset = si->lightmapSampleOffset;
642 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
644 if ( lm->axisNum < 0 ) {
645 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
647 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
648 origin[ lm->axisNum ] -= lightmapSampleOffset;
651 origin[ lm->axisNum ] += lightmapSampleOffset;
654 VectorCopy( origin,origintwo );
655 if ( lightmapExtraVisClusterNudge ) {
656 origintwo[0] += vecs[2][0];
657 origintwo[1] += vecs[2][1];
658 origintwo[2] += vecs[2][2];
662 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
664 /* another retarded hack, storing nudge count in luxel[ 1 ] */
667 /* point in solid? (except in dark mode) */
668 if ( pointCluster < 0 && dark == qfalse ) {
669 /* nudge the the location around */
671 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
673 /* nudge the vector around a bit */
674 for ( i = 0; i < 3; i++ )
676 /* set nudged point*/
677 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
681 /* get pvs cluster */
682 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
683 if ( pointCluster >= 0 ) {
684 VectorCopy( nudged, origin );
690 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
691 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
692 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
693 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
694 if ( pointCluster >= 0 ) {
695 VectorCopy( nudged, origin );
701 if ( pointCluster < 0 ) {
702 ( *cluster ) = CLUSTER_OCCLUDED;
703 VectorClear( origin );
704 VectorClear( normal );
710 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
712 /* do bumpmap calculations */
714 PerturbNormal( dv, si, pNormal, stv, ttv );
717 VectorCopy( dv->normal, pNormal );
720 /* store the cluster and normal */
721 ( *cluster ) = pointCluster;
722 VectorCopy( pNormal, normal );
724 /* store explicit mapping pass and implicit mapping pass */
739 recursively subdivides a triangle until its edges are shorter
740 than the distance between two luxels (thanks jc :)
743 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 ] ){
744 bspDrawVert_t mid, *dv2[ 3 ];
748 /* map the vertexes */
750 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
751 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
752 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
758 float *a, *b, dx, dy, dist, maxDist;
761 /* find the longest edge and split it */
764 for ( i = 0; i < 3; i++ )
767 a = dv[ i ]->lightmap[ 0 ];
768 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
771 dx = a[ 0 ] - b[ 0 ];
772 dy = a[ 1 ] - b[ 1 ];
773 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
776 if ( dist > maxDist ) {
782 /* try to early out */
783 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
788 /* split the longest edge and map it */
789 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
790 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
792 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
793 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
795 /* recurse to first triangle */
796 VectorCopy( dv, dv2 );
798 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
800 /* recurse to second triangle */
801 VectorCopy( dv, dv2 );
802 dv2[ ( max + 1 ) % 3 ] = ∣
803 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
810 seed function for MapTriangle_r()
811 requires a cw ordered triangle
814 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
817 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
818 vec3_t worldverts[ 3 ];
821 /* get plane if possible */
822 if ( lm->plane != NULL ) {
823 VectorCopy( lm->plane, plane );
824 plane[ 3 ] = lm->plane[ 3 ];
827 /* otherwise make one from the points */
828 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
832 /* check to see if we need to calculate texture->world tangent vectors */
833 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
843 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
844 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
845 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
847 /* map the vertexes */
848 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
849 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
850 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
852 /* 2002-11-20: prefer axial triangle edges */
854 /* subdivide the triangle */
855 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
859 for ( i = 0; i < 3; i++ )
862 bspDrawVert_t *dv2[ 3 ];
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
869 /* make degenerate triangles for mapping edges */
870 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
872 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
873 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
875 /* map the degenerate triangle */
876 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
887 recursively subdivides a quad until its edges are shorter
888 than the distance between two luxels
891 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 ] ){
892 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
899 float *a, *b, dx, dy, dist, maxDist;
902 /* find the longest edge and split it */
905 for ( i = 0; i < 4; i++ )
908 a = dv[ i ]->lightmap[ 0 ];
909 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
912 dx = a[ 0 ] - b[ 0 ];
913 dy = a[ 1 ] - b[ 1 ];
914 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
917 if ( dist > maxDist ) {
923 /* try to early out */
924 if ( max < 0 || maxDist <= subdivideThreshold ) {
929 /* we only care about even/odd edges */
932 /* split the longest edges */
933 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
934 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
936 /* map the vertexes */
937 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
938 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
942 /* recurse to first quad */
944 dv2[ 1 ] = &mid[ 0 ];
945 dv2[ 2 ] = &mid[ 1 ];
947 MapQuad_r( lm, info, dv2, plane, stv, ttv );
949 /* recurse to second quad */
950 dv2[ 0 ] = &mid[ 0 ];
953 dv2[ 3 ] = &mid[ 1 ];
954 MapQuad_r( lm, info, dv2, plane, stv, ttv );
960 /* recurse to first quad */
963 dv2[ 2 ] = &mid[ 0 ];
964 dv2[ 3 ] = &mid[ 1 ];
965 MapQuad_r( lm, info, dv2, plane, stv, ttv );
967 /* recurse to second quad */
968 dv2[ 0 ] = &mid[ 1 ];
969 dv2[ 1 ] = &mid[ 0 ];
972 MapQuad_r( lm, info, dv2, plane, stv, ttv );
980 seed function for MapQuad_r()
981 requires a cw ordered triangle quad
984 #define QUAD_PLANAR_EPSILON 0.5f
986 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
989 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
992 /* get plane if possible */
993 if ( lm->plane != NULL ) {
994 VectorCopy( lm->plane, plane );
995 plane[ 3 ] = lm->plane[ 3 ];
998 /* otherwise make one from the points */
999 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
1003 /* 4th point must fall on the plane */
1004 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
1005 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
1009 /* check to see if we need to calculate texture->world tangent vectors */
1010 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
1020 /* map the vertexes */
1021 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1022 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1023 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1024 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1026 /* subdivide the quad */
1027 MapQuad_r( lm, info, dv, plane, stv, ttv );
1035 maps the locations, normals, and pvs clusters for a raw lightmap
1038 #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)
1040 void MapRawLightmap( int rawLightmapNum ){
1041 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1042 float *luxel, *origin, *normal, samples, radius, pass;
1044 bspDrawSurface_t *ds;
1045 surfaceInfo_t *info;
1046 mesh_t src, *subdivided, *mesh;
1047 bspDrawVert_t *verts, *dv[ 4 ], fake;
1050 /* bail if this number exceeds the number of raw lightmaps */
1051 if ( rawLightmapNum >= numRawLightmaps ) {
1056 lm = &rawLightmaps[ rawLightmapNum ];
1058 /* -----------------------------------------------------------------
1059 map referenced surfaces onto the raw lightmap
1060 ----------------------------------------------------------------- */
1062 /* walk the list of surfaces on this raw lightmap */
1063 for ( n = 0; n < lm->numLightSurfaces; n++ )
1065 /* with > 1 surface per raw lightmap, clear occluded */
1067 for ( y = 0; y < lm->sh; y++ )
1069 for ( x = 0; x < lm->sw; x++ )
1072 cluster = SUPER_CLUSTER( x, y );
1073 if ( *cluster < 0 ) {
1074 *cluster = CLUSTER_UNMAPPED;
1081 num = lightSurfaces[ lm->firstLightSurface + n ];
1082 ds = &bspDrawSurfaces[ num ];
1083 info = &surfaceInfos[ num ];
1085 /* bail if no lightmap to calculate */
1086 if ( info->lm != lm ) {
1091 /* map the surface onto the lightmap origin/cluster/normal buffers */
1092 switch ( ds->surfaceType )
1096 verts = yDrawVerts + ds->firstVert;
1098 /* map the triangles */
1099 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1101 for ( i = 0; i < ds->numIndexes; i += 3 )
1103 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1104 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1105 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1106 MapTriangle( lm, info, dv, mapNonAxial );
1112 /* make a mesh from the drawsurf */
1113 src.width = ds->patchWidth;
1114 src.height = ds->patchHeight;
1115 src.verts = &yDrawVerts[ ds->firstVert ];
1116 //% subdivided = SubdivideMesh( src, 8, 512 );
1117 subdivided = SubdivideMesh2( src, info->patchIterations );
1119 /* fit it to the curve and remove colinear verts on rows/columns */
1120 PutMeshOnCurve( *subdivided );
1121 mesh = RemoveLinearMeshColumnsRows( subdivided );
1122 FreeMesh( subdivided );
1125 verts = mesh->verts;
1130 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1131 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1132 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1133 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1137 /* map the mesh quads */
1140 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1142 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1144 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1147 pw[ 0 ] = x + ( y * mesh->width );
1148 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1149 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1150 pw[ 3 ] = x + 1 + ( y * mesh->width );
1151 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1156 /* get drawverts and map first triangle */
1157 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1158 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1159 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1160 MapTriangle( lm, info, dv, mapNonAxial );
1162 /* get drawverts and map second triangle */
1163 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1164 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1165 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1166 MapTriangle( lm, info, dv, mapNonAxial );
1173 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1175 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1178 pw[ 0 ] = x + ( y * mesh->width );
1179 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1180 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1181 pw[ 3 ] = x + 1 + ( y * mesh->width );
1187 /* attempt to map quad first */
1188 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1189 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1190 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1191 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1192 if ( MapQuad( lm, info, dv ) ) {
1196 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1198 /* get drawverts and map first triangle */
1199 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1200 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1201 MapTriangle( lm, info, dv, mapNonAxial );
1203 /* get drawverts and map second triangle */
1204 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1205 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1206 MapTriangle( lm, info, dv, mapNonAxial );
1222 /* -----------------------------------------------------------------
1223 average and clean up luxel normals
1224 ----------------------------------------------------------------- */
1226 /* walk the luxels */
1227 for ( y = 0; y < lm->sh; y++ )
1229 for ( x = 0; x < lm->sw; x++ )
1232 luxel = SUPER_LUXEL( 0, x, y );
1233 normal = SUPER_NORMAL( x, y );
1234 cluster = SUPER_CLUSTER( x, y );
1236 /* only look at mapped luxels */
1237 if ( *cluster < 0 ) {
1241 /* the normal data could be the sum of multiple samples */
1242 if ( luxel[ 3 ] > 1.0f ) {
1243 VectorNormalize( normal, normal );
1246 /* mark this luxel as having only one normal */
1251 /* non-planar surfaces stop here */
1252 if ( lm->plane == NULL ) {
1256 /* -----------------------------------------------------------------
1257 map occluded or unuxed luxels
1258 ----------------------------------------------------------------- */
1260 /* walk the luxels */
1261 /* FIXME: superSample is int, no need in floor() */
1262 radius = floor( superSample / 2 );
1263 radius = radius > 0 ? radius : 1.0f;
1265 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1267 for ( y = 0; y < lm->sh; y++ )
1269 for ( x = 0; x < lm->sw; x++ )
1272 luxel = SUPER_LUXEL( 0, x, y );
1273 normal = SUPER_NORMAL( x, y );
1274 cluster = SUPER_CLUSTER( x, y );
1276 /* only look at unmapped luxels */
1277 if ( *cluster != CLUSTER_UNMAPPED ) {
1281 /* divine a normal and origin from neighboring luxels */
1282 VectorClear( fake.xyz );
1283 VectorClear( fake.normal );
1284 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1285 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1287 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1289 if ( sy < 0 || sy >= lm->sh ) {
1293 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1295 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1299 /* get neighboring luxel */
1300 luxel = SUPER_LUXEL( 0, sx, sy );
1301 origin = SUPER_ORIGIN( sx, sy );
1302 normal = SUPER_NORMAL( sx, sy );
1303 cluster = SUPER_CLUSTER( sx, sy );
1305 /* only consider luxels mapped in previous passes */
1306 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1310 /* add its distinctiveness to our own */
1311 VectorAdd( fake.xyz, origin, fake.xyz );
1312 VectorAdd( fake.normal, normal, fake.normal );
1313 samples += luxel[ 3 ];
1318 if ( samples == 0.0f ) {
1323 VectorDivide( fake.xyz, samples, fake.xyz );
1324 //% VectorDivide( fake.normal, samples, fake.normal );
1325 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1329 /* map the fake vert */
1330 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1335 /* -----------------------------------------------------------------
1336 average and clean up luxel normals
1337 ----------------------------------------------------------------- */
1339 /* walk the luxels */
1340 for ( y = 0; y < lm->sh; y++ )
1342 for ( x = 0; x < lm->sw; x++ )
1345 luxel = SUPER_LUXEL( 0, x, y );
1346 normal = SUPER_NORMAL( x, y );
1347 cluster = SUPER_CLUSTER( x, y );
1349 /* only look at mapped luxels */
1350 if ( *cluster < 0 ) {
1354 /* the normal data could be the sum of multiple samples */
1355 if ( luxel[ 3 ] > 1.0f ) {
1356 VectorNormalize( normal, normal );
1359 /* mark this luxel as having only one normal */
1367 for ( y = 0; y < lm->sh; y++ )
1369 for ( x = 0; x < lm->sw; x++ )
1374 cluster = SUPER_CLUSTER( x, y );
1375 origin = SUPER_ORIGIN( x, y );
1376 normal = SUPER_NORMAL( x, y );
1377 luxel = SUPER_LUXEL( x, y );
1379 if ( *cluster < 0 ) {
1383 /* check if within the bounding boxes of all surfaces referenced */
1384 ClearBounds( mins, maxs );
1385 for ( n = 0; n < lm->numLightSurfaces; n++ )
1388 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1389 TOL = info->sampleSize + 2;
1390 AddPointToBounds( info->mins, mins, maxs );
1391 AddPointToBounds( info->maxs, mins, maxs );
1392 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1393 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1394 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1400 if ( n < lm->numLightSurfaces ) {
1404 /* report bogus origin */
1405 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",
1406 rawLightmapNum, x, y, *cluster,
1407 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1408 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1409 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1420 sets up dirtmap (ambient occlusion)
1423 #define DIRT_CONE_ANGLE 88 /* degrees */
1424 #define DIRT_NUM_ANGLE_STEPS 16
1425 #define DIRT_NUM_ELEVATION_STEPS 3
1426 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1428 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1429 static int numDirtVectors = 0;
1431 void SetupDirt( void ){
1433 float angle, elevation, angleStep, elevationStep;
1437 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1439 /* calculate angular steps */
1440 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1441 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1445 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1447 /* iterate elevation */
1448 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1450 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1451 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1452 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1457 /* emit some statistics */
1458 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1464 calculates dirt value for a given sample
1467 float DirtForSample( trace_t *trace ){
1469 float gatherDirt, outDirt, angle, elevation, ooDepth;
1470 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1477 if ( trace == NULL || trace->cluster < 0 ) {
1483 ooDepth = 1.0f / dirtDepth;
1484 VectorCopy( trace->normal, normal );
1486 /* check if the normal is aligned to the world-up */
1487 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1488 if ( normal[ 2 ] == 1.0f ) {
1489 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1490 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1492 else if ( normal[ 2 ] == -1.0f ) {
1493 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1494 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1499 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1500 CrossProduct( normal, worldUp, myRt );
1501 VectorNormalize( myRt, myRt );
1502 CrossProduct( myRt, normal, myUp );
1503 VectorNormalize( myUp, myUp );
1506 /* 1 = random mode, 0 (well everything else) = non-random mode */
1507 if ( dirtMode == 1 ) {
1509 for ( i = 0; i < numDirtVectors; i++ )
1511 /* get random vector */
1512 angle = Random() * DEG2RAD( 360.0f );
1513 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1514 temp[ 0 ] = cos( angle ) * sin( elevation );
1515 temp[ 1 ] = sin( angle ) * sin( elevation );
1516 temp[ 2 ] = cos( elevation );
1518 /* transform into tangent space */
1519 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1520 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1521 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1524 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1525 SetupTrace( trace );
1526 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1530 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1531 VectorSubtract( trace->hit, trace->origin, displacement );
1532 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1538 /* iterate through ordered vectors */
1539 for ( i = 0; i < numDirtVectors; i++ )
1541 /* transform vector into tangent space */
1542 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1543 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1544 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1547 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1548 SetupTrace( trace );
1549 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1553 if ( trace->opaque ) {
1554 VectorSubtract( trace->hit, trace->origin, displacement );
1555 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1561 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1562 SetupTrace( trace );
1563 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1567 if ( trace->opaque ) {
1568 VectorSubtract( trace->hit, trace->origin, displacement );
1569 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1573 if ( gatherDirt <= 0.0f ) {
1577 /* apply gain (does this even do much? heh) */
1578 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1579 if ( outDirt > 1.0f ) {
1584 outDirt *= dirtScale;
1585 if ( outDirt > 1.0f ) {
1589 /* return to sender */
1590 return 1.0f - outDirt;
1597 calculates dirty fraction for each luxel
1600 void DirtyRawLightmap( int rawLightmapNum ){
1601 int i, x, y, sx, sy, *cluster;
1602 float *origin, *normal, *dirt, *dirt2, average, samples;
1604 surfaceInfo_t *info;
1609 /* bail if this number exceeds the number of raw lightmaps */
1610 if ( rawLightmapNum >= numRawLightmaps ) {
1615 lm = &rawLightmaps[ rawLightmapNum ];
1618 trace.testOcclusion = qtrue;
1619 trace.forceSunlight = qfalse;
1620 trace.recvShadows = lm->recvShadows;
1621 trace.numSurfaces = lm->numLightSurfaces;
1622 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1623 trace.inhibitRadius = 0.0f;
1624 trace.testAll = qfalse;
1626 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1627 trace.twoSided = qfalse;
1628 for ( i = 0; i < trace.numSurfaces; i++ )
1631 info = &surfaceInfos[ trace.surfaces[ i ] ];
1633 /* check twosidedness */
1634 if ( info->si->twoSided ) {
1635 trace.twoSided = qtrue;
1641 for ( i = 0; i < trace.numSurfaces; i++ )
1644 info = &surfaceInfos[ trace.surfaces[ i ] ];
1646 /* check twosidedness */
1647 if ( info->si->noDirty ) {
1654 for ( y = 0; y < lm->sh; y++ )
1656 for ( x = 0; x < lm->sw; x++ )
1659 cluster = SUPER_CLUSTER( x, y );
1660 origin = SUPER_ORIGIN( x, y );
1661 normal = SUPER_NORMAL( x, y );
1662 dirt = SUPER_DIRT( x, y );
1664 /* set default dirt */
1667 /* only look at mapped luxels */
1668 if ( *cluster < 0 ) {
1672 /* don't apply dirty on this surface */
1679 trace.cluster = *cluster;
1680 VectorCopy( origin, trace.origin );
1681 VectorCopy( normal, trace.normal );
1684 *dirt = DirtForSample( &trace );
1688 /* testing no filtering */
1692 for ( y = 0; y < lm->sh; y++ )
1694 for ( x = 0; x < lm->sw; x++ )
1697 cluster = SUPER_CLUSTER( x, y );
1698 dirt = SUPER_DIRT( x, y );
1700 /* filter dirt by adjacency to unmapped luxels */
1703 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1705 if ( sy < 0 || sy >= lm->sh ) {
1709 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1711 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1715 /* get neighboring luxel */
1716 cluster = SUPER_CLUSTER( sx, sy );
1717 dirt2 = SUPER_DIRT( sx, sy );
1718 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1728 if ( samples <= 0.0f ) {
1734 if ( samples <= 0.0f ) {
1739 *dirt = average / samples;
1748 calculates the pvs cluster, origin, normal of a sub-luxel
1751 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1752 int i, *cluster, *cluster2;
1753 float *origin, *origin2, *normal; //% , *normal2;
1754 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1757 /* calulate x vector */
1758 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1759 cluster = SUPER_CLUSTER( x, y );
1760 origin = SUPER_ORIGIN( x, y );
1761 //% normal = SUPER_NORMAL( x, y );
1762 cluster2 = SUPER_CLUSTER( x + 1, y );
1763 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1764 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1766 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1767 cluster = SUPER_CLUSTER( x - 1, y );
1768 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1769 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1770 cluster2 = SUPER_CLUSTER( x, y );
1771 origin2 = SUPER_ORIGIN( x, y );
1772 //% normal2 = SUPER_NORMAL( x, y );
1775 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1778 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1779 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1781 /* calulate y vector */
1782 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1783 cluster = SUPER_CLUSTER( x, y );
1784 origin = SUPER_ORIGIN( x, y );
1785 //% normal = SUPER_NORMAL( x, y );
1786 cluster2 = SUPER_CLUSTER( x, y + 1 );
1787 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1788 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1790 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1791 cluster = SUPER_CLUSTER( x, y - 1 );
1792 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1793 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1794 cluster2 = SUPER_CLUSTER( x, y );
1795 origin2 = SUPER_ORIGIN( x, y );
1796 //% normal2 = SUPER_NORMAL( x, y );
1799 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1802 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1804 /* calculate new origin */
1805 for ( i = 0; i < 3; i++ )
1806 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1809 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1810 if ( *sampleCluster < 0 ) {
1814 /* calculate new normal */
1815 normal = SUPER_NORMAL( x, y );
1816 VectorCopy( normal, sampleNormal );
1824 SubsampleRawLuxel_r()
1825 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1828 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1829 int b, samples, mapped, lighted;
1832 vec3_t deluxel[ 4 ];
1833 vec3_t origin[ 4 ], normal[ 4 ];
1834 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1835 vec3_t color, direction = { 0, 0, 0 }, total;
1839 if ( lightLuxel[ 3 ] >= lightSamples ) {
1844 VectorClear( total );
1848 /* make 2x2 subsample stamp */
1849 for ( b = 0; b < 4; b++ )
1852 VectorCopy( sampleOrigin, origin[ b ] );
1854 /* calculate position */
1855 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1861 /* increment sample count */
1862 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1865 trace->cluster = *cluster;
1866 VectorCopy( origin[ b ], trace->origin );
1867 VectorCopy( normal[ b ], trace->normal );
1871 LightContributionToSample( trace );
1872 if ( trace->forceSubsampling > 1.0f ) {
1873 /* alphashadow: we subsample as deep as we can */
1879 /* add to totals (fixme: make contrast function) */
1880 VectorCopy( trace->color, luxel[ b ] );
1881 if ( lightDeluxel ) {
1882 VectorCopy( trace->directionContribution, deluxel[ b ] );
1884 VectorAdd( total, trace->color, total );
1885 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1890 /* subsample further? */
1891 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1892 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1893 lighted != 0 && lighted != mapped ) {
1894 for ( b = 0; b < 4; b++ )
1896 if ( cluster[ b ] < 0 ) {
1899 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1904 //% VectorClear( color );
1906 VectorCopy( lightLuxel, color );
1907 if ( lightDeluxel ) {
1908 VectorCopy( lightDeluxel, direction );
1911 for ( b = 0; b < 4; b++ )
1913 if ( cluster[ b ] < 0 ) {
1916 VectorAdd( color, luxel[ b ], color );
1917 if ( lightDeluxel ) {
1918 VectorAdd( direction, deluxel[ b ], direction );
1924 if ( samples > 0 ) {
1926 color[ 0 ] /= samples;
1927 color[ 1 ] /= samples;
1928 color[ 2 ] /= samples;
1931 VectorCopy( color, lightLuxel );
1932 lightLuxel[ 3 ] += 1.0f;
1934 if ( lightDeluxel ) {
1935 direction[ 0 ] /= samples;
1936 direction[ 1 ] /= samples;
1937 direction[ 2 ] /= samples;
1938 VectorCopy( direction, lightDeluxel );
1943 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1944 static void GaussLikeRandom( float sigma, float *x, float *y ){
1946 r = Random() * 2 * Q_PI;
1947 *x = sigma * 2.73861278752581783822 * cos( r );
1948 *y = sigma * 2.73861278752581783822 * sin( r );
1955 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1958 vec3_t origin, normal;
1959 vec3_t total, totaldirection;
1962 VectorClear( total );
1963 VectorClear( totaldirection );
1965 for ( b = 0; b < lightSamples; ++b )
1968 VectorCopy( sampleOrigin, origin );
1969 GaussLikeRandom( bias, &dx, &dy );
1971 /* calculate position */
1972 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1978 trace->cluster = cluster;
1979 VectorCopy( origin, trace->origin );
1980 VectorCopy( normal, trace->normal );
1982 LightContributionToSample( trace );
1983 VectorAdd( total, trace->color, total );
1984 if ( lightDeluxel ) {
1985 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1992 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1993 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1994 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1996 if ( lightDeluxel ) {
1997 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1998 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1999 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
2007 IlluminateRawLightmap()
2008 illuminates the luxels
2011 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
2012 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
2013 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
2015 void IlluminateRawLightmap( int rawLightmapNum ){
2016 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
2017 int *cluster, *cluster2, mapped, lighted, totalLighted;
2018 size_t llSize, ldSize;
2020 surfaceInfo_t *info;
2021 qboolean filterColor, filterDir;
2023 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2024 unsigned char *flag;
2025 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2026 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2027 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2029 float stackLightLuxels[ STACK_LL_SIZE ];
2032 /* bail if this number exceeds the number of raw lightmaps */
2033 if ( rawLightmapNum >= numRawLightmaps ) {
2038 lm = &rawLightmaps[ rawLightmapNum ];
2041 trace.testOcclusion = !noTrace;
2042 trace.forceSunlight = qfalse;
2043 trace.recvShadows = lm->recvShadows;
2044 trace.numSurfaces = lm->numLightSurfaces;
2045 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2046 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2048 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2049 trace.twoSided = qfalse;
2050 for ( i = 0; i < trace.numSurfaces; i++ )
2053 info = &surfaceInfos[ trace.surfaces[ i ] ];
2055 /* check twosidedness */
2056 if ( info->si->twoSided ) {
2057 trace.twoSided = qtrue;
2062 /* create a culled light list for this raw lightmap */
2063 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2065 /* -----------------------------------------------------------------
2067 ----------------------------------------------------------------- */
2070 numLuxelsIlluminated += ( lm->sw * lm->sh );
2072 /* test debugging state */
2073 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2074 /* debug fill the luxels */
2075 for ( y = 0; y < lm->sh; y++ )
2077 for ( x = 0; x < lm->sw; x++ )
2080 cluster = SUPER_CLUSTER( x, y );
2082 /* only fill mapped luxels */
2083 if ( *cluster < 0 ) {
2087 /* get particulars */
2088 luxel = SUPER_LUXEL( 0, x, y );
2089 origin = SUPER_ORIGIN( x, y );
2090 normal = SUPER_NORMAL( x, y );
2092 /* color the luxel with raw lightmap num? */
2093 if ( debugSurfaces ) {
2094 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2097 /* color the luxel with lightmap axis? */
2098 else if ( debugAxis ) {
2099 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2100 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2101 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2104 /* color the luxel with luxel cluster? */
2105 else if ( debugCluster ) {
2106 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2109 /* color the luxel with luxel origin? */
2110 else if ( debugOrigin ) {
2111 VectorSubtract( lm->maxs, lm->mins, temp );
2112 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2113 VectorSubtract( origin, lm->mins, temp2 );
2114 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2115 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2116 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2119 /* color the luxel with the normal */
2120 else if ( normalmap ) {
2121 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2122 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2123 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2126 /* otherwise clear it */
2128 VectorClear( luxel );
2138 /* allocate temporary per-light luxel storage */
2139 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2140 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2141 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2142 lightLuxels = stackLightLuxels;
2145 lightLuxels = safe_malloc( llSize );
2148 lightDeluxels = safe_malloc( ldSize );
2151 lightDeluxels = NULL;
2155 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2157 /* set ambient color */
2158 for ( y = 0; y < lm->sh; y++ )
2160 for ( x = 0; x < lm->sw; x++ )
2163 cluster = SUPER_CLUSTER( x, y );
2164 luxel = SUPER_LUXEL( 0, x, y );
2165 normal = SUPER_NORMAL( x, y );
2166 deluxel = SUPER_DELUXEL( x, y );
2168 /* blacken unmapped clusters */
2169 if ( *cluster < 0 ) {
2170 VectorClear( luxel );
2176 VectorCopy( ambientColor, luxel );
2178 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2180 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2181 if ( brightness < 0.00390625f ) {
2182 brightness = 0.00390625f;
2185 VectorScale( normal, brightness, deluxel );
2192 /* clear styled lightmaps */
2193 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2194 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2196 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2197 memset( lm->superLuxels[ lightmapNum ], 0, size );
2201 /* debugging code */
2202 //% if( trace.numLights <= 0 )
2203 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2205 /* walk light list */
2206 for ( i = 0; i < trace.numLights; i++ )
2209 trace.light = trace.lights[ i ];
2212 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2214 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2215 lm->styles[ lightmapNum ] == LS_NONE ) {
2220 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2221 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2222 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2227 memset( lightLuxels, 0, llSize );
2229 memset( lightDeluxels, 0, ldSize );
2233 /* determine filter radius */
2234 filterRadius = lm->filterRadius > trace.light->filterRadius
2236 : trace.light->filterRadius;
2237 if ( filterRadius < 0.0f ) {
2238 filterRadius = 0.0f;
2241 /* set luxel filter radius */
2242 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2243 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2244 luxelFilterRadius = 1;
2247 /* allocate sampling flags storage */
2248 if ( lightSamples > 1 || lightRandomSamples ) {
2249 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2250 if ( lm->superFlags == NULL ) {
2251 lm->superFlags = safe_malloc( size );
2253 memset( (void *) lm->superFlags, 0, size );
2256 /* initial pass, one sample per luxel */
2257 for ( y = 0; y < lm->sh; y++ )
2259 for ( x = 0; x < lm->sw; x++ )
2262 cluster = SUPER_CLUSTER( x, y );
2263 if ( *cluster < 0 ) {
2267 /* get particulars */
2268 lightLuxel = LIGHT_LUXEL( x, y );
2269 lightDeluxel = LIGHT_DELUXEL( x, y );
2270 origin = SUPER_ORIGIN( x, y );
2271 normal = SUPER_NORMAL( x, y );
2272 flag = SUPER_FLAG( x, y );
2274 /* set contribution count */
2275 lightLuxel[ 3 ] = 1.0f;
2278 trace.cluster = *cluster;
2279 VectorCopy( origin, trace.origin );
2280 VectorCopy( normal, trace.normal );
2282 /* get light for this sample */
2283 LightContributionToSample( &trace );
2284 VectorCopy( trace.color, lightLuxel );
2286 /* add the contribution to the deluxemap */
2288 VectorCopy( trace.directionContribution, lightDeluxel );
2291 /* check for evilness */
2292 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) ) {
2294 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2297 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2303 /* don't even bother with everything else if nothing was lit */
2304 if ( totalLighted == 0 ) {
2308 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2309 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2310 if ( lightSamples > 1 || lightRandomSamples ) {
2312 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2314 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2319 VectorClear( total );
2321 /* test 2x2 stamp */
2322 for ( t = 0; t < 4; t++ )
2324 /* set sample coords */
2325 sx = x + tests[ t ][ 0 ];
2326 sy = y + tests[ t ][ 1 ];
2329 cluster = SUPER_CLUSTER( sx, sy );
2330 if ( *cluster < 0 ) {
2336 flag = SUPER_FLAG( sx, sy );
2337 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2338 /* force a lighted/mapped discrepancy so we subsample */
2343 lightLuxel = LIGHT_LUXEL( sx, sy );
2344 VectorAdd( total, lightLuxel, total );
2345 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2350 /* if total color is under a certain amount, then don't bother subsampling */
2351 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2355 /* if all 4 pixels are either in shadow or light, then don't subsample */
2356 if ( lighted != 0 && lighted != mapped ) {
2357 for ( t = 0; t < 4; t++ )
2359 /* set sample coords */
2360 sx = x + tests[ t ][ 0 ];
2361 sy = y + tests[ t ][ 1 ];
2364 cluster = SUPER_CLUSTER( sx, sy );
2365 if ( *cluster < 0 ) {
2368 flag = SUPER_FLAG( sx, sy );
2369 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2372 lightLuxel = LIGHT_LUXEL( sx, sy );
2373 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2374 origin = SUPER_ORIGIN( sx, sy );
2376 /* only subsample shadowed luxels */
2377 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2381 if ( lightRandomSamples ) {
2382 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2385 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2388 *flag |= FLAG_ALREADY_SUBSAMPLED;
2390 /* debug code to colorize subsampled areas to yellow */
2391 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2392 //% VectorSet( luxel, 255, 204, 0 );
2399 /* tertiary pass, apply dirt map (ambient occlusion) */
2402 for ( y = 0; y < lm->sh; y++ )
2404 for ( x = 0; x < lm->sw; x++ )
2407 cluster = SUPER_CLUSTER( x, y );
2408 if ( *cluster < 0 ) {
2412 /* get particulars */
2413 lightLuxel = LIGHT_LUXEL( x, y );
2414 dirt = SUPER_DIRT( x, y );
2416 /* scale light value */
2417 VectorScale( lightLuxel, *dirt, lightLuxel );
2422 /* allocate sampling lightmap storage */
2423 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2424 /* allocate sampling lightmap storage */
2425 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2426 lm->superLuxels[ lightmapNum ] = safe_malloc0( size );
2430 if ( lightmapNum > 0 ) {
2431 lm->styles[ lightmapNum ] = trace.light->style;
2432 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2435 /* copy to permanent luxels */
2436 for ( y = 0; y < lm->sh; y++ )
2438 for ( x = 0; x < lm->sw; x++ )
2440 /* get cluster and origin */
2441 cluster = SUPER_CLUSTER( x, y );
2442 if ( *cluster < 0 ) {
2445 origin = SUPER_ORIGIN( x, y );
2448 if ( luxelFilterRadius ) {
2450 VectorClear( averageColor );
2451 VectorClear( averageDir );
2454 /* cheaper distance-based filtering */
2455 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2457 if ( sy < 0 || sy >= lm->sh ) {
2461 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2463 if ( sx < 0 || sx >= lm->sw ) {
2467 /* get particulars */
2468 cluster = SUPER_CLUSTER( sx, sy );
2469 if ( *cluster < 0 ) {
2472 lightLuxel = LIGHT_LUXEL( sx, sy );
2473 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2476 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2477 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2479 /* scale luxel by filter weight */
2480 VectorScale( lightLuxel, weight, color );
2481 VectorAdd( averageColor, color, averageColor );
2483 VectorScale( lightDeluxel, weight, direction );
2484 VectorAdd( averageDir, direction, averageDir );
2491 if ( samples <= 0.0f ) {
2495 /* scale into luxel */
2496 luxel = SUPER_LUXEL( lightmapNum, x, y );
2499 /* handle negative light */
2500 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2501 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2502 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2503 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2506 /* handle normal light */
2509 luxel[ 0 ] += averageColor[ 0 ] / samples;
2510 luxel[ 1 ] += averageColor[ 1 ] / samples;
2511 luxel[ 2 ] += averageColor[ 2 ] / samples;
2515 /* scale into luxel */
2516 deluxel = SUPER_DELUXEL( x, y );
2517 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2518 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2519 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2526 /* get particulars */
2527 lightLuxel = LIGHT_LUXEL( x, y );
2528 lightDeluxel = LIGHT_DELUXEL( x, y );
2529 luxel = SUPER_LUXEL( lightmapNum, x, y );
2530 deluxel = SUPER_DELUXEL( x, y );
2532 /* handle negative light */
2533 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2534 VectorScale( averageColor, -1.0f, averageColor );
2540 /* handle negative light */
2541 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2542 VectorSubtract( luxel, lightLuxel, luxel );
2545 /* handle normal light */
2547 VectorAdd( luxel, lightLuxel, luxel );
2551 VectorAdd( deluxel, lightDeluxel, deluxel );
2558 /* free temporary luxels */
2559 if ( lightLuxels != stackLightLuxels ) {
2560 free( lightLuxels );
2564 free( lightDeluxels );
2568 /* free light list */
2569 FreeTraceLights( &trace );
2571 /* floodlight pass */
2572 if ( floodlighty ) {
2573 FloodlightIlluminateLightmap( lm );
2576 if ( debugnormals ) {
2577 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2580 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2584 for ( y = 0; y < lm->sh; y++ )
2586 for ( x = 0; x < lm->sw; x++ )
2589 cluster = SUPER_CLUSTER( x, y );
2590 //% if( *cluster < 0 )
2593 /* get particulars */
2594 luxel = SUPER_LUXEL( lightmapNum, x, y );
2595 normal = SUPER_NORMAL( x, y );
2597 luxel[0] = ( normal[0] * 127 ) + 127;
2598 luxel[1] = ( normal[1] * 127 ) + 127;
2599 luxel[2] = ( normal[2] * 127 ) + 127;
2605 /* -----------------------------------------------------------------
2607 ----------------------------------------------------------------- */
2610 /* walk lightmaps */
2611 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2614 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2618 /* apply dirt to each luxel */
2619 for ( y = 0; y < lm->sh; y++ )
2621 for ( x = 0; x < lm->sw; x++ )
2624 cluster = SUPER_CLUSTER( x, y );
2626 /* get particulars */
2627 luxel = SUPER_LUXEL( lightmapNum, x, y );
2628 dirt = SUPER_DIRT( x, y );
2631 VectorScale( luxel, *dirt, luxel );
2635 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2642 /* -----------------------------------------------------------------
2644 ----------------------------------------------------------------- */
2646 /* walk lightmaps */
2647 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2650 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2654 /* average occluded luxels from neighbors */
2655 for ( y = 0; y < lm->sh; y++ )
2657 for ( x = 0; x < lm->sw; x++ )
2659 /* get particulars */
2660 cluster = SUPER_CLUSTER( x, y );
2661 luxel = SUPER_LUXEL( lightmapNum, x, y );
2662 deluxel = SUPER_DELUXEL( x, y );
2663 normal = SUPER_NORMAL( x, y );
2665 /* determine if filtering is necessary */
2666 filterColor = qfalse;
2668 if ( *cluster < 0 ||
2669 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2670 filterColor = qtrue;
2673 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2677 if ( !filterColor && !filterDir ) {
2681 /* choose seed amount */
2682 VectorClear( averageColor );
2683 VectorClear( averageDir );
2686 /* walk 3x3 matrix */
2687 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2689 if ( sy < 0 || sy >= lm->sh ) {
2693 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2695 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2699 /* get neighbor's particulars */
2700 cluster2 = SUPER_CLUSTER( sx, sy );
2701 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2702 deluxel2 = SUPER_DELUXEL( sx, sy );
2704 /* ignore unmapped/unlit luxels */
2705 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2706 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2710 /* add its distinctiveness to our own */
2711 VectorAdd( averageColor, luxel2, averageColor );
2712 samples += luxel2[ 3 ];
2714 VectorAdd( averageDir, deluxel2, averageDir );
2720 if ( samples <= 0.0f ) {
2724 /* dark lightmap seams */
2726 if ( lightmapNum == 0 ) {
2727 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2733 if ( filterColor ) {
2734 VectorDivide( averageColor, samples, luxel );
2738 VectorDivide( averageDir, samples, deluxel );
2741 /* set cluster to -3 */
2742 if ( *cluster < 0 ) {
2743 *cluster = CLUSTER_FLOODED;
2753 IlluminateVertexes()
2754 light the surface vertexes
2757 #define VERTEX_NUDGE 4.0f
2759 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 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 ) {
2785 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2786 trace.forceSunlight = info->si->forceSunlight;
2787 trace.recvShadows = info->recvShadows;
2788 trace.numSurfaces = 1;
2789 trace.surfaces = #
2790 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2792 /* twosided lighting */
2793 trace.twoSided = info->si->twoSided;
2795 /* make light list for this surface */
2796 CreateTraceLightsForSurface( num, &trace );
2799 verts = yDrawVerts + ds->firstVert;
2801 memset( avgColors, 0, sizeof( avgColors ) );
2803 /* walk the surface verts */
2804 for ( i = 0; i < ds->numVerts; i++ )
2806 /* get vertex luxel */
2807 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2809 /* color the luxel with raw lightmap num? */
2810 if ( debugSurfaces ) {
2811 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2814 /* color the luxel with luxel origin? */
2815 else if ( debugOrigin ) {
2816 VectorSubtract( info->maxs, info->mins, temp );
2817 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2818 VectorSubtract( verts[ i ].xyz, info->mins, temp2 );
2819 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2820 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2821 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2824 /* color the luxel with the normal */
2825 else if ( normalmap ) {
2826 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2827 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2828 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2831 else if ( info->si->noVertexLight ) {
2832 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
2835 else if ( noVertexLighting > 0 ) {
2836 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
2839 /* illuminate the vertex */
2842 /* clear vertex luxel */
2843 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2845 /* try at initial origin */
2846 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2847 if ( trace.cluster >= 0 ) {
2849 VectorCopy( verts[ i ].xyz, trace.origin );
2850 VectorCopy( verts[ i ].normal, trace.normal );
2853 if ( dirty && !bouncing ) {
2854 dirt = DirtForSample( &trace );
2860 /* jal: floodlight */
2861 floodLightAmount = 0.0f;
2862 VectorClear( floodColor );
2863 if ( floodlighty && !bouncing ) {
2864 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2865 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2869 LightingAtSample( &trace, ds->vertexStyles, colors );
2872 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2875 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2877 /* jal: floodlight */
2878 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2881 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2882 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2883 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2887 /* is this sample bright enough? */
2888 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2889 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2890 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2891 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2892 /* nudge the sample point around a bit */
2893 for ( x = 0; x < 5; x++ )
2895 /* two's complement 0, 1, -1, 2, -2, etc */
2896 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2898 for ( y = 0; y < 5; y++ )
2900 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2902 for ( z = 0; z < 5; z++ )
2904 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2907 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2908 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2909 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2911 /* try at nudged origin */
2912 trace.cluster = ClusterForPointExtFilter( trace.origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2913 if ( trace.cluster < 0 ) {
2918 if ( dirty && !bouncing ) {
2919 dirt = DirtForSample( &trace );
2925 /* jal: floodlight */
2926 floodLightAmount = 0.0f;
2927 VectorClear( floodColor );
2928 if ( floodlighty && !bouncing ) {
2929 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2930 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2934 LightingAtSample( &trace, ds->vertexStyles, colors );
2937 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2940 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2942 /* jal: floodlight */
2943 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2946 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2947 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2950 /* bright enough? */
2951 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2952 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2953 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2954 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2962 /* add to average? */
2963 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2964 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2965 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2966 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2968 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2970 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2971 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2976 /* another happy customer */
2977 numVertsIlluminated++;
2980 /* set average color */
2982 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2983 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2987 VectorCopy( ambientColor, avgColors[ 0 ] );
2990 /* clean up and store vertex color */
2991 for ( i = 0; i < ds->numVerts; i++ )
2993 /* get vertex luxel */
2994 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2996 /* store average in occluded vertexes */
2997 if ( radVertLuxel[ 0 ] < 0.0f ) {
2998 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3000 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3001 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
3004 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3009 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3012 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3013 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3016 if ( bouncing || bounce == 0 || !bounceOnly ) {
3017 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3019 if ( !info->si->noVertexLight ) {
3020 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3025 /* free light list */
3026 FreeTraceLights( &trace );
3028 /* return to sender */
3032 /* -----------------------------------------------------------------
3033 reconstitute vertex lighting from the luxels
3034 ----------------------------------------------------------------- */
3036 /* set styles from lightmap */
3037 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3038 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3040 /* get max search radius */
3042 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3044 /* walk the surface verts */
3045 verts = yDrawVerts + ds->firstVert;
3046 for ( i = 0; i < ds->numVerts; i++ )
3048 /* do each lightmap */
3049 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3052 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3056 /* get luxel coords */
3057 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3058 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3062 else if ( x >= lm->sw ) {
3068 else if ( y >= lm->sh ) {
3072 /* get vertex luxels */
3073 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3074 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3076 /* color the luxel with the normal? */
3078 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3079 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3080 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3083 /* color the luxel with surface num? */
3084 else if ( debugSurfaces ) {
3085 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3088 else if ( info->si->noVertexLight ) {
3089 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
3092 else if ( noVertexLighting > 0 ) {
3093 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
3096 /* divine color from the superluxels */
3099 /* increasing radius */
3100 VectorClear( radVertLuxel );
3102 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3104 /* sample within radius */
3105 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3107 if ( sy < 0 || sy >= lm->sh ) {
3111 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3113 if ( sx < 0 || sx >= lm->sw ) {
3117 /* get luxel particulars */
3118 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3119 cluster = SUPER_CLUSTER( sx, sy );
3120 if ( *cluster < 0 ) {
3124 /* testing: must be brigher than ambient color */
3125 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3128 /* add its distinctiveness to our own */
3129 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3130 samples += luxel[ 3 ];
3136 if ( samples > 0.0f ) {
3137 VectorDivide( radVertLuxel, samples, radVertLuxel );
3140 VectorCopy( ambientColor, radVertLuxel );
3144 /* store into floating point storage */
3145 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3146 numVertsIlluminated++;
3148 /* store into bytes (for vertex approximation) */
3149 if ( !info->si->noVertexLight ) {
3150 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3158 /* -------------------------------------------------------------------------------
3160 light optimization (-fast)
3162 creates a list of lights that will affect a surface and stores it in tw
3163 this is to optimize surface lighting by culling out as many of the
3164 lights in the world as possible from further calculation
3166 ------------------------------------------------------------------------------- */
3170 determines opaque brushes in the world and find sky shaders for sunlight calculations
3173 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3175 unsigned int compileFlags, allCompileFlags;
3177 bspBrushSide_t *side;
3178 bspShader_t *shader;
3183 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3186 if ( opaqueBrushes == NULL ) {
3187 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3191 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3192 numOpaqueBrushes = 0;
3194 /* walk the list of worldspawn brushes */
3195 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3198 b = bspModels[ 0 ].firstBSPBrush + i;
3199 brush = &bspBrushes[ b ];
3201 /* check all sides */
3203 allCompileFlags = ~( 0u );
3204 for ( j = 0; j < brush->numSides; j++ )
3206 /* do bsp shader calculations */
3207 side = &bspBrushSides[ brush->firstSide + j ];
3208 shader = &bspShaders[ side->shaderNum ];
3210 /* get shader info */
3211 si = ShaderInfoForShaderNull( shader->shader );
3216 /* or together compile flags */
3217 compileFlags |= si->compileFlags;
3218 allCompileFlags &= si->compileFlags;
3221 /* determine if this brush is opaque to light */
3222 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3223 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3229 /* emit some statistics */
3230 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3232 void SetupBrushes( void ){
3233 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3240 determines if two clusters are visible to each other using the PVS
3243 qboolean ClusterVisible( int a, int b ){
3249 if ( a < 0 || b < 0 ) {
3259 if ( numBSPVisBytes <= 8 ) {
3264 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3265 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3266 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3269 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3279 borrowed from vlight.c
3282 int PointInLeafNum_r( vec3_t point, int nodenum ){
3289 while ( nodenum >= 0 )
3291 node = &bspNodes[ nodenum ];
3292 plane = &bspPlanes[ node->planeNum ];
3293 dist = DotProduct( point, plane->normal ) - plane->dist;
3295 nodenum = node->children[ 0 ];
3297 else if ( dist < -0.1 ) {
3298 nodenum = node->children[ 1 ];
3302 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3303 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3306 nodenum = node->children[ 1 ];
3310 leafnum = -nodenum - 1;
3318 borrowed from vlight.c
3321 int PointInLeafNum( vec3_t point ){
3322 return PointInLeafNum_r( point, 0 );
3328 ClusterVisibleToPoint() - ydnar
3329 returns qtrue if point can "see" cluster
3332 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3336 /* get leafNum for point */
3337 pointCluster = ClusterForPoint( point );
3338 if ( pointCluster < 0 ) {
3343 return ClusterVisible( pointCluster, cluster );
3349 ClusterForPoint() - ydnar
3350 returns the pvs cluster for point
3353 int ClusterForPoint( vec3_t point ){
3357 /* get leafNum for point */
3358 leafNum = PointInLeafNum( point );
3359 if ( leafNum < 0 ) {
3363 /* return the cluster */
3364 return bspLeafs[ leafNum ].cluster;
3370 ClusterForPointExt() - ydnar
3371 also takes brushes into account for occlusion testing
3374 int ClusterForPointExt( vec3_t point, float epsilon ){
3375 int i, j, b, leafNum, cluster;
3378 int *brushes, numBSPBrushes;
3384 /* get leaf for point */
3385 leafNum = PointInLeafNum( point );
3386 if ( leafNum < 0 ) {
3389 leaf = &bspLeafs[ leafNum ];
3391 /* get the cluster */
3392 cluster = leaf->cluster;
3393 if ( cluster < 0 ) {
3397 /* transparent leaf, so check point against all brushes in the leaf */
3398 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3399 numBSPBrushes = leaf->numBSPLeafBrushes;
3400 for ( i = 0; i < numBSPBrushes; i++ )
3404 if ( b > maxOpaqueBrush ) {
3407 brush = &bspBrushes[ b ];
3408 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3412 /* check point against all planes */
3414 for ( j = 0; j < brush->numSides && inside; j++ )
3416 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3417 dot = DotProduct( point, plane->normal );
3419 if ( dot > epsilon ) {
3424 /* if inside, return bogus cluster */
3430 /* if the point made it this far, it's not inside any opaque brushes */
3437 ClusterForPointExtFilter() - ydnar
3438 adds cluster checking against a list of known valid clusters
3441 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3445 /* get cluster for point */
3446 cluster = ClusterForPointExt( point, epsilon );
3448 /* check if filtering is necessary */
3449 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3454 for ( i = 0; i < numClusters; i++ )
3456 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3468 ShaderForPointInLeaf() - ydnar
3469 checks a point against all brushes in a leaf, returning the shader of the brush
3470 also sets the cumulative surface and content flags for the brush hit
3473 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3477 int *brushes, numBSPBrushes;
3480 bspBrushSide_t *side;
3482 bspShader_t *shader;
3483 int allSurfaceFlags, allContentFlags;
3486 /* clear things out first */
3491 if ( leafNum < 0 ) {
3494 leaf = &bspLeafs[ leafNum ];
3496 /* transparent leaf, so check point against all brushes in the leaf */
3497 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3498 numBSPBrushes = leaf->numBSPLeafBrushes;
3499 for ( i = 0; i < numBSPBrushes; i++ )
3502 brush = &bspBrushes[ brushes[ i ] ];
3504 /* check point against all planes */
3506 allSurfaceFlags = 0;
3507 allContentFlags = 0;
3508 for ( j = 0; j < brush->numSides && inside; j++ )
3510 side = &bspBrushSides[ brush->firstSide + j ];
3511 plane = &bspPlanes[ side->planeNum ];
3512 dot = DotProduct( point, plane->normal );
3514 if ( dot > epsilon ) {
3519 shader = &bspShaders[ side->shaderNum ];
3520 allSurfaceFlags |= shader->surfaceFlags;
3521 allContentFlags |= shader->contentFlags;
3525 /* handle if inside */
3527 /* if there are desired flags, check for same and continue if they aren't matched */
3528 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3531 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3535 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3536 *surfaceFlags = allSurfaceFlags;
3537 *contentFlags = allContentFlags;
3538 return brush->shaderNum;
3542 /* if the point made it this far, it's not inside any brushes */
3550 chops a bounding box by the plane defined by origin and normal
3551 returns qfalse if the bounds is entirely clipped away
3553 this is not exactly the fastest way to do this...
3556 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3557 /* FIXME: rewrite this so it doesn't use bloody brushes */
3565 calculates each light's effective envelope,
3566 taking into account brightness, type, and pvs.
3569 #define LIGHT_EPSILON 0.125f
3570 #define LIGHT_NUDGE 2.0f
3572 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3573 int i, x, y, z, x1, y1, z1;
3574 light_t *light, *light2, **owner;
3576 vec3_t origin, dir, mins, maxs;
3577 float radius, intensity;
3578 light_t *buckets[ 256 ];
3581 /* early out for weird cases where there are no lights */
3582 if ( lights == NULL ) {
3587 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3591 numCulledLights = 0;
3593 while ( *owner != NULL )
3598 /* handle negative lights */
3599 if ( light->photons < 0.0f || light->add < 0.0f ) {
3600 light->photons *= -1.0f;
3601 light->add *= -1.0f;
3602 light->flags |= LIGHT_NEGATIVE;
3606 if ( light->type == EMIT_SUN ) {
3609 light->envelope = MAX_WORLD_COORD * 8.0f;
3610 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3611 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3614 /* everything else */
3617 /* get pvs cluster for light */
3618 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3620 /* invalid cluster? */
3621 if ( light->cluster < 0 ) {
3622 /* nudge the sample point around a bit */
3623 for ( x = 0; x < 4; x++ )
3625 /* two's complement 0, 1, -1, 2, -2, etc */
3626 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3628 for ( y = 0; y < 4; y++ )
3630 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3632 for ( z = 0; z < 4; z++ )
3634 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3637 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3638 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3639 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3641 /* try at nudged origin */
3642 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3643 if ( light->cluster < 0 ) {
3648 VectorCopy( origin, light->origin );
3654 /* only calculate for lights in pvs and outside of opaque brushes */
3655 if ( light->cluster >= 0 ) {
3656 /* set light fast flag */
3658 light->flags |= LIGHT_FAST_TEMP;
3661 light->flags &= ~LIGHT_FAST_TEMP;
3663 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3664 light->flags |= LIGHT_FAST_TEMP;
3666 if ( light->si && light->si->noFast ) {
3667 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3670 /* clear light envelope */
3671 light->envelope = 0;
3673 /* handle area lights */
3674 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3675 light->envelope = MAX_WORLD_COORD * 8.0f;
3677 /* check for fast mode */
3678 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3679 /* ugly hack to calculate extent for area lights, but only done once */
3680 VectorScale( light->normal, -1.0f, dir );
3681 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3685 VectorMA( light->origin, radius, light->normal, origin );
3686 factor = PointToPolygonFormFactor( origin, dir, light->w );
3687 if ( factor < 0.0f ) {
3690 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3691 light->envelope = radius;
3697 intensity = light->photons; /* hopefully not used */
3702 intensity = light->photons;
3706 if ( light->envelope <= 0.0f ) {
3707 /* solve distance for non-distance lights */
3708 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3709 light->envelope = MAX_WORLD_COORD * 8.0f;
3712 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3713 /* solve distance for linear lights */
3714 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3715 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3719 add = angle * light->photons * linearScale - (dist * light->fade);
3720 T = (light->photons * linearScale) - (dist * light->fade);
3721 T + (dist * light->fade) = (light->photons * linearScale);
3722 dist * light->fade = (light->photons * linearScale) - T;
3723 dist = ((light->photons * linearScale) - T) / light->fade;
3726 /* solve for inverse square falloff */
3728 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3732 add = light->photons / (dist * dist);
3733 T = light->photons / (dist * dist);
3734 T * (dist * dist) = light->photons;
3735 dist = sqrt( light->photons / T );
3740 /* solve distance for linear lights */
3741 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3742 light->envelope = ( intensity * linearScale ) / light->fade;
3745 /* can't cull these */
3747 light->envelope = MAX_WORLD_COORD * 8.0f;
3752 /* chop radius against pvs */
3755 ClearBounds( mins, maxs );
3757 /* check all leaves */
3758 for ( i = 0; i < numBSPLeafs; i++ )
3761 leaf = &bspLeafs[ i ];
3764 if ( leaf->cluster < 0 ) {
3767 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3771 /* add this leafs bbox to the bounds */
3772 VectorCopy( leaf->mins, origin );
3773 AddPointToBounds( origin, mins, maxs );
3774 VectorCopy( leaf->maxs, origin );
3775 AddPointToBounds( origin, mins, maxs );
3778 /* test to see if bounds encompass light */
3779 for ( i = 0; i < 3; i++ )
3781 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3782 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3783 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3784 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3785 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3786 AddPointToBounds( light->origin, mins, maxs );
3790 /* chop the bounds by a plane for area lights and spotlights */
3791 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3792 ChopBounds( mins, maxs, light->origin, light->normal );
3796 VectorCopy( mins, light->mins );
3797 VectorCopy( maxs, light->maxs );
3799 /* reflect bounds around light origin */
3800 //% VectorMA( light->origin, -1.0f, origin, origin );
3801 VectorScale( light->origin, 2, origin );
3802 VectorSubtract( origin, maxs, origin );
3803 AddPointToBounds( origin, mins, maxs );
3804 //% VectorMA( light->origin, -1.0f, mins, origin );
3805 VectorScale( light->origin, 2, origin );
3806 VectorSubtract( origin, mins, origin );
3807 AddPointToBounds( origin, mins, maxs );
3809 /* calculate spherical bounds */
3810 VectorSubtract( maxs, light->origin, dir );
3811 radius = (float) VectorLength( dir );
3813 /* if this radius is smaller than the envelope, then set the envelope to it */
3814 if ( radius < light->envelope ) {
3815 light->envelope = radius;
3816 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3819 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3822 /* add grid/surface only check */
3824 if ( !( light->flags & LIGHT_GRID ) ) {
3825 light->envelope = 0.0f;
3830 if ( !( light->flags & LIGHT_SURFACES ) ) {
3831 light->envelope = 0.0f;
3837 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3839 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3841 /* delete the light */
3843 *owner = light->next;
3844 if ( light->w != NULL ) {
3852 /* square envelope */
3853 light->envelope2 = ( light->envelope * light->envelope );
3855 /* increment light count */
3858 /* set next light */
3859 owner = &( ( **owner ).next );
3862 /* bucket sort lights by style */
3863 memset( buckets, 0, sizeof( buckets ) );
3865 for ( light = lights; light != NULL; light = light2 )
3867 /* get next light */
3868 light2 = light->next;
3870 /* filter into correct bucket */
3871 light->next = buckets[ light->style ];
3872 buckets[ light->style ] = light;
3874 /* if any styled light is present, automatically set nocollapse */
3875 if ( light->style != LS_NORMAL ) {
3880 /* filter back into light list */
3882 for ( i = 255; i >= 0; i-- )
3885 for ( light = buckets[ i ]; light != NULL; light = light2 )
3887 light2 = light->next;
3888 light->next = lights;
3893 /* emit some statistics */
3894 Sys_Printf( "%9d total lights\n", numLights );
3895 Sys_Printf( "%9d culled lights\n", numCulledLights );
3901 CreateTraceLightsForBounds()
3902 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3905 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3908 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3909 float radius, dist, length;
3912 /* potential pre-setup */
3913 if ( numLights == 0 ) {
3914 SetupEnvelopes( qfalse, fast );
3918 //% 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 ] );
3920 /* allocate the light list */
3921 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3922 trace->numLights = 0;
3924 /* calculate spherical bounds */
3925 VectorAdd( mins, maxs, origin );
3926 VectorScale( origin, 0.5f, origin );
3927 VectorSubtract( maxs, origin, dir );
3928 radius = (float) VectorLength( dir );
3930 /* get length of normal vector */
3931 if ( normal != NULL ) {
3932 length = VectorLength( normal );
3936 normal = nullVector;
3940 /* test each light and see if it reaches the sphere */
3941 /* note: the attenuation code MUST match LightingAtSample() */
3942 for ( light = lights; light; light = light->next )
3944 /* check zero sized envelope */
3945 if ( light->envelope <= 0 ) {
3946 lightsEnvelopeCulled++;
3951 if ( !( light->flags & flags ) ) {
3955 /* sunlight skips all this nonsense */
3956 if ( light->type != EMIT_SUN ) {
3962 /* check against pvs cluster */
3963 if ( numClusters > 0 && clusters != NULL ) {
3964 for ( i = 0; i < numClusters; i++ )
3966 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3972 if ( i == numClusters ) {
3973 lightsClusterCulled++;
3978 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3979 VectorSubtract( light->origin, origin, dir );
3980 dist = VectorLength( dir );
3981 dist -= light->envelope;
3984 lightsEnvelopeCulled++;
3988 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3991 for ( i = 0; i < 3; i++ )
3993 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3998 lightsBoundsCulled++;
4004 /* planar surfaces (except twosided surfaces) have a couple more checks */
4005 if ( length > 0.0f && trace->twoSided == qfalse ) {
4006 /* lights coplanar with a surface won't light it */
4007 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
4008 lightsPlaneCulled++;
4012 /* check to see if light is behind the plane */
4013 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4014 lightsPlaneCulled++;
4019 /* add this light */
4020 trace->lights[ trace->numLights++ ] = light;
4023 /* make last night null */
4024 trace->lights[ trace->numLights ] = NULL;
4029 void FreeTraceLights( trace_t *trace ){
4030 if ( trace->lights != NULL ) {
4031 free( trace->lights );
4038 CreateTraceLightsForSurface()
4039 creates a list of lights that can potentially affect a drawsurface
4042 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4044 vec3_t mins, maxs, normal;
4046 bspDrawSurface_t *ds;
4047 surfaceInfo_t *info;
4055 /* get drawsurface and info */
4056 ds = &bspDrawSurfaces[ num ];
4057 info = &surfaceInfos[ num ];
4059 /* get the mins/maxs for the dsurf */
4060 ClearBounds( mins, maxs );
4061 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4062 for ( i = 0; i < ds->numVerts; i++ )
4064 dv = &yDrawVerts[ ds->firstVert + i ];
4065 AddPointToBounds( dv->xyz, mins, maxs );
4066 if ( !VectorCompare( dv->normal, normal ) ) {
4067 VectorClear( normal );
4071 /* create the lights for the bounding box */
4072 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4075 /////////////////////////////////////////////////////////////
4077 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4078 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4079 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4080 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4082 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4083 static int numFloodVectors = 0;
4085 void SetupFloodLight( void ){
4087 float angle, elevation, angleStep, elevationStep;
4089 double v1,v2,v3,v4,v5,v6;
4092 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4094 /* calculate angular steps */
4095 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4096 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4100 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4102 /* iterate elevation */
4103 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4105 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4106 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4107 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4112 /* emit some statistics */
4113 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4116 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4118 if ( value[ 0 ] != '\0' ) {
4120 v4 = floodlightDistance;
4121 v5 = floodlightIntensity;
4122 v6 = floodlightDirectionScale;
4124 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4126 floodlightRGB[0] = v1;
4127 floodlightRGB[1] = v2;
4128 floodlightRGB[2] = v3;
4130 if ( VectorLength( floodlightRGB ) == 0 ) {
4131 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4144 floodlightDistance = v4;
4145 floodlightIntensity = v5;
4146 floodlightDirectionScale = v6;
4148 floodlighty = qtrue;
4149 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4153 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4156 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4157 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4158 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4160 ColorNormalize( floodlightRGB,floodlightRGB );
4164 FloodLightForSample()
4165 calculates floodlight value for a given sample
4166 once again, kudos to the dirtmapping coder
4169 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4174 float gatherLight, outLight;
4175 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4183 if ( trace == NULL || trace->cluster < 0 ) {
4189 dd = floodLightDistance;
4190 VectorCopy( trace->normal, normal );
4192 /* check if the normal is aligned to the world-up */
4193 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4194 if ( normal[ 2 ] == 1.0f ) {
4195 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4196 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4198 else if ( normal[ 2 ] == -1.0f ) {
4199 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4200 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4205 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4206 CrossProduct( normal, worldUp, myRt );
4207 VectorNormalize( myRt, myRt );
4208 CrossProduct( myRt, normal, myUp );
4209 VectorNormalize( myUp, myUp );
4212 /* vortex: optimise floodLightLowQuality a bit */
4213 if ( floodLightLowQuality == qtrue ) {
4214 /* iterate through ordered vectors */
4215 for ( i = 0; i < numFloodVectors; i++ )
4216 if ( rand() % 10 != 0 ) {
4222 /* iterate through ordered vectors */
4223 for ( i = 0; i < numFloodVectors; i++ )
4227 /* transform vector into tangent space */
4228 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4229 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4230 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4233 VectorMA( trace->origin, dd, direction, trace->end );
4235 //VectorMA( trace->origin, 1, direction, trace->origin );
4237 SetupTrace( trace );
4238 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4243 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4244 contribution = 1.0f;
4246 else if ( trace->opaque ) {
4247 VectorSubtract( trace->hit, trace->origin, displacement );
4248 d = VectorLength( displacement );
4250 // d=trace->distance;
4251 //if (d>256) gatherDirt+=1;
4252 contribution = d / dd;
4253 if ( contribution > 1 ) {
4254 contribution = 1.0f;
4257 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4260 gatherLight += contribution;
4265 if ( gatherLight <= 0.0f ) {
4274 gatherLight /= ( sub );
4276 outLight = gatherLight;
4277 if ( outLight > 1.0f ) {
4281 /* return to sender */
4286 FloodLightRawLightmap
4287 lighttracer style ambient occlusion light hack.
4288 Kudos to the dirtmapping author for most of this source.
4289 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4290 VorteX: fixed problems with deluxemapping
4293 // floodlight pass on a lightmap
4294 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4295 int i, x, y, *cluster;
4296 float *origin, *normal, *floodlight, floodLightAmount;
4297 surfaceInfo_t *info;
4300 // float samples, average, *floodlight2;
4302 memset( &trace,0,sizeof( trace_t ) );
4305 trace.testOcclusion = qtrue;
4306 trace.forceSunlight = qfalse;
4307 trace.twoSided = qtrue;
4308 trace.recvShadows = lm->recvShadows;
4309 trace.numSurfaces = lm->numLightSurfaces;
4310 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4311 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4312 trace.testAll = qfalse;
4313 trace.distance = 1024;
4315 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4316 //trace.twoSided = qfalse;
4317 for ( i = 0; i < trace.numSurfaces; i++ )
4320 info = &surfaceInfos[ trace.surfaces[ i ] ];
4322 /* check twosidedness */
4323 if ( info->si->twoSided ) {
4324 trace.twoSided = qtrue;
4329 /* gather floodlight */
4330 for ( y = 0; y < lm->sh; y++ )
4332 for ( x = 0; x < lm->sw; x++ )
4335 cluster = SUPER_CLUSTER( x, y );
4336 origin = SUPER_ORIGIN( x, y );
4337 normal = SUPER_NORMAL( x, y );
4338 floodlight = SUPER_FLOODLIGHT( x, y );
4340 /* set default dirt */
4343 /* only look at mapped luxels */
4344 if ( *cluster < 0 ) {
4349 trace.cluster = *cluster;
4350 VectorCopy( origin, trace.origin );
4351 VectorCopy( normal, trace.normal );
4353 /* get floodlight */
4354 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4356 /* add floodlight */
4357 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4358 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4359 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4360 floodlight[3] += floodlightDirectionScale;
4364 /* testing no filtering */
4370 for ( y = 0; y < lm->sh; y++ )
4372 for ( x = 0; x < lm->sw; x++ )
4375 cluster = SUPER_CLUSTER( x, y );
4376 floodlight = SUPER_FLOODLIGHT( x, y );
4378 /* filter dirt by adjacency to unmapped luxels */
4379 average = *floodlight;
4381 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4383 if ( sy < 0 || sy >= lm->sh ) {
4387 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4389 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4393 /* get neighboring luxel */
4394 cluster = SUPER_CLUSTER( sx, sy );
4395 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4396 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4401 average += *floodlight2;
4406 if ( samples <= 0.0f ) {
4412 if ( samples <= 0.0f ) {
4417 *floodlight = average / samples;
4423 void FloodLightRawLightmap( int rawLightmapNum ){
4426 /* bail if this number exceeds the number of raw lightmaps */
4427 if ( rawLightmapNum >= numRawLightmaps ) {
4431 lm = &rawLightmaps[ rawLightmapNum ];
4434 if ( floodlighty && floodlightIntensity ) {
4435 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4439 if ( lm->floodlightIntensity ) {
4440 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4441 numSurfacesFloodlighten += 1;
4445 void FloodlightRawLightmaps(){
4446 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4447 numSurfacesFloodlighten = 0;
4448 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4449 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4453 FloodLightIlluminate()
4454 illuminate floodlight into lightmap luxels
4457 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4458 float *luxel, *floodlight, *deluxel, *normal;
4461 int x, y, lightmapNum;
4463 /* walk lightmaps */
4464 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4467 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4471 if( lm->styles[lightmapNum] != LS_NORMAL && lm->styles[lightmapNum] != LS_NONE ) // isStyleLight
4474 /* apply floodlight to each luxel */
4475 for ( y = 0; y < lm->sh; y++ )
4477 for ( x = 0; x < lm->sw; x++ )
4479 /* get floodlight */
4480 floodlight = SUPER_FLOODLIGHT( x, y );
4481 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4486 cluster = SUPER_CLUSTER( x, y );
4488 /* only process mapped luxels */
4489 if ( *cluster < 0 ) {
4493 /* get particulars */
4494 luxel = SUPER_LUXEL( lightmapNum, x, y );
4495 deluxel = SUPER_DELUXEL( x, y );
4497 /* add to lightmap */
4498 luxel[0] += floodlight[0];
4499 luxel[1] += floodlight[1];
4500 luxel[2] += floodlight[2];
4502 if ( luxel[3] == 0 ) {
4506 /* add to deluxemap */
4507 if ( deluxemap && floodlight[3] > 0 ) {
4510 normal = SUPER_NORMAL( x, y );
4511 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4513 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4514 if ( brightness < 0.00390625f ) {
4515 brightness = 0.00390625f;
4518 VectorScale( normal, brightness, lightvector );
4519 VectorAdd( deluxel, lightvector, deluxel );