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 );
1745 Error( "Spurious lightmap S vector\n" );
1748 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1749 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1751 /* calulate y vector */
1752 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1753 cluster = SUPER_CLUSTER( x, y );
1754 origin = SUPER_ORIGIN( x, y );
1755 //% normal = SUPER_NORMAL( x, y );
1756 cluster2 = SUPER_CLUSTER( x, y + 1 );
1757 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1758 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1760 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1761 cluster = SUPER_CLUSTER( x, y - 1 );
1762 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1763 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1764 cluster2 = SUPER_CLUSTER( x, y );
1765 origin2 = SUPER_ORIGIN( x, y );
1766 //% normal2 = SUPER_NORMAL( x, y );
1769 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1772 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1773 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1775 /* calculate new origin */
1776 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1777 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1778 for ( i = 0; i < 3; i++ )
1779 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1782 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1783 if ( *sampleCluster < 0 ) {
1787 /* calculate new normal */
1788 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1789 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1790 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1792 normal = SUPER_NORMAL( x, y );
1793 VectorCopy( normal, sampleNormal );
1801 SubsampleRawLuxel_r()
1802 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1805 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1806 int b, samples, mapped, lighted;
1809 vec3_t deluxel[ 3 ];
1810 vec3_t origin[ 4 ], normal[ 4 ];
1811 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1812 vec3_t color, direction = { 0, 0, 0 }, total;
1816 if ( lightLuxel[ 3 ] >= lightSamples ) {
1821 VectorClear( total );
1825 /* make 2x2 subsample stamp */
1826 for ( b = 0; b < 4; b++ )
1829 VectorCopy( sampleOrigin, origin[ b ] );
1831 /* calculate position */
1832 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1838 /* increment sample count */
1839 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1842 trace->cluster = *cluster;
1843 VectorCopy( origin[ b ], trace->origin );
1844 VectorCopy( normal[ b ], trace->normal );
1848 LightContributionToSample( trace );
1849 if ( trace->forceSubsampling > 1.0f ) {
1850 /* alphashadow: we subsample as deep as we can */
1856 /* add to totals (fixme: make contrast function) */
1857 VectorCopy( trace->color, luxel[ b ] );
1858 if ( lightDeluxel ) {
1859 VectorCopy( trace->directionContribution, deluxel[ b ] );
1861 VectorAdd( total, trace->color, total );
1862 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1867 /* subsample further? */
1868 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1869 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1870 lighted != 0 && lighted != mapped ) {
1871 for ( b = 0; b < 4; b++ )
1873 if ( cluster[ b ] < 0 ) {
1876 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1881 //% VectorClear( color );
1883 VectorCopy( lightLuxel, color );
1884 if ( lightDeluxel ) {
1885 VectorCopy( lightDeluxel, direction );
1888 for ( b = 0; b < 4; b++ )
1890 if ( cluster[ b ] < 0 ) {
1893 VectorAdd( color, luxel[ b ], color );
1894 if ( lightDeluxel ) {
1895 VectorAdd( direction, deluxel[ b ], direction );
1901 if ( samples > 0 ) {
1903 color[ 0 ] /= samples;
1904 color[ 1 ] /= samples;
1905 color[ 2 ] /= samples;
1908 VectorCopy( color, lightLuxel );
1909 lightLuxel[ 3 ] += 1.0f;
1911 if ( lightDeluxel ) {
1912 direction[ 0 ] /= samples;
1913 direction[ 1 ] /= samples;
1914 direction[ 2 ] /= samples;
1915 VectorCopy( direction, lightDeluxel );
1920 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1921 static void GaussLikeRandom( float sigma, float *x, float *y ){
1923 r = Random() * 2 * Q_PI;
1924 *x = sigma * 2.73861278752581783822 * cos( r );
1925 *y = sigma * 2.73861278752581783822 * sin( r );
1932 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1935 vec3_t origin, normal;
1936 vec3_t total, totaldirection;
1939 VectorClear( total );
1940 VectorClear( totaldirection );
1942 for ( b = 0; b < lightSamples; ++b )
1945 VectorCopy( sampleOrigin, origin );
1946 GaussLikeRandom( bias, &dx, &dy );
1948 /* calculate position */
1949 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1955 trace->cluster = cluster;
1956 VectorCopy( origin, trace->origin );
1957 VectorCopy( normal, trace->normal );
1959 LightContributionToSample( trace );
1960 VectorAdd( total, trace->color, total );
1961 if ( lightDeluxel ) {
1962 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1969 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1970 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1971 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1973 if ( lightDeluxel ) {
1974 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1975 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1976 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1984 IlluminateRawLightmap()
1985 illuminates the luxels
1988 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1989 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1990 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1992 void IlluminateRawLightmap( int rawLightmapNum ){
1993 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1994 int *cluster, *cluster2, mapped, lighted, totalLighted;
1995 size_t llSize, ldSize;
1997 surfaceInfo_t *info;
1998 qboolean filterColor, filterDir;
2000 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2001 unsigned char *flag;
2002 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2003 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2004 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2006 float stackLightLuxels[ STACK_LL_SIZE ];
2009 /* bail if this number exceeds the number of raw lightmaps */
2010 if ( rawLightmapNum >= numRawLightmaps ) {
2015 lm = &rawLightmaps[ rawLightmapNum ];
2018 trace.testOcclusion = !noTrace;
2019 trace.forceSunlight = qfalse;
2020 trace.recvShadows = lm->recvShadows;
2021 trace.numSurfaces = lm->numLightSurfaces;
2022 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2023 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2025 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2026 trace.twoSided = qfalse;
2027 for ( i = 0; i < trace.numSurfaces; i++ )
2030 info = &surfaceInfos[ trace.surfaces[ i ] ];
2032 /* check twosidedness */
2033 if ( info->si->twoSided ) {
2034 trace.twoSided = qtrue;
2039 /* create a culled light list for this raw lightmap */
2040 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2042 /* -----------------------------------------------------------------
2044 ----------------------------------------------------------------- */
2047 numLuxelsIlluminated += ( lm->sw * lm->sh );
2049 /* test debugging state */
2050 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2051 /* debug fill the luxels */
2052 for ( y = 0; y < lm->sh; y++ )
2054 for ( x = 0; x < lm->sw; x++ )
2057 cluster = SUPER_CLUSTER( x, y );
2059 /* only fill mapped luxels */
2060 if ( *cluster < 0 ) {
2064 /* get particulars */
2065 luxel = SUPER_LUXEL( 0, x, y );
2066 origin = SUPER_ORIGIN( x, y );
2067 normal = SUPER_NORMAL( x, y );
2069 /* color the luxel with raw lightmap num? */
2070 if ( debugSurfaces ) {
2071 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2074 /* color the luxel with lightmap axis? */
2075 else if ( debugAxis ) {
2076 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2077 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2078 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2081 /* color the luxel with luxel cluster? */
2082 else if ( debugCluster ) {
2083 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2086 /* color the luxel with luxel origin? */
2087 else if ( debugOrigin ) {
2088 VectorSubtract( lm->maxs, lm->mins, temp );
2089 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2090 VectorSubtract( origin, lm->mins, temp2 );
2091 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2092 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2093 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2096 /* color the luxel with the normal */
2097 else if ( normalmap ) {
2098 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2099 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2100 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2103 /* otherwise clear it */
2105 VectorClear( luxel );
2115 /* allocate temporary per-light luxel storage */
2116 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2117 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2118 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2119 lightLuxels = stackLightLuxels;
2122 lightLuxels = safe_malloc( llSize );
2125 lightDeluxels = safe_malloc( ldSize );
2128 lightDeluxels = NULL;
2132 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2134 /* set ambient color */
2135 for ( y = 0; y < lm->sh; y++ )
2137 for ( x = 0; x < lm->sw; x++ )
2140 cluster = SUPER_CLUSTER( x, y );
2141 luxel = SUPER_LUXEL( 0, x, y );
2142 normal = SUPER_NORMAL( x, y );
2143 deluxel = SUPER_DELUXEL( x, y );
2145 /* blacken unmapped clusters */
2146 if ( *cluster < 0 ) {
2147 VectorClear( luxel );
2153 VectorCopy( ambientColor, luxel );
2155 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2157 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2158 if ( brightness < 0.00390625f ) {
2159 brightness = 0.00390625f;
2162 VectorScale( normal, brightness, deluxel );
2169 /* clear styled lightmaps */
2170 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2171 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2173 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2174 memset( lm->superLuxels[ lightmapNum ], 0, size );
2178 /* debugging code */
2179 //% if( trace.numLights <= 0 )
2180 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2182 /* walk light list */
2183 for ( i = 0; i < trace.numLights; i++ )
2186 trace.light = trace.lights[ i ];
2189 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2191 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2192 lm->styles[ lightmapNum ] == LS_NONE ) {
2197 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2198 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2199 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2204 memset( lightLuxels, 0, llSize );
2206 memset( lightDeluxels, 0, ldSize );
2210 /* determine filter radius */
2211 filterRadius = lm->filterRadius > trace.light->filterRadius
2213 : trace.light->filterRadius;
2214 if ( filterRadius < 0.0f ) {
2215 filterRadius = 0.0f;
2218 /* set luxel filter radius */
2219 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2220 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2221 luxelFilterRadius = 1;
2224 /* allocate sampling flags storage */
2225 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2226 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2227 if ( lm->superFlags == NULL ) {
2228 lm->superFlags = safe_malloc( size );
2230 memset( (void *) lm->superFlags, 0, size );
2233 /* initial pass, one sample per luxel */
2234 for ( y = 0; y < lm->sh; y++ )
2236 for ( x = 0; x < lm->sw; x++ )
2239 cluster = SUPER_CLUSTER( x, y );
2240 if ( *cluster < 0 ) {
2244 /* get particulars */
2245 lightLuxel = LIGHT_LUXEL( x, y );
2246 lightDeluxel = LIGHT_DELUXEL( x, y );
2247 origin = SUPER_ORIGIN( x, y );
2248 normal = SUPER_NORMAL( x, y );
2249 flag = SUPER_FLAG( x, y );
2252 ////////// 27's temp hack for testing edge clipping ////
2253 if ( origin[0] == 0 && origin[1] == 0 && origin[2] == 0 ) {
2254 lightLuxel[ 1 ] = 255;
2255 lightLuxel[ 3 ] = 1.0f;
2261 /* set contribution count */
2262 lightLuxel[ 3 ] = 1.0f;
2265 trace.cluster = *cluster;
2266 VectorCopy( origin, trace.origin );
2267 VectorCopy( normal, trace.normal );
2269 /* get light for this sample */
2270 LightContributionToSample( &trace );
2271 VectorCopy( trace.color, lightLuxel );
2273 /* add the contribution to the deluxemap */
2275 VectorCopy( trace.directionContribution, lightDeluxel );
2278 /* check for evilness */
2279 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2281 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2284 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2291 /* don't even bother with everything else if nothing was lit */
2292 if ( totalLighted == 0 ) {
2296 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2297 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2298 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2300 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2302 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2307 VectorClear( total );
2309 /* test 2x2 stamp */
2310 for ( t = 0; t < 4; t++ )
2312 /* set sample coords */
2313 sx = x + tests[ t ][ 0 ];
2314 sy = y + tests[ t ][ 1 ];
2317 cluster = SUPER_CLUSTER( sx, sy );
2318 if ( *cluster < 0 ) {
2324 flag = SUPER_FLAG( sx, sy );
2325 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2326 /* force a lighted/mapped discrepancy so we subsample */
2331 lightLuxel = LIGHT_LUXEL( sx, sy );
2332 VectorAdd( total, lightLuxel, total );
2333 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2338 /* if total color is under a certain amount, then don't bother subsampling */
2339 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2343 /* if all 4 pixels are either in shadow or light, then don't subsample */
2344 if ( lighted != 0 && lighted != mapped ) {
2345 for ( t = 0; t < 4; t++ )
2347 /* set sample coords */
2348 sx = x + tests[ t ][ 0 ];
2349 sy = y + tests[ t ][ 1 ];
2352 cluster = SUPER_CLUSTER( sx, sy );
2353 if ( *cluster < 0 ) {
2356 flag = SUPER_FLAG( sx, sy );
2357 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2360 lightLuxel = LIGHT_LUXEL( sx, sy );
2361 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2362 origin = SUPER_ORIGIN( sx, sy );
2364 /* only subsample shadowed luxels */
2365 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2369 if ( lightRandomSamples ) {
2370 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2373 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2376 *flag |= FLAG_ALREADY_SUBSAMPLED;
2378 /* debug code to colorize subsampled areas to yellow */
2379 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2380 //% VectorSet( luxel, 255, 204, 0 );
2387 /* tertiary pass, apply dirt map (ambient occlusion) */
2390 for ( y = 0; y < lm->sh; y++ )
2392 for ( x = 0; x < lm->sw; x++ )
2395 cluster = SUPER_CLUSTER( x, y );
2396 if ( *cluster < 0 ) {
2400 /* get particulars */
2401 lightLuxel = LIGHT_LUXEL( x, y );
2402 dirt = SUPER_DIRT( x, y );
2404 /* scale light value */
2405 VectorScale( lightLuxel, *dirt, lightLuxel );
2410 /* allocate sampling lightmap storage */
2411 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2412 /* allocate sampling lightmap storage */
2413 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2414 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2415 memset( lm->superLuxels[ lightmapNum ], 0, size );
2419 if ( lightmapNum > 0 ) {
2420 lm->styles[ lightmapNum ] = trace.light->style;
2421 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2424 /* copy to permanent luxels */
2425 for ( y = 0; y < lm->sh; y++ )
2427 for ( x = 0; x < lm->sw; x++ )
2429 /* get cluster and origin */
2430 cluster = SUPER_CLUSTER( x, y );
2431 if ( *cluster < 0 ) {
2434 origin = SUPER_ORIGIN( x, y );
2437 if ( luxelFilterRadius ) {
2439 VectorClear( averageColor );
2440 VectorClear( averageDir );
2443 /* cheaper distance-based filtering */
2444 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2446 if ( sy < 0 || sy >= lm->sh ) {
2450 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2452 if ( sx < 0 || sx >= lm->sw ) {
2456 /* get particulars */
2457 cluster = SUPER_CLUSTER( sx, sy );
2458 if ( *cluster < 0 ) {
2461 lightLuxel = LIGHT_LUXEL( sx, sy );
2462 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2465 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2466 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2468 /* scale luxel by filter weight */
2469 VectorScale( lightLuxel, weight, color );
2470 VectorAdd( averageColor, color, averageColor );
2472 VectorScale( lightDeluxel, weight, direction );
2473 VectorAdd( averageDir, direction, averageDir );
2480 if ( samples <= 0.0f ) {
2484 /* scale into luxel */
2485 luxel = SUPER_LUXEL( lightmapNum, x, y );
2488 /* handle negative light */
2489 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2490 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2491 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2492 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2495 /* handle normal light */
2498 luxel[ 0 ] += averageColor[ 0 ] / samples;
2499 luxel[ 1 ] += averageColor[ 1 ] / samples;
2500 luxel[ 2 ] += averageColor[ 2 ] / samples;
2504 /* scale into luxel */
2505 deluxel = SUPER_DELUXEL( x, y );
2506 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2507 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2508 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2515 /* get particulars */
2516 lightLuxel = LIGHT_LUXEL( x, y );
2517 lightDeluxel = LIGHT_DELUXEL( x, y );
2518 luxel = SUPER_LUXEL( lightmapNum, x, y );
2519 deluxel = SUPER_DELUXEL( x, y );
2521 /* handle negative light */
2522 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2523 VectorScale( averageColor, -1.0f, averageColor );
2529 /* handle negative light */
2530 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2531 VectorSubtract( luxel, lightLuxel, luxel );
2534 /* handle normal light */
2536 VectorAdd( luxel, lightLuxel, luxel );
2540 VectorAdd( deluxel, lightDeluxel, deluxel );
2547 /* free temporary luxels */
2548 if ( lightLuxels != stackLightLuxels ) {
2549 free( lightLuxels );
2553 free( lightDeluxels );
2557 /* free light list */
2558 FreeTraceLights( &trace );
2560 /* floodlight pass */
2561 if ( floodlighty ) {
2562 FloodlightIlluminateLightmap( lm );
2565 if ( debugnormals ) {
2566 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2569 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2573 for ( y = 0; y < lm->sh; y++ )
2575 for ( x = 0; x < lm->sw; x++ )
2578 cluster = SUPER_CLUSTER( x, y );
2579 //% if( *cluster < 0 )
2582 /* get particulars */
2583 luxel = SUPER_LUXEL( lightmapNum, x, y );
2584 normal = SUPER_NORMAL( x, y );
2586 luxel[0] = ( normal[0] * 127 ) + 127;
2587 luxel[1] = ( normal[1] * 127 ) + 127;
2588 luxel[2] = ( normal[2] * 127 ) + 127;
2594 /* -----------------------------------------------------------------
2596 ----------------------------------------------------------------- */
2599 /* walk lightmaps */
2600 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2603 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2607 /* apply dirt to each luxel */
2608 for ( y = 0; y < lm->sh; y++ )
2610 for ( x = 0; x < lm->sw; x++ )
2613 cluster = SUPER_CLUSTER( x, y );
2614 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2617 /* get particulars */
2618 luxel = SUPER_LUXEL( lightmapNum, x, y );
2619 dirt = SUPER_DIRT( x, y );
2622 VectorScale( luxel, *dirt, luxel );
2626 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2633 /* -----------------------------------------------------------------
2635 ----------------------------------------------------------------- */
2637 /* walk lightmaps */
2638 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2641 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2645 /* average occluded luxels from neighbors */
2646 for ( y = 0; y < lm->sh; y++ )
2648 for ( x = 0; x < lm->sw; x++ )
2650 /* get particulars */
2651 cluster = SUPER_CLUSTER( x, y );
2652 luxel = SUPER_LUXEL( lightmapNum, x, y );
2653 deluxel = SUPER_DELUXEL( x, y );
2654 normal = SUPER_NORMAL( x, y );
2656 /* determine if filtering is necessary */
2657 filterColor = qfalse;
2659 if ( *cluster < 0 ||
2660 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2661 filterColor = qtrue;
2664 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2668 if ( !filterColor && !filterDir ) {
2672 /* choose seed amount */
2673 VectorClear( averageColor );
2674 VectorClear( averageDir );
2677 /* walk 3x3 matrix */
2678 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2680 if ( sy < 0 || sy >= lm->sh ) {
2684 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2686 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2690 /* get neighbor's particulars */
2691 cluster2 = SUPER_CLUSTER( sx, sy );
2692 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2693 deluxel2 = SUPER_DELUXEL( sx, sy );
2695 /* ignore unmapped/unlit luxels */
2696 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2697 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2701 /* add its distinctiveness to our own */
2702 VectorAdd( averageColor, luxel2, averageColor );
2703 samples += luxel2[ 3 ];
2705 VectorAdd( averageDir, deluxel2, averageDir );
2711 if ( samples <= 0.0f ) {
2715 /* dark lightmap seams */
2717 if ( lightmapNum == 0 ) {
2718 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2724 if ( filterColor ) {
2725 VectorDivide( averageColor, samples, luxel );
2729 VectorDivide( averageDir, samples, deluxel );
2732 /* set cluster to -3 */
2733 if ( *cluster < 0 ) {
2734 *cluster = CLUSTER_FLOODED;
2743 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2746 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2749 for ( y = 0; y < lm->sh; y++ )
2750 for ( x = 0; x < lm->sw; x++ )
2753 cluster = SUPER_CLUSTER( x, y );
2754 luxel = SUPER_LUXEL( lightmapNum, x, y );
2755 deluxel = SUPER_DELUXEL( x, y );
2756 if ( !luxel || !deluxel || !cluster ) {
2757 Sys_FPrintf( SYS_VRB, "WARNING: I got NULL'd.\n" );
2760 else if ( *cluster < 0 ) {
2762 // should have neither deluxemap nor lightmap
2764 Sys_FPrintf( SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n" );
2770 // should have both deluxemap and lightmap
2772 Sys_FPrintf( SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n" );
2783 IlluminateVertexes()
2784 light the surface vertexes
2787 #define VERTEX_NUDGE 4.0f
2789 void IlluminateVertexes( int num ){
2790 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2791 int lightmapNum, numAvg;
2792 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2793 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2794 bspDrawSurface_t *ds;
2795 surfaceInfo_t *info;
2797 bspDrawVert_t *verts;
2799 float floodLightAmount;
2803 /* get surface, info, and raw lightmap */
2804 ds = &bspDrawSurfaces[ num ];
2805 info = &surfaceInfos[ num ];
2808 /* -----------------------------------------------------------------
2809 illuminate the vertexes
2810 ----------------------------------------------------------------- */
2812 /* calculate vertex lighting for surfaces without lightmaps */
2813 if ( lm == NULL || cpmaHack ) {
2815 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2816 trace.forceSunlight = info->si->forceSunlight;
2817 trace.recvShadows = info->recvShadows;
2818 trace.numSurfaces = 1;
2819 trace.surfaces = #
2820 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2822 /* twosided lighting */
2823 trace.twoSided = info->si->twoSided;
2825 /* make light list for this surface */
2826 CreateTraceLightsForSurface( num, &trace );
2829 verts = yDrawVerts + ds->firstVert;
2831 memset( avgColors, 0, sizeof( avgColors ) );
2833 /* walk the surface verts */
2834 for ( i = 0; i < ds->numVerts; i++ )
2836 /* get vertex luxel */
2837 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2839 /* color the luxel with raw lightmap num? */
2840 if ( debugSurfaces ) {
2841 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2844 /* color the luxel with luxel origin? */
2845 else if ( debugOrigin ) {
2846 VectorSubtract( info->maxs, info->mins, temp );
2847 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2848 VectorSubtract( origin, lm->mins, temp2 );
2849 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2850 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2851 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2854 /* color the luxel with the normal */
2855 else if ( normalmap ) {
2856 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2857 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2858 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2861 /* illuminate the vertex */
2864 /* clear vertex luxel */
2865 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2867 /* try at initial origin */
2868 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2869 if ( trace.cluster >= 0 ) {
2871 VectorCopy( verts[ i ].xyz, trace.origin );
2872 VectorCopy( verts[ i ].normal, trace.normal );
2875 if ( dirty && !bouncing ) {
2876 dirt = DirtForSample( &trace );
2882 /* jal: floodlight */
2883 floodLightAmount = 0.0f;
2884 VectorClear( floodColor );
2885 if ( floodlighty && !bouncing ) {
2886 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2887 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2891 LightingAtSample( &trace, ds->vertexStyles, colors );
2894 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2897 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2899 /* jal: floodlight */
2900 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2903 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2904 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2905 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2909 /* is this sample bright enough? */
2910 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2911 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2912 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2913 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2914 /* nudge the sample point around a bit */
2915 for ( x = 0; x < 5; x++ )
2917 /* two's complement 0, 1, -1, 2, -2, etc */
2918 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2920 for ( y = 0; y < 5; y++ )
2922 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2924 for ( z = 0; z < 5; z++ )
2926 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2929 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2930 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2931 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2933 /* try at nudged origin */
2934 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2935 if ( trace.cluster < 0 ) {
2940 if ( dirty && !bouncing ) {
2941 dirt = DirtForSample( &trace );
2947 /* jal: floodlight */
2948 floodLightAmount = 0.0f;
2949 VectorClear( floodColor );
2950 if ( floodlighty && !bouncing ) {
2951 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2952 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2956 LightingAtSample( &trace, ds->vertexStyles, colors );
2959 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2962 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2964 /* jal: floodlight */
2965 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2968 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2969 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2972 /* bright enough? */
2973 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2974 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2975 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2976 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2984 /* add to average? */
2985 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2986 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2987 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2988 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2990 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2992 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2993 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2998 /* another happy customer */
2999 numVertsIlluminated++;
3002 /* set average color */
3004 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3005 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
3009 VectorCopy( ambientColor, avgColors[ 0 ] );
3012 /* clean up and store vertex color */
3013 for ( i = 0; i < ds->numVerts; i++ )
3015 /* get vertex luxel */
3016 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
3018 /* store average in occluded vertexes */
3019 if ( radVertLuxel[ 0 ] < 0.0f ) {
3020 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3022 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3023 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
3026 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3031 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3034 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3035 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3038 if ( bouncing || bounce == 0 || !bounceOnly ) {
3039 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3041 if ( !info->si->noVertexLight ) {
3042 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3047 /* free light list */
3048 FreeTraceLights( &trace );
3050 /* return to sender */
3054 /* -----------------------------------------------------------------
3055 reconstitute vertex lighting from the luxels
3056 ----------------------------------------------------------------- */
3058 /* set styles from lightmap */
3059 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3060 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3062 /* get max search radius */
3064 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3066 /* walk the surface verts */
3067 verts = yDrawVerts + ds->firstVert;
3068 for ( i = 0; i < ds->numVerts; i++ )
3070 /* do each lightmap */
3071 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3074 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3078 /* get luxel coords */
3079 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3080 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3084 else if ( x >= lm->sw ) {
3090 else if ( y >= lm->sh ) {
3094 /* get vertex luxels */
3095 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3096 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3098 /* color the luxel with the normal? */
3100 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3101 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3102 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3105 /* color the luxel with surface num? */
3106 else if ( debugSurfaces ) {
3107 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3110 /* divine color from the superluxels */
3113 /* increasing radius */
3114 VectorClear( radVertLuxel );
3116 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3118 /* sample within radius */
3119 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3121 if ( sy < 0 || sy >= lm->sh ) {
3125 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3127 if ( sx < 0 || sx >= lm->sw ) {
3131 /* get luxel particulars */
3132 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3133 cluster = SUPER_CLUSTER( sx, sy );
3134 if ( *cluster < 0 ) {
3138 /* testing: must be brigher than ambient color */
3139 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3142 /* add its distinctiveness to our own */
3143 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3144 samples += luxel[ 3 ];
3150 if ( samples > 0.0f ) {
3151 VectorDivide( radVertLuxel, samples, radVertLuxel );
3154 VectorCopy( ambientColor, radVertLuxel );
3158 /* store into floating point storage */
3159 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3160 numVertsIlluminated++;
3162 /* store into bytes (for vertex approximation) */
3163 if ( !info->si->noVertexLight ) {
3164 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3172 /* -------------------------------------------------------------------------------
3174 light optimization (-fast)
3176 creates a list of lights that will affect a surface and stores it in tw
3177 this is to optimize surface lighting by culling out as many of the
3178 lights in the world as possible from further calculation
3180 ------------------------------------------------------------------------------- */
3184 determines opaque brushes in the world and find sky shaders for sunlight calculations
3187 void SetupBrushesFlags( int mask_any, int test_any, int mask_all, int test_all ){
3189 unsigned int compileFlags, allCompileFlags;
3192 bspBrushSide_t *side;
3193 bspShader_t *shader;
3198 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3201 if ( opaqueBrushes == NULL ) {
3202 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3206 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3207 numOpaqueBrushes = 0;
3209 /* walk the list of worldspawn brushes */
3210 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3213 b = bspModels[ 0 ].firstBSPBrush + i;
3214 brush = &bspBrushes[ b ];
3216 /* check all sides */
3219 allCompileFlags = ~( 0u );
3220 for ( j = 0; j < brush->numSides && inside; j++ )
3222 /* do bsp shader calculations */
3223 side = &bspBrushSides[ brush->firstSide + j ];
3224 shader = &bspShaders[ side->shaderNum ];
3226 /* get shader info */
3227 si = ShaderInfoForShaderNull( shader->shader );
3232 /* or together compile flags */
3233 compileFlags |= si->compileFlags;
3234 allCompileFlags &= si->compileFlags;
3237 /* determine if this brush is opaque to light */
3238 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3239 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3245 /* emit some statistics */
3246 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3248 void SetupBrushes( void ){
3249 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3256 determines if two clusters are visible to each other using the PVS
3259 qboolean ClusterVisible( int a, int b ){
3265 if ( a < 0 || b < 0 ) {
3275 if ( numBSPVisBytes <= 8 ) {
3280 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3281 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3282 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3285 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3295 borrowed from vlight.c
3298 int PointInLeafNum_r( vec3_t point, int nodenum ){
3305 while ( nodenum >= 0 )
3307 node = &bspNodes[ nodenum ];
3308 plane = &bspPlanes[ node->planeNum ];
3309 dist = DotProduct( point, plane->normal ) - plane->dist;
3311 nodenum = node->children[ 0 ];
3313 else if ( dist < -0.1 ) {
3314 nodenum = node->children[ 1 ];
3318 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3319 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3322 nodenum = node->children[ 1 ];
3326 leafnum = -nodenum - 1;
3334 borrowed from vlight.c
3337 int PointInLeafNum( vec3_t point ){
3338 return PointInLeafNum_r( point, 0 );
3344 ClusterVisibleToPoint() - ydnar
3345 returns qtrue if point can "see" cluster
3348 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3352 /* get leafNum for point */
3353 pointCluster = ClusterForPoint( point );
3354 if ( pointCluster < 0 ) {
3359 return ClusterVisible( pointCluster, cluster );
3365 ClusterForPoint() - ydnar
3366 returns the pvs cluster for point
3369 int ClusterForPoint( vec3_t point ){
3373 /* get leafNum for point */
3374 leafNum = PointInLeafNum( point );
3375 if ( leafNum < 0 ) {
3379 /* return the cluster */
3380 return bspLeafs[ leafNum ].cluster;
3386 ClusterForPointExt() - ydnar
3387 also takes brushes into account for occlusion testing
3390 int ClusterForPointExt( vec3_t point, float epsilon ){
3391 int i, j, b, leafNum, cluster;
3394 int *brushes, numBSPBrushes;
3400 /* get leaf for point */
3401 leafNum = PointInLeafNum( point );
3402 if ( leafNum < 0 ) {
3405 leaf = &bspLeafs[ leafNum ];
3407 /* get the cluster */
3408 cluster = leaf->cluster;
3409 if ( cluster < 0 ) {
3413 /* transparent leaf, so check point against all brushes in the leaf */
3414 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3415 numBSPBrushes = leaf->numBSPLeafBrushes;
3416 for ( i = 0; i < numBSPBrushes; i++ )
3420 if ( b > maxOpaqueBrush ) {
3423 brush = &bspBrushes[ b ];
3424 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3428 /* check point against all planes */
3430 for ( j = 0; j < brush->numSides && inside; j++ )
3432 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3433 dot = DotProduct( point, plane->normal );
3435 if ( dot > epsilon ) {
3440 /* if inside, return bogus cluster */
3446 /* if the point made it this far, it's not inside any opaque brushes */
3453 ClusterForPointExtFilter() - ydnar
3454 adds cluster checking against a list of known valid clusters
3457 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3461 /* get cluster for point */
3462 cluster = ClusterForPointExt( point, epsilon );
3464 /* check if filtering is necessary */
3465 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3470 for ( i = 0; i < numClusters; i++ )
3472 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3484 ShaderForPointInLeaf() - ydnar
3485 checks a point against all brushes in a leaf, returning the shader of the brush
3486 also sets the cumulative surface and content flags for the brush hit
3489 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3493 int *brushes, numBSPBrushes;
3496 bspBrushSide_t *side;
3498 bspShader_t *shader;
3499 int allSurfaceFlags, allContentFlags;
3502 /* clear things out first */
3507 if ( leafNum < 0 ) {
3510 leaf = &bspLeafs[ leafNum ];
3512 /* transparent leaf, so check point against all brushes in the leaf */
3513 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3514 numBSPBrushes = leaf->numBSPLeafBrushes;
3515 for ( i = 0; i < numBSPBrushes; i++ )
3518 brush = &bspBrushes[ brushes[ i ] ];
3520 /* check point against all planes */
3522 allSurfaceFlags = 0;
3523 allContentFlags = 0;
3524 for ( j = 0; j < brush->numSides && inside; j++ )
3526 side = &bspBrushSides[ brush->firstSide + j ];
3527 plane = &bspPlanes[ side->planeNum ];
3528 dot = DotProduct( point, plane->normal );
3530 if ( dot > epsilon ) {
3535 shader = &bspShaders[ side->shaderNum ];
3536 allSurfaceFlags |= shader->surfaceFlags;
3537 allContentFlags |= shader->contentFlags;
3541 /* handle if inside */
3543 /* if there are desired flags, check for same and continue if they aren't matched */
3544 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3547 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3551 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3552 *surfaceFlags = allSurfaceFlags;
3553 *contentFlags = allContentFlags;
3554 return brush->shaderNum;
3558 /* if the point made it this far, it's not inside any brushes */
3566 chops a bounding box by the plane defined by origin and normal
3567 returns qfalse if the bounds is entirely clipped away
3569 this is not exactly the fastest way to do this...
3572 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3573 /* FIXME: rewrite this so it doesn't use bloody brushes */
3581 calculates each light's effective envelope,
3582 taking into account brightness, type, and pvs.
3585 #define LIGHT_EPSILON 0.125f
3586 #define LIGHT_NUDGE 2.0f
3588 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3589 int i, x, y, z, x1, y1, z1;
3590 light_t *light, *light2, **owner;
3592 vec3_t origin, dir, mins, maxs;
3593 float radius, intensity;
3594 light_t *buckets[ 256 ];
3597 /* early out for weird cases where there are no lights */
3598 if ( lights == NULL ) {
3603 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3607 numCulledLights = 0;
3609 while ( *owner != NULL )
3614 /* handle negative lights */
3615 if ( light->photons < 0.0f || light->add < 0.0f ) {
3616 light->photons *= -1.0f;
3617 light->add *= -1.0f;
3618 light->flags |= LIGHT_NEGATIVE;
3622 if ( light->type == EMIT_SUN ) {
3625 light->envelope = MAX_WORLD_COORD * 8.0f;
3626 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3627 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3630 /* everything else */
3633 /* get pvs cluster for light */
3634 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3636 /* invalid cluster? */
3637 if ( light->cluster < 0 ) {
3638 /* nudge the sample point around a bit */
3639 for ( x = 0; x < 4; x++ )
3641 /* two's complement 0, 1, -1, 2, -2, etc */
3642 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3644 for ( y = 0; y < 4; y++ )
3646 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3648 for ( z = 0; z < 4; z++ )
3650 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3653 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3654 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3655 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3657 /* try at nudged origin */
3658 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3659 if ( light->cluster < 0 ) {
3664 VectorCopy( origin, light->origin );
3670 /* only calculate for lights in pvs and outside of opaque brushes */
3671 if ( light->cluster >= 0 ) {
3672 /* set light fast flag */
3674 light->flags |= LIGHT_FAST_TEMP;
3677 light->flags &= ~LIGHT_FAST_TEMP;
3679 if ( fastpoint && ( light->flags != EMIT_AREA ) ) {
3680 light->flags |= LIGHT_FAST_TEMP;
3682 if ( light->si && light->si->noFast ) {
3683 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3686 /* clear light envelope */
3687 light->envelope = 0;
3689 /* handle area lights */
3690 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3691 light->envelope = MAX_WORLD_COORD * 8.0f;
3693 /* check for fast mode */
3694 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3695 /* ugly hack to calculate extent for area lights, but only done once */
3696 VectorScale( light->normal, -1.0f, dir );
3697 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3701 VectorMA( light->origin, radius, light->normal, origin );
3702 factor = PointToPolygonFormFactor( origin, dir, light->w );
3703 if ( factor < 0.0f ) {
3706 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3707 light->envelope = radius;
3713 intensity = light->photons; /* hopefully not used */
3718 intensity = light->photons;
3722 if ( light->envelope <= 0.0f ) {
3723 /* solve distance for non-distance lights */
3724 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3725 light->envelope = MAX_WORLD_COORD * 8.0f;
3728 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3729 /* solve distance for linear lights */
3730 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3731 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3735 add = angle * light->photons * linearScale - (dist * light->fade);
3736 T = (light->photons * linearScale) - (dist * light->fade);
3737 T + (dist * light->fade) = (light->photons * linearScale);
3738 dist * light->fade = (light->photons * linearScale) - T;
3739 dist = ((light->photons * linearScale) - T) / light->fade;
3742 /* solve for inverse square falloff */
3744 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3748 add = light->photons / (dist * dist);
3749 T = light->photons / (dist * dist);
3750 T * (dist * dist) = light->photons;
3751 dist = sqrt( light->photons / T );
3756 /* solve distance for linear lights */
3757 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3758 light->envelope = ( intensity * linearScale ) / light->fade;
3761 /* can't cull these */
3763 light->envelope = MAX_WORLD_COORD * 8.0f;
3768 /* chop radius against pvs */
3771 ClearBounds( mins, maxs );
3773 /* check all leaves */
3774 for ( i = 0; i < numBSPLeafs; i++ )
3777 leaf = &bspLeafs[ i ];
3780 if ( leaf->cluster < 0 ) {
3783 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3787 /* add this leafs bbox to the bounds */
3788 VectorCopy( leaf->mins, origin );
3789 AddPointToBounds( origin, mins, maxs );
3790 VectorCopy( leaf->maxs, origin );
3791 AddPointToBounds( origin, mins, maxs );
3794 /* test to see if bounds encompass light */
3795 for ( i = 0; i < 3; i++ )
3797 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3798 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3799 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3800 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3801 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3802 AddPointToBounds( light->origin, mins, maxs );
3806 /* chop the bounds by a plane for area lights and spotlights */
3807 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3808 ChopBounds( mins, maxs, light->origin, light->normal );
3812 VectorCopy( mins, light->mins );
3813 VectorCopy( maxs, light->maxs );
3815 /* reflect bounds around light origin */
3816 //% VectorMA( light->origin, -1.0f, origin, origin );
3817 VectorScale( light->origin, 2, origin );
3818 VectorSubtract( origin, maxs, origin );
3819 AddPointToBounds( origin, mins, maxs );
3820 //% VectorMA( light->origin, -1.0f, mins, origin );
3821 VectorScale( light->origin, 2, origin );
3822 VectorSubtract( origin, mins, origin );
3823 AddPointToBounds( origin, mins, maxs );
3825 /* calculate spherical bounds */
3826 VectorSubtract( maxs, light->origin, dir );
3827 radius = (float) VectorLength( dir );
3829 /* if this radius is smaller than the envelope, then set the envelope to it */
3830 if ( radius < light->envelope ) {
3831 light->envelope = radius;
3832 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3835 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3838 /* add grid/surface only check */
3840 if ( !( light->flags & LIGHT_GRID ) ) {
3841 light->envelope = 0.0f;
3846 if ( !( light->flags & LIGHT_SURFACES ) ) {
3847 light->envelope = 0.0f;
3853 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3855 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3857 /* delete the light */
3859 *owner = light->next;
3860 if ( light->w != NULL ) {
3868 /* square envelope */
3869 light->envelope2 = ( light->envelope * light->envelope );
3871 /* increment light count */
3874 /* set next light */
3875 owner = &( ( **owner ).next );
3878 /* bucket sort lights by style */
3879 memset( buckets, 0, sizeof( buckets ) );
3881 for ( light = lights; light != NULL; light = light2 )
3883 /* get next light */
3884 light2 = light->next;
3886 /* filter into correct bucket */
3887 light->next = buckets[ light->style ];
3888 buckets[ light->style ] = light;
3890 /* if any styled light is present, automatically set nocollapse */
3891 if ( light->style != LS_NORMAL ) {
3896 /* filter back into light list */
3898 for ( i = 255; i >= 0; i-- )
3901 for ( light = buckets[ i ]; light != NULL; light = light2 )
3903 light2 = light->next;
3904 light->next = lights;
3909 /* emit some statistics */
3910 Sys_Printf( "%9d total lights\n", numLights );
3911 Sys_Printf( "%9d culled lights\n", numCulledLights );
3917 CreateTraceLightsForBounds()
3918 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3921 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3924 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3925 float radius, dist, length;
3928 /* potential pre-setup */
3929 if ( numLights == 0 ) {
3930 SetupEnvelopes( qfalse, fast );
3934 //% 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 ] );
3936 /* allocate the light list */
3937 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3938 trace->numLights = 0;
3940 /* calculate spherical bounds */
3941 VectorAdd( mins, maxs, origin );
3942 VectorScale( origin, 0.5f, origin );
3943 VectorSubtract( maxs, origin, dir );
3944 radius = (float) VectorLength( dir );
3946 /* get length of normal vector */
3947 if ( normal != NULL ) {
3948 length = VectorLength( normal );
3952 normal = nullVector;
3956 /* test each light and see if it reaches the sphere */
3957 /* note: the attenuation code MUST match LightingAtSample() */
3958 for ( light = lights; light; light = light->next )
3960 /* check zero sized envelope */
3961 if ( light->envelope <= 0 ) {
3962 lightsEnvelopeCulled++;
3967 if ( !( light->flags & flags ) ) {
3971 /* sunlight skips all this nonsense */
3972 if ( light->type != EMIT_SUN ) {
3978 /* check against pvs cluster */
3979 if ( numClusters > 0 && clusters != NULL ) {
3980 for ( i = 0; i < numClusters; i++ )
3982 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3988 if ( i == numClusters ) {
3989 lightsClusterCulled++;
3994 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3995 VectorSubtract( light->origin, origin, dir );
3996 dist = VectorLength( dir );
3997 dist -= light->envelope;
4000 lightsEnvelopeCulled++;
4004 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
4007 for ( i = 0; i < 3; i++ )
4009 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
4014 lightsBoundsCulled++;
4020 /* planar surfaces (except twosided surfaces) have a couple more checks */
4021 if ( length > 0.0f && trace->twoSided == qfalse ) {
4022 /* lights coplanar with a surface won't light it */
4023 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
4024 lightsPlaneCulled++;
4028 /* check to see if light is behind the plane */
4029 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4030 lightsPlaneCulled++;
4035 /* add this light */
4036 trace->lights[ trace->numLights++ ] = light;
4039 /* make last night null */
4040 trace->lights[ trace->numLights ] = NULL;
4045 void FreeTraceLights( trace_t *trace ){
4046 if ( trace->lights != NULL ) {
4047 free( trace->lights );
4054 CreateTraceLightsForSurface()
4055 creates a list of lights that can potentially affect a drawsurface
4058 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4060 vec3_t mins, maxs, normal;
4062 bspDrawSurface_t *ds;
4063 surfaceInfo_t *info;
4071 /* get drawsurface and info */
4072 ds = &bspDrawSurfaces[ num ];
4073 info = &surfaceInfos[ num ];
4075 /* get the mins/maxs for the dsurf */
4076 ClearBounds( mins, maxs );
4077 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4078 for ( i = 0; i < ds->numVerts; i++ )
4080 dv = &yDrawVerts[ ds->firstVert + i ];
4081 AddPointToBounds( dv->xyz, mins, maxs );
4082 if ( !VectorCompare( dv->normal, normal ) ) {
4083 VectorClear( normal );
4087 /* create the lights for the bounding box */
4088 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4091 /////////////////////////////////////////////////////////////
4093 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4094 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4095 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4096 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4098 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4099 static int numFloodVectors = 0;
4101 void SetupFloodLight( void ){
4103 float angle, elevation, angleStep, elevationStep;
4105 double v1,v2,v3,v4,v5,v6;
4108 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4110 /* calculate angular steps */
4111 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4112 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4116 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4118 /* iterate elevation */
4119 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4121 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4122 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4123 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4128 /* emit some statistics */
4129 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4132 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4134 if ( value[ 0 ] != '\0' ) {
4136 v4 = floodlightDistance;
4137 v5 = floodlightIntensity;
4138 v6 = floodlightDirectionScale;
4140 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4142 floodlightRGB[0] = v1;
4143 floodlightRGB[1] = v2;
4144 floodlightRGB[2] = v3;
4146 if ( VectorLength( floodlightRGB ) == 0 ) {
4147 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4160 floodlightDistance = v4;
4161 floodlightIntensity = v5;
4162 floodlightDirectionScale = v6;
4164 floodlighty = qtrue;
4165 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4169 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4172 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4173 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4174 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4176 ColorNormalize( floodlightRGB,floodlightRGB );
4180 FloodLightForSample()
4181 calculates floodlight value for a given sample
4182 once again, kudos to the dirtmapping coder
4185 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4190 float gatherLight, outLight;
4191 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4199 if ( trace == NULL || trace->cluster < 0 ) {
4205 dd = floodLightDistance;
4206 VectorCopy( trace->normal, normal );
4208 /* check if the normal is aligned to the world-up */
4209 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4210 if ( normal[ 2 ] == 1.0f ) {
4211 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4212 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4214 else if ( normal[ 2 ] == -1.0f ) {
4215 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4216 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4221 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4222 CrossProduct( normal, worldUp, myRt );
4223 VectorNormalize( myRt, myRt );
4224 CrossProduct( myRt, normal, myUp );
4225 VectorNormalize( myUp, myUp );
4228 /* vortex: optimise floodLightLowQuality a bit */
4229 if ( floodLightLowQuality == qtrue ) {
4230 /* iterate through ordered vectors */
4231 for ( i = 0; i < numFloodVectors; i++ )
4232 if ( rand() % 10 != 0 ) {
4238 /* iterate through ordered vectors */
4239 for ( i = 0; i < numFloodVectors; i++ )
4243 /* transform vector into tangent space */
4244 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4245 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4246 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4249 VectorMA( trace->origin, dd, direction, trace->end );
4251 //VectorMA( trace->origin, 1, direction, trace->origin );
4253 SetupTrace( trace );
4254 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4259 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4260 contribution = 1.0f;
4262 else if ( trace->opaque ) {
4263 VectorSubtract( trace->hit, trace->origin, displacement );
4264 d = VectorLength( displacement );
4266 // d=trace->distance;
4267 //if (d>256) gatherDirt+=1;
4268 contribution = d / dd;
4269 if ( contribution > 1 ) {
4270 contribution = 1.0f;
4273 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4276 gatherLight += contribution;
4281 if ( gatherLight <= 0.0f ) {
4290 gatherLight /= ( sub );
4292 outLight = gatherLight;
4293 if ( outLight > 1.0f ) {
4297 /* return to sender */
4302 FloodLightRawLightmap
4303 lighttracer style ambient occlusion light hack.
4304 Kudos to the dirtmapping author for most of this source.
4305 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4306 VorteX: fixed problems with deluxemapping
4309 // floodlight pass on a lightmap
4310 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4311 int i, x, y, *cluster;
4312 float *origin, *normal, *floodlight, floodLightAmount;
4313 surfaceInfo_t *info;
4316 // float samples, average, *floodlight2;
4318 memset( &trace,0,sizeof( trace_t ) );
4321 trace.testOcclusion = qtrue;
4322 trace.forceSunlight = qfalse;
4323 trace.twoSided = qtrue;
4324 trace.recvShadows = lm->recvShadows;
4325 trace.numSurfaces = lm->numLightSurfaces;
4326 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4327 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4328 trace.testAll = qfalse;
4329 trace.distance = 1024;
4331 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4332 //trace.twoSided = qfalse;
4333 for ( i = 0; i < trace.numSurfaces; i++ )
4336 info = &surfaceInfos[ trace.surfaces[ i ] ];
4338 /* check twosidedness */
4339 if ( info->si->twoSided ) {
4340 trace.twoSided = qtrue;
4345 /* gather floodlight */
4346 for ( y = 0; y < lm->sh; y++ )
4348 for ( x = 0; x < lm->sw; x++ )
4351 cluster = SUPER_CLUSTER( x, y );
4352 origin = SUPER_ORIGIN( x, y );
4353 normal = SUPER_NORMAL( x, y );
4354 floodlight = SUPER_FLOODLIGHT( x, y );
4356 /* set default dirt */
4359 /* only look at mapped luxels */
4360 if ( *cluster < 0 ) {
4365 trace.cluster = *cluster;
4366 VectorCopy( origin, trace.origin );
4367 VectorCopy( normal, trace.normal );
4369 /* get floodlight */
4370 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4372 /* add floodlight */
4373 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4374 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4375 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4376 floodlight[3] += floodlightDirectionScale;
4380 /* testing no filtering */
4386 for ( y = 0; y < lm->sh; y++ )
4388 for ( x = 0; x < lm->sw; x++ )
4391 cluster = SUPER_CLUSTER( x, y );
4392 floodlight = SUPER_FLOODLIGHT( x, y );
4394 /* filter dirt by adjacency to unmapped luxels */
4395 average = *floodlight;
4397 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4399 if ( sy < 0 || sy >= lm->sh ) {
4403 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4405 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4409 /* get neighboring luxel */
4410 cluster = SUPER_CLUSTER( sx, sy );
4411 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4412 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4417 average += *floodlight2;
4422 if ( samples <= 0.0f ) {
4428 if ( samples <= 0.0f ) {
4433 *floodlight = average / samples;
4439 void FloodLightRawLightmap( int rawLightmapNum ){
4442 /* bail if this number exceeds the number of raw lightmaps */
4443 if ( rawLightmapNum >= numRawLightmaps ) {
4447 lm = &rawLightmaps[ rawLightmapNum ];
4450 if ( floodlighty && floodlightIntensity ) {
4451 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4455 if ( lm->floodlightIntensity ) {
4456 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4457 numSurfacesFloodlighten += 1;
4461 void FloodlightRawLightmaps(){
4462 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4463 numSurfacesFloodlighten = 0;
4464 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4465 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4469 FloodLightIlluminate()
4470 illuminate floodlight into lightmap luxels
4473 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4474 float *luxel, *floodlight, *deluxel, *normal;
4477 int x, y, lightmapNum;
4479 /* walk lightmaps */
4480 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4483 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4487 /* apply floodlight to each luxel */
4488 for ( y = 0; y < lm->sh; y++ )
4490 for ( x = 0; x < lm->sw; x++ )
4492 /* get floodlight */
4493 floodlight = SUPER_FLOODLIGHT( x, y );
4494 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4499 cluster = SUPER_CLUSTER( x, y );
4501 /* only process mapped luxels */
4502 if ( *cluster < 0 ) {
4506 /* get particulars */
4507 luxel = SUPER_LUXEL( lightmapNum, x, y );
4508 deluxel = SUPER_DELUXEL( x, y );
4510 /* add to lightmap */
4511 luxel[0] += floodlight[0];
4512 luxel[1] += floodlight[1];
4513 luxel[2] += floodlight[2];
4515 if ( luxel[3] == 0 ) {
4519 /* add to deluxemap */
4520 if ( deluxemap && floodlight[3] > 0 ) {
4523 normal = SUPER_NORMAL( x, y );
4524 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4526 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4527 if ( brightness < 0.00390625f ) {
4528 brightness = 0.00390625f;
4531 VectorScale( normal, brightness, lightvector );
4532 VectorAdd( deluxel, lightvector, deluxel );