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 scale *= lightmapBrightness;
61 /* make a local copy */
62 VectorScale( color, scale, sample );
65 gamma = 1.0f / lightmapGamma;
66 for ( i = 0; i < 3; i++ )
68 /* handle negative light */
69 if ( sample[ i ] < 0.0f ) {
75 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
78 if ( lightmapExposure == 0 ) {
79 /* clamp with color normalization */
81 if ( sample[ 1 ] > max ) {
84 if ( sample[ 2 ] > max ) {
88 VectorScale( sample, ( 255.0f / max ), sample );
93 inv = 1.f / lightmapExposure;
97 if ( sample[ 1 ] > max ) {
100 if ( sample[ 2 ] > max ) {
104 dif = ( 1 - exp( -max * inv ) ) * 255;
114 for ( i = 0; i < 3; i++ )
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
125 if ( lightmapContrast != 1.0f ){
126 for ( i = 0; i < 3; i++ ){
127 sample[i] = lightmapContrast * ( sample[i] - 128 ) + 128;
128 if ( sample[i] < 0 ){
132 if ( ( sample[0] > 255 ) || ( sample[1] > 255 ) || ( sample[2] > 255 ) ) {
133 max = sample[0] > sample[1] ? sample[0] : sample[1];
134 max = max > sample[2] ? max : sample[2];
135 sample[0] = sample[0] * 255 / max;
136 sample[1] = sample[1] * 255 / max;
137 sample[2] = sample[2] * 255 / max;
142 if ( lightmapsRGB ) {
143 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
144 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
145 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
149 colorBytes[ 0 ] = sample[ 0 ];
150 colorBytes[ 1 ] = sample[ 1 ];
151 colorBytes[ 2 ] = sample[ 2 ];
155 * Same as ColorToBytes, but if the output color will never contain zero
156 * components. Used to avoid returning 0 0 0 due to an ioq3 issue. Reason
157 * to also map 0 0 1 to 1 1 1 is to ensure monotonicity in the color mapping
158 * to prevent banding-like artifacts on lightmaps.
160 void ColorToBytesNonZero( const float *color, byte *colorBytes, float scale) {
162 ColorToBytes(color, colorBytes, scale);
163 for (i = 0; i < 3; ++i)
164 if (colorBytes[i] == 0)
169 /* -------------------------------------------------------------------------------
171 this section deals with phong shading (normal interpolation across brush faces)
173 ------------------------------------------------------------------------------- */
177 smooths together coincident vertex normals across the bsp
180 #define MAX_SAMPLES 256
181 #define THETA_EPSILON 0.000001
182 #define EQUAL_NORMAL_EPSILON 0.01
184 void SmoothNormals( void ){
185 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
186 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
187 bspDrawSurface_t *ds;
191 vec3_t average, diff;
192 int indexes[ MAX_SAMPLES ];
193 vec3_t votes[ MAX_SAMPLES ];
196 /* allocate shade angle table */
197 shadeAngles = safe_malloc0( numBSPDrawVerts * sizeof( float ) );
199 /* allocate smoothed table */
200 cs = ( numBSPDrawVerts / 8 ) + 1;
201 smoothed = safe_malloc0( cs );
203 /* set default shade angle */
204 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
207 /* run through every surface and flag verts belonging to non-lightmapped surfaces
208 and set per-vertex smoothing angle */
209 for ( i = 0; i < numBSPDrawSurfaces; i++ )
212 ds = &bspDrawSurfaces[ i ];
214 /* get shader for shade angle */
215 si = surfaceInfos[ i ].si;
216 if ( si->shadeAngleDegrees ) {
217 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
220 shadeAngle = defaultShadeAngle;
222 if ( shadeAngle > maxShadeAngle ) {
223 maxShadeAngle = shadeAngle;
227 for ( j = 0; j < ds->numVerts; j++ )
229 f = ds->firstVert + j;
230 shadeAngles[ f ] = shadeAngle;
231 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
232 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
236 /* ydnar: optional force-to-trisoup */
237 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
238 ds->surfaceType = MST_TRIANGLE_SOUP;
239 ds->lightmapNum[ 0 ] = -3;
243 /* bail if no surfaces have a shade angle */
244 if ( maxShadeAngle == 0 ) {
252 start = I_FloatTime();
254 /* go through the list of vertexes */
255 for ( i = 0; i < numBSPDrawVerts; i++ )
258 f = 10 * i / numBSPDrawVerts;
261 Sys_Printf( "%i...", f );
264 /* already smoothed? */
265 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
270 VectorClear( average );
274 /* build a table of coincident vertexes */
275 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
277 /* already smoothed? */
278 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
283 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
287 /* use smallest shade angle */
288 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
290 /* check shade angle */
291 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
295 else if ( dot < -1.0 ) {
298 testAngle = acos( dot ) + THETA_EPSILON;
299 if ( testAngle >= shadeAngle ) {
300 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
303 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
305 /* add to the list */
306 indexes[ numVerts++ ] = j;
309 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
311 /* see if this normal has already been voted */
312 for ( k = 0; k < numVotes; k++ )
314 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
315 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
316 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
317 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
322 /* add a new vote? */
323 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
324 VectorAdd( average, bspDrawVerts[ j ].normal, average );
325 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
330 /* don't average for less than 2 verts */
331 if ( numVerts < 2 ) {
336 if ( VectorNormalize( average, average ) > 0 ) {
338 for ( j = 0; j < numVerts; j++ )
339 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
343 /* free the tables */
348 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
353 /* -------------------------------------------------------------------------------
355 this section deals with phong shaded lightmap tracing
357 ------------------------------------------------------------------------------- */
359 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
363 calculates the st tangent vectors for normalmapping
366 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
372 /* calculate barycentric basis for the triangle */
373 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 ] );
374 if ( fabs( bb ) < 0.00000001f ) {
379 for ( i = 0; i < numVerts; i++ )
381 /* calculate s tangent vector */
382 s = dv[ i ]->st[ 0 ] + 10.0f;
383 t = dv[ i ]->st[ 1 ];
384 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
385 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
386 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
388 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
389 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
390 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
392 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
393 VectorNormalize( stv[ i ], stv[ i ] );
395 /* calculate t tangent vector */
396 s = dv[ i ]->st[ 0 ];
397 t = dv[ i ]->st[ 1 ] + 10.0f;
398 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
399 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
400 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
402 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
403 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
404 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
406 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
407 VectorNormalize( ttv[ i ], ttv[ i ] );
410 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
411 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
414 /* return to caller */
423 perterbs the normal by the shader's normalmap in tangent space
426 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
432 VectorCopy( dv->normal, pNormal );
434 /* sample normalmap */
435 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
439 /* remap sampled normal from [0,255] to [-1,-1] */
440 for ( i = 0; i < 3; i++ )
441 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
443 /* scale tangent vectors and add to original normal */
444 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
445 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
446 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
448 /* renormalize and return */
449 VectorNormalize( pNormal, pNormal );
456 maps a luxel for triangle bv at
460 #define BOGUS_NUDGE -99999.0f
462 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 ] ){
463 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
464 float *luxel, *origin, *normal, d, lightmapSampleOffset;
471 vec4_t sideplane, hostplane;
476 static float nudges[][ 2 ] =
478 //%{ 0, 0 }, /* try center first */
479 { -NUDGE, 0 }, /* left */
480 { NUDGE, 0 }, /* right */
481 { 0, NUDGE }, /* up */
482 { 0, -NUDGE }, /* down */
483 { -NUDGE, NUDGE }, /* left/up */
484 { NUDGE, -NUDGE }, /* right/down */
485 { NUDGE, NUDGE }, /* right/up */
486 { -NUDGE, -NUDGE }, /* left/down */
487 { BOGUS_NUDGE, BOGUS_NUDGE }
491 /* find luxel xy coords (fixme: subtract 0.5?) */
492 x = dv->lightmap[ 0 ][ 0 ];
493 y = dv->lightmap[ 0 ][ 1 ];
497 else if ( x >= lm->sw ) {
503 else if ( y >= lm->sh ) {
507 /* set shader and cluster list */
508 if ( info != NULL ) {
510 numClusters = info->numSurfaceClusters;
511 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
520 /* get luxel, origin, cluster, and normal */
521 luxel = SUPER_LUXEL( 0, x, y );
522 origin = SUPER_ORIGIN( x, y );
523 normal = SUPER_NORMAL( x, y );
524 cluster = SUPER_CLUSTER( x, y );
526 /* don't attempt to remap occluded luxels for planar surfaces */
527 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
531 /* only average the normal for premapped luxels */
532 else if ( ( *cluster ) >= 0 ) {
533 /* do bumpmap calculations */
535 PerturbNormal( dv, si, pNormal, stv, ttv );
538 VectorCopy( dv->normal, pNormal );
541 /* add the additional normal data */
542 VectorAdd( normal, pNormal, normal );
547 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
551 /* axial lightmap projection */
552 if ( lm->vecs != NULL ) {
553 /* calculate an origin for the sample from the lightmap vectors */
554 VectorCopy( lm->origin, origin );
555 for ( i = 0; i < 3; i++ )
557 /* add unless it's the axis, which is taken care of later */
558 if ( i == lm->axisNum ) {
561 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
564 /* project the origin onto the plane */
565 d = DotProduct( origin, plane ) - plane[ 3 ];
566 d /= plane[ lm->axisNum ];
567 origin[ lm->axisNum ] -= d;
570 /* non axial lightmap projection (explicit xyz) */
572 VectorCopy( dv->xyz, origin );
575 //////////////////////
576 //27's test to make sure samples stay within the triangle boundaries
577 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
578 //2) if it does, nudge it onto the correct side.
580 if ( worldverts != NULL && lightmapTriangleCheck ) {
581 for ( j = 0; j < 3; j++ )
583 VectorCopy( worldverts[j],cverts[j] );
585 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
587 for ( j = 0; j < 3; j++ )
589 for ( i = 0; i < 3; i++ )
591 //build plane using 2 edges and a normal
592 next = ( i + 1 ) % 3;
594 VectorCopy( cverts[next],temp );
595 VectorAdd( temp,hostplane,temp );
596 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
598 //planetest sample point
599 e = DotProduct( origin,sideplane );
600 e = e - sideplane[3];
603 //VectorClear(origin);
604 //Move the sample point back inside triangle bounds
605 origin[0] -= sideplane[0] * ( e + 1 );
606 origin[1] -= sideplane[1] * ( e + 1 );
607 origin[2] -= sideplane[2] * ( e + 1 );
609 VectorClear( origin );
616 ////////////////////////
618 /* planar surfaces have precalculated lightmap vectors for nudging */
619 if ( lm->plane != NULL ) {
620 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
621 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
622 VectorCopy( lm->plane, vecs[ 2 ] );
625 /* non-planar surfaces must calculate them */
628 if ( plane != NULL ) {
629 VectorCopy( plane, vecs[ 2 ] );
632 VectorCopy( dv->normal, vecs[ 2 ] );
634 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
637 /* push the origin off the surface a bit */
639 lightmapSampleOffset = si->lightmapSampleOffset;
642 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
644 if ( lm->axisNum < 0 ) {
645 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
647 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
648 origin[ lm->axisNum ] -= lightmapSampleOffset;
651 origin[ lm->axisNum ] += lightmapSampleOffset;
654 VectorCopy( origin,origintwo );
655 if ( lightmapExtraVisClusterNudge ) {
656 origintwo[0] += vecs[2][0];
657 origintwo[1] += vecs[2][1];
658 origintwo[2] += vecs[2][2];
662 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
664 /* another retarded hack, storing nudge count in luxel[ 1 ] */
667 /* point in solid? (except in dark mode) */
668 if ( pointCluster < 0 && dark == qfalse ) {
669 /* nudge the the location around */
671 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
673 /* nudge the vector around a bit */
674 for ( i = 0; i < 3; i++ )
676 /* set nudged point*/
677 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
681 /* get pvs cluster */
682 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
683 if ( pointCluster >= 0 ) {
684 VectorCopy( nudged, origin );
690 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
691 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
692 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
693 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
694 if ( pointCluster >= 0 ) {
695 VectorCopy( nudged, origin );
701 if ( pointCluster < 0 ) {
702 ( *cluster ) = CLUSTER_OCCLUDED;
703 VectorClear( origin );
704 VectorClear( normal );
710 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
712 /* do bumpmap calculations */
714 PerturbNormal( dv, si, pNormal, stv, ttv );
717 VectorCopy( dv->normal, pNormal );
720 /* store the cluster and normal */
721 ( *cluster ) = pointCluster;
722 VectorCopy( pNormal, normal );
724 /* store explicit mapping pass and implicit mapping pass */
739 recursively subdivides a triangle until its edges are shorter
740 than the distance between two luxels (thanks jc :)
743 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 ] ){
744 bspDrawVert_t mid, *dv2[ 3 ];
748 /* map the vertexes */
750 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
751 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
752 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
758 float *a, *b, dx, dy, dist, maxDist;
761 /* find the longest edge and split it */
764 for ( i = 0; i < 3; i++ )
767 a = dv[ i ]->lightmap[ 0 ];
768 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
771 dx = a[ 0 ] - b[ 0 ];
772 dy = a[ 1 ] - b[ 1 ];
773 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
776 if ( dist > maxDist ) {
782 /* try to early out */
783 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
788 /* split the longest edge and map it */
789 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
790 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
792 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
793 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
795 /* recurse to first triangle */
796 VectorCopy( dv, dv2 );
798 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
800 /* recurse to second triangle */
801 VectorCopy( dv, dv2 );
802 dv2[ ( max + 1 ) % 3 ] = ∣
803 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
810 seed function for MapTriangle_r()
811 requires a cw ordered triangle
814 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
817 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
818 vec3_t worldverts[ 3 ];
821 /* get plane if possible */
822 if ( lm->plane != NULL ) {
823 VectorCopy( lm->plane, plane );
824 plane[ 3 ] = lm->plane[ 3 ];
827 /* otherwise make one from the points */
828 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
832 /* check to see if we need to calculate texture->world tangent vectors */
833 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
843 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
844 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
845 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
847 /* map the vertexes */
848 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
849 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
850 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
852 /* 2002-11-20: prefer axial triangle edges */
854 /* subdivide the triangle */
855 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
859 for ( i = 0; i < 3; i++ )
862 bspDrawVert_t *dv2[ 3 ];
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
869 /* make degenerate triangles for mapping edges */
870 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
872 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
873 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
875 /* map the degenerate triangle */
876 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
887 recursively subdivides a quad until its edges are shorter
888 than the distance between two luxels
891 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 ] ){
892 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
899 float *a, *b, dx, dy, dist, maxDist;
902 /* find the longest edge and split it */
905 for ( i = 0; i < 4; i++ )
908 a = dv[ i ]->lightmap[ 0 ];
909 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
912 dx = a[ 0 ] - b[ 0 ];
913 dy = a[ 1 ] - b[ 1 ];
914 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
917 if ( dist > maxDist ) {
923 /* try to early out */
924 if ( max < 0 || maxDist <= subdivideThreshold ) {
929 /* we only care about even/odd edges */
932 /* split the longest edges */
933 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
934 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
936 /* map the vertexes */
937 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
938 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
942 /* recurse to first quad */
944 dv2[ 1 ] = &mid[ 0 ];
945 dv2[ 2 ] = &mid[ 1 ];
947 MapQuad_r( lm, info, dv2, plane, stv, ttv );
949 /* recurse to second quad */
950 dv2[ 0 ] = &mid[ 0 ];
953 dv2[ 3 ] = &mid[ 1 ];
954 MapQuad_r( lm, info, dv2, plane, stv, ttv );
960 /* recurse to first quad */
963 dv2[ 2 ] = &mid[ 0 ];
964 dv2[ 3 ] = &mid[ 1 ];
965 MapQuad_r( lm, info, dv2, plane, stv, ttv );
967 /* recurse to second quad */
968 dv2[ 0 ] = &mid[ 1 ];
969 dv2[ 1 ] = &mid[ 0 ];
972 MapQuad_r( lm, info, dv2, plane, stv, ttv );
980 seed function for MapQuad_r()
981 requires a cw ordered triangle quad
984 #define QUAD_PLANAR_EPSILON 0.5f
986 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
989 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
992 /* get plane if possible */
993 if ( lm->plane != NULL ) {
994 VectorCopy( lm->plane, plane );
995 plane[ 3 ] = lm->plane[ 3 ];
998 /* otherwise make one from the points */
999 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
1003 /* 4th point must fall on the plane */
1004 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
1005 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
1009 /* check to see if we need to calculate texture->world tangent vectors */
1010 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
1020 /* map the vertexes */
1021 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1022 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1023 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1024 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1026 /* subdivide the quad */
1027 MapQuad_r( lm, info, dv, plane, stv, ttv );
1035 maps the locations, normals, and pvs clusters for a raw lightmap
1038 #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)
1040 void MapRawLightmap( int rawLightmapNum ){
1041 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1042 float *luxel, *origin, *normal, samples, radius, pass;
1044 bspDrawSurface_t *ds;
1045 surfaceInfo_t *info;
1046 mesh_t src, *subdivided, *mesh;
1047 bspDrawVert_t *verts, *dv[ 4 ], fake;
1050 /* bail if this number exceeds the number of raw lightmaps */
1051 if ( rawLightmapNum >= numRawLightmaps ) {
1056 lm = &rawLightmaps[ rawLightmapNum ];
1058 /* -----------------------------------------------------------------
1059 map referenced surfaces onto the raw lightmap
1060 ----------------------------------------------------------------- */
1062 /* walk the list of surfaces on this raw lightmap */
1063 for ( n = 0; n < lm->numLightSurfaces; n++ )
1065 /* with > 1 surface per raw lightmap, clear occluded */
1067 for ( y = 0; y < lm->sh; y++ )
1069 for ( x = 0; x < lm->sw; x++ )
1072 cluster = SUPER_CLUSTER( x, y );
1073 if ( *cluster < 0 ) {
1074 *cluster = CLUSTER_UNMAPPED;
1081 num = lightSurfaces[ lm->firstLightSurface + n ];
1082 ds = &bspDrawSurfaces[ num ];
1083 info = &surfaceInfos[ num ];
1085 /* bail if no lightmap to calculate */
1086 if ( info->lm != lm ) {
1091 /* map the surface onto the lightmap origin/cluster/normal buffers */
1092 switch ( ds->surfaceType )
1096 verts = yDrawVerts + ds->firstVert;
1098 /* map the triangles */
1099 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1101 for ( i = 0; i < ds->numIndexes; i += 3 )
1103 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1104 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1105 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1106 MapTriangle( lm, info, dv, mapNonAxial );
1112 /* make a mesh from the drawsurf */
1113 src.width = ds->patchWidth;
1114 src.height = ds->patchHeight;
1115 src.verts = &yDrawVerts[ ds->firstVert ];
1116 //% subdivided = SubdivideMesh( src, 8, 512 );
1117 subdivided = SubdivideMesh2( src, info->patchIterations );
1119 /* fit it to the curve and remove colinear verts on rows/columns */
1120 PutMeshOnCurve( *subdivided );
1121 mesh = RemoveLinearMeshColumnsRows( subdivided );
1122 FreeMesh( subdivided );
1125 verts = mesh->verts;
1130 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1131 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1132 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1133 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1137 /* map the mesh quads */
1140 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1142 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1144 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1147 pw[ 0 ] = x + ( y * mesh->width );
1148 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1149 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1150 pw[ 3 ] = x + 1 + ( y * mesh->width );
1151 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1156 /* get drawverts and map first triangle */
1157 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1158 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1159 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1160 MapTriangle( lm, info, dv, mapNonAxial );
1162 /* get drawverts and map second triangle */
1163 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1164 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1165 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1166 MapTriangle( lm, info, dv, mapNonAxial );
1173 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1175 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1178 pw[ 0 ] = x + ( y * mesh->width );
1179 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1180 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1181 pw[ 3 ] = x + 1 + ( y * mesh->width );
1187 /* attempt to map quad first */
1188 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1189 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1190 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1191 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1192 if ( MapQuad( lm, info, dv ) ) {
1196 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1198 /* get drawverts and map first triangle */
1199 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1200 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1201 MapTriangle( lm, info, dv, mapNonAxial );
1203 /* get drawverts and map second triangle */
1204 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1205 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1206 MapTriangle( lm, info, dv, mapNonAxial );
1222 /* -----------------------------------------------------------------
1223 average and clean up luxel normals
1224 ----------------------------------------------------------------- */
1226 /* walk the luxels */
1227 for ( y = 0; y < lm->sh; y++ )
1229 for ( x = 0; x < lm->sw; x++ )
1232 luxel = SUPER_LUXEL( 0, x, y );
1233 normal = SUPER_NORMAL( x, y );
1234 cluster = SUPER_CLUSTER( x, y );
1236 /* only look at mapped luxels */
1237 if ( *cluster < 0 ) {
1241 /* the normal data could be the sum of multiple samples */
1242 if ( luxel[ 3 ] > 1.0f ) {
1243 VectorNormalize( normal, normal );
1246 /* mark this luxel as having only one normal */
1251 /* non-planar surfaces stop here */
1252 if ( lm->plane == NULL ) {
1256 /* -----------------------------------------------------------------
1257 map occluded or unuxed luxels
1258 ----------------------------------------------------------------- */
1260 /* walk the luxels */
1261 radius = floor( superSample / 2 );
1262 radius = radius > 0 ? radius : 1.0f;
1264 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1266 for ( y = 0; y < lm->sh; y++ )
1268 for ( x = 0; x < lm->sw; x++ )
1271 luxel = SUPER_LUXEL( 0, x, y );
1272 normal = SUPER_NORMAL( x, y );
1273 cluster = SUPER_CLUSTER( x, y );
1275 /* only look at unmapped luxels */
1276 if ( *cluster != CLUSTER_UNMAPPED ) {
1280 /* divine a normal and origin from neighboring luxels */
1281 VectorClear( fake.xyz );
1282 VectorClear( fake.normal );
1283 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1284 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1286 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1288 if ( sy < 0 || sy >= lm->sh ) {
1292 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1294 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1298 /* get neighboring luxel */
1299 luxel = SUPER_LUXEL( 0, sx, sy );
1300 origin = SUPER_ORIGIN( sx, sy );
1301 normal = SUPER_NORMAL( sx, sy );
1302 cluster = SUPER_CLUSTER( sx, sy );
1304 /* only consider luxels mapped in previous passes */
1305 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1309 /* add its distinctiveness to our own */
1310 VectorAdd( fake.xyz, origin, fake.xyz );
1311 VectorAdd( fake.normal, normal, fake.normal );
1312 samples += luxel[ 3 ];
1317 if ( samples == 0.0f ) {
1322 VectorDivide( fake.xyz, samples, fake.xyz );
1323 //% VectorDivide( fake.normal, samples, fake.normal );
1324 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1328 /* map the fake vert */
1329 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1334 /* -----------------------------------------------------------------
1335 average and clean up luxel normals
1336 ----------------------------------------------------------------- */
1338 /* walk the luxels */
1339 for ( y = 0; y < lm->sh; y++ )
1341 for ( x = 0; x < lm->sw; x++ )
1344 luxel = SUPER_LUXEL( 0, x, y );
1345 normal = SUPER_NORMAL( x, y );
1346 cluster = SUPER_CLUSTER( x, y );
1348 /* only look at mapped luxels */
1349 if ( *cluster < 0 ) {
1353 /* the normal data could be the sum of multiple samples */
1354 if ( luxel[ 3 ] > 1.0f ) {
1355 VectorNormalize( normal, normal );
1358 /* mark this luxel as having only one normal */
1366 for ( y = 0; y < lm->sh; y++ )
1368 for ( x = 0; x < lm->sw; x++ )
1373 cluster = SUPER_CLUSTER( x, y );
1374 origin = SUPER_ORIGIN( x, y );
1375 normal = SUPER_NORMAL( x, y );
1376 luxel = SUPER_LUXEL( x, y );
1378 if ( *cluster < 0 ) {
1382 /* check if within the bounding boxes of all surfaces referenced */
1383 ClearBounds( mins, maxs );
1384 for ( n = 0; n < lm->numLightSurfaces; n++ )
1387 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1388 TOL = info->sampleSize + 2;
1389 AddPointToBounds( info->mins, mins, maxs );
1390 AddPointToBounds( info->maxs, mins, maxs );
1391 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1392 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1393 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1399 if ( n < lm->numLightSurfaces ) {
1403 /* report bogus origin */
1404 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",
1405 rawLightmapNum, x, y, *cluster,
1406 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1407 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1408 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1419 sets up dirtmap (ambient occlusion)
1422 #define DIRT_CONE_ANGLE 88 /* degrees */
1423 #define DIRT_NUM_ANGLE_STEPS 16
1424 #define DIRT_NUM_ELEVATION_STEPS 3
1425 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1427 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1428 static int numDirtVectors = 0;
1430 void SetupDirt( void ){
1432 float angle, elevation, angleStep, elevationStep;
1436 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1438 /* calculate angular steps */
1439 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1440 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1444 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1446 /* iterate elevation */
1447 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1449 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1450 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1451 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1456 /* emit some statistics */
1457 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1463 calculates dirt value for a given sample
1466 float DirtForSample( trace_t *trace ){
1468 float gatherDirt, outDirt, angle, elevation, ooDepth;
1469 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1476 if ( trace == NULL || trace->cluster < 0 ) {
1482 ooDepth = 1.0f / dirtDepth;
1483 VectorCopy( trace->normal, normal );
1485 /* check if the normal is aligned to the world-up */
1486 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1487 if ( normal[ 2 ] == 1.0f ) {
1488 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1489 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1491 else if ( normal[ 2 ] == -1.0f ) {
1492 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1493 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1498 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1499 CrossProduct( normal, worldUp, myRt );
1500 VectorNormalize( myRt, myRt );
1501 CrossProduct( myRt, normal, myUp );
1502 VectorNormalize( myUp, myUp );
1505 /* 1 = random mode, 0 (well everything else) = non-random mode */
1506 if ( dirtMode == 1 ) {
1508 for ( i = 0; i < numDirtVectors; i++ )
1510 /* get random vector */
1511 angle = Random() * DEG2RAD( 360.0f );
1512 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1513 temp[ 0 ] = cos( angle ) * sin( elevation );
1514 temp[ 1 ] = sin( angle ) * sin( elevation );
1515 temp[ 2 ] = cos( elevation );
1517 /* transform into tangent space */
1518 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1519 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1520 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1523 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1524 SetupTrace( trace );
1525 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1529 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1530 VectorSubtract( trace->hit, trace->origin, displacement );
1531 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1537 /* iterate through ordered vectors */
1538 for ( i = 0; i < numDirtVectors; i++ )
1540 /* transform vector into tangent space */
1541 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1542 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1543 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1546 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1547 SetupTrace( trace );
1548 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1552 if ( trace->opaque ) {
1553 VectorSubtract( trace->hit, trace->origin, displacement );
1554 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1560 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1561 SetupTrace( trace );
1562 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1566 if ( trace->opaque ) {
1567 VectorSubtract( trace->hit, trace->origin, displacement );
1568 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1572 if ( gatherDirt <= 0.0f ) {
1576 /* apply gain (does this even do much? heh) */
1577 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1578 if ( outDirt > 1.0f ) {
1583 outDirt *= dirtScale;
1584 if ( outDirt > 1.0f ) {
1588 /* return to sender */
1589 return 1.0f - outDirt;
1596 calculates dirty fraction for each luxel
1599 void DirtyRawLightmap( int rawLightmapNum ){
1600 int i, x, y, sx, sy, *cluster;
1601 float *origin, *normal, *dirt, *dirt2, average, samples;
1603 surfaceInfo_t *info;
1608 /* bail if this number exceeds the number of raw lightmaps */
1609 if ( rawLightmapNum >= numRawLightmaps ) {
1614 lm = &rawLightmaps[ rawLightmapNum ];
1617 trace.testOcclusion = qtrue;
1618 trace.forceSunlight = qfalse;
1619 trace.recvShadows = lm->recvShadows;
1620 trace.numSurfaces = lm->numLightSurfaces;
1621 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1622 trace.inhibitRadius = 0.0f;
1623 trace.testAll = qfalse;
1625 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1626 trace.twoSided = qfalse;
1627 for ( i = 0; i < trace.numSurfaces; i++ )
1630 info = &surfaceInfos[ trace.surfaces[ i ] ];
1632 /* check twosidedness */
1633 if ( info->si->twoSided ) {
1634 trace.twoSided = qtrue;
1640 for ( i = 0; i < trace.numSurfaces; i++ )
1643 info = &surfaceInfos[ trace.surfaces[ i ] ];
1645 /* check twosidedness */
1646 if ( info->si->noDirty ) {
1653 for ( y = 0; y < lm->sh; y++ )
1655 for ( x = 0; x < lm->sw; x++ )
1658 cluster = SUPER_CLUSTER( x, y );
1659 origin = SUPER_ORIGIN( x, y );
1660 normal = SUPER_NORMAL( x, y );
1661 dirt = SUPER_DIRT( x, y );
1663 /* set default dirt */
1666 /* only look at mapped luxels */
1667 if ( *cluster < 0 ) {
1671 /* don't apply dirty on this surface */
1678 trace.cluster = *cluster;
1679 VectorCopy( origin, trace.origin );
1680 VectorCopy( normal, trace.normal );
1683 *dirt = DirtForSample( &trace );
1687 /* testing no filtering */
1691 for ( y = 0; y < lm->sh; y++ )
1693 for ( x = 0; x < lm->sw; x++ )
1696 cluster = SUPER_CLUSTER( x, y );
1697 dirt = SUPER_DIRT( x, y );
1699 /* filter dirt by adjacency to unmapped luxels */
1702 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1704 if ( sy < 0 || sy >= lm->sh ) {
1708 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1710 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1714 /* get neighboring luxel */
1715 cluster = SUPER_CLUSTER( sx, sy );
1716 dirt2 = SUPER_DIRT( sx, sy );
1717 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1727 if ( samples <= 0.0f ) {
1733 if ( samples <= 0.0f ) {
1738 *dirt = average / samples;
1747 calculates the pvs cluster, origin, normal of a sub-luxel
1750 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1751 int i, *cluster, *cluster2;
1752 float *origin, *origin2, *normal; //% , *normal2;
1753 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1756 /* calulate x vector */
1757 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1758 cluster = SUPER_CLUSTER( x, y );
1759 origin = SUPER_ORIGIN( x, y );
1760 //% normal = SUPER_NORMAL( x, y );
1761 cluster2 = SUPER_CLUSTER( x + 1, y );
1762 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1763 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1765 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1766 cluster = SUPER_CLUSTER( x - 1, y );
1767 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1768 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1769 cluster2 = SUPER_CLUSTER( x, y );
1770 origin2 = SUPER_ORIGIN( x, y );
1771 //% normal2 = SUPER_NORMAL( x, y );
1774 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1777 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1778 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1780 /* calulate y vector */
1781 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1782 cluster = SUPER_CLUSTER( x, y );
1783 origin = SUPER_ORIGIN( x, y );
1784 //% normal = SUPER_NORMAL( x, y );
1785 cluster2 = SUPER_CLUSTER( x, y + 1 );
1786 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1787 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1789 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1790 cluster = SUPER_CLUSTER( x, y - 1 );
1791 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1792 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1793 cluster2 = SUPER_CLUSTER( x, y );
1794 origin2 = SUPER_ORIGIN( x, y );
1795 //% normal2 = SUPER_NORMAL( x, y );
1798 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1801 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1803 /* calculate new origin */
1804 for ( i = 0; i < 3; i++ )
1805 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1808 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1809 if ( *sampleCluster < 0 ) {
1813 /* calculate new normal */
1814 normal = SUPER_NORMAL( x, y );
1815 VectorCopy( normal, sampleNormal );
1823 SubsampleRawLuxel_r()
1824 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1827 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1828 int b, samples, mapped, lighted;
1831 vec3_t deluxel[ 4 ];
1832 vec3_t origin[ 4 ], normal[ 4 ];
1833 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1834 vec3_t color, direction = { 0, 0, 0 }, total;
1838 if ( lightLuxel[ 3 ] >= lightSamples ) {
1843 VectorClear( total );
1847 /* make 2x2 subsample stamp */
1848 for ( b = 0; b < 4; b++ )
1851 VectorCopy( sampleOrigin, origin[ b ] );
1853 /* calculate position */
1854 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1860 /* increment sample count */
1861 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1864 trace->cluster = *cluster;
1865 VectorCopy( origin[ b ], trace->origin );
1866 VectorCopy( normal[ b ], trace->normal );
1870 LightContributionToSample( trace );
1871 if ( trace->forceSubsampling > 1.0f ) {
1872 /* alphashadow: we subsample as deep as we can */
1878 /* add to totals (fixme: make contrast function) */
1879 VectorCopy( trace->color, luxel[ b ] );
1880 if ( lightDeluxel ) {
1881 VectorCopy( trace->directionContribution, deluxel[ b ] );
1883 VectorAdd( total, trace->color, total );
1884 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1889 /* subsample further? */
1890 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1891 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1892 lighted != 0 && lighted != mapped ) {
1893 for ( b = 0; b < 4; b++ )
1895 if ( cluster[ b ] < 0 ) {
1898 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1903 //% VectorClear( color );
1905 VectorCopy( lightLuxel, color );
1906 if ( lightDeluxel ) {
1907 VectorCopy( lightDeluxel, direction );
1910 for ( b = 0; b < 4; b++ )
1912 if ( cluster[ b ] < 0 ) {
1915 VectorAdd( color, luxel[ b ], color );
1916 if ( lightDeluxel ) {
1917 VectorAdd( direction, deluxel[ b ], direction );
1923 if ( samples > 0 ) {
1925 color[ 0 ] /= samples;
1926 color[ 1 ] /= samples;
1927 color[ 2 ] /= samples;
1930 VectorCopy( color, lightLuxel );
1931 lightLuxel[ 3 ] += 1.0f;
1933 if ( lightDeluxel ) {
1934 direction[ 0 ] /= samples;
1935 direction[ 1 ] /= samples;
1936 direction[ 2 ] /= samples;
1937 VectorCopy( direction, lightDeluxel );
1942 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1943 static void GaussLikeRandom( float sigma, float *x, float *y ){
1945 r = Random() * 2 * Q_PI;
1946 *x = sigma * 2.73861278752581783822 * cos( r );
1947 *y = sigma * 2.73861278752581783822 * sin( r );
1954 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1957 vec3_t origin, normal;
1958 vec3_t total, totaldirection;
1961 VectorClear( total );
1962 VectorClear( totaldirection );
1964 for ( b = 0; b < lightSamples; ++b )
1967 VectorCopy( sampleOrigin, origin );
1968 GaussLikeRandom( bias, &dx, &dy );
1970 /* calculate position */
1971 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1977 trace->cluster = cluster;
1978 VectorCopy( origin, trace->origin );
1979 VectorCopy( normal, trace->normal );
1981 LightContributionToSample( trace );
1982 VectorAdd( total, trace->color, total );
1983 if ( lightDeluxel ) {
1984 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1991 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1992 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1993 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1995 if ( lightDeluxel ) {
1996 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1997 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1998 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
2006 IlluminateRawLightmap()
2007 illuminates the luxels
2010 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
2011 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
2012 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
2014 void IlluminateRawLightmap( int rawLightmapNum ){
2015 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
2016 int *cluster, *cluster2, mapped, lighted, totalLighted;
2017 size_t llSize, ldSize;
2019 surfaceInfo_t *info;
2020 qboolean filterColor, filterDir;
2022 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2023 unsigned char *flag;
2024 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2025 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2026 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2028 float stackLightLuxels[ STACK_LL_SIZE ];
2031 /* bail if this number exceeds the number of raw lightmaps */
2032 if ( rawLightmapNum >= numRawLightmaps ) {
2037 lm = &rawLightmaps[ rawLightmapNum ];
2040 trace.testOcclusion = !noTrace;
2041 trace.forceSunlight = qfalse;
2042 trace.recvShadows = lm->recvShadows;
2043 trace.numSurfaces = lm->numLightSurfaces;
2044 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2045 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2047 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2048 trace.twoSided = qfalse;
2049 for ( i = 0; i < trace.numSurfaces; i++ )
2052 info = &surfaceInfos[ trace.surfaces[ i ] ];
2054 /* check twosidedness */
2055 if ( info->si->twoSided ) {
2056 trace.twoSided = qtrue;
2061 /* create a culled light list for this raw lightmap */
2062 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2064 /* -----------------------------------------------------------------
2066 ----------------------------------------------------------------- */
2069 numLuxelsIlluminated += ( lm->sw * lm->sh );
2071 /* test debugging state */
2072 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2073 /* debug fill the luxels */
2074 for ( y = 0; y < lm->sh; y++ )
2076 for ( x = 0; x < lm->sw; x++ )
2079 cluster = SUPER_CLUSTER( x, y );
2081 /* only fill mapped luxels */
2082 if ( *cluster < 0 ) {
2086 /* get particulars */
2087 luxel = SUPER_LUXEL( 0, x, y );
2088 origin = SUPER_ORIGIN( x, y );
2089 normal = SUPER_NORMAL( x, y );
2091 /* color the luxel with raw lightmap num? */
2092 if ( debugSurfaces ) {
2093 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2096 /* color the luxel with lightmap axis? */
2097 else if ( debugAxis ) {
2098 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2099 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2100 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2103 /* color the luxel with luxel cluster? */
2104 else if ( debugCluster ) {
2105 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2108 /* color the luxel with luxel origin? */
2109 else if ( debugOrigin ) {
2110 VectorSubtract( lm->maxs, lm->mins, temp );
2111 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2112 VectorSubtract( origin, lm->mins, temp2 );
2113 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2114 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2115 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2118 /* color the luxel with the normal */
2119 else if ( normalmap ) {
2120 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2121 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2122 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2125 /* otherwise clear it */
2127 VectorClear( luxel );
2137 /* allocate temporary per-light luxel storage */
2138 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2139 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2140 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2141 lightLuxels = stackLightLuxels;
2144 lightLuxels = safe_malloc( llSize );
2147 lightDeluxels = safe_malloc( ldSize );
2150 lightDeluxels = NULL;
2154 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2156 /* set ambient color */
2157 for ( y = 0; y < lm->sh; y++ )
2159 for ( x = 0; x < lm->sw; x++ )
2162 cluster = SUPER_CLUSTER( x, y );
2163 luxel = SUPER_LUXEL( 0, x, y );
2164 normal = SUPER_NORMAL( x, y );
2165 deluxel = SUPER_DELUXEL( x, y );
2167 /* blacken unmapped clusters */
2168 if ( *cluster < 0 ) {
2169 VectorClear( luxel );
2175 VectorCopy( ambientColor, luxel );
2177 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2179 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2180 if ( brightness < 0.00390625f ) {
2181 brightness = 0.00390625f;
2184 VectorScale( normal, brightness, deluxel );
2191 /* clear styled lightmaps */
2192 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2193 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2195 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2196 memset( lm->superLuxels[ lightmapNum ], 0, size );
2200 /* debugging code */
2201 //% if( trace.numLights <= 0 )
2202 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2204 /* walk light list */
2205 for ( i = 0; i < trace.numLights; i++ )
2208 trace.light = trace.lights[ i ];
2211 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2213 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2214 lm->styles[ lightmapNum ] == LS_NONE ) {
2219 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2220 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2221 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2226 memset( lightLuxels, 0, llSize );
2228 memset( lightDeluxels, 0, ldSize );
2232 /* determine filter radius */
2233 filterRadius = lm->filterRadius > trace.light->filterRadius
2235 : trace.light->filterRadius;
2236 if ( filterRadius < 0.0f ) {
2237 filterRadius = 0.0f;
2240 /* set luxel filter radius */
2241 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2242 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2243 luxelFilterRadius = 1;
2246 /* allocate sampling flags storage */
2247 if ( lightSamples > 1 || lightRandomSamples ) {
2248 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2249 if ( lm->superFlags == NULL ) {
2250 lm->superFlags = safe_malloc( size );
2252 memset( (void *) lm->superFlags, 0, size );
2255 /* initial pass, one sample per luxel */
2256 for ( y = 0; y < lm->sh; y++ )
2258 for ( x = 0; x < lm->sw; x++ )
2261 cluster = SUPER_CLUSTER( x, y );
2262 if ( *cluster < 0 ) {
2266 /* get particulars */
2267 lightLuxel = LIGHT_LUXEL( x, y );
2268 lightDeluxel = LIGHT_DELUXEL( x, y );
2269 origin = SUPER_ORIGIN( x, y );
2270 normal = SUPER_NORMAL( x, y );
2271 flag = SUPER_FLAG( x, y );
2273 /* set contribution count */
2274 lightLuxel[ 3 ] = 1.0f;
2277 trace.cluster = *cluster;
2278 VectorCopy( origin, trace.origin );
2279 VectorCopy( normal, trace.normal );
2281 /* get light for this sample */
2282 LightContributionToSample( &trace );
2283 VectorCopy( trace.color, lightLuxel );
2285 /* add the contribution to the deluxemap */
2287 VectorCopy( trace.directionContribution, lightDeluxel );
2290 /* check for evilness */
2291 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) ) {
2293 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2296 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2302 /* don't even bother with everything else if nothing was lit */
2303 if ( totalLighted == 0 ) {
2307 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2308 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2309 if ( lightSamples > 1 || lightRandomSamples ) {
2311 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2313 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2318 VectorClear( total );
2320 /* test 2x2 stamp */
2321 for ( t = 0; t < 4; t++ )
2323 /* set sample coords */
2324 sx = x + tests[ t ][ 0 ];
2325 sy = y + tests[ t ][ 1 ];
2328 cluster = SUPER_CLUSTER( sx, sy );
2329 if ( *cluster < 0 ) {
2335 flag = SUPER_FLAG( sx, sy );
2336 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2337 /* force a lighted/mapped discrepancy so we subsample */
2342 lightLuxel = LIGHT_LUXEL( sx, sy );
2343 VectorAdd( total, lightLuxel, total );
2344 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2349 /* if total color is under a certain amount, then don't bother subsampling */
2350 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2354 /* if all 4 pixels are either in shadow or light, then don't subsample */
2355 if ( lighted != 0 && lighted != mapped ) {
2356 for ( t = 0; t < 4; t++ )
2358 /* set sample coords */
2359 sx = x + tests[ t ][ 0 ];
2360 sy = y + tests[ t ][ 1 ];
2363 cluster = SUPER_CLUSTER( sx, sy );
2364 if ( *cluster < 0 ) {
2367 flag = SUPER_FLAG( sx, sy );
2368 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2371 lightLuxel = LIGHT_LUXEL( sx, sy );
2372 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2373 origin = SUPER_ORIGIN( sx, sy );
2375 /* only subsample shadowed luxels */
2376 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2380 if ( lightRandomSamples ) {
2381 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2384 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2387 *flag |= FLAG_ALREADY_SUBSAMPLED;
2389 /* debug code to colorize subsampled areas to yellow */
2390 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2391 //% VectorSet( luxel, 255, 204, 0 );
2398 /* tertiary pass, apply dirt map (ambient occlusion) */
2401 for ( y = 0; y < lm->sh; y++ )
2403 for ( x = 0; x < lm->sw; x++ )
2406 cluster = SUPER_CLUSTER( x, y );
2407 if ( *cluster < 0 ) {
2411 /* get particulars */
2412 lightLuxel = LIGHT_LUXEL( x, y );
2413 dirt = SUPER_DIRT( x, y );
2415 /* scale light value */
2416 VectorScale( lightLuxel, *dirt, lightLuxel );
2421 /* allocate sampling lightmap storage */
2422 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2423 /* allocate sampling lightmap storage */
2424 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2425 lm->superLuxels[ lightmapNum ] = safe_malloc0( size );
2429 if ( lightmapNum > 0 ) {
2430 lm->styles[ lightmapNum ] = trace.light->style;
2431 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2434 /* copy to permanent luxels */
2435 for ( y = 0; y < lm->sh; y++ )
2437 for ( x = 0; x < lm->sw; x++ )
2439 /* get cluster and origin */
2440 cluster = SUPER_CLUSTER( x, y );
2441 if ( *cluster < 0 ) {
2444 origin = SUPER_ORIGIN( x, y );
2447 if ( luxelFilterRadius ) {
2449 VectorClear( averageColor );
2450 VectorClear( averageDir );
2453 /* cheaper distance-based filtering */
2454 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2456 if ( sy < 0 || sy >= lm->sh ) {
2460 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2462 if ( sx < 0 || sx >= lm->sw ) {
2466 /* get particulars */
2467 cluster = SUPER_CLUSTER( sx, sy );
2468 if ( *cluster < 0 ) {
2471 lightLuxel = LIGHT_LUXEL( sx, sy );
2472 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2475 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2476 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2478 /* scale luxel by filter weight */
2479 VectorScale( lightLuxel, weight, color );
2480 VectorAdd( averageColor, color, averageColor );
2482 VectorScale( lightDeluxel, weight, direction );
2483 VectorAdd( averageDir, direction, averageDir );
2490 if ( samples <= 0.0f ) {
2494 /* scale into luxel */
2495 luxel = SUPER_LUXEL( lightmapNum, x, y );
2498 /* handle negative light */
2499 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2500 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2501 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2502 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2505 /* handle normal light */
2508 luxel[ 0 ] += averageColor[ 0 ] / samples;
2509 luxel[ 1 ] += averageColor[ 1 ] / samples;
2510 luxel[ 2 ] += averageColor[ 2 ] / samples;
2514 /* scale into luxel */
2515 deluxel = SUPER_DELUXEL( x, y );
2516 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2517 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2518 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2525 /* get particulars */
2526 lightLuxel = LIGHT_LUXEL( x, y );
2527 lightDeluxel = LIGHT_DELUXEL( x, y );
2528 luxel = SUPER_LUXEL( lightmapNum, x, y );
2529 deluxel = SUPER_DELUXEL( x, y );
2531 /* handle negative light */
2532 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2533 VectorScale( averageColor, -1.0f, averageColor );
2539 /* handle negative light */
2540 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2541 VectorSubtract( luxel, lightLuxel, luxel );
2544 /* handle normal light */
2546 VectorAdd( luxel, lightLuxel, luxel );
2550 VectorAdd( deluxel, lightDeluxel, deluxel );
2557 /* free temporary luxels */
2558 if ( lightLuxels != stackLightLuxels ) {
2559 free( lightLuxels );
2563 free( lightDeluxels );
2567 /* free light list */
2568 FreeTraceLights( &trace );
2570 /* floodlight pass */
2571 if ( floodlighty ) {
2572 FloodlightIlluminateLightmap( lm );
2575 if ( debugnormals ) {
2576 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2579 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2583 for ( y = 0; y < lm->sh; y++ )
2585 for ( x = 0; x < lm->sw; x++ )
2588 cluster = SUPER_CLUSTER( x, y );
2589 //% if( *cluster < 0 )
2592 /* get particulars */
2593 luxel = SUPER_LUXEL( lightmapNum, x, y );
2594 normal = SUPER_NORMAL( x, y );
2596 luxel[0] = ( normal[0] * 127 ) + 127;
2597 luxel[1] = ( normal[1] * 127 ) + 127;
2598 luxel[2] = ( normal[2] * 127 ) + 127;
2604 /* -----------------------------------------------------------------
2606 ----------------------------------------------------------------- */
2609 /* walk lightmaps */
2610 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2613 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2617 /* apply dirt to each luxel */
2618 for ( y = 0; y < lm->sh; y++ )
2620 for ( x = 0; x < lm->sw; x++ )
2623 cluster = SUPER_CLUSTER( x, y );
2625 /* get particulars */
2626 luxel = SUPER_LUXEL( lightmapNum, x, y );
2627 dirt = SUPER_DIRT( x, y );
2630 VectorScale( luxel, *dirt, luxel );
2634 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2641 /* -----------------------------------------------------------------
2643 ----------------------------------------------------------------- */
2645 /* walk lightmaps */
2646 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2649 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2653 /* average occluded luxels from neighbors */
2654 for ( y = 0; y < lm->sh; y++ )
2656 for ( x = 0; x < lm->sw; x++ )
2658 /* get particulars */
2659 cluster = SUPER_CLUSTER( x, y );
2660 luxel = SUPER_LUXEL( lightmapNum, x, y );
2661 deluxel = SUPER_DELUXEL( x, y );
2662 normal = SUPER_NORMAL( x, y );
2664 /* determine if filtering is necessary */
2665 filterColor = qfalse;
2667 if ( *cluster < 0 ||
2668 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2669 filterColor = qtrue;
2672 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2676 if ( !filterColor && !filterDir ) {
2680 /* choose seed amount */
2681 VectorClear( averageColor );
2682 VectorClear( averageDir );
2685 /* walk 3x3 matrix */
2686 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2688 if ( sy < 0 || sy >= lm->sh ) {
2692 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2694 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2698 /* get neighbor's particulars */
2699 cluster2 = SUPER_CLUSTER( sx, sy );
2700 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2701 deluxel2 = SUPER_DELUXEL( sx, sy );
2703 /* ignore unmapped/unlit luxels */
2704 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2705 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2709 /* add its distinctiveness to our own */
2710 VectorAdd( averageColor, luxel2, averageColor );
2711 samples += luxel2[ 3 ];
2713 VectorAdd( averageDir, deluxel2, averageDir );
2719 if ( samples <= 0.0f ) {
2723 /* dark lightmap seams */
2725 if ( lightmapNum == 0 ) {
2726 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2732 if ( filterColor ) {
2733 VectorDivide( averageColor, samples, luxel );
2737 VectorDivide( averageDir, samples, deluxel );
2740 /* set cluster to -3 */
2741 if ( *cluster < 0 ) {
2742 *cluster = CLUSTER_FLOODED;
2752 IlluminateVertexes()
2753 light the surface vertexes
2756 #define VERTEX_NUDGE 4.0f
2758 void IlluminateVertexes( int num ){
2759 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2760 int lightmapNum, numAvg;
2761 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2762 vec3_t temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2763 bspDrawSurface_t *ds;
2764 surfaceInfo_t *info;
2766 bspDrawVert_t *verts;
2768 float floodLightAmount;
2772 /* get surface, info, and raw lightmap */
2773 ds = &bspDrawSurfaces[ num ];
2774 info = &surfaceInfos[ num ];
2777 /* -----------------------------------------------------------------
2778 illuminate the vertexes
2779 ----------------------------------------------------------------- */
2781 /* calculate vertex lighting for surfaces without lightmaps */
2782 if ( lm == NULL || cpmaHack ) {
2784 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2785 trace.forceSunlight = info->si->forceSunlight;
2786 trace.recvShadows = info->recvShadows;
2787 trace.numSurfaces = 1;
2788 trace.surfaces = #
2789 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2791 /* twosided lighting */
2792 trace.twoSided = info->si->twoSided;
2794 /* make light list for this surface */
2795 CreateTraceLightsForSurface( num, &trace );
2798 verts = yDrawVerts + ds->firstVert;
2800 memset( avgColors, 0, sizeof( avgColors ) );
2802 /* walk the surface verts */
2803 for ( i = 0; i < ds->numVerts; i++ )
2805 /* get vertex luxel */
2806 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2808 /* color the luxel with raw lightmap num? */
2809 if ( debugSurfaces ) {
2810 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2813 /* color the luxel with luxel origin? */
2814 else if ( debugOrigin ) {
2815 VectorSubtract( info->maxs, info->mins, temp );
2816 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2817 VectorSubtract( verts[ i ].xyz, info->mins, temp2 );
2818 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2819 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2820 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2823 /* color the luxel with the normal */
2824 else if ( normalmap ) {
2825 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2826 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2827 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2830 else if ( info->si->noVertexLight ) {
2831 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
2834 else if ( noVertexLighting > 0 ) {
2835 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
2838 /* illuminate the vertex */
2841 /* clear vertex luxel */
2842 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2844 /* try at initial origin */
2845 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2846 if ( trace.cluster >= 0 ) {
2848 VectorCopy( verts[ i ].xyz, trace.origin );
2849 VectorCopy( verts[ i ].normal, trace.normal );
2852 if ( dirty && !bouncing ) {
2853 dirt = DirtForSample( &trace );
2859 /* jal: floodlight */
2860 floodLightAmount = 0.0f;
2861 VectorClear( floodColor );
2862 if ( floodlighty && !bouncing ) {
2863 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2864 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2868 LightingAtSample( &trace, ds->vertexStyles, colors );
2871 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2874 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2876 /* jal: floodlight */
2877 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2880 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2881 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2882 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2886 /* is this sample bright enough? */
2887 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2888 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2889 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2890 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2891 /* nudge the sample point around a bit */
2892 for ( x = 0; x < 5; x++ )
2894 /* two's complement 0, 1, -1, 2, -2, etc */
2895 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2897 for ( y = 0; y < 5; y++ )
2899 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2901 for ( z = 0; z < 5; z++ )
2903 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2906 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2907 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2908 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2910 /* try at nudged origin */
2911 trace.cluster = ClusterForPointExtFilter( trace.origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2912 if ( trace.cluster < 0 ) {
2917 if ( dirty && !bouncing ) {
2918 dirt = DirtForSample( &trace );
2924 /* jal: floodlight */
2925 floodLightAmount = 0.0f;
2926 VectorClear( floodColor );
2927 if ( floodlighty && !bouncing ) {
2928 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2929 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2933 LightingAtSample( &trace, ds->vertexStyles, colors );
2936 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2939 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2941 /* jal: floodlight */
2942 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2945 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2946 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2949 /* bright enough? */
2950 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2951 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2952 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2953 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2961 /* add to average? */
2962 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2963 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2964 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2965 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2967 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2969 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2970 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2975 /* another happy customer */
2976 numVertsIlluminated++;
2979 /* set average color */
2981 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2982 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2986 VectorCopy( ambientColor, avgColors[ 0 ] );
2989 /* clean up and store vertex color */
2990 for ( i = 0; i < ds->numVerts; i++ )
2992 /* get vertex luxel */
2993 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2995 /* store average in occluded vertexes */
2996 if ( radVertLuxel[ 0 ] < 0.0f ) {
2997 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2999 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3000 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
3003 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3008 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3011 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3012 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3015 if ( bouncing || bounce == 0 || !bounceOnly ) {
3016 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3018 if ( !info->si->noVertexLight ) {
3019 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3024 /* free light list */
3025 FreeTraceLights( &trace );
3027 /* return to sender */
3031 /* -----------------------------------------------------------------
3032 reconstitute vertex lighting from the luxels
3033 ----------------------------------------------------------------- */
3035 /* set styles from lightmap */
3036 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3037 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3039 /* get max search radius */
3041 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3043 /* walk the surface verts */
3044 verts = yDrawVerts + ds->firstVert;
3045 for ( i = 0; i < ds->numVerts; i++ )
3047 /* do each lightmap */
3048 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3051 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3055 /* get luxel coords */
3056 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3057 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3061 else if ( x >= lm->sw ) {
3067 else if ( y >= lm->sh ) {
3071 /* get vertex luxels */
3072 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3073 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3075 /* color the luxel with the normal? */
3077 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3078 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3079 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3082 /* color the luxel with surface num? */
3083 else if ( debugSurfaces ) {
3084 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3087 else if ( info->si->noVertexLight ) {
3088 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
3091 else if ( noVertexLighting > 0 ) {
3092 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
3095 /* divine color from the superluxels */
3098 /* increasing radius */
3099 VectorClear( radVertLuxel );
3101 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3103 /* sample within radius */
3104 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3106 if ( sy < 0 || sy >= lm->sh ) {
3110 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3112 if ( sx < 0 || sx >= lm->sw ) {
3116 /* get luxel particulars */
3117 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3118 cluster = SUPER_CLUSTER( sx, sy );
3119 if ( *cluster < 0 ) {
3123 /* testing: must be brigher than ambient color */
3124 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3127 /* add its distinctiveness to our own */
3128 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3129 samples += luxel[ 3 ];
3135 if ( samples > 0.0f ) {
3136 VectorDivide( radVertLuxel, samples, radVertLuxel );
3139 VectorCopy( ambientColor, radVertLuxel );
3143 /* store into floating point storage */
3144 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3145 numVertsIlluminated++;
3147 /* store into bytes (for vertex approximation) */
3148 if ( !info->si->noVertexLight ) {
3149 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3157 /* -------------------------------------------------------------------------------
3159 light optimization (-fast)
3161 creates a list of lights that will affect a surface and stores it in tw
3162 this is to optimize surface lighting by culling out as many of the
3163 lights in the world as possible from further calculation
3165 ------------------------------------------------------------------------------- */
3169 determines opaque brushes in the world and find sky shaders for sunlight calculations
3172 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3174 unsigned int compileFlags, allCompileFlags;
3176 bspBrushSide_t *side;
3177 bspShader_t *shader;
3182 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3185 if ( opaqueBrushes == NULL ) {
3186 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3190 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3191 numOpaqueBrushes = 0;
3193 /* walk the list of worldspawn brushes */
3194 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3197 b = bspModels[ 0 ].firstBSPBrush + i;
3198 brush = &bspBrushes[ b ];
3200 /* check all sides */
3202 allCompileFlags = ~( 0u );
3203 for ( j = 0; j < brush->numSides; j++ )
3205 /* do bsp shader calculations */
3206 side = &bspBrushSides[ brush->firstSide + j ];
3207 shader = &bspShaders[ side->shaderNum ];
3209 /* get shader info */
3210 si = ShaderInfoForShaderNull( shader->shader );
3215 /* or together compile flags */
3216 compileFlags |= si->compileFlags;
3217 allCompileFlags &= si->compileFlags;
3220 /* determine if this brush is opaque to light */
3221 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3222 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3228 /* emit some statistics */
3229 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3231 void SetupBrushes( void ){
3232 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3239 determines if two clusters are visible to each other using the PVS
3242 qboolean ClusterVisible( int a, int b ){
3248 if ( a < 0 || b < 0 ) {
3258 if ( numBSPVisBytes <= 8 ) {
3263 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3264 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3265 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3268 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3278 borrowed from vlight.c
3281 int PointInLeafNum_r( vec3_t point, int nodenum ){
3288 while ( nodenum >= 0 )
3290 node = &bspNodes[ nodenum ];
3291 plane = &bspPlanes[ node->planeNum ];
3292 dist = DotProduct( point, plane->normal ) - plane->dist;
3294 nodenum = node->children[ 0 ];
3296 else if ( dist < -0.1 ) {
3297 nodenum = node->children[ 1 ];
3301 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3302 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3305 nodenum = node->children[ 1 ];
3309 leafnum = -nodenum - 1;
3317 borrowed from vlight.c
3320 int PointInLeafNum( vec3_t point ){
3321 return PointInLeafNum_r( point, 0 );
3327 ClusterVisibleToPoint() - ydnar
3328 returns qtrue if point can "see" cluster
3331 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3335 /* get leafNum for point */
3336 pointCluster = ClusterForPoint( point );
3337 if ( pointCluster < 0 ) {
3342 return ClusterVisible( pointCluster, cluster );
3348 ClusterForPoint() - ydnar
3349 returns the pvs cluster for point
3352 int ClusterForPoint( vec3_t point ){
3356 /* get leafNum for point */
3357 leafNum = PointInLeafNum( point );
3358 if ( leafNum < 0 ) {
3362 /* return the cluster */
3363 return bspLeafs[ leafNum ].cluster;
3369 ClusterForPointExt() - ydnar
3370 also takes brushes into account for occlusion testing
3373 int ClusterForPointExt( vec3_t point, float epsilon ){
3374 int i, j, b, leafNum, cluster;
3377 int *brushes, numBSPBrushes;
3383 /* get leaf for point */
3384 leafNum = PointInLeafNum( point );
3385 if ( leafNum < 0 ) {
3388 leaf = &bspLeafs[ leafNum ];
3390 /* get the cluster */
3391 cluster = leaf->cluster;
3392 if ( cluster < 0 ) {
3396 /* transparent leaf, so check point against all brushes in the leaf */
3397 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3398 numBSPBrushes = leaf->numBSPLeafBrushes;
3399 for ( i = 0; i < numBSPBrushes; i++ )
3403 if ( b > maxOpaqueBrush ) {
3406 brush = &bspBrushes[ b ];
3407 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3411 /* check point against all planes */
3413 for ( j = 0; j < brush->numSides && inside; j++ )
3415 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3416 dot = DotProduct( point, plane->normal );
3418 if ( dot > epsilon ) {
3423 /* if inside, return bogus cluster */
3429 /* if the point made it this far, it's not inside any opaque brushes */
3436 ClusterForPointExtFilter() - ydnar
3437 adds cluster checking against a list of known valid clusters
3440 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3444 /* get cluster for point */
3445 cluster = ClusterForPointExt( point, epsilon );
3447 /* check if filtering is necessary */
3448 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3453 for ( i = 0; i < numClusters; i++ )
3455 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3467 ShaderForPointInLeaf() - ydnar
3468 checks a point against all brushes in a leaf, returning the shader of the brush
3469 also sets the cumulative surface and content flags for the brush hit
3472 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3476 int *brushes, numBSPBrushes;
3479 bspBrushSide_t *side;
3481 bspShader_t *shader;
3482 int allSurfaceFlags, allContentFlags;
3485 /* clear things out first */
3490 if ( leafNum < 0 ) {
3493 leaf = &bspLeafs[ leafNum ];
3495 /* transparent leaf, so check point against all brushes in the leaf */
3496 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3497 numBSPBrushes = leaf->numBSPLeafBrushes;
3498 for ( i = 0; i < numBSPBrushes; i++ )
3501 brush = &bspBrushes[ brushes[ i ] ];
3503 /* check point against all planes */
3505 allSurfaceFlags = 0;
3506 allContentFlags = 0;
3507 for ( j = 0; j < brush->numSides && inside; j++ )
3509 side = &bspBrushSides[ brush->firstSide + j ];
3510 plane = &bspPlanes[ side->planeNum ];
3511 dot = DotProduct( point, plane->normal );
3513 if ( dot > epsilon ) {
3518 shader = &bspShaders[ side->shaderNum ];
3519 allSurfaceFlags |= shader->surfaceFlags;
3520 allContentFlags |= shader->contentFlags;
3524 /* handle if inside */
3526 /* if there are desired flags, check for same and continue if they aren't matched */
3527 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3530 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3534 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3535 *surfaceFlags = allSurfaceFlags;
3536 *contentFlags = allContentFlags;
3537 return brush->shaderNum;
3541 /* if the point made it this far, it's not inside any brushes */
3549 chops a bounding box by the plane defined by origin and normal
3550 returns qfalse if the bounds is entirely clipped away
3552 this is not exactly the fastest way to do this...
3555 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3556 /* FIXME: rewrite this so it doesn't use bloody brushes */
3564 calculates each light's effective envelope,
3565 taking into account brightness, type, and pvs.
3568 #define LIGHT_EPSILON 0.125f
3569 #define LIGHT_NUDGE 2.0f
3571 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3572 int i, x, y, z, x1, y1, z1;
3573 light_t *light, *light2, **owner;
3575 vec3_t origin, dir, mins, maxs;
3576 float radius, intensity;
3577 light_t *buckets[ 256 ];
3580 /* early out for weird cases where there are no lights */
3581 if ( lights == NULL ) {
3586 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3590 numCulledLights = 0;
3592 while ( *owner != NULL )
3597 /* handle negative lights */
3598 if ( light->photons < 0.0f || light->add < 0.0f ) {
3599 light->photons *= -1.0f;
3600 light->add *= -1.0f;
3601 light->flags |= LIGHT_NEGATIVE;
3605 if ( light->type == EMIT_SUN ) {
3608 light->envelope = MAX_WORLD_COORD * 8.0f;
3609 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3610 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3613 /* everything else */
3616 /* get pvs cluster for light */
3617 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3619 /* invalid cluster? */
3620 if ( light->cluster < 0 ) {
3621 /* nudge the sample point around a bit */
3622 for ( x = 0; x < 4; x++ )
3624 /* two's complement 0, 1, -1, 2, -2, etc */
3625 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3627 for ( y = 0; y < 4; y++ )
3629 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3631 for ( z = 0; z < 4; z++ )
3633 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3636 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3637 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3638 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3640 /* try at nudged origin */
3641 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3642 if ( light->cluster < 0 ) {
3647 VectorCopy( origin, light->origin );
3653 /* only calculate for lights in pvs and outside of opaque brushes */
3654 if ( light->cluster >= 0 ) {
3655 /* set light fast flag */
3657 light->flags |= LIGHT_FAST_TEMP;
3660 light->flags &= ~LIGHT_FAST_TEMP;
3662 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3663 light->flags |= LIGHT_FAST_TEMP;
3665 if ( light->si && light->si->noFast ) {
3666 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3669 /* clear light envelope */
3670 light->envelope = 0;
3672 /* handle area lights */
3673 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3674 light->envelope = MAX_WORLD_COORD * 8.0f;
3676 /* check for fast mode */
3677 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3678 /* ugly hack to calculate extent for area lights, but only done once */
3679 VectorScale( light->normal, -1.0f, dir );
3680 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3684 VectorMA( light->origin, radius, light->normal, origin );
3685 factor = PointToPolygonFormFactor( origin, dir, light->w );
3686 if ( factor < 0.0f ) {
3689 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3690 light->envelope = radius;
3696 intensity = light->photons; /* hopefully not used */
3701 intensity = light->photons;
3705 if ( light->envelope <= 0.0f ) {
3706 /* solve distance for non-distance lights */
3707 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3708 light->envelope = MAX_WORLD_COORD * 8.0f;
3711 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3712 /* solve distance for linear lights */
3713 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3714 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3718 add = angle * light->photons * linearScale - (dist * light->fade);
3719 T = (light->photons * linearScale) - (dist * light->fade);
3720 T + (dist * light->fade) = (light->photons * linearScale);
3721 dist * light->fade = (light->photons * linearScale) - T;
3722 dist = ((light->photons * linearScale) - T) / light->fade;
3725 /* solve for inverse square falloff */
3727 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3731 add = light->photons / (dist * dist);
3732 T = light->photons / (dist * dist);
3733 T * (dist * dist) = light->photons;
3734 dist = sqrt( light->photons / T );
3739 /* solve distance for linear lights */
3740 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3741 light->envelope = ( intensity * linearScale ) / light->fade;
3744 /* can't cull these */
3746 light->envelope = MAX_WORLD_COORD * 8.0f;
3751 /* chop radius against pvs */
3754 ClearBounds( mins, maxs );
3756 /* check all leaves */
3757 for ( i = 0; i < numBSPLeafs; i++ )
3760 leaf = &bspLeafs[ i ];
3763 if ( leaf->cluster < 0 ) {
3766 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3770 /* add this leafs bbox to the bounds */
3771 VectorCopy( leaf->mins, origin );
3772 AddPointToBounds( origin, mins, maxs );
3773 VectorCopy( leaf->maxs, origin );
3774 AddPointToBounds( origin, mins, maxs );
3777 /* test to see if bounds encompass light */
3778 for ( i = 0; i < 3; i++ )
3780 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3781 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3782 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3783 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3784 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3785 AddPointToBounds( light->origin, mins, maxs );
3789 /* chop the bounds by a plane for area lights and spotlights */
3790 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3791 ChopBounds( mins, maxs, light->origin, light->normal );
3795 VectorCopy( mins, light->mins );
3796 VectorCopy( maxs, light->maxs );
3798 /* reflect bounds around light origin */
3799 //% VectorMA( light->origin, -1.0f, origin, origin );
3800 VectorScale( light->origin, 2, origin );
3801 VectorSubtract( origin, maxs, origin );
3802 AddPointToBounds( origin, mins, maxs );
3803 //% VectorMA( light->origin, -1.0f, mins, origin );
3804 VectorScale( light->origin, 2, origin );
3805 VectorSubtract( origin, mins, origin );
3806 AddPointToBounds( origin, mins, maxs );
3808 /* calculate spherical bounds */
3809 VectorSubtract( maxs, light->origin, dir );
3810 radius = (float) VectorLength( dir );
3812 /* if this radius is smaller than the envelope, then set the envelope to it */
3813 if ( radius < light->envelope ) {
3814 light->envelope = radius;
3815 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3818 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3821 /* add grid/surface only check */
3823 if ( !( light->flags & LIGHT_GRID ) ) {
3824 light->envelope = 0.0f;
3829 if ( !( light->flags & LIGHT_SURFACES ) ) {
3830 light->envelope = 0.0f;
3836 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3838 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3840 /* delete the light */
3842 *owner = light->next;
3843 if ( light->w != NULL ) {
3851 /* square envelope */
3852 light->envelope2 = ( light->envelope * light->envelope );
3854 /* increment light count */
3857 /* set next light */
3858 owner = &( ( **owner ).next );
3861 /* bucket sort lights by style */
3862 memset( buckets, 0, sizeof( buckets ) );
3864 for ( light = lights; light != NULL; light = light2 )
3866 /* get next light */
3867 light2 = light->next;
3869 /* filter into correct bucket */
3870 light->next = buckets[ light->style ];
3871 buckets[ light->style ] = light;
3873 /* if any styled light is present, automatically set nocollapse */
3874 if ( light->style != LS_NORMAL ) {
3879 /* filter back into light list */
3881 for ( i = 255; i >= 0; i-- )
3884 for ( light = buckets[ i ]; light != NULL; light = light2 )
3886 light2 = light->next;
3887 light->next = lights;
3892 /* emit some statistics */
3893 Sys_Printf( "%9d total lights\n", numLights );
3894 Sys_Printf( "%9d culled lights\n", numCulledLights );
3900 CreateTraceLightsForBounds()
3901 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3904 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3907 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3908 float radius, dist, length;
3911 /* potential pre-setup */
3912 if ( numLights == 0 ) {
3913 SetupEnvelopes( qfalse, fast );
3917 //% 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 ] );
3919 /* allocate the light list */
3920 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3921 trace->numLights = 0;
3923 /* calculate spherical bounds */
3924 VectorAdd( mins, maxs, origin );
3925 VectorScale( origin, 0.5f, origin );
3926 VectorSubtract( maxs, origin, dir );
3927 radius = (float) VectorLength( dir );
3929 /* get length of normal vector */
3930 if ( normal != NULL ) {
3931 length = VectorLength( normal );
3935 normal = nullVector;
3939 /* test each light and see if it reaches the sphere */
3940 /* note: the attenuation code MUST match LightingAtSample() */
3941 for ( light = lights; light; light = light->next )
3943 /* check zero sized envelope */
3944 if ( light->envelope <= 0 ) {
3945 lightsEnvelopeCulled++;
3950 if ( !( light->flags & flags ) ) {
3954 /* sunlight skips all this nonsense */
3955 if ( light->type != EMIT_SUN ) {
3961 /* check against pvs cluster */
3962 if ( numClusters > 0 && clusters != NULL ) {
3963 for ( i = 0; i < numClusters; i++ )
3965 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3971 if ( i == numClusters ) {
3972 lightsClusterCulled++;
3977 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3978 VectorSubtract( light->origin, origin, dir );
3979 dist = VectorLength( dir );
3980 dist -= light->envelope;
3983 lightsEnvelopeCulled++;
3987 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3990 for ( i = 0; i < 3; i++ )
3992 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3997 lightsBoundsCulled++;
4003 /* planar surfaces (except twosided surfaces) have a couple more checks */
4004 if ( length > 0.0f && trace->twoSided == qfalse ) {
4005 /* lights coplanar with a surface won't light it */
4006 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
4007 lightsPlaneCulled++;
4011 /* check to see if light is behind the plane */
4012 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4013 lightsPlaneCulled++;
4018 /* add this light */
4019 trace->lights[ trace->numLights++ ] = light;
4022 /* make last night null */
4023 trace->lights[ trace->numLights ] = NULL;
4028 void FreeTraceLights( trace_t *trace ){
4029 if ( trace->lights != NULL ) {
4030 free( trace->lights );
4037 CreateTraceLightsForSurface()
4038 creates a list of lights that can potentially affect a drawsurface
4041 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4043 vec3_t mins, maxs, normal;
4045 bspDrawSurface_t *ds;
4046 surfaceInfo_t *info;
4054 /* get drawsurface and info */
4055 ds = &bspDrawSurfaces[ num ];
4056 info = &surfaceInfos[ num ];
4058 /* get the mins/maxs for the dsurf */
4059 ClearBounds( mins, maxs );
4060 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4061 for ( i = 0; i < ds->numVerts; i++ )
4063 dv = &yDrawVerts[ ds->firstVert + i ];
4064 AddPointToBounds( dv->xyz, mins, maxs );
4065 if ( !VectorCompare( dv->normal, normal ) ) {
4066 VectorClear( normal );
4070 /* create the lights for the bounding box */
4071 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4074 /////////////////////////////////////////////////////////////
4076 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4077 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4078 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4079 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4081 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4082 static int numFloodVectors = 0;
4084 void SetupFloodLight( void ){
4086 float angle, elevation, angleStep, elevationStep;
4088 double v1,v2,v3,v4,v5,v6;
4091 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4093 /* calculate angular steps */
4094 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4095 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4099 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4101 /* iterate elevation */
4102 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4104 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4105 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4106 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4111 /* emit some statistics */
4112 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4115 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4117 if ( value[ 0 ] != '\0' ) {
4119 v4 = floodlightDistance;
4120 v5 = floodlightIntensity;
4121 v6 = floodlightDirectionScale;
4123 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4125 floodlightRGB[0] = v1;
4126 floodlightRGB[1] = v2;
4127 floodlightRGB[2] = v3;
4129 if ( VectorLength( floodlightRGB ) == 0 ) {
4130 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4143 floodlightDistance = v4;
4144 floodlightIntensity = v5;
4145 floodlightDirectionScale = v6;
4147 floodlighty = qtrue;
4148 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4152 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4155 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4156 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4157 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4159 ColorNormalize( floodlightRGB,floodlightRGB );
4163 FloodLightForSample()
4164 calculates floodlight value for a given sample
4165 once again, kudos to the dirtmapping coder
4168 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4173 float gatherLight, outLight;
4174 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4182 if ( trace == NULL || trace->cluster < 0 ) {
4188 dd = floodLightDistance;
4189 VectorCopy( trace->normal, normal );
4191 /* check if the normal is aligned to the world-up */
4192 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4193 if ( normal[ 2 ] == 1.0f ) {
4194 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4195 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4197 else if ( normal[ 2 ] == -1.0f ) {
4198 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4199 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4204 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4205 CrossProduct( normal, worldUp, myRt );
4206 VectorNormalize( myRt, myRt );
4207 CrossProduct( myRt, normal, myUp );
4208 VectorNormalize( myUp, myUp );
4211 /* vortex: optimise floodLightLowQuality a bit */
4212 if ( floodLightLowQuality == qtrue ) {
4213 /* iterate through ordered vectors */
4214 for ( i = 0; i < numFloodVectors; i++ )
4215 if ( rand() % 10 != 0 ) {
4221 /* iterate through ordered vectors */
4222 for ( i = 0; i < numFloodVectors; i++ )
4226 /* transform vector into tangent space */
4227 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4228 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4229 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4232 VectorMA( trace->origin, dd, direction, trace->end );
4234 //VectorMA( trace->origin, 1, direction, trace->origin );
4236 SetupTrace( trace );
4237 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4242 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4243 contribution = 1.0f;
4245 else if ( trace->opaque ) {
4246 VectorSubtract( trace->hit, trace->origin, displacement );
4247 d = VectorLength( displacement );
4249 // d=trace->distance;
4250 //if (d>256) gatherDirt+=1;
4251 contribution = d / dd;
4252 if ( contribution > 1 ) {
4253 contribution = 1.0f;
4256 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4259 gatherLight += contribution;
4264 if ( gatherLight <= 0.0f ) {
4273 gatherLight /= ( sub );
4275 outLight = gatherLight;
4276 if ( outLight > 1.0f ) {
4280 /* return to sender */
4285 FloodLightRawLightmap
4286 lighttracer style ambient occlusion light hack.
4287 Kudos to the dirtmapping author for most of this source.
4288 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4289 VorteX: fixed problems with deluxemapping
4292 // floodlight pass on a lightmap
4293 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4294 int i, x, y, *cluster;
4295 float *origin, *normal, *floodlight, floodLightAmount;
4296 surfaceInfo_t *info;
4299 // float samples, average, *floodlight2;
4301 memset( &trace,0,sizeof( trace_t ) );
4304 trace.testOcclusion = qtrue;
4305 trace.forceSunlight = qfalse;
4306 trace.twoSided = qtrue;
4307 trace.recvShadows = lm->recvShadows;
4308 trace.numSurfaces = lm->numLightSurfaces;
4309 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4310 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4311 trace.testAll = qfalse;
4312 trace.distance = 1024;
4314 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4315 //trace.twoSided = qfalse;
4316 for ( i = 0; i < trace.numSurfaces; i++ )
4319 info = &surfaceInfos[ trace.surfaces[ i ] ];
4321 /* check twosidedness */
4322 if ( info->si->twoSided ) {
4323 trace.twoSided = qtrue;
4328 /* gather floodlight */
4329 for ( y = 0; y < lm->sh; y++ )
4331 for ( x = 0; x < lm->sw; x++ )
4334 cluster = SUPER_CLUSTER( x, y );
4335 origin = SUPER_ORIGIN( x, y );
4336 normal = SUPER_NORMAL( x, y );
4337 floodlight = SUPER_FLOODLIGHT( x, y );
4339 /* set default dirt */
4342 /* only look at mapped luxels */
4343 if ( *cluster < 0 ) {
4348 trace.cluster = *cluster;
4349 VectorCopy( origin, trace.origin );
4350 VectorCopy( normal, trace.normal );
4352 /* get floodlight */
4353 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4355 /* add floodlight */
4356 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4357 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4358 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4359 floodlight[3] += floodlightDirectionScale;
4363 /* testing no filtering */
4369 for ( y = 0; y < lm->sh; y++ )
4371 for ( x = 0; x < lm->sw; x++ )
4374 cluster = SUPER_CLUSTER( x, y );
4375 floodlight = SUPER_FLOODLIGHT( x, y );
4377 /* filter dirt by adjacency to unmapped luxels */
4378 average = *floodlight;
4380 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4382 if ( sy < 0 || sy >= lm->sh ) {
4386 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4388 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4392 /* get neighboring luxel */
4393 cluster = SUPER_CLUSTER( sx, sy );
4394 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4395 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4400 average += *floodlight2;
4405 if ( samples <= 0.0f ) {
4411 if ( samples <= 0.0f ) {
4416 *floodlight = average / samples;
4422 void FloodLightRawLightmap( int rawLightmapNum ){
4425 /* bail if this number exceeds the number of raw lightmaps */
4426 if ( rawLightmapNum >= numRawLightmaps ) {
4430 lm = &rawLightmaps[ rawLightmapNum ];
4433 if ( floodlighty && floodlightIntensity ) {
4434 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4438 if ( lm->floodlightIntensity ) {
4439 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4440 numSurfacesFloodlighten += 1;
4444 void FloodlightRawLightmaps(){
4445 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4446 numSurfacesFloodlighten = 0;
4447 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4448 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4452 FloodLightIlluminate()
4453 illuminate floodlight into lightmap luxels
4456 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4457 float *luxel, *floodlight, *deluxel, *normal;
4460 int x, y, lightmapNum;
4462 /* walk lightmaps */
4463 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4466 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4470 if( lm->styles[lightmapNum] != LS_NORMAL && lm->styles[lightmapNum] != LS_NONE ) // isStyleLight
4473 /* apply floodlight to each luxel */
4474 for ( y = 0; y < lm->sh; y++ )
4476 for ( x = 0; x < lm->sw; x++ )
4478 /* get floodlight */
4479 floodlight = SUPER_FLOODLIGHT( x, y );
4480 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4485 cluster = SUPER_CLUSTER( x, y );
4487 /* only process mapped luxels */
4488 if ( *cluster < 0 ) {
4492 /* get particulars */
4493 luxel = SUPER_LUXEL( lightmapNum, x, y );
4494 deluxel = SUPER_DELUXEL( x, y );
4496 /* add to lightmap */
4497 luxel[0] += floodlight[0];
4498 luxel[1] += floodlight[1];
4499 luxel[2] += floodlight[2];
4501 if ( luxel[3] == 0 ) {
4505 /* add to deluxemap */
4506 if ( deluxemap && floodlight[3] > 0 ) {
4509 normal = SUPER_NORMAL( x, y );
4510 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4512 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4513 if ( brightness < 0.00390625f ) {
4514 brightness = 0.00390625f;
4517 VectorScale( normal, brightness, lightvector );
4518 VectorAdd( deluxel, lightvector, deluxel );