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 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for ( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if ( sample[ i ] < 0.0f ) {
73 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
76 if ( lightmapExposure == 0 ) {
77 /* clamp with color normalization */
79 if ( sample[ 1 ] > max ) {
82 if ( sample[ 2 ] > max ) {
86 VectorScale( sample, ( 255.0f / max ), sample );
91 inv = 1.f / lightmapExposure;
95 if ( sample[ 1 ] > max ) {
98 if ( sample[ 2 ] > max ) {
102 dif = ( 1 - exp( -max * inv ) ) * 255;
112 for ( i = 0; i < 3; i++ )
119 /* compensate for ingame overbrighting/bitshifting */
120 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
123 if ( lightmapsRGB ) {
124 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
125 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
126 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
130 colorBytes[ 0 ] = sample[ 0 ];
131 colorBytes[ 1 ] = sample[ 1 ];
132 colorBytes[ 2 ] = sample[ 2 ];
137 /* -------------------------------------------------------------------------------
139 this section deals with phong shading (normal interpolation across brush faces)
141 ------------------------------------------------------------------------------- */
145 smooths together coincident vertex normals across the bsp
148 #define MAX_SAMPLES 256
149 #define THETA_EPSILON 0.000001
150 #define EQUAL_NORMAL_EPSILON 0.01
152 void SmoothNormals( void ){
153 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
154 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
155 bspDrawSurface_t *ds;
159 vec3_t average, diff;
160 int indexes[ MAX_SAMPLES ];
161 vec3_t votes[ MAX_SAMPLES ];
164 /* allocate shade angle table */
165 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
166 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
168 /* allocate smoothed table */
169 cs = ( numBSPDrawVerts / 8 ) + 1;
170 smoothed = safe_malloc( cs );
171 memset( smoothed, 0, cs );
173 /* set default shade angle */
174 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
177 /* run through every surface and flag verts belonging to non-lightmapped surfaces
178 and set per-vertex smoothing angle */
179 for ( i = 0; i < numBSPDrawSurfaces; i++ )
182 ds = &bspDrawSurfaces[ i ];
184 /* get shader for shade angle */
185 si = surfaceInfos[ i ].si;
186 if ( si->shadeAngleDegrees ) {
187 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
190 shadeAngle = defaultShadeAngle;
192 if ( shadeAngle > maxShadeAngle ) {
193 maxShadeAngle = shadeAngle;
197 for ( j = 0; j < ds->numVerts; j++ )
199 f = ds->firstVert + j;
200 shadeAngles[ f ] = shadeAngle;
201 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
202 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
206 /* ydnar: optional force-to-trisoup */
207 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
208 ds->surfaceType = MST_TRIANGLE_SOUP;
209 ds->lightmapNum[ 0 ] = -3;
213 /* bail if no surfaces have a shade angle */
214 if ( maxShadeAngle == 0 ) {
222 start = I_FloatTime();
224 /* go through the list of vertexes */
225 for ( i = 0; i < numBSPDrawVerts; i++ )
228 f = 10 * i / numBSPDrawVerts;
231 Sys_Printf( "%i...", f );
234 /* already smoothed? */
235 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
240 VectorClear( average );
244 /* build a table of coincident vertexes */
245 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
247 /* already smoothed? */
248 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
253 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
257 /* use smallest shade angle */
258 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
260 /* check shade angle */
261 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
265 else if ( dot < -1.0 ) {
268 testAngle = acos( dot ) + THETA_EPSILON;
269 if ( testAngle >= shadeAngle ) {
270 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
273 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
275 /* add to the list */
276 indexes[ numVerts++ ] = j;
279 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
281 /* see if this normal has already been voted */
282 for ( k = 0; k < numVotes; k++ )
284 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
285 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
286 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
287 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
292 /* add a new vote? */
293 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
294 VectorAdd( average, bspDrawVerts[ j ].normal, average );
295 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
300 /* don't average for less than 2 verts */
301 if ( numVerts < 2 ) {
306 if ( VectorNormalize( average, average ) > 0 ) {
308 for ( j = 0; j < numVerts; j++ )
309 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
313 /* free the tables */
318 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
323 /* -------------------------------------------------------------------------------
325 this section deals with phong shaded lightmap tracing
327 ------------------------------------------------------------------------------- */
329 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
333 calculates the st tangent vectors for normalmapping
336 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
342 /* calculate barycentric basis for the triangle */
343 bb = ( dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ] ) * ( dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ] ) - ( dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ] ) * ( dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ] );
344 if ( fabs( bb ) < 0.00000001f ) {
349 for ( i = 0; i < numVerts; i++ )
351 /* calculate s tangent vector */
352 s = dv[ i ]->st[ 0 ] + 10.0f;
353 t = dv[ i ]->st[ 1 ];
354 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
355 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
356 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
358 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
359 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
360 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
362 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
363 VectorNormalize( stv[ i ], stv[ i ] );
365 /* calculate t tangent vector */
366 s = dv[ i ]->st[ 0 ];
367 t = dv[ i ]->st[ 1 ] + 10.0f;
368 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
369 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
370 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
372 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
373 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
374 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
376 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
377 VectorNormalize( ttv[ i ], ttv[ i ] );
380 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
381 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
384 /* return to caller */
393 perterbs the normal by the shader's normalmap in tangent space
396 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
402 VectorCopy( dv->normal, pNormal );
404 /* sample normalmap */
405 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
409 /* remap sampled normal from [0,255] to [-1,-1] */
410 for ( i = 0; i < 3; i++ )
411 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
413 /* scale tangent vectors and add to original normal */
414 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
415 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
416 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
418 /* renormalize and return */
419 VectorNormalize( pNormal, pNormal );
426 maps a luxel for triangle bv at
430 #define BOGUS_NUDGE -99999.0f
432 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] ){
433 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
434 float *luxel, *origin, *normal, d, lightmapSampleOffset;
441 vec4_t sideplane, hostplane;
446 static float nudges[][ 2 ] =
448 //%{ 0, 0 }, /* try center first */
449 { -NUDGE, 0 }, /* left */
450 { NUDGE, 0 }, /* right */
451 { 0, NUDGE }, /* up */
452 { 0, -NUDGE }, /* down */
453 { -NUDGE, NUDGE }, /* left/up */
454 { NUDGE, -NUDGE }, /* right/down */
455 { NUDGE, NUDGE }, /* right/up */
456 { -NUDGE, -NUDGE }, /* left/down */
457 { BOGUS_NUDGE, BOGUS_NUDGE }
461 /* find luxel xy coords (fixme: subtract 0.5?) */
462 x = dv->lightmap[ 0 ][ 0 ];
463 y = dv->lightmap[ 0 ][ 1 ];
467 else if ( x >= lm->sw ) {
473 else if ( y >= lm->sh ) {
477 /* set shader and cluster list */
478 if ( info != NULL ) {
480 numClusters = info->numSurfaceClusters;
481 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
490 /* get luxel, origin, cluster, and normal */
491 luxel = SUPER_LUXEL( 0, x, y );
492 origin = SUPER_ORIGIN( x, y );
493 normal = SUPER_NORMAL( x, y );
494 cluster = SUPER_CLUSTER( x, y );
496 /* don't attempt to remap occluded luxels for planar surfaces */
497 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
501 /* only average the normal for premapped luxels */
502 else if ( ( *cluster ) >= 0 ) {
503 /* do bumpmap calculations */
505 PerturbNormal( dv, si, pNormal, stv, ttv );
508 VectorCopy( dv->normal, pNormal );
511 /* add the additional normal data */
512 VectorAdd( normal, pNormal, normal );
517 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
521 /* axial lightmap projection */
522 if ( lm->vecs != NULL ) {
523 /* calculate an origin for the sample from the lightmap vectors */
524 VectorCopy( lm->origin, origin );
525 for ( i = 0; i < 3; i++ )
527 /* add unless it's the axis, which is taken care of later */
528 if ( i == lm->axisNum ) {
531 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
534 /* project the origin onto the plane */
535 d = DotProduct( origin, plane ) - plane[ 3 ];
536 d /= plane[ lm->axisNum ];
537 origin[ lm->axisNum ] -= d;
540 /* non axial lightmap projection (explicit xyz) */
542 VectorCopy( dv->xyz, origin );
545 //////////////////////
546 //27's test to make sure samples stay within the triangle boundaries
547 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
548 //2) if it does, nudge it onto the correct side.
550 if ( worldverts != NULL && lightmapTriangleCheck ) {
551 for ( j = 0; j < 3; j++ )
553 VectorCopy( worldverts[j],cverts[j] );
555 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
557 for ( j = 0; j < 3; j++ )
559 for ( i = 0; i < 3; i++ )
561 //build plane using 2 edges and a normal
562 next = ( i + 1 ) % 3;
564 VectorCopy( cverts[next],temp );
565 VectorAdd( temp,hostplane,temp );
566 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
568 //planetest sample point
569 e = DotProduct( origin,sideplane );
570 e = e - sideplane[3];
573 //VectorClear(origin);
574 //Move the sample point back inside triangle bounds
575 origin[0] -= sideplane[0] * ( e + 1 );
576 origin[1] -= sideplane[1] * ( e + 1 );
577 origin[2] -= sideplane[2] * ( e + 1 );
579 VectorClear( origin );
586 ////////////////////////
588 /* planar surfaces have precalculated lightmap vectors for nudging */
589 if ( lm->plane != NULL ) {
590 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
591 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
592 VectorCopy( lm->plane, vecs[ 2 ] );
595 /* non-planar surfaces must calculate them */
598 if ( plane != NULL ) {
599 VectorCopy( plane, vecs[ 2 ] );
602 VectorCopy( dv->normal, vecs[ 2 ] );
604 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
607 /* push the origin off the surface a bit */
609 lightmapSampleOffset = si->lightmapSampleOffset;
612 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
614 if ( lm->axisNum < 0 ) {
615 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
617 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
618 origin[ lm->axisNum ] -= lightmapSampleOffset;
621 origin[ lm->axisNum ] += lightmapSampleOffset;
624 VectorCopy( origin,origintwo );
625 if ( lightmapExtraVisClusterNudge ) {
626 origintwo[0] += vecs[2][0];
627 origintwo[1] += vecs[2][1];
628 origintwo[2] += vecs[2][2];
632 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
634 /* another retarded hack, storing nudge count in luxel[ 1 ] */
637 /* point in solid? (except in dark mode) */
638 if ( pointCluster < 0 && dark == qfalse ) {
639 /* nudge the the location around */
641 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
643 /* nudge the vector around a bit */
644 for ( i = 0; i < 3; i++ )
646 /* set nudged point*/
647 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
651 /* get pvs cluster */
652 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
653 if ( pointCluster >= 0 ) {
654 VectorCopy( nudged, origin );
660 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
661 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
662 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
663 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
664 if ( pointCluster >= 0 ) {
665 VectorCopy( nudged, origin );
671 if ( pointCluster < 0 ) {
672 ( *cluster ) = CLUSTER_OCCLUDED;
673 VectorClear( origin );
674 VectorClear( normal );
680 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
682 /* do bumpmap calculations */
684 PerturbNormal( dv, si, pNormal, stv, ttv );
687 VectorCopy( dv->normal, pNormal );
690 /* store the cluster and normal */
691 ( *cluster ) = pointCluster;
692 VectorCopy( pNormal, normal );
694 /* store explicit mapping pass and implicit mapping pass */
709 recursively subdivides a triangle until its edges are shorter
710 than the distance between two luxels (thanks jc :)
713 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 ] ){
714 bspDrawVert_t mid, *dv2[ 3 ];
718 /* map the vertexes */
720 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
721 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
722 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
728 float *a, *b, dx, dy, dist, maxDist;
731 /* find the longest edge and split it */
734 for ( i = 0; i < 3; i++ )
737 a = dv[ i ]->lightmap[ 0 ];
738 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
741 dx = a[ 0 ] - b[ 0 ];
742 dy = a[ 1 ] - b[ 1 ];
743 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
746 if ( dist > maxDist ) {
752 /* try to early out */
753 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
758 /* split the longest edge and map it */
759 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
760 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
762 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
763 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
765 /* recurse to first triangle */
766 VectorCopy( dv, dv2 );
768 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
770 /* recurse to second triangle */
771 VectorCopy( dv, dv2 );
772 dv2[ ( max + 1 ) % 3 ] = ∣
773 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
780 seed function for MapTriangle_r()
781 requires a cw ordered triangle
784 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
787 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
788 vec3_t worldverts[ 3 ];
791 /* get plane if possible */
792 if ( lm->plane != NULL ) {
793 VectorCopy( lm->plane, plane );
794 plane[ 3 ] = lm->plane[ 3 ];
797 /* otherwise make one from the points */
798 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
802 /* check to see if we need to calculate texture->world tangent vectors */
803 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
813 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
814 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
815 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
817 /* map the vertexes */
818 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
819 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
820 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
822 /* 2002-11-20: prefer axial triangle edges */
824 /* subdivide the triangle */
825 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
829 for ( i = 0; i < 3; i++ )
832 bspDrawVert_t *dv2[ 3 ];
836 a = dv[ i ]->lightmap[ 0 ];
837 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
839 /* make degenerate triangles for mapping edges */
840 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
842 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
843 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
845 /* map the degenerate triangle */
846 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
857 recursively subdivides a quad until its edges are shorter
858 than the distance between two luxels
861 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 ] ){
862 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
869 float *a, *b, dx, dy, dist, maxDist;
872 /* find the longest edge and split it */
875 for ( i = 0; i < 4; i++ )
878 a = dv[ i ]->lightmap[ 0 ];
879 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
882 dx = a[ 0 ] - b[ 0 ];
883 dy = a[ 1 ] - b[ 1 ];
884 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
887 if ( dist > maxDist ) {
893 /* try to early out */
894 if ( max < 0 || maxDist <= subdivideThreshold ) {
899 /* we only care about even/odd edges */
902 /* split the longest edges */
903 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
904 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
906 /* map the vertexes */
907 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
908 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
912 /* recurse to first quad */
914 dv2[ 1 ] = &mid[ 0 ];
915 dv2[ 2 ] = &mid[ 1 ];
917 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to second quad */
920 dv2[ 0 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
930 /* recurse to first quad */
933 dv2[ 2 ] = &mid[ 0 ];
934 dv2[ 3 ] = &mid[ 1 ];
935 MapQuad_r( lm, info, dv2, plane, stv, ttv );
937 /* recurse to second quad */
938 dv2[ 0 ] = &mid[ 1 ];
939 dv2[ 1 ] = &mid[ 0 ];
942 MapQuad_r( lm, info, dv2, plane, stv, ttv );
950 seed function for MapQuad_r()
951 requires a cw ordered triangle quad
954 #define QUAD_PLANAR_EPSILON 0.5f
956 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
959 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
962 /* get plane if possible */
963 if ( lm->plane != NULL ) {
964 VectorCopy( lm->plane, plane );
965 plane[ 3 ] = lm->plane[ 3 ];
968 /* otherwise make one from the points */
969 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
973 /* 4th point must fall on the plane */
974 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
975 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
979 /* check to see if we need to calculate texture->world tangent vectors */
980 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
990 /* map the vertexes */
991 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
992 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
993 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
994 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
996 /* subdivide the quad */
997 MapQuad_r( lm, info, dv, plane, stv, ttv );
1005 maps the locations, normals, and pvs clusters for a raw lightmap
1008 #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)
1010 void MapRawLightmap( int rawLightmapNum ){
1011 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1012 float *luxel, *origin, *normal, samples, radius, pass;
1014 bspDrawSurface_t *ds;
1015 surfaceInfo_t *info;
1016 mesh_t src, *subdivided, *mesh;
1017 bspDrawVert_t *verts, *dv[ 4 ], fake;
1020 /* bail if this number exceeds the number of raw lightmaps */
1021 if ( rawLightmapNum >= numRawLightmaps ) {
1026 lm = &rawLightmaps[ rawLightmapNum ];
1028 /* -----------------------------------------------------------------
1029 map referenced surfaces onto the raw lightmap
1030 ----------------------------------------------------------------- */
1032 /* walk the list of surfaces on this raw lightmap */
1033 for ( n = 0; n < lm->numLightSurfaces; n++ )
1035 /* with > 1 surface per raw lightmap, clear occluded */
1037 for ( y = 0; y < lm->sh; y++ )
1039 for ( x = 0; x < lm->sw; x++ )
1042 cluster = SUPER_CLUSTER( x, y );
1043 if ( *cluster < 0 ) {
1044 *cluster = CLUSTER_UNMAPPED;
1051 num = lightSurfaces[ lm->firstLightSurface + n ];
1052 ds = &bspDrawSurfaces[ num ];
1053 info = &surfaceInfos[ num ];
1055 /* bail if no lightmap to calculate */
1056 if ( info->lm != lm ) {
1061 /* map the surface onto the lightmap origin/cluster/normal buffers */
1062 switch ( ds->surfaceType )
1066 verts = yDrawVerts + ds->firstVert;
1068 /* map the triangles */
1069 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1071 for ( i = 0; i < ds->numIndexes; i += 3 )
1073 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1074 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1075 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1076 MapTriangle( lm, info, dv, mapNonAxial );
1082 /* make a mesh from the drawsurf */
1083 src.width = ds->patchWidth;
1084 src.height = ds->patchHeight;
1085 src.verts = &yDrawVerts[ ds->firstVert ];
1086 //% subdivided = SubdivideMesh( src, 8, 512 );
1087 subdivided = SubdivideMesh2( src, info->patchIterations );
1089 /* fit it to the curve and remove colinear verts on rows/columns */
1090 PutMeshOnCurve( *subdivided );
1091 mesh = RemoveLinearMeshColumnsRows( subdivided );
1092 FreeMesh( subdivided );
1095 verts = mesh->verts;
1100 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1101 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1102 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1103 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1107 /* map the mesh quads */
1110 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1112 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1114 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1117 pw[ 0 ] = x + ( y * mesh->width );
1118 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1119 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1120 pw[ 3 ] = x + 1 + ( y * mesh->width );
1121 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1126 /* get drawverts and map first triangle */
1127 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1128 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1129 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1130 MapTriangle( lm, info, dv, mapNonAxial );
1132 /* get drawverts and map second triangle */
1133 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1134 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1135 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1136 MapTriangle( lm, info, dv, mapNonAxial );
1143 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1145 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1148 pw[ 0 ] = x + ( y * mesh->width );
1149 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1150 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1151 pw[ 3 ] = x + 1 + ( y * mesh->width );
1157 /* attempt to map quad first */
1158 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1159 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1160 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1161 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1162 if ( MapQuad( lm, info, dv ) ) {
1166 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1168 /* get drawverts and map first triangle */
1169 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1170 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1171 MapTriangle( lm, info, dv, mapNonAxial );
1173 /* get drawverts and map second triangle */
1174 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1175 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1176 MapTriangle( lm, info, dv, mapNonAxial );
1192 /* -----------------------------------------------------------------
1193 average and clean up luxel normals
1194 ----------------------------------------------------------------- */
1196 /* walk the luxels */
1197 for ( y = 0; y < lm->sh; y++ )
1199 for ( x = 0; x < lm->sw; x++ )
1202 luxel = SUPER_LUXEL( 0, x, y );
1203 normal = SUPER_NORMAL( x, y );
1204 cluster = SUPER_CLUSTER( x, y );
1206 /* only look at mapped luxels */
1207 if ( *cluster < 0 ) {
1211 /* the normal data could be the sum of multiple samples */
1212 if ( luxel[ 3 ] > 1.0f ) {
1213 VectorNormalize( normal, normal );
1216 /* mark this luxel as having only one normal */
1221 /* non-planar surfaces stop here */
1222 if ( lm->plane == NULL ) {
1226 /* -----------------------------------------------------------------
1227 map occluded or unuxed luxels
1228 ----------------------------------------------------------------- */
1230 /* walk the luxels */
1231 radius = floor( superSample / 2 );
1232 radius = radius > 0 ? radius : 1.0f;
1234 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1236 for ( y = 0; y < lm->sh; y++ )
1238 for ( x = 0; x < lm->sw; x++ )
1241 luxel = SUPER_LUXEL( 0, x, y );
1242 normal = SUPER_NORMAL( x, y );
1243 cluster = SUPER_CLUSTER( x, y );
1245 /* only look at unmapped luxels */
1246 if ( *cluster != CLUSTER_UNMAPPED ) {
1250 /* divine a normal and origin from neighboring luxels */
1251 VectorClear( fake.xyz );
1252 VectorClear( fake.normal );
1253 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1254 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1256 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1258 if ( sy < 0 || sy >= lm->sh ) {
1262 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1264 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1268 /* get neighboring luxel */
1269 luxel = SUPER_LUXEL( 0, sx, sy );
1270 origin = SUPER_ORIGIN( sx, sy );
1271 normal = SUPER_NORMAL( sx, sy );
1272 cluster = SUPER_CLUSTER( sx, sy );
1274 /* only consider luxels mapped in previous passes */
1275 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1279 /* add its distinctiveness to our own */
1280 VectorAdd( fake.xyz, origin, fake.xyz );
1281 VectorAdd( fake.normal, normal, fake.normal );
1282 samples += luxel[ 3 ];
1287 if ( samples == 0.0f ) {
1292 VectorDivide( fake.xyz, samples, fake.xyz );
1293 //% VectorDivide( fake.normal, samples, fake.normal );
1294 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1298 /* map the fake vert */
1299 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1304 /* -----------------------------------------------------------------
1305 average and clean up luxel normals
1306 ----------------------------------------------------------------- */
1308 /* walk the luxels */
1309 for ( y = 0; y < lm->sh; y++ )
1311 for ( x = 0; x < lm->sw; x++ )
1314 luxel = SUPER_LUXEL( 0, x, y );
1315 normal = SUPER_NORMAL( x, y );
1316 cluster = SUPER_CLUSTER( x, y );
1318 /* only look at mapped luxels */
1319 if ( *cluster < 0 ) {
1323 /* the normal data could be the sum of multiple samples */
1324 if ( luxel[ 3 ] > 1.0f ) {
1325 VectorNormalize( normal, normal );
1328 /* mark this luxel as having only one normal */
1336 for ( y = 0; y < lm->sh; y++ )
1338 for ( x = 0; x < lm->sw; x++ )
1343 cluster = SUPER_CLUSTER( x, y );
1344 origin = SUPER_ORIGIN( x, y );
1345 normal = SUPER_NORMAL( x, y );
1346 luxel = SUPER_LUXEL( x, y );
1348 if ( *cluster < 0 ) {
1352 /* check if within the bounding boxes of all surfaces referenced */
1353 ClearBounds( mins, maxs );
1354 for ( n = 0; n < lm->numLightSurfaces; n++ )
1357 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1358 TOL = info->sampleSize + 2;
1359 AddPointToBounds( info->mins, mins, maxs );
1360 AddPointToBounds( info->maxs, mins, maxs );
1361 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1362 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1363 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1369 if ( n < lm->numLightSurfaces ) {
1373 /* report bogus origin */
1374 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",
1375 rawLightmapNum, x, y, *cluster,
1376 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1377 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1378 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1389 sets up dirtmap (ambient occlusion)
1392 #define DIRT_CONE_ANGLE 88 /* degrees */
1393 #define DIRT_NUM_ANGLE_STEPS 16
1394 #define DIRT_NUM_ELEVATION_STEPS 3
1395 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1397 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1398 static int numDirtVectors = 0;
1400 void SetupDirt( void ){
1402 float angle, elevation, angleStep, elevationStep;
1406 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1408 /* calculate angular steps */
1409 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1410 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1414 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1416 /* iterate elevation */
1417 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1419 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1420 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1421 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1426 /* emit some statistics */
1427 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1433 calculates dirt value for a given sample
1436 float DirtForSample( trace_t *trace ){
1438 float gatherDirt, outDirt, angle, elevation, ooDepth;
1439 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1446 if ( trace == NULL || trace->cluster < 0 ) {
1452 ooDepth = 1.0f / dirtDepth;
1453 VectorCopy( trace->normal, normal );
1455 /* check if the normal is aligned to the world-up */
1456 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1457 if ( normal[ 2 ] == 1.0f ) {
1458 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1459 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1461 else if ( normal[ 2 ] == -1.0f ) {
1462 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1463 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1468 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1469 CrossProduct( normal, worldUp, myRt );
1470 VectorNormalize( myRt, myRt );
1471 CrossProduct( myRt, normal, myUp );
1472 VectorNormalize( myUp, myUp );
1475 /* 1 = random mode, 0 (well everything else) = non-random mode */
1476 if ( dirtMode == 1 ) {
1478 for ( i = 0; i < numDirtVectors; i++ )
1480 /* get random vector */
1481 angle = Random() * DEG2RAD( 360.0f );
1482 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1483 temp[ 0 ] = cos( angle ) * sin( elevation );
1484 temp[ 1 ] = sin( angle ) * sin( elevation );
1485 temp[ 2 ] = cos( elevation );
1487 /* transform into tangent space */
1488 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1489 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1490 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1493 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1494 SetupTrace( trace );
1495 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1499 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1500 VectorSubtract( trace->hit, trace->origin, displacement );
1501 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1507 /* iterate through ordered vectors */
1508 for ( i = 0; i < numDirtVectors; i++ )
1510 /* transform vector into tangent space */
1511 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1512 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1513 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1516 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1517 SetupTrace( trace );
1518 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1522 if ( trace->opaque ) {
1523 VectorSubtract( trace->hit, trace->origin, displacement );
1524 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1530 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1531 SetupTrace( trace );
1532 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1536 if ( trace->opaque ) {
1537 VectorSubtract( trace->hit, trace->origin, displacement );
1538 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1542 if ( gatherDirt <= 0.0f ) {
1546 /* apply gain (does this even do much? heh) */
1547 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1548 if ( outDirt > 1.0f ) {
1553 outDirt *= dirtScale;
1554 if ( outDirt > 1.0f ) {
1558 /* return to sender */
1559 return 1.0f - outDirt;
1566 calculates dirty fraction for each luxel
1569 void DirtyRawLightmap( int rawLightmapNum ){
1570 int i, x, y, sx, sy, *cluster;
1571 float *origin, *normal, *dirt, *dirt2, average, samples;
1573 surfaceInfo_t *info;
1578 /* bail if this number exceeds the number of raw lightmaps */
1579 if ( rawLightmapNum >= numRawLightmaps ) {
1584 lm = &rawLightmaps[ rawLightmapNum ];
1587 trace.testOcclusion = qtrue;
1588 trace.forceSunlight = qfalse;
1589 trace.recvShadows = lm->recvShadows;
1590 trace.numSurfaces = lm->numLightSurfaces;
1591 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1592 trace.inhibitRadius = 0.0f;
1593 trace.testAll = qfalse;
1595 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1596 trace.twoSided = qfalse;
1597 for ( i = 0; i < trace.numSurfaces; i++ )
1600 info = &surfaceInfos[ trace.surfaces[ i ] ];
1602 /* check twosidedness */
1603 if ( info->si->twoSided ) {
1604 trace.twoSided = qtrue;
1610 for ( i = 0; i < trace.numSurfaces; i++ )
1613 info = &surfaceInfos[ trace.surfaces[ i ] ];
1615 /* check twosidedness */
1616 if ( info->si->noDirty ) {
1623 for ( y = 0; y < lm->sh; y++ )
1625 for ( x = 0; x < lm->sw; x++ )
1628 cluster = SUPER_CLUSTER( x, y );
1629 origin = SUPER_ORIGIN( x, y );
1630 normal = SUPER_NORMAL( x, y );
1631 dirt = SUPER_DIRT( x, y );
1633 /* set default dirt */
1636 /* only look at mapped luxels */
1637 if ( *cluster < 0 ) {
1641 /* don't apply dirty on this surface */
1648 trace.cluster = *cluster;
1649 VectorCopy( origin, trace.origin );
1650 VectorCopy( normal, trace.normal );
1653 *dirt = DirtForSample( &trace );
1657 /* testing no filtering */
1661 for ( y = 0; y < lm->sh; y++ )
1663 for ( x = 0; x < lm->sw; x++ )
1666 cluster = SUPER_CLUSTER( x, y );
1667 dirt = SUPER_DIRT( x, y );
1669 /* filter dirt by adjacency to unmapped luxels */
1672 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1674 if ( sy < 0 || sy >= lm->sh ) {
1678 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1680 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1684 /* get neighboring luxel */
1685 cluster = SUPER_CLUSTER( sx, sy );
1686 dirt2 = SUPER_DIRT( sx, sy );
1687 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1697 if ( samples <= 0.0f ) {
1703 if ( samples <= 0.0f ) {
1708 *dirt = average / samples;
1717 calculates the pvs cluster, origin, normal of a sub-luxel
1720 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1721 int i, *cluster, *cluster2;
1722 float *origin = NULL, *origin2 = NULL, *normal; //% , *normal2;
1723 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1726 /* calulate x vector */
1727 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1728 cluster = SUPER_CLUSTER( x, y );
1729 origin = SUPER_ORIGIN( x, y );
1730 //% normal = SUPER_NORMAL( x, y );
1731 cluster2 = SUPER_CLUSTER( x + 1, y );
1732 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1733 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1735 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1736 cluster = SUPER_CLUSTER( x - 1, y );
1737 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1738 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1739 cluster2 = SUPER_CLUSTER( x, y );
1740 origin2 = SUPER_ORIGIN( x, y );
1741 //% normal2 = SUPER_NORMAL( x, y );
1744 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1747 if (origin && origin2) {
1748 VectorSubtract(origin2, origin, originVecs[0]);
1750 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1752 origin = origin2 = NULL;
1754 /* calulate y vector */
1755 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1756 cluster = SUPER_CLUSTER( x, y );
1757 origin = SUPER_ORIGIN( x, y );
1758 //% normal = SUPER_NORMAL( x, y );
1759 cluster2 = SUPER_CLUSTER( x, y + 1 );
1760 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1761 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1763 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1764 cluster = SUPER_CLUSTER( x, y - 1 );
1765 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1766 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1767 cluster2 = SUPER_CLUSTER( x, y );
1768 origin2 = SUPER_ORIGIN( x, y );
1769 //% normal2 = SUPER_NORMAL( x, y );
1772 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1775 if (origin && origin2) {
1776 VectorSubtract(origin2, origin, originVecs[1]);
1779 /* calculate new origin */
1780 for ( i = 0; i < 3; i++ )
1781 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1784 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1785 if ( *sampleCluster < 0 ) {
1789 /* calculate new normal */
1790 normal = SUPER_NORMAL( x, y );
1791 VectorCopy( normal, sampleNormal );
1799 SubsampleRawLuxel_r()
1800 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1803 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1804 int b, samples, mapped, lighted;
1807 vec3_t deluxel[ 3 ];
1808 vec3_t origin[ 4 ], normal[ 4 ];
1809 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1810 vec3_t color, direction = { 0, 0, 0 }, total;
1814 if ( lightLuxel[ 3 ] >= lightSamples ) {
1819 VectorClear( total );
1823 /* make 2x2 subsample stamp */
1824 for ( b = 0; b < 4; b++ )
1827 VectorCopy( sampleOrigin, origin[ b ] );
1829 /* calculate position */
1830 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1836 /* increment sample count */
1837 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1840 trace->cluster = *cluster;
1841 VectorCopy( origin[ b ], trace->origin );
1842 VectorCopy( normal[ b ], trace->normal );
1846 LightContributionToSample( trace );
1847 if ( trace->forceSubsampling > 1.0f ) {
1848 /* alphashadow: we subsample as deep as we can */
1854 /* add to totals (fixme: make contrast function) */
1855 VectorCopy( trace->color, luxel[ b ] );
1856 if ( lightDeluxel ) {
1857 VectorCopy( trace->directionContribution, deluxel[ b ] );
1859 VectorAdd( total, trace->color, total );
1860 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1865 /* subsample further? */
1866 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1867 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1868 lighted != 0 && lighted != mapped ) {
1869 for ( b = 0; b < 4; b++ )
1871 if ( cluster[ b ] < 0 ) {
1874 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1879 //% VectorClear( color );
1881 VectorCopy( lightLuxel, color );
1882 if ( lightDeluxel ) {
1883 VectorCopy( lightDeluxel, direction );
1886 for ( b = 0; b < 4; b++ )
1888 if ( cluster[ b ] < 0 ) {
1891 VectorAdd( color, luxel[ b ], color );
1892 if ( lightDeluxel ) {
1893 VectorAdd( direction, deluxel[ b ], direction );
1899 if ( samples > 0 ) {
1901 color[ 0 ] /= samples;
1902 color[ 1 ] /= samples;
1903 color[ 2 ] /= samples;
1906 VectorCopy( color, lightLuxel );
1907 lightLuxel[ 3 ] += 1.0f;
1909 if ( lightDeluxel ) {
1910 direction[ 0 ] /= samples;
1911 direction[ 1 ] /= samples;
1912 direction[ 2 ] /= samples;
1913 VectorCopy( direction, lightDeluxel );
1918 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1919 static void GaussLikeRandom( float sigma, float *x, float *y ){
1921 r = Random() * 2 * Q_PI;
1922 *x = sigma * 2.73861278752581783822 * cos( r );
1923 *y = sigma * 2.73861278752581783822 * sin( r );
1930 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1933 vec3_t origin, normal;
1934 vec3_t total, totaldirection;
1937 VectorClear( total );
1938 VectorClear( totaldirection );
1940 for ( b = 0; b < lightSamples; ++b )
1943 VectorCopy( sampleOrigin, origin );
1944 GaussLikeRandom( bias, &dx, &dy );
1946 /* calculate position */
1947 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1953 trace->cluster = cluster;
1954 VectorCopy( origin, trace->origin );
1955 VectorCopy( normal, trace->normal );
1957 LightContributionToSample( trace );
1958 VectorAdd( total, trace->color, total );
1959 if ( lightDeluxel ) {
1960 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1967 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1968 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1969 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1971 if ( lightDeluxel ) {
1972 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1973 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1974 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1982 IlluminateRawLightmap()
1983 illuminates the luxels
1986 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1987 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1988 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1990 void IlluminateRawLightmap( int rawLightmapNum ){
1991 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1992 int *cluster, *cluster2, mapped, lighted, totalLighted;
1993 size_t llSize, ldSize;
1995 surfaceInfo_t *info;
1996 qboolean filterColor, filterDir;
1998 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1999 unsigned char *flag;
2000 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2001 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2002 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2004 float stackLightLuxels[ STACK_LL_SIZE ];
2007 /* bail if this number exceeds the number of raw lightmaps */
2008 if ( rawLightmapNum >= numRawLightmaps ) {
2013 lm = &rawLightmaps[ rawLightmapNum ];
2016 trace.testOcclusion = !noTrace;
2017 trace.forceSunlight = qfalse;
2018 trace.recvShadows = lm->recvShadows;
2019 trace.numSurfaces = lm->numLightSurfaces;
2020 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2021 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2023 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2024 trace.twoSided = qfalse;
2025 for ( i = 0; i < trace.numSurfaces; i++ )
2028 info = &surfaceInfos[ trace.surfaces[ i ] ];
2030 /* check twosidedness */
2031 if ( info->si->twoSided ) {
2032 trace.twoSided = qtrue;
2037 /* create a culled light list for this raw lightmap */
2038 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2040 /* -----------------------------------------------------------------
2042 ----------------------------------------------------------------- */
2045 numLuxelsIlluminated += ( lm->sw * lm->sh );
2047 /* test debugging state */
2048 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2049 /* debug fill the luxels */
2050 for ( y = 0; y < lm->sh; y++ )
2052 for ( x = 0; x < lm->sw; x++ )
2055 cluster = SUPER_CLUSTER( x, y );
2057 /* only fill mapped luxels */
2058 if ( *cluster < 0 ) {
2062 /* get particulars */
2063 luxel = SUPER_LUXEL( 0, x, y );
2064 origin = SUPER_ORIGIN( x, y );
2065 normal = SUPER_NORMAL( x, y );
2067 /* color the luxel with raw lightmap num? */
2068 if ( debugSurfaces ) {
2069 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2072 /* color the luxel with lightmap axis? */
2073 else if ( debugAxis ) {
2074 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2075 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2076 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2079 /* color the luxel with luxel cluster? */
2080 else if ( debugCluster ) {
2081 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2084 /* color the luxel with luxel origin? */
2085 else if ( debugOrigin ) {
2086 VectorSubtract( lm->maxs, lm->mins, temp );
2087 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2088 VectorSubtract( origin, lm->mins, temp2 );
2089 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2090 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2091 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2094 /* color the luxel with the normal */
2095 else if ( normalmap ) {
2096 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2097 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2098 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2101 /* otherwise clear it */
2103 VectorClear( luxel );
2113 /* allocate temporary per-light luxel storage */
2114 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2115 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2116 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2117 lightLuxels = stackLightLuxels;
2120 lightLuxels = safe_malloc( llSize );
2123 lightDeluxels = safe_malloc( ldSize );
2126 lightDeluxels = NULL;
2130 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2132 /* set ambient color */
2133 for ( y = 0; y < lm->sh; y++ )
2135 for ( x = 0; x < lm->sw; x++ )
2138 cluster = SUPER_CLUSTER( x, y );
2139 luxel = SUPER_LUXEL( 0, x, y );
2140 normal = SUPER_NORMAL( x, y );
2141 deluxel = SUPER_DELUXEL( x, y );
2143 /* blacken unmapped clusters */
2144 if ( *cluster < 0 ) {
2145 VectorClear( luxel );
2151 VectorCopy( ambientColor, luxel );
2153 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2155 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2156 if ( brightness < 0.00390625f ) {
2157 brightness = 0.00390625f;
2160 VectorScale( normal, brightness, deluxel );
2167 /* clear styled lightmaps */
2168 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2169 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2171 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2172 memset( lm->superLuxels[ lightmapNum ], 0, size );
2176 /* debugging code */
2177 //% if( trace.numLights <= 0 )
2178 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2180 /* walk light list */
2181 for ( i = 0; i < trace.numLights; i++ )
2184 trace.light = trace.lights[ i ];
2187 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2189 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2190 lm->styles[ lightmapNum ] == LS_NONE ) {
2195 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2196 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2197 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2202 memset( lightLuxels, 0, llSize );
2204 memset( lightDeluxels, 0, ldSize );
2208 /* determine filter radius */
2209 filterRadius = lm->filterRadius > trace.light->filterRadius
2211 : trace.light->filterRadius;
2212 if ( filterRadius < 0.0f ) {
2213 filterRadius = 0.0f;
2216 /* set luxel filter radius */
2217 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2218 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2219 luxelFilterRadius = 1;
2222 /* allocate sampling flags storage */
2223 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2224 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2225 if ( lm->superFlags == NULL ) {
2226 lm->superFlags = safe_malloc( size );
2228 memset( (void *) lm->superFlags, 0, size );
2231 /* initial pass, one sample per luxel */
2232 for ( y = 0; y < lm->sh; y++ )
2234 for ( x = 0; x < lm->sw; x++ )
2237 cluster = SUPER_CLUSTER( x, y );
2238 if ( *cluster < 0 ) {
2242 /* get particulars */
2243 lightLuxel = LIGHT_LUXEL( x, y );
2244 lightDeluxel = LIGHT_DELUXEL( x, y );
2245 origin = SUPER_ORIGIN( x, y );
2246 normal = SUPER_NORMAL( x, y );
2247 flag = SUPER_FLAG( x, y );
2249 /* set contribution count */
2250 lightLuxel[ 3 ] = 1.0f;
2253 trace.cluster = *cluster;
2254 VectorCopy( origin, trace.origin );
2255 VectorCopy( normal, trace.normal );
2257 /* get light for this sample */
2258 LightContributionToSample( &trace );
2259 VectorCopy( trace.color, lightLuxel );
2261 /* add the contribution to the deluxemap */
2263 VectorCopy( trace.directionContribution, lightDeluxel );
2266 /* check for evilness */
2267 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2269 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2272 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2278 /* don't even bother with everything else if nothing was lit */
2279 if ( totalLighted == 0 ) {
2283 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2284 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2285 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2287 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2289 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2294 VectorClear( total );
2296 /* test 2x2 stamp */
2297 for ( t = 0; t < 4; t++ )
2299 /* set sample coords */
2300 sx = x + tests[ t ][ 0 ];
2301 sy = y + tests[ t ][ 1 ];
2304 cluster = SUPER_CLUSTER( sx, sy );
2305 if ( *cluster < 0 ) {
2311 flag = SUPER_FLAG( sx, sy );
2312 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2313 /* force a lighted/mapped discrepancy so we subsample */
2318 lightLuxel = LIGHT_LUXEL( sx, sy );
2319 VectorAdd( total, lightLuxel, total );
2320 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2325 /* if total color is under a certain amount, then don't bother subsampling */
2326 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2330 /* if all 4 pixels are either in shadow or light, then don't subsample */
2331 if ( lighted != 0 && lighted != mapped ) {
2332 for ( t = 0; t < 4; t++ )
2334 /* set sample coords */
2335 sx = x + tests[ t ][ 0 ];
2336 sy = y + tests[ t ][ 1 ];
2339 cluster = SUPER_CLUSTER( sx, sy );
2340 if ( *cluster < 0 ) {
2343 flag = SUPER_FLAG( sx, sy );
2344 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2347 lightLuxel = LIGHT_LUXEL( sx, sy );
2348 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2349 origin = SUPER_ORIGIN( sx, sy );
2351 /* only subsample shadowed luxels */
2352 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2356 if ( lightRandomSamples ) {
2357 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2360 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2363 *flag |= FLAG_ALREADY_SUBSAMPLED;
2365 /* debug code to colorize subsampled areas to yellow */
2366 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2367 //% VectorSet( luxel, 255, 204, 0 );
2374 /* tertiary pass, apply dirt map (ambient occlusion) */
2377 for ( y = 0; y < lm->sh; y++ )
2379 for ( x = 0; x < lm->sw; x++ )
2382 cluster = SUPER_CLUSTER( x, y );
2383 if ( *cluster < 0 ) {
2387 /* get particulars */
2388 lightLuxel = LIGHT_LUXEL( x, y );
2389 dirt = SUPER_DIRT( x, y );
2391 /* scale light value */
2392 VectorScale( lightLuxel, *dirt, lightLuxel );
2397 /* allocate sampling lightmap storage */
2398 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2399 /* allocate sampling lightmap storage */
2400 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2401 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2402 memset( lm->superLuxels[ lightmapNum ], 0, size );
2406 if ( lightmapNum > 0 ) {
2407 lm->styles[ lightmapNum ] = trace.light->style;
2408 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2411 /* copy to permanent luxels */
2412 for ( y = 0; y < lm->sh; y++ )
2414 for ( x = 0; x < lm->sw; x++ )
2416 /* get cluster and origin */
2417 cluster = SUPER_CLUSTER( x, y );
2418 if ( *cluster < 0 ) {
2421 origin = SUPER_ORIGIN( x, y );
2424 if ( luxelFilterRadius ) {
2426 VectorClear( averageColor );
2427 VectorClear( averageDir );
2430 /* cheaper distance-based filtering */
2431 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2433 if ( sy < 0 || sy >= lm->sh ) {
2437 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2439 if ( sx < 0 || sx >= lm->sw ) {
2443 /* get particulars */
2444 cluster = SUPER_CLUSTER( sx, sy );
2445 if ( *cluster < 0 ) {
2448 lightLuxel = LIGHT_LUXEL( sx, sy );
2449 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2452 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2453 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2455 /* scale luxel by filter weight */
2456 VectorScale( lightLuxel, weight, color );
2457 VectorAdd( averageColor, color, averageColor );
2459 VectorScale( lightDeluxel, weight, direction );
2460 VectorAdd( averageDir, direction, averageDir );
2467 if ( samples <= 0.0f ) {
2471 /* scale into luxel */
2472 luxel = SUPER_LUXEL( lightmapNum, x, y );
2475 /* handle negative light */
2476 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2477 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2478 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2479 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2482 /* handle normal light */
2485 luxel[ 0 ] += averageColor[ 0 ] / samples;
2486 luxel[ 1 ] += averageColor[ 1 ] / samples;
2487 luxel[ 2 ] += averageColor[ 2 ] / samples;
2491 /* scale into luxel */
2492 deluxel = SUPER_DELUXEL( x, y );
2493 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2494 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2495 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2502 /* get particulars */
2503 lightLuxel = LIGHT_LUXEL( x, y );
2504 lightDeluxel = LIGHT_DELUXEL( x, y );
2505 luxel = SUPER_LUXEL( lightmapNum, x, y );
2506 deluxel = SUPER_DELUXEL( x, y );
2508 /* handle negative light */
2509 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2510 VectorScale( averageColor, -1.0f, averageColor );
2516 /* handle negative light */
2517 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2518 VectorSubtract( luxel, lightLuxel, luxel );
2521 /* handle normal light */
2523 VectorAdd( luxel, lightLuxel, luxel );
2527 VectorAdd( deluxel, lightDeluxel, deluxel );
2534 /* free temporary luxels */
2535 if ( lightLuxels != stackLightLuxels ) {
2536 free( lightLuxels );
2540 free( lightDeluxels );
2544 /* free light list */
2545 FreeTraceLights( &trace );
2547 /* floodlight pass */
2548 if ( floodlighty ) {
2549 FloodlightIlluminateLightmap( lm );
2552 if ( debugnormals ) {
2553 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2556 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2560 for ( y = 0; y < lm->sh; y++ )
2562 for ( x = 0; x < lm->sw; x++ )
2565 cluster = SUPER_CLUSTER( x, y );
2566 //% if( *cluster < 0 )
2569 /* get particulars */
2570 luxel = SUPER_LUXEL( lightmapNum, x, y );
2571 normal = SUPER_NORMAL( x, y );
2573 luxel[0] = ( normal[0] * 127 ) + 127;
2574 luxel[1] = ( normal[1] * 127 ) + 127;
2575 luxel[2] = ( normal[2] * 127 ) + 127;
2581 /* -----------------------------------------------------------------
2583 ----------------------------------------------------------------- */
2586 /* walk lightmaps */
2587 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2590 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2594 /* apply dirt to each luxel */
2595 for ( y = 0; y < lm->sh; y++ )
2597 for ( x = 0; x < lm->sw; x++ )
2600 cluster = SUPER_CLUSTER( x, y );
2602 /* get particulars */
2603 luxel = SUPER_LUXEL( lightmapNum, x, y );
2604 dirt = SUPER_DIRT( x, y );
2607 VectorScale( luxel, *dirt, luxel );
2611 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2618 /* -----------------------------------------------------------------
2620 ----------------------------------------------------------------- */
2622 /* walk lightmaps */
2623 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2626 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2630 /* average occluded luxels from neighbors */
2631 for ( y = 0; y < lm->sh; y++ )
2633 for ( x = 0; x < lm->sw; x++ )
2635 /* get particulars */
2636 cluster = SUPER_CLUSTER( x, y );
2637 luxel = SUPER_LUXEL( lightmapNum, x, y );
2638 deluxel = SUPER_DELUXEL( x, y );
2639 normal = SUPER_NORMAL( x, y );
2641 /* determine if filtering is necessary */
2642 filterColor = qfalse;
2644 if ( *cluster < 0 ||
2645 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2646 filterColor = qtrue;
2649 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2653 if ( !filterColor && !filterDir ) {
2657 /* choose seed amount */
2658 VectorClear( averageColor );
2659 VectorClear( averageDir );
2662 /* walk 3x3 matrix */
2663 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2665 if ( sy < 0 || sy >= lm->sh ) {
2669 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2671 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2675 /* get neighbor's particulars */
2676 cluster2 = SUPER_CLUSTER( sx, sy );
2677 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2678 deluxel2 = SUPER_DELUXEL( sx, sy );
2680 /* ignore unmapped/unlit luxels */
2681 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2682 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2686 /* add its distinctiveness to our own */
2687 VectorAdd( averageColor, luxel2, averageColor );
2688 samples += luxel2[ 3 ];
2690 VectorAdd( averageDir, deluxel2, averageDir );
2696 if ( samples <= 0.0f ) {
2700 /* dark lightmap seams */
2702 if ( lightmapNum == 0 ) {
2703 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2709 if ( filterColor ) {
2710 VectorDivide( averageColor, samples, luxel );
2714 VectorDivide( averageDir, samples, deluxel );
2717 /* set cluster to -3 */
2718 if ( *cluster < 0 ) {
2719 *cluster = CLUSTER_FLOODED;
2729 IlluminateVertexes()
2730 light the surface vertexes
2733 #define VERTEX_NUDGE 4.0f
2735 void IlluminateVertexes( int num ){
2736 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2737 int lightmapNum, numAvg;
2738 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2739 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2740 bspDrawSurface_t *ds;
2741 surfaceInfo_t *info;
2743 bspDrawVert_t *verts;
2745 float floodLightAmount;
2749 /* get surface, info, and raw lightmap */
2750 ds = &bspDrawSurfaces[ num ];
2751 info = &surfaceInfos[ num ];
2754 /* -----------------------------------------------------------------
2755 illuminate the vertexes
2756 ----------------------------------------------------------------- */
2758 /* calculate vertex lighting for surfaces without lightmaps */
2759 if ( lm == NULL || cpmaHack ) {
2761 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2762 trace.forceSunlight = info->si->forceSunlight;
2763 trace.recvShadows = info->recvShadows;
2764 trace.numSurfaces = 1;
2765 trace.surfaces = #
2766 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2768 /* twosided lighting */
2769 trace.twoSided = info->si->twoSided;
2771 /* make light list for this surface */
2772 CreateTraceLightsForSurface( num, &trace );
2775 verts = yDrawVerts + ds->firstVert;
2777 memset( avgColors, 0, sizeof( avgColors ) );
2779 /* walk the surface verts */
2780 for ( i = 0; i < ds->numVerts; i++ )
2782 /* get vertex luxel */
2783 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2785 /* color the luxel with raw lightmap num? */
2786 if ( debugSurfaces ) {
2787 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2790 /* color the luxel with luxel origin? */
2791 else if ( debugOrigin ) {
2792 VectorSubtract( info->maxs, info->mins, temp );
2793 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2794 VectorSubtract( origin, lm->mins, temp2 );
2795 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2796 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2797 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2800 /* color the luxel with the normal */
2801 else if ( normalmap ) {
2802 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2803 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2804 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2807 /* illuminate the vertex */
2810 /* clear vertex luxel */
2811 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2813 /* try at initial origin */
2814 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2815 if ( trace.cluster >= 0 ) {
2817 VectorCopy( verts[ i ].xyz, trace.origin );
2818 VectorCopy( verts[ i ].normal, trace.normal );
2821 if ( dirty && !bouncing ) {
2822 dirt = DirtForSample( &trace );
2828 /* jal: floodlight */
2829 floodLightAmount = 0.0f;
2830 VectorClear( floodColor );
2831 if ( floodlighty && !bouncing ) {
2832 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2833 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2837 LightingAtSample( &trace, ds->vertexStyles, colors );
2840 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2843 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2845 /* jal: floodlight */
2846 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2849 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2850 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2851 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2855 /* is this sample bright enough? */
2856 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2857 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2858 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2859 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2860 /* nudge the sample point around a bit */
2861 for ( x = 0; x < 5; x++ )
2863 /* two's complement 0, 1, -1, 2, -2, etc */
2864 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2866 for ( y = 0; y < 5; y++ )
2868 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2870 for ( z = 0; z < 5; z++ )
2872 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2875 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2876 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2877 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2879 /* try at nudged origin */
2880 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2881 if ( trace.cluster < 0 ) {
2886 if ( dirty && !bouncing ) {
2887 dirt = DirtForSample( &trace );
2893 /* jal: floodlight */
2894 floodLightAmount = 0.0f;
2895 VectorClear( floodColor );
2896 if ( floodlighty && !bouncing ) {
2897 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2898 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2902 LightingAtSample( &trace, ds->vertexStyles, colors );
2905 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2908 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2910 /* jal: floodlight */
2911 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2914 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2915 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2918 /* bright enough? */
2919 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2920 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2921 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2922 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2930 /* add to average? */
2931 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2932 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2933 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2934 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2936 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2938 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2939 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2944 /* another happy customer */
2945 numVertsIlluminated++;
2948 /* set average color */
2950 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2951 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2955 VectorCopy( ambientColor, avgColors[ 0 ] );
2958 /* clean up and store vertex color */
2959 for ( i = 0; i < ds->numVerts; i++ )
2961 /* get vertex luxel */
2962 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2964 /* store average in occluded vertexes */
2965 if ( radVertLuxel[ 0 ] < 0.0f ) {
2966 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2968 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2969 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2972 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2977 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2980 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2981 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2984 if ( bouncing || bounce == 0 || !bounceOnly ) {
2985 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2987 if ( !info->si->noVertexLight ) {
2988 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2993 /* free light list */
2994 FreeTraceLights( &trace );
2996 /* return to sender */
3000 /* -----------------------------------------------------------------
3001 reconstitute vertex lighting from the luxels
3002 ----------------------------------------------------------------- */
3004 /* set styles from lightmap */
3005 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3006 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3008 /* get max search radius */
3010 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3012 /* walk the surface verts */
3013 verts = yDrawVerts + ds->firstVert;
3014 for ( i = 0; i < ds->numVerts; i++ )
3016 /* do each lightmap */
3017 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3020 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3024 /* get luxel coords */
3025 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3026 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3030 else if ( x >= lm->sw ) {
3036 else if ( y >= lm->sh ) {
3040 /* get vertex luxels */
3041 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3042 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3044 /* color the luxel with the normal? */
3046 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3047 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3048 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3051 /* color the luxel with surface num? */
3052 else if ( debugSurfaces ) {
3053 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3056 /* divine color from the superluxels */
3059 /* increasing radius */
3060 VectorClear( radVertLuxel );
3062 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3064 /* sample within radius */
3065 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3067 if ( sy < 0 || sy >= lm->sh ) {
3071 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3073 if ( sx < 0 || sx >= lm->sw ) {
3077 /* get luxel particulars */
3078 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3079 cluster = SUPER_CLUSTER( sx, sy );
3080 if ( *cluster < 0 ) {
3084 /* testing: must be brigher than ambient color */
3085 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3088 /* add its distinctiveness to our own */
3089 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3090 samples += luxel[ 3 ];
3096 if ( samples > 0.0f ) {
3097 VectorDivide( radVertLuxel, samples, radVertLuxel );
3100 VectorCopy( ambientColor, radVertLuxel );
3104 /* store into floating point storage */
3105 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3106 numVertsIlluminated++;
3108 /* store into bytes (for vertex approximation) */
3109 if ( !info->si->noVertexLight ) {
3110 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3118 /* -------------------------------------------------------------------------------
3120 light optimization (-fast)
3122 creates a list of lights that will affect a surface and stores it in tw
3123 this is to optimize surface lighting by culling out as many of the
3124 lights in the world as possible from further calculation
3126 ------------------------------------------------------------------------------- */
3130 determines opaque brushes in the world and find sky shaders for sunlight calculations
3133 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3135 unsigned int compileFlags, allCompileFlags;
3138 bspBrushSide_t *side;
3139 bspShader_t *shader;
3144 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3147 if ( opaqueBrushes == NULL ) {
3148 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3152 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3153 numOpaqueBrushes = 0;
3155 /* walk the list of worldspawn brushes */
3156 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3159 b = bspModels[ 0 ].firstBSPBrush + i;
3160 brush = &bspBrushes[ b ];
3162 /* check all sides */
3165 allCompileFlags = ~( 0u );
3166 for ( j = 0; j < brush->numSides && inside; j++ )
3168 /* do bsp shader calculations */
3169 side = &bspBrushSides[ brush->firstSide + j ];
3170 shader = &bspShaders[ side->shaderNum ];
3172 /* get shader info */
3173 si = ShaderInfoForShaderNull( shader->shader );
3178 /* or together compile flags */
3179 compileFlags |= si->compileFlags;
3180 allCompileFlags &= si->compileFlags;
3183 /* determine if this brush is opaque to light */
3184 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3185 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3191 /* emit some statistics */
3192 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3194 void SetupBrushes( void ){
3195 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3202 determines if two clusters are visible to each other using the PVS
3205 qboolean ClusterVisible( int a, int b ){
3211 if ( a < 0 || b < 0 ) {
3221 if ( numBSPVisBytes <= 8 ) {
3226 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3227 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3228 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3231 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3241 borrowed from vlight.c
3244 int PointInLeafNum_r( vec3_t point, int nodenum ){
3251 while ( nodenum >= 0 )
3253 node = &bspNodes[ nodenum ];
3254 plane = &bspPlanes[ node->planeNum ];
3255 dist = DotProduct( point, plane->normal ) - plane->dist;
3257 nodenum = node->children[ 0 ];
3259 else if ( dist < -0.1 ) {
3260 nodenum = node->children[ 1 ];
3264 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3265 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3268 nodenum = node->children[ 1 ];
3272 leafnum = -nodenum - 1;
3280 borrowed from vlight.c
3283 int PointInLeafNum( vec3_t point ){
3284 return PointInLeafNum_r( point, 0 );
3290 ClusterVisibleToPoint() - ydnar
3291 returns qtrue if point can "see" cluster
3294 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3298 /* get leafNum for point */
3299 pointCluster = ClusterForPoint( point );
3300 if ( pointCluster < 0 ) {
3305 return ClusterVisible( pointCluster, cluster );
3311 ClusterForPoint() - ydnar
3312 returns the pvs cluster for point
3315 int ClusterForPoint( vec3_t point ){
3319 /* get leafNum for point */
3320 leafNum = PointInLeafNum( point );
3321 if ( leafNum < 0 ) {
3325 /* return the cluster */
3326 return bspLeafs[ leafNum ].cluster;
3332 ClusterForPointExt() - ydnar
3333 also takes brushes into account for occlusion testing
3336 int ClusterForPointExt( vec3_t point, float epsilon ){
3337 int i, j, b, leafNum, cluster;
3340 int *brushes, numBSPBrushes;
3346 /* get leaf for point */
3347 leafNum = PointInLeafNum( point );
3348 if ( leafNum < 0 ) {
3351 leaf = &bspLeafs[ leafNum ];
3353 /* get the cluster */
3354 cluster = leaf->cluster;
3355 if ( cluster < 0 ) {
3359 /* transparent leaf, so check point against all brushes in the leaf */
3360 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3361 numBSPBrushes = leaf->numBSPLeafBrushes;
3362 for ( i = 0; i < numBSPBrushes; i++ )
3366 if ( b > maxOpaqueBrush ) {
3369 brush = &bspBrushes[ b ];
3370 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3374 /* check point against all planes */
3376 for ( j = 0; j < brush->numSides && inside; j++ )
3378 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3379 dot = DotProduct( point, plane->normal );
3381 if ( dot > epsilon ) {
3386 /* if inside, return bogus cluster */
3392 /* if the point made it this far, it's not inside any opaque brushes */
3399 ClusterForPointExtFilter() - ydnar
3400 adds cluster checking against a list of known valid clusters
3403 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3407 /* get cluster for point */
3408 cluster = ClusterForPointExt( point, epsilon );
3410 /* check if filtering is necessary */
3411 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3416 for ( i = 0; i < numClusters; i++ )
3418 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3430 ShaderForPointInLeaf() - ydnar
3431 checks a point against all brushes in a leaf, returning the shader of the brush
3432 also sets the cumulative surface and content flags for the brush hit
3435 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3439 int *brushes, numBSPBrushes;
3442 bspBrushSide_t *side;
3444 bspShader_t *shader;
3445 int allSurfaceFlags, allContentFlags;
3448 /* clear things out first */
3453 if ( leafNum < 0 ) {
3456 leaf = &bspLeafs[ leafNum ];
3458 /* transparent leaf, so check point against all brushes in the leaf */
3459 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3460 numBSPBrushes = leaf->numBSPLeafBrushes;
3461 for ( i = 0; i < numBSPBrushes; i++ )
3464 brush = &bspBrushes[ brushes[ i ] ];
3466 /* check point against all planes */
3468 allSurfaceFlags = 0;
3469 allContentFlags = 0;
3470 for ( j = 0; j < brush->numSides && inside; j++ )
3472 side = &bspBrushSides[ brush->firstSide + j ];
3473 plane = &bspPlanes[ side->planeNum ];
3474 dot = DotProduct( point, plane->normal );
3476 if ( dot > epsilon ) {
3481 shader = &bspShaders[ side->shaderNum ];
3482 allSurfaceFlags |= shader->surfaceFlags;
3483 allContentFlags |= shader->contentFlags;
3487 /* handle if inside */
3489 /* if there are desired flags, check for same and continue if they aren't matched */
3490 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3493 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3497 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3498 *surfaceFlags = allSurfaceFlags;
3499 *contentFlags = allContentFlags;
3500 return brush->shaderNum;
3504 /* if the point made it this far, it's not inside any brushes */
3512 chops a bounding box by the plane defined by origin and normal
3513 returns qfalse if the bounds is entirely clipped away
3515 this is not exactly the fastest way to do this...
3518 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3519 /* FIXME: rewrite this so it doesn't use bloody brushes */
3527 calculates each light's effective envelope,
3528 taking into account brightness, type, and pvs.
3531 #define LIGHT_EPSILON 0.125f
3532 #define LIGHT_NUDGE 2.0f
3534 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3535 int i, x, y, z, x1, y1, z1;
3536 light_t *light, *light2, **owner;
3538 vec3_t origin, dir, mins, maxs;
3539 float radius, intensity;
3540 light_t *buckets[ 256 ];
3543 /* early out for weird cases where there are no lights */
3544 if ( lights == NULL ) {
3549 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3553 numCulledLights = 0;
3555 while ( *owner != NULL )
3560 /* handle negative lights */
3561 if ( light->photons < 0.0f || light->add < 0.0f ) {
3562 light->photons *= -1.0f;
3563 light->add *= -1.0f;
3564 light->flags |= LIGHT_NEGATIVE;
3568 if ( light->type == EMIT_SUN ) {
3571 light->envelope = MAX_WORLD_COORD * 8.0f;
3572 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3573 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3576 /* everything else */
3579 /* get pvs cluster for light */
3580 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3582 /* invalid cluster? */
3583 if ( light->cluster < 0 ) {
3584 /* nudge the sample point around a bit */
3585 for ( x = 0; x < 4; x++ )
3587 /* two's complement 0, 1, -1, 2, -2, etc */
3588 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3590 for ( y = 0; y < 4; y++ )
3592 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3594 for ( z = 0; z < 4; z++ )
3596 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3599 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3600 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3601 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3603 /* try at nudged origin */
3604 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3605 if ( light->cluster < 0 ) {
3610 VectorCopy( origin, light->origin );
3616 /* only calculate for lights in pvs and outside of opaque brushes */
3617 if ( light->cluster >= 0 ) {
3618 /* set light fast flag */
3620 light->flags |= LIGHT_FAST_TEMP;
3623 light->flags &= ~LIGHT_FAST_TEMP;
3625 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3626 light->flags |= LIGHT_FAST_TEMP;
3628 if ( light->si && light->si->noFast ) {
3629 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3632 /* clear light envelope */
3633 light->envelope = 0;
3635 /* handle area lights */
3636 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3637 light->envelope = MAX_WORLD_COORD * 8.0f;
3639 /* check for fast mode */
3640 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3641 /* ugly hack to calculate extent for area lights, but only done once */
3642 VectorScale( light->normal, -1.0f, dir );
3643 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3647 VectorMA( light->origin, radius, light->normal, origin );
3648 factor = PointToPolygonFormFactor( origin, dir, light->w );
3649 if ( factor < 0.0f ) {
3652 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3653 light->envelope = radius;
3659 intensity = light->photons; /* hopefully not used */
3664 intensity = light->photons;
3668 if ( light->envelope <= 0.0f ) {
3669 /* solve distance for non-distance lights */
3670 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3671 light->envelope = MAX_WORLD_COORD * 8.0f;
3674 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3675 /* solve distance for linear lights */
3676 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3677 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3681 add = angle * light->photons * linearScale - (dist * light->fade);
3682 T = (light->photons * linearScale) - (dist * light->fade);
3683 T + (dist * light->fade) = (light->photons * linearScale);
3684 dist * light->fade = (light->photons * linearScale) - T;
3685 dist = ((light->photons * linearScale) - T) / light->fade;
3688 /* solve for inverse square falloff */
3690 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3694 add = light->photons / (dist * dist);
3695 T = light->photons / (dist * dist);
3696 T * (dist * dist) = light->photons;
3697 dist = sqrt( light->photons / T );
3702 /* solve distance for linear lights */
3703 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3704 light->envelope = ( intensity * linearScale ) / light->fade;
3707 /* can't cull these */
3709 light->envelope = MAX_WORLD_COORD * 8.0f;
3714 /* chop radius against pvs */
3717 ClearBounds( mins, maxs );
3719 /* check all leaves */
3720 for ( i = 0; i < numBSPLeafs; i++ )
3723 leaf = &bspLeafs[ i ];
3726 if ( leaf->cluster < 0 ) {
3729 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3733 /* add this leafs bbox to the bounds */
3734 VectorCopy( leaf->mins, origin );
3735 AddPointToBounds( origin, mins, maxs );
3736 VectorCopy( leaf->maxs, origin );
3737 AddPointToBounds( origin, mins, maxs );
3740 /* test to see if bounds encompass light */
3741 for ( i = 0; i < 3; i++ )
3743 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3744 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3745 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3746 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3747 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3748 AddPointToBounds( light->origin, mins, maxs );
3752 /* chop the bounds by a plane for area lights and spotlights */
3753 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3754 ChopBounds( mins, maxs, light->origin, light->normal );
3758 VectorCopy( mins, light->mins );
3759 VectorCopy( maxs, light->maxs );
3761 /* reflect bounds around light origin */
3762 //% VectorMA( light->origin, -1.0f, origin, origin );
3763 VectorScale( light->origin, 2, origin );
3764 VectorSubtract( origin, maxs, origin );
3765 AddPointToBounds( origin, mins, maxs );
3766 //% VectorMA( light->origin, -1.0f, mins, origin );
3767 VectorScale( light->origin, 2, origin );
3768 VectorSubtract( origin, mins, origin );
3769 AddPointToBounds( origin, mins, maxs );
3771 /* calculate spherical bounds */
3772 VectorSubtract( maxs, light->origin, dir );
3773 radius = (float) VectorLength( dir );
3775 /* if this radius is smaller than the envelope, then set the envelope to it */
3776 if ( radius < light->envelope ) {
3777 light->envelope = radius;
3778 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3781 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3784 /* add grid/surface only check */
3786 if ( !( light->flags & LIGHT_GRID ) ) {
3787 light->envelope = 0.0f;
3792 if ( !( light->flags & LIGHT_SURFACES ) ) {
3793 light->envelope = 0.0f;
3799 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3801 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3803 /* delete the light */
3805 *owner = light->next;
3806 if ( light->w != NULL ) {
3814 /* square envelope */
3815 light->envelope2 = ( light->envelope * light->envelope );
3817 /* increment light count */
3820 /* set next light */
3821 owner = &( ( **owner ).next );
3824 /* bucket sort lights by style */
3825 memset( buckets, 0, sizeof( buckets ) );
3827 for ( light = lights; light != NULL; light = light2 )
3829 /* get next light */
3830 light2 = light->next;
3832 /* filter into correct bucket */
3833 light->next = buckets[ light->style ];
3834 buckets[ light->style ] = light;
3836 /* if any styled light is present, automatically set nocollapse */
3837 if ( light->style != LS_NORMAL ) {
3842 /* filter back into light list */
3844 for ( i = 255; i >= 0; i-- )
3847 for ( light = buckets[ i ]; light != NULL; light = light2 )
3849 light2 = light->next;
3850 light->next = lights;
3855 /* emit some statistics */
3856 Sys_Printf( "%9d total lights\n", numLights );
3857 Sys_Printf( "%9d culled lights\n", numCulledLights );
3863 CreateTraceLightsForBounds()
3864 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3867 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3870 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3871 float radius, dist, length;
3874 /* potential pre-setup */
3875 if ( numLights == 0 ) {
3876 SetupEnvelopes( qfalse, fast );
3880 //% 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 ] );
3882 /* allocate the light list */
3883 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3884 trace->numLights = 0;
3886 /* calculate spherical bounds */
3887 VectorAdd( mins, maxs, origin );
3888 VectorScale( origin, 0.5f, origin );
3889 VectorSubtract( maxs, origin, dir );
3890 radius = (float) VectorLength( dir );
3892 /* get length of normal vector */
3893 if ( normal != NULL ) {
3894 length = VectorLength( normal );
3898 normal = nullVector;
3902 /* test each light and see if it reaches the sphere */
3903 /* note: the attenuation code MUST match LightingAtSample() */
3904 for ( light = lights; light; light = light->next )
3906 /* check zero sized envelope */
3907 if ( light->envelope <= 0 ) {
3908 lightsEnvelopeCulled++;
3913 if ( !( light->flags & flags ) ) {
3917 /* sunlight skips all this nonsense */
3918 if ( light->type != EMIT_SUN ) {
3924 /* check against pvs cluster */
3925 if ( numClusters > 0 && clusters != NULL ) {
3926 for ( i = 0; i < numClusters; i++ )
3928 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3934 if ( i == numClusters ) {
3935 lightsClusterCulled++;
3940 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3941 VectorSubtract( light->origin, origin, dir );
3942 dist = VectorLength( dir );
3943 dist -= light->envelope;
3946 lightsEnvelopeCulled++;
3950 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3953 for ( i = 0; i < 3; i++ )
3955 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3960 lightsBoundsCulled++;
3966 /* planar surfaces (except twosided surfaces) have a couple more checks */
3967 if ( length > 0.0f && trace->twoSided == qfalse ) {
3968 /* lights coplanar with a surface won't light it */
3969 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3970 lightsPlaneCulled++;
3974 /* check to see if light is behind the plane */
3975 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3976 lightsPlaneCulled++;
3981 /* add this light */
3982 trace->lights[ trace->numLights++ ] = light;
3985 /* make last night null */
3986 trace->lights[ trace->numLights ] = NULL;
3991 void FreeTraceLights( trace_t *trace ){
3992 if ( trace->lights != NULL ) {
3993 free( trace->lights );
4000 CreateTraceLightsForSurface()
4001 creates a list of lights that can potentially affect a drawsurface
4004 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4006 vec3_t mins, maxs, normal;
4008 bspDrawSurface_t *ds;
4009 surfaceInfo_t *info;
4017 /* get drawsurface and info */
4018 ds = &bspDrawSurfaces[ num ];
4019 info = &surfaceInfos[ num ];
4021 /* get the mins/maxs for the dsurf */
4022 ClearBounds( mins, maxs );
4023 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4024 for ( i = 0; i < ds->numVerts; i++ )
4026 dv = &yDrawVerts[ ds->firstVert + i ];
4027 AddPointToBounds( dv->xyz, mins, maxs );
4028 if ( !VectorCompare( dv->normal, normal ) ) {
4029 VectorClear( normal );
4033 /* create the lights for the bounding box */
4034 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4037 /////////////////////////////////////////////////////////////
4039 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4040 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4041 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4042 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4044 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4045 static int numFloodVectors = 0;
4047 void SetupFloodLight( void ){
4049 float angle, elevation, angleStep, elevationStep;
4051 double v1,v2,v3,v4,v5,v6;
4054 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4056 /* calculate angular steps */
4057 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4058 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4062 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4064 /* iterate elevation */
4065 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4067 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4068 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4069 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4074 /* emit some statistics */
4075 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4078 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4080 if ( value[ 0 ] != '\0' ) {
4082 v4 = floodlightDistance;
4083 v5 = floodlightIntensity;
4084 v6 = floodlightDirectionScale;
4086 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4088 floodlightRGB[0] = v1;
4089 floodlightRGB[1] = v2;
4090 floodlightRGB[2] = v3;
4092 if ( VectorLength( floodlightRGB ) == 0 ) {
4093 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4106 floodlightDistance = v4;
4107 floodlightIntensity = v5;
4108 floodlightDirectionScale = v6;
4110 floodlighty = qtrue;
4111 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4115 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4118 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4119 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4120 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4122 ColorNormalize( floodlightRGB,floodlightRGB );
4126 FloodLightForSample()
4127 calculates floodlight value for a given sample
4128 once again, kudos to the dirtmapping coder
4131 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4136 float gatherLight, outLight;
4137 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4145 if ( trace == NULL || trace->cluster < 0 ) {
4151 dd = floodLightDistance;
4152 VectorCopy( trace->normal, normal );
4154 /* check if the normal is aligned to the world-up */
4155 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4156 if ( normal[ 2 ] == 1.0f ) {
4157 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4158 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4160 else if ( normal[ 2 ] == -1.0f ) {
4161 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4162 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4167 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4168 CrossProduct( normal, worldUp, myRt );
4169 VectorNormalize( myRt, myRt );
4170 CrossProduct( myRt, normal, myUp );
4171 VectorNormalize( myUp, myUp );
4174 /* vortex: optimise floodLightLowQuality a bit */
4175 if ( floodLightLowQuality == qtrue ) {
4176 /* iterate through ordered vectors */
4177 for ( i = 0; i < numFloodVectors; i++ )
4178 if ( rand() % 10 != 0 ) {
4184 /* iterate through ordered vectors */
4185 for ( i = 0; i < numFloodVectors; i++ )
4189 /* transform vector into tangent space */
4190 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4191 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4192 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4195 VectorMA( trace->origin, dd, direction, trace->end );
4197 //VectorMA( trace->origin, 1, direction, trace->origin );
4199 SetupTrace( trace );
4200 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4205 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4206 contribution = 1.0f;
4208 else if ( trace->opaque ) {
4209 VectorSubtract( trace->hit, trace->origin, displacement );
4210 d = VectorLength( displacement );
4212 // d=trace->distance;
4213 //if (d>256) gatherDirt+=1;
4214 contribution = d / dd;
4215 if ( contribution > 1 ) {
4216 contribution = 1.0f;
4219 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4222 gatherLight += contribution;
4227 if ( gatherLight <= 0.0f ) {
4236 gatherLight /= ( sub );
4238 outLight = gatherLight;
4239 if ( outLight > 1.0f ) {
4243 /* return to sender */
4248 FloodLightRawLightmap
4249 lighttracer style ambient occlusion light hack.
4250 Kudos to the dirtmapping author for most of this source.
4251 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4252 VorteX: fixed problems with deluxemapping
4255 // floodlight pass on a lightmap
4256 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4257 int i, x, y, *cluster;
4258 float *origin, *normal, *floodlight, floodLightAmount;
4259 surfaceInfo_t *info;
4262 // float samples, average, *floodlight2;
4264 memset( &trace,0,sizeof( trace_t ) );
4267 trace.testOcclusion = qtrue;
4268 trace.forceSunlight = qfalse;
4269 trace.twoSided = qtrue;
4270 trace.recvShadows = lm->recvShadows;
4271 trace.numSurfaces = lm->numLightSurfaces;
4272 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4273 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4274 trace.testAll = qfalse;
4275 trace.distance = 1024;
4277 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4278 //trace.twoSided = qfalse;
4279 for ( i = 0; i < trace.numSurfaces; i++ )
4282 info = &surfaceInfos[ trace.surfaces[ i ] ];
4284 /* check twosidedness */
4285 if ( info->si->twoSided ) {
4286 trace.twoSided = qtrue;
4291 /* gather floodlight */
4292 for ( y = 0; y < lm->sh; y++ )
4294 for ( x = 0; x < lm->sw; x++ )
4297 cluster = SUPER_CLUSTER( x, y );
4298 origin = SUPER_ORIGIN( x, y );
4299 normal = SUPER_NORMAL( x, y );
4300 floodlight = SUPER_FLOODLIGHT( x, y );
4302 /* set default dirt */
4305 /* only look at mapped luxels */
4306 if ( *cluster < 0 ) {
4311 trace.cluster = *cluster;
4312 VectorCopy( origin, trace.origin );
4313 VectorCopy( normal, trace.normal );
4315 /* get floodlight */
4316 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4318 /* add floodlight */
4319 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4320 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4321 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4322 floodlight[3] += floodlightDirectionScale;
4326 /* testing no filtering */
4332 for ( y = 0; y < lm->sh; y++ )
4334 for ( x = 0; x < lm->sw; x++ )
4337 cluster = SUPER_CLUSTER( x, y );
4338 floodlight = SUPER_FLOODLIGHT( x, y );
4340 /* filter dirt by adjacency to unmapped luxels */
4341 average = *floodlight;
4343 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4345 if ( sy < 0 || sy >= lm->sh ) {
4349 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4351 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4355 /* get neighboring luxel */
4356 cluster = SUPER_CLUSTER( sx, sy );
4357 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4358 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4363 average += *floodlight2;
4368 if ( samples <= 0.0f ) {
4374 if ( samples <= 0.0f ) {
4379 *floodlight = average / samples;
4385 void FloodLightRawLightmap( int rawLightmapNum ){
4388 /* bail if this number exceeds the number of raw lightmaps */
4389 if ( rawLightmapNum >= numRawLightmaps ) {
4393 lm = &rawLightmaps[ rawLightmapNum ];
4396 if ( floodlighty && floodlightIntensity ) {
4397 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4401 if ( lm->floodlightIntensity ) {
4402 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4403 numSurfacesFloodlighten += 1;
4407 void FloodlightRawLightmaps(){
4408 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4409 numSurfacesFloodlighten = 0;
4410 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4411 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4415 FloodLightIlluminate()
4416 illuminate floodlight into lightmap luxels
4419 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4420 float *luxel, *floodlight, *deluxel, *normal;
4423 int x, y, lightmapNum;
4425 /* walk lightmaps */
4426 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4429 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4433 /* apply floodlight to each luxel */
4434 for ( y = 0; y < lm->sh; y++ )
4436 for ( x = 0; x < lm->sw; x++ )
4438 /* get floodlight */
4439 floodlight = SUPER_FLOODLIGHT( x, y );
4440 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4445 cluster = SUPER_CLUSTER( x, y );
4447 /* only process mapped luxels */
4448 if ( *cluster < 0 ) {
4452 /* get particulars */
4453 luxel = SUPER_LUXEL( lightmapNum, x, y );
4454 deluxel = SUPER_DELUXEL( x, y );
4456 /* add to lightmap */
4457 luxel[0] += floodlight[0];
4458 luxel[1] += floodlight[1];
4459 luxel[2] += floodlight[2];
4461 if ( luxel[3] == 0 ) {
4465 /* add to deluxemap */
4466 if ( deluxemap && floodlight[3] > 0 ) {
4469 normal = SUPER_NORMAL( x, y );
4470 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4472 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4473 if ( brightness < 0.00390625f ) {
4474 brightness = 0.00390625f;
4477 VectorScale( normal, brightness, lightvector );
4478 VectorAdd( deluxel, lightvector, deluxel );