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 = superSample * filterRadius / lm->sampleSize;
2223 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2224 luxelFilterRadius = 1;
2227 /* allocate sampling flags storage */
2228 if ( lightSamples > 1 || lightRandomSamples ) {
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 ) ) {
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 ) {
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 else if ( info->si->noVertexLight ) {
2812 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
2815 else if ( noVertexLighting > 0 ) {
2816 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
2819 /* illuminate the vertex */
2822 /* clear vertex luxel */
2823 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2825 /* try at initial origin */
2826 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2827 if ( trace.cluster >= 0 ) {
2829 VectorCopy( verts[ i ].xyz, trace.origin );
2830 VectorCopy( verts[ i ].normal, trace.normal );
2833 if ( dirty && !bouncing ) {
2834 dirt = DirtForSample( &trace );
2840 /* jal: floodlight */
2841 floodLightAmount = 0.0f;
2842 VectorClear( floodColor );
2843 if ( floodlighty && !bouncing ) {
2844 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2845 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2849 LightingAtSample( &trace, ds->vertexStyles, colors );
2852 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2855 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2857 /* jal: floodlight */
2858 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2861 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2862 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2863 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2867 /* is this sample bright enough? */
2868 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2869 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2870 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2871 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2872 /* nudge the sample point around a bit */
2873 for ( x = 0; x < 5; x++ )
2875 /* two's complement 0, 1, -1, 2, -2, etc */
2876 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2878 for ( y = 0; y < 5; y++ )
2880 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2882 for ( z = 0; z < 5; z++ )
2884 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2887 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2888 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2889 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2891 /* try at nudged origin */
2892 trace.cluster = ClusterForPointExtFilter( trace.origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2893 if ( trace.cluster < 0 ) {
2898 if ( dirty && !bouncing ) {
2899 dirt = DirtForSample( &trace );
2905 /* jal: floodlight */
2906 floodLightAmount = 0.0f;
2907 VectorClear( floodColor );
2908 if ( floodlighty && !bouncing ) {
2909 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2910 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2914 LightingAtSample( &trace, ds->vertexStyles, colors );
2917 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2920 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2922 /* jal: floodlight */
2923 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2926 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2927 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2930 /* bright enough? */
2931 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2932 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2933 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2934 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2942 /* add to average? */
2943 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2944 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2945 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2946 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2948 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2950 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2951 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2956 /* another happy customer */
2957 numVertsIlluminated++;
2960 /* set average color */
2962 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2963 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2967 VectorCopy( ambientColor, avgColors[ 0 ] );
2970 /* clean up and store vertex color */
2971 for ( i = 0; i < ds->numVerts; i++ )
2973 /* get vertex luxel */
2974 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2976 /* store average in occluded vertexes */
2977 if ( radVertLuxel[ 0 ] < 0.0f ) {
2978 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2980 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2981 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2984 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2989 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2992 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2993 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2996 if ( bouncing || bounce == 0 || !bounceOnly ) {
2997 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2999 if ( !info->si->noVertexLight ) {
3000 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3005 /* free light list */
3006 FreeTraceLights( &trace );
3008 /* return to sender */
3012 /* -----------------------------------------------------------------
3013 reconstitute vertex lighting from the luxels
3014 ----------------------------------------------------------------- */
3016 /* set styles from lightmap */
3017 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3018 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3020 /* get max search radius */
3022 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3024 /* walk the surface verts */
3025 verts = yDrawVerts + ds->firstVert;
3026 for ( i = 0; i < ds->numVerts; i++ )
3028 /* do each lightmap */
3029 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3032 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3036 /* get luxel coords */
3037 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3038 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3042 else if ( x >= lm->sw ) {
3048 else if ( y >= lm->sh ) {
3052 /* get vertex luxels */
3053 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3054 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3056 /* color the luxel with the normal? */
3058 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3059 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3060 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3063 /* color the luxel with surface num? */
3064 else if ( debugSurfaces ) {
3065 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3068 else if ( info->si->noVertexLight ) {
3069 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
3072 else if ( noVertexLighting > 0 ) {
3073 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
3076 /* divine color from the superluxels */
3079 /* increasing radius */
3080 VectorClear( radVertLuxel );
3082 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3084 /* sample within radius */
3085 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3087 if ( sy < 0 || sy >= lm->sh ) {
3091 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3093 if ( sx < 0 || sx >= lm->sw ) {
3097 /* get luxel particulars */
3098 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3099 cluster = SUPER_CLUSTER( sx, sy );
3100 if ( *cluster < 0 ) {
3104 /* testing: must be brigher than ambient color */
3105 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3108 /* add its distinctiveness to our own */
3109 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3110 samples += luxel[ 3 ];
3116 if ( samples > 0.0f ) {
3117 VectorDivide( radVertLuxel, samples, radVertLuxel );
3120 VectorCopy( ambientColor, radVertLuxel );
3124 /* store into floating point storage */
3125 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3126 numVertsIlluminated++;
3128 /* store into bytes (for vertex approximation) */
3129 if ( !info->si->noVertexLight ) {
3130 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3138 /* -------------------------------------------------------------------------------
3140 light optimization (-fast)
3142 creates a list of lights that will affect a surface and stores it in tw
3143 this is to optimize surface lighting by culling out as many of the
3144 lights in the world as possible from further calculation
3146 ------------------------------------------------------------------------------- */
3150 determines opaque brushes in the world and find sky shaders for sunlight calculations
3153 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3155 unsigned int compileFlags, allCompileFlags;
3158 bspBrushSide_t *side;
3159 bspShader_t *shader;
3164 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3167 if ( opaqueBrushes == NULL ) {
3168 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3172 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3173 numOpaqueBrushes = 0;
3175 /* walk the list of worldspawn brushes */
3176 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3179 b = bspModels[ 0 ].firstBSPBrush + i;
3180 brush = &bspBrushes[ b ];
3182 /* check all sides */
3185 allCompileFlags = ~( 0u );
3186 for ( j = 0; j < brush->numSides && inside; j++ )
3188 /* do bsp shader calculations */
3189 side = &bspBrushSides[ brush->firstSide + j ];
3190 shader = &bspShaders[ side->shaderNum ];
3192 /* get shader info */
3193 si = ShaderInfoForShaderNull( shader->shader );
3198 /* or together compile flags */
3199 compileFlags |= si->compileFlags;
3200 allCompileFlags &= si->compileFlags;
3203 /* determine if this brush is opaque to light */
3204 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3205 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3211 /* emit some statistics */
3212 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3214 void SetupBrushes( void ){
3215 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3222 determines if two clusters are visible to each other using the PVS
3225 qboolean ClusterVisible( int a, int b ){
3231 if ( a < 0 || b < 0 ) {
3241 if ( numBSPVisBytes <= 8 ) {
3246 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3247 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3248 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3251 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3261 borrowed from vlight.c
3264 int PointInLeafNum_r( vec3_t point, int nodenum ){
3271 while ( nodenum >= 0 )
3273 node = &bspNodes[ nodenum ];
3274 plane = &bspPlanes[ node->planeNum ];
3275 dist = DotProduct( point, plane->normal ) - plane->dist;
3277 nodenum = node->children[ 0 ];
3279 else if ( dist < -0.1 ) {
3280 nodenum = node->children[ 1 ];
3284 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3285 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3288 nodenum = node->children[ 1 ];
3292 leafnum = -nodenum - 1;
3300 borrowed from vlight.c
3303 int PointInLeafNum( vec3_t point ){
3304 return PointInLeafNum_r( point, 0 );
3310 ClusterVisibleToPoint() - ydnar
3311 returns qtrue if point can "see" cluster
3314 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3318 /* get leafNum for point */
3319 pointCluster = ClusterForPoint( point );
3320 if ( pointCluster < 0 ) {
3325 return ClusterVisible( pointCluster, cluster );
3331 ClusterForPoint() - ydnar
3332 returns the pvs cluster for point
3335 int ClusterForPoint( vec3_t point ){
3339 /* get leafNum for point */
3340 leafNum = PointInLeafNum( point );
3341 if ( leafNum < 0 ) {
3345 /* return the cluster */
3346 return bspLeafs[ leafNum ].cluster;
3352 ClusterForPointExt() - ydnar
3353 also takes brushes into account for occlusion testing
3356 int ClusterForPointExt( vec3_t point, float epsilon ){
3357 int i, j, b, leafNum, cluster;
3360 int *brushes, numBSPBrushes;
3366 /* get leaf for point */
3367 leafNum = PointInLeafNum( point );
3368 if ( leafNum < 0 ) {
3371 leaf = &bspLeafs[ leafNum ];
3373 /* get the cluster */
3374 cluster = leaf->cluster;
3375 if ( cluster < 0 ) {
3379 /* transparent leaf, so check point against all brushes in the leaf */
3380 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3381 numBSPBrushes = leaf->numBSPLeafBrushes;
3382 for ( i = 0; i < numBSPBrushes; i++ )
3386 if ( b > maxOpaqueBrush ) {
3389 brush = &bspBrushes[ b ];
3390 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3394 /* check point against all planes */
3396 for ( j = 0; j < brush->numSides && inside; j++ )
3398 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3399 dot = DotProduct( point, plane->normal );
3401 if ( dot > epsilon ) {
3406 /* if inside, return bogus cluster */
3412 /* if the point made it this far, it's not inside any opaque brushes */
3419 ClusterForPointExtFilter() - ydnar
3420 adds cluster checking against a list of known valid clusters
3423 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3427 /* get cluster for point */
3428 cluster = ClusterForPointExt( point, epsilon );
3430 /* check if filtering is necessary */
3431 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3436 for ( i = 0; i < numClusters; i++ )
3438 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3450 ShaderForPointInLeaf() - ydnar
3451 checks a point against all brushes in a leaf, returning the shader of the brush
3452 also sets the cumulative surface and content flags for the brush hit
3455 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3459 int *brushes, numBSPBrushes;
3462 bspBrushSide_t *side;
3464 bspShader_t *shader;
3465 int allSurfaceFlags, allContentFlags;
3468 /* clear things out first */
3473 if ( leafNum < 0 ) {
3476 leaf = &bspLeafs[ leafNum ];
3478 /* transparent leaf, so check point against all brushes in the leaf */
3479 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3480 numBSPBrushes = leaf->numBSPLeafBrushes;
3481 for ( i = 0; i < numBSPBrushes; i++ )
3484 brush = &bspBrushes[ brushes[ i ] ];
3486 /* check point against all planes */
3488 allSurfaceFlags = 0;
3489 allContentFlags = 0;
3490 for ( j = 0; j < brush->numSides && inside; j++ )
3492 side = &bspBrushSides[ brush->firstSide + j ];
3493 plane = &bspPlanes[ side->planeNum ];
3494 dot = DotProduct( point, plane->normal );
3496 if ( dot > epsilon ) {
3501 shader = &bspShaders[ side->shaderNum ];
3502 allSurfaceFlags |= shader->surfaceFlags;
3503 allContentFlags |= shader->contentFlags;
3507 /* handle if inside */
3509 /* if there are desired flags, check for same and continue if they aren't matched */
3510 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3513 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3517 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3518 *surfaceFlags = allSurfaceFlags;
3519 *contentFlags = allContentFlags;
3520 return brush->shaderNum;
3524 /* if the point made it this far, it's not inside any brushes */
3532 chops a bounding box by the plane defined by origin and normal
3533 returns qfalse if the bounds is entirely clipped away
3535 this is not exactly the fastest way to do this...
3538 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3539 /* FIXME: rewrite this so it doesn't use bloody brushes */
3547 calculates each light's effective envelope,
3548 taking into account brightness, type, and pvs.
3551 #define LIGHT_EPSILON 0.125f
3552 #define LIGHT_NUDGE 2.0f
3554 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3555 int i, x, y, z, x1, y1, z1;
3556 light_t *light, *light2, **owner;
3558 vec3_t origin, dir, mins, maxs;
3559 float radius, intensity;
3560 light_t *buckets[ 256 ];
3563 /* early out for weird cases where there are no lights */
3564 if ( lights == NULL ) {
3569 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3573 numCulledLights = 0;
3575 while ( *owner != NULL )
3580 /* handle negative lights */
3581 if ( light->photons < 0.0f || light->add < 0.0f ) {
3582 light->photons *= -1.0f;
3583 light->add *= -1.0f;
3584 light->flags |= LIGHT_NEGATIVE;
3588 if ( light->type == EMIT_SUN ) {
3591 light->envelope = MAX_WORLD_COORD * 8.0f;
3592 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3593 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3596 /* everything else */
3599 /* get pvs cluster for light */
3600 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3602 /* invalid cluster? */
3603 if ( light->cluster < 0 ) {
3604 /* nudge the sample point around a bit */
3605 for ( x = 0; x < 4; x++ )
3607 /* two's complement 0, 1, -1, 2, -2, etc */
3608 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3610 for ( y = 0; y < 4; y++ )
3612 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3614 for ( z = 0; z < 4; z++ )
3616 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3619 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3620 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3621 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3623 /* try at nudged origin */
3624 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3625 if ( light->cluster < 0 ) {
3630 VectorCopy( origin, light->origin );
3636 /* only calculate for lights in pvs and outside of opaque brushes */
3637 if ( light->cluster >= 0 ) {
3638 /* set light fast flag */
3640 light->flags |= LIGHT_FAST_TEMP;
3643 light->flags &= ~LIGHT_FAST_TEMP;
3645 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3646 light->flags |= LIGHT_FAST_TEMP;
3648 if ( light->si && light->si->noFast ) {
3649 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3652 /* clear light envelope */
3653 light->envelope = 0;
3655 /* handle area lights */
3656 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3657 light->envelope = MAX_WORLD_COORD * 8.0f;
3659 /* check for fast mode */
3660 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3661 /* ugly hack to calculate extent for area lights, but only done once */
3662 VectorScale( light->normal, -1.0f, dir );
3663 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3667 VectorMA( light->origin, radius, light->normal, origin );
3668 factor = PointToPolygonFormFactor( origin, dir, light->w );
3669 if ( factor < 0.0f ) {
3672 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3673 light->envelope = radius;
3679 intensity = light->photons; /* hopefully not used */
3684 intensity = light->photons;
3688 if ( light->envelope <= 0.0f ) {
3689 /* solve distance for non-distance lights */
3690 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3691 light->envelope = MAX_WORLD_COORD * 8.0f;
3694 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3695 /* solve distance for linear lights */
3696 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3697 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3701 add = angle * light->photons * linearScale - (dist * light->fade);
3702 T = (light->photons * linearScale) - (dist * light->fade);
3703 T + (dist * light->fade) = (light->photons * linearScale);
3704 dist * light->fade = (light->photons * linearScale) - T;
3705 dist = ((light->photons * linearScale) - T) / light->fade;
3708 /* solve for inverse square falloff */
3710 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3714 add = light->photons / (dist * dist);
3715 T = light->photons / (dist * dist);
3716 T * (dist * dist) = light->photons;
3717 dist = sqrt( light->photons / T );
3722 /* solve distance for linear lights */
3723 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3724 light->envelope = ( intensity * linearScale ) / light->fade;
3727 /* can't cull these */
3729 light->envelope = MAX_WORLD_COORD * 8.0f;
3734 /* chop radius against pvs */
3737 ClearBounds( mins, maxs );
3739 /* check all leaves */
3740 for ( i = 0; i < numBSPLeafs; i++ )
3743 leaf = &bspLeafs[ i ];
3746 if ( leaf->cluster < 0 ) {
3749 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3753 /* add this leafs bbox to the bounds */
3754 VectorCopy( leaf->mins, origin );
3755 AddPointToBounds( origin, mins, maxs );
3756 VectorCopy( leaf->maxs, origin );
3757 AddPointToBounds( origin, mins, maxs );
3760 /* test to see if bounds encompass light */
3761 for ( i = 0; i < 3; i++ )
3763 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3764 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3765 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3766 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3767 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3768 AddPointToBounds( light->origin, mins, maxs );
3772 /* chop the bounds by a plane for area lights and spotlights */
3773 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3774 ChopBounds( mins, maxs, light->origin, light->normal );
3778 VectorCopy( mins, light->mins );
3779 VectorCopy( maxs, light->maxs );
3781 /* reflect bounds around light origin */
3782 //% VectorMA( light->origin, -1.0f, origin, origin );
3783 VectorScale( light->origin, 2, origin );
3784 VectorSubtract( origin, maxs, origin );
3785 AddPointToBounds( origin, mins, maxs );
3786 //% VectorMA( light->origin, -1.0f, mins, origin );
3787 VectorScale( light->origin, 2, origin );
3788 VectorSubtract( origin, mins, origin );
3789 AddPointToBounds( origin, mins, maxs );
3791 /* calculate spherical bounds */
3792 VectorSubtract( maxs, light->origin, dir );
3793 radius = (float) VectorLength( dir );
3795 /* if this radius is smaller than the envelope, then set the envelope to it */
3796 if ( radius < light->envelope ) {
3797 light->envelope = radius;
3798 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3801 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3804 /* add grid/surface only check */
3806 if ( !( light->flags & LIGHT_GRID ) ) {
3807 light->envelope = 0.0f;
3812 if ( !( light->flags & LIGHT_SURFACES ) ) {
3813 light->envelope = 0.0f;
3819 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3821 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3823 /* delete the light */
3825 *owner = light->next;
3826 if ( light->w != NULL ) {
3834 /* square envelope */
3835 light->envelope2 = ( light->envelope * light->envelope );
3837 /* increment light count */
3840 /* set next light */
3841 owner = &( ( **owner ).next );
3844 /* bucket sort lights by style */
3845 memset( buckets, 0, sizeof( buckets ) );
3847 for ( light = lights; light != NULL; light = light2 )
3849 /* get next light */
3850 light2 = light->next;
3852 /* filter into correct bucket */
3853 light->next = buckets[ light->style ];
3854 buckets[ light->style ] = light;
3856 /* if any styled light is present, automatically set nocollapse */
3857 if ( light->style != LS_NORMAL ) {
3862 /* filter back into light list */
3864 for ( i = 255; i >= 0; i-- )
3867 for ( light = buckets[ i ]; light != NULL; light = light2 )
3869 light2 = light->next;
3870 light->next = lights;
3875 /* emit some statistics */
3876 Sys_Printf( "%9d total lights\n", numLights );
3877 Sys_Printf( "%9d culled lights\n", numCulledLights );
3883 CreateTraceLightsForBounds()
3884 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3887 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3890 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3891 float radius, dist, length;
3894 /* potential pre-setup */
3895 if ( numLights == 0 ) {
3896 SetupEnvelopes( qfalse, fast );
3900 //% 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 ] );
3902 /* allocate the light list */
3903 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3904 trace->numLights = 0;
3906 /* calculate spherical bounds */
3907 VectorAdd( mins, maxs, origin );
3908 VectorScale( origin, 0.5f, origin );
3909 VectorSubtract( maxs, origin, dir );
3910 radius = (float) VectorLength( dir );
3912 /* get length of normal vector */
3913 if ( normal != NULL ) {
3914 length = VectorLength( normal );
3918 normal = nullVector;
3922 /* test each light and see if it reaches the sphere */
3923 /* note: the attenuation code MUST match LightingAtSample() */
3924 for ( light = lights; light; light = light->next )
3926 /* check zero sized envelope */
3927 if ( light->envelope <= 0 ) {
3928 lightsEnvelopeCulled++;
3933 if ( !( light->flags & flags ) ) {
3937 /* sunlight skips all this nonsense */
3938 if ( light->type != EMIT_SUN ) {
3944 /* check against pvs cluster */
3945 if ( numClusters > 0 && clusters != NULL ) {
3946 for ( i = 0; i < numClusters; i++ )
3948 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3954 if ( i == numClusters ) {
3955 lightsClusterCulled++;
3960 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3961 VectorSubtract( light->origin, origin, dir );
3962 dist = VectorLength( dir );
3963 dist -= light->envelope;
3966 lightsEnvelopeCulled++;
3970 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3973 for ( i = 0; i < 3; i++ )
3975 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3980 lightsBoundsCulled++;
3986 /* planar surfaces (except twosided surfaces) have a couple more checks */
3987 if ( length > 0.0f && trace->twoSided == qfalse ) {
3988 /* lights coplanar with a surface won't light it */
3989 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3990 lightsPlaneCulled++;
3994 /* check to see if light is behind the plane */
3995 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3996 lightsPlaneCulled++;
4001 /* add this light */
4002 trace->lights[ trace->numLights++ ] = light;
4005 /* make last night null */
4006 trace->lights[ trace->numLights ] = NULL;
4011 void FreeTraceLights( trace_t *trace ){
4012 if ( trace->lights != NULL ) {
4013 free( trace->lights );
4020 CreateTraceLightsForSurface()
4021 creates a list of lights that can potentially affect a drawsurface
4024 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4026 vec3_t mins, maxs, normal;
4028 bspDrawSurface_t *ds;
4029 surfaceInfo_t *info;
4037 /* get drawsurface and info */
4038 ds = &bspDrawSurfaces[ num ];
4039 info = &surfaceInfos[ num ];
4041 /* get the mins/maxs for the dsurf */
4042 ClearBounds( mins, maxs );
4043 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4044 for ( i = 0; i < ds->numVerts; i++ )
4046 dv = &yDrawVerts[ ds->firstVert + i ];
4047 AddPointToBounds( dv->xyz, mins, maxs );
4048 if ( !VectorCompare( dv->normal, normal ) ) {
4049 VectorClear( normal );
4053 /* create the lights for the bounding box */
4054 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4057 /////////////////////////////////////////////////////////////
4059 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4060 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4061 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4062 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4064 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4065 static int numFloodVectors = 0;
4067 void SetupFloodLight( void ){
4069 float angle, elevation, angleStep, elevationStep;
4071 double v1,v2,v3,v4,v5,v6;
4074 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4076 /* calculate angular steps */
4077 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4078 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4082 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4084 /* iterate elevation */
4085 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4087 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4088 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4089 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4094 /* emit some statistics */
4095 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4098 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4100 if ( value[ 0 ] != '\0' ) {
4102 v4 = floodlightDistance;
4103 v5 = floodlightIntensity;
4104 v6 = floodlightDirectionScale;
4106 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4108 floodlightRGB[0] = v1;
4109 floodlightRGB[1] = v2;
4110 floodlightRGB[2] = v3;
4112 if ( VectorLength( floodlightRGB ) == 0 ) {
4113 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4126 floodlightDistance = v4;
4127 floodlightIntensity = v5;
4128 floodlightDirectionScale = v6;
4130 floodlighty = qtrue;
4131 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4135 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4138 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4139 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4140 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4142 ColorNormalize( floodlightRGB,floodlightRGB );
4146 FloodLightForSample()
4147 calculates floodlight value for a given sample
4148 once again, kudos to the dirtmapping coder
4151 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4156 float gatherLight, outLight;
4157 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4165 if ( trace == NULL || trace->cluster < 0 ) {
4171 dd = floodLightDistance;
4172 VectorCopy( trace->normal, normal );
4174 /* check if the normal is aligned to the world-up */
4175 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4176 if ( normal[ 2 ] == 1.0f ) {
4177 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4178 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4180 else if ( normal[ 2 ] == -1.0f ) {
4181 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4182 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4187 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4188 CrossProduct( normal, worldUp, myRt );
4189 VectorNormalize( myRt, myRt );
4190 CrossProduct( myRt, normal, myUp );
4191 VectorNormalize( myUp, myUp );
4194 /* vortex: optimise floodLightLowQuality a bit */
4195 if ( floodLightLowQuality == qtrue ) {
4196 /* iterate through ordered vectors */
4197 for ( i = 0; i < numFloodVectors; i++ )
4198 if ( rand() % 10 != 0 ) {
4204 /* iterate through ordered vectors */
4205 for ( i = 0; i < numFloodVectors; i++ )
4209 /* transform vector into tangent space */
4210 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4211 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4212 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4215 VectorMA( trace->origin, dd, direction, trace->end );
4217 //VectorMA( trace->origin, 1, direction, trace->origin );
4219 SetupTrace( trace );
4220 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4225 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4226 contribution = 1.0f;
4228 else if ( trace->opaque ) {
4229 VectorSubtract( trace->hit, trace->origin, displacement );
4230 d = VectorLength( displacement );
4232 // d=trace->distance;
4233 //if (d>256) gatherDirt+=1;
4234 contribution = d / dd;
4235 if ( contribution > 1 ) {
4236 contribution = 1.0f;
4239 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4242 gatherLight += contribution;
4247 if ( gatherLight <= 0.0f ) {
4256 gatherLight /= ( sub );
4258 outLight = gatherLight;
4259 if ( outLight > 1.0f ) {
4263 /* return to sender */
4268 FloodLightRawLightmap
4269 lighttracer style ambient occlusion light hack.
4270 Kudos to the dirtmapping author for most of this source.
4271 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4272 VorteX: fixed problems with deluxemapping
4275 // floodlight pass on a lightmap
4276 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4277 int i, x, y, *cluster;
4278 float *origin, *normal, *floodlight, floodLightAmount;
4279 surfaceInfo_t *info;
4282 // float samples, average, *floodlight2;
4284 memset( &trace,0,sizeof( trace_t ) );
4287 trace.testOcclusion = qtrue;
4288 trace.forceSunlight = qfalse;
4289 trace.twoSided = qtrue;
4290 trace.recvShadows = lm->recvShadows;
4291 trace.numSurfaces = lm->numLightSurfaces;
4292 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4293 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4294 trace.testAll = qfalse;
4295 trace.distance = 1024;
4297 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4298 //trace.twoSided = qfalse;
4299 for ( i = 0; i < trace.numSurfaces; i++ )
4302 info = &surfaceInfos[ trace.surfaces[ i ] ];
4304 /* check twosidedness */
4305 if ( info->si->twoSided ) {
4306 trace.twoSided = qtrue;
4311 /* gather floodlight */
4312 for ( y = 0; y < lm->sh; y++ )
4314 for ( x = 0; x < lm->sw; x++ )
4317 cluster = SUPER_CLUSTER( x, y );
4318 origin = SUPER_ORIGIN( x, y );
4319 normal = SUPER_NORMAL( x, y );
4320 floodlight = SUPER_FLOODLIGHT( x, y );
4322 /* set default dirt */
4325 /* only look at mapped luxels */
4326 if ( *cluster < 0 ) {
4331 trace.cluster = *cluster;
4332 VectorCopy( origin, trace.origin );
4333 VectorCopy( normal, trace.normal );
4335 /* get floodlight */
4336 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4338 /* add floodlight */
4339 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4340 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4341 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4342 floodlight[3] += floodlightDirectionScale;
4346 /* testing no filtering */
4352 for ( y = 0; y < lm->sh; y++ )
4354 for ( x = 0; x < lm->sw; x++ )
4357 cluster = SUPER_CLUSTER( x, y );
4358 floodlight = SUPER_FLOODLIGHT( x, y );
4360 /* filter dirt by adjacency to unmapped luxels */
4361 average = *floodlight;
4363 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4365 if ( sy < 0 || sy >= lm->sh ) {
4369 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4371 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4375 /* get neighboring luxel */
4376 cluster = SUPER_CLUSTER( sx, sy );
4377 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4378 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4383 average += *floodlight2;
4388 if ( samples <= 0.0f ) {
4394 if ( samples <= 0.0f ) {
4399 *floodlight = average / samples;
4405 void FloodLightRawLightmap( int rawLightmapNum ){
4408 /* bail if this number exceeds the number of raw lightmaps */
4409 if ( rawLightmapNum >= numRawLightmaps ) {
4413 lm = &rawLightmaps[ rawLightmapNum ];
4416 if ( floodlighty && floodlightIntensity ) {
4417 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4421 if ( lm->floodlightIntensity ) {
4422 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4423 numSurfacesFloodlighten += 1;
4427 void FloodlightRawLightmaps(){
4428 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4429 numSurfacesFloodlighten = 0;
4430 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4431 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4435 FloodLightIlluminate()
4436 illuminate floodlight into lightmap luxels
4439 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4440 float *luxel, *floodlight, *deluxel, *normal;
4443 int x, y, lightmapNum;
4445 /* walk lightmaps */
4446 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4449 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4453 if( lm->styles[lightmapNum] != LS_NORMAL && lm->styles[lightmapNum] != LS_NONE ) // isStyleLight
4456 /* apply floodlight to each luxel */
4457 for ( y = 0; y < lm->sh; y++ )
4459 for ( x = 0; x < lm->sw; x++ )
4461 /* get floodlight */
4462 floodlight = SUPER_FLOODLIGHT( x, y );
4463 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4468 cluster = SUPER_CLUSTER( x, y );
4470 /* only process mapped luxels */
4471 if ( *cluster < 0 ) {
4475 /* get particulars */
4476 luxel = SUPER_LUXEL( lightmapNum, x, y );
4477 deluxel = SUPER_DELUXEL( x, y );
4479 /* add to lightmap */
4480 luxel[0] += floodlight[0];
4481 luxel[1] += floodlight[1];
4482 luxel[2] += floodlight[2];
4484 if ( luxel[3] == 0 ) {
4488 /* add to deluxemap */
4489 if ( deluxemap && floodlight[3] > 0 ) {
4492 normal = SUPER_NORMAL( x, y );
4493 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4495 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4496 if ( brightness < 0.00390625f ) {
4497 brightness = 0.00390625f;
4500 VectorScale( normal, brightness, lightvector );
4501 VectorAdd( deluxel, lightvector, deluxel );