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 )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
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 )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 1)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 if (lightmapExposure==0)
92 lightmapExposure=1.0f;
94 inv=1.f/lightmapExposure;
98 if( sample[ 1 ] > max )
100 if( sample[ 2 ] > max )
103 dif = (1- exp(-max * inv) ) * 255;
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, (1.0f / lightmapCompensate), sample );
125 colorBytes[ 0 ] = sample[ 0 ];
126 colorBytes[ 1 ] = sample[ 1 ];
127 colorBytes[ 2 ] = sample[ 2 ];
132 /* -------------------------------------------------------------------------------
134 this section deals with phong shading (normal interpolation across brush faces)
136 ------------------------------------------------------------------------------- */
140 smooths together coincident vertex normals across the bsp
143 #define MAX_SAMPLES 256
144 #define THETA_EPSILON 0.000001
145 #define EQUAL_NORMAL_EPSILON 0.01
147 void SmoothNormals( void )
149 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
150 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151 bspDrawSurface_t *ds;
155 vec3_t average, diff;
156 int indexes[ MAX_SAMPLES ];
157 vec3_t votes[ MAX_SAMPLES ];
160 /* allocate shade angle table */
161 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
164 /* allocate smoothed table */
165 cs = (numBSPDrawVerts / 8) + 1;
166 smoothed = safe_malloc( cs );
167 memset( smoothed, 0, cs );
169 /* set default shade angle */
170 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
173 /* run through every surface and flag verts belonging to non-lightmapped surfaces
174 and set per-vertex smoothing angle */
175 for( i = 0; i < numBSPDrawSurfaces; i++ )
178 ds = &bspDrawSurfaces[ i ];
180 /* get shader for shade angle */
181 si = surfaceInfos[ i ].si;
182 if( si->shadeAngleDegrees )
183 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
185 shadeAngle = defaultShadeAngle;
186 if( shadeAngle > maxShadeAngle )
187 maxShadeAngle = shadeAngle;
190 for( j = 0; j < ds->numVerts; j++ )
192 f = ds->firstVert + j;
193 shadeAngles[ f ] = shadeAngle;
194 if( ds->surfaceType == MST_TRIANGLE_SOUP )
195 smoothed[ f >> 3 ] |= (1 << (f & 7));
198 /* ydnar: optional force-to-trisoup */
199 if( trisoup && ds->surfaceType == MST_PLANAR )
201 ds->surfaceType = MST_TRIANGLE_SOUP;
202 ds->lightmapNum[ 0 ] = -3;
206 /* bail if no surfaces have a shade angle */
207 if( maxShadeAngle == 0 )
216 start = I_FloatTime();
218 /* go through the list of vertexes */
219 for( i = 0; i < numBSPDrawVerts; i++ )
222 f = 10 * i / numBSPDrawVerts;
226 Sys_Printf( "%i...", f );
229 /* already smoothed? */
230 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
234 VectorClear( average );
238 /* build a table of coincident vertexes */
239 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
241 /* already smoothed? */
242 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
246 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
249 /* use smallest shade angle */
250 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
252 /* check shade angle */
253 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
256 else if( dot < -1.0 )
258 testAngle = acos( dot ) + THETA_EPSILON;
259 if( testAngle >= shadeAngle )
261 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
264 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
266 /* add to the list */
267 indexes[ numVerts++ ] = j;
270 smoothed[ j >> 3 ] |= (1 << (j & 7));
272 /* see if this normal has already been voted */
273 for( k = 0; k < numVotes; k++ )
275 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
282 /* add a new vote? */
283 if( k == numVotes && numVotes < MAX_SAMPLES )
285 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
291 /* don't average for less than 2 verts */
296 if( VectorNormalize( average, average ) > 0 )
299 for( j = 0; j < numVerts; j++ )
300 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
304 /* free the tables */
309 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
314 /* -------------------------------------------------------------------------------
316 this section deals with phong shaded lightmap tracing
318 ------------------------------------------------------------------------------- */
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
324 calculates the st tangent vectors for normalmapping
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
334 /* calculate barycentric basis for the triangle */
335 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 ]);
336 if( fabs( bb ) < 0.00000001f )
340 for( i = 0; i < numVerts; i++ )
342 /* calculate s tangent vector */
343 s = dv[ i ]->st[ 0 ] + 10.0f;
344 t = dv[ i ]->st[ 1 ];
345 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
349 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
353 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354 VectorNormalize( stv[ i ], stv[ i ] );
356 /* calculate t tangent vector */
357 s = dv[ i ]->st[ 0 ];
358 t = dv[ i ]->st[ 1 ] + 10.0f;
359 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
363 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
367 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368 VectorNormalize( ttv[ i ], ttv[ i ] );
371 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
375 /* return to caller */
384 perterbs the normal by the shader's normalmap in tangent space
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
394 VectorCopy( dv->normal, pNormal );
396 /* sample normalmap */
397 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
400 /* remap sampled normal from [0,255] to [-1,-1] */
401 for( i = 0; i < 3; i++ )
402 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
404 /* scale tangent vectors and add to original normal */
405 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
409 /* renormalize and return */
410 VectorNormalize( pNormal, pNormal );
417 maps a luxel for triangle bv at
421 #define BOGUS_NUDGE -99999.0f
423 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 ] )
425 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
426 float *luxel, *origin, *normal, d, lightmapSampleOffset;
433 vec4_t sideplane, hostplane;
438 static float nudges[][ 2 ] =
440 //%{ 0, 0 }, /* try center first */
441 { -NUDGE, 0 }, /* left */
442 { NUDGE, 0 }, /* right */
443 { 0, NUDGE }, /* up */
444 { 0, -NUDGE }, /* down */
445 { -NUDGE, NUDGE }, /* left/up */
446 { NUDGE, -NUDGE }, /* right/down */
447 { NUDGE, NUDGE }, /* right/up */
448 { -NUDGE, -NUDGE }, /* left/down */
449 { BOGUS_NUDGE, BOGUS_NUDGE }
453 /* find luxel xy coords (fixme: subtract 0.5?) */
454 x = dv->lightmap[ 0 ][ 0 ];
455 y = dv->lightmap[ 0 ][ 1 ];
458 else if( x >= lm->sw )
462 else if( y >= lm->sh )
465 /* set shader and cluster list */
469 numClusters = info->numSurfaceClusters;
470 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
479 /* get luxel, origin, cluster, and normal */
480 luxel = SUPER_LUXEL( 0, x, y );
481 origin = SUPER_ORIGIN( x, y );
482 normal = SUPER_NORMAL( x, y );
483 cluster = SUPER_CLUSTER( x, y );
485 /* don't attempt to remap occluded luxels for planar surfaces */
486 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
489 /* only average the normal for premapped luxels */
490 else if( (*cluster) >= 0 )
492 /* do bumpmap calculations */
494 PerturbNormal( dv, si, pNormal, stv, ttv );
496 VectorCopy( dv->normal, pNormal );
498 /* add the additional normal data */
499 VectorAdd( normal, pNormal, normal );
504 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
508 /* axial lightmap projection */
509 if( lm->vecs != NULL )
511 /* calculate an origin for the sample from the lightmap vectors */
512 VectorCopy( lm->origin, origin );
513 for( i = 0; i < 3; i++ )
515 /* add unless it's the axis, which is taken care of later */
516 if( i == lm->axisNum )
518 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
521 /* project the origin onto the plane */
522 d = DotProduct( origin, plane ) - plane[ 3 ];
523 d /= plane[ lm->axisNum ];
524 origin[ lm->axisNum ] -= d;
527 /* non axial lightmap projection (explicit xyz) */
529 VectorCopy( dv->xyz, origin );
531 //////////////////////
532 //27's test to make sure samples stay within the triangle boundaries
533 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534 //2) if it does, nudge it onto the correct side.
536 if (worldverts!=NULL && lightmapTriangleCheck)
540 VectorCopy(worldverts[j],cverts[j]);
542 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
548 //build plane using 2 edges and a normal
551 VectorCopy(cverts[next],temp);
552 VectorAdd(temp,hostplane,temp);
553 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
555 //planetest sample point
556 e=DotProduct(origin,sideplane);
561 //VectorClear(origin);
562 //Move the sample point back inside triangle bounds
563 origin[0]-=sideplane[0]*(e+1);
564 origin[1]-=sideplane[1]*(e+1);
565 origin[2]-=sideplane[2]*(e+1);
574 ////////////////////////
576 /* planar surfaces have precalculated lightmap vectors for nudging */
577 if( lm->plane != NULL )
579 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581 VectorCopy( lm->plane, vecs[ 2 ] );
584 /* non-planar surfaces must calculate them */
588 VectorCopy( plane, vecs[ 2 ] );
590 VectorCopy( dv->normal, vecs[ 2 ] );
591 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
594 /* push the origin off the surface a bit */
596 lightmapSampleOffset = si->lightmapSampleOffset;
598 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599 if( lm->axisNum < 0 )
600 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602 origin[ lm->axisNum ] -= lightmapSampleOffset;
604 origin[ lm->axisNum ] += lightmapSampleOffset;
606 VectorCopy(origin,origintwo);
607 if(lightmapExtraVisClusterNudge)
609 origintwo[0]+=vecs[2][0];
610 origintwo[1]+=vecs[2][1];
611 origintwo[2]+=vecs[2][2];
615 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
617 /* another retarded hack, storing nudge count in luxel[ 1 ] */
620 /* point in solid? (except in dark mode) */
621 if( pointCluster < 0 && dark == qfalse )
623 /* nudge the the location around */
625 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
627 /* nudge the vector around a bit */
628 for( i = 0; i < 3; i++ )
630 /* set nudged point*/
631 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
635 /* get pvs cluster */
636 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637 if( pointCluster >= 0 )
638 VectorCopy( nudged, origin );
643 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644 if( pointCluster < 0 && si != NULL && dark == qfalse )
646 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648 if( pointCluster >= 0 )
649 VectorCopy( nudged, origin );
654 if( pointCluster < 0 )
656 (*cluster) = CLUSTER_OCCLUDED;
657 VectorClear( origin );
658 VectorClear( normal );
664 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
666 /* do bumpmap calculations */
668 PerturbNormal( dv, si, pNormal, stv, ttv );
670 VectorCopy( dv->normal, pNormal );
672 /* store the cluster and normal */
673 (*cluster) = pointCluster;
674 VectorCopy( pNormal, normal );
676 /* store explicit mapping pass and implicit mapping pass */
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
695 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 ] )
697 bspDrawVert_t mid, *dv2[ 3 ];
701 /* map the vertexes */
703 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
711 float *a, *b, dx, dy, dist, maxDist;
714 /* find the longest edge and split it */
717 for( i = 0; i < 3; i++ )
720 a = dv[ i ]->lightmap[ 0 ];
721 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
724 dx = a[ 0 ] - b[ 0 ];
725 dy = a[ 1 ] - b[ 1 ];
726 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
736 /* try to early out */
737 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
741 /* split the longest edge and map it */
742 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
745 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
748 /* recurse to first triangle */
749 VectorCopy( dv, dv2 );
751 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
753 /* recurse to second triangle */
754 VectorCopy( dv, dv2 );
755 dv2[ (max + 1) % 3 ] = ∣
756 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
771 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772 vec3_t worldverts[ 3 ];
775 /* get plane if possible */
776 if( lm->plane != NULL )
778 VectorCopy( lm->plane, plane );
779 plane[ 3 ] = lm->plane[ 3 ];
782 /* otherwise make one from the points */
783 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
786 /* check to see if we need to calculate texture->world tangent vectors */
787 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
798 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
802 /* map the vertexes */
803 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
807 /* 2002-11-20: prefer axial triangle edges */
810 /* subdivide the triangle */
811 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
815 for( i = 0; i < 3; i++ )
818 bspDrawVert_t *dv2[ 3 ];
822 a = dv[ i ]->lightmap[ 0 ];
823 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
825 /* make degenerate triangles for mapping edges */
826 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
829 dv2[ 1 ] = dv[ (i + 1) % 3 ];
830 dv2[ 2 ] = dv[ (i + 1) % 3 ];
832 /* map the degenerate triangle */
833 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
848 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 ] )
850 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
857 float *a, *b, dx, dy, dist, maxDist;
860 /* find the longest edge and split it */
863 for( i = 0; i < 4; i++ )
866 a = dv[ i ]->lightmap[ 0 ];
867 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
870 dx = a[ 0 ] - b[ 0 ];
871 dy = a[ 1 ] - b[ 1 ];
872 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
882 /* try to early out */
883 if( max < 0 || maxDist <= subdivideThreshold )
887 /* we only care about even/odd edges */
890 /* split the longest edges */
891 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
894 /* map the vertexes */
895 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
901 /* recurse to first quad */
903 dv2[ 1 ] = &mid[ 0 ];
904 dv2[ 2 ] = &mid[ 1 ];
906 MapQuad_r( lm, info, dv2, plane, stv, ttv );
908 /* recurse to second quad */
909 dv2[ 0 ] = &mid[ 0 ];
912 dv2[ 3 ] = &mid[ 1 ];
913 MapQuad_r( lm, info, dv2, plane, stv, ttv );
919 /* recurse to first quad */
922 dv2[ 2 ] = &mid[ 0 ];
923 dv2[ 3 ] = &mid[ 1 ];
924 MapQuad_r( lm, info, dv2, plane, stv, ttv );
926 /* recurse to second quad */
927 dv2[ 0 ] = &mid[ 1 ];
928 dv2[ 1 ] = &mid[ 0 ];
931 MapQuad_r( lm, info, dv2, plane, stv, ttv );
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
943 #define QUAD_PLANAR_EPSILON 0.5f
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
949 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
952 /* get plane if possible */
953 if( lm->plane != NULL )
955 VectorCopy( lm->plane, plane );
956 plane[ 3 ] = lm->plane[ 3 ];
959 /* otherwise make one from the points */
960 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
963 /* 4th point must fall on the plane */
964 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
968 /* check to see if we need to calculate texture->world tangent vectors */
969 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
980 /* map the vertexes */
981 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
986 /* subdivide the quad */
987 MapQuad_r( lm, info, dv, plane, stv, ttv );
995 maps the locations, normals, and pvs clusters for a raw lightmap
998 #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)
1000 void MapRawLightmap( int rawLightmapNum )
1002 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003 float *luxel, *origin, *normal, samples, radius, pass;
1005 bspDrawSurface_t *ds;
1006 surfaceInfo_t *info;
1007 mesh_t src, *subdivided, *mesh;
1008 bspDrawVert_t *verts, *dv[ 4 ], fake;
1011 /* bail if this number exceeds the number of raw lightmaps */
1012 if( rawLightmapNum >= numRawLightmaps )
1016 lm = &rawLightmaps[ rawLightmapNum ];
1018 /* -----------------------------------------------------------------
1019 map referenced surfaces onto the raw lightmap
1020 ----------------------------------------------------------------- */
1022 /* walk the list of surfaces on this raw lightmap */
1023 for( n = 0; n < lm->numLightSurfaces; n++ )
1025 /* with > 1 surface per raw lightmap, clear occluded */
1028 for( y = 0; y < lm->sh; y++ )
1030 for( x = 0; x < lm->sw; x++ )
1033 cluster = SUPER_CLUSTER( x, y );
1035 *cluster = CLUSTER_UNMAPPED;
1041 num = lightSurfaces[ lm->firstLightSurface + n ];
1042 ds = &bspDrawSurfaces[ num ];
1043 info = &surfaceInfos[ num ];
1045 /* bail if no lightmap to calculate */
1046 if( info->lm != lm )
1052 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053 switch( ds->surfaceType )
1057 verts = yDrawVerts + ds->firstVert;
1059 /* map the triangles */
1060 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1062 for( i = 0; i < ds->numIndexes; i += 3 )
1064 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067 MapTriangle( lm, info, dv, mapNonAxial );
1073 /* make a mesh from the drawsurf */
1074 src.width = ds->patchWidth;
1075 src.height = ds->patchHeight;
1076 src.verts = &yDrawVerts[ ds->firstVert ];
1077 //% subdivided = SubdivideMesh( src, 8, 512 );
1078 subdivided = SubdivideMesh2( src, info->patchIterations );
1080 /* fit it to the curve and remove colinear verts on rows/columns */
1081 PutMeshOnCurve( *subdivided );
1082 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083 FreeMesh( subdivided );
1086 verts = mesh->verts;
1092 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1099 /* map the mesh quads */
1102 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1104 for( y = 0; y < (mesh->height - 1); y++ )
1106 for( x = 0; x < (mesh->width - 1); x++ )
1109 pw[ 0 ] = x + (y * mesh->width);
1110 pw[ 1 ] = x + ((y + 1) * mesh->width);
1111 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112 pw[ 3 ] = x + 1 + (y * mesh->width);
1113 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1118 /* get drawverts and map first triangle */
1119 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122 MapTriangle( lm, info, dv, mapNonAxial );
1124 /* get drawverts and map second triangle */
1125 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128 MapTriangle( lm, info, dv, mapNonAxial );
1135 for( y = 0; y < (mesh->height - 1); y++ )
1137 for( x = 0; x < (mesh->width - 1); x++ )
1140 pw[ 0 ] = x + (y * mesh->width);
1141 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143 pw[ 3 ] = x + 1 + (y * mesh->width);
1149 /* attempt to map quad first */
1150 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154 if( MapQuad( lm, info, dv ) )
1157 /* get drawverts and map first triangle */
1158 MapTriangle( lm, info, dv, mapNonAxial );
1160 /* get drawverts and map second triangle */
1161 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1162 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1163 MapTriangle( lm, info, dv, mapNonAxial );
1178 /* -----------------------------------------------------------------
1179 average and clean up luxel normals
1180 ----------------------------------------------------------------- */
1182 /* walk the luxels */
1183 for( y = 0; y < lm->sh; y++ )
1185 for( x = 0; x < lm->sw; x++ )
1188 luxel = SUPER_LUXEL( 0, x, y );
1189 normal = SUPER_NORMAL( x, y );
1190 cluster = SUPER_CLUSTER( x, y );
1192 /* only look at mapped luxels */
1196 /* the normal data could be the sum of multiple samples */
1197 if( luxel[ 3 ] > 1.0f )
1198 VectorNormalize( normal, normal );
1200 /* mark this luxel as having only one normal */
1205 /* non-planar surfaces stop here */
1206 if( lm->plane == NULL )
1209 /* -----------------------------------------------------------------
1210 map occluded or unuxed luxels
1211 ----------------------------------------------------------------- */
1213 /* walk the luxels */
1214 radius = floor( superSample / 2 );
1215 radius = radius > 0 ? radius : 1.0f;
1217 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1219 for( y = 0; y < lm->sh; y++ )
1221 for( x = 0; x < lm->sw; x++ )
1224 luxel = SUPER_LUXEL( 0, x, y );
1225 normal = SUPER_NORMAL( x, y );
1226 cluster = SUPER_CLUSTER( x, y );
1228 /* only look at unmapped luxels */
1229 if( *cluster != CLUSTER_UNMAPPED )
1232 /* divine a normal and origin from neighboring luxels */
1233 VectorClear( fake.xyz );
1234 VectorClear( fake.normal );
1235 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1236 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1238 for( sy = (y - 1); sy <= (y + 1); sy++ )
1240 if( sy < 0 || sy >= lm->sh )
1243 for( sx = (x - 1); sx <= (x + 1); sx++ )
1245 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1248 /* get neighboring luxel */
1249 luxel = SUPER_LUXEL( 0, sx, sy );
1250 origin = SUPER_ORIGIN( sx, sy );
1251 normal = SUPER_NORMAL( sx, sy );
1252 cluster = SUPER_CLUSTER( sx, sy );
1254 /* only consider luxels mapped in previous passes */
1255 if( *cluster < 0 || luxel[ 0 ] >= pass )
1258 /* add its distinctiveness to our own */
1259 VectorAdd( fake.xyz, origin, fake.xyz );
1260 VectorAdd( fake.normal, normal, fake.normal );
1261 samples += luxel[ 3 ];
1266 if( samples == 0.0f )
1270 VectorDivide( fake.xyz, samples, fake.xyz );
1271 //% VectorDivide( fake.normal, samples, fake.normal );
1272 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1275 /* map the fake vert */
1276 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1281 /* -----------------------------------------------------------------
1282 average and clean up luxel normals
1283 ----------------------------------------------------------------- */
1285 /* walk the luxels */
1286 for( y = 0; y < lm->sh; y++ )
1288 for( x = 0; x < lm->sw; x++ )
1291 luxel = SUPER_LUXEL( 0, x, y );
1292 normal = SUPER_NORMAL( x, y );
1293 cluster = SUPER_CLUSTER( x, y );
1295 /* only look at mapped luxels */
1299 /* the normal data could be the sum of multiple samples */
1300 if( luxel[ 3 ] > 1.0f )
1301 VectorNormalize( normal, normal );
1303 /* mark this luxel as having only one normal */
1311 for( y = 0; y < lm->sh; y++ )
1313 for( x = 0; x < lm->sw; x++ )
1318 cluster = SUPER_CLUSTER( x, y );
1319 origin = SUPER_ORIGIN( x, y );
1320 normal = SUPER_NORMAL( x, y );
1321 luxel = SUPER_LUXEL( x, y );
1326 /* check if within the bounding boxes of all surfaces referenced */
1327 ClearBounds( mins, maxs );
1328 for( n = 0; n < lm->numLightSurfaces; n++ )
1331 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1332 TOL = info->sampleSize + 2;
1333 AddPointToBounds( info->mins, mins, maxs );
1334 AddPointToBounds( info->maxs, mins, maxs );
1335 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1336 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1337 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1342 if( n < lm->numLightSurfaces )
1345 /* report bogus origin */
1346 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",
1347 rawLightmapNum, x, y, *cluster,
1348 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1349 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1350 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1361 sets up dirtmap (ambient occlusion)
1364 #define DIRT_CONE_ANGLE 88 /* degrees */
1365 #define DIRT_NUM_ANGLE_STEPS 16
1366 #define DIRT_NUM_ELEVATION_STEPS 3
1367 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1369 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1370 static int numDirtVectors = 0;
1372 void SetupDirt( void )
1375 float angle, elevation, angleStep, elevationStep;
1379 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1381 /* calculate angular steps */
1382 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1383 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1387 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1389 /* iterate elevation */
1390 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1392 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1393 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1394 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1399 /* emit some statistics */
1400 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1406 calculates dirt value for a given sample
1409 float DirtForSample( trace_t *trace )
1412 float gatherDirt, outDirt, angle, elevation, ooDepth;
1413 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1419 if( trace == NULL || trace->cluster < 0 )
1424 ooDepth = 1.0f / dirtDepth;
1425 VectorCopy( trace->normal, normal );
1427 /* check if the normal is aligned to the world-up */
1428 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1430 if( normal[ 2 ] == 1.0f )
1432 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1433 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1435 else if( normal[ 2 ] == -1.0f )
1437 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1438 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1443 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1444 CrossProduct( normal, worldUp, myRt );
1445 VectorNormalize( myRt, myRt );
1446 CrossProduct( myRt, normal, myUp );
1447 VectorNormalize( myUp, myUp );
1450 /* 1 = random mode, 0 (well everything else) = non-random mode */
1454 for( i = 0; i < numDirtVectors; i++ )
1456 /* get random vector */
1457 angle = Random() * DEG2RAD( 360.0f );
1458 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1459 temp[ 0 ] = cos( angle ) * sin( elevation );
1460 temp[ 1 ] = sin( angle ) * sin( elevation );
1461 temp[ 2 ] = cos( elevation );
1463 /* transform into tangent space */
1464 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1465 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1466 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1469 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1470 SetupTrace( trace );
1476 VectorSubtract( trace->hit, trace->origin, displacement );
1477 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1483 /* iterate through ordered vectors */
1484 for( i = 0; i < numDirtVectors; i++ )
1486 /* transform vector into tangent space */
1487 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1488 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1489 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1492 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1493 SetupTrace( trace );
1499 VectorSubtract( trace->hit, trace->origin, displacement );
1500 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1506 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1507 SetupTrace( trace );
1513 VectorSubtract( trace->hit, trace->origin, displacement );
1514 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1518 if( gatherDirt <= 0.0f )
1521 /* apply gain (does this even do much? heh) */
1522 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1523 if( outDirt > 1.0f )
1527 outDirt *= dirtScale;
1528 if( outDirt > 1.0f )
1531 /* return to sender */
1532 return 1.0f - outDirt;
1539 calculates dirty fraction for each luxel
1542 void DirtyRawLightmap( int rawLightmapNum )
1544 int i, x, y, sx, sy, *cluster;
1545 float *origin, *normal, *dirt, *dirt2, average, samples;
1547 surfaceInfo_t *info;
1552 /* bail if this number exceeds the number of raw lightmaps */
1553 if( rawLightmapNum >= numRawLightmaps )
1557 lm = &rawLightmaps[ rawLightmapNum ];
1560 trace.testOcclusion = qtrue;
1561 trace.forceSunlight = qfalse;
1562 trace.recvShadows = lm->recvShadows;
1563 trace.numSurfaces = lm->numLightSurfaces;
1564 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1565 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1566 trace.testAll = qtrue;
1568 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1569 trace.twoSided = qfalse;
1570 for( i = 0; i < trace.numSurfaces; i++ )
1573 info = &surfaceInfos[ trace.surfaces[ i ] ];
1575 /* check twosidedness */
1576 if( info->si->twoSided )
1578 trace.twoSided = qtrue;
1584 for( i = 0; i < trace.numSurfaces; i++ )
1587 info = &surfaceInfos[ trace.surfaces[ i ] ];
1589 /* check twosidedness */
1590 if( info->si->noDirty )
1598 for( y = 0; y < lm->sh; y++ )
1600 for( x = 0; x < lm->sw; x++ )
1603 cluster = SUPER_CLUSTER( x, y );
1604 origin = SUPER_ORIGIN( x, y );
1605 normal = SUPER_NORMAL( x, y );
1606 dirt = SUPER_DIRT( x, y );
1608 /* set default dirt */
1611 /* only look at mapped luxels */
1615 /* don't apply dirty on this surface */
1623 trace.cluster = *cluster;
1624 VectorCopy( origin, trace.origin );
1625 VectorCopy( normal, trace.normal );
1628 *dirt = DirtForSample( &trace );
1632 /* testing no filtering */
1636 for( y = 0; y < lm->sh; y++ )
1638 for( x = 0; x < lm->sw; x++ )
1641 cluster = SUPER_CLUSTER( x, y );
1642 dirt = SUPER_DIRT( x, y );
1644 /* filter dirt by adjacency to unmapped luxels */
1647 for( sy = (y - 1); sy <= (y + 1); sy++ )
1649 if( sy < 0 || sy >= lm->sh )
1652 for( sx = (x - 1); sx <= (x + 1); sx++ )
1654 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1657 /* get neighboring luxel */
1658 cluster = SUPER_CLUSTER( sx, sy );
1659 dirt2 = SUPER_DIRT( sx, sy );
1660 if( *cluster < 0 || *dirt2 <= 0.0f )
1669 if( samples <= 0.0f )
1674 if( samples <= 0.0f )
1678 *dirt = average / samples;
1687 calculates the pvs cluster, origin, normal of a sub-luxel
1690 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1692 int i, *cluster, *cluster2;
1693 float *origin, *origin2, *normal; //% , *normal2;
1694 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1697 /* calulate x vector */
1698 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1700 cluster = SUPER_CLUSTER( x, y );
1701 origin = SUPER_ORIGIN( x, y );
1702 //% normal = SUPER_NORMAL( x, y );
1703 cluster2 = SUPER_CLUSTER( x + 1, y );
1704 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1705 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1707 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1709 cluster = SUPER_CLUSTER( x - 1, y );
1710 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1711 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1712 cluster2 = SUPER_CLUSTER( x, y );
1713 origin2 = SUPER_ORIGIN( x, y );
1714 //% normal2 = SUPER_NORMAL( x, y );
1717 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1719 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1720 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1722 /* calulate y vector */
1723 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1725 cluster = SUPER_CLUSTER( x, y );
1726 origin = SUPER_ORIGIN( x, y );
1727 //% normal = SUPER_NORMAL( x, y );
1728 cluster2 = SUPER_CLUSTER( x, y + 1 );
1729 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1730 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1732 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1734 cluster = SUPER_CLUSTER( x, y - 1 );
1735 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1736 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1737 cluster2 = SUPER_CLUSTER( x, y );
1738 origin2 = SUPER_ORIGIN( x, y );
1739 //% normal2 = SUPER_NORMAL( x, y );
1742 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1744 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1745 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1747 /* calculate new origin */
1748 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1749 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1750 for( i = 0; i < 3; i++ )
1751 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1754 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1755 if( *sampleCluster < 0 )
1758 /* calculate new normal */
1759 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1760 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1761 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1763 normal = SUPER_NORMAL( x, y );
1764 VectorCopy( normal, sampleNormal );
1772 SubsampleRawLuxel_r()
1773 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1776 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1778 int b, samples, mapped, lighted;
1781 vec3_t deluxel[ 3 ];
1782 vec3_t origin[ 4 ], normal[ 4 ];
1783 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1784 vec3_t color, direction, total;
1788 if( lightLuxel[ 3 ] >= lightSamples )
1792 VectorClear( total );
1796 /* make 2x2 subsample stamp */
1797 for( b = 0; b < 4; b++ )
1800 VectorCopy( sampleOrigin, origin[ b ] );
1802 /* calculate position */
1803 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1810 /* increment sample count */
1811 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1814 trace->cluster = *cluster;
1815 VectorCopy( origin[ b ], trace->origin );
1816 VectorCopy( normal[ b ], trace->normal );
1820 LightContributionToSample( trace );
1821 if(trace->forceSubsampling > 1.0f)
1823 /* alphashadow: we subsample as deep as we can */
1829 /* add to totals (fixme: make contrast function) */
1830 VectorCopy( trace->color, luxel[ b ] );
1833 VectorCopy( trace->directionContribution, deluxel[ b ] );
1835 VectorAdd( total, trace->color, total );
1836 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1840 /* subsample further? */
1841 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1842 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1843 lighted != 0 && lighted != mapped )
1845 for( b = 0; b < 4; b++ )
1847 if( cluster[ b ] < 0 )
1849 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1854 //% VectorClear( color );
1856 VectorCopy( lightLuxel, color );
1857 VectorCopy( lightDeluxel, direction );
1859 for( b = 0; b < 4; b++ )
1861 if( cluster[ b ] < 0 )
1863 VectorAdd( color, luxel[ b ], color );
1866 VectorAdd( direction, deluxel[ b ], direction );
1875 color[ 0 ] /= samples;
1876 color[ 1 ] /= samples;
1877 color[ 2 ] /= samples;
1880 VectorCopy( color, lightLuxel );
1881 lightLuxel[ 3 ] += 1.0f;
1885 direction[ 0 ] /= samples;
1886 direction[ 1 ] /= samples;
1887 direction[ 2 ] /= samples;
1888 VectorCopy( direction, lightDeluxel );
1892 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1896 vec3_t origin, normal;
1897 vec3_t total, totaldirection;
1899 VectorClear( total );
1901 for(b = 0; b < lightSamples; ++b)
1904 VectorCopy( sampleOrigin, origin );
1906 /* calculate position */
1907 if( !SubmapRawLuxel( lm, x, y, (bias * (2 * Random() - 1)), (bias * (2 * Random() - 1)), &cluster, origin, normal ) )
1914 trace->cluster = cluster;
1915 VectorCopy( origin, trace->origin );
1916 VectorCopy( normal, trace->normal );
1918 LightContributionToSample( trace );
1919 VectorAdd( total, trace->color, total );
1922 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1930 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1931 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1932 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1936 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1937 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1938 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1946 IlluminateRawLightmap()
1947 illuminates the luxels
1950 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1951 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1952 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1954 void IlluminateRawLightmap( int rawLightmapNum )
1956 int i, t, x, y, sx, sy, size, llSize, ldSize, luxelFilterRadius, lightmapNum;
1957 int *cluster, *cluster2, mapped, lighted, totalLighted;
1959 surfaceInfo_t *info;
1960 qboolean filterColor, filterDir;
1962 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1963 unsigned char *flag;
1964 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1965 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1966 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1968 float stackLightLuxels[ STACK_LL_SIZE ];
1971 /* bail if this number exceeds the number of raw lightmaps */
1972 if( rawLightmapNum >= numRawLightmaps )
1976 lm = &rawLightmaps[ rawLightmapNum ];
1979 trace.testOcclusion = !noTrace;
1980 trace.forceSunlight = qfalse;
1981 trace.recvShadows = lm->recvShadows;
1982 trace.numSurfaces = lm->numLightSurfaces;
1983 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1984 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1986 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1987 trace.twoSided = qfalse;
1988 for( i = 0; i < trace.numSurfaces; i++ )
1991 info = &surfaceInfos[ trace.surfaces[ i ] ];
1993 /* check twosidedness */
1994 if( info->si->twoSided )
1996 trace.twoSided = qtrue;
2001 /* create a culled light list for this raw lightmap */
2002 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2004 /* -----------------------------------------------------------------
2006 ----------------------------------------------------------------- */
2009 numLuxelsIlluminated += (lm->sw * lm->sh);
2011 /* test debugging state */
2012 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2014 /* debug fill the luxels */
2015 for( y = 0; y < lm->sh; y++ )
2017 for( x = 0; x < lm->sw; x++ )
2020 cluster = SUPER_CLUSTER( x, y );
2022 /* only fill mapped luxels */
2026 /* get particulars */
2027 luxel = SUPER_LUXEL( 0, x, y );
2028 origin = SUPER_ORIGIN( x, y );
2029 normal = SUPER_NORMAL( x, y );
2031 /* color the luxel with raw lightmap num? */
2033 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2035 /* color the luxel with lightmap axis? */
2036 else if( debugAxis )
2038 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2039 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2040 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2043 /* color the luxel with luxel cluster? */
2044 else if( debugCluster )
2045 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2047 /* color the luxel with luxel origin? */
2048 else if( debugOrigin )
2050 VectorSubtract( lm->maxs, lm->mins, temp );
2051 VectorScale( temp, (1.0f / 255.0f), temp );
2052 VectorSubtract( origin, lm->mins, temp2 );
2053 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2054 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2055 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2058 /* color the luxel with the normal */
2059 else if( normalmap )
2061 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2062 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2063 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2066 /* otherwise clear it */
2068 VectorClear( luxel );
2077 /* allocate temporary per-light luxel storage */
2078 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2079 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2080 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2081 lightLuxels = stackLightLuxels;
2083 lightLuxels = safe_malloc( llSize );
2085 lightDeluxels = safe_malloc( ldSize );
2087 lightDeluxels = NULL;
2090 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2092 /* set ambient color */
2093 for( y = 0; y < lm->sh; y++ )
2095 for( x = 0; x < lm->sw; x++ )
2098 cluster = SUPER_CLUSTER( x, y );
2099 luxel = SUPER_LUXEL( 0, x, y );
2100 normal = SUPER_NORMAL( x, y );
2101 deluxel = SUPER_DELUXEL( x, y );
2103 /* blacken unmapped clusters */
2105 VectorClear( luxel );
2110 VectorCopy( ambientColor, luxel );
2113 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2115 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2116 if(brightness < 0.00390625f)
2117 brightness = 0.00390625f;
2119 VectorScale( normal, brightness, deluxel );
2126 /* clear styled lightmaps */
2127 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2128 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2130 if( lm->superLuxels[ lightmapNum ] != NULL )
2131 memset( lm->superLuxels[ lightmapNum ], 0, size );
2134 /* debugging code */
2135 //% if( trace.numLights <= 0 )
2136 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2138 /* walk light list */
2139 for( i = 0; i < trace.numLights; i++ )
2142 trace.light = trace.lights[ i ];
2145 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2147 if( lm->styles[ lightmapNum ] == trace.light->style ||
2148 lm->styles[ lightmapNum ] == LS_NONE )
2152 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2153 if( lightmapNum >= MAX_LIGHTMAPS )
2155 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2160 memset( lightLuxels, 0, llSize );
2162 memset( lightDeluxels, 0, ldSize );
2165 /* determine filter radius */
2166 filterRadius = lm->filterRadius > trace.light->filterRadius
2168 : trace.light->filterRadius;
2169 if( filterRadius < 0.0f )
2170 filterRadius = 0.0f;
2172 /* set luxel filter radius */
2173 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2174 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2175 luxelFilterRadius = 1;
2177 /* allocate sampling flags storage */
2178 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2180 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2181 if(lm->superFlags == NULL)
2182 lm->superFlags = safe_malloc( size );
2183 memset( (void *) lm->superFlags, 0, size );
2186 /* initial pass, one sample per luxel */
2187 for( y = 0; y < lm->sh; y++ )
2189 for( x = 0; x < lm->sw; x++ )
2192 cluster = SUPER_CLUSTER( x, y );
2196 /* get particulars */
2197 lightLuxel = LIGHT_LUXEL( x, y );
2198 lightDeluxel = LIGHT_DELUXEL( x, y );
2199 origin = SUPER_ORIGIN( x, y );
2200 normal = SUPER_NORMAL( x, y );
2201 flag = SUPER_FLAG( x, y );
2204 ////////// 27's temp hack for testing edge clipping ////
2205 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2207 lightLuxel[ 1 ] = 255;
2208 lightLuxel[ 3 ] = 1.0f;
2214 /* set contribution count */
2215 lightLuxel[ 3 ] = 1.0f;
2218 trace.cluster = *cluster;
2219 VectorCopy( origin, trace.origin );
2220 VectorCopy( normal, trace.normal );
2222 /* get light for this sample */
2223 LightContributionToSample( &trace );
2224 VectorCopy( trace.color, lightLuxel );
2226 /* add the contribution to the deluxemap */
2229 VectorCopy( trace.directionContribution, lightDeluxel );
2232 /* check for evilness */
2233 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2236 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2239 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2245 /* don't even bother with everything else if nothing was lit */
2246 if( totalLighted == 0 )
2249 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2250 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2251 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2254 for( y = 0; y < (lm->sh - 1); y++ )
2256 for( x = 0; x < (lm->sw - 1); x++ )
2261 VectorClear( total );
2263 /* test 2x2 stamp */
2264 for( t = 0; t < 4; t++ )
2266 /* set sample coords */
2267 sx = x + tests[ t ][ 0 ];
2268 sy = y + tests[ t ][ 1 ];
2271 cluster = SUPER_CLUSTER( sx, sy );
2277 flag = SUPER_FLAG( sx, sy );
2278 if(*flag & FLAG_FORCE_SUBSAMPLING)
2280 /* force a lighted/mapped discrepancy so we subsample */
2285 lightLuxel = LIGHT_LUXEL( sx, sy );
2286 VectorAdd( total, lightLuxel, total );
2287 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2291 /* if total color is under a certain amount, then don't bother subsampling */
2292 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2295 /* if all 4 pixels are either in shadow or light, then don't subsample */
2296 if( lighted != 0 && lighted != mapped )
2298 for( t = 0; t < 4; t++ )
2300 /* set sample coords */
2301 sx = x + tests[ t ][ 0 ];
2302 sy = y + tests[ t ][ 1 ];
2305 cluster = SUPER_CLUSTER( sx, sy );
2308 flag = SUPER_FLAG( sx, sy );
2309 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2311 lightLuxel = LIGHT_LUXEL( sx, sy );
2312 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2313 origin = SUPER_ORIGIN( sx, sy );
2315 /* only subsample shadowed luxels */
2316 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2320 if(lightRandomSamples)
2321 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f, lightLuxel, deluxemap ? lightDeluxel : NULL );
2323 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2325 *flag |= FLAG_ALREADY_SUBSAMPLED;
2327 /* debug code to colorize subsampled areas to yellow */
2328 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2329 //% VectorSet( luxel, 255, 204, 0 );
2336 /* tertiary pass, apply dirt map (ambient occlusion) */
2340 for( y = 0; y < lm->sh; y++ )
2342 for( x = 0; x < lm->sw; x++ )
2345 cluster = SUPER_CLUSTER( x, y );
2349 /* get particulars */
2350 lightLuxel = LIGHT_LUXEL( x, y );
2351 dirt = SUPER_DIRT( x, y );
2353 /* scale light value */
2354 VectorScale( lightLuxel, *dirt, lightLuxel );
2359 /* allocate sampling lightmap storage */
2360 if( lm->superLuxels[ lightmapNum ] == NULL )
2362 /* allocate sampling lightmap storage */
2363 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2364 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2365 memset( lm->superLuxels[ lightmapNum ], 0, size );
2369 if( lightmapNum > 0 )
2371 lm->styles[ lightmapNum ] = trace.light->style;
2372 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2375 /* copy to permanent luxels */
2376 for( y = 0; y < lm->sh; y++ )
2378 for( x = 0; x < lm->sw; x++ )
2380 /* get cluster and origin */
2381 cluster = SUPER_CLUSTER( x, y );
2384 origin = SUPER_ORIGIN( x, y );
2387 if( luxelFilterRadius )
2390 VectorClear( averageColor );
2391 VectorClear( averageDir );
2394 /* cheaper distance-based filtering */
2395 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2397 if( sy < 0 || sy >= lm->sh )
2400 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2402 if( sx < 0 || sx >= lm->sw )
2405 /* get particulars */
2406 cluster = SUPER_CLUSTER( sx, sy );
2409 lightLuxel = LIGHT_LUXEL( sx, sy );
2410 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2413 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2414 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2416 /* scale luxel by filter weight */
2417 VectorScale( lightLuxel, weight, color );
2418 VectorAdd( averageColor, color, averageColor );
2421 VectorScale( lightDeluxel, weight, direction );
2422 VectorAdd( averageDir, direction, averageDir );
2429 if( samples <= 0.0f )
2432 /* scale into luxel */
2433 luxel = SUPER_LUXEL( lightmapNum, x, y );
2436 /* handle negative light */
2437 if( trace.light->flags & LIGHT_NEGATIVE )
2439 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2440 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2441 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2444 /* handle normal light */
2447 luxel[ 0 ] += averageColor[ 0 ] / samples;
2448 luxel[ 1 ] += averageColor[ 1 ] / samples;
2449 luxel[ 2 ] += averageColor[ 2 ] / samples;
2454 /* scale into luxel */
2455 deluxel = SUPER_DELUXEL( x, y );
2456 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2457 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2458 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2465 /* get particulars */
2466 lightLuxel = LIGHT_LUXEL( x, y );
2467 lightDeluxel = LIGHT_DELUXEL( x, y );
2468 luxel = SUPER_LUXEL( lightmapNum, x, y );
2469 deluxel = SUPER_DELUXEL( x, y );
2471 /* handle negative light */
2472 if( trace.light->flags & LIGHT_NEGATIVE )
2473 VectorScale( averageColor, -1.0f, averageColor );
2478 /* handle negative light */
2479 if( trace.light->flags & LIGHT_NEGATIVE )
2480 VectorSubtract( luxel, lightLuxel, luxel );
2482 /* handle normal light */
2484 VectorAdd( luxel, lightLuxel, luxel );
2488 VectorAdd( deluxel, lightDeluxel, deluxel );
2495 /* free temporary luxels */
2496 if( lightLuxels != stackLightLuxels )
2497 free( lightLuxels );
2500 free( lightDeluxels );
2503 /* free light list */
2504 FreeTraceLights( &trace );
2506 /* floodlight pass */
2508 FloodlightIlluminateLightmap(lm);
2512 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2515 if( lm->superLuxels[ lightmapNum ] == NULL )
2518 for( y = 0; y < lm->sh; y++ )
2520 for( x = 0; x < lm->sw; x++ )
2523 cluster = SUPER_CLUSTER( x, y );
2524 //% if( *cluster < 0 )
2527 /* get particulars */
2528 luxel = SUPER_LUXEL( lightmapNum, x, y );
2529 normal = SUPER_NORMAL ( x, y );
2531 luxel[0]=(normal[0]*127)+127;
2532 luxel[1]=(normal[1]*127)+127;
2533 luxel[2]=(normal[2]*127)+127;
2539 /* -----------------------------------------------------------------
2541 ----------------------------------------------------------------- */
2545 /* walk lightmaps */
2546 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2549 if( lm->superLuxels[ lightmapNum ] == NULL )
2552 /* apply dirt to each luxel */
2553 for( y = 0; y < lm->sh; y++ )
2555 for( x = 0; x < lm->sw; x++ )
2558 cluster = SUPER_CLUSTER( x, y );
2559 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2562 /* get particulars */
2563 luxel = SUPER_LUXEL( lightmapNum, x, y );
2564 dirt = SUPER_DIRT( x, y );
2567 VectorScale( luxel, *dirt, luxel );
2571 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2577 /* -----------------------------------------------------------------
2579 ----------------------------------------------------------------- */
2581 /* walk lightmaps */
2582 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2585 if( lm->superLuxels[ lightmapNum ] == NULL )
2588 /* average occluded luxels from neighbors */
2589 for( y = 0; y < lm->sh; y++ )
2591 for( x = 0; x < lm->sw; x++ )
2593 /* get particulars */
2594 cluster = SUPER_CLUSTER( x, y );
2595 luxel = SUPER_LUXEL( lightmapNum, x, y );
2596 deluxel = SUPER_DELUXEL( x, y );
2597 normal = SUPER_NORMAL( x, y );
2599 /* determine if filtering is necessary */
2600 filterColor = qfalse;
2603 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2604 filterColor = qtrue;
2606 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2609 if( !filterColor && !filterDir )
2612 /* choose seed amount */
2613 VectorClear( averageColor );
2614 VectorClear( averageDir );
2617 /* walk 3x3 matrix */
2618 for( sy = (y - 1); sy <= (y + 1); sy++ )
2620 if( sy < 0 || sy >= lm->sh )
2623 for( sx = (x - 1); sx <= (x + 1); sx++ )
2625 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2628 /* get neighbor's particulars */
2629 cluster2 = SUPER_CLUSTER( sx, sy );
2630 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2631 deluxel2 = SUPER_DELUXEL( sx, sy );
2633 /* ignore unmapped/unlit luxels */
2634 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2635 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2638 /* add its distinctiveness to our own */
2639 VectorAdd( averageColor, luxel2, averageColor );
2640 samples += luxel2[ 3 ];
2642 VectorAdd( averageDir, deluxel2, averageDir );
2647 if( samples <= 0.0f )
2650 /* dark lightmap seams */
2653 if( lightmapNum == 0 )
2654 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2661 VectorDivide( averageColor, samples, luxel );
2665 VectorDivide( averageDir, samples, deluxel );
2667 /* set cluster to -3 */
2669 *cluster = CLUSTER_FLOODED;
2677 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2680 if( lm->superLuxels[ lightmapNum ] == NULL )
2682 for( y = 0; y < lm->sh; y++ )
2683 for( x = 0; x < lm->sw; x++ )
2686 cluster = SUPER_CLUSTER( x, y );
2687 luxel = SUPER_LUXEL( lightmapNum, x, y );
2688 deluxel = SUPER_DELUXEL( x, y );
2689 if(!luxel || !deluxel || !cluster)
2691 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2694 else if(*cluster < 0)
2697 // should have neither deluxemap nor lightmap
2699 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2704 // should have both deluxemap and lightmap
2706 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2716 IlluminateVertexes()
2717 light the surface vertexes
2720 #define VERTEX_NUDGE 4.0f
2722 void IlluminateVertexes( int num )
2724 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2725 int lightmapNum, numAvg;
2726 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2727 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2728 bspDrawSurface_t *ds;
2729 surfaceInfo_t *info;
2731 bspDrawVert_t *verts;
2733 float floodLightAmount;
2737 /* get surface, info, and raw lightmap */
2738 ds = &bspDrawSurfaces[ num ];
2739 info = &surfaceInfos[ num ];
2742 /* -----------------------------------------------------------------
2743 illuminate the vertexes
2744 ----------------------------------------------------------------- */
2746 /* calculate vertex lighting for surfaces without lightmaps */
2747 if( lm == NULL || cpmaHack )
2750 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2751 trace.forceSunlight = info->si->forceSunlight;
2752 trace.recvShadows = info->recvShadows;
2753 trace.numSurfaces = 1;
2754 trace.surfaces = #
2755 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2757 /* twosided lighting */
2758 trace.twoSided = info->si->twoSided;
2760 /* make light list for this surface */
2761 CreateTraceLightsForSurface( num, &trace );
2764 verts = yDrawVerts + ds->firstVert;
2766 memset( avgColors, 0, sizeof( avgColors ) );
2768 /* walk the surface verts */
2769 for( i = 0; i < ds->numVerts; i++ )
2771 /* get vertex luxel */
2772 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2774 /* color the luxel with raw lightmap num? */
2776 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2778 /* color the luxel with luxel origin? */
2779 else if( debugOrigin )
2781 VectorSubtract( info->maxs, info->mins, temp );
2782 VectorScale( temp, (1.0f / 255.0f), temp );
2783 VectorSubtract( origin, lm->mins, temp2 );
2784 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2785 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2786 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2789 /* color the luxel with the normal */
2790 else if( normalmap )
2792 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2793 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2794 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2797 /* illuminate the vertex */
2800 /* clear vertex luxel */
2801 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2803 /* try at initial origin */
2804 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2805 if( trace.cluster >= 0 )
2808 VectorCopy( verts[ i ].xyz, trace.origin );
2809 VectorCopy( verts[ i ].normal, trace.normal );
2812 if( dirty && !bouncing )
2813 dirt = DirtForSample( &trace );
2817 /* jal: floodlight */
2818 floodLightAmount = 0.0f;
2819 VectorClear( floodColor );
2820 if( floodlighty && !bouncing )
2822 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2823 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2827 LightingAtSample( &trace, ds->vertexStyles, colors );
2830 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2833 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2835 /* jal: floodlight */
2836 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2839 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2840 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2841 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2845 /* is this sample bright enough? */
2846 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2847 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2848 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2849 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2851 /* nudge the sample point around a bit */
2852 for( x = 0; x < 4; x++ )
2854 /* two's complement 0, 1, -1, 2, -2, etc */
2855 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2857 for( y = 0; y < 4; y++ )
2859 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2861 for( z = 0; z < 4; z++ )
2863 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2866 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2867 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2868 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2870 /* try at nudged origin */
2871 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2872 if( trace.cluster < 0 )
2876 LightingAtSample( &trace, ds->vertexStyles, colors );
2879 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2882 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2884 /* jal: floodlight */
2885 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2888 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2889 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2892 /* bright enough? */
2893 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2894 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2895 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2896 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2903 /* add to average? */
2904 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2905 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2906 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2907 radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2910 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2912 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2913 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2918 /* another happy customer */
2919 numVertsIlluminated++;
2922 /* set average color */
2925 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2926 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2930 VectorCopy( ambientColor, avgColors[ 0 ] );
2933 /* clean up and store vertex color */
2934 for( i = 0; i < ds->numVerts; i++ )
2936 /* get vertex luxel */
2937 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2939 /* store average in occluded vertexes */
2940 if( radVertLuxel[ 0 ] < 0.0f )
2942 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2944 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2945 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2948 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2953 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2956 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2957 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2960 if( bouncing || bounce == 0 || !bounceOnly )
2961 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2962 if( !info->si->noVertexLight )
2963 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2967 /* free light list */
2968 FreeTraceLights( &trace );
2970 /* return to sender */
2974 /* -----------------------------------------------------------------
2975 reconstitute vertex lighting from the luxels
2976 ----------------------------------------------------------------- */
2978 /* set styles from lightmap */
2979 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2980 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2982 /* get max search radius */
2984 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2986 /* walk the surface verts */
2987 verts = yDrawVerts + ds->firstVert;
2988 for( i = 0; i < ds->numVerts; i++ )
2990 /* do each lightmap */
2991 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2994 if( lm->superLuxels[ lightmapNum ] == NULL )
2997 /* get luxel coords */
2998 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2999 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3002 else if( x >= lm->sw )
3006 else if( y >= lm->sh )
3009 /* get vertex luxels */
3010 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3011 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3013 /* color the luxel with the normal? */
3016 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3017 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3018 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3021 /* color the luxel with surface num? */
3022 else if( debugSurfaces )
3023 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3025 /* divine color from the superluxels */
3028 /* increasing radius */
3029 VectorClear( radVertLuxel );
3031 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3033 /* sample within radius */
3034 for( sy = (y - radius); sy <= (y + radius); sy++ )
3036 if( sy < 0 || sy >= lm->sh )
3039 for( sx = (x - radius); sx <= (x + radius); sx++ )
3041 if( sx < 0 || sx >= lm->sw )
3044 /* get luxel particulars */
3045 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3046 cluster = SUPER_CLUSTER( sx, sy );
3050 /* testing: must be brigher than ambient color */
3051 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3054 /* add its distinctiveness to our own */
3055 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3056 samples += luxel[ 3 ];
3062 if( samples > 0.0f )
3063 VectorDivide( radVertLuxel, samples, radVertLuxel );
3065 VectorCopy( ambientColor, radVertLuxel );
3068 /* store into floating point storage */
3069 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3070 numVertsIlluminated++;
3072 /* store into bytes (for vertex approximation) */
3073 if( !info->si->noVertexLight )
3074 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3081 /* -------------------------------------------------------------------------------
3083 light optimization (-fast)
3085 creates a list of lights that will affect a surface and stores it in tw
3086 this is to optimize surface lighting by culling out as many of the
3087 lights in the world as possible from further calculation
3089 ------------------------------------------------------------------------------- */
3093 determines opaque brushes in the world and find sky shaders for sunlight calculations
3096 void SetupBrushes( void )
3098 int i, j, b, compileFlags;
3101 bspBrushSide_t *side;
3102 bspShader_t *shader;
3107 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3110 if( opaqueBrushes == NULL )
3111 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3114 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3115 numOpaqueBrushes = 0;
3117 /* walk the list of worldspawn brushes */
3118 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3121 b = bspModels[ 0 ].firstBSPBrush + i;
3122 brush = &bspBrushes[ b ];
3124 /* check all sides */
3127 for( j = 0; j < brush->numSides && inside; j++ )
3129 /* do bsp shader calculations */
3130 side = &bspBrushSides[ brush->firstSide + j ];
3131 shader = &bspShaders[ side->shaderNum ];
3133 /* get shader info */
3134 si = ShaderInfoForShader( shader->shader );
3138 /* or together compile flags */
3139 compileFlags |= si->compileFlags;
3142 /* determine if this brush is opaque to light */
3143 if( !(compileFlags & C_TRANSLUCENT) )
3145 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3151 /* emit some statistics */
3152 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3159 determines if two clusters are visible to each other using the PVS
3162 qboolean ClusterVisible( int a, int b )
3164 int portalClusters, leafBytes;
3169 if( a < 0 || b < 0 )
3177 if( numBSPVisBytes <=8 )
3181 portalClusters = ((int *) bspVisBytes)[ 0 ];
3182 leafBytes = ((int*) bspVisBytes)[ 1 ];
3183 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3186 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3195 borrowed from vlight.c
3198 int PointInLeafNum_r( vec3_t point, int nodenum )
3206 while( nodenum >= 0 )
3208 node = &bspNodes[ nodenum ];
3209 plane = &bspPlanes[ node->planeNum ];
3210 dist = DotProduct( point, plane->normal ) - plane->dist;
3212 nodenum = node->children[ 0 ];
3213 else if( dist < -0.1 )
3214 nodenum = node->children[ 1 ];
3217 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3218 if( bspLeafs[ leafnum ].cluster != -1 )
3220 nodenum = node->children[ 1 ];
3224 leafnum = -nodenum - 1;
3232 borrowed from vlight.c
3235 int PointInLeafNum( vec3_t point )
3237 return PointInLeafNum_r( point, 0 );
3243 ClusterVisibleToPoint() - ydnar
3244 returns qtrue if point can "see" cluster
3247 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3252 /* get leafNum for point */
3253 pointCluster = ClusterForPoint( point );
3254 if( pointCluster < 0 )
3258 return ClusterVisible( pointCluster, cluster );
3264 ClusterForPoint() - ydnar
3265 returns the pvs cluster for point
3268 int ClusterForPoint( vec3_t point )
3273 /* get leafNum for point */
3274 leafNum = PointInLeafNum( point );
3278 /* return the cluster */
3279 return bspLeafs[ leafNum ].cluster;
3285 ClusterForPointExt() - ydnar
3286 also takes brushes into account for occlusion testing
3289 int ClusterForPointExt( vec3_t point, float epsilon )
3291 int i, j, b, leafNum, cluster;
3294 int *brushes, numBSPBrushes;
3300 /* get leaf for point */
3301 leafNum = PointInLeafNum( point );
3304 leaf = &bspLeafs[ leafNum ];
3306 /* get the cluster */
3307 cluster = leaf->cluster;
3311 /* transparent leaf, so check point against all brushes in the leaf */
3312 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3313 numBSPBrushes = leaf->numBSPLeafBrushes;
3314 for( i = 0; i < numBSPBrushes; i++ )
3318 if( b > maxOpaqueBrush )
3320 brush = &bspBrushes[ b ];
3321 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3324 /* check point against all planes */
3326 for( j = 0; j < brush->numSides && inside; j++ )
3328 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3329 dot = DotProduct( point, plane->normal );
3335 /* if inside, return bogus cluster */
3340 /* if the point made it this far, it's not inside any opaque brushes */
3347 ClusterForPointExtFilter() - ydnar
3348 adds cluster checking against a list of known valid clusters
3351 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3356 /* get cluster for point */
3357 cluster = ClusterForPointExt( point, epsilon );
3359 /* check if filtering is necessary */
3360 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3364 for( i = 0; i < numClusters; i++ )
3366 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3377 ShaderForPointInLeaf() - ydnar
3378 checks a point against all brushes in a leaf, returning the shader of the brush
3379 also sets the cumulative surface and content flags for the brush hit
3382 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3387 int *brushes, numBSPBrushes;
3390 bspBrushSide_t *side;
3392 bspShader_t *shader;
3393 int allSurfaceFlags, allContentFlags;
3396 /* clear things out first */
3403 leaf = &bspLeafs[ leafNum ];
3405 /* transparent leaf, so check point against all brushes in the leaf */
3406 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3407 numBSPBrushes = leaf->numBSPLeafBrushes;
3408 for( i = 0; i < numBSPBrushes; i++ )
3411 brush = &bspBrushes[ brushes[ i ] ];
3413 /* check point against all planes */
3415 allSurfaceFlags = 0;
3416 allContentFlags = 0;
3417 for( j = 0; j < brush->numSides && inside; j++ )
3419 side = &bspBrushSides[ brush->firstSide + j ];
3420 plane = &bspPlanes[ side->planeNum ];
3421 dot = DotProduct( point, plane->normal );
3427 shader = &bspShaders[ side->shaderNum ];
3428 allSurfaceFlags |= shader->surfaceFlags;
3429 allContentFlags |= shader->contentFlags;
3433 /* handle if inside */
3436 /* if there are desired flags, check for same and continue if they aren't matched */
3437 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3439 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3442 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3443 *surfaceFlags = allSurfaceFlags;
3444 *contentFlags = allContentFlags;
3445 return brush->shaderNum;
3449 /* if the point made it this far, it's not inside any brushes */
3457 chops a bounding box by the plane defined by origin and normal
3458 returns qfalse if the bounds is entirely clipped away
3460 this is not exactly the fastest way to do this...
3463 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3465 /* FIXME: rewrite this so it doesn't use bloody brushes */
3473 calculates each light's effective envelope,
3474 taking into account brightness, type, and pvs.
3477 #define LIGHT_EPSILON 0.125f
3478 #define LIGHT_NUDGE 2.0f
3480 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3482 int i, x, y, z, x1, y1, z1;
3483 light_t *light, *light2, **owner;
3485 vec3_t origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3486 float radius, intensity;
3487 light_t *buckets[ 256 ];
3490 /* early out for weird cases where there are no lights */
3491 if( lights == NULL )
3495 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3499 numCulledLights = 0;
3501 while( *owner != NULL )
3506 /* handle negative lights */
3507 if( light->photons < 0.0f || light->add < 0.0f )
3509 light->photons *= -1.0f;
3510 light->add *= -1.0f;
3511 light->flags |= LIGHT_NEGATIVE;
3515 if( light->type == EMIT_SUN )
3519 light->envelope = MAX_WORLD_COORD * 8.0f;
3520 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3521 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3524 /* everything else */
3527 /* get pvs cluster for light */
3528 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3530 /* invalid cluster? */
3531 if( light->cluster < 0 )
3533 /* nudge the sample point around a bit */
3534 for( x = 0; x < 4; x++ )
3536 /* two's complement 0, 1, -1, 2, -2, etc */
3537 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3539 for( y = 0; y < 4; y++ )
3541 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3543 for( z = 0; z < 4; z++ )
3545 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3548 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3549 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3550 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3552 /* try at nudged origin */
3553 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3554 if( light->cluster < 0 )
3558 VectorCopy( origin, light->origin );
3564 /* only calculate for lights in pvs and outside of opaque brushes */
3565 if( light->cluster >= 0 )
3567 /* set light fast flag */
3569 light->flags |= LIGHT_FAST_TEMP;
3571 light->flags &= ~LIGHT_FAST_TEMP;
3572 if( light->si && light->si->noFast )
3573 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3575 /* clear light envelope */
3576 light->envelope = 0;
3578 /* handle area lights */
3579 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3581 /* ugly hack to calculate extent for area lights, but only done once */
3582 VectorScale( light->normal, -1.0f, dir );
3583 for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3587 VectorMA( light->origin, radius, light->normal, origin );
3588 factor = PointToPolygonFormFactor( origin, dir, light->w );
3591 if( (factor * light->add) <= light->falloffTolerance )
3592 light->envelope = radius;
3595 /* check for fast mode */
3596 if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3597 light->envelope = MAX_WORLD_COORD * 8.0f;
3602 intensity = light->photons;
3606 if( light->envelope <= 0.0f )
3608 /* solve distance for non-distance lights */
3609 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3610 light->envelope = MAX_WORLD_COORD * 8.0f;
3612 /* solve distance for linear lights */
3613 else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3614 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3615 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3618 add = angle * light->photons * linearScale - (dist * light->fade);
3619 T = (light->photons * linearScale) - (dist * light->fade);
3620 T + (dist * light->fade) = (light->photons * linearScale);
3621 dist * light->fade = (light->photons * linearScale) - T;
3622 dist = ((light->photons * linearScale) - T) / light->fade;
3625 /* solve for inverse square falloff */
3627 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3630 add = light->photons / (dist * dist);
3631 T = light->photons / (dist * dist);
3632 T * (dist * dist) = light->photons;
3633 dist = sqrt( light->photons / T );
3637 /* chop radius against pvs */
3640 ClearBounds( mins, maxs );
3642 /* check all leaves */
3643 for( i = 0; i < numBSPLeafs; i++ )
3646 leaf = &bspLeafs[ i ];
3649 if( leaf->cluster < 0 )
3651 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3654 /* add this leafs bbox to the bounds */
3655 VectorCopy( leaf->mins, origin );
3656 AddPointToBounds( origin, mins, maxs );
3657 VectorCopy( leaf->maxs, origin );
3658 AddPointToBounds( origin, mins, maxs );
3661 /* test to see if bounds encompass light */
3662 for( i = 0; i < 3; i++ )
3664 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3666 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3667 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3668 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3669 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3670 AddPointToBounds( light->origin, mins, maxs );
3674 /* chop the bounds by a plane for area lights and spotlights */
3675 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3676 ChopBounds( mins, maxs, light->origin, light->normal );
3679 VectorCopy( mins, light->mins );
3680 VectorCopy( maxs, light->maxs );
3682 /* reflect bounds around light origin */
3683 //% VectorMA( light->origin, -1.0f, origin, origin );
3684 VectorScale( light->origin, 2, origin );
3685 VectorSubtract( origin, maxs, origin );
3686 AddPointToBounds( origin, mins, maxs );
3687 //% VectorMA( light->origin, -1.0f, mins, origin );
3688 VectorScale( light->origin, 2, origin );
3689 VectorSubtract( origin, mins, origin );
3690 AddPointToBounds( origin, mins, maxs );
3692 /* calculate spherical bounds */
3693 VectorSubtract( maxs, light->origin, dir );
3694 radius = (float) VectorLength( dir );
3696 /* if this radius is smaller than the envelope, then set the envelope to it */
3697 if( radius < light->envelope )
3699 light->envelope = radius;
3700 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3703 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3706 /* add grid/surface only check */
3709 if( !(light->flags & LIGHT_GRID) )
3710 light->envelope = 0.0f;
3714 if( !(light->flags & LIGHT_SURFACES) )
3715 light->envelope = 0.0f;
3720 if( light->cluster < 0 || light->envelope <= 0.0f )
3723 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3725 /* delete the light */
3727 *owner = light->next;
3728 if( light->w != NULL )
3735 /* square envelope */
3736 light->envelope2 = (light->envelope * light->envelope);
3738 /* increment light count */
3741 /* set next light */
3742 owner = &((**owner).next);
3745 /* bucket sort lights by style */
3746 memset( buckets, 0, sizeof( buckets ) );
3748 for( light = lights; light != NULL; light = light2 )
3750 /* get next light */
3751 light2 = light->next;
3753 /* filter into correct bucket */
3754 light->next = buckets[ light->style ];
3755 buckets[ light->style ] = light;
3757 /* if any styled light is present, automatically set nocollapse */
3758 if( light->style != LS_NORMAL )
3762 /* filter back into light list */
3764 for( i = 255; i >= 0; i-- )
3767 for( light = buckets[ i ]; light != NULL; light = light2 )
3769 light2 = light->next;
3770 light->next = lights;
3775 /* emit some statistics */
3776 Sys_Printf( "%9d total lights\n", numLights );
3777 Sys_Printf( "%9d culled lights\n", numCulledLights );
3783 CreateTraceLightsForBounds()
3784 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3787 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3791 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3792 float radius, dist, length;
3795 /* potential pre-setup */
3796 if( numLights == 0 )
3797 SetupEnvelopes( qfalse, fast );
3800 //% 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 ] );
3802 /* allocate the light list */
3803 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3804 trace->numLights = 0;
3806 /* calculate spherical bounds */
3807 VectorAdd( mins, maxs, origin );
3808 VectorScale( origin, 0.5f, origin );
3809 VectorSubtract( maxs, origin, dir );
3810 radius = (float) VectorLength( dir );
3812 /* get length of normal vector */
3813 if( normal != NULL )
3814 length = VectorLength( normal );
3817 normal = nullVector;
3821 /* test each light and see if it reaches the sphere */
3822 /* note: the attenuation code MUST match LightingAtSample() */
3823 for( light = lights; light; light = light->next )
3825 /* check zero sized envelope */
3826 if( light->envelope <= 0 )
3828 lightsEnvelopeCulled++;
3833 if( !(light->flags & flags) )
3836 /* sunlight skips all this nonsense */
3837 if( light->type != EMIT_SUN )
3843 /* check against pvs cluster */
3844 if( numClusters > 0 && clusters != NULL )
3846 for( i = 0; i < numClusters; i++ )
3848 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3853 if( i == numClusters )
3855 lightsClusterCulled++;
3860 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3861 VectorSubtract( light->origin, origin, dir );
3862 dist = VectorLength( dir );
3863 dist -= light->envelope;
3867 lightsEnvelopeCulled++;
3871 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3874 for( i = 0; i < 3; i++ )
3876 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3881 lightsBoundsCulled++;
3887 /* planar surfaces (except twosided surfaces) have a couple more checks */
3888 if( length > 0.0f && trace->twoSided == qfalse )
3890 /* lights coplanar with a surface won't light it */
3891 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3893 lightsPlaneCulled++;
3897 /* check to see if light is behind the plane */
3898 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3900 lightsPlaneCulled++;
3905 /* add this light */
3906 trace->lights[ trace->numLights++ ] = light;
3909 /* make last night null */
3910 trace->lights[ trace->numLights ] = NULL;
3915 void FreeTraceLights( trace_t *trace )
3917 if( trace->lights != NULL )
3918 free( trace->lights );
3924 CreateTraceLightsForSurface()
3925 creates a list of lights that can potentially affect a drawsurface
3928 void CreateTraceLightsForSurface( int num, trace_t *trace )
3931 vec3_t mins, maxs, normal;
3933 bspDrawSurface_t *ds;
3934 surfaceInfo_t *info;
3941 /* get drawsurface and info */
3942 ds = &bspDrawSurfaces[ num ];
3943 info = &surfaceInfos[ num ];
3945 /* get the mins/maxs for the dsurf */
3946 ClearBounds( mins, maxs );
3947 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3948 for( i = 0; i < ds->numVerts; i++ )
3950 dv = &yDrawVerts[ ds->firstVert + i ];
3951 AddPointToBounds( dv->xyz, mins, maxs );
3952 if( !VectorCompare( dv->normal, normal ) )
3953 VectorClear( normal );
3956 /* create the lights for the bounding box */
3957 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3960 /////////////////////////////////////////////////////////////
3962 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
3963 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
3964 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
3965 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3967 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3968 static int numFloodVectors = 0;
3970 void SetupFloodLight( void )
3973 float angle, elevation, angleStep, elevationStep;
3975 double v1,v2,v3,v4,v5;
3978 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3980 /* calculate angular steps */
3981 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3982 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3986 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3988 /* iterate elevation */
3989 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3991 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3992 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3993 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3998 /* emit some statistics */
3999 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4002 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4004 if( value[ 0 ] != '\0' )
4007 v4=floodlightDistance;
4008 v5=floodlightIntensity;
4010 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
4012 floodlightRGB[0]=v1;
4013 floodlightRGB[1]=v2;
4014 floodlightRGB[2]=v3;
4016 if (VectorLength(floodlightRGB)==0)
4018 VectorSet(floodlightRGB,240,240,255);
4024 floodlightDistance=v4;
4025 floodlightIntensity=v5;
4027 floodlighty = qtrue;
4028 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4032 VectorSet(floodlightRGB,240,240,255);
4033 //floodlighty = qtrue;
4034 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4036 VectorNormalize(floodlightRGB,floodlightRGB);
4040 FloodLightForSample()
4041 calculates floodlight value for a given sample
4042 once again, kudos to the dirtmapping coder
4045 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4051 float gatherLight, outLight;
4052 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4060 if( trace == NULL || trace->cluster < 0 )
4065 dd = floodLightDistance;
4066 VectorCopy( trace->normal, normal );
4068 /* check if the normal is aligned to the world-up */
4069 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4071 if( normal[ 2 ] == 1.0f )
4073 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4074 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4076 else if( normal[ 2 ] == -1.0f )
4078 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4079 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4084 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4085 CrossProduct( normal, worldUp, myRt );
4086 VectorNormalize( myRt, myRt );
4087 CrossProduct( myRt, normal, myUp );
4088 VectorNormalize( myUp, myUp );
4091 /* vortex: optimise floodLightLowQuality a bit */
4092 if ( floodLightLowQuality == qtrue )
4094 /* iterate through ordered vectors */
4095 for( i = 0; i < numFloodVectors; i++ )
4096 if (rand()%10 != 0 ) continue;
4100 /* iterate through ordered vectors */
4101 for( i = 0; i < numFloodVectors; i++ )
4105 /* transform vector into tangent space */
4106 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4107 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4108 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4111 VectorMA( trace->origin, dd, direction, trace->end );
4113 //VectorMA( trace->origin, 1, direction, trace->origin );
4115 SetupTrace( trace );
4120 if (trace->compileFlags & C_SKY )
4124 else if ( trace->opaque )
4126 VectorSubtract( trace->hit, trace->origin, displacement );
4127 d=VectorLength( displacement );
4129 // d=trace->distance;
4130 //if (d>256) gatherDirt+=1;
4132 if (contribution>1) contribution=1.0f;
4134 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4137 gatherLight+=contribution;
4142 if( gatherLight <= 0.0f )
4150 outLight=gatherLight;
4151 if( outLight > 1.0f )
4154 /* return to sender */
4159 FloodLightRawLightmap
4160 lighttracer style ambient occlusion light hack.
4161 Kudos to the dirtmapping author for most of this source.
4162 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4163 VorteX: fixed problems with deluxemapping
4166 // floodlight pass on a lightmap
4167 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4169 int i, x, y, *cluster;
4170 float *origin, *normal, *floodlight, floodLightAmount;
4171 surfaceInfo_t *info;
4174 // float samples, average, *floodlight2;
4176 memset(&trace,0,sizeof(trace_t));
4179 trace.testOcclusion = qtrue;
4180 trace.forceSunlight = qfalse;
4181 trace.twoSided = qtrue;
4182 trace.recvShadows = lm->recvShadows;
4183 trace.numSurfaces = lm->numLightSurfaces;
4184 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4185 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4186 trace.testAll = qfalse;
4187 trace.distance = 1024;
4189 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4190 //trace.twoSided = qfalse;
4191 for( i = 0; i < trace.numSurfaces; i++ )
4194 info = &surfaceInfos[ trace.surfaces[ i ] ];
4196 /* check twosidedness */
4197 if( info->si->twoSided )
4199 trace.twoSided = qtrue;
4204 /* gather floodlight */
4205 for( y = 0; y < lm->sh; y++ )
4207 for( x = 0; x < lm->sw; x++ )
4210 cluster = SUPER_CLUSTER( x, y );
4211 origin = SUPER_ORIGIN( x, y );
4212 normal = SUPER_NORMAL( x, y );
4213 floodlight = SUPER_FLOODLIGHT( x, y );
4215 /* set default dirt */
4218 /* only look at mapped luxels */
4223 trace.cluster = *cluster;
4224 VectorCopy( origin, trace.origin );
4225 VectorCopy( normal, trace.normal );
4227 /* get floodlight */
4228 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4230 /* add floodlight */
4231 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4232 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4233 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4234 floodlight[3] += floodlightDirectionScale;
4238 /* testing no filtering */
4244 for( y = 0; y < lm->sh; y++ )
4246 for( x = 0; x < lm->sw; x++ )
4249 cluster = SUPER_CLUSTER( x, y );
4250 floodlight = SUPER_FLOODLIGHT(x, y );
4252 /* filter dirt by adjacency to unmapped luxels */
4253 average = *floodlight;
4255 for( sy = (y - 1); sy <= (y + 1); sy++ )
4257 if( sy < 0 || sy >= lm->sh )
4260 for( sx = (x - 1); sx <= (x + 1); sx++ )
4262 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4265 /* get neighboring luxel */
4266 cluster = SUPER_CLUSTER( sx, sy );
4267 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4268 if( *cluster < 0 || *floodlight2 <= 0.0f )
4272 average += *floodlight2;
4277 if( samples <= 0.0f )
4282 if( samples <= 0.0f )
4286 *floodlight = average / samples;
4292 void FloodLightRawLightmap( int rawLightmapNum )
4296 /* bail if this number exceeds the number of raw lightmaps */
4297 if( rawLightmapNum >= numRawLightmaps )
4300 lm = &rawLightmaps[ rawLightmapNum ];
4303 if (floodlighty && floodlightIntensity)
4304 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4307 if (lm->floodlightIntensity)
4309 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4310 numSurfacesFloodlighten += 1;
4314 void FloodlightRawLightmaps()
4316 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4317 numSurfacesFloodlighten = 0;
4318 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4319 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4323 FloodLightIlluminate()
4324 illuminate floodlight into lightmap luxels
4327 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4329 float *luxel, *floodlight, *deluxel, *normal;
4332 int x, y, lightmapNum;
4334 /* walk lightmaps */
4335 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4338 if( lm->superLuxels[ lightmapNum ] == NULL )
4341 /* apply floodlight to each luxel */
4342 for( y = 0; y < lm->sh; y++ )
4344 for( x = 0; x < lm->sw; x++ )
4346 /* get floodlight */
4347 floodlight = SUPER_FLOODLIGHT( x, y );
4348 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4352 cluster = SUPER_CLUSTER( x, y );
4354 /* only process mapped luxels */
4358 /* get particulars */
4359 luxel = SUPER_LUXEL( lightmapNum, x, y );
4360 deluxel = SUPER_DELUXEL( x, y );
4362 /* add to lightmap */
4363 luxel[0]+=floodlight[0];
4364 luxel[1]+=floodlight[1];
4365 luxel[2]+=floodlight[2];
4367 if (luxel[3]==0) luxel[3]=1;
4369 /* add to deluxemap */
4370 if (deluxemap && floodlight[3] > 0)
4374 normal = SUPER_NORMAL( x, y );
4375 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4377 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4378 if(brightness < 0.00390625f)
4379 brightness = 0.00390625f;
4381 VectorScale( normal, brightness, lightvector );
4382 VectorAdd( deluxel, lightvector, deluxel );