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 ] );
1772 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1774 /* calculate new origin */
1775 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1776 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1777 for ( i = 0; i < 3; i++ )
1778 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1781 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1782 if ( *sampleCluster < 0 ) {
1786 /* calculate new normal */
1787 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1788 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1789 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1791 normal = SUPER_NORMAL( x, y );
1792 VectorCopy( normal, sampleNormal );
1800 SubsampleRawLuxel_r()
1801 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1804 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1805 int b, samples, mapped, lighted;
1808 vec3_t deluxel[ 3 ];
1809 vec3_t origin[ 4 ], normal[ 4 ];
1810 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1811 vec3_t color, direction = { 0, 0, 0 }, total;
1815 if ( lightLuxel[ 3 ] >= lightSamples ) {
1820 VectorClear( total );
1824 /* make 2x2 subsample stamp */
1825 for ( b = 0; b < 4; b++ )
1828 VectorCopy( sampleOrigin, origin[ b ] );
1830 /* calculate position */
1831 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1837 /* increment sample count */
1838 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1841 trace->cluster = *cluster;
1842 VectorCopy( origin[ b ], trace->origin );
1843 VectorCopy( normal[ b ], trace->normal );
1847 LightContributionToSample( trace );
1848 if ( trace->forceSubsampling > 1.0f ) {
1849 /* alphashadow: we subsample as deep as we can */
1855 /* add to totals (fixme: make contrast function) */
1856 VectorCopy( trace->color, luxel[ b ] );
1857 if ( lightDeluxel ) {
1858 VectorCopy( trace->directionContribution, deluxel[ b ] );
1860 VectorAdd( total, trace->color, total );
1861 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1866 /* subsample further? */
1867 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1868 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1869 lighted != 0 && lighted != mapped ) {
1870 for ( b = 0; b < 4; b++ )
1872 if ( cluster[ b ] < 0 ) {
1875 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1880 //% VectorClear( color );
1882 VectorCopy( lightLuxel, color );
1883 if ( lightDeluxel ) {
1884 VectorCopy( lightDeluxel, direction );
1887 for ( b = 0; b < 4; b++ )
1889 if ( cluster[ b ] < 0 ) {
1892 VectorAdd( color, luxel[ b ], color );
1893 if ( lightDeluxel ) {
1894 VectorAdd( direction, deluxel[ b ], direction );
1900 if ( samples > 0 ) {
1902 color[ 0 ] /= samples;
1903 color[ 1 ] /= samples;
1904 color[ 2 ] /= samples;
1907 VectorCopy( color, lightLuxel );
1908 lightLuxel[ 3 ] += 1.0f;
1910 if ( lightDeluxel ) {
1911 direction[ 0 ] /= samples;
1912 direction[ 1 ] /= samples;
1913 direction[ 2 ] /= samples;
1914 VectorCopy( direction, lightDeluxel );
1919 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1920 static void GaussLikeRandom( float sigma, float *x, float *y ){
1922 r = Random() * 2 * Q_PI;
1923 *x = sigma * 2.73861278752581783822 * cos( r );
1924 *y = sigma * 2.73861278752581783822 * sin( r );
1931 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1934 vec3_t origin, normal;
1935 vec3_t total, totaldirection;
1938 VectorClear( total );
1939 VectorClear( totaldirection );
1941 for ( b = 0; b < lightSamples; ++b )
1944 VectorCopy( sampleOrigin, origin );
1945 GaussLikeRandom( bias, &dx, &dy );
1947 /* calculate position */
1948 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1954 trace->cluster = cluster;
1955 VectorCopy( origin, trace->origin );
1956 VectorCopy( normal, trace->normal );
1958 LightContributionToSample( trace );
1959 VectorAdd( total, trace->color, total );
1960 if ( lightDeluxel ) {
1961 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1968 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1969 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1970 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1972 if ( lightDeluxel ) {
1973 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1974 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1975 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1983 IlluminateRawLightmap()
1984 illuminates the luxels
1987 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1988 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1989 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1991 void IlluminateRawLightmap( int rawLightmapNum ){
1992 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1993 int *cluster, *cluster2, mapped, lighted, totalLighted;
1994 size_t llSize, ldSize;
1996 surfaceInfo_t *info;
1997 qboolean filterColor, filterDir;
1999 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2000 unsigned char *flag;
2001 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2002 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2003 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2005 float stackLightLuxels[ STACK_LL_SIZE ];
2008 /* bail if this number exceeds the number of raw lightmaps */
2009 if ( rawLightmapNum >= numRawLightmaps ) {
2014 lm = &rawLightmaps[ rawLightmapNum ];
2017 trace.testOcclusion = !noTrace;
2018 trace.forceSunlight = qfalse;
2019 trace.recvShadows = lm->recvShadows;
2020 trace.numSurfaces = lm->numLightSurfaces;
2021 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2022 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2024 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2025 trace.twoSided = qfalse;
2026 for ( i = 0; i < trace.numSurfaces; i++ )
2029 info = &surfaceInfos[ trace.surfaces[ i ] ];
2031 /* check twosidedness */
2032 if ( info->si->twoSided ) {
2033 trace.twoSided = qtrue;
2038 /* create a culled light list for this raw lightmap */
2039 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2041 /* -----------------------------------------------------------------
2043 ----------------------------------------------------------------- */
2046 numLuxelsIlluminated += ( lm->sw * lm->sh );
2048 /* test debugging state */
2049 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2050 /* debug fill the luxels */
2051 for ( y = 0; y < lm->sh; y++ )
2053 for ( x = 0; x < lm->sw; x++ )
2056 cluster = SUPER_CLUSTER( x, y );
2058 /* only fill mapped luxels */
2059 if ( *cluster < 0 ) {
2063 /* get particulars */
2064 luxel = SUPER_LUXEL( 0, x, y );
2065 origin = SUPER_ORIGIN( x, y );
2066 normal = SUPER_NORMAL( x, y );
2068 /* color the luxel with raw lightmap num? */
2069 if ( debugSurfaces ) {
2070 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2073 /* color the luxel with lightmap axis? */
2074 else if ( debugAxis ) {
2075 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2076 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2077 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2080 /* color the luxel with luxel cluster? */
2081 else if ( debugCluster ) {
2082 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2085 /* color the luxel with luxel origin? */
2086 else if ( debugOrigin ) {
2087 VectorSubtract( lm->maxs, lm->mins, temp );
2088 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2089 VectorSubtract( origin, lm->mins, temp2 );
2090 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2091 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2092 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2095 /* color the luxel with the normal */
2096 else if ( normalmap ) {
2097 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2098 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2099 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2102 /* otherwise clear it */
2104 VectorClear( luxel );
2114 /* allocate temporary per-light luxel storage */
2115 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2116 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2117 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2118 lightLuxels = stackLightLuxels;
2121 lightLuxels = safe_malloc( llSize );
2124 lightDeluxels = safe_malloc( ldSize );
2127 lightDeluxels = NULL;
2131 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2133 /* set ambient color */
2134 for ( y = 0; y < lm->sh; y++ )
2136 for ( x = 0; x < lm->sw; x++ )
2139 cluster = SUPER_CLUSTER( x, y );
2140 luxel = SUPER_LUXEL( 0, x, y );
2141 normal = SUPER_NORMAL( x, y );
2142 deluxel = SUPER_DELUXEL( x, y );
2144 /* blacken unmapped clusters */
2145 if ( *cluster < 0 ) {
2146 VectorClear( luxel );
2152 VectorCopy( ambientColor, luxel );
2154 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2156 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2157 if ( brightness < 0.00390625f ) {
2158 brightness = 0.00390625f;
2161 VectorScale( normal, brightness, deluxel );
2168 /* clear styled lightmaps */
2169 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2170 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2172 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2173 memset( lm->superLuxels[ lightmapNum ], 0, size );
2177 /* debugging code */
2178 //% if( trace.numLights <= 0 )
2179 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2181 /* walk light list */
2182 for ( i = 0; i < trace.numLights; i++ )
2185 trace.light = trace.lights[ i ];
2188 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2190 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2191 lm->styles[ lightmapNum ] == LS_NONE ) {
2196 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2197 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2198 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2203 memset( lightLuxels, 0, llSize );
2205 memset( lightDeluxels, 0, ldSize );
2209 /* determine filter radius */
2210 filterRadius = lm->filterRadius > trace.light->filterRadius
2212 : trace.light->filterRadius;
2213 if ( filterRadius < 0.0f ) {
2214 filterRadius = 0.0f;
2217 /* set luxel filter radius */
2218 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2219 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2220 luxelFilterRadius = 1;
2223 /* allocate sampling flags storage */
2224 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2225 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2226 if ( lm->superFlags == NULL ) {
2227 lm->superFlags = safe_malloc( size );
2229 memset( (void *) lm->superFlags, 0, size );
2232 /* initial pass, one sample per luxel */
2233 for ( y = 0; y < lm->sh; y++ )
2235 for ( x = 0; x < lm->sw; x++ )
2238 cluster = SUPER_CLUSTER( x, y );
2239 if ( *cluster < 0 ) {
2243 /* get particulars */
2244 lightLuxel = LIGHT_LUXEL( x, y );
2245 lightDeluxel = LIGHT_DELUXEL( x, y );
2246 origin = SUPER_ORIGIN( x, y );
2247 normal = SUPER_NORMAL( x, y );
2248 flag = SUPER_FLAG( x, y );
2251 ////////// 27's temp hack for testing edge clipping ////
2252 if ( origin[0] == 0 && origin[1] == 0 && origin[2] == 0 ) {
2253 lightLuxel[ 1 ] = 255;
2254 lightLuxel[ 3 ] = 1.0f;
2260 /* set contribution count */
2261 lightLuxel[ 3 ] = 1.0f;
2264 trace.cluster = *cluster;
2265 VectorCopy( origin, trace.origin );
2266 VectorCopy( normal, trace.normal );
2268 /* get light for this sample */
2269 LightContributionToSample( &trace );
2270 VectorCopy( trace.color, lightLuxel );
2272 /* add the contribution to the deluxemap */
2274 VectorCopy( trace.directionContribution, lightDeluxel );
2277 /* check for evilness */
2278 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2280 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2283 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2290 /* don't even bother with everything else if nothing was lit */
2291 if ( totalLighted == 0 ) {
2295 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2296 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2297 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2299 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2301 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2306 VectorClear( total );
2308 /* test 2x2 stamp */
2309 for ( t = 0; t < 4; t++ )
2311 /* set sample coords */
2312 sx = x + tests[ t ][ 0 ];
2313 sy = y + tests[ t ][ 1 ];
2316 cluster = SUPER_CLUSTER( sx, sy );
2317 if ( *cluster < 0 ) {
2323 flag = SUPER_FLAG( sx, sy );
2324 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2325 /* force a lighted/mapped discrepancy so we subsample */
2330 lightLuxel = LIGHT_LUXEL( sx, sy );
2331 VectorAdd( total, lightLuxel, total );
2332 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2337 /* if total color is under a certain amount, then don't bother subsampling */
2338 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2342 /* if all 4 pixels are either in shadow or light, then don't subsample */
2343 if ( lighted != 0 && lighted != mapped ) {
2344 for ( t = 0; t < 4; t++ )
2346 /* set sample coords */
2347 sx = x + tests[ t ][ 0 ];
2348 sy = y + tests[ t ][ 1 ];
2351 cluster = SUPER_CLUSTER( sx, sy );
2352 if ( *cluster < 0 ) {
2355 flag = SUPER_FLAG( sx, sy );
2356 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2359 lightLuxel = LIGHT_LUXEL( sx, sy );
2360 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2361 origin = SUPER_ORIGIN( sx, sy );
2363 /* only subsample shadowed luxels */
2364 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2368 if ( lightRandomSamples ) {
2369 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2372 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2375 *flag |= FLAG_ALREADY_SUBSAMPLED;
2377 /* debug code to colorize subsampled areas to yellow */
2378 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2379 //% VectorSet( luxel, 255, 204, 0 );
2386 /* tertiary pass, apply dirt map (ambient occlusion) */
2389 for ( y = 0; y < lm->sh; y++ )
2391 for ( x = 0; x < lm->sw; x++ )
2394 cluster = SUPER_CLUSTER( x, y );
2395 if ( *cluster < 0 ) {
2399 /* get particulars */
2400 lightLuxel = LIGHT_LUXEL( x, y );
2401 dirt = SUPER_DIRT( x, y );
2403 /* scale light value */
2404 VectorScale( lightLuxel, *dirt, lightLuxel );
2409 /* allocate sampling lightmap storage */
2410 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2411 /* allocate sampling lightmap storage */
2412 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2413 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2414 memset( lm->superLuxels[ lightmapNum ], 0, size );
2418 if ( lightmapNum > 0 ) {
2419 lm->styles[ lightmapNum ] = trace.light->style;
2420 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2423 /* copy to permanent luxels */
2424 for ( y = 0; y < lm->sh; y++ )
2426 for ( x = 0; x < lm->sw; x++ )
2428 /* get cluster and origin */
2429 cluster = SUPER_CLUSTER( x, y );
2430 if ( *cluster < 0 ) {
2433 origin = SUPER_ORIGIN( x, y );
2436 if ( luxelFilterRadius ) {
2438 VectorClear( averageColor );
2439 VectorClear( averageDir );
2442 /* cheaper distance-based filtering */
2443 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2445 if ( sy < 0 || sy >= lm->sh ) {
2449 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2451 if ( sx < 0 || sx >= lm->sw ) {
2455 /* get particulars */
2456 cluster = SUPER_CLUSTER( sx, sy );
2457 if ( *cluster < 0 ) {
2460 lightLuxel = LIGHT_LUXEL( sx, sy );
2461 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2464 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2465 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2467 /* scale luxel by filter weight */
2468 VectorScale( lightLuxel, weight, color );
2469 VectorAdd( averageColor, color, averageColor );
2471 VectorScale( lightDeluxel, weight, direction );
2472 VectorAdd( averageDir, direction, averageDir );
2479 if ( samples <= 0.0f ) {
2483 /* scale into luxel */
2484 luxel = SUPER_LUXEL( lightmapNum, x, y );
2487 /* handle negative light */
2488 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2489 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2490 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2491 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2494 /* handle normal light */
2497 luxel[ 0 ] += averageColor[ 0 ] / samples;
2498 luxel[ 1 ] += averageColor[ 1 ] / samples;
2499 luxel[ 2 ] += averageColor[ 2 ] / samples;
2503 /* scale into luxel */
2504 deluxel = SUPER_DELUXEL( x, y );
2505 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2506 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2507 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2514 /* get particulars */
2515 lightLuxel = LIGHT_LUXEL( x, y );
2516 lightDeluxel = LIGHT_DELUXEL( x, y );
2517 luxel = SUPER_LUXEL( lightmapNum, x, y );
2518 deluxel = SUPER_DELUXEL( x, y );
2520 /* handle negative light */
2521 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2522 VectorScale( averageColor, -1.0f, averageColor );
2528 /* handle negative light */
2529 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2530 VectorSubtract( luxel, lightLuxel, luxel );
2533 /* handle normal light */
2535 VectorAdd( luxel, lightLuxel, luxel );
2539 VectorAdd( deluxel, lightDeluxel, deluxel );
2546 /* free temporary luxels */
2547 if ( lightLuxels != stackLightLuxels ) {
2548 free( lightLuxels );
2552 free( lightDeluxels );
2556 /* free light list */
2557 FreeTraceLights( &trace );
2559 /* floodlight pass */
2560 if ( floodlighty ) {
2561 FloodlightIlluminateLightmap( lm );
2564 if ( debugnormals ) {
2565 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2568 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2572 for ( y = 0; y < lm->sh; y++ )
2574 for ( x = 0; x < lm->sw; x++ )
2577 cluster = SUPER_CLUSTER( x, y );
2578 //% if( *cluster < 0 )
2581 /* get particulars */
2582 luxel = SUPER_LUXEL( lightmapNum, x, y );
2583 normal = SUPER_NORMAL( x, y );
2585 luxel[0] = ( normal[0] * 127 ) + 127;
2586 luxel[1] = ( normal[1] * 127 ) + 127;
2587 luxel[2] = ( normal[2] * 127 ) + 127;
2593 /* -----------------------------------------------------------------
2595 ----------------------------------------------------------------- */
2598 /* walk lightmaps */
2599 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2602 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2606 /* apply dirt to each luxel */
2607 for ( y = 0; y < lm->sh; y++ )
2609 for ( x = 0; x < lm->sw; x++ )
2612 cluster = SUPER_CLUSTER( x, y );
2613 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2616 /* get particulars */
2617 luxel = SUPER_LUXEL( lightmapNum, x, y );
2618 dirt = SUPER_DIRT( x, y );
2621 VectorScale( luxel, *dirt, luxel );
2625 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2632 /* -----------------------------------------------------------------
2634 ----------------------------------------------------------------- */
2636 /* walk lightmaps */
2637 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2640 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2644 /* average occluded luxels from neighbors */
2645 for ( y = 0; y < lm->sh; y++ )
2647 for ( x = 0; x < lm->sw; x++ )
2649 /* get particulars */
2650 cluster = SUPER_CLUSTER( x, y );
2651 luxel = SUPER_LUXEL( lightmapNum, x, y );
2652 deluxel = SUPER_DELUXEL( x, y );
2653 normal = SUPER_NORMAL( x, y );
2655 /* determine if filtering is necessary */
2656 filterColor = qfalse;
2658 if ( *cluster < 0 ||
2659 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2660 filterColor = qtrue;
2663 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2667 if ( !filterColor && !filterDir ) {
2671 /* choose seed amount */
2672 VectorClear( averageColor );
2673 VectorClear( averageDir );
2676 /* walk 3x3 matrix */
2677 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2679 if ( sy < 0 || sy >= lm->sh ) {
2683 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2685 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2689 /* get neighbor's particulars */
2690 cluster2 = SUPER_CLUSTER( sx, sy );
2691 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2692 deluxel2 = SUPER_DELUXEL( sx, sy );
2694 /* ignore unmapped/unlit luxels */
2695 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2696 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2700 /* add its distinctiveness to our own */
2701 VectorAdd( averageColor, luxel2, averageColor );
2702 samples += luxel2[ 3 ];
2704 VectorAdd( averageDir, deluxel2, averageDir );
2710 if ( samples <= 0.0f ) {
2714 /* dark lightmap seams */
2716 if ( lightmapNum == 0 ) {
2717 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2723 if ( filterColor ) {
2724 VectorDivide( averageColor, samples, luxel );
2728 VectorDivide( averageDir, samples, deluxel );
2731 /* set cluster to -3 */
2732 if ( *cluster < 0 ) {
2733 *cluster = CLUSTER_FLOODED;
2742 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2745 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2748 for ( y = 0; y < lm->sh; y++ )
2749 for ( x = 0; x < lm->sw; x++ )
2752 cluster = SUPER_CLUSTER( x, y );
2753 luxel = SUPER_LUXEL( lightmapNum, x, y );
2754 deluxel = SUPER_DELUXEL( x, y );
2755 if ( !luxel || !deluxel || !cluster ) {
2756 Sys_FPrintf( SYS_VRB, "WARNING: I got NULL'd.\n" );
2759 else if ( *cluster < 0 ) {
2761 // should have neither deluxemap nor lightmap
2763 Sys_FPrintf( SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n" );
2769 // should have both deluxemap and lightmap
2771 Sys_FPrintf( SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n" );
2782 IlluminateVertexes()
2783 light the surface vertexes
2786 #define VERTEX_NUDGE 4.0f
2788 void IlluminateVertexes( int num ){
2789 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2790 int lightmapNum, numAvg;
2791 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2792 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2793 bspDrawSurface_t *ds;
2794 surfaceInfo_t *info;
2796 bspDrawVert_t *verts;
2798 float floodLightAmount;
2802 /* get surface, info, and raw lightmap */
2803 ds = &bspDrawSurfaces[ num ];
2804 info = &surfaceInfos[ num ];
2807 /* -----------------------------------------------------------------
2808 illuminate the vertexes
2809 ----------------------------------------------------------------- */
2811 /* calculate vertex lighting for surfaces without lightmaps */
2812 if ( lm == NULL || cpmaHack ) {
2814 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2815 trace.forceSunlight = info->si->forceSunlight;
2816 trace.recvShadows = info->recvShadows;
2817 trace.numSurfaces = 1;
2818 trace.surfaces = #
2819 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2821 /* twosided lighting */
2822 trace.twoSided = info->si->twoSided;
2824 /* make light list for this surface */
2825 CreateTraceLightsForSurface( num, &trace );
2828 verts = yDrawVerts + ds->firstVert;
2830 memset( avgColors, 0, sizeof( avgColors ) );
2832 /* walk the surface verts */
2833 for ( i = 0; i < ds->numVerts; i++ )
2835 /* get vertex luxel */
2836 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2838 /* color the luxel with raw lightmap num? */
2839 if ( debugSurfaces ) {
2840 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2843 /* color the luxel with luxel origin? */
2844 else if ( debugOrigin ) {
2845 VectorSubtract( info->maxs, info->mins, temp );
2846 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2847 VectorSubtract( origin, lm->mins, temp2 );
2848 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2849 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2850 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2853 /* color the luxel with the normal */
2854 else if ( normalmap ) {
2855 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2856 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2857 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2860 /* illuminate the vertex */
2863 /* clear vertex luxel */
2864 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2866 /* try at initial origin */
2867 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2868 if ( trace.cluster >= 0 ) {
2870 VectorCopy( verts[ i ].xyz, trace.origin );
2871 VectorCopy( verts[ i ].normal, trace.normal );
2874 if ( dirty && !bouncing ) {
2875 dirt = DirtForSample( &trace );
2881 /* jal: floodlight */
2882 floodLightAmount = 0.0f;
2883 VectorClear( floodColor );
2884 if ( floodlighty && !bouncing ) {
2885 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2886 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2890 LightingAtSample( &trace, ds->vertexStyles, colors );
2893 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2896 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2898 /* jal: floodlight */
2899 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2902 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2903 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2904 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2908 /* is this sample bright enough? */
2909 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2910 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2911 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2912 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2913 /* nudge the sample point around a bit */
2914 for ( x = 0; x < 5; x++ )
2916 /* two's complement 0, 1, -1, 2, -2, etc */
2917 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2919 for ( y = 0; y < 5; y++ )
2921 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2923 for ( z = 0; z < 5; z++ )
2925 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2928 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2929 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2930 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2932 /* try at nudged origin */
2933 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2934 if ( trace.cluster < 0 ) {
2939 if ( dirty && !bouncing ) {
2940 dirt = DirtForSample( &trace );
2946 /* jal: floodlight */
2947 floodLightAmount = 0.0f;
2948 VectorClear( floodColor );
2949 if ( floodlighty && !bouncing ) {
2950 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2951 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2955 LightingAtSample( &trace, ds->vertexStyles, colors );
2958 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2961 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2963 /* jal: floodlight */
2964 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2967 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2968 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2971 /* bright enough? */
2972 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2973 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2974 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2975 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2983 /* add to average? */
2984 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2985 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2986 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2987 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2989 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2991 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2992 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2997 /* another happy customer */
2998 numVertsIlluminated++;
3001 /* set average color */
3003 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3004 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
3008 VectorCopy( ambientColor, avgColors[ 0 ] );
3011 /* clean up and store vertex color */
3012 for ( i = 0; i < ds->numVerts; i++ )
3014 /* get vertex luxel */
3015 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
3017 /* store average in occluded vertexes */
3018 if ( radVertLuxel[ 0 ] < 0.0f ) {
3019 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3021 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3022 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
3025 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3030 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3033 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3034 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3037 if ( bouncing || bounce == 0 || !bounceOnly ) {
3038 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3040 if ( !info->si->noVertexLight ) {
3041 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3046 /* free light list */
3047 FreeTraceLights( &trace );
3049 /* return to sender */
3053 /* -----------------------------------------------------------------
3054 reconstitute vertex lighting from the luxels
3055 ----------------------------------------------------------------- */
3057 /* set styles from lightmap */
3058 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3059 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3061 /* get max search radius */
3063 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3065 /* walk the surface verts */
3066 verts = yDrawVerts + ds->firstVert;
3067 for ( i = 0; i < ds->numVerts; i++ )
3069 /* do each lightmap */
3070 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3073 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3077 /* get luxel coords */
3078 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3079 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3083 else if ( x >= lm->sw ) {
3089 else if ( y >= lm->sh ) {
3093 /* get vertex luxels */
3094 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3095 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3097 /* color the luxel with the normal? */
3099 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3100 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3101 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3104 /* color the luxel with surface num? */
3105 else if ( debugSurfaces ) {
3106 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3109 /* divine color from the superluxels */
3112 /* increasing radius */
3113 VectorClear( radVertLuxel );
3115 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3117 /* sample within radius */
3118 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3120 if ( sy < 0 || sy >= lm->sh ) {
3124 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3126 if ( sx < 0 || sx >= lm->sw ) {
3130 /* get luxel particulars */
3131 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3132 cluster = SUPER_CLUSTER( sx, sy );
3133 if ( *cluster < 0 ) {
3137 /* testing: must be brigher than ambient color */
3138 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3141 /* add its distinctiveness to our own */
3142 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3143 samples += luxel[ 3 ];
3149 if ( samples > 0.0f ) {
3150 VectorDivide( radVertLuxel, samples, radVertLuxel );
3153 VectorCopy( ambientColor, radVertLuxel );
3157 /* store into floating point storage */
3158 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3159 numVertsIlluminated++;
3161 /* store into bytes (for vertex approximation) */
3162 if ( !info->si->noVertexLight ) {
3163 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3171 /* -------------------------------------------------------------------------------
3173 light optimization (-fast)
3175 creates a list of lights that will affect a surface and stores it in tw
3176 this is to optimize surface lighting by culling out as many of the
3177 lights in the world as possible from further calculation
3179 ------------------------------------------------------------------------------- */
3183 determines opaque brushes in the world and find sky shaders for sunlight calculations
3186 void SetupBrushesFlags( int mask_any, int test_any, int mask_all, int test_all ){
3188 unsigned int compileFlags, allCompileFlags;
3191 bspBrushSide_t *side;
3192 bspShader_t *shader;
3197 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3200 if ( opaqueBrushes == NULL ) {
3201 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3205 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3206 numOpaqueBrushes = 0;
3208 /* walk the list of worldspawn brushes */
3209 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3212 b = bspModels[ 0 ].firstBSPBrush + i;
3213 brush = &bspBrushes[ b ];
3215 /* check all sides */
3218 allCompileFlags = ~( 0u );
3219 for ( j = 0; j < brush->numSides && inside; j++ )
3221 /* do bsp shader calculations */
3222 side = &bspBrushSides[ brush->firstSide + j ];
3223 shader = &bspShaders[ side->shaderNum ];
3225 /* get shader info */
3226 si = ShaderInfoForShaderNull( shader->shader );
3231 /* or together compile flags */
3232 compileFlags |= si->compileFlags;
3233 allCompileFlags &= si->compileFlags;
3236 /* determine if this brush is opaque to light */
3237 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3238 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3244 /* emit some statistics */
3245 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3247 void SetupBrushes( void ){
3248 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3255 determines if two clusters are visible to each other using the PVS
3258 qboolean ClusterVisible( int a, int b ){
3264 if ( a < 0 || b < 0 ) {
3274 if ( numBSPVisBytes <= 8 ) {
3279 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3280 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3281 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3284 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3294 borrowed from vlight.c
3297 int PointInLeafNum_r( vec3_t point, int nodenum ){
3304 while ( nodenum >= 0 )
3306 node = &bspNodes[ nodenum ];
3307 plane = &bspPlanes[ node->planeNum ];
3308 dist = DotProduct( point, plane->normal ) - plane->dist;
3310 nodenum = node->children[ 0 ];
3312 else if ( dist < -0.1 ) {
3313 nodenum = node->children[ 1 ];
3317 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3318 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3321 nodenum = node->children[ 1 ];
3325 leafnum = -nodenum - 1;
3333 borrowed from vlight.c
3336 int PointInLeafNum( vec3_t point ){
3337 return PointInLeafNum_r( point, 0 );
3343 ClusterVisibleToPoint() - ydnar
3344 returns qtrue if point can "see" cluster
3347 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3351 /* get leafNum for point */
3352 pointCluster = ClusterForPoint( point );
3353 if ( pointCluster < 0 ) {
3358 return ClusterVisible( pointCluster, cluster );
3364 ClusterForPoint() - ydnar
3365 returns the pvs cluster for point
3368 int ClusterForPoint( vec3_t point ){
3372 /* get leafNum for point */
3373 leafNum = PointInLeafNum( point );
3374 if ( leafNum < 0 ) {
3378 /* return the cluster */
3379 return bspLeafs[ leafNum ].cluster;
3385 ClusterForPointExt() - ydnar
3386 also takes brushes into account for occlusion testing
3389 int ClusterForPointExt( vec3_t point, float epsilon ){
3390 int i, j, b, leafNum, cluster;
3393 int *brushes, numBSPBrushes;
3399 /* get leaf for point */
3400 leafNum = PointInLeafNum( point );
3401 if ( leafNum < 0 ) {
3404 leaf = &bspLeafs[ leafNum ];
3406 /* get the cluster */
3407 cluster = leaf->cluster;
3408 if ( cluster < 0 ) {
3412 /* transparent leaf, so check point against all brushes in the leaf */
3413 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3414 numBSPBrushes = leaf->numBSPLeafBrushes;
3415 for ( i = 0; i < numBSPBrushes; i++ )
3419 if ( b > maxOpaqueBrush ) {
3422 brush = &bspBrushes[ b ];
3423 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3427 /* check point against all planes */
3429 for ( j = 0; j < brush->numSides && inside; j++ )
3431 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3432 dot = DotProduct( point, plane->normal );
3434 if ( dot > epsilon ) {
3439 /* if inside, return bogus cluster */
3445 /* if the point made it this far, it's not inside any opaque brushes */
3452 ClusterForPointExtFilter() - ydnar
3453 adds cluster checking against a list of known valid clusters
3456 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3460 /* get cluster for point */
3461 cluster = ClusterForPointExt( point, epsilon );
3463 /* check if filtering is necessary */
3464 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3469 for ( i = 0; i < numClusters; i++ )
3471 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3483 ShaderForPointInLeaf() - ydnar
3484 checks a point against all brushes in a leaf, returning the shader of the brush
3485 also sets the cumulative surface and content flags for the brush hit
3488 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3492 int *brushes, numBSPBrushes;
3495 bspBrushSide_t *side;
3497 bspShader_t *shader;
3498 int allSurfaceFlags, allContentFlags;
3501 /* clear things out first */
3506 if ( leafNum < 0 ) {
3509 leaf = &bspLeafs[ leafNum ];
3511 /* transparent leaf, so check point against all brushes in the leaf */
3512 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3513 numBSPBrushes = leaf->numBSPLeafBrushes;
3514 for ( i = 0; i < numBSPBrushes; i++ )
3517 brush = &bspBrushes[ brushes[ i ] ];
3519 /* check point against all planes */
3521 allSurfaceFlags = 0;
3522 allContentFlags = 0;
3523 for ( j = 0; j < brush->numSides && inside; j++ )
3525 side = &bspBrushSides[ brush->firstSide + j ];
3526 plane = &bspPlanes[ side->planeNum ];
3527 dot = DotProduct( point, plane->normal );
3529 if ( dot > epsilon ) {
3534 shader = &bspShaders[ side->shaderNum ];
3535 allSurfaceFlags |= shader->surfaceFlags;
3536 allContentFlags |= shader->contentFlags;
3540 /* handle if inside */
3542 /* if there are desired flags, check for same and continue if they aren't matched */
3543 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3546 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3550 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3551 *surfaceFlags = allSurfaceFlags;
3552 *contentFlags = allContentFlags;
3553 return brush->shaderNum;
3557 /* if the point made it this far, it's not inside any brushes */
3565 chops a bounding box by the plane defined by origin and normal
3566 returns qfalse if the bounds is entirely clipped away
3568 this is not exactly the fastest way to do this...
3571 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3572 /* FIXME: rewrite this so it doesn't use bloody brushes */
3580 calculates each light's effective envelope,
3581 taking into account brightness, type, and pvs.
3584 #define LIGHT_EPSILON 0.125f
3585 #define LIGHT_NUDGE 2.0f
3587 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3588 int i, x, y, z, x1, y1, z1;
3589 light_t *light, *light2, **owner;
3591 vec3_t origin, dir, mins, maxs;
3592 float radius, intensity;
3593 light_t *buckets[ 256 ];
3596 /* early out for weird cases where there are no lights */
3597 if ( lights == NULL ) {
3602 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3606 numCulledLights = 0;
3608 while ( *owner != NULL )
3613 /* handle negative lights */
3614 if ( light->photons < 0.0f || light->add < 0.0f ) {
3615 light->photons *= -1.0f;
3616 light->add *= -1.0f;
3617 light->flags |= LIGHT_NEGATIVE;
3621 if ( light->type == EMIT_SUN ) {
3624 light->envelope = MAX_WORLD_COORD * 8.0f;
3625 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3626 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3629 /* everything else */
3632 /* get pvs cluster for light */
3633 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3635 /* invalid cluster? */
3636 if ( light->cluster < 0 ) {
3637 /* nudge the sample point around a bit */
3638 for ( x = 0; x < 4; x++ )
3640 /* two's complement 0, 1, -1, 2, -2, etc */
3641 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3643 for ( y = 0; y < 4; y++ )
3645 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3647 for ( z = 0; z < 4; z++ )
3649 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3652 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3653 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3654 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3656 /* try at nudged origin */
3657 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3658 if ( light->cluster < 0 ) {
3663 VectorCopy( origin, light->origin );
3669 /* only calculate for lights in pvs and outside of opaque brushes */
3670 if ( light->cluster >= 0 ) {
3671 /* set light fast flag */
3673 light->flags |= LIGHT_FAST_TEMP;
3676 light->flags &= ~LIGHT_FAST_TEMP;
3678 if ( fastpoint && ( light->flags != EMIT_AREA ) ) {
3679 light->flags |= LIGHT_FAST_TEMP;
3681 if ( light->si && light->si->noFast ) {
3682 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3685 /* clear light envelope */
3686 light->envelope = 0;
3688 /* handle area lights */
3689 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3690 light->envelope = MAX_WORLD_COORD * 8.0f;
3692 /* check for fast mode */
3693 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3694 /* ugly hack to calculate extent for area lights, but only done once */
3695 VectorScale( light->normal, -1.0f, dir );
3696 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3700 VectorMA( light->origin, radius, light->normal, origin );
3701 factor = PointToPolygonFormFactor( origin, dir, light->w );
3702 if ( factor < 0.0f ) {
3705 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3706 light->envelope = radius;
3712 intensity = light->photons; /* hopefully not used */
3717 intensity = light->photons;
3721 if ( light->envelope <= 0.0f ) {
3722 /* solve distance for non-distance lights */
3723 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3724 light->envelope = MAX_WORLD_COORD * 8.0f;
3727 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3728 /* solve distance for linear lights */
3729 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3730 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3734 add = angle * light->photons * linearScale - (dist * light->fade);
3735 T = (light->photons * linearScale) - (dist * light->fade);
3736 T + (dist * light->fade) = (light->photons * linearScale);
3737 dist * light->fade = (light->photons * linearScale) - T;
3738 dist = ((light->photons * linearScale) - T) / light->fade;
3741 /* solve for inverse square falloff */
3743 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3747 add = light->photons / (dist * dist);
3748 T = light->photons / (dist * dist);
3749 T * (dist * dist) = light->photons;
3750 dist = sqrt( light->photons / T );
3755 /* solve distance for linear lights */
3756 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3757 light->envelope = ( intensity * linearScale ) / light->fade;
3760 /* can't cull these */
3762 light->envelope = MAX_WORLD_COORD * 8.0f;
3767 /* chop radius against pvs */
3770 ClearBounds( mins, maxs );
3772 /* check all leaves */
3773 for ( i = 0; i < numBSPLeafs; i++ )
3776 leaf = &bspLeafs[ i ];
3779 if ( leaf->cluster < 0 ) {
3782 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3786 /* add this leafs bbox to the bounds */
3787 VectorCopy( leaf->mins, origin );
3788 AddPointToBounds( origin, mins, maxs );
3789 VectorCopy( leaf->maxs, origin );
3790 AddPointToBounds( origin, mins, maxs );
3793 /* test to see if bounds encompass light */
3794 for ( i = 0; i < 3; i++ )
3796 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3797 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3798 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3799 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3800 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3801 AddPointToBounds( light->origin, mins, maxs );
3805 /* chop the bounds by a plane for area lights and spotlights */
3806 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3807 ChopBounds( mins, maxs, light->origin, light->normal );
3811 VectorCopy( mins, light->mins );
3812 VectorCopy( maxs, light->maxs );
3814 /* reflect bounds around light origin */
3815 //% VectorMA( light->origin, -1.0f, origin, origin );
3816 VectorScale( light->origin, 2, origin );
3817 VectorSubtract( origin, maxs, origin );
3818 AddPointToBounds( origin, mins, maxs );
3819 //% VectorMA( light->origin, -1.0f, mins, origin );
3820 VectorScale( light->origin, 2, origin );
3821 VectorSubtract( origin, mins, origin );
3822 AddPointToBounds( origin, mins, maxs );
3824 /* calculate spherical bounds */
3825 VectorSubtract( maxs, light->origin, dir );
3826 radius = (float) VectorLength( dir );
3828 /* if this radius is smaller than the envelope, then set the envelope to it */
3829 if ( radius < light->envelope ) {
3830 light->envelope = radius;
3831 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3834 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3837 /* add grid/surface only check */
3839 if ( !( light->flags & LIGHT_GRID ) ) {
3840 light->envelope = 0.0f;
3845 if ( !( light->flags & LIGHT_SURFACES ) ) {
3846 light->envelope = 0.0f;
3852 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3854 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3856 /* delete the light */
3858 *owner = light->next;
3859 if ( light->w != NULL ) {
3867 /* square envelope */
3868 light->envelope2 = ( light->envelope * light->envelope );
3870 /* increment light count */
3873 /* set next light */
3874 owner = &( ( **owner ).next );
3877 /* bucket sort lights by style */
3878 memset( buckets, 0, sizeof( buckets ) );
3880 for ( light = lights; light != NULL; light = light2 )
3882 /* get next light */
3883 light2 = light->next;
3885 /* filter into correct bucket */
3886 light->next = buckets[ light->style ];
3887 buckets[ light->style ] = light;
3889 /* if any styled light is present, automatically set nocollapse */
3890 if ( light->style != LS_NORMAL ) {
3895 /* filter back into light list */
3897 for ( i = 255; i >= 0; i-- )
3900 for ( light = buckets[ i ]; light != NULL; light = light2 )
3902 light2 = light->next;
3903 light->next = lights;
3908 /* emit some statistics */
3909 Sys_Printf( "%9d total lights\n", numLights );
3910 Sys_Printf( "%9d culled lights\n", numCulledLights );
3916 CreateTraceLightsForBounds()
3917 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3920 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3923 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3924 float radius, dist, length;
3927 /* potential pre-setup */
3928 if ( numLights == 0 ) {
3929 SetupEnvelopes( qfalse, fast );
3933 //% 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 ] );
3935 /* allocate the light list */
3936 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3937 trace->numLights = 0;
3939 /* calculate spherical bounds */
3940 VectorAdd( mins, maxs, origin );
3941 VectorScale( origin, 0.5f, origin );
3942 VectorSubtract( maxs, origin, dir );
3943 radius = (float) VectorLength( dir );
3945 /* get length of normal vector */
3946 if ( normal != NULL ) {
3947 length = VectorLength( normal );
3951 normal = nullVector;
3955 /* test each light and see if it reaches the sphere */
3956 /* note: the attenuation code MUST match LightingAtSample() */
3957 for ( light = lights; light; light = light->next )
3959 /* check zero sized envelope */
3960 if ( light->envelope <= 0 ) {
3961 lightsEnvelopeCulled++;
3966 if ( !( light->flags & flags ) ) {
3970 /* sunlight skips all this nonsense */
3971 if ( light->type != EMIT_SUN ) {
3977 /* check against pvs cluster */
3978 if ( numClusters > 0 && clusters != NULL ) {
3979 for ( i = 0; i < numClusters; i++ )
3981 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3987 if ( i == numClusters ) {
3988 lightsClusterCulled++;
3993 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3994 VectorSubtract( light->origin, origin, dir );
3995 dist = VectorLength( dir );
3996 dist -= light->envelope;
3999 lightsEnvelopeCulled++;
4003 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
4006 for ( i = 0; i < 3; i++ )
4008 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
4013 lightsBoundsCulled++;
4019 /* planar surfaces (except twosided surfaces) have a couple more checks */
4020 if ( length > 0.0f && trace->twoSided == qfalse ) {
4021 /* lights coplanar with a surface won't light it */
4022 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
4023 lightsPlaneCulled++;
4027 /* check to see if light is behind the plane */
4028 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4029 lightsPlaneCulled++;
4034 /* add this light */
4035 trace->lights[ trace->numLights++ ] = light;
4038 /* make last night null */
4039 trace->lights[ trace->numLights ] = NULL;
4044 void FreeTraceLights( trace_t *trace ){
4045 if ( trace->lights != NULL ) {
4046 free( trace->lights );
4053 CreateTraceLightsForSurface()
4054 creates a list of lights that can potentially affect a drawsurface
4057 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4059 vec3_t mins, maxs, normal;
4061 bspDrawSurface_t *ds;
4062 surfaceInfo_t *info;
4070 /* get drawsurface and info */
4071 ds = &bspDrawSurfaces[ num ];
4072 info = &surfaceInfos[ num ];
4074 /* get the mins/maxs for the dsurf */
4075 ClearBounds( mins, maxs );
4076 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4077 for ( i = 0; i < ds->numVerts; i++ )
4079 dv = &yDrawVerts[ ds->firstVert + i ];
4080 AddPointToBounds( dv->xyz, mins, maxs );
4081 if ( !VectorCompare( dv->normal, normal ) ) {
4082 VectorClear( normal );
4086 /* create the lights for the bounding box */
4087 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4090 /////////////////////////////////////////////////////////////
4092 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4093 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4094 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4095 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4097 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4098 static int numFloodVectors = 0;
4100 void SetupFloodLight( void ){
4102 float angle, elevation, angleStep, elevationStep;
4104 double v1,v2,v3,v4,v5,v6;
4107 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4109 /* calculate angular steps */
4110 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4111 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4115 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4117 /* iterate elevation */
4118 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4120 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4121 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4122 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4127 /* emit some statistics */
4128 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4131 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4133 if ( value[ 0 ] != '\0' ) {
4135 v4 = floodlightDistance;
4136 v5 = floodlightIntensity;
4137 v6 = floodlightDirectionScale;
4139 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4141 floodlightRGB[0] = v1;
4142 floodlightRGB[1] = v2;
4143 floodlightRGB[2] = v3;
4145 if ( VectorLength( floodlightRGB ) == 0 ) {
4146 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4159 floodlightDistance = v4;
4160 floodlightIntensity = v5;
4161 floodlightDirectionScale = v6;
4163 floodlighty = qtrue;
4164 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4168 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4171 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4172 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4173 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4175 ColorNormalize( floodlightRGB,floodlightRGB );
4179 FloodLightForSample()
4180 calculates floodlight value for a given sample
4181 once again, kudos to the dirtmapping coder
4184 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4189 float gatherLight, outLight;
4190 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4198 if ( trace == NULL || trace->cluster < 0 ) {
4204 dd = floodLightDistance;
4205 VectorCopy( trace->normal, normal );
4207 /* check if the normal is aligned to the world-up */
4208 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4209 if ( normal[ 2 ] == 1.0f ) {
4210 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4211 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4213 else if ( normal[ 2 ] == -1.0f ) {
4214 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4215 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4220 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4221 CrossProduct( normal, worldUp, myRt );
4222 VectorNormalize( myRt, myRt );
4223 CrossProduct( myRt, normal, myUp );
4224 VectorNormalize( myUp, myUp );
4227 /* vortex: optimise floodLightLowQuality a bit */
4228 if ( floodLightLowQuality == qtrue ) {
4229 /* iterate through ordered vectors */
4230 for ( i = 0; i < numFloodVectors; i++ )
4231 if ( rand() % 10 != 0 ) {
4237 /* iterate through ordered vectors */
4238 for ( i = 0; i < numFloodVectors; i++ )
4242 /* transform vector into tangent space */
4243 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4244 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4245 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4248 VectorMA( trace->origin, dd, direction, trace->end );
4250 //VectorMA( trace->origin, 1, direction, trace->origin );
4252 SetupTrace( trace );
4253 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4258 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4259 contribution = 1.0f;
4261 else if ( trace->opaque ) {
4262 VectorSubtract( trace->hit, trace->origin, displacement );
4263 d = VectorLength( displacement );
4265 // d=trace->distance;
4266 //if (d>256) gatherDirt+=1;
4267 contribution = d / dd;
4268 if ( contribution > 1 ) {
4269 contribution = 1.0f;
4272 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4275 gatherLight += contribution;
4280 if ( gatherLight <= 0.0f ) {
4289 gatherLight /= ( sub );
4291 outLight = gatherLight;
4292 if ( outLight > 1.0f ) {
4296 /* return to sender */
4301 FloodLightRawLightmap
4302 lighttracer style ambient occlusion light hack.
4303 Kudos to the dirtmapping author for most of this source.
4304 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4305 VorteX: fixed problems with deluxemapping
4308 // floodlight pass on a lightmap
4309 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4310 int i, x, y, *cluster;
4311 float *origin, *normal, *floodlight, floodLightAmount;
4312 surfaceInfo_t *info;
4315 // float samples, average, *floodlight2;
4317 memset( &trace,0,sizeof( trace_t ) );
4320 trace.testOcclusion = qtrue;
4321 trace.forceSunlight = qfalse;
4322 trace.twoSided = qtrue;
4323 trace.recvShadows = lm->recvShadows;
4324 trace.numSurfaces = lm->numLightSurfaces;
4325 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4326 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4327 trace.testAll = qfalse;
4328 trace.distance = 1024;
4330 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4331 //trace.twoSided = qfalse;
4332 for ( i = 0; i < trace.numSurfaces; i++ )
4335 info = &surfaceInfos[ trace.surfaces[ i ] ];
4337 /* check twosidedness */
4338 if ( info->si->twoSided ) {
4339 trace.twoSided = qtrue;
4344 /* gather floodlight */
4345 for ( y = 0; y < lm->sh; y++ )
4347 for ( x = 0; x < lm->sw; x++ )
4350 cluster = SUPER_CLUSTER( x, y );
4351 origin = SUPER_ORIGIN( x, y );
4352 normal = SUPER_NORMAL( x, y );
4353 floodlight = SUPER_FLOODLIGHT( x, y );
4355 /* set default dirt */
4358 /* only look at mapped luxels */
4359 if ( *cluster < 0 ) {
4364 trace.cluster = *cluster;
4365 VectorCopy( origin, trace.origin );
4366 VectorCopy( normal, trace.normal );
4368 /* get floodlight */
4369 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4371 /* add floodlight */
4372 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4373 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4374 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4375 floodlight[3] += floodlightDirectionScale;
4379 /* testing no filtering */
4385 for ( y = 0; y < lm->sh; y++ )
4387 for ( x = 0; x < lm->sw; x++ )
4390 cluster = SUPER_CLUSTER( x, y );
4391 floodlight = SUPER_FLOODLIGHT( x, y );
4393 /* filter dirt by adjacency to unmapped luxels */
4394 average = *floodlight;
4396 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4398 if ( sy < 0 || sy >= lm->sh ) {
4402 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4404 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4408 /* get neighboring luxel */
4409 cluster = SUPER_CLUSTER( sx, sy );
4410 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4411 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4416 average += *floodlight2;
4421 if ( samples <= 0.0f ) {
4427 if ( samples <= 0.0f ) {
4432 *floodlight = average / samples;
4438 void FloodLightRawLightmap( int rawLightmapNum ){
4441 /* bail if this number exceeds the number of raw lightmaps */
4442 if ( rawLightmapNum >= numRawLightmaps ) {
4446 lm = &rawLightmaps[ rawLightmapNum ];
4449 if ( floodlighty && floodlightIntensity ) {
4450 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4454 if ( lm->floodlightIntensity ) {
4455 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4456 numSurfacesFloodlighten += 1;
4460 void FloodlightRawLightmaps(){
4461 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4462 numSurfacesFloodlighten = 0;
4463 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4464 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4468 FloodLightIlluminate()
4469 illuminate floodlight into lightmap luxels
4472 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4473 float *luxel, *floodlight, *deluxel, *normal;
4476 int x, y, lightmapNum;
4478 /* walk lightmaps */
4479 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4482 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4486 /* apply floodlight to each luxel */
4487 for ( y = 0; y < lm->sh; y++ )
4489 for ( x = 0; x < lm->sw; x++ )
4491 /* get floodlight */
4492 floodlight = SUPER_FLOODLIGHT( x, y );
4493 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4498 cluster = SUPER_CLUSTER( x, y );
4500 /* only process mapped luxels */
4501 if ( *cluster < 0 ) {
4505 /* get particulars */
4506 luxel = SUPER_LUXEL( lightmapNum, x, y );
4507 deluxel = SUPER_DELUXEL( x, y );
4509 /* add to lightmap */
4510 luxel[0] += floodlight[0];
4511 luxel[1] += floodlight[1];
4512 luxel[2] += floodlight[2];
4514 if ( luxel[3] == 0 ) {
4518 /* add to deluxemap */
4519 if ( deluxemap && floodlight[3] > 0 ) {
4522 normal = SUPER_NORMAL( x, y );
4523 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4525 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4526 if ( brightness < 0.00390625f ) {
4527 brightness = 0.00390625f;
4530 VectorScale( normal, brightness, lightvector );
4531 VectorAdd( deluxel, lightvector, deluxel );