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 = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2212 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2213 luxelFilterRadius = 1;
2216 /* allocate sampling flags storage */
2217 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
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 ) && luxelFilterRadius == 0 ) {
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 ) && luxelFilterRadius == 0 ) {
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 /* illuminate the vertex */
2804 /* clear vertex luxel */
2805 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2807 /* try at initial origin */
2808 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2809 if ( trace.cluster >= 0 ) {
2811 VectorCopy( verts[ i ].xyz, trace.origin );
2812 VectorCopy( verts[ i ].normal, trace.normal );
2815 if ( dirty && !bouncing ) {
2816 dirt = DirtForSample( &trace );
2822 /* jal: floodlight */
2823 floodLightAmount = 0.0f;
2824 VectorClear( floodColor );
2825 if ( floodlighty && !bouncing ) {
2826 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2827 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2831 LightingAtSample( &trace, ds->vertexStyles, colors );
2834 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2837 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2839 /* jal: floodlight */
2840 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2843 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2844 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2845 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2849 /* is this sample bright enough? */
2850 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2851 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2852 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2853 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2854 /* nudge the sample point around a bit */
2855 for ( x = 0; x < 5; x++ )
2857 /* two's complement 0, 1, -1, 2, -2, etc */
2858 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2860 for ( y = 0; y < 5; y++ )
2862 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2864 for ( z = 0; z < 5; z++ )
2866 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2869 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2870 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2871 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2873 /* try at nudged origin */
2874 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2875 if ( trace.cluster < 0 ) {
2880 if ( dirty && !bouncing ) {
2881 dirt = DirtForSample( &trace );
2887 /* jal: floodlight */
2888 floodLightAmount = 0.0f;
2889 VectorClear( floodColor );
2890 if ( floodlighty && !bouncing ) {
2891 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2892 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2896 LightingAtSample( &trace, ds->vertexStyles, colors );
2899 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2902 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2904 /* jal: floodlight */
2905 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2908 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2909 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2912 /* bright enough? */
2913 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2914 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2915 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2916 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2924 /* add to average? */
2925 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2926 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2927 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2928 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2930 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2932 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2933 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2938 /* another happy customer */
2939 numVertsIlluminated++;
2942 /* set average color */
2944 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2945 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2949 VectorCopy( ambientColor, avgColors[ 0 ] );
2952 /* clean up and store vertex color */
2953 for ( i = 0; i < ds->numVerts; i++ )
2955 /* get vertex luxel */
2956 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2958 /* store average in occluded vertexes */
2959 if ( radVertLuxel[ 0 ] < 0.0f ) {
2960 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2962 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2963 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2966 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2971 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2974 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2975 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2978 if ( bouncing || bounce == 0 || !bounceOnly ) {
2979 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2981 if ( !info->si->noVertexLight ) {
2982 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2987 /* free light list */
2988 FreeTraceLights( &trace );
2990 /* return to sender */
2994 /* -----------------------------------------------------------------
2995 reconstitute vertex lighting from the luxels
2996 ----------------------------------------------------------------- */
2998 /* set styles from lightmap */
2999 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3000 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3002 /* get max search radius */
3004 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3006 /* walk the surface verts */
3007 verts = yDrawVerts + ds->firstVert;
3008 for ( i = 0; i < ds->numVerts; i++ )
3010 /* do each lightmap */
3011 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3014 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3018 /* get luxel coords */
3019 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3020 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3024 else if ( x >= lm->sw ) {
3030 else if ( y >= lm->sh ) {
3034 /* get vertex luxels */
3035 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3036 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3038 /* color the luxel with the normal? */
3040 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3041 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3042 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3045 /* color the luxel with surface num? */
3046 else if ( debugSurfaces ) {
3047 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3050 /* divine color from the superluxels */
3053 /* increasing radius */
3054 VectorClear( radVertLuxel );
3056 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3058 /* sample within radius */
3059 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3061 if ( sy < 0 || sy >= lm->sh ) {
3065 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3067 if ( sx < 0 || sx >= lm->sw ) {
3071 /* get luxel particulars */
3072 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3073 cluster = SUPER_CLUSTER( sx, sy );
3074 if ( *cluster < 0 ) {
3078 /* testing: must be brigher than ambient color */
3079 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3082 /* add its distinctiveness to our own */
3083 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3084 samples += luxel[ 3 ];
3090 if ( samples > 0.0f ) {
3091 VectorDivide( radVertLuxel, samples, radVertLuxel );
3094 VectorCopy( ambientColor, radVertLuxel );
3098 /* store into floating point storage */
3099 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3100 numVertsIlluminated++;
3102 /* store into bytes (for vertex approximation) */
3103 if ( !info->si->noVertexLight ) {
3104 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3112 /* -------------------------------------------------------------------------------
3114 light optimization (-fast)
3116 creates a list of lights that will affect a surface and stores it in tw
3117 this is to optimize surface lighting by culling out as many of the
3118 lights in the world as possible from further calculation
3120 ------------------------------------------------------------------------------- */
3124 determines opaque brushes in the world and find sky shaders for sunlight calculations
3127 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3129 unsigned int compileFlags, allCompileFlags;
3132 bspBrushSide_t *side;
3133 bspShader_t *shader;
3138 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3141 if ( opaqueBrushes == NULL ) {
3142 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3146 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3147 numOpaqueBrushes = 0;
3149 /* walk the list of worldspawn brushes */
3150 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3153 b = bspModels[ 0 ].firstBSPBrush + i;
3154 brush = &bspBrushes[ b ];
3156 /* check all sides */
3159 allCompileFlags = ~( 0u );
3160 for ( j = 0; j < brush->numSides && inside; j++ )
3162 /* do bsp shader calculations */
3163 side = &bspBrushSides[ brush->firstSide + j ];
3164 shader = &bspShaders[ side->shaderNum ];
3166 /* get shader info */
3167 si = ShaderInfoForShaderNull( shader->shader );
3172 /* or together compile flags */
3173 compileFlags |= si->compileFlags;
3174 allCompileFlags &= si->compileFlags;
3177 /* determine if this brush is opaque to light */
3178 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3179 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3185 /* emit some statistics */
3186 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3188 void SetupBrushes( void ){
3189 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3196 determines if two clusters are visible to each other using the PVS
3199 qboolean ClusterVisible( int a, int b ){
3205 if ( a < 0 || b < 0 ) {
3215 if ( numBSPVisBytes <= 8 ) {
3220 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3221 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3222 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3225 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3235 borrowed from vlight.c
3238 int PointInLeafNum_r( vec3_t point, int nodenum ){
3245 while ( nodenum >= 0 )
3247 node = &bspNodes[ nodenum ];
3248 plane = &bspPlanes[ node->planeNum ];
3249 dist = DotProduct( point, plane->normal ) - plane->dist;
3251 nodenum = node->children[ 0 ];
3253 else if ( dist < -0.1 ) {
3254 nodenum = node->children[ 1 ];
3258 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3259 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3262 nodenum = node->children[ 1 ];
3266 leafnum = -nodenum - 1;
3274 borrowed from vlight.c
3277 int PointInLeafNum( vec3_t point ){
3278 return PointInLeafNum_r( point, 0 );
3284 ClusterVisibleToPoint() - ydnar
3285 returns qtrue if point can "see" cluster
3288 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3292 /* get leafNum for point */
3293 pointCluster = ClusterForPoint( point );
3294 if ( pointCluster < 0 ) {
3299 return ClusterVisible( pointCluster, cluster );
3305 ClusterForPoint() - ydnar
3306 returns the pvs cluster for point
3309 int ClusterForPoint( vec3_t point ){
3313 /* get leafNum for point */
3314 leafNum = PointInLeafNum( point );
3315 if ( leafNum < 0 ) {
3319 /* return the cluster */
3320 return bspLeafs[ leafNum ].cluster;
3326 ClusterForPointExt() - ydnar
3327 also takes brushes into account for occlusion testing
3330 int ClusterForPointExt( vec3_t point, float epsilon ){
3331 int i, j, b, leafNum, cluster;
3334 int *brushes, numBSPBrushes;
3340 /* get leaf for point */
3341 leafNum = PointInLeafNum( point );
3342 if ( leafNum < 0 ) {
3345 leaf = &bspLeafs[ leafNum ];
3347 /* get the cluster */
3348 cluster = leaf->cluster;
3349 if ( cluster < 0 ) {
3353 /* transparent leaf, so check point against all brushes in the leaf */
3354 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3355 numBSPBrushes = leaf->numBSPLeafBrushes;
3356 for ( i = 0; i < numBSPBrushes; i++ )
3360 if ( b > maxOpaqueBrush ) {
3363 brush = &bspBrushes[ b ];
3364 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3368 /* check point against all planes */
3370 for ( j = 0; j < brush->numSides && inside; j++ )
3372 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3373 dot = DotProduct( point, plane->normal );
3375 if ( dot > epsilon ) {
3380 /* if inside, return bogus cluster */
3386 /* if the point made it this far, it's not inside any opaque brushes */
3393 ClusterForPointExtFilter() - ydnar
3394 adds cluster checking against a list of known valid clusters
3397 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3401 /* get cluster for point */
3402 cluster = ClusterForPointExt( point, epsilon );
3404 /* check if filtering is necessary */
3405 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3410 for ( i = 0; i < numClusters; i++ )
3412 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3424 ShaderForPointInLeaf() - ydnar
3425 checks a point against all brushes in a leaf, returning the shader of the brush
3426 also sets the cumulative surface and content flags for the brush hit
3429 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3433 int *brushes, numBSPBrushes;
3436 bspBrushSide_t *side;
3438 bspShader_t *shader;
3439 int allSurfaceFlags, allContentFlags;
3442 /* clear things out first */
3447 if ( leafNum < 0 ) {
3450 leaf = &bspLeafs[ leafNum ];
3452 /* transparent leaf, so check point against all brushes in the leaf */
3453 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3454 numBSPBrushes = leaf->numBSPLeafBrushes;
3455 for ( i = 0; i < numBSPBrushes; i++ )
3458 brush = &bspBrushes[ brushes[ i ] ];
3460 /* check point against all planes */
3462 allSurfaceFlags = 0;
3463 allContentFlags = 0;
3464 for ( j = 0; j < brush->numSides && inside; j++ )
3466 side = &bspBrushSides[ brush->firstSide + j ];
3467 plane = &bspPlanes[ side->planeNum ];
3468 dot = DotProduct( point, plane->normal );
3470 if ( dot > epsilon ) {
3475 shader = &bspShaders[ side->shaderNum ];
3476 allSurfaceFlags |= shader->surfaceFlags;
3477 allContentFlags |= shader->contentFlags;
3481 /* handle if inside */
3483 /* if there are desired flags, check for same and continue if they aren't matched */
3484 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3487 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3491 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3492 *surfaceFlags = allSurfaceFlags;
3493 *contentFlags = allContentFlags;
3494 return brush->shaderNum;
3498 /* if the point made it this far, it's not inside any brushes */
3506 chops a bounding box by the plane defined by origin and normal
3507 returns qfalse if the bounds is entirely clipped away
3509 this is not exactly the fastest way to do this...
3512 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3513 /* FIXME: rewrite this so it doesn't use bloody brushes */
3521 calculates each light's effective envelope,
3522 taking into account brightness, type, and pvs.
3525 #define LIGHT_EPSILON 0.125f
3526 #define LIGHT_NUDGE 2.0f
3528 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3529 int i, x, y, z, x1, y1, z1;
3530 light_t *light, *light2, **owner;
3532 vec3_t origin, dir, mins, maxs;
3533 float radius, intensity;
3534 light_t *buckets[ 256 ];
3537 /* early out for weird cases where there are no lights */
3538 if ( lights == NULL ) {
3543 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3547 numCulledLights = 0;
3549 while ( *owner != NULL )
3554 /* handle negative lights */
3555 if ( light->photons < 0.0f || light->add < 0.0f ) {
3556 light->photons *= -1.0f;
3557 light->add *= -1.0f;
3558 light->flags |= LIGHT_NEGATIVE;
3562 if ( light->type == EMIT_SUN ) {
3565 light->envelope = MAX_WORLD_COORD * 8.0f;
3566 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3567 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3570 /* everything else */
3573 /* get pvs cluster for light */
3574 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3576 /* invalid cluster? */
3577 if ( light->cluster < 0 ) {
3578 /* nudge the sample point around a bit */
3579 for ( x = 0; x < 4; x++ )
3581 /* two's complement 0, 1, -1, 2, -2, etc */
3582 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3584 for ( y = 0; y < 4; y++ )
3586 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3588 for ( z = 0; z < 4; z++ )
3590 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3593 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3594 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3595 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3597 /* try at nudged origin */
3598 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3599 if ( light->cluster < 0 ) {
3604 VectorCopy( origin, light->origin );
3610 /* only calculate for lights in pvs and outside of opaque brushes */
3611 if ( light->cluster >= 0 ) {
3612 /* set light fast flag */
3614 light->flags |= LIGHT_FAST_TEMP;
3617 light->flags &= ~LIGHT_FAST_TEMP;
3619 if ( fastpoint && ( light->flags != EMIT_AREA ) ) {
3620 light->flags |= LIGHT_FAST_TEMP;
3622 if ( light->si && light->si->noFast ) {
3623 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3626 /* clear light envelope */
3627 light->envelope = 0;
3629 /* handle area lights */
3630 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3631 light->envelope = MAX_WORLD_COORD * 8.0f;
3633 /* check for fast mode */
3634 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3635 /* ugly hack to calculate extent for area lights, but only done once */
3636 VectorScale( light->normal, -1.0f, dir );
3637 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3641 VectorMA( light->origin, radius, light->normal, origin );
3642 factor = PointToPolygonFormFactor( origin, dir, light->w );
3643 if ( factor < 0.0f ) {
3646 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3647 light->envelope = radius;
3653 intensity = light->photons; /* hopefully not used */
3658 intensity = light->photons;
3662 if ( light->envelope <= 0.0f ) {
3663 /* solve distance for non-distance lights */
3664 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3665 light->envelope = MAX_WORLD_COORD * 8.0f;
3668 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3669 /* solve distance for linear lights */
3670 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3671 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3675 add = angle * light->photons * linearScale - (dist * light->fade);
3676 T = (light->photons * linearScale) - (dist * light->fade);
3677 T + (dist * light->fade) = (light->photons * linearScale);
3678 dist * light->fade = (light->photons * linearScale) - T;
3679 dist = ((light->photons * linearScale) - T) / light->fade;
3682 /* solve for inverse square falloff */
3684 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3688 add = light->photons / (dist * dist);
3689 T = light->photons / (dist * dist);
3690 T * (dist * dist) = light->photons;
3691 dist = sqrt( light->photons / T );
3696 /* solve distance for linear lights */
3697 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3698 light->envelope = ( intensity * linearScale ) / light->fade;
3701 /* can't cull these */
3703 light->envelope = MAX_WORLD_COORD * 8.0f;
3708 /* chop radius against pvs */
3711 ClearBounds( mins, maxs );
3713 /* check all leaves */
3714 for ( i = 0; i < numBSPLeafs; i++ )
3717 leaf = &bspLeafs[ i ];
3720 if ( leaf->cluster < 0 ) {
3723 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3727 /* add this leafs bbox to the bounds */
3728 VectorCopy( leaf->mins, origin );
3729 AddPointToBounds( origin, mins, maxs );
3730 VectorCopy( leaf->maxs, origin );
3731 AddPointToBounds( origin, mins, maxs );
3734 /* test to see if bounds encompass light */
3735 for ( i = 0; i < 3; i++ )
3737 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3738 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3739 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3740 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3741 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3742 AddPointToBounds( light->origin, mins, maxs );
3746 /* chop the bounds by a plane for area lights and spotlights */
3747 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3748 ChopBounds( mins, maxs, light->origin, light->normal );
3752 VectorCopy( mins, light->mins );
3753 VectorCopy( maxs, light->maxs );
3755 /* reflect bounds around light origin */
3756 //% VectorMA( light->origin, -1.0f, origin, origin );
3757 VectorScale( light->origin, 2, origin );
3758 VectorSubtract( origin, maxs, origin );
3759 AddPointToBounds( origin, mins, maxs );
3760 //% VectorMA( light->origin, -1.0f, mins, origin );
3761 VectorScale( light->origin, 2, origin );
3762 VectorSubtract( origin, mins, origin );
3763 AddPointToBounds( origin, mins, maxs );
3765 /* calculate spherical bounds */
3766 VectorSubtract( maxs, light->origin, dir );
3767 radius = (float) VectorLength( dir );
3769 /* if this radius is smaller than the envelope, then set the envelope to it */
3770 if ( radius < light->envelope ) {
3771 light->envelope = radius;
3772 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3775 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3778 /* add grid/surface only check */
3780 if ( !( light->flags & LIGHT_GRID ) ) {
3781 light->envelope = 0.0f;
3786 if ( !( light->flags & LIGHT_SURFACES ) ) {
3787 light->envelope = 0.0f;
3793 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3795 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3797 /* delete the light */
3799 *owner = light->next;
3800 if ( light->w != NULL ) {
3808 /* square envelope */
3809 light->envelope2 = ( light->envelope * light->envelope );
3811 /* increment light count */
3814 /* set next light */
3815 owner = &( ( **owner ).next );
3818 /* bucket sort lights by style */
3819 memset( buckets, 0, sizeof( buckets ) );
3821 for ( light = lights; light != NULL; light = light2 )
3823 /* get next light */
3824 light2 = light->next;
3826 /* filter into correct bucket */
3827 light->next = buckets[ light->style ];
3828 buckets[ light->style ] = light;
3830 /* if any styled light is present, automatically set nocollapse */
3831 if ( light->style != LS_NORMAL ) {
3836 /* filter back into light list */
3838 for ( i = 255; i >= 0; i-- )
3841 for ( light = buckets[ i ]; light != NULL; light = light2 )
3843 light2 = light->next;
3844 light->next = lights;
3849 /* emit some statistics */
3850 Sys_Printf( "%9d total lights\n", numLights );
3851 Sys_Printf( "%9d culled lights\n", numCulledLights );
3857 CreateTraceLightsForBounds()
3858 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3861 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3864 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3865 float radius, dist, length;
3868 /* potential pre-setup */
3869 if ( numLights == 0 ) {
3870 SetupEnvelopes( qfalse, fast );
3874 //% 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 ] );
3876 /* allocate the light list */
3877 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3878 trace->numLights = 0;
3880 /* calculate spherical bounds */
3881 VectorAdd( mins, maxs, origin );
3882 VectorScale( origin, 0.5f, origin );
3883 VectorSubtract( maxs, origin, dir );
3884 radius = (float) VectorLength( dir );
3886 /* get length of normal vector */
3887 if ( normal != NULL ) {
3888 length = VectorLength( normal );
3892 normal = nullVector;
3896 /* test each light and see if it reaches the sphere */
3897 /* note: the attenuation code MUST match LightingAtSample() */
3898 for ( light = lights; light; light = light->next )
3900 /* check zero sized envelope */
3901 if ( light->envelope <= 0 ) {
3902 lightsEnvelopeCulled++;
3907 if ( !( light->flags & flags ) ) {
3911 /* sunlight skips all this nonsense */
3912 if ( light->type != EMIT_SUN ) {
3918 /* check against pvs cluster */
3919 if ( numClusters > 0 && clusters != NULL ) {
3920 for ( i = 0; i < numClusters; i++ )
3922 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3928 if ( i == numClusters ) {
3929 lightsClusterCulled++;
3934 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3935 VectorSubtract( light->origin, origin, dir );
3936 dist = VectorLength( dir );
3937 dist -= light->envelope;
3940 lightsEnvelopeCulled++;
3944 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3947 for ( i = 0; i < 3; i++ )
3949 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3954 lightsBoundsCulled++;
3960 /* planar surfaces (except twosided surfaces) have a couple more checks */
3961 if ( length > 0.0f && trace->twoSided == qfalse ) {
3962 /* lights coplanar with a surface won't light it */
3963 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3964 lightsPlaneCulled++;
3968 /* check to see if light is behind the plane */
3969 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3970 lightsPlaneCulled++;
3975 /* add this light */
3976 trace->lights[ trace->numLights++ ] = light;
3979 /* make last night null */
3980 trace->lights[ trace->numLights ] = NULL;
3985 void FreeTraceLights( trace_t *trace ){
3986 if ( trace->lights != NULL ) {
3987 free( trace->lights );
3994 CreateTraceLightsForSurface()
3995 creates a list of lights that can potentially affect a drawsurface
3998 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4000 vec3_t mins, maxs, normal;
4002 bspDrawSurface_t *ds;
4003 surfaceInfo_t *info;
4011 /* get drawsurface and info */
4012 ds = &bspDrawSurfaces[ num ];
4013 info = &surfaceInfos[ num ];
4015 /* get the mins/maxs for the dsurf */
4016 ClearBounds( mins, maxs );
4017 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4018 for ( i = 0; i < ds->numVerts; i++ )
4020 dv = &yDrawVerts[ ds->firstVert + i ];
4021 AddPointToBounds( dv->xyz, mins, maxs );
4022 if ( !VectorCompare( dv->normal, normal ) ) {
4023 VectorClear( normal );
4027 /* create the lights for the bounding box */
4028 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4031 /////////////////////////////////////////////////////////////
4033 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4034 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4035 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4036 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4038 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4039 static int numFloodVectors = 0;
4041 void SetupFloodLight( void ){
4043 float angle, elevation, angleStep, elevationStep;
4045 double v1,v2,v3,v4,v5,v6;
4048 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4050 /* calculate angular steps */
4051 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4052 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4056 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4058 /* iterate elevation */
4059 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4061 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4062 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4063 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4068 /* emit some statistics */
4069 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4072 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4074 if ( value[ 0 ] != '\0' ) {
4076 v4 = floodlightDistance;
4077 v5 = floodlightIntensity;
4078 v6 = floodlightDirectionScale;
4080 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4082 floodlightRGB[0] = v1;
4083 floodlightRGB[1] = v2;
4084 floodlightRGB[2] = v3;
4086 if ( VectorLength( floodlightRGB ) == 0 ) {
4087 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4100 floodlightDistance = v4;
4101 floodlightIntensity = v5;
4102 floodlightDirectionScale = v6;
4104 floodlighty = qtrue;
4105 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4109 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4112 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4113 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4114 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4116 ColorNormalize( floodlightRGB,floodlightRGB );
4120 FloodLightForSample()
4121 calculates floodlight value for a given sample
4122 once again, kudos to the dirtmapping coder
4125 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4130 float gatherLight, outLight;
4131 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4139 if ( trace == NULL || trace->cluster < 0 ) {
4145 dd = floodLightDistance;
4146 VectorCopy( trace->normal, normal );
4148 /* check if the normal is aligned to the world-up */
4149 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4150 if ( normal[ 2 ] == 1.0f ) {
4151 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4152 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4154 else if ( normal[ 2 ] == -1.0f ) {
4155 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4156 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4161 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4162 CrossProduct( normal, worldUp, myRt );
4163 VectorNormalize( myRt, myRt );
4164 CrossProduct( myRt, normal, myUp );
4165 VectorNormalize( myUp, myUp );
4168 /* vortex: optimise floodLightLowQuality a bit */
4169 if ( floodLightLowQuality == qtrue ) {
4170 /* iterate through ordered vectors */
4171 for ( i = 0; i < numFloodVectors; i++ )
4172 if ( rand() % 10 != 0 ) {
4178 /* iterate through ordered vectors */
4179 for ( i = 0; i < numFloodVectors; i++ )
4183 /* transform vector into tangent space */
4184 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4185 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4186 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4189 VectorMA( trace->origin, dd, direction, trace->end );
4191 //VectorMA( trace->origin, 1, direction, trace->origin );
4193 SetupTrace( trace );
4194 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4199 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4200 contribution = 1.0f;
4202 else if ( trace->opaque ) {
4203 VectorSubtract( trace->hit, trace->origin, displacement );
4204 d = VectorLength( displacement );
4206 // d=trace->distance;
4207 //if (d>256) gatherDirt+=1;
4208 contribution = d / dd;
4209 if ( contribution > 1 ) {
4210 contribution = 1.0f;
4213 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4216 gatherLight += contribution;
4221 if ( gatherLight <= 0.0f ) {
4230 gatherLight /= ( sub );
4232 outLight = gatherLight;
4233 if ( outLight > 1.0f ) {
4237 /* return to sender */
4242 FloodLightRawLightmap
4243 lighttracer style ambient occlusion light hack.
4244 Kudos to the dirtmapping author for most of this source.
4245 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4246 VorteX: fixed problems with deluxemapping
4249 // floodlight pass on a lightmap
4250 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4251 int i, x, y, *cluster;
4252 float *origin, *normal, *floodlight, floodLightAmount;
4253 surfaceInfo_t *info;
4256 // float samples, average, *floodlight2;
4258 memset( &trace,0,sizeof( trace_t ) );
4261 trace.testOcclusion = qtrue;
4262 trace.forceSunlight = qfalse;
4263 trace.twoSided = qtrue;
4264 trace.recvShadows = lm->recvShadows;
4265 trace.numSurfaces = lm->numLightSurfaces;
4266 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4267 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4268 trace.testAll = qfalse;
4269 trace.distance = 1024;
4271 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4272 //trace.twoSided = qfalse;
4273 for ( i = 0; i < trace.numSurfaces; i++ )
4276 info = &surfaceInfos[ trace.surfaces[ i ] ];
4278 /* check twosidedness */
4279 if ( info->si->twoSided ) {
4280 trace.twoSided = qtrue;
4285 /* gather floodlight */
4286 for ( y = 0; y < lm->sh; y++ )
4288 for ( x = 0; x < lm->sw; x++ )
4291 cluster = SUPER_CLUSTER( x, y );
4292 origin = SUPER_ORIGIN( x, y );
4293 normal = SUPER_NORMAL( x, y );
4294 floodlight = SUPER_FLOODLIGHT( x, y );
4296 /* set default dirt */
4299 /* only look at mapped luxels */
4300 if ( *cluster < 0 ) {
4305 trace.cluster = *cluster;
4306 VectorCopy( origin, trace.origin );
4307 VectorCopy( normal, trace.normal );
4309 /* get floodlight */
4310 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4312 /* add floodlight */
4313 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4314 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4315 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4316 floodlight[3] += floodlightDirectionScale;
4320 /* testing no filtering */
4326 for ( y = 0; y < lm->sh; y++ )
4328 for ( x = 0; x < lm->sw; x++ )
4331 cluster = SUPER_CLUSTER( x, y );
4332 floodlight = SUPER_FLOODLIGHT( x, y );
4334 /* filter dirt by adjacency to unmapped luxels */
4335 average = *floodlight;
4337 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4339 if ( sy < 0 || sy >= lm->sh ) {
4343 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4345 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4349 /* get neighboring luxel */
4350 cluster = SUPER_CLUSTER( sx, sy );
4351 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4352 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4357 average += *floodlight2;
4362 if ( samples <= 0.0f ) {
4368 if ( samples <= 0.0f ) {
4373 *floodlight = average / samples;
4379 void FloodLightRawLightmap( int rawLightmapNum ){
4382 /* bail if this number exceeds the number of raw lightmaps */
4383 if ( rawLightmapNum >= numRawLightmaps ) {
4387 lm = &rawLightmaps[ rawLightmapNum ];
4390 if ( floodlighty && floodlightIntensity ) {
4391 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4395 if ( lm->floodlightIntensity ) {
4396 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4397 numSurfacesFloodlighten += 1;
4401 void FloodlightRawLightmaps(){
4402 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4403 numSurfacesFloodlighten = 0;
4404 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4405 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4409 FloodLightIlluminate()
4410 illuminate floodlight into lightmap luxels
4413 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4414 float *luxel, *floodlight, *deluxel, *normal;
4417 int x, y, lightmapNum;
4419 /* walk lightmaps */
4420 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4423 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4427 /* apply floodlight to each luxel */
4428 for ( y = 0; y < lm->sh; y++ )
4430 for ( x = 0; x < lm->sw; x++ )
4432 /* get floodlight */
4433 floodlight = SUPER_FLOODLIGHT( x, y );
4434 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4439 cluster = SUPER_CLUSTER( x, y );
4441 /* only process mapped luxels */
4442 if ( *cluster < 0 ) {
4446 /* get particulars */
4447 luxel = SUPER_LUXEL( lightmapNum, x, y );
4448 deluxel = SUPER_DELUXEL( x, y );
4450 /* add to lightmap */
4451 luxel[0] += floodlight[0];
4452 luxel[1] += floodlight[1];
4453 luxel[2] += floodlight[2];
4455 if ( luxel[3] == 0 ) {
4459 /* add to deluxemap */
4460 if ( deluxemap && floodlight[3] > 0 ) {
4463 normal = SUPER_NORMAL( x, y );
4464 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4466 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4467 if ( brightness < 0.00390625f ) {
4468 brightness = 0.00390625f;
4471 VectorScale( normal, brightness, lightvector );
4472 VectorAdd( deluxel, lightvector, deluxel );