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, *origin2, *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 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1748 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1750 /* calulate y vector */
1751 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1752 cluster = SUPER_CLUSTER( x, y );
1753 origin = SUPER_ORIGIN( x, y );
1754 //% normal = SUPER_NORMAL( x, y );
1755 cluster2 = SUPER_CLUSTER( x, y + 1 );
1756 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1757 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1759 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1760 cluster = SUPER_CLUSTER( x, y - 1 );
1761 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1762 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1763 cluster2 = SUPER_CLUSTER( x, y );
1764 origin2 = SUPER_ORIGIN( x, y );
1765 //% normal2 = SUPER_NORMAL( x, y );
1768 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1771 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1773 /* calculate new origin */
1774 for ( i = 0; i < 3; i++ )
1775 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1778 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1779 if ( *sampleCluster < 0 ) {
1783 /* calculate new normal */
1784 normal = SUPER_NORMAL( x, y );
1785 VectorCopy( normal, sampleNormal );
1793 SubsampleRawLuxel_r()
1794 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1797 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1798 int b, samples, mapped, lighted;
1801 vec3_t deluxel[ 3 ];
1802 vec3_t origin[ 4 ], normal[ 4 ];
1803 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1804 vec3_t color, direction = { 0, 0, 0 }, total;
1808 if ( lightLuxel[ 3 ] >= lightSamples ) {
1813 VectorClear( total );
1817 /* make 2x2 subsample stamp */
1818 for ( b = 0; b < 4; b++ )
1821 VectorCopy( sampleOrigin, origin[ b ] );
1823 /* calculate position */
1824 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1830 /* increment sample count */
1831 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1834 trace->cluster = *cluster;
1835 VectorCopy( origin[ b ], trace->origin );
1836 VectorCopy( normal[ b ], trace->normal );
1840 LightContributionToSample( trace );
1841 if ( trace->forceSubsampling > 1.0f ) {
1842 /* alphashadow: we subsample as deep as we can */
1848 /* add to totals (fixme: make contrast function) */
1849 VectorCopy( trace->color, luxel[ b ] );
1850 if ( lightDeluxel ) {
1851 VectorCopy( trace->directionContribution, deluxel[ b ] );
1853 VectorAdd( total, trace->color, total );
1854 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1859 /* subsample further? */
1860 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1861 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1862 lighted != 0 && lighted != mapped ) {
1863 for ( b = 0; b < 4; b++ )
1865 if ( cluster[ b ] < 0 ) {
1868 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1873 //% VectorClear( color );
1875 VectorCopy( lightLuxel, color );
1876 if ( lightDeluxel ) {
1877 VectorCopy( lightDeluxel, direction );
1880 for ( b = 0; b < 4; b++ )
1882 if ( cluster[ b ] < 0 ) {
1885 VectorAdd( color, luxel[ b ], color );
1886 if ( lightDeluxel ) {
1887 VectorAdd( direction, deluxel[ b ], direction );
1893 if ( samples > 0 ) {
1895 color[ 0 ] /= samples;
1896 color[ 1 ] /= samples;
1897 color[ 2 ] /= samples;
1900 VectorCopy( color, lightLuxel );
1901 lightLuxel[ 3 ] += 1.0f;
1903 if ( lightDeluxel ) {
1904 direction[ 0 ] /= samples;
1905 direction[ 1 ] /= samples;
1906 direction[ 2 ] /= samples;
1907 VectorCopy( direction, lightDeluxel );
1912 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1913 static void GaussLikeRandom( float sigma, float *x, float *y ){
1915 r = Random() * 2 * Q_PI;
1916 *x = sigma * 2.73861278752581783822 * cos( r );
1917 *y = sigma * 2.73861278752581783822 * sin( r );
1924 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1927 vec3_t origin, normal;
1928 vec3_t total, totaldirection;
1931 VectorClear( total );
1932 VectorClear( totaldirection );
1934 for ( b = 0; b < lightSamples; ++b )
1937 VectorCopy( sampleOrigin, origin );
1938 GaussLikeRandom( bias, &dx, &dy );
1940 /* calculate position */
1941 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1947 trace->cluster = cluster;
1948 VectorCopy( origin, trace->origin );
1949 VectorCopy( normal, trace->normal );
1951 LightContributionToSample( trace );
1952 VectorAdd( total, trace->color, total );
1953 if ( lightDeluxel ) {
1954 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1961 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1962 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1963 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1965 if ( lightDeluxel ) {
1966 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1967 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1968 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1976 IlluminateRawLightmap()
1977 illuminates the luxels
1980 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1981 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1982 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1984 void IlluminateRawLightmap( int rawLightmapNum ){
1985 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1986 int *cluster, *cluster2, mapped, lighted, totalLighted;
1987 size_t llSize, ldSize;
1989 surfaceInfo_t *info;
1990 qboolean filterColor, filterDir;
1992 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1993 unsigned char *flag;
1994 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1995 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1996 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1998 float stackLightLuxels[ STACK_LL_SIZE ];
2001 /* bail if this number exceeds the number of raw lightmaps */
2002 if ( rawLightmapNum >= numRawLightmaps ) {
2007 lm = &rawLightmaps[ rawLightmapNum ];
2010 trace.testOcclusion = !noTrace;
2011 trace.forceSunlight = qfalse;
2012 trace.recvShadows = lm->recvShadows;
2013 trace.numSurfaces = lm->numLightSurfaces;
2014 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2015 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2017 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2018 trace.twoSided = qfalse;
2019 for ( i = 0; i < trace.numSurfaces; i++ )
2022 info = &surfaceInfos[ trace.surfaces[ i ] ];
2024 /* check twosidedness */
2025 if ( info->si->twoSided ) {
2026 trace.twoSided = qtrue;
2031 /* create a culled light list for this raw lightmap */
2032 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2034 /* -----------------------------------------------------------------
2036 ----------------------------------------------------------------- */
2039 numLuxelsIlluminated += ( lm->sw * lm->sh );
2041 /* test debugging state */
2042 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2043 /* debug fill the luxels */
2044 for ( y = 0; y < lm->sh; y++ )
2046 for ( x = 0; x < lm->sw; x++ )
2049 cluster = SUPER_CLUSTER( x, y );
2051 /* only fill mapped luxels */
2052 if ( *cluster < 0 ) {
2056 /* get particulars */
2057 luxel = SUPER_LUXEL( 0, x, y );
2058 origin = SUPER_ORIGIN( x, y );
2059 normal = SUPER_NORMAL( x, y );
2061 /* color the luxel with raw lightmap num? */
2062 if ( debugSurfaces ) {
2063 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2066 /* color the luxel with lightmap axis? */
2067 else if ( debugAxis ) {
2068 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2069 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2070 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2073 /* color the luxel with luxel cluster? */
2074 else if ( debugCluster ) {
2075 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2078 /* color the luxel with luxel origin? */
2079 else if ( debugOrigin ) {
2080 VectorSubtract( lm->maxs, lm->mins, temp );
2081 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2082 VectorSubtract( origin, lm->mins, temp2 );
2083 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2084 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2085 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2088 /* color the luxel with the normal */
2089 else if ( normalmap ) {
2090 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2091 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2092 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2095 /* otherwise clear it */
2097 VectorClear( luxel );
2107 /* allocate temporary per-light luxel storage */
2108 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2109 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2110 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2111 lightLuxels = stackLightLuxels;
2114 lightLuxels = safe_malloc( llSize );
2117 lightDeluxels = safe_malloc( ldSize );
2120 lightDeluxels = NULL;
2124 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2126 /* set ambient color */
2127 for ( y = 0; y < lm->sh; y++ )
2129 for ( x = 0; x < lm->sw; x++ )
2132 cluster = SUPER_CLUSTER( x, y );
2133 luxel = SUPER_LUXEL( 0, x, y );
2134 normal = SUPER_NORMAL( x, y );
2135 deluxel = SUPER_DELUXEL( x, y );
2137 /* blacken unmapped clusters */
2138 if ( *cluster < 0 ) {
2139 VectorClear( luxel );
2145 VectorCopy( ambientColor, luxel );
2147 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2149 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2150 if ( brightness < 0.00390625f ) {
2151 brightness = 0.00390625f;
2154 VectorScale( normal, brightness, deluxel );
2161 /* clear styled lightmaps */
2162 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2163 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2165 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2166 memset( lm->superLuxels[ lightmapNum ], 0, size );
2170 /* debugging code */
2171 //% if( trace.numLights <= 0 )
2172 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2174 /* walk light list */
2175 for ( i = 0; i < trace.numLights; i++ )
2178 trace.light = trace.lights[ i ];
2181 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2183 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2184 lm->styles[ lightmapNum ] == LS_NONE ) {
2189 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2190 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2191 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2196 memset( lightLuxels, 0, llSize );
2198 memset( lightDeluxels, 0, ldSize );
2202 /* determine filter radius */
2203 filterRadius = lm->filterRadius > trace.light->filterRadius
2205 : trace.light->filterRadius;
2206 if ( filterRadius < 0.0f ) {
2207 filterRadius = 0.0f;
2210 /* set luxel filter radius */
2211 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2212 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2213 luxelFilterRadius = 1;
2216 /* allocate sampling flags storage */
2217 if ( lightSamples > 1 || lightRandomSamples ) {
2218 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2219 if ( lm->superFlags == NULL ) {
2220 lm->superFlags = safe_malloc( size );
2222 memset( (void *) lm->superFlags, 0, size );
2225 /* initial pass, one sample per luxel */
2226 for ( y = 0; y < lm->sh; y++ )
2228 for ( x = 0; x < lm->sw; x++ )
2231 cluster = SUPER_CLUSTER( x, y );
2232 if ( *cluster < 0 ) {
2236 /* get particulars */
2237 lightLuxel = LIGHT_LUXEL( x, y );
2238 lightDeluxel = LIGHT_DELUXEL( x, y );
2239 origin = SUPER_ORIGIN( x, y );
2240 normal = SUPER_NORMAL( x, y );
2241 flag = SUPER_FLAG( x, y );
2243 /* set contribution count */
2244 lightLuxel[ 3 ] = 1.0f;
2247 trace.cluster = *cluster;
2248 VectorCopy( origin, trace.origin );
2249 VectorCopy( normal, trace.normal );
2251 /* get light for this sample */
2252 LightContributionToSample( &trace );
2253 VectorCopy( trace.color, lightLuxel );
2255 /* add the contribution to the deluxemap */
2257 VectorCopy( trace.directionContribution, lightDeluxel );
2260 /* check for evilness */
2261 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) ) {
2263 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2266 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2272 /* don't even bother with everything else if nothing was lit */
2273 if ( totalLighted == 0 ) {
2277 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2278 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2279 if ( lightSamples > 1 || lightRandomSamples ) {
2281 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2283 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2288 VectorClear( total );
2290 /* test 2x2 stamp */
2291 for ( t = 0; t < 4; t++ )
2293 /* set sample coords */
2294 sx = x + tests[ t ][ 0 ];
2295 sy = y + tests[ t ][ 1 ];
2298 cluster = SUPER_CLUSTER( sx, sy );
2299 if ( *cluster < 0 ) {
2305 flag = SUPER_FLAG( sx, sy );
2306 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2307 /* force a lighted/mapped discrepancy so we subsample */
2312 lightLuxel = LIGHT_LUXEL( sx, sy );
2313 VectorAdd( total, lightLuxel, total );
2314 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2319 /* if total color is under a certain amount, then don't bother subsampling */
2320 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2324 /* if all 4 pixels are either in shadow or light, then don't subsample */
2325 if ( lighted != 0 && lighted != mapped ) {
2326 for ( t = 0; t < 4; t++ )
2328 /* set sample coords */
2329 sx = x + tests[ t ][ 0 ];
2330 sy = y + tests[ t ][ 1 ];
2333 cluster = SUPER_CLUSTER( sx, sy );
2334 if ( *cluster < 0 ) {
2337 flag = SUPER_FLAG( sx, sy );
2338 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2341 lightLuxel = LIGHT_LUXEL( sx, sy );
2342 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2343 origin = SUPER_ORIGIN( sx, sy );
2345 /* only subsample shadowed luxels */
2346 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2350 if ( lightRandomSamples ) {
2351 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2354 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2357 *flag |= FLAG_ALREADY_SUBSAMPLED;
2359 /* debug code to colorize subsampled areas to yellow */
2360 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2361 //% VectorSet( luxel, 255, 204, 0 );
2368 /* tertiary pass, apply dirt map (ambient occlusion) */
2371 for ( y = 0; y < lm->sh; y++ )
2373 for ( x = 0; x < lm->sw; x++ )
2376 cluster = SUPER_CLUSTER( x, y );
2377 if ( *cluster < 0 ) {
2381 /* get particulars */
2382 lightLuxel = LIGHT_LUXEL( x, y );
2383 dirt = SUPER_DIRT( x, y );
2385 /* scale light value */
2386 VectorScale( lightLuxel, *dirt, lightLuxel );
2391 /* allocate sampling lightmap storage */
2392 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2393 /* allocate sampling lightmap storage */
2394 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2395 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2396 memset( lm->superLuxels[ lightmapNum ], 0, size );
2400 if ( lightmapNum > 0 ) {
2401 lm->styles[ lightmapNum ] = trace.light->style;
2402 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2405 /* copy to permanent luxels */
2406 for ( y = 0; y < lm->sh; y++ )
2408 for ( x = 0; x < lm->sw; x++ )
2410 /* get cluster and origin */
2411 cluster = SUPER_CLUSTER( x, y );
2412 if ( *cluster < 0 ) {
2415 origin = SUPER_ORIGIN( x, y );
2418 if ( luxelFilterRadius ) {
2420 VectorClear( averageColor );
2421 VectorClear( averageDir );
2424 /* cheaper distance-based filtering */
2425 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2427 if ( sy < 0 || sy >= lm->sh ) {
2431 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2433 if ( sx < 0 || sx >= lm->sw ) {
2437 /* get particulars */
2438 cluster = SUPER_CLUSTER( sx, sy );
2439 if ( *cluster < 0 ) {
2442 lightLuxel = LIGHT_LUXEL( sx, sy );
2443 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2446 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2447 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2449 /* scale luxel by filter weight */
2450 VectorScale( lightLuxel, weight, color );
2451 VectorAdd( averageColor, color, averageColor );
2453 VectorScale( lightDeluxel, weight, direction );
2454 VectorAdd( averageDir, direction, averageDir );
2461 if ( samples <= 0.0f ) {
2465 /* scale into luxel */
2466 luxel = SUPER_LUXEL( lightmapNum, x, y );
2469 /* handle negative light */
2470 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2471 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2472 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2473 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2476 /* handle normal light */
2479 luxel[ 0 ] += averageColor[ 0 ] / samples;
2480 luxel[ 1 ] += averageColor[ 1 ] / samples;
2481 luxel[ 2 ] += averageColor[ 2 ] / samples;
2485 /* scale into luxel */
2486 deluxel = SUPER_DELUXEL( x, y );
2487 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2488 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2489 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2496 /* get particulars */
2497 lightLuxel = LIGHT_LUXEL( x, y );
2498 lightDeluxel = LIGHT_DELUXEL( x, y );
2499 luxel = SUPER_LUXEL( lightmapNum, x, y );
2500 deluxel = SUPER_DELUXEL( x, y );
2502 /* handle negative light */
2503 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2504 VectorScale( averageColor, -1.0f, averageColor );
2510 /* handle negative light */
2511 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2512 VectorSubtract( luxel, lightLuxel, luxel );
2515 /* handle normal light */
2517 VectorAdd( luxel, lightLuxel, luxel );
2521 VectorAdd( deluxel, lightDeluxel, deluxel );
2528 /* free temporary luxels */
2529 if ( lightLuxels != stackLightLuxels ) {
2530 free( lightLuxels );
2534 free( lightDeluxels );
2538 /* free light list */
2539 FreeTraceLights( &trace );
2541 /* floodlight pass */
2542 if ( floodlighty ) {
2543 FloodlightIlluminateLightmap( lm );
2546 if ( debugnormals ) {
2547 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2550 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2554 for ( y = 0; y < lm->sh; y++ )
2556 for ( x = 0; x < lm->sw; x++ )
2559 cluster = SUPER_CLUSTER( x, y );
2560 //% if( *cluster < 0 )
2563 /* get particulars */
2564 luxel = SUPER_LUXEL( lightmapNum, x, y );
2565 normal = SUPER_NORMAL( x, y );
2567 luxel[0] = ( normal[0] * 127 ) + 127;
2568 luxel[1] = ( normal[1] * 127 ) + 127;
2569 luxel[2] = ( normal[2] * 127 ) + 127;
2575 /* -----------------------------------------------------------------
2577 ----------------------------------------------------------------- */
2580 /* walk lightmaps */
2581 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2584 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2588 /* apply dirt to each luxel */
2589 for ( y = 0; y < lm->sh; y++ )
2591 for ( x = 0; x < lm->sw; x++ )
2594 cluster = SUPER_CLUSTER( x, y );
2596 /* get particulars */
2597 luxel = SUPER_LUXEL( lightmapNum, x, y );
2598 dirt = SUPER_DIRT( x, y );
2601 VectorScale( luxel, *dirt, luxel );
2605 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2612 /* -----------------------------------------------------------------
2614 ----------------------------------------------------------------- */
2616 /* walk lightmaps */
2617 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2620 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2624 /* average occluded luxels from neighbors */
2625 for ( y = 0; y < lm->sh; y++ )
2627 for ( x = 0; x < lm->sw; x++ )
2629 /* get particulars */
2630 cluster = SUPER_CLUSTER( x, y );
2631 luxel = SUPER_LUXEL( lightmapNum, x, y );
2632 deluxel = SUPER_DELUXEL( x, y );
2633 normal = SUPER_NORMAL( x, y );
2635 /* determine if filtering is necessary */
2636 filterColor = qfalse;
2638 if ( *cluster < 0 ||
2639 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2640 filterColor = qtrue;
2643 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2647 if ( !filterColor && !filterDir ) {
2651 /* choose seed amount */
2652 VectorClear( averageColor );
2653 VectorClear( averageDir );
2656 /* walk 3x3 matrix */
2657 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2659 if ( sy < 0 || sy >= lm->sh ) {
2663 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2665 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2669 /* get neighbor's particulars */
2670 cluster2 = SUPER_CLUSTER( sx, sy );
2671 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2672 deluxel2 = SUPER_DELUXEL( sx, sy );
2674 /* ignore unmapped/unlit luxels */
2675 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2676 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2680 /* add its distinctiveness to our own */
2681 VectorAdd( averageColor, luxel2, averageColor );
2682 samples += luxel2[ 3 ];
2684 VectorAdd( averageDir, deluxel2, averageDir );
2690 if ( samples <= 0.0f ) {
2694 /* dark lightmap seams */
2696 if ( lightmapNum == 0 ) {
2697 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2703 if ( filterColor ) {
2704 VectorDivide( averageColor, samples, luxel );
2708 VectorDivide( averageDir, samples, deluxel );
2711 /* set cluster to -3 */
2712 if ( *cluster < 0 ) {
2713 *cluster = CLUSTER_FLOODED;
2723 IlluminateVertexes()
2724 light the surface vertexes
2727 #define VERTEX_NUDGE 4.0f
2729 void IlluminateVertexes( int num ){
2730 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2731 int lightmapNum, numAvg;
2732 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2733 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2734 bspDrawSurface_t *ds;
2735 surfaceInfo_t *info;
2737 bspDrawVert_t *verts;
2739 float floodLightAmount;
2743 /* get surface, info, and raw lightmap */
2744 ds = &bspDrawSurfaces[ num ];
2745 info = &surfaceInfos[ num ];
2748 /* -----------------------------------------------------------------
2749 illuminate the vertexes
2750 ----------------------------------------------------------------- */
2752 /* calculate vertex lighting for surfaces without lightmaps */
2753 if ( lm == NULL || cpmaHack ) {
2755 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2756 trace.forceSunlight = info->si->forceSunlight;
2757 trace.recvShadows = info->recvShadows;
2758 trace.numSurfaces = 1;
2759 trace.surfaces = #
2760 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2762 /* twosided lighting */
2763 trace.twoSided = info->si->twoSided;
2765 /* make light list for this surface */
2766 CreateTraceLightsForSurface( num, &trace );
2769 verts = yDrawVerts + ds->firstVert;
2771 memset( avgColors, 0, sizeof( avgColors ) );
2773 /* walk the surface verts */
2774 for ( i = 0; i < ds->numVerts; i++ )
2776 /* get vertex luxel */
2777 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2779 /* color the luxel with raw lightmap num? */
2780 if ( debugSurfaces ) {
2781 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2784 /* color the luxel with luxel origin? */
2785 else if ( debugOrigin ) {
2786 VectorSubtract( info->maxs, info->mins, temp );
2787 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2788 VectorSubtract( origin, lm->mins, temp2 );
2789 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2790 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2791 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2794 /* color the luxel with the normal */
2795 else if ( normalmap ) {
2796 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2797 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2798 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2801 else if ( info->si->noVertexLight ) {
2802 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
2805 else if ( noVertexLighting > 0 ) {
2806 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
2809 /* illuminate the vertex */
2812 /* clear vertex luxel */
2813 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2815 /* try at initial origin */
2816 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2817 if ( trace.cluster >= 0 ) {
2819 VectorCopy( verts[ i ].xyz, trace.origin );
2820 VectorCopy( verts[ i ].normal, trace.normal );
2823 if ( dirty && !bouncing ) {
2824 dirt = DirtForSample( &trace );
2830 /* jal: floodlight */
2831 floodLightAmount = 0.0f;
2832 VectorClear( floodColor );
2833 if ( floodlighty && !bouncing ) {
2834 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2835 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2839 LightingAtSample( &trace, ds->vertexStyles, colors );
2842 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2845 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2847 /* jal: floodlight */
2848 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2851 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2852 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2853 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2857 /* is this sample bright enough? */
2858 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2859 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2860 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2861 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2862 /* nudge the sample point around a bit */
2863 for ( x = 0; x < 5; x++ )
2865 /* two's complement 0, 1, -1, 2, -2, etc */
2866 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2868 for ( y = 0; y < 5; y++ )
2870 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2872 for ( z = 0; z < 5; z++ )
2874 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2877 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2878 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2879 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2881 /* try at nudged origin */
2882 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2883 if ( trace.cluster < 0 ) {
2888 if ( dirty && !bouncing ) {
2889 dirt = DirtForSample( &trace );
2895 /* jal: floodlight */
2896 floodLightAmount = 0.0f;
2897 VectorClear( floodColor );
2898 if ( floodlighty && !bouncing ) {
2899 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2900 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2904 LightingAtSample( &trace, ds->vertexStyles, colors );
2907 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2910 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2912 /* jal: floodlight */
2913 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2916 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2917 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2920 /* bright enough? */
2921 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2922 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2923 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2924 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2932 /* add to average? */
2933 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2934 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2935 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2936 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2938 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2940 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2941 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2946 /* another happy customer */
2947 numVertsIlluminated++;
2950 /* set average color */
2952 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2953 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2957 VectorCopy( ambientColor, avgColors[ 0 ] );
2960 /* clean up and store vertex color */
2961 for ( i = 0; i < ds->numVerts; i++ )
2963 /* get vertex luxel */
2964 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2966 /* store average in occluded vertexes */
2967 if ( radVertLuxel[ 0 ] < 0.0f ) {
2968 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2970 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2971 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2974 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2979 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2982 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2983 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2986 if ( bouncing || bounce == 0 || !bounceOnly ) {
2987 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2989 if ( !info->si->noVertexLight ) {
2990 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2995 /* free light list */
2996 FreeTraceLights( &trace );
2998 /* return to sender */
3002 /* -----------------------------------------------------------------
3003 reconstitute vertex lighting from the luxels
3004 ----------------------------------------------------------------- */
3006 /* set styles from lightmap */
3007 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3008 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3010 /* get max search radius */
3012 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3014 /* walk the surface verts */
3015 verts = yDrawVerts + ds->firstVert;
3016 for ( i = 0; i < ds->numVerts; i++ )
3018 /* do each lightmap */
3019 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3022 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3026 /* get luxel coords */
3027 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3028 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3032 else if ( x >= lm->sw ) {
3038 else if ( y >= lm->sh ) {
3042 /* get vertex luxels */
3043 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3044 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3046 /* color the luxel with the normal? */
3048 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3049 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3050 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3053 /* color the luxel with surface num? */
3054 else if ( debugSurfaces ) {
3055 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3058 else if ( info->si->noVertexLight ) {
3059 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
3062 else if ( noVertexLighting > 0 ) {
3063 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
3066 /* divine color from the superluxels */
3069 /* increasing radius */
3070 VectorClear( radVertLuxel );
3072 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3074 /* sample within radius */
3075 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3077 if ( sy < 0 || sy >= lm->sh ) {
3081 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3083 if ( sx < 0 || sx >= lm->sw ) {
3087 /* get luxel particulars */
3088 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3089 cluster = SUPER_CLUSTER( sx, sy );
3090 if ( *cluster < 0 ) {
3094 /* testing: must be brigher than ambient color */
3095 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3098 /* add its distinctiveness to our own */
3099 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3100 samples += luxel[ 3 ];
3106 if ( samples > 0.0f ) {
3107 VectorDivide( radVertLuxel, samples, radVertLuxel );
3110 VectorCopy( ambientColor, radVertLuxel );
3114 /* store into floating point storage */
3115 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3116 numVertsIlluminated++;
3118 /* store into bytes (for vertex approximation) */
3119 if ( !info->si->noVertexLight ) {
3120 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3128 /* -------------------------------------------------------------------------------
3130 light optimization (-fast)
3132 creates a list of lights that will affect a surface and stores it in tw
3133 this is to optimize surface lighting by culling out as many of the
3134 lights in the world as possible from further calculation
3136 ------------------------------------------------------------------------------- */
3140 determines opaque brushes in the world and find sky shaders for sunlight calculations
3143 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3145 unsigned int compileFlags, allCompileFlags;
3148 bspBrushSide_t *side;
3149 bspShader_t *shader;
3154 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3157 if ( opaqueBrushes == NULL ) {
3158 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3162 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3163 numOpaqueBrushes = 0;
3165 /* walk the list of worldspawn brushes */
3166 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3169 b = bspModels[ 0 ].firstBSPBrush + i;
3170 brush = &bspBrushes[ b ];
3172 /* check all sides */
3175 allCompileFlags = ~( 0u );
3176 for ( j = 0; j < brush->numSides && inside; j++ )
3178 /* do bsp shader calculations */
3179 side = &bspBrushSides[ brush->firstSide + j ];
3180 shader = &bspShaders[ side->shaderNum ];
3182 /* get shader info */
3183 si = ShaderInfoForShaderNull( shader->shader );
3188 /* or together compile flags */
3189 compileFlags |= si->compileFlags;
3190 allCompileFlags &= si->compileFlags;
3193 /* determine if this brush is opaque to light */
3194 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3195 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3201 /* emit some statistics */
3202 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3204 void SetupBrushes( void ){
3205 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3212 determines if two clusters are visible to each other using the PVS
3215 qboolean ClusterVisible( int a, int b ){
3221 if ( a < 0 || b < 0 ) {
3231 if ( numBSPVisBytes <= 8 ) {
3236 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3237 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3238 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3241 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3251 borrowed from vlight.c
3254 int PointInLeafNum_r( vec3_t point, int nodenum ){
3261 while ( nodenum >= 0 )
3263 node = &bspNodes[ nodenum ];
3264 plane = &bspPlanes[ node->planeNum ];
3265 dist = DotProduct( point, plane->normal ) - plane->dist;
3267 nodenum = node->children[ 0 ];
3269 else if ( dist < -0.1 ) {
3270 nodenum = node->children[ 1 ];
3274 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3275 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3278 nodenum = node->children[ 1 ];
3282 leafnum = -nodenum - 1;
3290 borrowed from vlight.c
3293 int PointInLeafNum( vec3_t point ){
3294 return PointInLeafNum_r( point, 0 );
3300 ClusterVisibleToPoint() - ydnar
3301 returns qtrue if point can "see" cluster
3304 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3308 /* get leafNum for point */
3309 pointCluster = ClusterForPoint( point );
3310 if ( pointCluster < 0 ) {
3315 return ClusterVisible( pointCluster, cluster );
3321 ClusterForPoint() - ydnar
3322 returns the pvs cluster for point
3325 int ClusterForPoint( vec3_t point ){
3329 /* get leafNum for point */
3330 leafNum = PointInLeafNum( point );
3331 if ( leafNum < 0 ) {
3335 /* return the cluster */
3336 return bspLeafs[ leafNum ].cluster;
3342 ClusterForPointExt() - ydnar
3343 also takes brushes into account for occlusion testing
3346 int ClusterForPointExt( vec3_t point, float epsilon ){
3347 int i, j, b, leafNum, cluster;
3350 int *brushes, numBSPBrushes;
3356 /* get leaf for point */
3357 leafNum = PointInLeafNum( point );
3358 if ( leafNum < 0 ) {
3361 leaf = &bspLeafs[ leafNum ];
3363 /* get the cluster */
3364 cluster = leaf->cluster;
3365 if ( cluster < 0 ) {
3369 /* transparent leaf, so check point against all brushes in the leaf */
3370 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3371 numBSPBrushes = leaf->numBSPLeafBrushes;
3372 for ( i = 0; i < numBSPBrushes; i++ )
3376 if ( b > maxOpaqueBrush ) {
3379 brush = &bspBrushes[ b ];
3380 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3384 /* check point against all planes */
3386 for ( j = 0; j < brush->numSides && inside; j++ )
3388 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3389 dot = DotProduct( point, plane->normal );
3391 if ( dot > epsilon ) {
3396 /* if inside, return bogus cluster */
3402 /* if the point made it this far, it's not inside any opaque brushes */
3409 ClusterForPointExtFilter() - ydnar
3410 adds cluster checking against a list of known valid clusters
3413 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3417 /* get cluster for point */
3418 cluster = ClusterForPointExt( point, epsilon );
3420 /* check if filtering is necessary */
3421 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3426 for ( i = 0; i < numClusters; i++ )
3428 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3440 ShaderForPointInLeaf() - ydnar
3441 checks a point against all brushes in a leaf, returning the shader of the brush
3442 also sets the cumulative surface and content flags for the brush hit
3445 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3449 int *brushes, numBSPBrushes;
3452 bspBrushSide_t *side;
3454 bspShader_t *shader;
3455 int allSurfaceFlags, allContentFlags;
3458 /* clear things out first */
3463 if ( leafNum < 0 ) {
3466 leaf = &bspLeafs[ leafNum ];
3468 /* transparent leaf, so check point against all brushes in the leaf */
3469 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3470 numBSPBrushes = leaf->numBSPLeafBrushes;
3471 for ( i = 0; i < numBSPBrushes; i++ )
3474 brush = &bspBrushes[ brushes[ i ] ];
3476 /* check point against all planes */
3478 allSurfaceFlags = 0;
3479 allContentFlags = 0;
3480 for ( j = 0; j < brush->numSides && inside; j++ )
3482 side = &bspBrushSides[ brush->firstSide + j ];
3483 plane = &bspPlanes[ side->planeNum ];
3484 dot = DotProduct( point, plane->normal );
3486 if ( dot > epsilon ) {
3491 shader = &bspShaders[ side->shaderNum ];
3492 allSurfaceFlags |= shader->surfaceFlags;
3493 allContentFlags |= shader->contentFlags;
3497 /* handle if inside */
3499 /* if there are desired flags, check for same and continue if they aren't matched */
3500 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3503 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3507 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3508 *surfaceFlags = allSurfaceFlags;
3509 *contentFlags = allContentFlags;
3510 return brush->shaderNum;
3514 /* if the point made it this far, it's not inside any brushes */
3522 chops a bounding box by the plane defined by origin and normal
3523 returns qfalse if the bounds is entirely clipped away
3525 this is not exactly the fastest way to do this...
3528 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3529 /* FIXME: rewrite this so it doesn't use bloody brushes */
3537 calculates each light's effective envelope,
3538 taking into account brightness, type, and pvs.
3541 #define LIGHT_EPSILON 0.125f
3542 #define LIGHT_NUDGE 2.0f
3544 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3545 int i, x, y, z, x1, y1, z1;
3546 light_t *light, *light2, **owner;
3548 vec3_t origin, dir, mins, maxs;
3549 float radius, intensity;
3550 light_t *buckets[ 256 ];
3553 /* early out for weird cases where there are no lights */
3554 if ( lights == NULL ) {
3559 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3563 numCulledLights = 0;
3565 while ( *owner != NULL )
3570 /* handle negative lights */
3571 if ( light->photons < 0.0f || light->add < 0.0f ) {
3572 light->photons *= -1.0f;
3573 light->add *= -1.0f;
3574 light->flags |= LIGHT_NEGATIVE;
3578 if ( light->type == EMIT_SUN ) {
3581 light->envelope = MAX_WORLD_COORD * 8.0f;
3582 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3583 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3586 /* everything else */
3589 /* get pvs cluster for light */
3590 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3592 /* invalid cluster? */
3593 if ( light->cluster < 0 ) {
3594 /* nudge the sample point around a bit */
3595 for ( x = 0; x < 4; x++ )
3597 /* two's complement 0, 1, -1, 2, -2, etc */
3598 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3600 for ( y = 0; y < 4; y++ )
3602 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3604 for ( z = 0; z < 4; z++ )
3606 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3609 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3610 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3611 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3613 /* try at nudged origin */
3614 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3615 if ( light->cluster < 0 ) {
3620 VectorCopy( origin, light->origin );
3626 /* only calculate for lights in pvs and outside of opaque brushes */
3627 if ( light->cluster >= 0 ) {
3628 /* set light fast flag */
3630 light->flags |= LIGHT_FAST_TEMP;
3633 light->flags &= ~LIGHT_FAST_TEMP;
3635 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3636 light->flags |= LIGHT_FAST_TEMP;
3638 if ( light->si && light->si->noFast ) {
3639 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3642 /* clear light envelope */
3643 light->envelope = 0;
3645 /* handle area lights */
3646 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3647 light->envelope = MAX_WORLD_COORD * 8.0f;
3649 /* check for fast mode */
3650 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3651 /* ugly hack to calculate extent for area lights, but only done once */
3652 VectorScale( light->normal, -1.0f, dir );
3653 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3657 VectorMA( light->origin, radius, light->normal, origin );
3658 factor = PointToPolygonFormFactor( origin, dir, light->w );
3659 if ( factor < 0.0f ) {
3662 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3663 light->envelope = radius;
3669 intensity = light->photons; /* hopefully not used */
3674 intensity = light->photons;
3678 if ( light->envelope <= 0.0f ) {
3679 /* solve distance for non-distance lights */
3680 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3681 light->envelope = MAX_WORLD_COORD * 8.0f;
3684 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3685 /* solve distance for linear lights */
3686 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3687 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3691 add = angle * light->photons * linearScale - (dist * light->fade);
3692 T = (light->photons * linearScale) - (dist * light->fade);
3693 T + (dist * light->fade) = (light->photons * linearScale);
3694 dist * light->fade = (light->photons * linearScale) - T;
3695 dist = ((light->photons * linearScale) - T) / light->fade;
3698 /* solve for inverse square falloff */
3700 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3704 add = light->photons / (dist * dist);
3705 T = light->photons / (dist * dist);
3706 T * (dist * dist) = light->photons;
3707 dist = sqrt( light->photons / T );
3712 /* solve distance for linear lights */
3713 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3714 light->envelope = ( intensity * linearScale ) / light->fade;
3717 /* can't cull these */
3719 light->envelope = MAX_WORLD_COORD * 8.0f;
3724 /* chop radius against pvs */
3727 ClearBounds( mins, maxs );
3729 /* check all leaves */
3730 for ( i = 0; i < numBSPLeafs; i++ )
3733 leaf = &bspLeafs[ i ];
3736 if ( leaf->cluster < 0 ) {
3739 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3743 /* add this leafs bbox to the bounds */
3744 VectorCopy( leaf->mins, origin );
3745 AddPointToBounds( origin, mins, maxs );
3746 VectorCopy( leaf->maxs, origin );
3747 AddPointToBounds( origin, mins, maxs );
3750 /* test to see if bounds encompass light */
3751 for ( i = 0; i < 3; i++ )
3753 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3754 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3755 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3756 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3757 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3758 AddPointToBounds( light->origin, mins, maxs );
3762 /* chop the bounds by a plane for area lights and spotlights */
3763 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3764 ChopBounds( mins, maxs, light->origin, light->normal );
3768 VectorCopy( mins, light->mins );
3769 VectorCopy( maxs, light->maxs );
3771 /* reflect bounds around light origin */
3772 //% VectorMA( light->origin, -1.0f, origin, origin );
3773 VectorScale( light->origin, 2, origin );
3774 VectorSubtract( origin, maxs, origin );
3775 AddPointToBounds( origin, mins, maxs );
3776 //% VectorMA( light->origin, -1.0f, mins, origin );
3777 VectorScale( light->origin, 2, origin );
3778 VectorSubtract( origin, mins, origin );
3779 AddPointToBounds( origin, mins, maxs );
3781 /* calculate spherical bounds */
3782 VectorSubtract( maxs, light->origin, dir );
3783 radius = (float) VectorLength( dir );
3785 /* if this radius is smaller than the envelope, then set the envelope to it */
3786 if ( radius < light->envelope ) {
3787 light->envelope = radius;
3788 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3791 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3794 /* add grid/surface only check */
3796 if ( !( light->flags & LIGHT_GRID ) ) {
3797 light->envelope = 0.0f;
3802 if ( !( light->flags & LIGHT_SURFACES ) ) {
3803 light->envelope = 0.0f;
3809 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3811 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3813 /* delete the light */
3815 *owner = light->next;
3816 if ( light->w != NULL ) {
3824 /* square envelope */
3825 light->envelope2 = ( light->envelope * light->envelope );
3827 /* increment light count */
3830 /* set next light */
3831 owner = &( ( **owner ).next );
3834 /* bucket sort lights by style */
3835 memset( buckets, 0, sizeof( buckets ) );
3837 for ( light = lights; light != NULL; light = light2 )
3839 /* get next light */
3840 light2 = light->next;
3842 /* filter into correct bucket */
3843 light->next = buckets[ light->style ];
3844 buckets[ light->style ] = light;
3846 /* if any styled light is present, automatically set nocollapse */
3847 if ( light->style != LS_NORMAL ) {
3852 /* filter back into light list */
3854 for ( i = 255; i >= 0; i-- )
3857 for ( light = buckets[ i ]; light != NULL; light = light2 )
3859 light2 = light->next;
3860 light->next = lights;
3865 /* emit some statistics */
3866 Sys_Printf( "%9d total lights\n", numLights );
3867 Sys_Printf( "%9d culled lights\n", numCulledLights );
3873 CreateTraceLightsForBounds()
3874 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3877 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3880 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3881 float radius, dist, length;
3884 /* potential pre-setup */
3885 if ( numLights == 0 ) {
3886 SetupEnvelopes( qfalse, fast );
3890 //% 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 ] );
3892 /* allocate the light list */
3893 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3894 trace->numLights = 0;
3896 /* calculate spherical bounds */
3897 VectorAdd( mins, maxs, origin );
3898 VectorScale( origin, 0.5f, origin );
3899 VectorSubtract( maxs, origin, dir );
3900 radius = (float) VectorLength( dir );
3902 /* get length of normal vector */
3903 if ( normal != NULL ) {
3904 length = VectorLength( normal );
3908 normal = nullVector;
3912 /* test each light and see if it reaches the sphere */
3913 /* note: the attenuation code MUST match LightingAtSample() */
3914 for ( light = lights; light; light = light->next )
3916 /* check zero sized envelope */
3917 if ( light->envelope <= 0 ) {
3918 lightsEnvelopeCulled++;
3923 if ( !( light->flags & flags ) ) {
3927 /* sunlight skips all this nonsense */
3928 if ( light->type != EMIT_SUN ) {
3934 /* check against pvs cluster */
3935 if ( numClusters > 0 && clusters != NULL ) {
3936 for ( i = 0; i < numClusters; i++ )
3938 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3944 if ( i == numClusters ) {
3945 lightsClusterCulled++;
3950 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3951 VectorSubtract( light->origin, origin, dir );
3952 dist = VectorLength( dir );
3953 dist -= light->envelope;
3956 lightsEnvelopeCulled++;
3960 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3963 for ( i = 0; i < 3; i++ )
3965 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3970 lightsBoundsCulled++;
3976 /* planar surfaces (except twosided surfaces) have a couple more checks */
3977 if ( length > 0.0f && trace->twoSided == qfalse ) {
3978 /* lights coplanar with a surface won't light it */
3979 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3980 lightsPlaneCulled++;
3984 /* check to see if light is behind the plane */
3985 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3986 lightsPlaneCulled++;
3991 /* add this light */
3992 trace->lights[ trace->numLights++ ] = light;
3995 /* make last night null */
3996 trace->lights[ trace->numLights ] = NULL;
4001 void FreeTraceLights( trace_t *trace ){
4002 if ( trace->lights != NULL ) {
4003 free( trace->lights );
4010 CreateTraceLightsForSurface()
4011 creates a list of lights that can potentially affect a drawsurface
4014 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4016 vec3_t mins, maxs, normal;
4018 bspDrawSurface_t *ds;
4019 surfaceInfo_t *info;
4027 /* get drawsurface and info */
4028 ds = &bspDrawSurfaces[ num ];
4029 info = &surfaceInfos[ num ];
4031 /* get the mins/maxs for the dsurf */
4032 ClearBounds( mins, maxs );
4033 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4034 for ( i = 0; i < ds->numVerts; i++ )
4036 dv = &yDrawVerts[ ds->firstVert + i ];
4037 AddPointToBounds( dv->xyz, mins, maxs );
4038 if ( !VectorCompare( dv->normal, normal ) ) {
4039 VectorClear( normal );
4043 /* create the lights for the bounding box */
4044 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4047 /////////////////////////////////////////////////////////////
4049 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4050 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4051 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4052 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4054 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4055 static int numFloodVectors = 0;
4057 void SetupFloodLight( void ){
4059 float angle, elevation, angleStep, elevationStep;
4061 double v1,v2,v3,v4,v5,v6;
4064 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4066 /* calculate angular steps */
4067 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4068 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4072 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4074 /* iterate elevation */
4075 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4077 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4078 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4079 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4084 /* emit some statistics */
4085 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4088 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4090 if ( value[ 0 ] != '\0' ) {
4092 v4 = floodlightDistance;
4093 v5 = floodlightIntensity;
4094 v6 = floodlightDirectionScale;
4096 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4098 floodlightRGB[0] = v1;
4099 floodlightRGB[1] = v2;
4100 floodlightRGB[2] = v3;
4102 if ( VectorLength( floodlightRGB ) == 0 ) {
4103 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4116 floodlightDistance = v4;
4117 floodlightIntensity = v5;
4118 floodlightDirectionScale = v6;
4120 floodlighty = qtrue;
4121 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4125 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4128 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4129 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4130 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4132 ColorNormalize( floodlightRGB,floodlightRGB );
4136 FloodLightForSample()
4137 calculates floodlight value for a given sample
4138 once again, kudos to the dirtmapping coder
4141 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4146 float gatherLight, outLight;
4147 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4155 if ( trace == NULL || trace->cluster < 0 ) {
4161 dd = floodLightDistance;
4162 VectorCopy( trace->normal, normal );
4164 /* check if the normal is aligned to the world-up */
4165 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4166 if ( normal[ 2 ] == 1.0f ) {
4167 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4168 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4170 else if ( normal[ 2 ] == -1.0f ) {
4171 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4172 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4177 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4178 CrossProduct( normal, worldUp, myRt );
4179 VectorNormalize( myRt, myRt );
4180 CrossProduct( myRt, normal, myUp );
4181 VectorNormalize( myUp, myUp );
4184 /* vortex: optimise floodLightLowQuality a bit */
4185 if ( floodLightLowQuality == qtrue ) {
4186 /* iterate through ordered vectors */
4187 for ( i = 0; i < numFloodVectors; i++ )
4188 if ( rand() % 10 != 0 ) {
4194 /* iterate through ordered vectors */
4195 for ( i = 0; i < numFloodVectors; i++ )
4199 /* transform vector into tangent space */
4200 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4201 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4202 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4205 VectorMA( trace->origin, dd, direction, trace->end );
4207 //VectorMA( trace->origin, 1, direction, trace->origin );
4209 SetupTrace( trace );
4210 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4215 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4216 contribution = 1.0f;
4218 else if ( trace->opaque ) {
4219 VectorSubtract( trace->hit, trace->origin, displacement );
4220 d = VectorLength( displacement );
4222 // d=trace->distance;
4223 //if (d>256) gatherDirt+=1;
4224 contribution = d / dd;
4225 if ( contribution > 1 ) {
4226 contribution = 1.0f;
4229 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4232 gatherLight += contribution;
4237 if ( gatherLight <= 0.0f ) {
4246 gatherLight /= ( sub );
4248 outLight = gatherLight;
4249 if ( outLight > 1.0f ) {
4253 /* return to sender */
4258 FloodLightRawLightmap
4259 lighttracer style ambient occlusion light hack.
4260 Kudos to the dirtmapping author for most of this source.
4261 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4262 VorteX: fixed problems with deluxemapping
4265 // floodlight pass on a lightmap
4266 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4267 int i, x, y, *cluster;
4268 float *origin, *normal, *floodlight, floodLightAmount;
4269 surfaceInfo_t *info;
4272 // float samples, average, *floodlight2;
4274 memset( &trace,0,sizeof( trace_t ) );
4277 trace.testOcclusion = qtrue;
4278 trace.forceSunlight = qfalse;
4279 trace.twoSided = qtrue;
4280 trace.recvShadows = lm->recvShadows;
4281 trace.numSurfaces = lm->numLightSurfaces;
4282 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4283 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4284 trace.testAll = qfalse;
4285 trace.distance = 1024;
4287 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4288 //trace.twoSided = qfalse;
4289 for ( i = 0; i < trace.numSurfaces; i++ )
4292 info = &surfaceInfos[ trace.surfaces[ i ] ];
4294 /* check twosidedness */
4295 if ( info->si->twoSided ) {
4296 trace.twoSided = qtrue;
4301 /* gather floodlight */
4302 for ( y = 0; y < lm->sh; y++ )
4304 for ( x = 0; x < lm->sw; x++ )
4307 cluster = SUPER_CLUSTER( x, y );
4308 origin = SUPER_ORIGIN( x, y );
4309 normal = SUPER_NORMAL( x, y );
4310 floodlight = SUPER_FLOODLIGHT( x, y );
4312 /* set default dirt */
4315 /* only look at mapped luxels */
4316 if ( *cluster < 0 ) {
4321 trace.cluster = *cluster;
4322 VectorCopy( origin, trace.origin );
4323 VectorCopy( normal, trace.normal );
4325 /* get floodlight */
4326 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4328 /* add floodlight */
4329 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4330 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4331 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4332 floodlight[3] += floodlightDirectionScale;
4336 /* testing no filtering */
4342 for ( y = 0; y < lm->sh; y++ )
4344 for ( x = 0; x < lm->sw; x++ )
4347 cluster = SUPER_CLUSTER( x, y );
4348 floodlight = SUPER_FLOODLIGHT( x, y );
4350 /* filter dirt by adjacency to unmapped luxels */
4351 average = *floodlight;
4353 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4355 if ( sy < 0 || sy >= lm->sh ) {
4359 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4361 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4365 /* get neighboring luxel */
4366 cluster = SUPER_CLUSTER( sx, sy );
4367 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4368 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4373 average += *floodlight2;
4378 if ( samples <= 0.0f ) {
4384 if ( samples <= 0.0f ) {
4389 *floodlight = average / samples;
4395 void FloodLightRawLightmap( int rawLightmapNum ){
4398 /* bail if this number exceeds the number of raw lightmaps */
4399 if ( rawLightmapNum >= numRawLightmaps ) {
4403 lm = &rawLightmaps[ rawLightmapNum ];
4406 if ( floodlighty && floodlightIntensity ) {
4407 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4411 if ( lm->floodlightIntensity ) {
4412 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4413 numSurfacesFloodlighten += 1;
4417 void FloodlightRawLightmaps(){
4418 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4419 numSurfacesFloodlighten = 0;
4420 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4421 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4425 FloodLightIlluminate()
4426 illuminate floodlight into lightmap luxels
4429 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4430 float *luxel, *floodlight, *deluxel, *normal;
4433 int x, y, lightmapNum;
4435 /* walk lightmaps */
4436 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4439 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4443 /* apply floodlight to each luxel */
4444 for ( y = 0; y < lm->sh; y++ )
4446 for ( x = 0; x < lm->sw; x++ )
4448 /* get floodlight */
4449 floodlight = SUPER_FLOODLIGHT( x, y );
4450 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4455 cluster = SUPER_CLUSTER( x, y );
4457 /* only process mapped luxels */
4458 if ( *cluster < 0 ) {
4462 /* get particulars */
4463 luxel = SUPER_LUXEL( lightmapNum, x, y );
4464 deluxel = SUPER_DELUXEL( x, y );
4466 /* add to lightmap */
4467 luxel[0] += floodlight[0];
4468 luxel[1] += floodlight[1];
4469 luxel[2] += floodlight[2];
4471 if ( luxel[3] == 0 ) {
4475 /* add to deluxemap */
4476 if ( deluxemap && floodlight[3] > 0 ) {
4479 normal = SUPER_NORMAL( x, y );
4480 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4482 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4483 if ( brightness < 0.00390625f ) {
4484 brightness = 0.00390625f;
4487 VectorScale( normal, brightness, lightvector );
4488 VectorAdd( deluxel, lightvector, deluxel );