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[ 3 ];
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;
3177 bspBrushSide_t *side;
3178 bspShader_t *shader;
3183 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3186 if ( opaqueBrushes == NULL ) {
3187 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3191 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3192 numOpaqueBrushes = 0;
3194 /* walk the list of worldspawn brushes */
3195 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3198 b = bspModels[ 0 ].firstBSPBrush + i;
3199 brush = &bspBrushes[ b ];
3201 /* check all sides */
3204 allCompileFlags = ~( 0u );
3205 for ( j = 0; j < brush->numSides && inside; j++ )
3207 /* do bsp shader calculations */
3208 side = &bspBrushSides[ brush->firstSide + j ];
3209 shader = &bspShaders[ side->shaderNum ];
3211 /* get shader info */
3212 si = ShaderInfoForShaderNull( shader->shader );
3217 /* or together compile flags */
3218 compileFlags |= si->compileFlags;
3219 allCompileFlags &= si->compileFlags;
3222 /* determine if this brush is opaque to light */
3223 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3224 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3230 /* emit some statistics */
3231 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3233 void SetupBrushes( void ){
3234 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3241 determines if two clusters are visible to each other using the PVS
3244 qboolean ClusterVisible( int a, int b ){
3250 if ( a < 0 || b < 0 ) {
3260 if ( numBSPVisBytes <= 8 ) {
3265 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3266 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3267 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3270 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3280 borrowed from vlight.c
3283 int PointInLeafNum_r( vec3_t point, int nodenum ){
3290 while ( nodenum >= 0 )
3292 node = &bspNodes[ nodenum ];
3293 plane = &bspPlanes[ node->planeNum ];
3294 dist = DotProduct( point, plane->normal ) - plane->dist;
3296 nodenum = node->children[ 0 ];
3298 else if ( dist < -0.1 ) {
3299 nodenum = node->children[ 1 ];
3303 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3304 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3307 nodenum = node->children[ 1 ];
3311 leafnum = -nodenum - 1;
3319 borrowed from vlight.c
3322 int PointInLeafNum( vec3_t point ){
3323 return PointInLeafNum_r( point, 0 );
3329 ClusterVisibleToPoint() - ydnar
3330 returns qtrue if point can "see" cluster
3333 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3337 /* get leafNum for point */
3338 pointCluster = ClusterForPoint( point );
3339 if ( pointCluster < 0 ) {
3344 return ClusterVisible( pointCluster, cluster );
3350 ClusterForPoint() - ydnar
3351 returns the pvs cluster for point
3354 int ClusterForPoint( vec3_t point ){
3358 /* get leafNum for point */
3359 leafNum = PointInLeafNum( point );
3360 if ( leafNum < 0 ) {
3364 /* return the cluster */
3365 return bspLeafs[ leafNum ].cluster;
3371 ClusterForPointExt() - ydnar
3372 also takes brushes into account for occlusion testing
3375 int ClusterForPointExt( vec3_t point, float epsilon ){
3376 int i, j, b, leafNum, cluster;
3379 int *brushes, numBSPBrushes;
3385 /* get leaf for point */
3386 leafNum = PointInLeafNum( point );
3387 if ( leafNum < 0 ) {
3390 leaf = &bspLeafs[ leafNum ];
3392 /* get the cluster */
3393 cluster = leaf->cluster;
3394 if ( cluster < 0 ) {
3398 /* transparent leaf, so check point against all brushes in the leaf */
3399 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3400 numBSPBrushes = leaf->numBSPLeafBrushes;
3401 for ( i = 0; i < numBSPBrushes; i++ )
3405 if ( b > maxOpaqueBrush ) {
3408 brush = &bspBrushes[ b ];
3409 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3413 /* check point against all planes */
3415 for ( j = 0; j < brush->numSides && inside; j++ )
3417 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3418 dot = DotProduct( point, plane->normal );
3420 if ( dot > epsilon ) {
3425 /* if inside, return bogus cluster */
3431 /* if the point made it this far, it's not inside any opaque brushes */
3438 ClusterForPointExtFilter() - ydnar
3439 adds cluster checking against a list of known valid clusters
3442 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3446 /* get cluster for point */
3447 cluster = ClusterForPointExt( point, epsilon );
3449 /* check if filtering is necessary */
3450 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3455 for ( i = 0; i < numClusters; i++ )
3457 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3469 ShaderForPointInLeaf() - ydnar
3470 checks a point against all brushes in a leaf, returning the shader of the brush
3471 also sets the cumulative surface and content flags for the brush hit
3474 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3478 int *brushes, numBSPBrushes;
3481 bspBrushSide_t *side;
3483 bspShader_t *shader;
3484 int allSurfaceFlags, allContentFlags;
3487 /* clear things out first */
3492 if ( leafNum < 0 ) {
3495 leaf = &bspLeafs[ leafNum ];
3497 /* transparent leaf, so check point against all brushes in the leaf */
3498 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3499 numBSPBrushes = leaf->numBSPLeafBrushes;
3500 for ( i = 0; i < numBSPBrushes; i++ )
3503 brush = &bspBrushes[ brushes[ i ] ];
3505 /* check point against all planes */
3507 allSurfaceFlags = 0;
3508 allContentFlags = 0;
3509 for ( j = 0; j < brush->numSides && inside; j++ )
3511 side = &bspBrushSides[ brush->firstSide + j ];
3512 plane = &bspPlanes[ side->planeNum ];
3513 dot = DotProduct( point, plane->normal );
3515 if ( dot > epsilon ) {
3520 shader = &bspShaders[ side->shaderNum ];
3521 allSurfaceFlags |= shader->surfaceFlags;
3522 allContentFlags |= shader->contentFlags;
3526 /* handle if inside */
3528 /* if there are desired flags, check for same and continue if they aren't matched */
3529 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3532 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3536 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3537 *surfaceFlags = allSurfaceFlags;
3538 *contentFlags = allContentFlags;
3539 return brush->shaderNum;
3543 /* if the point made it this far, it's not inside any brushes */
3551 chops a bounding box by the plane defined by origin and normal
3552 returns qfalse if the bounds is entirely clipped away
3554 this is not exactly the fastest way to do this...
3557 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3558 /* FIXME: rewrite this so it doesn't use bloody brushes */
3566 calculates each light's effective envelope,
3567 taking into account brightness, type, and pvs.
3570 #define LIGHT_EPSILON 0.125f
3571 #define LIGHT_NUDGE 2.0f
3573 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3574 int i, x, y, z, x1, y1, z1;
3575 light_t *light, *light2, **owner;
3577 vec3_t origin, dir, mins, maxs;
3578 float radius, intensity;
3579 light_t *buckets[ 256 ];
3582 /* early out for weird cases where there are no lights */
3583 if ( lights == NULL ) {
3588 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3592 numCulledLights = 0;
3594 while ( *owner != NULL )
3599 /* handle negative lights */
3600 if ( light->photons < 0.0f || light->add < 0.0f ) {
3601 light->photons *= -1.0f;
3602 light->add *= -1.0f;
3603 light->flags |= LIGHT_NEGATIVE;
3607 if ( light->type == EMIT_SUN ) {
3610 light->envelope = MAX_WORLD_COORD * 8.0f;
3611 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3612 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3615 /* everything else */
3618 /* get pvs cluster for light */
3619 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3621 /* invalid cluster? */
3622 if ( light->cluster < 0 ) {
3623 /* nudge the sample point around a bit */
3624 for ( x = 0; x < 4; x++ )
3626 /* two's complement 0, 1, -1, 2, -2, etc */
3627 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3629 for ( y = 0; y < 4; y++ )
3631 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3633 for ( z = 0; z < 4; z++ )
3635 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3638 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3639 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3640 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3642 /* try at nudged origin */
3643 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3644 if ( light->cluster < 0 ) {
3649 VectorCopy( origin, light->origin );
3655 /* only calculate for lights in pvs and outside of opaque brushes */
3656 if ( light->cluster >= 0 ) {
3657 /* set light fast flag */
3659 light->flags |= LIGHT_FAST_TEMP;
3662 light->flags &= ~LIGHT_FAST_TEMP;
3664 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3665 light->flags |= LIGHT_FAST_TEMP;
3667 if ( light->si && light->si->noFast ) {
3668 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3671 /* clear light envelope */
3672 light->envelope = 0;
3674 /* handle area lights */
3675 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3676 light->envelope = MAX_WORLD_COORD * 8.0f;
3678 /* check for fast mode */
3679 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3680 /* ugly hack to calculate extent for area lights, but only done once */
3681 VectorScale( light->normal, -1.0f, dir );
3682 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3686 VectorMA( light->origin, radius, light->normal, origin );
3687 factor = PointToPolygonFormFactor( origin, dir, light->w );
3688 if ( factor < 0.0f ) {
3691 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3692 light->envelope = radius;
3698 intensity = light->photons; /* hopefully not used */
3703 intensity = light->photons;
3707 if ( light->envelope <= 0.0f ) {
3708 /* solve distance for non-distance lights */
3709 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3710 light->envelope = MAX_WORLD_COORD * 8.0f;
3713 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3714 /* solve distance for linear lights */
3715 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3716 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3720 add = angle * light->photons * linearScale - (dist * light->fade);
3721 T = (light->photons * linearScale) - (dist * light->fade);
3722 T + (dist * light->fade) = (light->photons * linearScale);
3723 dist * light->fade = (light->photons * linearScale) - T;
3724 dist = ((light->photons * linearScale) - T) / light->fade;
3727 /* solve for inverse square falloff */
3729 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3733 add = light->photons / (dist * dist);
3734 T = light->photons / (dist * dist);
3735 T * (dist * dist) = light->photons;
3736 dist = sqrt( light->photons / T );
3741 /* solve distance for linear lights */
3742 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3743 light->envelope = ( intensity * linearScale ) / light->fade;
3746 /* can't cull these */
3748 light->envelope = MAX_WORLD_COORD * 8.0f;
3753 /* chop radius against pvs */
3756 ClearBounds( mins, maxs );
3758 /* check all leaves */
3759 for ( i = 0; i < numBSPLeafs; i++ )
3762 leaf = &bspLeafs[ i ];
3765 if ( leaf->cluster < 0 ) {
3768 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3772 /* add this leafs bbox to the bounds */
3773 VectorCopy( leaf->mins, origin );
3774 AddPointToBounds( origin, mins, maxs );
3775 VectorCopy( leaf->maxs, origin );
3776 AddPointToBounds( origin, mins, maxs );
3779 /* test to see if bounds encompass light */
3780 for ( i = 0; i < 3; i++ )
3782 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3783 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3784 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3785 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3786 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3787 AddPointToBounds( light->origin, mins, maxs );
3791 /* chop the bounds by a plane for area lights and spotlights */
3792 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3793 ChopBounds( mins, maxs, light->origin, light->normal );
3797 VectorCopy( mins, light->mins );
3798 VectorCopy( maxs, light->maxs );
3800 /* reflect bounds around light origin */
3801 //% VectorMA( light->origin, -1.0f, origin, origin );
3802 VectorScale( light->origin, 2, origin );
3803 VectorSubtract( origin, maxs, origin );
3804 AddPointToBounds( origin, mins, maxs );
3805 //% VectorMA( light->origin, -1.0f, mins, origin );
3806 VectorScale( light->origin, 2, origin );
3807 VectorSubtract( origin, mins, origin );
3808 AddPointToBounds( origin, mins, maxs );
3810 /* calculate spherical bounds */
3811 VectorSubtract( maxs, light->origin, dir );
3812 radius = (float) VectorLength( dir );
3814 /* if this radius is smaller than the envelope, then set the envelope to it */
3815 if ( radius < light->envelope ) {
3816 light->envelope = radius;
3817 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3820 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3823 /* add grid/surface only check */
3825 if ( !( light->flags & LIGHT_GRID ) ) {
3826 light->envelope = 0.0f;
3831 if ( !( light->flags & LIGHT_SURFACES ) ) {
3832 light->envelope = 0.0f;
3838 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3840 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3842 /* delete the light */
3844 *owner = light->next;
3845 if ( light->w != NULL ) {
3853 /* square envelope */
3854 light->envelope2 = ( light->envelope * light->envelope );
3856 /* increment light count */
3859 /* set next light */
3860 owner = &( ( **owner ).next );
3863 /* bucket sort lights by style */
3864 memset( buckets, 0, sizeof( buckets ) );
3866 for ( light = lights; light != NULL; light = light2 )
3868 /* get next light */
3869 light2 = light->next;
3871 /* filter into correct bucket */
3872 light->next = buckets[ light->style ];
3873 buckets[ light->style ] = light;
3875 /* if any styled light is present, automatically set nocollapse */
3876 if ( light->style != LS_NORMAL ) {
3881 /* filter back into light list */
3883 for ( i = 255; i >= 0; i-- )
3886 for ( light = buckets[ i ]; light != NULL; light = light2 )
3888 light2 = light->next;
3889 light->next = lights;
3894 /* emit some statistics */
3895 Sys_Printf( "%9d total lights\n", numLights );
3896 Sys_Printf( "%9d culled lights\n", numCulledLights );
3902 CreateTraceLightsForBounds()
3903 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3906 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3909 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3910 float radius, dist, length;
3913 /* potential pre-setup */
3914 if ( numLights == 0 ) {
3915 SetupEnvelopes( qfalse, fast );
3919 //% 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 ] );
3921 /* allocate the light list */
3922 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3923 trace->numLights = 0;
3925 /* calculate spherical bounds */
3926 VectorAdd( mins, maxs, origin );
3927 VectorScale( origin, 0.5f, origin );
3928 VectorSubtract( maxs, origin, dir );
3929 radius = (float) VectorLength( dir );
3931 /* get length of normal vector */
3932 if ( normal != NULL ) {
3933 length = VectorLength( normal );
3937 normal = nullVector;
3941 /* test each light and see if it reaches the sphere */
3942 /* note: the attenuation code MUST match LightingAtSample() */
3943 for ( light = lights; light; light = light->next )
3945 /* check zero sized envelope */
3946 if ( light->envelope <= 0 ) {
3947 lightsEnvelopeCulled++;
3952 if ( !( light->flags & flags ) ) {
3956 /* sunlight skips all this nonsense */
3957 if ( light->type != EMIT_SUN ) {
3963 /* check against pvs cluster */
3964 if ( numClusters > 0 && clusters != NULL ) {
3965 for ( i = 0; i < numClusters; i++ )
3967 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3973 if ( i == numClusters ) {
3974 lightsClusterCulled++;
3979 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3980 VectorSubtract( light->origin, origin, dir );
3981 dist = VectorLength( dir );
3982 dist -= light->envelope;
3985 lightsEnvelopeCulled++;
3989 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3992 for ( i = 0; i < 3; i++ )
3994 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3999 lightsBoundsCulled++;
4005 /* planar surfaces (except twosided surfaces) have a couple more checks */
4006 if ( length > 0.0f && trace->twoSided == qfalse ) {
4007 /* lights coplanar with a surface won't light it */
4008 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
4009 lightsPlaneCulled++;
4013 /* check to see if light is behind the plane */
4014 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4015 lightsPlaneCulled++;
4020 /* add this light */
4021 trace->lights[ trace->numLights++ ] = light;
4024 /* make last night null */
4025 trace->lights[ trace->numLights ] = NULL;
4030 void FreeTraceLights( trace_t *trace ){
4031 if ( trace->lights != NULL ) {
4032 free( trace->lights );
4039 CreateTraceLightsForSurface()
4040 creates a list of lights that can potentially affect a drawsurface
4043 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4045 vec3_t mins, maxs, normal;
4047 bspDrawSurface_t *ds;
4048 surfaceInfo_t *info;
4056 /* get drawsurface and info */
4057 ds = &bspDrawSurfaces[ num ];
4058 info = &surfaceInfos[ num ];
4060 /* get the mins/maxs for the dsurf */
4061 ClearBounds( mins, maxs );
4062 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4063 for ( i = 0; i < ds->numVerts; i++ )
4065 dv = &yDrawVerts[ ds->firstVert + i ];
4066 AddPointToBounds( dv->xyz, mins, maxs );
4067 if ( !VectorCompare( dv->normal, normal ) ) {
4068 VectorClear( normal );
4072 /* create the lights for the bounding box */
4073 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4076 /////////////////////////////////////////////////////////////
4078 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4079 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4080 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4081 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4083 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4084 static int numFloodVectors = 0;
4086 void SetupFloodLight( void ){
4088 float angle, elevation, angleStep, elevationStep;
4090 double v1,v2,v3,v4,v5,v6;
4093 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4095 /* calculate angular steps */
4096 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4097 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4101 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4103 /* iterate elevation */
4104 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4106 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4107 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4108 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4113 /* emit some statistics */
4114 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4117 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4119 if ( value[ 0 ] != '\0' ) {
4121 v4 = floodlightDistance;
4122 v5 = floodlightIntensity;
4123 v6 = floodlightDirectionScale;
4125 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4127 floodlightRGB[0] = v1;
4128 floodlightRGB[1] = v2;
4129 floodlightRGB[2] = v3;
4131 if ( VectorLength( floodlightRGB ) == 0 ) {
4132 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4145 floodlightDistance = v4;
4146 floodlightIntensity = v5;
4147 floodlightDirectionScale = v6;
4149 floodlighty = qtrue;
4150 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4154 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4157 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4158 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4159 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4161 ColorNormalize( floodlightRGB,floodlightRGB );
4165 FloodLightForSample()
4166 calculates floodlight value for a given sample
4167 once again, kudos to the dirtmapping coder
4170 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4175 float gatherLight, outLight;
4176 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4184 if ( trace == NULL || trace->cluster < 0 ) {
4190 dd = floodLightDistance;
4191 VectorCopy( trace->normal, normal );
4193 /* check if the normal is aligned to the world-up */
4194 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4195 if ( normal[ 2 ] == 1.0f ) {
4196 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4197 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4199 else if ( normal[ 2 ] == -1.0f ) {
4200 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4201 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4206 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4207 CrossProduct( normal, worldUp, myRt );
4208 VectorNormalize( myRt, myRt );
4209 CrossProduct( myRt, normal, myUp );
4210 VectorNormalize( myUp, myUp );
4213 /* vortex: optimise floodLightLowQuality a bit */
4214 if ( floodLightLowQuality == qtrue ) {
4215 /* iterate through ordered vectors */
4216 for ( i = 0; i < numFloodVectors; i++ )
4217 if ( rand() % 10 != 0 ) {
4223 /* iterate through ordered vectors */
4224 for ( i = 0; i < numFloodVectors; i++ )
4228 /* transform vector into tangent space */
4229 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4230 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4231 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4234 VectorMA( trace->origin, dd, direction, trace->end );
4236 //VectorMA( trace->origin, 1, direction, trace->origin );
4238 SetupTrace( trace );
4239 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4244 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4245 contribution = 1.0f;
4247 else if ( trace->opaque ) {
4248 VectorSubtract( trace->hit, trace->origin, displacement );
4249 d = VectorLength( displacement );
4251 // d=trace->distance;
4252 //if (d>256) gatherDirt+=1;
4253 contribution = d / dd;
4254 if ( contribution > 1 ) {
4255 contribution = 1.0f;
4258 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4261 gatherLight += contribution;
4266 if ( gatherLight <= 0.0f ) {
4275 gatherLight /= ( sub );
4277 outLight = gatherLight;
4278 if ( outLight > 1.0f ) {
4282 /* return to sender */
4287 FloodLightRawLightmap
4288 lighttracer style ambient occlusion light hack.
4289 Kudos to the dirtmapping author for most of this source.
4290 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4291 VorteX: fixed problems with deluxemapping
4294 // floodlight pass on a lightmap
4295 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4296 int i, x, y, *cluster;
4297 float *origin, *normal, *floodlight, floodLightAmount;
4298 surfaceInfo_t *info;
4301 // float samples, average, *floodlight2;
4303 memset( &trace,0,sizeof( trace_t ) );
4306 trace.testOcclusion = qtrue;
4307 trace.forceSunlight = qfalse;
4308 trace.twoSided = qtrue;
4309 trace.recvShadows = lm->recvShadows;
4310 trace.numSurfaces = lm->numLightSurfaces;
4311 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4312 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4313 trace.testAll = qfalse;
4314 trace.distance = 1024;
4316 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4317 //trace.twoSided = qfalse;
4318 for ( i = 0; i < trace.numSurfaces; i++ )
4321 info = &surfaceInfos[ trace.surfaces[ i ] ];
4323 /* check twosidedness */
4324 if ( info->si->twoSided ) {
4325 trace.twoSided = qtrue;
4330 /* gather floodlight */
4331 for ( y = 0; y < lm->sh; y++ )
4333 for ( x = 0; x < lm->sw; x++ )
4336 cluster = SUPER_CLUSTER( x, y );
4337 origin = SUPER_ORIGIN( x, y );
4338 normal = SUPER_NORMAL( x, y );
4339 floodlight = SUPER_FLOODLIGHT( x, y );
4341 /* set default dirt */
4344 /* only look at mapped luxels */
4345 if ( *cluster < 0 ) {
4350 trace.cluster = *cluster;
4351 VectorCopy( origin, trace.origin );
4352 VectorCopy( normal, trace.normal );
4354 /* get floodlight */
4355 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4357 /* add floodlight */
4358 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4359 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4360 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4361 floodlight[3] += floodlightDirectionScale;
4365 /* testing no filtering */
4371 for ( y = 0; y < lm->sh; y++ )
4373 for ( x = 0; x < lm->sw; x++ )
4376 cluster = SUPER_CLUSTER( x, y );
4377 floodlight = SUPER_FLOODLIGHT( x, y );
4379 /* filter dirt by adjacency to unmapped luxels */
4380 average = *floodlight;
4382 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4384 if ( sy < 0 || sy >= lm->sh ) {
4388 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4390 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4394 /* get neighboring luxel */
4395 cluster = SUPER_CLUSTER( sx, sy );
4396 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4397 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4402 average += *floodlight2;
4407 if ( samples <= 0.0f ) {
4413 if ( samples <= 0.0f ) {
4418 *floodlight = average / samples;
4424 void FloodLightRawLightmap( int rawLightmapNum ){
4427 /* bail if this number exceeds the number of raw lightmaps */
4428 if ( rawLightmapNum >= numRawLightmaps ) {
4432 lm = &rawLightmaps[ rawLightmapNum ];
4435 if ( floodlighty && floodlightIntensity ) {
4436 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4440 if ( lm->floodlightIntensity ) {
4441 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4442 numSurfacesFloodlighten += 1;
4446 void FloodlightRawLightmaps(){
4447 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4448 numSurfacesFloodlighten = 0;
4449 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4450 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4454 FloodLightIlluminate()
4455 illuminate floodlight into lightmap luxels
4458 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4459 float *luxel, *floodlight, *deluxel, *normal;
4462 int x, y, lightmapNum;
4464 /* walk lightmaps */
4465 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4468 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4472 if( lm->styles[lightmapNum] != LS_NORMAL && lm->styles[lightmapNum] != LS_NONE ) // isStyleLight
4475 /* apply floodlight to each luxel */
4476 for ( y = 0; y < lm->sh; y++ )
4478 for ( x = 0; x < lm->sw; x++ )
4480 /* get floodlight */
4481 floodlight = SUPER_FLOODLIGHT( x, y );
4482 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4487 cluster = SUPER_CLUSTER( x, y );
4489 /* only process mapped luxels */
4490 if ( *cluster < 0 ) {
4494 /* get particulars */
4495 luxel = SUPER_LUXEL( lightmapNum, x, y );
4496 deluxel = SUPER_DELUXEL( x, y );
4498 /* add to lightmap */
4499 luxel[0] += floodlight[0];
4500 luxel[1] += floodlight[1];
4501 luxel[2] += floodlight[2];
4503 if ( luxel[3] == 0 ) {
4507 /* add to deluxemap */
4508 if ( deluxemap && floodlight[3] > 0 ) {
4511 normal = SUPER_NORMAL( x, y );
4512 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4514 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4515 if ( brightness < 0.00390625f ) {
4516 brightness = 0.00390625f;
4519 VectorScale( normal, brightness, lightvector );
4520 VectorAdd( deluxel, lightvector, deluxel );