1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale ){
54 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
55 if ( scale <= 0.0f ) {
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for ( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if ( sample[ i ] < 0.0f ) {
73 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
76 if ( lightmapExposure == 0 ) {
77 /* clamp with color normalization */
79 if ( sample[ 1 ] > max ) {
82 if ( sample[ 2 ] > max ) {
86 VectorScale( sample, ( 255.0f / max ), sample );
91 inv = 1.f / lightmapExposure;
95 if ( sample[ 1 ] > max ) {
98 if ( sample[ 2 ] > max ) {
102 dif = ( 1 - exp( -max * inv ) ) * 255;
112 for ( i = 0; i < 3; i++ )
119 /* compensate for ingame overbrighting/bitshifting */
120 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
123 if ( lightmapsRGB ) {
124 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
125 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
126 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
130 colorBytes[ 0 ] = sample[ 0 ];
131 colorBytes[ 1 ] = sample[ 1 ];
132 colorBytes[ 2 ] = sample[ 2 ];
136 * Same as ColorToBytes, but if the output color will never contain zero
137 * components. Used to avoid returning 0 0 0 due to an ioq3 issue. Reason
138 * to also map 0 0 1 to 1 1 1 is to ensure monotonicity in the color mapping
139 * to prevent banding-like artifacts on lightmaps.
141 void ColorToBytesNonZero( const float *color, byte *colorBytes, float scale) {
143 ColorToBytes(color, colorBytes, scale);
144 for (i = 0; i < 3; ++i)
145 if (colorBytes[i] == 0)
150 /* -------------------------------------------------------------------------------
152 this section deals with phong shading (normal interpolation across brush faces)
154 ------------------------------------------------------------------------------- */
158 smooths together coincident vertex normals across the bsp
161 #define MAX_SAMPLES 256
162 #define THETA_EPSILON 0.000001
163 #define EQUAL_NORMAL_EPSILON 0.01
165 void SmoothNormals( void ){
166 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
167 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
168 bspDrawSurface_t *ds;
172 vec3_t average, diff;
173 int indexes[ MAX_SAMPLES ];
174 vec3_t votes[ MAX_SAMPLES ];
177 /* allocate shade angle table */
178 shadeAngles = safe_malloc0( numBSPDrawVerts * sizeof( float ) );
180 /* allocate smoothed table */
181 cs = ( numBSPDrawVerts / 8 ) + 1;
182 smoothed = safe_malloc0( cs );
184 /* set default shade angle */
185 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
188 /* run through every surface and flag verts belonging to non-lightmapped surfaces
189 and set per-vertex smoothing angle */
190 for ( i = 0; i < numBSPDrawSurfaces; i++ )
193 ds = &bspDrawSurfaces[ i ];
195 /* get shader for shade angle */
196 si = surfaceInfos[ i ].si;
197 if ( si->shadeAngleDegrees ) {
198 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
201 shadeAngle = defaultShadeAngle;
203 if ( shadeAngle > maxShadeAngle ) {
204 maxShadeAngle = shadeAngle;
208 for ( j = 0; j < ds->numVerts; j++ )
210 f = ds->firstVert + j;
211 shadeAngles[ f ] = shadeAngle;
212 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
213 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
217 /* ydnar: optional force-to-trisoup */
218 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
219 ds->surfaceType = MST_TRIANGLE_SOUP;
220 ds->lightmapNum[ 0 ] = -3;
224 /* bail if no surfaces have a shade angle */
225 if ( maxShadeAngle == 0 ) {
233 start = I_FloatTime();
235 /* go through the list of vertexes */
236 for ( i = 0; i < numBSPDrawVerts; i++ )
239 f = 10 * i / numBSPDrawVerts;
242 Sys_Printf( "%i...", f );
245 /* already smoothed? */
246 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
251 VectorClear( average );
255 /* build a table of coincident vertexes */
256 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
258 /* already smoothed? */
259 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
264 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
268 /* use smallest shade angle */
269 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
271 /* check shade angle */
272 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
276 else if ( dot < -1.0 ) {
279 testAngle = acos( dot ) + THETA_EPSILON;
280 if ( testAngle >= shadeAngle ) {
281 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
284 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
286 /* add to the list */
287 indexes[ numVerts++ ] = j;
290 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
292 /* see if this normal has already been voted */
293 for ( k = 0; k < numVotes; k++ )
295 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
296 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
297 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
298 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
303 /* add a new vote? */
304 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
305 VectorAdd( average, bspDrawVerts[ j ].normal, average );
306 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
311 /* don't average for less than 2 verts */
312 if ( numVerts < 2 ) {
317 if ( VectorNormalize( average, average ) > 0 ) {
319 for ( j = 0; j < numVerts; j++ )
320 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
324 /* free the tables */
329 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
334 /* -------------------------------------------------------------------------------
336 this section deals with phong shaded lightmap tracing
338 ------------------------------------------------------------------------------- */
340 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
344 calculates the st tangent vectors for normalmapping
347 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
353 /* calculate barycentric basis for the triangle */
354 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 ] );
355 if ( fabs( bb ) < 0.00000001f ) {
360 for ( i = 0; i < numVerts; i++ )
362 /* calculate s tangent vector */
363 s = dv[ i ]->st[ 0 ] + 10.0f;
364 t = dv[ i ]->st[ 1 ];
365 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
366 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
367 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
369 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
370 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
371 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
373 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
374 VectorNormalize( stv[ i ], stv[ i ] );
376 /* calculate t tangent vector */
377 s = dv[ i ]->st[ 0 ];
378 t = dv[ i ]->st[ 1 ] + 10.0f;
379 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
380 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
381 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
383 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
384 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
385 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
387 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
388 VectorNormalize( ttv[ i ], ttv[ i ] );
391 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
392 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
395 /* return to caller */
404 perterbs the normal by the shader's normalmap in tangent space
407 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
413 VectorCopy( dv->normal, pNormal );
415 /* sample normalmap */
416 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
420 /* remap sampled normal from [0,255] to [-1,-1] */
421 for ( i = 0; i < 3; i++ )
422 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
424 /* scale tangent vectors and add to original normal */
425 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
426 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
427 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
429 /* renormalize and return */
430 VectorNormalize( pNormal, pNormal );
437 maps a luxel for triangle bv at
441 #define BOGUS_NUDGE -99999.0f
443 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 ] ){
444 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
445 float *luxel, *origin, *normal, d, lightmapSampleOffset;
452 vec4_t sideplane, hostplane;
457 static float nudges[][ 2 ] =
459 //%{ 0, 0 }, /* try center first */
460 { -NUDGE, 0 }, /* left */
461 { NUDGE, 0 }, /* right */
462 { 0, NUDGE }, /* up */
463 { 0, -NUDGE }, /* down */
464 { -NUDGE, NUDGE }, /* left/up */
465 { NUDGE, -NUDGE }, /* right/down */
466 { NUDGE, NUDGE }, /* right/up */
467 { -NUDGE, -NUDGE }, /* left/down */
468 { BOGUS_NUDGE, BOGUS_NUDGE }
472 /* find luxel xy coords (fixme: subtract 0.5?) */
473 x = dv->lightmap[ 0 ][ 0 ];
474 y = dv->lightmap[ 0 ][ 1 ];
478 else if ( x >= lm->sw ) {
484 else if ( y >= lm->sh ) {
488 /* set shader and cluster list */
489 if ( info != NULL ) {
491 numClusters = info->numSurfaceClusters;
492 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
501 /* get luxel, origin, cluster, and normal */
502 luxel = SUPER_LUXEL( 0, x, y );
503 origin = SUPER_ORIGIN( x, y );
504 normal = SUPER_NORMAL( x, y );
505 cluster = SUPER_CLUSTER( x, y );
507 /* don't attempt to remap occluded luxels for planar surfaces */
508 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
512 /* only average the normal for premapped luxels */
513 else if ( ( *cluster ) >= 0 ) {
514 /* do bumpmap calculations */
516 PerturbNormal( dv, si, pNormal, stv, ttv );
519 VectorCopy( dv->normal, pNormal );
522 /* add the additional normal data */
523 VectorAdd( normal, pNormal, normal );
528 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
532 /* axial lightmap projection */
533 if ( lm->vecs != NULL ) {
534 /* calculate an origin for the sample from the lightmap vectors */
535 VectorCopy( lm->origin, origin );
536 for ( i = 0; i < 3; i++ )
538 /* add unless it's the axis, which is taken care of later */
539 if ( i == lm->axisNum ) {
542 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
545 /* project the origin onto the plane */
546 d = DotProduct( origin, plane ) - plane[ 3 ];
547 d /= plane[ lm->axisNum ];
548 origin[ lm->axisNum ] -= d;
551 /* non axial lightmap projection (explicit xyz) */
553 VectorCopy( dv->xyz, origin );
556 //////////////////////
557 //27's test to make sure samples stay within the triangle boundaries
558 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
559 //2) if it does, nudge it onto the correct side.
561 if ( worldverts != NULL && lightmapTriangleCheck ) {
562 for ( j = 0; j < 3; j++ )
564 VectorCopy( worldverts[j],cverts[j] );
566 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
568 for ( j = 0; j < 3; j++ )
570 for ( i = 0; i < 3; i++ )
572 //build plane using 2 edges and a normal
573 next = ( i + 1 ) % 3;
575 VectorCopy( cverts[next],temp );
576 VectorAdd( temp,hostplane,temp );
577 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
579 //planetest sample point
580 e = DotProduct( origin,sideplane );
581 e = e - sideplane[3];
584 //VectorClear(origin);
585 //Move the sample point back inside triangle bounds
586 origin[0] -= sideplane[0] * ( e + 1 );
587 origin[1] -= sideplane[1] * ( e + 1 );
588 origin[2] -= sideplane[2] * ( e + 1 );
590 VectorClear( origin );
597 ////////////////////////
599 /* planar surfaces have precalculated lightmap vectors for nudging */
600 if ( lm->plane != NULL ) {
601 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
602 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
603 VectorCopy( lm->plane, vecs[ 2 ] );
606 /* non-planar surfaces must calculate them */
609 if ( plane != NULL ) {
610 VectorCopy( plane, vecs[ 2 ] );
613 VectorCopy( dv->normal, vecs[ 2 ] );
615 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
618 /* push the origin off the surface a bit */
620 lightmapSampleOffset = si->lightmapSampleOffset;
623 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
625 if ( lm->axisNum < 0 ) {
626 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
628 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
629 origin[ lm->axisNum ] -= lightmapSampleOffset;
632 origin[ lm->axisNum ] += lightmapSampleOffset;
635 VectorCopy( origin,origintwo );
636 if ( lightmapExtraVisClusterNudge ) {
637 origintwo[0] += vecs[2][0];
638 origintwo[1] += vecs[2][1];
639 origintwo[2] += vecs[2][2];
643 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
645 /* another retarded hack, storing nudge count in luxel[ 1 ] */
648 /* point in solid? (except in dark mode) */
649 if ( pointCluster < 0 && dark == qfalse ) {
650 /* nudge the the location around */
652 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
654 /* nudge the vector around a bit */
655 for ( i = 0; i < 3; i++ )
657 /* set nudged point*/
658 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
662 /* get pvs cluster */
663 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
664 if ( pointCluster >= 0 ) {
665 VectorCopy( nudged, origin );
671 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
672 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
673 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
674 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
675 if ( pointCluster >= 0 ) {
676 VectorCopy( nudged, origin );
682 if ( pointCluster < 0 ) {
683 ( *cluster ) = CLUSTER_OCCLUDED;
684 VectorClear( origin );
685 VectorClear( normal );
691 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
693 /* do bumpmap calculations */
695 PerturbNormal( dv, si, pNormal, stv, ttv );
698 VectorCopy( dv->normal, pNormal );
701 /* store the cluster and normal */
702 ( *cluster ) = pointCluster;
703 VectorCopy( pNormal, normal );
705 /* store explicit mapping pass and implicit mapping pass */
720 recursively subdivides a triangle until its edges are shorter
721 than the distance between two luxels (thanks jc :)
724 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 ] ){
725 bspDrawVert_t mid, *dv2[ 3 ];
729 /* map the vertexes */
731 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
732 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
733 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
739 float *a, *b, dx, dy, dist, maxDist;
742 /* find the longest edge and split it */
745 for ( i = 0; i < 3; i++ )
748 a = dv[ i ]->lightmap[ 0 ];
749 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
752 dx = a[ 0 ] - b[ 0 ];
753 dy = a[ 1 ] - b[ 1 ];
754 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
757 if ( dist > maxDist ) {
763 /* try to early out */
764 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
769 /* split the longest edge and map it */
770 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
771 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
773 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
774 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
776 /* recurse to first triangle */
777 VectorCopy( dv, dv2 );
779 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
781 /* recurse to second triangle */
782 VectorCopy( dv, dv2 );
783 dv2[ ( max + 1 ) % 3 ] = ∣
784 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
791 seed function for MapTriangle_r()
792 requires a cw ordered triangle
795 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
798 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
799 vec3_t worldverts[ 3 ];
802 /* get plane if possible */
803 if ( lm->plane != NULL ) {
804 VectorCopy( lm->plane, plane );
805 plane[ 3 ] = lm->plane[ 3 ];
808 /* otherwise make one from the points */
809 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
813 /* check to see if we need to calculate texture->world tangent vectors */
814 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
824 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
825 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
826 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
828 /* map the vertexes */
829 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
830 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
831 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
833 /* 2002-11-20: prefer axial triangle edges */
835 /* subdivide the triangle */
836 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
840 for ( i = 0; i < 3; i++ )
843 bspDrawVert_t *dv2[ 3 ];
847 a = dv[ i ]->lightmap[ 0 ];
848 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
850 /* make degenerate triangles for mapping edges */
851 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
853 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
854 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
856 /* map the degenerate triangle */
857 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
868 recursively subdivides a quad until its edges are shorter
869 than the distance between two luxels
872 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 ] ){
873 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
880 float *a, *b, dx, dy, dist, maxDist;
883 /* find the longest edge and split it */
886 for ( i = 0; i < 4; i++ )
889 a = dv[ i ]->lightmap[ 0 ];
890 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
893 dx = a[ 0 ] - b[ 0 ];
894 dy = a[ 1 ] - b[ 1 ];
895 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
898 if ( dist > maxDist ) {
904 /* try to early out */
905 if ( max < 0 || maxDist <= subdivideThreshold ) {
910 /* we only care about even/odd edges */
913 /* split the longest edges */
914 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
915 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
917 /* map the vertexes */
918 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
919 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
923 /* recurse to first quad */
925 dv2[ 1 ] = &mid[ 0 ];
926 dv2[ 2 ] = &mid[ 1 ];
928 MapQuad_r( lm, info, dv2, plane, stv, ttv );
930 /* recurse to second quad */
931 dv2[ 0 ] = &mid[ 0 ];
934 dv2[ 3 ] = &mid[ 1 ];
935 MapQuad_r( lm, info, dv2, plane, stv, ttv );
941 /* recurse to first quad */
944 dv2[ 2 ] = &mid[ 0 ];
945 dv2[ 3 ] = &mid[ 1 ];
946 MapQuad_r( lm, info, dv2, plane, stv, ttv );
948 /* recurse to second quad */
949 dv2[ 0 ] = &mid[ 1 ];
950 dv2[ 1 ] = &mid[ 0 ];
953 MapQuad_r( lm, info, dv2, plane, stv, ttv );
961 seed function for MapQuad_r()
962 requires a cw ordered triangle quad
965 #define QUAD_PLANAR_EPSILON 0.5f
967 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
970 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
973 /* get plane if possible */
974 if ( lm->plane != NULL ) {
975 VectorCopy( lm->plane, plane );
976 plane[ 3 ] = lm->plane[ 3 ];
979 /* otherwise make one from the points */
980 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
984 /* 4th point must fall on the plane */
985 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
986 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
990 /* check to see if we need to calculate texture->world tangent vectors */
991 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
1001 /* map the vertexes */
1002 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1003 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1004 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1005 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1007 /* subdivide the quad */
1008 MapQuad_r( lm, info, dv, plane, stv, ttv );
1016 maps the locations, normals, and pvs clusters for a raw lightmap
1019 #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)
1021 void MapRawLightmap( int rawLightmapNum ){
1022 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1023 float *luxel, *origin, *normal, samples, radius, pass;
1025 bspDrawSurface_t *ds;
1026 surfaceInfo_t *info;
1027 mesh_t src, *subdivided, *mesh;
1028 bspDrawVert_t *verts, *dv[ 4 ], fake;
1031 /* bail if this number exceeds the number of raw lightmaps */
1032 if ( rawLightmapNum >= numRawLightmaps ) {
1037 lm = &rawLightmaps[ rawLightmapNum ];
1039 /* -----------------------------------------------------------------
1040 map referenced surfaces onto the raw lightmap
1041 ----------------------------------------------------------------- */
1043 /* walk the list of surfaces on this raw lightmap */
1044 for ( n = 0; n < lm->numLightSurfaces; n++ )
1046 /* with > 1 surface per raw lightmap, clear occluded */
1048 for ( y = 0; y < lm->sh; y++ )
1050 for ( x = 0; x < lm->sw; x++ )
1053 cluster = SUPER_CLUSTER( x, y );
1054 if ( *cluster < 0 ) {
1055 *cluster = CLUSTER_UNMAPPED;
1062 num = lightSurfaces[ lm->firstLightSurface + n ];
1063 ds = &bspDrawSurfaces[ num ];
1064 info = &surfaceInfos[ num ];
1066 /* bail if no lightmap to calculate */
1067 if ( info->lm != lm ) {
1072 /* map the surface onto the lightmap origin/cluster/normal buffers */
1073 switch ( ds->surfaceType )
1077 verts = yDrawVerts + ds->firstVert;
1079 /* map the triangles */
1080 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1082 for ( i = 0; i < ds->numIndexes; i += 3 )
1084 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1085 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1086 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1087 MapTriangle( lm, info, dv, mapNonAxial );
1093 /* make a mesh from the drawsurf */
1094 src.width = ds->patchWidth;
1095 src.height = ds->patchHeight;
1096 src.verts = &yDrawVerts[ ds->firstVert ];
1097 //% subdivided = SubdivideMesh( src, 8, 512 );
1098 subdivided = SubdivideMesh2( src, info->patchIterations );
1100 /* fit it to the curve and remove colinear verts on rows/columns */
1101 PutMeshOnCurve( *subdivided );
1102 mesh = RemoveLinearMeshColumnsRows( subdivided );
1103 FreeMesh( subdivided );
1106 verts = mesh->verts;
1111 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1112 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1113 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1114 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1118 /* map the mesh quads */
1121 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1123 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1125 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1128 pw[ 0 ] = x + ( y * mesh->width );
1129 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1130 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1131 pw[ 3 ] = x + 1 + ( y * mesh->width );
1132 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1137 /* get drawverts and map first triangle */
1138 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1139 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1140 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1141 MapTriangle( lm, info, dv, mapNonAxial );
1143 /* get drawverts and map second triangle */
1144 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1145 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1146 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1147 MapTriangle( lm, info, dv, mapNonAxial );
1154 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1156 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1159 pw[ 0 ] = x + ( y * mesh->width );
1160 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1161 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1162 pw[ 3 ] = x + 1 + ( y * mesh->width );
1168 /* attempt to map quad first */
1169 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1170 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1171 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1172 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1173 if ( MapQuad( lm, info, dv ) ) {
1177 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1179 /* get drawverts and map first triangle */
1180 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1181 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1182 MapTriangle( lm, info, dv, mapNonAxial );
1184 /* get drawverts and map second triangle */
1185 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1186 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1187 MapTriangle( lm, info, dv, mapNonAxial );
1203 /* -----------------------------------------------------------------
1204 average and clean up luxel normals
1205 ----------------------------------------------------------------- */
1207 /* walk the luxels */
1208 for ( y = 0; y < lm->sh; y++ )
1210 for ( x = 0; x < lm->sw; x++ )
1213 luxel = SUPER_LUXEL( 0, x, y );
1214 normal = SUPER_NORMAL( x, y );
1215 cluster = SUPER_CLUSTER( x, y );
1217 /* only look at mapped luxels */
1218 if ( *cluster < 0 ) {
1222 /* the normal data could be the sum of multiple samples */
1223 if ( luxel[ 3 ] > 1.0f ) {
1224 VectorNormalize( normal, normal );
1227 /* mark this luxel as having only one normal */
1232 /* non-planar surfaces stop here */
1233 if ( lm->plane == NULL ) {
1237 /* -----------------------------------------------------------------
1238 map occluded or unuxed luxels
1239 ----------------------------------------------------------------- */
1241 /* walk the luxels */
1242 radius = floor( superSample / 2 );
1243 radius = radius > 0 ? radius : 1.0f;
1245 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1247 for ( y = 0; y < lm->sh; y++ )
1249 for ( x = 0; x < lm->sw; x++ )
1252 luxel = SUPER_LUXEL( 0, x, y );
1253 normal = SUPER_NORMAL( x, y );
1254 cluster = SUPER_CLUSTER( x, y );
1256 /* only look at unmapped luxels */
1257 if ( *cluster != CLUSTER_UNMAPPED ) {
1261 /* divine a normal and origin from neighboring luxels */
1262 VectorClear( fake.xyz );
1263 VectorClear( fake.normal );
1264 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1265 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1267 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1269 if ( sy < 0 || sy >= lm->sh ) {
1273 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1275 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1279 /* get neighboring luxel */
1280 luxel = SUPER_LUXEL( 0, sx, sy );
1281 origin = SUPER_ORIGIN( sx, sy );
1282 normal = SUPER_NORMAL( sx, sy );
1283 cluster = SUPER_CLUSTER( sx, sy );
1285 /* only consider luxels mapped in previous passes */
1286 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1290 /* add its distinctiveness to our own */
1291 VectorAdd( fake.xyz, origin, fake.xyz );
1292 VectorAdd( fake.normal, normal, fake.normal );
1293 samples += luxel[ 3 ];
1298 if ( samples == 0.0f ) {
1303 VectorDivide( fake.xyz, samples, fake.xyz );
1304 //% VectorDivide( fake.normal, samples, fake.normal );
1305 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1309 /* map the fake vert */
1310 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1315 /* -----------------------------------------------------------------
1316 average and clean up luxel normals
1317 ----------------------------------------------------------------- */
1319 /* walk the luxels */
1320 for ( y = 0; y < lm->sh; y++ )
1322 for ( x = 0; x < lm->sw; x++ )
1325 luxel = SUPER_LUXEL( 0, x, y );
1326 normal = SUPER_NORMAL( x, y );
1327 cluster = SUPER_CLUSTER( x, y );
1329 /* only look at mapped luxels */
1330 if ( *cluster < 0 ) {
1334 /* the normal data could be the sum of multiple samples */
1335 if ( luxel[ 3 ] > 1.0f ) {
1336 VectorNormalize( normal, normal );
1339 /* mark this luxel as having only one normal */
1347 for ( y = 0; y < lm->sh; y++ )
1349 for ( x = 0; x < lm->sw; x++ )
1354 cluster = SUPER_CLUSTER( x, y );
1355 origin = SUPER_ORIGIN( x, y );
1356 normal = SUPER_NORMAL( x, y );
1357 luxel = SUPER_LUXEL( x, y );
1359 if ( *cluster < 0 ) {
1363 /* check if within the bounding boxes of all surfaces referenced */
1364 ClearBounds( mins, maxs );
1365 for ( n = 0; n < lm->numLightSurfaces; n++ )
1368 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1369 TOL = info->sampleSize + 2;
1370 AddPointToBounds( info->mins, mins, maxs );
1371 AddPointToBounds( info->maxs, mins, maxs );
1372 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1373 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1374 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1380 if ( n < lm->numLightSurfaces ) {
1384 /* report bogus origin */
1385 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",
1386 rawLightmapNum, x, y, *cluster,
1387 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1388 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1389 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1400 sets up dirtmap (ambient occlusion)
1403 #define DIRT_CONE_ANGLE 88 /* degrees */
1404 #define DIRT_NUM_ANGLE_STEPS 16
1405 #define DIRT_NUM_ELEVATION_STEPS 3
1406 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1408 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1409 static int numDirtVectors = 0;
1411 void SetupDirt( void ){
1413 float angle, elevation, angleStep, elevationStep;
1417 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1419 /* calculate angular steps */
1420 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1421 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1425 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1427 /* iterate elevation */
1428 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1430 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1431 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1432 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1437 /* emit some statistics */
1438 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1444 calculates dirt value for a given sample
1447 float DirtForSample( trace_t *trace ){
1449 float gatherDirt, outDirt, angle, elevation, ooDepth;
1450 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1457 if ( trace == NULL || trace->cluster < 0 ) {
1463 ooDepth = 1.0f / dirtDepth;
1464 VectorCopy( trace->normal, normal );
1466 /* check if the normal is aligned to the world-up */
1467 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1468 if ( normal[ 2 ] == 1.0f ) {
1469 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1470 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1472 else if ( normal[ 2 ] == -1.0f ) {
1473 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1474 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1479 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1480 CrossProduct( normal, worldUp, myRt );
1481 VectorNormalize( myRt, myRt );
1482 CrossProduct( myRt, normal, myUp );
1483 VectorNormalize( myUp, myUp );
1486 /* 1 = random mode, 0 (well everything else) = non-random mode */
1487 if ( dirtMode == 1 ) {
1489 for ( i = 0; i < numDirtVectors; i++ )
1491 /* get random vector */
1492 angle = Random() * DEG2RAD( 360.0f );
1493 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1494 temp[ 0 ] = cos( angle ) * sin( elevation );
1495 temp[ 1 ] = sin( angle ) * sin( elevation );
1496 temp[ 2 ] = cos( elevation );
1498 /* transform into tangent space */
1499 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1500 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1501 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1504 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1505 SetupTrace( trace );
1506 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1510 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1511 VectorSubtract( trace->hit, trace->origin, displacement );
1512 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1518 /* iterate through ordered vectors */
1519 for ( i = 0; i < numDirtVectors; i++ )
1521 /* transform vector into tangent space */
1522 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1523 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1524 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1527 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1528 SetupTrace( trace );
1529 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1533 if ( trace->opaque ) {
1534 VectorSubtract( trace->hit, trace->origin, displacement );
1535 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1541 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1542 SetupTrace( trace );
1543 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1547 if ( trace->opaque ) {
1548 VectorSubtract( trace->hit, trace->origin, displacement );
1549 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1553 if ( gatherDirt <= 0.0f ) {
1557 /* apply gain (does this even do much? heh) */
1558 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1559 if ( outDirt > 1.0f ) {
1564 outDirt *= dirtScale;
1565 if ( outDirt > 1.0f ) {
1569 /* return to sender */
1570 return 1.0f - outDirt;
1577 calculates dirty fraction for each luxel
1580 void DirtyRawLightmap( int rawLightmapNum ){
1581 int i, x, y, sx, sy, *cluster;
1582 float *origin, *normal, *dirt, *dirt2, average, samples;
1584 surfaceInfo_t *info;
1589 /* bail if this number exceeds the number of raw lightmaps */
1590 if ( rawLightmapNum >= numRawLightmaps ) {
1595 lm = &rawLightmaps[ rawLightmapNum ];
1598 trace.testOcclusion = qtrue;
1599 trace.forceSunlight = qfalse;
1600 trace.recvShadows = lm->recvShadows;
1601 trace.numSurfaces = lm->numLightSurfaces;
1602 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1603 trace.inhibitRadius = 0.0f;
1604 trace.testAll = qfalse;
1606 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1607 trace.twoSided = qfalse;
1608 for ( i = 0; i < trace.numSurfaces; i++ )
1611 info = &surfaceInfos[ trace.surfaces[ i ] ];
1613 /* check twosidedness */
1614 if ( info->si->twoSided ) {
1615 trace.twoSided = qtrue;
1621 for ( i = 0; i < trace.numSurfaces; i++ )
1624 info = &surfaceInfos[ trace.surfaces[ i ] ];
1626 /* check twosidedness */
1627 if ( info->si->noDirty ) {
1634 for ( y = 0; y < lm->sh; y++ )
1636 for ( x = 0; x < lm->sw; x++ )
1639 cluster = SUPER_CLUSTER( x, y );
1640 origin = SUPER_ORIGIN( x, y );
1641 normal = SUPER_NORMAL( x, y );
1642 dirt = SUPER_DIRT( x, y );
1644 /* set default dirt */
1647 /* only look at mapped luxels */
1648 if ( *cluster < 0 ) {
1652 /* don't apply dirty on this surface */
1659 trace.cluster = *cluster;
1660 VectorCopy( origin, trace.origin );
1661 VectorCopy( normal, trace.normal );
1664 *dirt = DirtForSample( &trace );
1668 /* testing no filtering */
1672 for ( y = 0; y < lm->sh; y++ )
1674 for ( x = 0; x < lm->sw; x++ )
1677 cluster = SUPER_CLUSTER( x, y );
1678 dirt = SUPER_DIRT( x, y );
1680 /* filter dirt by adjacency to unmapped luxels */
1683 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1685 if ( sy < 0 || sy >= lm->sh ) {
1689 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1691 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1695 /* get neighboring luxel */
1696 cluster = SUPER_CLUSTER( sx, sy );
1697 dirt2 = SUPER_DIRT( sx, sy );
1698 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1708 if ( samples <= 0.0f ) {
1714 if ( samples <= 0.0f ) {
1719 *dirt = average / samples;
1728 calculates the pvs cluster, origin, normal of a sub-luxel
1731 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1732 int i, *cluster, *cluster2;
1733 float *origin, *origin2, *normal; //% , *normal2;
1734 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1737 /* calulate x vector */
1738 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1739 cluster = SUPER_CLUSTER( x, y );
1740 origin = SUPER_ORIGIN( x, y );
1741 //% normal = SUPER_NORMAL( x, y );
1742 cluster2 = SUPER_CLUSTER( x + 1, y );
1743 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1744 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1746 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1747 cluster = SUPER_CLUSTER( x - 1, y );
1748 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1749 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1750 cluster2 = SUPER_CLUSTER( x, y );
1751 origin2 = SUPER_ORIGIN( x, y );
1752 //% normal2 = SUPER_NORMAL( x, y );
1755 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1758 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1759 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1761 /* calulate y vector */
1762 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1763 cluster = SUPER_CLUSTER( x, y );
1764 origin = SUPER_ORIGIN( x, y );
1765 //% normal = SUPER_NORMAL( x, y );
1766 cluster2 = SUPER_CLUSTER( x, y + 1 );
1767 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1768 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1770 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1771 cluster = SUPER_CLUSTER( x, y - 1 );
1772 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1773 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1774 cluster2 = SUPER_CLUSTER( x, y );
1775 origin2 = SUPER_ORIGIN( x, y );
1776 //% normal2 = SUPER_NORMAL( x, y );
1779 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1782 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1784 /* calculate new origin */
1785 for ( i = 0; i < 3; i++ )
1786 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1789 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1790 if ( *sampleCluster < 0 ) {
1794 /* calculate new normal */
1795 normal = SUPER_NORMAL( x, y );
1796 VectorCopy( normal, sampleNormal );
1804 SubsampleRawLuxel_r()
1805 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1808 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1809 int b, samples, mapped, lighted;
1812 vec3_t deluxel[ 3 ];
1813 vec3_t origin[ 4 ], normal[ 4 ];
1814 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1815 vec3_t color, direction = { 0, 0, 0 }, total;
1819 if ( lightLuxel[ 3 ] >= lightSamples ) {
1824 VectorClear( total );
1828 /* make 2x2 subsample stamp */
1829 for ( b = 0; b < 4; b++ )
1832 VectorCopy( sampleOrigin, origin[ b ] );
1834 /* calculate position */
1835 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1841 /* increment sample count */
1842 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1845 trace->cluster = *cluster;
1846 VectorCopy( origin[ b ], trace->origin );
1847 VectorCopy( normal[ b ], trace->normal );
1851 LightContributionToSample( trace );
1852 if ( trace->forceSubsampling > 1.0f ) {
1853 /* alphashadow: we subsample as deep as we can */
1859 /* add to totals (fixme: make contrast function) */
1860 VectorCopy( trace->color, luxel[ b ] );
1861 if ( lightDeluxel ) {
1862 VectorCopy( trace->directionContribution, deluxel[ b ] );
1864 VectorAdd( total, trace->color, total );
1865 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1870 /* subsample further? */
1871 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1872 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1873 lighted != 0 && lighted != mapped ) {
1874 for ( b = 0; b < 4; b++ )
1876 if ( cluster[ b ] < 0 ) {
1879 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1884 //% VectorClear( color );
1886 VectorCopy( lightLuxel, color );
1887 if ( lightDeluxel ) {
1888 VectorCopy( lightDeluxel, direction );
1891 for ( b = 0; b < 4; b++ )
1893 if ( cluster[ b ] < 0 ) {
1896 VectorAdd( color, luxel[ b ], color );
1897 if ( lightDeluxel ) {
1898 VectorAdd( direction, deluxel[ b ], direction );
1904 if ( samples > 0 ) {
1906 color[ 0 ] /= samples;
1907 color[ 1 ] /= samples;
1908 color[ 2 ] /= samples;
1911 VectorCopy( color, lightLuxel );
1912 lightLuxel[ 3 ] += 1.0f;
1914 if ( lightDeluxel ) {
1915 direction[ 0 ] /= samples;
1916 direction[ 1 ] /= samples;
1917 direction[ 2 ] /= samples;
1918 VectorCopy( direction, lightDeluxel );
1923 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1924 static void GaussLikeRandom( float sigma, float *x, float *y ){
1926 r = Random() * 2 * Q_PI;
1927 *x = sigma * 2.73861278752581783822 * cos( r );
1928 *y = sigma * 2.73861278752581783822 * sin( r );
1935 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1938 vec3_t origin, normal;
1939 vec3_t total, totaldirection;
1942 VectorClear( total );
1943 VectorClear( totaldirection );
1945 for ( b = 0; b < lightSamples; ++b )
1948 VectorCopy( sampleOrigin, origin );
1949 GaussLikeRandom( bias, &dx, &dy );
1951 /* calculate position */
1952 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1958 trace->cluster = cluster;
1959 VectorCopy( origin, trace->origin );
1960 VectorCopy( normal, trace->normal );
1962 LightContributionToSample( trace );
1963 VectorAdd( total, trace->color, total );
1964 if ( lightDeluxel ) {
1965 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1972 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1973 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1974 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1976 if ( lightDeluxel ) {
1977 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1978 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1979 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1987 IlluminateRawLightmap()
1988 illuminates the luxels
1991 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
1992 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1993 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1995 void IlluminateRawLightmap( int rawLightmapNum ){
1996 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1997 int *cluster, *cluster2, mapped, lighted, totalLighted;
1998 size_t llSize, ldSize;
2000 surfaceInfo_t *info;
2001 qboolean filterColor, filterDir;
2003 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2004 unsigned char *flag;
2005 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2006 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2007 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2009 float stackLightLuxels[ STACK_LL_SIZE ];
2012 /* bail if this number exceeds the number of raw lightmaps */
2013 if ( rawLightmapNum >= numRawLightmaps ) {
2018 lm = &rawLightmaps[ rawLightmapNum ];
2021 trace.testOcclusion = !noTrace;
2022 trace.forceSunlight = qfalse;
2023 trace.recvShadows = lm->recvShadows;
2024 trace.numSurfaces = lm->numLightSurfaces;
2025 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2026 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2028 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2029 trace.twoSided = qfalse;
2030 for ( i = 0; i < trace.numSurfaces; i++ )
2033 info = &surfaceInfos[ trace.surfaces[ i ] ];
2035 /* check twosidedness */
2036 if ( info->si->twoSided ) {
2037 trace.twoSided = qtrue;
2042 /* create a culled light list for this raw lightmap */
2043 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2045 /* -----------------------------------------------------------------
2047 ----------------------------------------------------------------- */
2050 numLuxelsIlluminated += ( lm->sw * lm->sh );
2052 /* test debugging state */
2053 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2054 /* debug fill the luxels */
2055 for ( y = 0; y < lm->sh; y++ )
2057 for ( x = 0; x < lm->sw; x++ )
2060 cluster = SUPER_CLUSTER( x, y );
2062 /* only fill mapped luxels */
2063 if ( *cluster < 0 ) {
2067 /* get particulars */
2068 luxel = SUPER_LUXEL( 0, x, y );
2069 origin = SUPER_ORIGIN( x, y );
2070 normal = SUPER_NORMAL( x, y );
2072 /* color the luxel with raw lightmap num? */
2073 if ( debugSurfaces ) {
2074 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2077 /* color the luxel with lightmap axis? */
2078 else if ( debugAxis ) {
2079 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2080 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2081 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2084 /* color the luxel with luxel cluster? */
2085 else if ( debugCluster ) {
2086 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2089 /* color the luxel with luxel origin? */
2090 else if ( debugOrigin ) {
2091 VectorSubtract( lm->maxs, lm->mins, temp );
2092 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2093 VectorSubtract( origin, lm->mins, temp2 );
2094 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2095 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2096 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2099 /* color the luxel with the normal */
2100 else if ( normalmap ) {
2101 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2102 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2103 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2106 /* otherwise clear it */
2108 VectorClear( luxel );
2118 /* allocate temporary per-light luxel storage */
2119 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2120 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2121 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2122 lightLuxels = stackLightLuxels;
2125 lightLuxels = safe_malloc( llSize );
2128 lightDeluxels = safe_malloc( ldSize );
2131 lightDeluxels = NULL;
2135 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2137 /* set ambient color */
2138 for ( y = 0; y < lm->sh; y++ )
2140 for ( x = 0; x < lm->sw; x++ )
2143 cluster = SUPER_CLUSTER( x, y );
2144 luxel = SUPER_LUXEL( 0, x, y );
2145 normal = SUPER_NORMAL( x, y );
2146 deluxel = SUPER_DELUXEL( x, y );
2148 /* blacken unmapped clusters */
2149 if ( *cluster < 0 ) {
2150 VectorClear( luxel );
2156 VectorCopy( ambientColor, luxel );
2158 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2160 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2161 if ( brightness < 0.00390625f ) {
2162 brightness = 0.00390625f;
2165 VectorScale( normal, brightness, deluxel );
2172 /* clear styled lightmaps */
2173 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2174 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2176 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2177 memset( lm->superLuxels[ lightmapNum ], 0, size );
2181 /* debugging code */
2182 //% if( trace.numLights <= 0 )
2183 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2185 /* walk light list */
2186 for ( i = 0; i < trace.numLights; i++ )
2189 trace.light = trace.lights[ i ];
2192 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2194 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2195 lm->styles[ lightmapNum ] == LS_NONE ) {
2200 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2201 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2202 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2207 memset( lightLuxels, 0, llSize );
2209 memset( lightDeluxels, 0, ldSize );
2213 /* determine filter radius */
2214 filterRadius = lm->filterRadius > trace.light->filterRadius
2216 : trace.light->filterRadius;
2217 if ( filterRadius < 0.0f ) {
2218 filterRadius = 0.0f;
2221 /* set luxel filter radius */
2222 luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2223 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2224 luxelFilterRadius = 1;
2227 /* allocate sampling flags storage */
2228 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2229 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2230 if ( lm->superFlags == NULL ) {
2231 lm->superFlags = safe_malloc( size );
2233 memset( (void *) lm->superFlags, 0, size );
2236 /* initial pass, one sample per luxel */
2237 for ( y = 0; y < lm->sh; y++ )
2239 for ( x = 0; x < lm->sw; x++ )
2242 cluster = SUPER_CLUSTER( x, y );
2243 if ( *cluster < 0 ) {
2247 /* get particulars */
2248 lightLuxel = LIGHT_LUXEL( x, y );
2249 lightDeluxel = LIGHT_DELUXEL( x, y );
2250 origin = SUPER_ORIGIN( x, y );
2251 normal = SUPER_NORMAL( x, y );
2252 flag = SUPER_FLAG( x, y );
2254 /* set contribution count */
2255 lightLuxel[ 3 ] = 1.0f;
2258 trace.cluster = *cluster;
2259 VectorCopy( origin, trace.origin );
2260 VectorCopy( normal, trace.normal );
2262 /* get light for this sample */
2263 LightContributionToSample( &trace );
2264 VectorCopy( trace.color, lightLuxel );
2266 /* add the contribution to the deluxemap */
2268 VectorCopy( trace.directionContribution, lightDeluxel );
2271 /* check for evilness */
2272 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2274 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2277 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2283 /* don't even bother with everything else if nothing was lit */
2284 if ( totalLighted == 0 ) {
2288 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2289 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2290 if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2292 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2294 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2299 VectorClear( total );
2301 /* test 2x2 stamp */
2302 for ( t = 0; t < 4; t++ )
2304 /* set sample coords */
2305 sx = x + tests[ t ][ 0 ];
2306 sy = y + tests[ t ][ 1 ];
2309 cluster = SUPER_CLUSTER( sx, sy );
2310 if ( *cluster < 0 ) {
2316 flag = SUPER_FLAG( sx, sy );
2317 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2318 /* force a lighted/mapped discrepancy so we subsample */
2323 lightLuxel = LIGHT_LUXEL( sx, sy );
2324 VectorAdd( total, lightLuxel, total );
2325 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2330 /* if total color is under a certain amount, then don't bother subsampling */
2331 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2335 /* if all 4 pixels are either in shadow or light, then don't subsample */
2336 if ( lighted != 0 && lighted != mapped ) {
2337 for ( t = 0; t < 4; t++ )
2339 /* set sample coords */
2340 sx = x + tests[ t ][ 0 ];
2341 sy = y + tests[ t ][ 1 ];
2344 cluster = SUPER_CLUSTER( sx, sy );
2345 if ( *cluster < 0 ) {
2348 flag = SUPER_FLAG( sx, sy );
2349 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2352 lightLuxel = LIGHT_LUXEL( sx, sy );
2353 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2354 origin = SUPER_ORIGIN( sx, sy );
2356 /* only subsample shadowed luxels */
2357 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2361 if ( lightRandomSamples ) {
2362 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2365 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2368 *flag |= FLAG_ALREADY_SUBSAMPLED;
2370 /* debug code to colorize subsampled areas to yellow */
2371 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2372 //% VectorSet( luxel, 255, 204, 0 );
2379 /* tertiary pass, apply dirt map (ambient occlusion) */
2382 for ( y = 0; y < lm->sh; y++ )
2384 for ( x = 0; x < lm->sw; x++ )
2387 cluster = SUPER_CLUSTER( x, y );
2388 if ( *cluster < 0 ) {
2392 /* get particulars */
2393 lightLuxel = LIGHT_LUXEL( x, y );
2394 dirt = SUPER_DIRT( x, y );
2396 /* scale light value */
2397 VectorScale( lightLuxel, *dirt, lightLuxel );
2402 /* allocate sampling lightmap storage */
2403 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2404 /* allocate sampling lightmap storage */
2405 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2406 lm->superLuxels[ lightmapNum ] = safe_malloc0( size );
2410 if ( lightmapNum > 0 ) {
2411 lm->styles[ lightmapNum ] = trace.light->style;
2412 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2415 /* copy to permanent luxels */
2416 for ( y = 0; y < lm->sh; y++ )
2418 for ( x = 0; x < lm->sw; x++ )
2420 /* get cluster and origin */
2421 cluster = SUPER_CLUSTER( x, y );
2422 if ( *cluster < 0 ) {
2425 origin = SUPER_ORIGIN( x, y );
2428 if ( luxelFilterRadius ) {
2430 VectorClear( averageColor );
2431 VectorClear( averageDir );
2434 /* cheaper distance-based filtering */
2435 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2437 if ( sy < 0 || sy >= lm->sh ) {
2441 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2443 if ( sx < 0 || sx >= lm->sw ) {
2447 /* get particulars */
2448 cluster = SUPER_CLUSTER( sx, sy );
2449 if ( *cluster < 0 ) {
2452 lightLuxel = LIGHT_LUXEL( sx, sy );
2453 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2456 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2457 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2459 /* scale luxel by filter weight */
2460 VectorScale( lightLuxel, weight, color );
2461 VectorAdd( averageColor, color, averageColor );
2463 VectorScale( lightDeluxel, weight, direction );
2464 VectorAdd( averageDir, direction, averageDir );
2471 if ( samples <= 0.0f ) {
2475 /* scale into luxel */
2476 luxel = SUPER_LUXEL( lightmapNum, x, y );
2479 /* handle negative light */
2480 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2481 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2482 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2483 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2486 /* handle normal light */
2489 luxel[ 0 ] += averageColor[ 0 ] / samples;
2490 luxel[ 1 ] += averageColor[ 1 ] / samples;
2491 luxel[ 2 ] += averageColor[ 2 ] / samples;
2495 /* scale into luxel */
2496 deluxel = SUPER_DELUXEL( x, y );
2497 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2498 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2499 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2506 /* get particulars */
2507 lightLuxel = LIGHT_LUXEL( x, y );
2508 lightDeluxel = LIGHT_DELUXEL( x, y );
2509 luxel = SUPER_LUXEL( lightmapNum, x, y );
2510 deluxel = SUPER_DELUXEL( x, y );
2512 /* handle negative light */
2513 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2514 VectorScale( averageColor, -1.0f, averageColor );
2520 /* handle negative light */
2521 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2522 VectorSubtract( luxel, lightLuxel, luxel );
2525 /* handle normal light */
2527 VectorAdd( luxel, lightLuxel, luxel );
2531 VectorAdd( deluxel, lightDeluxel, deluxel );
2538 /* free temporary luxels */
2539 if ( lightLuxels != stackLightLuxels ) {
2540 free( lightLuxels );
2544 free( lightDeluxels );
2548 /* free light list */
2549 FreeTraceLights( &trace );
2551 /* floodlight pass */
2552 if ( floodlighty ) {
2553 FloodlightIlluminateLightmap( lm );
2556 if ( debugnormals ) {
2557 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2560 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2564 for ( y = 0; y < lm->sh; y++ )
2566 for ( x = 0; x < lm->sw; x++ )
2569 cluster = SUPER_CLUSTER( x, y );
2570 //% if( *cluster < 0 )
2573 /* get particulars */
2574 luxel = SUPER_LUXEL( lightmapNum, x, y );
2575 normal = SUPER_NORMAL( x, y );
2577 luxel[0] = ( normal[0] * 127 ) + 127;
2578 luxel[1] = ( normal[1] * 127 ) + 127;
2579 luxel[2] = ( normal[2] * 127 ) + 127;
2585 /* -----------------------------------------------------------------
2587 ----------------------------------------------------------------- */
2590 /* walk lightmaps */
2591 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2594 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2598 /* apply dirt to each luxel */
2599 for ( y = 0; y < lm->sh; y++ )
2601 for ( x = 0; x < lm->sw; x++ )
2604 cluster = SUPER_CLUSTER( x, y );
2606 /* get particulars */
2607 luxel = SUPER_LUXEL( lightmapNum, x, y );
2608 dirt = SUPER_DIRT( x, y );
2611 VectorScale( luxel, *dirt, luxel );
2615 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2622 /* -----------------------------------------------------------------
2624 ----------------------------------------------------------------- */
2626 /* walk lightmaps */
2627 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2630 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2634 /* average occluded luxels from neighbors */
2635 for ( y = 0; y < lm->sh; y++ )
2637 for ( x = 0; x < lm->sw; x++ )
2639 /* get particulars */
2640 cluster = SUPER_CLUSTER( x, y );
2641 luxel = SUPER_LUXEL( lightmapNum, x, y );
2642 deluxel = SUPER_DELUXEL( x, y );
2643 normal = SUPER_NORMAL( x, y );
2645 /* determine if filtering is necessary */
2646 filterColor = qfalse;
2648 if ( *cluster < 0 ||
2649 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2650 filterColor = qtrue;
2653 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2657 if ( !filterColor && !filterDir ) {
2661 /* choose seed amount */
2662 VectorClear( averageColor );
2663 VectorClear( averageDir );
2666 /* walk 3x3 matrix */
2667 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2669 if ( sy < 0 || sy >= lm->sh ) {
2673 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2675 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2679 /* get neighbor's particulars */
2680 cluster2 = SUPER_CLUSTER( sx, sy );
2681 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2682 deluxel2 = SUPER_DELUXEL( sx, sy );
2684 /* ignore unmapped/unlit luxels */
2685 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2686 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2690 /* add its distinctiveness to our own */
2691 VectorAdd( averageColor, luxel2, averageColor );
2692 samples += luxel2[ 3 ];
2694 VectorAdd( averageDir, deluxel2, averageDir );
2700 if ( samples <= 0.0f ) {
2704 /* dark lightmap seams */
2706 if ( lightmapNum == 0 ) {
2707 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2713 if ( filterColor ) {
2714 VectorDivide( averageColor, samples, luxel );
2718 VectorDivide( averageDir, samples, deluxel );
2721 /* set cluster to -3 */
2722 if ( *cluster < 0 ) {
2723 *cluster = CLUSTER_FLOODED;
2733 IlluminateVertexes()
2734 light the surface vertexes
2737 #define VERTEX_NUDGE 4.0f
2739 void IlluminateVertexes( int num ){
2740 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2741 int lightmapNum, numAvg;
2742 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2743 vec3_t temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2744 bspDrawSurface_t *ds;
2745 surfaceInfo_t *info;
2747 bspDrawVert_t *verts;
2749 float floodLightAmount;
2753 /* get surface, info, and raw lightmap */
2754 ds = &bspDrawSurfaces[ num ];
2755 info = &surfaceInfos[ num ];
2758 /* -----------------------------------------------------------------
2759 illuminate the vertexes
2760 ----------------------------------------------------------------- */
2762 /* calculate vertex lighting for surfaces without lightmaps */
2763 if ( lm == NULL || cpmaHack ) {
2765 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2766 trace.forceSunlight = info->si->forceSunlight;
2767 trace.recvShadows = info->recvShadows;
2768 trace.numSurfaces = 1;
2769 trace.surfaces = #
2770 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2772 /* twosided lighting */
2773 trace.twoSided = info->si->twoSided;
2775 /* make light list for this surface */
2776 CreateTraceLightsForSurface( num, &trace );
2779 verts = yDrawVerts + ds->firstVert;
2781 memset( avgColors, 0, sizeof( avgColors ) );
2783 /* walk the surface verts */
2784 for ( i = 0; i < ds->numVerts; i++ )
2786 /* get vertex luxel */
2787 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2789 /* color the luxel with raw lightmap num? */
2790 if ( debugSurfaces ) {
2791 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2794 /* color the luxel with luxel origin? */
2795 else if ( debugOrigin ) {
2796 VectorSubtract( info->maxs, info->mins, temp );
2797 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2798 VectorSubtract( verts[ i ].xyz, info->mins, temp2 );
2799 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2800 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2801 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2804 /* color the luxel with the normal */
2805 else if ( normalmap ) {
2806 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2807 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2808 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2811 /* illuminate the vertex */
2814 /* clear vertex luxel */
2815 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2817 /* try at initial origin */
2818 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2819 if ( trace.cluster >= 0 ) {
2821 VectorCopy( verts[ i ].xyz, trace.origin );
2822 VectorCopy( verts[ i ].normal, trace.normal );
2825 if ( dirty && !bouncing ) {
2826 dirt = DirtForSample( &trace );
2832 /* jal: floodlight */
2833 floodLightAmount = 0.0f;
2834 VectorClear( floodColor );
2835 if ( floodlighty && !bouncing ) {
2836 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2837 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2841 LightingAtSample( &trace, ds->vertexStyles, colors );
2844 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2847 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2849 /* jal: floodlight */
2850 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2853 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2854 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2855 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2859 /* is this sample bright enough? */
2860 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2861 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2862 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2863 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2864 /* nudge the sample point around a bit */
2865 for ( x = 0; x < 5; x++ )
2867 /* two's complement 0, 1, -1, 2, -2, etc */
2868 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2870 for ( y = 0; y < 5; y++ )
2872 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2874 for ( z = 0; z < 5; z++ )
2876 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2879 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2880 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2881 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2883 /* try at nudged origin */
2884 trace.cluster = ClusterForPointExtFilter( trace.origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2885 if ( trace.cluster < 0 ) {
2890 if ( dirty && !bouncing ) {
2891 dirt = DirtForSample( &trace );
2897 /* jal: floodlight */
2898 floodLightAmount = 0.0f;
2899 VectorClear( floodColor );
2900 if ( floodlighty && !bouncing ) {
2901 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2902 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2906 LightingAtSample( &trace, ds->vertexStyles, colors );
2909 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2912 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2914 /* jal: floodlight */
2915 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2918 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2919 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2922 /* bright enough? */
2923 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2924 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2925 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2926 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2934 /* add to average? */
2935 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2936 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2937 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2938 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2940 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2942 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2943 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2948 /* another happy customer */
2949 numVertsIlluminated++;
2952 /* set average color */
2954 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2955 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2959 VectorCopy( ambientColor, avgColors[ 0 ] );
2962 /* clean up and store vertex color */
2963 for ( i = 0; i < ds->numVerts; i++ )
2965 /* get vertex luxel */
2966 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2968 /* store average in occluded vertexes */
2969 if ( radVertLuxel[ 0 ] < 0.0f ) {
2970 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2972 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2973 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2976 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2981 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2984 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2985 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2988 if ( bouncing || bounce == 0 || !bounceOnly ) {
2989 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2991 if ( !info->si->noVertexLight ) {
2992 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2997 /* free light list */
2998 FreeTraceLights( &trace );
3000 /* return to sender */
3004 /* -----------------------------------------------------------------
3005 reconstitute vertex lighting from the luxels
3006 ----------------------------------------------------------------- */
3008 /* set styles from lightmap */
3009 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3010 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3012 /* get max search radius */
3014 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3016 /* walk the surface verts */
3017 verts = yDrawVerts + ds->firstVert;
3018 for ( i = 0; i < ds->numVerts; i++ )
3020 /* do each lightmap */
3021 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3024 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3028 /* get luxel coords */
3029 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3030 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3034 else if ( x >= lm->sw ) {
3040 else if ( y >= lm->sh ) {
3044 /* get vertex luxels */
3045 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3046 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3048 /* color the luxel with the normal? */
3050 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3051 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3052 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3055 /* color the luxel with surface num? */
3056 else if ( debugSurfaces ) {
3057 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3060 /* divine color from the superluxels */
3063 /* increasing radius */
3064 VectorClear( radVertLuxel );
3066 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3068 /* sample within radius */
3069 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3071 if ( sy < 0 || sy >= lm->sh ) {
3075 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3077 if ( sx < 0 || sx >= lm->sw ) {
3081 /* get luxel particulars */
3082 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3083 cluster = SUPER_CLUSTER( sx, sy );
3084 if ( *cluster < 0 ) {
3088 /* testing: must be brigher than ambient color */
3089 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3092 /* add its distinctiveness to our own */
3093 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3094 samples += luxel[ 3 ];
3100 if ( samples > 0.0f ) {
3101 VectorDivide( radVertLuxel, samples, radVertLuxel );
3104 VectorCopy( ambientColor, radVertLuxel );
3108 /* store into floating point storage */
3109 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3110 numVertsIlluminated++;
3112 /* store into bytes (for vertex approximation) */
3113 if ( !info->si->noVertexLight ) {
3114 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3122 /* -------------------------------------------------------------------------------
3124 light optimization (-fast)
3126 creates a list of lights that will affect a surface and stores it in tw
3127 this is to optimize surface lighting by culling out as many of the
3128 lights in the world as possible from further calculation
3130 ------------------------------------------------------------------------------- */
3134 determines opaque brushes in the world and find sky shaders for sunlight calculations
3137 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3139 unsigned int compileFlags, allCompileFlags;
3142 bspBrushSide_t *side;
3143 bspShader_t *shader;
3148 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3151 if ( opaqueBrushes == NULL ) {
3152 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3156 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3157 numOpaqueBrushes = 0;
3159 /* walk the list of worldspawn brushes */
3160 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3163 b = bspModels[ 0 ].firstBSPBrush + i;
3164 brush = &bspBrushes[ b ];
3166 /* check all sides */
3169 allCompileFlags = ~( 0u );
3170 for ( j = 0; j < brush->numSides && inside; j++ )
3172 /* do bsp shader calculations */
3173 side = &bspBrushSides[ brush->firstSide + j ];
3174 shader = &bspShaders[ side->shaderNum ];
3176 /* get shader info */
3177 si = ShaderInfoForShaderNull( shader->shader );
3182 /* or together compile flags */
3183 compileFlags |= si->compileFlags;
3184 allCompileFlags &= si->compileFlags;
3187 /* determine if this brush is opaque to light */
3188 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3189 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3195 /* emit some statistics */
3196 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3198 void SetupBrushes( void ){
3199 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3206 determines if two clusters are visible to each other using the PVS
3209 qboolean ClusterVisible( int a, int b ){
3215 if ( a < 0 || b < 0 ) {
3225 if ( numBSPVisBytes <= 8 ) {
3230 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3231 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3232 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3235 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3245 borrowed from vlight.c
3248 int PointInLeafNum_r( vec3_t point, int nodenum ){
3255 while ( nodenum >= 0 )
3257 node = &bspNodes[ nodenum ];
3258 plane = &bspPlanes[ node->planeNum ];
3259 dist = DotProduct( point, plane->normal ) - plane->dist;
3261 nodenum = node->children[ 0 ];
3263 else if ( dist < -0.1 ) {
3264 nodenum = node->children[ 1 ];
3268 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3269 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3272 nodenum = node->children[ 1 ];
3276 leafnum = -nodenum - 1;
3284 borrowed from vlight.c
3287 int PointInLeafNum( vec3_t point ){
3288 return PointInLeafNum_r( point, 0 );
3294 ClusterVisibleToPoint() - ydnar
3295 returns qtrue if point can "see" cluster
3298 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3302 /* get leafNum for point */
3303 pointCluster = ClusterForPoint( point );
3304 if ( pointCluster < 0 ) {
3309 return ClusterVisible( pointCluster, cluster );
3315 ClusterForPoint() - ydnar
3316 returns the pvs cluster for point
3319 int ClusterForPoint( vec3_t point ){
3323 /* get leafNum for point */
3324 leafNum = PointInLeafNum( point );
3325 if ( leafNum < 0 ) {
3329 /* return the cluster */
3330 return bspLeafs[ leafNum ].cluster;
3336 ClusterForPointExt() - ydnar
3337 also takes brushes into account for occlusion testing
3340 int ClusterForPointExt( vec3_t point, float epsilon ){
3341 int i, j, b, leafNum, cluster;
3344 int *brushes, numBSPBrushes;
3350 /* get leaf for point */
3351 leafNum = PointInLeafNum( point );
3352 if ( leafNum < 0 ) {
3355 leaf = &bspLeafs[ leafNum ];
3357 /* get the cluster */
3358 cluster = leaf->cluster;
3359 if ( cluster < 0 ) {
3363 /* transparent leaf, so check point against all brushes in the leaf */
3364 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3365 numBSPBrushes = leaf->numBSPLeafBrushes;
3366 for ( i = 0; i < numBSPBrushes; i++ )
3370 if ( b > maxOpaqueBrush ) {
3373 brush = &bspBrushes[ b ];
3374 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3378 /* check point against all planes */
3380 for ( j = 0; j < brush->numSides && inside; j++ )
3382 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3383 dot = DotProduct( point, plane->normal );
3385 if ( dot > epsilon ) {
3390 /* if inside, return bogus cluster */
3396 /* if the point made it this far, it's not inside any opaque brushes */
3403 ClusterForPointExtFilter() - ydnar
3404 adds cluster checking against a list of known valid clusters
3407 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3411 /* get cluster for point */
3412 cluster = ClusterForPointExt( point, epsilon );
3414 /* check if filtering is necessary */
3415 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3420 for ( i = 0; i < numClusters; i++ )
3422 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3434 ShaderForPointInLeaf() - ydnar
3435 checks a point against all brushes in a leaf, returning the shader of the brush
3436 also sets the cumulative surface and content flags for the brush hit
3439 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3443 int *brushes, numBSPBrushes;
3446 bspBrushSide_t *side;
3448 bspShader_t *shader;
3449 int allSurfaceFlags, allContentFlags;
3452 /* clear things out first */
3457 if ( leafNum < 0 ) {
3460 leaf = &bspLeafs[ leafNum ];
3462 /* transparent leaf, so check point against all brushes in the leaf */
3463 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3464 numBSPBrushes = leaf->numBSPLeafBrushes;
3465 for ( i = 0; i < numBSPBrushes; i++ )
3468 brush = &bspBrushes[ brushes[ i ] ];
3470 /* check point against all planes */
3472 allSurfaceFlags = 0;
3473 allContentFlags = 0;
3474 for ( j = 0; j < brush->numSides && inside; j++ )
3476 side = &bspBrushSides[ brush->firstSide + j ];
3477 plane = &bspPlanes[ side->planeNum ];
3478 dot = DotProduct( point, plane->normal );
3480 if ( dot > epsilon ) {
3485 shader = &bspShaders[ side->shaderNum ];
3486 allSurfaceFlags |= shader->surfaceFlags;
3487 allContentFlags |= shader->contentFlags;
3491 /* handle if inside */
3493 /* if there are desired flags, check for same and continue if they aren't matched */
3494 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3497 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3501 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3502 *surfaceFlags = allSurfaceFlags;
3503 *contentFlags = allContentFlags;
3504 return brush->shaderNum;
3508 /* if the point made it this far, it's not inside any brushes */
3516 chops a bounding box by the plane defined by origin and normal
3517 returns qfalse if the bounds is entirely clipped away
3519 this is not exactly the fastest way to do this...
3522 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3523 /* FIXME: rewrite this so it doesn't use bloody brushes */
3531 calculates each light's effective envelope,
3532 taking into account brightness, type, and pvs.
3535 #define LIGHT_EPSILON 0.125f
3536 #define LIGHT_NUDGE 2.0f
3538 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3539 int i, x, y, z, x1, y1, z1;
3540 light_t *light, *light2, **owner;
3542 vec3_t origin, dir, mins, maxs;
3543 float radius, intensity;
3544 light_t *buckets[ 256 ];
3547 /* early out for weird cases where there are no lights */
3548 if ( lights == NULL ) {
3553 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3557 numCulledLights = 0;
3559 while ( *owner != NULL )
3564 /* handle negative lights */
3565 if ( light->photons < 0.0f || light->add < 0.0f ) {
3566 light->photons *= -1.0f;
3567 light->add *= -1.0f;
3568 light->flags |= LIGHT_NEGATIVE;
3572 if ( light->type == EMIT_SUN ) {
3575 light->envelope = MAX_WORLD_COORD * 8.0f;
3576 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3577 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3580 /* everything else */
3583 /* get pvs cluster for light */
3584 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3586 /* invalid cluster? */
3587 if ( light->cluster < 0 ) {
3588 /* nudge the sample point around a bit */
3589 for ( x = 0; x < 4; x++ )
3591 /* two's complement 0, 1, -1, 2, -2, etc */
3592 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3594 for ( y = 0; y < 4; y++ )
3596 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3598 for ( z = 0; z < 4; z++ )
3600 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3603 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3604 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3605 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3607 /* try at nudged origin */
3608 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3609 if ( light->cluster < 0 ) {
3614 VectorCopy( origin, light->origin );
3620 /* only calculate for lights in pvs and outside of opaque brushes */
3621 if ( light->cluster >= 0 ) {
3622 /* set light fast flag */
3624 light->flags |= LIGHT_FAST_TEMP;
3627 light->flags &= ~LIGHT_FAST_TEMP;
3629 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3630 light->flags |= LIGHT_FAST_TEMP;
3632 if ( light->si && light->si->noFast ) {
3633 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3636 /* clear light envelope */
3637 light->envelope = 0;
3639 /* handle area lights */
3640 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3641 light->envelope = MAX_WORLD_COORD * 8.0f;
3643 /* check for fast mode */
3644 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3645 /* ugly hack to calculate extent for area lights, but only done once */
3646 VectorScale( light->normal, -1.0f, dir );
3647 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3651 VectorMA( light->origin, radius, light->normal, origin );
3652 factor = PointToPolygonFormFactor( origin, dir, light->w );
3653 if ( factor < 0.0f ) {
3656 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3657 light->envelope = radius;
3663 intensity = light->photons; /* hopefully not used */
3668 intensity = light->photons;
3672 if ( light->envelope <= 0.0f ) {
3673 /* solve distance for non-distance lights */
3674 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3675 light->envelope = MAX_WORLD_COORD * 8.0f;
3678 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3679 /* solve distance for linear lights */
3680 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3681 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3685 add = angle * light->photons * linearScale - (dist * light->fade);
3686 T = (light->photons * linearScale) - (dist * light->fade);
3687 T + (dist * light->fade) = (light->photons * linearScale);
3688 dist * light->fade = (light->photons * linearScale) - T;
3689 dist = ((light->photons * linearScale) - T) / light->fade;
3692 /* solve for inverse square falloff */
3694 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3698 add = light->photons / (dist * dist);
3699 T = light->photons / (dist * dist);
3700 T * (dist * dist) = light->photons;
3701 dist = sqrt( light->photons / T );
3706 /* solve distance for linear lights */
3707 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3708 light->envelope = ( intensity * linearScale ) / light->fade;
3711 /* can't cull these */
3713 light->envelope = MAX_WORLD_COORD * 8.0f;
3718 /* chop radius against pvs */
3721 ClearBounds( mins, maxs );
3723 /* check all leaves */
3724 for ( i = 0; i < numBSPLeafs; i++ )
3727 leaf = &bspLeafs[ i ];
3730 if ( leaf->cluster < 0 ) {
3733 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3737 /* add this leafs bbox to the bounds */
3738 VectorCopy( leaf->mins, origin );
3739 AddPointToBounds( origin, mins, maxs );
3740 VectorCopy( leaf->maxs, origin );
3741 AddPointToBounds( origin, mins, maxs );
3744 /* test to see if bounds encompass light */
3745 for ( i = 0; i < 3; i++ )
3747 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3748 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3749 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3750 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3751 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3752 AddPointToBounds( light->origin, mins, maxs );
3756 /* chop the bounds by a plane for area lights and spotlights */
3757 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3758 ChopBounds( mins, maxs, light->origin, light->normal );
3762 VectorCopy( mins, light->mins );
3763 VectorCopy( maxs, light->maxs );
3765 /* reflect bounds around light origin */
3766 //% VectorMA( light->origin, -1.0f, origin, origin );
3767 VectorScale( light->origin, 2, origin );
3768 VectorSubtract( origin, maxs, origin );
3769 AddPointToBounds( origin, mins, maxs );
3770 //% VectorMA( light->origin, -1.0f, mins, origin );
3771 VectorScale( light->origin, 2, origin );
3772 VectorSubtract( origin, mins, origin );
3773 AddPointToBounds( origin, mins, maxs );
3775 /* calculate spherical bounds */
3776 VectorSubtract( maxs, light->origin, dir );
3777 radius = (float) VectorLength( dir );
3779 /* if this radius is smaller than the envelope, then set the envelope to it */
3780 if ( radius < light->envelope ) {
3781 light->envelope = radius;
3782 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3785 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3788 /* add grid/surface only check */
3790 if ( !( light->flags & LIGHT_GRID ) ) {
3791 light->envelope = 0.0f;
3796 if ( !( light->flags & LIGHT_SURFACES ) ) {
3797 light->envelope = 0.0f;
3803 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3805 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3807 /* delete the light */
3809 *owner = light->next;
3810 if ( light->w != NULL ) {
3818 /* square envelope */
3819 light->envelope2 = ( light->envelope * light->envelope );
3821 /* increment light count */
3824 /* set next light */
3825 owner = &( ( **owner ).next );
3828 /* bucket sort lights by style */
3829 memset( buckets, 0, sizeof( buckets ) );
3831 for ( light = lights; light != NULL; light = light2 )
3833 /* get next light */
3834 light2 = light->next;
3836 /* filter into correct bucket */
3837 light->next = buckets[ light->style ];
3838 buckets[ light->style ] = light;
3840 /* if any styled light is present, automatically set nocollapse */
3841 if ( light->style != LS_NORMAL ) {
3846 /* filter back into light list */
3848 for ( i = 255; i >= 0; i-- )
3851 for ( light = buckets[ i ]; light != NULL; light = light2 )
3853 light2 = light->next;
3854 light->next = lights;
3859 /* emit some statistics */
3860 Sys_Printf( "%9d total lights\n", numLights );
3861 Sys_Printf( "%9d culled lights\n", numCulledLights );
3867 CreateTraceLightsForBounds()
3868 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3871 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3874 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3875 float radius, dist, length;
3878 /* potential pre-setup */
3879 if ( numLights == 0 ) {
3880 SetupEnvelopes( qfalse, fast );
3884 //% 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 ] );
3886 /* allocate the light list */
3887 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3888 trace->numLights = 0;
3890 /* calculate spherical bounds */
3891 VectorAdd( mins, maxs, origin );
3892 VectorScale( origin, 0.5f, origin );
3893 VectorSubtract( maxs, origin, dir );
3894 radius = (float) VectorLength( dir );
3896 /* get length of normal vector */
3897 if ( normal != NULL ) {
3898 length = VectorLength( normal );
3902 normal = nullVector;
3906 /* test each light and see if it reaches the sphere */
3907 /* note: the attenuation code MUST match LightingAtSample() */
3908 for ( light = lights; light; light = light->next )
3910 /* check zero sized envelope */
3911 if ( light->envelope <= 0 ) {
3912 lightsEnvelopeCulled++;
3917 if ( !( light->flags & flags ) ) {
3921 /* sunlight skips all this nonsense */
3922 if ( light->type != EMIT_SUN ) {
3928 /* check against pvs cluster */
3929 if ( numClusters > 0 && clusters != NULL ) {
3930 for ( i = 0; i < numClusters; i++ )
3932 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3938 if ( i == numClusters ) {
3939 lightsClusterCulled++;
3944 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3945 VectorSubtract( light->origin, origin, dir );
3946 dist = VectorLength( dir );
3947 dist -= light->envelope;
3950 lightsEnvelopeCulled++;
3954 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3957 for ( i = 0; i < 3; i++ )
3959 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3964 lightsBoundsCulled++;
3970 /* planar surfaces (except twosided surfaces) have a couple more checks */
3971 if ( length > 0.0f && trace->twoSided == qfalse ) {
3972 /* lights coplanar with a surface won't light it */
3973 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3974 lightsPlaneCulled++;
3978 /* check to see if light is behind the plane */
3979 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3980 lightsPlaneCulled++;
3985 /* add this light */
3986 trace->lights[ trace->numLights++ ] = light;
3989 /* make last night null */
3990 trace->lights[ trace->numLights ] = NULL;
3995 void FreeTraceLights( trace_t *trace ){
3996 if ( trace->lights != NULL ) {
3997 free( trace->lights );
4004 CreateTraceLightsForSurface()
4005 creates a list of lights that can potentially affect a drawsurface
4008 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4010 vec3_t mins, maxs, normal;
4012 bspDrawSurface_t *ds;
4013 surfaceInfo_t *info;
4021 /* get drawsurface and info */
4022 ds = &bspDrawSurfaces[ num ];
4023 info = &surfaceInfos[ num ];
4025 /* get the mins/maxs for the dsurf */
4026 ClearBounds( mins, maxs );
4027 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4028 for ( i = 0; i < ds->numVerts; i++ )
4030 dv = &yDrawVerts[ ds->firstVert + i ];
4031 AddPointToBounds( dv->xyz, mins, maxs );
4032 if ( !VectorCompare( dv->normal, normal ) ) {
4033 VectorClear( normal );
4037 /* create the lights for the bounding box */
4038 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4041 /////////////////////////////////////////////////////////////
4043 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4044 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4045 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4046 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4048 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4049 static int numFloodVectors = 0;
4051 void SetupFloodLight( void ){
4053 float angle, elevation, angleStep, elevationStep;
4055 double v1,v2,v3,v4,v5,v6;
4058 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4060 /* calculate angular steps */
4061 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4062 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4066 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4068 /* iterate elevation */
4069 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4071 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4072 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4073 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4078 /* emit some statistics */
4079 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4082 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4084 if ( value[ 0 ] != '\0' ) {
4086 v4 = floodlightDistance;
4087 v5 = floodlightIntensity;
4088 v6 = floodlightDirectionScale;
4090 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4092 floodlightRGB[0] = v1;
4093 floodlightRGB[1] = v2;
4094 floodlightRGB[2] = v3;
4096 if ( VectorLength( floodlightRGB ) == 0 ) {
4097 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4110 floodlightDistance = v4;
4111 floodlightIntensity = v5;
4112 floodlightDirectionScale = v6;
4114 floodlighty = qtrue;
4115 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4119 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4122 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4123 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4124 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4126 ColorNormalize( floodlightRGB,floodlightRGB );
4130 FloodLightForSample()
4131 calculates floodlight value for a given sample
4132 once again, kudos to the dirtmapping coder
4135 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4140 float gatherLight, outLight;
4141 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4149 if ( trace == NULL || trace->cluster < 0 ) {
4155 dd = floodLightDistance;
4156 VectorCopy( trace->normal, normal );
4158 /* check if the normal is aligned to the world-up */
4159 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4160 if ( normal[ 2 ] == 1.0f ) {
4161 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4162 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4164 else if ( normal[ 2 ] == -1.0f ) {
4165 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4166 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4171 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4172 CrossProduct( normal, worldUp, myRt );
4173 VectorNormalize( myRt, myRt );
4174 CrossProduct( myRt, normal, myUp );
4175 VectorNormalize( myUp, myUp );
4178 /* vortex: optimise floodLightLowQuality a bit */
4179 if ( floodLightLowQuality == qtrue ) {
4180 /* iterate through ordered vectors */
4181 for ( i = 0; i < numFloodVectors; i++ )
4182 if ( rand() % 10 != 0 ) {
4188 /* iterate through ordered vectors */
4189 for ( i = 0; i < numFloodVectors; i++ )
4193 /* transform vector into tangent space */
4194 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4195 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4196 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4199 VectorMA( trace->origin, dd, direction, trace->end );
4201 //VectorMA( trace->origin, 1, direction, trace->origin );
4203 SetupTrace( trace );
4204 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4209 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4210 contribution = 1.0f;
4212 else if ( trace->opaque ) {
4213 VectorSubtract( trace->hit, trace->origin, displacement );
4214 d = VectorLength( displacement );
4216 // d=trace->distance;
4217 //if (d>256) gatherDirt+=1;
4218 contribution = d / dd;
4219 if ( contribution > 1 ) {
4220 contribution = 1.0f;
4223 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4226 gatherLight += contribution;
4231 if ( gatherLight <= 0.0f ) {
4240 gatherLight /= ( sub );
4242 outLight = gatherLight;
4243 if ( outLight > 1.0f ) {
4247 /* return to sender */
4252 FloodLightRawLightmap
4253 lighttracer style ambient occlusion light hack.
4254 Kudos to the dirtmapping author for most of this source.
4255 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4256 VorteX: fixed problems with deluxemapping
4259 // floodlight pass on a lightmap
4260 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4261 int i, x, y, *cluster;
4262 float *origin, *normal, *floodlight, floodLightAmount;
4263 surfaceInfo_t *info;
4266 // float samples, average, *floodlight2;
4268 memset( &trace,0,sizeof( trace_t ) );
4271 trace.testOcclusion = qtrue;
4272 trace.forceSunlight = qfalse;
4273 trace.twoSided = qtrue;
4274 trace.recvShadows = lm->recvShadows;
4275 trace.numSurfaces = lm->numLightSurfaces;
4276 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4277 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4278 trace.testAll = qfalse;
4279 trace.distance = 1024;
4281 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4282 //trace.twoSided = qfalse;
4283 for ( i = 0; i < trace.numSurfaces; i++ )
4286 info = &surfaceInfos[ trace.surfaces[ i ] ];
4288 /* check twosidedness */
4289 if ( info->si->twoSided ) {
4290 trace.twoSided = qtrue;
4295 /* gather floodlight */
4296 for ( y = 0; y < lm->sh; y++ )
4298 for ( x = 0; x < lm->sw; x++ )
4301 cluster = SUPER_CLUSTER( x, y );
4302 origin = SUPER_ORIGIN( x, y );
4303 normal = SUPER_NORMAL( x, y );
4304 floodlight = SUPER_FLOODLIGHT( x, y );
4306 /* set default dirt */
4309 /* only look at mapped luxels */
4310 if ( *cluster < 0 ) {
4315 trace.cluster = *cluster;
4316 VectorCopy( origin, trace.origin );
4317 VectorCopy( normal, trace.normal );
4319 /* get floodlight */
4320 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4322 /* add floodlight */
4323 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4324 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4325 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4326 floodlight[3] += floodlightDirectionScale;
4330 /* testing no filtering */
4336 for ( y = 0; y < lm->sh; y++ )
4338 for ( x = 0; x < lm->sw; x++ )
4341 cluster = SUPER_CLUSTER( x, y );
4342 floodlight = SUPER_FLOODLIGHT( x, y );
4344 /* filter dirt by adjacency to unmapped luxels */
4345 average = *floodlight;
4347 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4349 if ( sy < 0 || sy >= lm->sh ) {
4353 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4355 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4359 /* get neighboring luxel */
4360 cluster = SUPER_CLUSTER( sx, sy );
4361 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4362 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4367 average += *floodlight2;
4372 if ( samples <= 0.0f ) {
4378 if ( samples <= 0.0f ) {
4383 *floodlight = average / samples;
4389 void FloodLightRawLightmap( int rawLightmapNum ){
4392 /* bail if this number exceeds the number of raw lightmaps */
4393 if ( rawLightmapNum >= numRawLightmaps ) {
4397 lm = &rawLightmaps[ rawLightmapNum ];
4400 if ( floodlighty && floodlightIntensity ) {
4401 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4405 if ( lm->floodlightIntensity ) {
4406 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4407 numSurfacesFloodlighten += 1;
4411 void FloodlightRawLightmaps(){
4412 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4413 numSurfacesFloodlighten = 0;
4414 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4415 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4419 FloodLightIlluminate()
4420 illuminate floodlight into lightmap luxels
4423 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4424 float *luxel, *floodlight, *deluxel, *normal;
4427 int x, y, lightmapNum;
4429 /* walk lightmaps */
4430 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4433 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4437 if( lm->styles[lightmapNum] != LS_NORMAL && lm->styles[lightmapNum] != LS_NONE ) // isStyleLight
4440 /* apply floodlight to each luxel */
4441 for ( y = 0; y < lm->sh; y++ )
4443 for ( x = 0; x < lm->sw; x++ )
4445 /* get floodlight */
4446 floodlight = SUPER_FLOODLIGHT( x, y );
4447 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4452 cluster = SUPER_CLUSTER( x, y );
4454 /* only process mapped luxels */
4455 if ( *cluster < 0 ) {
4459 /* get particulars */
4460 luxel = SUPER_LUXEL( lightmapNum, x, y );
4461 deluxel = SUPER_DELUXEL( x, y );
4463 /* add to lightmap */
4464 luxel[0] += floodlight[0];
4465 luxel[1] += floodlight[1];
4466 luxel[2] += floodlight[2];
4468 if ( luxel[3] == 0 ) {
4472 /* add to deluxemap */
4473 if ( deluxemap && floodlight[3] > 0 ) {
4476 normal = SUPER_NORMAL( x, y );
4477 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4479 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4480 if ( brightness < 0.00390625f ) {
4481 brightness = 0.00390625f;
4484 VectorScale( normal, brightness, lightvector );
4485 VectorAdd( deluxel, lightvector, deluxel );