1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale ){
54 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
55 if ( scale <= 0.0f ) {
59 scale *= lightmapBrightness;
61 /* make a local copy */
62 VectorScale( color, scale, sample );
65 gamma = 1.0f / lightmapGamma;
66 for ( i = 0; i < 3; i++ )
68 /* handle negative light */
69 if ( sample[ i ] < 0.0f ) {
75 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
78 if ( lightmapExposure == 0 ) {
79 /* clamp with color normalization */
81 if ( sample[ 1 ] > max ) {
84 if ( sample[ 2 ] > max ) {
88 VectorScale( sample, ( 255.0f / max ), sample );
93 inv = 1.f / lightmapExposure;
97 if ( sample[ 1 ] > max ) {
100 if ( sample[ 2 ] > max ) {
104 dif = ( 1 - exp( -max * inv ) ) * 255;
114 for ( i = 0; i < 3; i++ )
121 /* compensate for ingame overbrighting/bitshifting */
122 VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
125 if ( lightmapContrast != 1.0f ){
126 for ( i = 0; i < 3; i++ ){
127 sample[i] = lightmapContrast * ( sample[i] - 128 ) + 128;
128 if ( sample[i] < 0 ){
132 if ( ( sample[0] > 255 ) || ( sample[1] > 255 ) || ( sample[2] > 255 ) ) {
133 max = sample[0] > sample[1] ? sample[0] : sample[1];
134 max = max > sample[2] ? max : sample[2];
135 sample[0] = sample[0] * 255 / max;
136 sample[1] = sample[1] * 255 / max;
137 sample[2] = sample[2] * 255 / max;
142 if ( lightmapsRGB ) {
143 sample[0] = floor( Image_sRGBFloatFromLinearFloat( sample[0] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
144 sample[1] = floor( Image_sRGBFloatFromLinearFloat( sample[1] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
145 sample[2] = floor( Image_sRGBFloatFromLinearFloat( sample[2] * ( 1.0 / 255.0 ) ) * 255.0 + 0.5 );
149 colorBytes[ 0 ] = sample[ 0 ];
150 colorBytes[ 1 ] = sample[ 1 ];
151 colorBytes[ 2 ] = sample[ 2 ];
156 /* -------------------------------------------------------------------------------
158 this section deals with phong shading (normal interpolation across brush faces)
160 ------------------------------------------------------------------------------- */
164 smooths together coincident vertex normals across the bsp
167 #define MAX_SAMPLES 256
168 #define THETA_EPSILON 0.000001
169 #define EQUAL_NORMAL_EPSILON 0.01
171 void SmoothNormals( void ){
172 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
173 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
174 bspDrawSurface_t *ds;
178 vec3_t average, diff;
179 int indexes[ MAX_SAMPLES ];
180 vec3_t votes[ MAX_SAMPLES ];
183 /* allocate shade angle table */
184 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
185 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
187 /* allocate smoothed table */
188 cs = ( numBSPDrawVerts / 8 ) + 1;
189 smoothed = safe_malloc( cs );
190 memset( smoothed, 0, cs );
192 /* set default shade angle */
193 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
196 /* run through every surface and flag verts belonging to non-lightmapped surfaces
197 and set per-vertex smoothing angle */
198 for ( i = 0; i < numBSPDrawSurfaces; i++ )
201 ds = &bspDrawSurfaces[ i ];
203 /* get shader for shade angle */
204 si = surfaceInfos[ i ].si;
205 if ( si->shadeAngleDegrees ) {
206 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
209 shadeAngle = defaultShadeAngle;
211 if ( shadeAngle > maxShadeAngle ) {
212 maxShadeAngle = shadeAngle;
216 for ( j = 0; j < ds->numVerts; j++ )
218 f = ds->firstVert + j;
219 shadeAngles[ f ] = shadeAngle;
220 if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
221 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
225 /* ydnar: optional force-to-trisoup */
226 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
227 ds->surfaceType = MST_TRIANGLE_SOUP;
228 ds->lightmapNum[ 0 ] = -3;
232 /* bail if no surfaces have a shade angle */
233 if ( maxShadeAngle == 0 ) {
241 start = I_FloatTime();
243 /* go through the list of vertexes */
244 for ( i = 0; i < numBSPDrawVerts; i++ )
247 f = 10 * i / numBSPDrawVerts;
250 Sys_Printf( "%i...", f );
253 /* already smoothed? */
254 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
259 VectorClear( average );
263 /* build a table of coincident vertexes */
264 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
266 /* already smoothed? */
267 if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
272 if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
276 /* use smallest shade angle */
277 shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
279 /* check shade angle */
280 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
284 else if ( dot < -1.0 ) {
287 testAngle = acos( dot ) + THETA_EPSILON;
288 if ( testAngle >= shadeAngle ) {
289 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
292 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
294 /* add to the list */
295 indexes[ numVerts++ ] = j;
298 smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
300 /* see if this normal has already been voted */
301 for ( k = 0; k < numVotes; k++ )
303 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
304 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
305 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
306 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
311 /* add a new vote? */
312 if ( k == numVotes && numVotes < MAX_SAMPLES ) {
313 VectorAdd( average, bspDrawVerts[ j ].normal, average );
314 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
319 /* don't average for less than 2 verts */
320 if ( numVerts < 2 ) {
325 if ( VectorNormalize( average, average ) > 0 ) {
327 for ( j = 0; j < numVerts; j++ )
328 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
332 /* free the tables */
337 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
342 /* -------------------------------------------------------------------------------
344 this section deals with phong shaded lightmap tracing
346 ------------------------------------------------------------------------------- */
348 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
352 calculates the st tangent vectors for normalmapping
355 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
361 /* calculate barycentric basis for the triangle */
362 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 ] );
363 if ( fabs( bb ) < 0.00000001f ) {
368 for ( i = 0; i < numVerts; i++ )
370 /* calculate s tangent vector */
371 s = dv[ i ]->st[ 0 ] + 10.0f;
372 t = dv[ i ]->st[ 1 ];
373 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
374 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
375 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
377 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
378 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
379 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
381 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
382 VectorNormalize( stv[ i ], stv[ i ] );
384 /* calculate t tangent vector */
385 s = dv[ i ]->st[ 0 ];
386 t = dv[ i ]->st[ 1 ] + 10.0f;
387 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
388 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
389 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
391 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
392 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
393 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
395 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
396 VectorNormalize( ttv[ i ], ttv[ i ] );
399 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
400 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
403 /* return to caller */
412 perterbs the normal by the shader's normalmap in tangent space
415 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
421 VectorCopy( dv->normal, pNormal );
423 /* sample normalmap */
424 if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
428 /* remap sampled normal from [0,255] to [-1,-1] */
429 for ( i = 0; i < 3; i++ )
430 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
432 /* scale tangent vectors and add to original normal */
433 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
434 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
435 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
437 /* renormalize and return */
438 VectorNormalize( pNormal, pNormal );
445 maps a luxel for triangle bv at
449 #define BOGUS_NUDGE -99999.0f
451 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 ] ){
452 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
453 float *luxel, *origin, *normal, d, lightmapSampleOffset;
460 vec4_t sideplane, hostplane;
465 static float nudges[][ 2 ] =
467 //%{ 0, 0 }, /* try center first */
468 { -NUDGE, 0 }, /* left */
469 { NUDGE, 0 }, /* right */
470 { 0, NUDGE }, /* up */
471 { 0, -NUDGE }, /* down */
472 { -NUDGE, NUDGE }, /* left/up */
473 { NUDGE, -NUDGE }, /* right/down */
474 { NUDGE, NUDGE }, /* right/up */
475 { -NUDGE, -NUDGE }, /* left/down */
476 { BOGUS_NUDGE, BOGUS_NUDGE }
480 /* find luxel xy coords (fixme: subtract 0.5?) */
481 x = dv->lightmap[ 0 ][ 0 ];
482 y = dv->lightmap[ 0 ][ 1 ];
486 else if ( x >= lm->sw ) {
492 else if ( y >= lm->sh ) {
496 /* set shader and cluster list */
497 if ( info != NULL ) {
499 numClusters = info->numSurfaceClusters;
500 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
509 /* get luxel, origin, cluster, and normal */
510 luxel = SUPER_LUXEL( 0, x, y );
511 origin = SUPER_ORIGIN( x, y );
512 normal = SUPER_NORMAL( x, y );
513 cluster = SUPER_CLUSTER( x, y );
515 /* don't attempt to remap occluded luxels for planar surfaces */
516 if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
520 /* only average the normal for premapped luxels */
521 else if ( ( *cluster ) >= 0 ) {
522 /* do bumpmap calculations */
524 PerturbNormal( dv, si, pNormal, stv, ttv );
527 VectorCopy( dv->normal, pNormal );
530 /* add the additional normal data */
531 VectorAdd( normal, pNormal, normal );
536 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
540 /* axial lightmap projection */
541 if ( lm->vecs != NULL ) {
542 /* calculate an origin for the sample from the lightmap vectors */
543 VectorCopy( lm->origin, origin );
544 for ( i = 0; i < 3; i++ )
546 /* add unless it's the axis, which is taken care of later */
547 if ( i == lm->axisNum ) {
550 origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
553 /* project the origin onto the plane */
554 d = DotProduct( origin, plane ) - plane[ 3 ];
555 d /= plane[ lm->axisNum ];
556 origin[ lm->axisNum ] -= d;
559 /* non axial lightmap projection (explicit xyz) */
561 VectorCopy( dv->xyz, origin );
564 //////////////////////
565 //27's test to make sure samples stay within the triangle boundaries
566 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
567 //2) if it does, nudge it onto the correct side.
569 if ( worldverts != NULL && lightmapTriangleCheck ) {
570 for ( j = 0; j < 3; j++ )
572 VectorCopy( worldverts[j],cverts[j] );
574 PlaneFromPoints( hostplane,cverts[0],cverts[1],cverts[2] );
576 for ( j = 0; j < 3; j++ )
578 for ( i = 0; i < 3; i++ )
580 //build plane using 2 edges and a normal
581 next = ( i + 1 ) % 3;
583 VectorCopy( cverts[next],temp );
584 VectorAdd( temp,hostplane,temp );
585 PlaneFromPoints( sideplane,cverts[i],cverts[ next ], temp );
587 //planetest sample point
588 e = DotProduct( origin,sideplane );
589 e = e - sideplane[3];
592 //VectorClear(origin);
593 //Move the sample point back inside triangle bounds
594 origin[0] -= sideplane[0] * ( e + 1 );
595 origin[1] -= sideplane[1] * ( e + 1 );
596 origin[2] -= sideplane[2] * ( e + 1 );
598 VectorClear( origin );
605 ////////////////////////
607 /* planar surfaces have precalculated lightmap vectors for nudging */
608 if ( lm->plane != NULL ) {
609 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
610 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
611 VectorCopy( lm->plane, vecs[ 2 ] );
614 /* non-planar surfaces must calculate them */
617 if ( plane != NULL ) {
618 VectorCopy( plane, vecs[ 2 ] );
621 VectorCopy( dv->normal, vecs[ 2 ] );
623 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
626 /* push the origin off the surface a bit */
628 lightmapSampleOffset = si->lightmapSampleOffset;
631 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
633 if ( lm->axisNum < 0 ) {
634 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
636 else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
637 origin[ lm->axisNum ] -= lightmapSampleOffset;
640 origin[ lm->axisNum ] += lightmapSampleOffset;
643 VectorCopy( origin,origintwo );
644 if ( lightmapExtraVisClusterNudge ) {
645 origintwo[0] += vecs[2][0];
646 origintwo[1] += vecs[2][1];
647 origintwo[2] += vecs[2][2];
651 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
653 /* another retarded hack, storing nudge count in luxel[ 1 ] */
656 /* point in solid? (except in dark mode) */
657 if ( pointCluster < 0 && dark == qfalse ) {
658 /* nudge the the location around */
660 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
662 /* nudge the vector around a bit */
663 for ( i = 0; i < 3; i++ )
665 /* set nudged point*/
666 nudged[ i ] = origintwo[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
670 /* get pvs cluster */
671 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
672 if ( pointCluster >= 0 ) {
673 VectorCopy( nudged, origin );
679 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
680 if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
681 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
682 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
683 if ( pointCluster >= 0 ) {
684 VectorCopy( nudged, origin );
690 if ( pointCluster < 0 ) {
691 ( *cluster ) = CLUSTER_OCCLUDED;
692 VectorClear( origin );
693 VectorClear( normal );
699 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
701 /* do bumpmap calculations */
703 PerturbNormal( dv, si, pNormal, stv, ttv );
706 VectorCopy( dv->normal, pNormal );
709 /* store the cluster and normal */
710 ( *cluster ) = pointCluster;
711 VectorCopy( pNormal, normal );
713 /* store explicit mapping pass and implicit mapping pass */
728 recursively subdivides a triangle until its edges are shorter
729 than the distance between two luxels (thanks jc :)
732 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 ] ){
733 bspDrawVert_t mid, *dv2[ 3 ];
737 /* map the vertexes */
739 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
740 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
741 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
747 float *a, *b, dx, dy, dist, maxDist;
750 /* find the longest edge and split it */
753 for ( i = 0; i < 3; i++ )
756 a = dv[ i ]->lightmap[ 0 ];
757 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
760 dx = a[ 0 ] - b[ 0 ];
761 dy = a[ 1 ] - b[ 1 ];
762 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
765 if ( dist > maxDist ) {
771 /* try to early out */
772 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
777 /* split the longest edge and map it */
778 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
779 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
781 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
782 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
784 /* recurse to first triangle */
785 VectorCopy( dv, dv2 );
787 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
789 /* recurse to second triangle */
790 VectorCopy( dv, dv2 );
791 dv2[ ( max + 1 ) % 3 ] = ∣
792 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
799 seed function for MapTriangle_r()
800 requires a cw ordered triangle
803 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
806 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
807 vec3_t worldverts[ 3 ];
810 /* get plane if possible */
811 if ( lm->plane != NULL ) {
812 VectorCopy( lm->plane, plane );
813 plane[ 3 ] = lm->plane[ 3 ];
816 /* otherwise make one from the points */
817 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
821 /* check to see if we need to calculate texture->world tangent vectors */
822 if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
832 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
833 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
834 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
836 /* map the vertexes */
837 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
838 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
839 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
841 /* 2002-11-20: prefer axial triangle edges */
843 /* subdivide the triangle */
844 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
848 for ( i = 0; i < 3; i++ )
851 bspDrawVert_t *dv2[ 3 ];
855 a = dv[ i ]->lightmap[ 0 ];
856 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
858 /* make degenerate triangles for mapping edges */
859 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
861 dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
862 dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
864 /* map the degenerate triangle */
865 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
876 recursively subdivides a quad until its edges are shorter
877 than the distance between two luxels
880 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 ] ){
881 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
888 float *a, *b, dx, dy, dist, maxDist;
891 /* find the longest edge and split it */
894 for ( i = 0; i < 4; i++ )
897 a = dv[ i ]->lightmap[ 0 ];
898 b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
901 dx = a[ 0 ] - b[ 0 ];
902 dy = a[ 1 ] - b[ 1 ];
903 dist = ( dx * dx ) + ( dy * dy ); //% sqrt( (dx * dx) + (dy * dy) );
906 if ( dist > maxDist ) {
912 /* try to early out */
913 if ( max < 0 || maxDist <= subdivideThreshold ) {
918 /* we only care about even/odd edges */
921 /* split the longest edges */
922 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
923 LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
925 /* map the vertexes */
926 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
927 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
931 /* recurse to first quad */
933 dv2[ 1 ] = &mid[ 0 ];
934 dv2[ 2 ] = &mid[ 1 ];
936 MapQuad_r( lm, info, dv2, plane, stv, ttv );
938 /* recurse to second quad */
939 dv2[ 0 ] = &mid[ 0 ];
942 dv2[ 3 ] = &mid[ 1 ];
943 MapQuad_r( lm, info, dv2, plane, stv, ttv );
949 /* recurse to first quad */
952 dv2[ 2 ] = &mid[ 0 ];
953 dv2[ 3 ] = &mid[ 1 ];
954 MapQuad_r( lm, info, dv2, plane, stv, ttv );
956 /* recurse to second quad */
957 dv2[ 0 ] = &mid[ 1 ];
958 dv2[ 1 ] = &mid[ 0 ];
961 MapQuad_r( lm, info, dv2, plane, stv, ttv );
969 seed function for MapQuad_r()
970 requires a cw ordered triangle quad
973 #define QUAD_PLANAR_EPSILON 0.5f
975 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
978 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
981 /* get plane if possible */
982 if ( lm->plane != NULL ) {
983 VectorCopy( lm->plane, plane );
984 plane[ 3 ] = lm->plane[ 3 ];
987 /* otherwise make one from the points */
988 else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
992 /* 4th point must fall on the plane */
993 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
994 if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
998 /* check to see if we need to calculate texture->world tangent vectors */
999 if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
1009 /* map the vertexes */
1010 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1011 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1012 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1013 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1015 /* subdivide the quad */
1016 MapQuad_r( lm, info, dv, plane, stv, ttv );
1024 maps the locations, normals, and pvs clusters for a raw lightmap
1027 #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)
1029 void MapRawLightmap( int rawLightmapNum ){
1030 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1031 float *luxel, *origin, *normal, samples, radius, pass;
1033 bspDrawSurface_t *ds;
1034 surfaceInfo_t *info;
1035 mesh_t src, *subdivided, *mesh;
1036 bspDrawVert_t *verts, *dv[ 4 ], fake;
1039 /* bail if this number exceeds the number of raw lightmaps */
1040 if ( rawLightmapNum >= numRawLightmaps ) {
1045 lm = &rawLightmaps[ rawLightmapNum ];
1047 /* -----------------------------------------------------------------
1048 map referenced surfaces onto the raw lightmap
1049 ----------------------------------------------------------------- */
1051 /* walk the list of surfaces on this raw lightmap */
1052 for ( n = 0; n < lm->numLightSurfaces; n++ )
1054 /* with > 1 surface per raw lightmap, clear occluded */
1056 for ( y = 0; y < lm->sh; y++ )
1058 for ( x = 0; x < lm->sw; x++ )
1061 cluster = SUPER_CLUSTER( x, y );
1062 if ( *cluster < 0 ) {
1063 *cluster = CLUSTER_UNMAPPED;
1070 num = lightSurfaces[ lm->firstLightSurface + n ];
1071 ds = &bspDrawSurfaces[ num ];
1072 info = &surfaceInfos[ num ];
1074 /* bail if no lightmap to calculate */
1075 if ( info->lm != lm ) {
1080 /* map the surface onto the lightmap origin/cluster/normal buffers */
1081 switch ( ds->surfaceType )
1085 verts = yDrawVerts + ds->firstVert;
1087 /* map the triangles */
1088 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1090 for ( i = 0; i < ds->numIndexes; i += 3 )
1092 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1093 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1094 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1095 MapTriangle( lm, info, dv, mapNonAxial );
1101 /* make a mesh from the drawsurf */
1102 src.width = ds->patchWidth;
1103 src.height = ds->patchHeight;
1104 src.verts = &yDrawVerts[ ds->firstVert ];
1105 //% subdivided = SubdivideMesh( src, 8, 512 );
1106 subdivided = SubdivideMesh2( src, info->patchIterations );
1108 /* fit it to the curve and remove colinear verts on rows/columns */
1109 PutMeshOnCurve( *subdivided );
1110 mesh = RemoveLinearMeshColumnsRows( subdivided );
1111 FreeMesh( subdivided );
1114 verts = mesh->verts;
1119 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1120 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1121 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1122 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1126 /* map the mesh quads */
1129 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1131 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1133 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1136 pw[ 0 ] = x + ( y * mesh->width );
1137 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1138 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1139 pw[ 3 ] = x + 1 + ( y * mesh->width );
1140 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1145 /* get drawverts and map first triangle */
1146 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1147 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1148 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1149 MapTriangle( lm, info, dv, mapNonAxial );
1151 /* get drawverts and map second triangle */
1152 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1153 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1154 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1155 MapTriangle( lm, info, dv, mapNonAxial );
1162 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1164 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1167 pw[ 0 ] = x + ( y * mesh->width );
1168 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1169 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1170 pw[ 3 ] = x + 1 + ( y * mesh->width );
1176 /* attempt to map quad first */
1177 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1178 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1179 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1180 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1181 if ( MapQuad( lm, info, dv ) ) {
1185 for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1187 /* get drawverts and map first triangle */
1188 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1189 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1190 MapTriangle( lm, info, dv, mapNonAxial );
1192 /* get drawverts and map second triangle */
1193 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1194 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1195 MapTriangle( lm, info, dv, mapNonAxial );
1211 /* -----------------------------------------------------------------
1212 average and clean up luxel normals
1213 ----------------------------------------------------------------- */
1215 /* walk the luxels */
1216 for ( y = 0; y < lm->sh; y++ )
1218 for ( x = 0; x < lm->sw; x++ )
1221 luxel = SUPER_LUXEL( 0, x, y );
1222 normal = SUPER_NORMAL( x, y );
1223 cluster = SUPER_CLUSTER( x, y );
1225 /* only look at mapped luxels */
1226 if ( *cluster < 0 ) {
1230 /* the normal data could be the sum of multiple samples */
1231 if ( luxel[ 3 ] > 1.0f ) {
1232 VectorNormalize( normal, normal );
1235 /* mark this luxel as having only one normal */
1240 /* non-planar surfaces stop here */
1241 if ( lm->plane == NULL ) {
1245 /* -----------------------------------------------------------------
1246 map occluded or unuxed luxels
1247 ----------------------------------------------------------------- */
1249 /* walk the luxels */
1250 radius = floor( superSample / 2 );
1251 radius = radius > 0 ? radius : 1.0f;
1253 for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1255 for ( y = 0; y < lm->sh; y++ )
1257 for ( x = 0; x < lm->sw; x++ )
1260 luxel = SUPER_LUXEL( 0, x, y );
1261 normal = SUPER_NORMAL( x, y );
1262 cluster = SUPER_CLUSTER( x, y );
1264 /* only look at unmapped luxels */
1265 if ( *cluster != CLUSTER_UNMAPPED ) {
1269 /* divine a normal and origin from neighboring luxels */
1270 VectorClear( fake.xyz );
1271 VectorClear( fake.normal );
1272 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1273 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1275 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1277 if ( sy < 0 || sy >= lm->sh ) {
1281 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1283 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1287 /* get neighboring luxel */
1288 luxel = SUPER_LUXEL( 0, sx, sy );
1289 origin = SUPER_ORIGIN( sx, sy );
1290 normal = SUPER_NORMAL( sx, sy );
1291 cluster = SUPER_CLUSTER( sx, sy );
1293 /* only consider luxels mapped in previous passes */
1294 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1298 /* add its distinctiveness to our own */
1299 VectorAdd( fake.xyz, origin, fake.xyz );
1300 VectorAdd( fake.normal, normal, fake.normal );
1301 samples += luxel[ 3 ];
1306 if ( samples == 0.0f ) {
1311 VectorDivide( fake.xyz, samples, fake.xyz );
1312 //% VectorDivide( fake.normal, samples, fake.normal );
1313 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1317 /* map the fake vert */
1318 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1323 /* -----------------------------------------------------------------
1324 average and clean up luxel normals
1325 ----------------------------------------------------------------- */
1327 /* walk the luxels */
1328 for ( y = 0; y < lm->sh; y++ )
1330 for ( x = 0; x < lm->sw; x++ )
1333 luxel = SUPER_LUXEL( 0, x, y );
1334 normal = SUPER_NORMAL( x, y );
1335 cluster = SUPER_CLUSTER( x, y );
1337 /* only look at mapped luxels */
1338 if ( *cluster < 0 ) {
1342 /* the normal data could be the sum of multiple samples */
1343 if ( luxel[ 3 ] > 1.0f ) {
1344 VectorNormalize( normal, normal );
1347 /* mark this luxel as having only one normal */
1355 for ( y = 0; y < lm->sh; y++ )
1357 for ( x = 0; x < lm->sw; x++ )
1362 cluster = SUPER_CLUSTER( x, y );
1363 origin = SUPER_ORIGIN( x, y );
1364 normal = SUPER_NORMAL( x, y );
1365 luxel = SUPER_LUXEL( x, y );
1367 if ( *cluster < 0 ) {
1371 /* check if within the bounding boxes of all surfaces referenced */
1372 ClearBounds( mins, maxs );
1373 for ( n = 0; n < lm->numLightSurfaces; n++ )
1376 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1377 TOL = info->sampleSize + 2;
1378 AddPointToBounds( info->mins, mins, maxs );
1379 AddPointToBounds( info->maxs, mins, maxs );
1380 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1381 origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1382 origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1388 if ( n < lm->numLightSurfaces ) {
1392 /* report bogus origin */
1393 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",
1394 rawLightmapNum, x, y, *cluster,
1395 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1396 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1397 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1408 sets up dirtmap (ambient occlusion)
1411 #define DIRT_CONE_ANGLE 88 /* degrees */
1412 #define DIRT_NUM_ANGLE_STEPS 16
1413 #define DIRT_NUM_ELEVATION_STEPS 3
1414 #define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1416 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1417 static int numDirtVectors = 0;
1419 void SetupDirt( void ){
1421 float angle, elevation, angleStep, elevationStep;
1425 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1427 /* calculate angular steps */
1428 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1429 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1433 for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1435 /* iterate elevation */
1436 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1438 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1439 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1440 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1445 /* emit some statistics */
1446 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1452 calculates dirt value for a given sample
1455 float DirtForSample( trace_t *trace ){
1457 float gatherDirt, outDirt, angle, elevation, ooDepth;
1458 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1465 if ( trace == NULL || trace->cluster < 0 ) {
1471 ooDepth = 1.0f / dirtDepth;
1472 VectorCopy( trace->normal, normal );
1474 /* check if the normal is aligned to the world-up */
1475 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
1476 if ( normal[ 2 ] == 1.0f ) {
1477 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1478 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1480 else if ( normal[ 2 ] == -1.0f ) {
1481 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1482 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1487 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1488 CrossProduct( normal, worldUp, myRt );
1489 VectorNormalize( myRt, myRt );
1490 CrossProduct( myRt, normal, myUp );
1491 VectorNormalize( myUp, myUp );
1494 /* 1 = random mode, 0 (well everything else) = non-random mode */
1495 if ( dirtMode == 1 ) {
1497 for ( i = 0; i < numDirtVectors; i++ )
1499 /* get random vector */
1500 angle = Random() * DEG2RAD( 360.0f );
1501 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1502 temp[ 0 ] = cos( angle ) * sin( elevation );
1503 temp[ 1 ] = sin( angle ) * sin( elevation );
1504 temp[ 2 ] = cos( elevation );
1506 /* transform into tangent space */
1507 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1508 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1509 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1512 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1513 SetupTrace( trace );
1514 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1518 if ( trace->opaque && !( trace->compileFlags & C_SKY ) ) {
1519 VectorSubtract( trace->hit, trace->origin, displacement );
1520 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1526 /* iterate through ordered vectors */
1527 for ( i = 0; i < numDirtVectors; i++ )
1529 /* transform vector into tangent space */
1530 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1531 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1532 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1535 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1536 SetupTrace( trace );
1537 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1541 if ( trace->opaque ) {
1542 VectorSubtract( trace->hit, trace->origin, displacement );
1543 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1549 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1550 SetupTrace( trace );
1551 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
1555 if ( trace->opaque ) {
1556 VectorSubtract( trace->hit, trace->origin, displacement );
1557 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1561 if ( gatherDirt <= 0.0f ) {
1565 /* apply gain (does this even do much? heh) */
1566 outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1567 if ( outDirt > 1.0f ) {
1572 outDirt *= dirtScale;
1573 if ( outDirt > 1.0f ) {
1577 /* return to sender */
1578 return 1.0f - outDirt;
1585 calculates dirty fraction for each luxel
1588 void DirtyRawLightmap( int rawLightmapNum ){
1589 int i, x, y, sx, sy, *cluster;
1590 float *origin, *normal, *dirt, *dirt2, average, samples;
1592 surfaceInfo_t *info;
1597 /* bail if this number exceeds the number of raw lightmaps */
1598 if ( rawLightmapNum >= numRawLightmaps ) {
1603 lm = &rawLightmaps[ rawLightmapNum ];
1606 trace.testOcclusion = qtrue;
1607 trace.forceSunlight = qfalse;
1608 trace.recvShadows = lm->recvShadows;
1609 trace.numSurfaces = lm->numLightSurfaces;
1610 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1611 trace.inhibitRadius = 0.0f;
1612 trace.testAll = qfalse;
1614 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1615 trace.twoSided = qfalse;
1616 for ( i = 0; i < trace.numSurfaces; i++ )
1619 info = &surfaceInfos[ trace.surfaces[ i ] ];
1621 /* check twosidedness */
1622 if ( info->si->twoSided ) {
1623 trace.twoSided = qtrue;
1629 for ( i = 0; i < trace.numSurfaces; i++ )
1632 info = &surfaceInfos[ trace.surfaces[ i ] ];
1634 /* check twosidedness */
1635 if ( info->si->noDirty ) {
1642 for ( y = 0; y < lm->sh; y++ )
1644 for ( x = 0; x < lm->sw; x++ )
1647 cluster = SUPER_CLUSTER( x, y );
1648 origin = SUPER_ORIGIN( x, y );
1649 normal = SUPER_NORMAL( x, y );
1650 dirt = SUPER_DIRT( x, y );
1652 /* set default dirt */
1655 /* only look at mapped luxels */
1656 if ( *cluster < 0 ) {
1660 /* don't apply dirty on this surface */
1667 trace.cluster = *cluster;
1668 VectorCopy( origin, trace.origin );
1669 VectorCopy( normal, trace.normal );
1672 *dirt = DirtForSample( &trace );
1676 /* testing no filtering */
1680 for ( y = 0; y < lm->sh; y++ )
1682 for ( x = 0; x < lm->sw; x++ )
1685 cluster = SUPER_CLUSTER( x, y );
1686 dirt = SUPER_DIRT( x, y );
1688 /* filter dirt by adjacency to unmapped luxels */
1691 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1693 if ( sy < 0 || sy >= lm->sh ) {
1697 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1699 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1703 /* get neighboring luxel */
1704 cluster = SUPER_CLUSTER( sx, sy );
1705 dirt2 = SUPER_DIRT( sx, sy );
1706 if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1716 if ( samples <= 0.0f ) {
1722 if ( samples <= 0.0f ) {
1727 *dirt = average / samples;
1736 calculates the pvs cluster, origin, normal of a sub-luxel
1739 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1740 int i, *cluster, *cluster2;
1741 float *origin, *origin2, *normal; //% , *normal2;
1742 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1745 /* calulate x vector */
1746 if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1747 cluster = SUPER_CLUSTER( x, y );
1748 origin = SUPER_ORIGIN( x, y );
1749 //% normal = SUPER_NORMAL( x, y );
1750 cluster2 = SUPER_CLUSTER( x + 1, y );
1751 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1752 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1754 else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1755 cluster = SUPER_CLUSTER( x - 1, y );
1756 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1757 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1758 cluster2 = SUPER_CLUSTER( x, y );
1759 origin2 = SUPER_ORIGIN( x, y );
1760 //% normal2 = SUPER_NORMAL( x, y );
1763 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap S vector\n" );
1766 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1767 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1769 /* calulate y vector */
1770 if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1771 cluster = SUPER_CLUSTER( x, y );
1772 origin = SUPER_ORIGIN( x, y );
1773 //% normal = SUPER_NORMAL( x, y );
1774 cluster2 = SUPER_CLUSTER( x, y + 1 );
1775 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1776 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1778 else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1779 cluster = SUPER_CLUSTER( x, y - 1 );
1780 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1781 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1782 cluster2 = SUPER_CLUSTER( x, y );
1783 origin2 = SUPER_ORIGIN( x, y );
1784 //% normal2 = SUPER_NORMAL( x, y );
1787 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1790 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1792 /* calculate new origin */
1793 for ( i = 0; i < 3; i++ )
1794 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1797 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1798 if ( *sampleCluster < 0 ) {
1802 /* calculate new normal */
1803 normal = SUPER_NORMAL( x, y );
1804 VectorCopy( normal, sampleNormal );
1812 SubsampleRawLuxel_r()
1813 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1816 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1817 int b, samples, mapped, lighted;
1820 vec3_t deluxel[ 3 ];
1821 vec3_t origin[ 4 ], normal[ 4 ];
1822 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1823 vec3_t color, direction = { 0, 0, 0 }, total;
1827 if ( lightLuxel[ 3 ] >= lightSamples ) {
1832 VectorClear( total );
1836 /* make 2x2 subsample stamp */
1837 for ( b = 0; b < 4; b++ )
1840 VectorCopy( sampleOrigin, origin[ b ] );
1842 /* calculate position */
1843 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1849 /* increment sample count */
1850 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1853 trace->cluster = *cluster;
1854 VectorCopy( origin[ b ], trace->origin );
1855 VectorCopy( normal[ b ], trace->normal );
1859 LightContributionToSample( trace );
1860 if ( trace->forceSubsampling > 1.0f ) {
1861 /* alphashadow: we subsample as deep as we can */
1867 /* add to totals (fixme: make contrast function) */
1868 VectorCopy( trace->color, luxel[ b ] );
1869 if ( lightDeluxel ) {
1870 VectorCopy( trace->directionContribution, deluxel[ b ] );
1872 VectorAdd( total, trace->color, total );
1873 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1878 /* subsample further? */
1879 if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1880 ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1881 lighted != 0 && lighted != mapped ) {
1882 for ( b = 0; b < 4; b++ )
1884 if ( cluster[ b ] < 0 ) {
1887 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1892 //% VectorClear( color );
1894 VectorCopy( lightLuxel, color );
1895 if ( lightDeluxel ) {
1896 VectorCopy( lightDeluxel, direction );
1899 for ( b = 0; b < 4; b++ )
1901 if ( cluster[ b ] < 0 ) {
1904 VectorAdd( color, luxel[ b ], color );
1905 if ( lightDeluxel ) {
1906 VectorAdd( direction, deluxel[ b ], direction );
1912 if ( samples > 0 ) {
1914 color[ 0 ] /= samples;
1915 color[ 1 ] /= samples;
1916 color[ 2 ] /= samples;
1919 VectorCopy( color, lightLuxel );
1920 lightLuxel[ 3 ] += 1.0f;
1922 if ( lightDeluxel ) {
1923 direction[ 0 ] /= samples;
1924 direction[ 1 ] /= samples;
1925 direction[ 2 ] /= samples;
1926 VectorCopy( direction, lightDeluxel );
1931 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1932 static void GaussLikeRandom( float sigma, float *x, float *y ){
1934 r = Random() * 2 * Q_PI;
1935 *x = sigma * 2.73861278752581783822 * cos( r );
1936 *y = sigma * 2.73861278752581783822 * sin( r );
1943 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1946 vec3_t origin, normal;
1947 vec3_t total, totaldirection;
1950 VectorClear( total );
1951 VectorClear( totaldirection );
1953 for ( b = 0; b < lightSamples; ++b )
1956 VectorCopy( sampleOrigin, origin );
1957 GaussLikeRandom( bias, &dx, &dy );
1959 /* calculate position */
1960 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1966 trace->cluster = cluster;
1967 VectorCopy( origin, trace->origin );
1968 VectorCopy( normal, trace->normal );
1970 LightContributionToSample( trace );
1971 VectorAdd( total, trace->color, total );
1972 if ( lightDeluxel ) {
1973 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1980 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1981 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1982 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1984 if ( lightDeluxel ) {
1985 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1986 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1987 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1995 IlluminateRawLightmap()
1996 illuminates the luxels
1999 #define STACK_LL_SIZE ( SUPER_LUXEL_SIZE * 64 * 64 )
2000 #define LIGHT_LUXEL( x, y ) ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
2001 #define LIGHT_DELUXEL( x, y ) ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
2003 void IlluminateRawLightmap( int rawLightmapNum ){
2004 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
2005 int *cluster, *cluster2, mapped, lighted, totalLighted;
2006 size_t llSize, ldSize;
2008 surfaceInfo_t *info;
2009 qboolean filterColor, filterDir;
2011 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
2012 unsigned char *flag;
2013 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2014 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2015 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2017 float stackLightLuxels[ STACK_LL_SIZE ];
2020 /* bail if this number exceeds the number of raw lightmaps */
2021 if ( rawLightmapNum >= numRawLightmaps ) {
2026 lm = &rawLightmaps[ rawLightmapNum ];
2029 trace.testOcclusion = !noTrace;
2030 trace.forceSunlight = qfalse;
2031 trace.recvShadows = lm->recvShadows;
2032 trace.numSurfaces = lm->numLightSurfaces;
2033 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2034 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2036 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2037 trace.twoSided = qfalse;
2038 for ( i = 0; i < trace.numSurfaces; i++ )
2041 info = &surfaceInfos[ trace.surfaces[ i ] ];
2043 /* check twosidedness */
2044 if ( info->si->twoSided ) {
2045 trace.twoSided = qtrue;
2050 /* create a culled light list for this raw lightmap */
2051 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2053 /* -----------------------------------------------------------------
2055 ----------------------------------------------------------------- */
2058 numLuxelsIlluminated += ( lm->sw * lm->sh );
2060 /* test debugging state */
2061 if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2062 /* debug fill the luxels */
2063 for ( y = 0; y < lm->sh; y++ )
2065 for ( x = 0; x < lm->sw; x++ )
2068 cluster = SUPER_CLUSTER( x, y );
2070 /* only fill mapped luxels */
2071 if ( *cluster < 0 ) {
2075 /* get particulars */
2076 luxel = SUPER_LUXEL( 0, x, y );
2077 origin = SUPER_ORIGIN( x, y );
2078 normal = SUPER_NORMAL( x, y );
2080 /* color the luxel with raw lightmap num? */
2081 if ( debugSurfaces ) {
2082 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2085 /* color the luxel with lightmap axis? */
2086 else if ( debugAxis ) {
2087 luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2088 luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2089 luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2092 /* color the luxel with luxel cluster? */
2093 else if ( debugCluster ) {
2094 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2097 /* color the luxel with luxel origin? */
2098 else if ( debugOrigin ) {
2099 VectorSubtract( lm->maxs, lm->mins, temp );
2100 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2101 VectorSubtract( origin, lm->mins, temp2 );
2102 luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2103 luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2104 luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2107 /* color the luxel with the normal */
2108 else if ( normalmap ) {
2109 luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2110 luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2111 luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2114 /* otherwise clear it */
2116 VectorClear( luxel );
2126 /* allocate temporary per-light luxel storage */
2127 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2128 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2129 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2130 lightLuxels = stackLightLuxels;
2133 lightLuxels = safe_malloc( llSize );
2136 lightDeluxels = safe_malloc( ldSize );
2139 lightDeluxels = NULL;
2143 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2145 /* set ambient color */
2146 for ( y = 0; y < lm->sh; y++ )
2148 for ( x = 0; x < lm->sw; x++ )
2151 cluster = SUPER_CLUSTER( x, y );
2152 luxel = SUPER_LUXEL( 0, x, y );
2153 normal = SUPER_NORMAL( x, y );
2154 deluxel = SUPER_DELUXEL( x, y );
2156 /* blacken unmapped clusters */
2157 if ( *cluster < 0 ) {
2158 VectorClear( luxel );
2164 VectorCopy( ambientColor, luxel );
2166 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2168 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2169 if ( brightness < 0.00390625f ) {
2170 brightness = 0.00390625f;
2173 VectorScale( normal, brightness, deluxel );
2180 /* clear styled lightmaps */
2181 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2182 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2184 if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2185 memset( lm->superLuxels[ lightmapNum ], 0, size );
2189 /* debugging code */
2190 //% if( trace.numLights <= 0 )
2191 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2193 /* walk light list */
2194 for ( i = 0; i < trace.numLights; i++ )
2197 trace.light = trace.lights[ i ];
2200 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2202 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2203 lm->styles[ lightmapNum ] == LS_NONE ) {
2208 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2209 if ( lightmapNum >= MAX_LIGHTMAPS ) {
2210 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2215 memset( lightLuxels, 0, llSize );
2217 memset( lightDeluxels, 0, ldSize );
2221 /* determine filter radius */
2222 filterRadius = lm->filterRadius > trace.light->filterRadius
2224 : trace.light->filterRadius;
2225 if ( filterRadius < 0.0f ) {
2226 filterRadius = 0.0f;
2229 /* set luxel filter radius */
2230 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2231 if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2232 luxelFilterRadius = 1;
2235 /* allocate sampling flags storage */
2236 if ( lightSamples > 1 || lightRandomSamples ) {
2237 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2238 if ( lm->superFlags == NULL ) {
2239 lm->superFlags = safe_malloc( size );
2241 memset( (void *) lm->superFlags, 0, size );
2244 /* initial pass, one sample per luxel */
2245 for ( y = 0; y < lm->sh; y++ )
2247 for ( x = 0; x < lm->sw; x++ )
2250 cluster = SUPER_CLUSTER( x, y );
2251 if ( *cluster < 0 ) {
2255 /* get particulars */
2256 lightLuxel = LIGHT_LUXEL( x, y );
2257 lightDeluxel = LIGHT_DELUXEL( x, y );
2258 origin = SUPER_ORIGIN( x, y );
2259 normal = SUPER_NORMAL( x, y );
2260 flag = SUPER_FLAG( x, y );
2262 /* set contribution count */
2263 lightLuxel[ 3 ] = 1.0f;
2266 trace.cluster = *cluster;
2267 VectorCopy( origin, trace.origin );
2268 VectorCopy( normal, trace.normal );
2270 /* get light for this sample */
2271 LightContributionToSample( &trace );
2272 VectorCopy( trace.color, lightLuxel );
2274 /* add the contribution to the deluxemap */
2276 VectorCopy( trace.directionContribution, lightDeluxel );
2279 /* check for evilness */
2280 if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) ) {
2282 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2285 else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2291 /* don't even bother with everything else if nothing was lit */
2292 if ( totalLighted == 0 ) {
2296 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2297 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2298 if ( lightSamples > 1 || lightRandomSamples ) {
2300 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2302 for ( x = 0; x < ( lm->sw - 1 ); x++ )
2307 VectorClear( total );
2309 /* test 2x2 stamp */
2310 for ( t = 0; t < 4; t++ )
2312 /* set sample coords */
2313 sx = x + tests[ t ][ 0 ];
2314 sy = y + tests[ t ][ 1 ];
2317 cluster = SUPER_CLUSTER( sx, sy );
2318 if ( *cluster < 0 ) {
2324 flag = SUPER_FLAG( sx, sy );
2325 if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2326 /* force a lighted/mapped discrepancy so we subsample */
2331 lightLuxel = LIGHT_LUXEL( sx, sy );
2332 VectorAdd( total, lightLuxel, total );
2333 if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2338 /* if total color is under a certain amount, then don't bother subsampling */
2339 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2343 /* if all 4 pixels are either in shadow or light, then don't subsample */
2344 if ( lighted != 0 && lighted != mapped ) {
2345 for ( t = 0; t < 4; t++ )
2347 /* set sample coords */
2348 sx = x + tests[ t ][ 0 ];
2349 sy = y + tests[ t ][ 1 ];
2352 cluster = SUPER_CLUSTER( sx, sy );
2353 if ( *cluster < 0 ) {
2356 flag = SUPER_FLAG( sx, sy );
2357 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2360 lightLuxel = LIGHT_LUXEL( sx, sy );
2361 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2362 origin = SUPER_ORIGIN( sx, sy );
2364 /* only subsample shadowed luxels */
2365 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2369 if ( lightRandomSamples ) {
2370 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2373 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2376 *flag |= FLAG_ALREADY_SUBSAMPLED;
2378 /* debug code to colorize subsampled areas to yellow */
2379 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2380 //% VectorSet( luxel, 255, 204, 0 );
2387 /* tertiary pass, apply dirt map (ambient occlusion) */
2390 for ( y = 0; y < lm->sh; y++ )
2392 for ( x = 0; x < lm->sw; x++ )
2395 cluster = SUPER_CLUSTER( x, y );
2396 if ( *cluster < 0 ) {
2400 /* get particulars */
2401 lightLuxel = LIGHT_LUXEL( x, y );
2402 dirt = SUPER_DIRT( x, y );
2404 /* scale light value */
2405 VectorScale( lightLuxel, *dirt, lightLuxel );
2410 /* allocate sampling lightmap storage */
2411 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2412 /* allocate sampling lightmap storage */
2413 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2414 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2415 memset( lm->superLuxels[ lightmapNum ], 0, size );
2419 if ( lightmapNum > 0 ) {
2420 lm->styles[ lightmapNum ] = trace.light->style;
2421 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2424 /* copy to permanent luxels */
2425 for ( y = 0; y < lm->sh; y++ )
2427 for ( x = 0; x < lm->sw; x++ )
2429 /* get cluster and origin */
2430 cluster = SUPER_CLUSTER( x, y );
2431 if ( *cluster < 0 ) {
2434 origin = SUPER_ORIGIN( x, y );
2437 if ( luxelFilterRadius ) {
2439 VectorClear( averageColor );
2440 VectorClear( averageDir );
2443 /* cheaper distance-based filtering */
2444 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2446 if ( sy < 0 || sy >= lm->sh ) {
2450 for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2452 if ( sx < 0 || sx >= lm->sw ) {
2456 /* get particulars */
2457 cluster = SUPER_CLUSTER( sx, sy );
2458 if ( *cluster < 0 ) {
2461 lightLuxel = LIGHT_LUXEL( sx, sy );
2462 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2465 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2466 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2468 /* scale luxel by filter weight */
2469 VectorScale( lightLuxel, weight, color );
2470 VectorAdd( averageColor, color, averageColor );
2472 VectorScale( lightDeluxel, weight, direction );
2473 VectorAdd( averageDir, direction, averageDir );
2480 if ( samples <= 0.0f ) {
2484 /* scale into luxel */
2485 luxel = SUPER_LUXEL( lightmapNum, x, y );
2488 /* handle negative light */
2489 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2490 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2491 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2492 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2495 /* handle normal light */
2498 luxel[ 0 ] += averageColor[ 0 ] / samples;
2499 luxel[ 1 ] += averageColor[ 1 ] / samples;
2500 luxel[ 2 ] += averageColor[ 2 ] / samples;
2504 /* scale into luxel */
2505 deluxel = SUPER_DELUXEL( x, y );
2506 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2507 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2508 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2515 /* get particulars */
2516 lightLuxel = LIGHT_LUXEL( x, y );
2517 lightDeluxel = LIGHT_DELUXEL( x, y );
2518 luxel = SUPER_LUXEL( lightmapNum, x, y );
2519 deluxel = SUPER_DELUXEL( x, y );
2521 /* handle negative light */
2522 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2523 VectorScale( averageColor, -1.0f, averageColor );
2529 /* handle negative light */
2530 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2531 VectorSubtract( luxel, lightLuxel, luxel );
2534 /* handle normal light */
2536 VectorAdd( luxel, lightLuxel, luxel );
2540 VectorAdd( deluxel, lightDeluxel, deluxel );
2547 /* free temporary luxels */
2548 if ( lightLuxels != stackLightLuxels ) {
2549 free( lightLuxels );
2553 free( lightDeluxels );
2557 /* free light list */
2558 FreeTraceLights( &trace );
2560 /* floodlight pass */
2561 if ( floodlighty ) {
2562 FloodlightIlluminateLightmap( lm );
2565 if ( debugnormals ) {
2566 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2569 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2573 for ( y = 0; y < lm->sh; y++ )
2575 for ( x = 0; x < lm->sw; x++ )
2578 cluster = SUPER_CLUSTER( x, y );
2579 //% if( *cluster < 0 )
2582 /* get particulars */
2583 luxel = SUPER_LUXEL( lightmapNum, x, y );
2584 normal = SUPER_NORMAL( x, y );
2586 luxel[0] = ( normal[0] * 127 ) + 127;
2587 luxel[1] = ( normal[1] * 127 ) + 127;
2588 luxel[2] = ( normal[2] * 127 ) + 127;
2594 /* -----------------------------------------------------------------
2596 ----------------------------------------------------------------- */
2599 /* walk lightmaps */
2600 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2603 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2607 /* apply dirt to each luxel */
2608 for ( y = 0; y < lm->sh; y++ )
2610 for ( x = 0; x < lm->sw; x++ )
2613 cluster = SUPER_CLUSTER( x, y );
2615 /* get particulars */
2616 luxel = SUPER_LUXEL( lightmapNum, x, y );
2617 dirt = SUPER_DIRT( x, y );
2620 VectorScale( luxel, *dirt, luxel );
2624 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2631 /* -----------------------------------------------------------------
2633 ----------------------------------------------------------------- */
2635 /* walk lightmaps */
2636 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2639 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2643 /* average occluded luxels from neighbors */
2644 for ( y = 0; y < lm->sh; y++ )
2646 for ( x = 0; x < lm->sw; x++ )
2648 /* get particulars */
2649 cluster = SUPER_CLUSTER( x, y );
2650 luxel = SUPER_LUXEL( lightmapNum, x, y );
2651 deluxel = SUPER_DELUXEL( x, y );
2652 normal = SUPER_NORMAL( x, y );
2654 /* determine if filtering is necessary */
2655 filterColor = qfalse;
2657 if ( *cluster < 0 ||
2658 ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2659 filterColor = qtrue;
2662 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2666 if ( !filterColor && !filterDir ) {
2670 /* choose seed amount */
2671 VectorClear( averageColor );
2672 VectorClear( averageDir );
2675 /* walk 3x3 matrix */
2676 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2678 if ( sy < 0 || sy >= lm->sh ) {
2682 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2684 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2688 /* get neighbor's particulars */
2689 cluster2 = SUPER_CLUSTER( sx, sy );
2690 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2691 deluxel2 = SUPER_DELUXEL( sx, sy );
2693 /* ignore unmapped/unlit luxels */
2694 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2695 ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2699 /* add its distinctiveness to our own */
2700 VectorAdd( averageColor, luxel2, averageColor );
2701 samples += luxel2[ 3 ];
2703 VectorAdd( averageDir, deluxel2, averageDir );
2709 if ( samples <= 0.0f ) {
2713 /* dark lightmap seams */
2715 if ( lightmapNum == 0 ) {
2716 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2722 if ( filterColor ) {
2723 VectorDivide( averageColor, samples, luxel );
2727 VectorDivide( averageDir, samples, deluxel );
2730 /* set cluster to -3 */
2731 if ( *cluster < 0 ) {
2732 *cluster = CLUSTER_FLOODED;
2742 IlluminateVertexes()
2743 light the surface vertexes
2746 #define VERTEX_NUDGE 4.0f
2748 void IlluminateVertexes( int num ){
2749 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2750 int lightmapNum, numAvg;
2751 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2752 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2753 bspDrawSurface_t *ds;
2754 surfaceInfo_t *info;
2756 bspDrawVert_t *verts;
2758 float floodLightAmount;
2762 /* get surface, info, and raw lightmap */
2763 ds = &bspDrawSurfaces[ num ];
2764 info = &surfaceInfos[ num ];
2767 /* -----------------------------------------------------------------
2768 illuminate the vertexes
2769 ----------------------------------------------------------------- */
2771 /* calculate vertex lighting for surfaces without lightmaps */
2772 if ( lm == NULL || cpmaHack ) {
2774 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2775 trace.forceSunlight = info->si->forceSunlight;
2776 trace.recvShadows = info->recvShadows;
2777 trace.numSurfaces = 1;
2778 trace.surfaces = #
2779 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2781 /* twosided lighting */
2782 trace.twoSided = info->si->twoSided;
2784 /* make light list for this surface */
2785 CreateTraceLightsForSurface( num, &trace );
2788 verts = yDrawVerts + ds->firstVert;
2790 memset( avgColors, 0, sizeof( avgColors ) );
2792 /* walk the surface verts */
2793 for ( i = 0; i < ds->numVerts; i++ )
2795 /* get vertex luxel */
2796 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2798 /* color the luxel with raw lightmap num? */
2799 if ( debugSurfaces ) {
2800 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2803 /* color the luxel with luxel origin? */
2804 else if ( debugOrigin ) {
2805 VectorSubtract( info->maxs, info->mins, temp );
2806 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2807 VectorSubtract( origin, lm->mins, temp2 );
2808 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2809 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2810 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2813 /* color the luxel with the normal */
2814 else if ( normalmap ) {
2815 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2816 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2817 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2820 else if ( info->si->noVertexLight ) {
2821 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
2824 else if ( noVertexLighting > 0 ) {
2825 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
2828 /* illuminate the vertex */
2831 /* clear vertex luxel */
2832 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2834 /* try at initial origin */
2835 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2836 if ( trace.cluster >= 0 ) {
2838 VectorCopy( verts[ i ].xyz, trace.origin );
2839 VectorCopy( verts[ i ].normal, trace.normal );
2842 if ( dirty && !bouncing ) {
2843 dirt = DirtForSample( &trace );
2849 /* jal: floodlight */
2850 floodLightAmount = 0.0f;
2851 VectorClear( floodColor );
2852 if ( floodlighty && !bouncing ) {
2853 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2854 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2858 LightingAtSample( &trace, ds->vertexStyles, colors );
2861 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2864 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2866 /* jal: floodlight */
2867 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2870 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2871 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2872 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2876 /* is this sample bright enough? */
2877 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2878 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2879 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2880 radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2881 /* nudge the sample point around a bit */
2882 for ( x = 0; x < 5; x++ )
2884 /* two's complement 0, 1, -1, 2, -2, etc */
2885 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2887 for ( y = 0; y < 5; y++ )
2889 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2891 for ( z = 0; z < 5; z++ )
2893 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2896 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2897 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2898 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2900 /* try at nudged origin */
2901 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2902 if ( trace.cluster < 0 ) {
2907 if ( dirty && !bouncing ) {
2908 dirt = DirtForSample( &trace );
2914 /* jal: floodlight */
2915 floodLightAmount = 0.0f;
2916 VectorClear( floodColor );
2917 if ( floodlighty && !bouncing ) {
2918 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2919 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2923 LightingAtSample( &trace, ds->vertexStyles, colors );
2926 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2929 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2931 /* jal: floodlight */
2932 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2935 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2936 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2939 /* bright enough? */
2940 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2941 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2942 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2943 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2951 /* add to average? */
2952 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2953 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2954 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2955 radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2957 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2959 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2960 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2965 /* another happy customer */
2966 numVertsIlluminated++;
2969 /* set average color */
2971 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2972 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2976 VectorCopy( ambientColor, avgColors[ 0 ] );
2979 /* clean up and store vertex color */
2980 for ( i = 0; i < ds->numVerts; i++ )
2982 /* get vertex luxel */
2983 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2985 /* store average in occluded vertexes */
2986 if ( radVertLuxel[ 0 ] < 0.0f ) {
2987 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2989 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2990 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2993 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2998 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3001 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3002 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3005 if ( bouncing || bounce == 0 || !bounceOnly ) {
3006 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3008 if ( !info->si->noVertexLight ) {
3009 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
3014 /* free light list */
3015 FreeTraceLights( &trace );
3017 /* return to sender */
3021 /* -----------------------------------------------------------------
3022 reconstitute vertex lighting from the luxels
3023 ----------------------------------------------------------------- */
3025 /* set styles from lightmap */
3026 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3027 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3029 /* get max search radius */
3031 maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3033 /* walk the surface verts */
3034 verts = yDrawVerts + ds->firstVert;
3035 for ( i = 0; i < ds->numVerts; i++ )
3037 /* do each lightmap */
3038 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3041 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3045 /* get luxel coords */
3046 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3047 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3051 else if ( x >= lm->sw ) {
3057 else if ( y >= lm->sh ) {
3061 /* get vertex luxels */
3062 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3063 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3065 /* color the luxel with the normal? */
3067 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3068 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3069 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3072 /* color the luxel with surface num? */
3073 else if ( debugSurfaces ) {
3074 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3077 else if ( info->si->noVertexLight ) {
3078 VectorSet( radVertLuxel, 127.5f, 127.5f, 127.5f );
3081 else if ( noVertexLighting > 0 ) {
3082 VectorSet( radVertLuxel, 127.5f * noVertexLighting, 127.5f * noVertexLighting, 127.5f * noVertexLighting );
3085 /* divine color from the superluxels */
3088 /* increasing radius */
3089 VectorClear( radVertLuxel );
3091 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3093 /* sample within radius */
3094 for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3096 if ( sy < 0 || sy >= lm->sh ) {
3100 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3102 if ( sx < 0 || sx >= lm->sw ) {
3106 /* get luxel particulars */
3107 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3108 cluster = SUPER_CLUSTER( sx, sy );
3109 if ( *cluster < 0 ) {
3113 /* testing: must be brigher than ambient color */
3114 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3117 /* add its distinctiveness to our own */
3118 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3119 samples += luxel[ 3 ];
3125 if ( samples > 0.0f ) {
3126 VectorDivide( radVertLuxel, samples, radVertLuxel );
3129 VectorCopy( ambientColor, radVertLuxel );
3133 /* store into floating point storage */
3134 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3135 numVertsIlluminated++;
3137 /* store into bytes (for vertex approximation) */
3138 if ( !info->si->noVertexLight ) {
3139 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3147 /* -------------------------------------------------------------------------------
3149 light optimization (-fast)
3151 creates a list of lights that will affect a surface and stores it in tw
3152 this is to optimize surface lighting by culling out as many of the
3153 lights in the world as possible from further calculation
3155 ------------------------------------------------------------------------------- */
3159 determines opaque brushes in the world and find sky shaders for sunlight calculations
3162 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3164 unsigned int compileFlags, allCompileFlags;
3167 bspBrushSide_t *side;
3168 bspShader_t *shader;
3173 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3176 if ( opaqueBrushes == NULL ) {
3177 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3181 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3182 numOpaqueBrushes = 0;
3184 /* walk the list of worldspawn brushes */
3185 for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3188 b = bspModels[ 0 ].firstBSPBrush + i;
3189 brush = &bspBrushes[ b ];
3191 /* check all sides */
3194 allCompileFlags = ~( 0u );
3195 for ( j = 0; j < brush->numSides && inside; j++ )
3197 /* do bsp shader calculations */
3198 side = &bspBrushSides[ brush->firstSide + j ];
3199 shader = &bspShaders[ side->shaderNum ];
3201 /* get shader info */
3202 si = ShaderInfoForShaderNull( shader->shader );
3207 /* or together compile flags */
3208 compileFlags |= si->compileFlags;
3209 allCompileFlags &= si->compileFlags;
3212 /* determine if this brush is opaque to light */
3213 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3214 opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3220 /* emit some statistics */
3221 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3223 void SetupBrushes( void ){
3224 SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3231 determines if two clusters are visible to each other using the PVS
3234 qboolean ClusterVisible( int a, int b ){
3240 if ( a < 0 || b < 0 ) {
3250 if ( numBSPVisBytes <= 8 ) {
3255 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3256 leafBytes = ( (int*) bspVisBytes )[ 1 ];
3257 pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3260 if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3270 borrowed from vlight.c
3273 int PointInLeafNum_r( vec3_t point, int nodenum ){
3280 while ( nodenum >= 0 )
3282 node = &bspNodes[ nodenum ];
3283 plane = &bspPlanes[ node->planeNum ];
3284 dist = DotProduct( point, plane->normal ) - plane->dist;
3286 nodenum = node->children[ 0 ];
3288 else if ( dist < -0.1 ) {
3289 nodenum = node->children[ 1 ];
3293 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3294 if ( bspLeafs[ leafnum ].cluster != -1 ) {
3297 nodenum = node->children[ 1 ];
3301 leafnum = -nodenum - 1;
3309 borrowed from vlight.c
3312 int PointInLeafNum( vec3_t point ){
3313 return PointInLeafNum_r( point, 0 );
3319 ClusterVisibleToPoint() - ydnar
3320 returns qtrue if point can "see" cluster
3323 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3327 /* get leafNum for point */
3328 pointCluster = ClusterForPoint( point );
3329 if ( pointCluster < 0 ) {
3334 return ClusterVisible( pointCluster, cluster );
3340 ClusterForPoint() - ydnar
3341 returns the pvs cluster for point
3344 int ClusterForPoint( vec3_t point ){
3348 /* get leafNum for point */
3349 leafNum = PointInLeafNum( point );
3350 if ( leafNum < 0 ) {
3354 /* return the cluster */
3355 return bspLeafs[ leafNum ].cluster;
3361 ClusterForPointExt() - ydnar
3362 also takes brushes into account for occlusion testing
3365 int ClusterForPointExt( vec3_t point, float epsilon ){
3366 int i, j, b, leafNum, cluster;
3369 int *brushes, numBSPBrushes;
3375 /* get leaf for point */
3376 leafNum = PointInLeafNum( point );
3377 if ( leafNum < 0 ) {
3380 leaf = &bspLeafs[ leafNum ];
3382 /* get the cluster */
3383 cluster = leaf->cluster;
3384 if ( cluster < 0 ) {
3388 /* transparent leaf, so check point against all brushes in the leaf */
3389 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3390 numBSPBrushes = leaf->numBSPLeafBrushes;
3391 for ( i = 0; i < numBSPBrushes; i++ )
3395 if ( b > maxOpaqueBrush ) {
3398 brush = &bspBrushes[ b ];
3399 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3403 /* check point against all planes */
3405 for ( j = 0; j < brush->numSides && inside; j++ )
3407 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3408 dot = DotProduct( point, plane->normal );
3410 if ( dot > epsilon ) {
3415 /* if inside, return bogus cluster */
3421 /* if the point made it this far, it's not inside any opaque brushes */
3428 ClusterForPointExtFilter() - ydnar
3429 adds cluster checking against a list of known valid clusters
3432 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3436 /* get cluster for point */
3437 cluster = ClusterForPointExt( point, epsilon );
3439 /* check if filtering is necessary */
3440 if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3445 for ( i = 0; i < numClusters; i++ )
3447 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3459 ShaderForPointInLeaf() - ydnar
3460 checks a point against all brushes in a leaf, returning the shader of the brush
3461 also sets the cumulative surface and content flags for the brush hit
3464 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3468 int *brushes, numBSPBrushes;
3471 bspBrushSide_t *side;
3473 bspShader_t *shader;
3474 int allSurfaceFlags, allContentFlags;
3477 /* clear things out first */
3482 if ( leafNum < 0 ) {
3485 leaf = &bspLeafs[ leafNum ];
3487 /* transparent leaf, so check point against all brushes in the leaf */
3488 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3489 numBSPBrushes = leaf->numBSPLeafBrushes;
3490 for ( i = 0; i < numBSPBrushes; i++ )
3493 brush = &bspBrushes[ brushes[ i ] ];
3495 /* check point against all planes */
3497 allSurfaceFlags = 0;
3498 allContentFlags = 0;
3499 for ( j = 0; j < brush->numSides && inside; j++ )
3501 side = &bspBrushSides[ brush->firstSide + j ];
3502 plane = &bspPlanes[ side->planeNum ];
3503 dot = DotProduct( point, plane->normal );
3505 if ( dot > epsilon ) {
3510 shader = &bspShaders[ side->shaderNum ];
3511 allSurfaceFlags |= shader->surfaceFlags;
3512 allContentFlags |= shader->contentFlags;
3516 /* handle if inside */
3518 /* if there are desired flags, check for same and continue if they aren't matched */
3519 if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3522 if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3526 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3527 *surfaceFlags = allSurfaceFlags;
3528 *contentFlags = allContentFlags;
3529 return brush->shaderNum;
3533 /* if the point made it this far, it's not inside any brushes */
3541 chops a bounding box by the plane defined by origin and normal
3542 returns qfalse if the bounds is entirely clipped away
3544 this is not exactly the fastest way to do this...
3547 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3548 /* FIXME: rewrite this so it doesn't use bloody brushes */
3556 calculates each light's effective envelope,
3557 taking into account brightness, type, and pvs.
3560 #define LIGHT_EPSILON 0.125f
3561 #define LIGHT_NUDGE 2.0f
3563 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3564 int i, x, y, z, x1, y1, z1;
3565 light_t *light, *light2, **owner;
3567 vec3_t origin, dir, mins, maxs;
3568 float radius, intensity;
3569 light_t *buckets[ 256 ];
3572 /* early out for weird cases where there are no lights */
3573 if ( lights == NULL ) {
3578 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3582 numCulledLights = 0;
3584 while ( *owner != NULL )
3589 /* handle negative lights */
3590 if ( light->photons < 0.0f || light->add < 0.0f ) {
3591 light->photons *= -1.0f;
3592 light->add *= -1.0f;
3593 light->flags |= LIGHT_NEGATIVE;
3597 if ( light->type == EMIT_SUN ) {
3600 light->envelope = MAX_WORLD_COORD * 8.0f;
3601 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3602 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3605 /* everything else */
3608 /* get pvs cluster for light */
3609 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3611 /* invalid cluster? */
3612 if ( light->cluster < 0 ) {
3613 /* nudge the sample point around a bit */
3614 for ( x = 0; x < 4; x++ )
3616 /* two's complement 0, 1, -1, 2, -2, etc */
3617 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3619 for ( y = 0; y < 4; y++ )
3621 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3623 for ( z = 0; z < 4; z++ )
3625 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3628 origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3629 origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3630 origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3632 /* try at nudged origin */
3633 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3634 if ( light->cluster < 0 ) {
3639 VectorCopy( origin, light->origin );
3645 /* only calculate for lights in pvs and outside of opaque brushes */
3646 if ( light->cluster >= 0 ) {
3647 /* set light fast flag */
3649 light->flags |= LIGHT_FAST_TEMP;
3652 light->flags &= ~LIGHT_FAST_TEMP;
3654 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3655 light->flags |= LIGHT_FAST_TEMP;
3657 if ( light->si && light->si->noFast ) {
3658 light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3661 /* clear light envelope */
3662 light->envelope = 0;
3664 /* handle area lights */
3665 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3666 light->envelope = MAX_WORLD_COORD * 8.0f;
3668 /* check for fast mode */
3669 if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3670 /* ugly hack to calculate extent for area lights, but only done once */
3671 VectorScale( light->normal, -1.0f, dir );
3672 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3676 VectorMA( light->origin, radius, light->normal, origin );
3677 factor = PointToPolygonFormFactor( origin, dir, light->w );
3678 if ( factor < 0.0f ) {
3681 if ( ( factor * light->add ) <= light->falloffTolerance ) {
3682 light->envelope = radius;
3688 intensity = light->photons; /* hopefully not used */
3693 intensity = light->photons;
3697 if ( light->envelope <= 0.0f ) {
3698 /* solve distance for non-distance lights */
3699 if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3700 light->envelope = MAX_WORLD_COORD * 8.0f;
3703 else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3704 /* solve distance for linear lights */
3705 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3706 light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3710 add = angle * light->photons * linearScale - (dist * light->fade);
3711 T = (light->photons * linearScale) - (dist * light->fade);
3712 T + (dist * light->fade) = (light->photons * linearScale);
3713 dist * light->fade = (light->photons * linearScale) - T;
3714 dist = ((light->photons * linearScale) - T) / light->fade;
3717 /* solve for inverse square falloff */
3719 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3723 add = light->photons / (dist * dist);
3724 T = light->photons / (dist * dist);
3725 T * (dist * dist) = light->photons;
3726 dist = sqrt( light->photons / T );
3731 /* solve distance for linear lights */
3732 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3733 light->envelope = ( intensity * linearScale ) / light->fade;
3736 /* can't cull these */
3738 light->envelope = MAX_WORLD_COORD * 8.0f;
3743 /* chop radius against pvs */
3746 ClearBounds( mins, maxs );
3748 /* check all leaves */
3749 for ( i = 0; i < numBSPLeafs; i++ )
3752 leaf = &bspLeafs[ i ];
3755 if ( leaf->cluster < 0 ) {
3758 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3762 /* add this leafs bbox to the bounds */
3763 VectorCopy( leaf->mins, origin );
3764 AddPointToBounds( origin, mins, maxs );
3765 VectorCopy( leaf->maxs, origin );
3766 AddPointToBounds( origin, mins, maxs );
3769 /* test to see if bounds encompass light */
3770 for ( i = 0; i < 3; i++ )
3772 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3773 //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3774 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3775 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3776 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3777 AddPointToBounds( light->origin, mins, maxs );
3781 /* chop the bounds by a plane for area lights and spotlights */
3782 if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3783 ChopBounds( mins, maxs, light->origin, light->normal );
3787 VectorCopy( mins, light->mins );
3788 VectorCopy( maxs, light->maxs );
3790 /* reflect bounds around light origin */
3791 //% VectorMA( light->origin, -1.0f, origin, origin );
3792 VectorScale( light->origin, 2, origin );
3793 VectorSubtract( origin, maxs, origin );
3794 AddPointToBounds( origin, mins, maxs );
3795 //% VectorMA( light->origin, -1.0f, mins, origin );
3796 VectorScale( light->origin, 2, origin );
3797 VectorSubtract( origin, mins, origin );
3798 AddPointToBounds( origin, mins, maxs );
3800 /* calculate spherical bounds */
3801 VectorSubtract( maxs, light->origin, dir );
3802 radius = (float) VectorLength( dir );
3804 /* if this radius is smaller than the envelope, then set the envelope to it */
3805 if ( radius < light->envelope ) {
3806 light->envelope = radius;
3807 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3810 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3813 /* add grid/surface only check */
3815 if ( !( light->flags & LIGHT_GRID ) ) {
3816 light->envelope = 0.0f;
3821 if ( !( light->flags & LIGHT_SURFACES ) ) {
3822 light->envelope = 0.0f;
3828 if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3830 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3832 /* delete the light */
3834 *owner = light->next;
3835 if ( light->w != NULL ) {
3843 /* square envelope */
3844 light->envelope2 = ( light->envelope * light->envelope );
3846 /* increment light count */
3849 /* set next light */
3850 owner = &( ( **owner ).next );
3853 /* bucket sort lights by style */
3854 memset( buckets, 0, sizeof( buckets ) );
3856 for ( light = lights; light != NULL; light = light2 )
3858 /* get next light */
3859 light2 = light->next;
3861 /* filter into correct bucket */
3862 light->next = buckets[ light->style ];
3863 buckets[ light->style ] = light;
3865 /* if any styled light is present, automatically set nocollapse */
3866 if ( light->style != LS_NORMAL ) {
3871 /* filter back into light list */
3873 for ( i = 255; i >= 0; i-- )
3876 for ( light = buckets[ i ]; light != NULL; light = light2 )
3878 light2 = light->next;
3879 light->next = lights;
3884 /* emit some statistics */
3885 Sys_Printf( "%9d total lights\n", numLights );
3886 Sys_Printf( "%9d culled lights\n", numCulledLights );
3892 CreateTraceLightsForBounds()
3893 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3896 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3899 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3900 float radius, dist, length;
3903 /* potential pre-setup */
3904 if ( numLights == 0 ) {
3905 SetupEnvelopes( qfalse, fast );
3909 //% 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 ] );
3911 /* allocate the light list */
3912 trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3913 trace->numLights = 0;
3915 /* calculate spherical bounds */
3916 VectorAdd( mins, maxs, origin );
3917 VectorScale( origin, 0.5f, origin );
3918 VectorSubtract( maxs, origin, dir );
3919 radius = (float) VectorLength( dir );
3921 /* get length of normal vector */
3922 if ( normal != NULL ) {
3923 length = VectorLength( normal );
3927 normal = nullVector;
3931 /* test each light and see if it reaches the sphere */
3932 /* note: the attenuation code MUST match LightingAtSample() */
3933 for ( light = lights; light; light = light->next )
3935 /* check zero sized envelope */
3936 if ( light->envelope <= 0 ) {
3937 lightsEnvelopeCulled++;
3942 if ( !( light->flags & flags ) ) {
3946 /* sunlight skips all this nonsense */
3947 if ( light->type != EMIT_SUN ) {
3953 /* check against pvs cluster */
3954 if ( numClusters > 0 && clusters != NULL ) {
3955 for ( i = 0; i < numClusters; i++ )
3957 if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3963 if ( i == numClusters ) {
3964 lightsClusterCulled++;
3969 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3970 VectorSubtract( light->origin, origin, dir );
3971 dist = VectorLength( dir );
3972 dist -= light->envelope;
3975 lightsEnvelopeCulled++;
3979 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3982 for ( i = 0; i < 3; i++ )
3984 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3989 lightsBoundsCulled++;
3995 /* planar surfaces (except twosided surfaces) have a couple more checks */
3996 if ( length > 0.0f && trace->twoSided == qfalse ) {
3997 /* lights coplanar with a surface won't light it */
3998 if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3999 lightsPlaneCulled++;
4003 /* check to see if light is behind the plane */
4004 if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
4005 lightsPlaneCulled++;
4010 /* add this light */
4011 trace->lights[ trace->numLights++ ] = light;
4014 /* make last night null */
4015 trace->lights[ trace->numLights ] = NULL;
4020 void FreeTraceLights( trace_t *trace ){
4021 if ( trace->lights != NULL ) {
4022 free( trace->lights );
4029 CreateTraceLightsForSurface()
4030 creates a list of lights that can potentially affect a drawsurface
4033 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4035 vec3_t mins, maxs, normal;
4037 bspDrawSurface_t *ds;
4038 surfaceInfo_t *info;
4046 /* get drawsurface and info */
4047 ds = &bspDrawSurfaces[ num ];
4048 info = &surfaceInfos[ num ];
4050 /* get the mins/maxs for the dsurf */
4051 ClearBounds( mins, maxs );
4052 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4053 for ( i = 0; i < ds->numVerts; i++ )
4055 dv = &yDrawVerts[ ds->firstVert + i ];
4056 AddPointToBounds( dv->xyz, mins, maxs );
4057 if ( !VectorCompare( dv->normal, normal ) ) {
4058 VectorClear( normal );
4062 /* create the lights for the bounding box */
4063 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4066 /////////////////////////////////////////////////////////////
4068 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4069 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4070 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4071 #define FLOODLIGHT_NUM_VECTORS ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4073 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4074 static int numFloodVectors = 0;
4076 void SetupFloodLight( void ){
4078 float angle, elevation, angleStep, elevationStep;
4080 double v1,v2,v3,v4,v5,v6;
4083 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4085 /* calculate angular steps */
4086 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4087 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4091 for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4093 /* iterate elevation */
4094 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4096 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4097 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4098 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4103 /* emit some statistics */
4104 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4107 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4109 if ( value[ 0 ] != '\0' ) {
4111 v4 = floodlightDistance;
4112 v5 = floodlightIntensity;
4113 v6 = floodlightDirectionScale;
4115 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4117 floodlightRGB[0] = v1;
4118 floodlightRGB[1] = v2;
4119 floodlightRGB[2] = v3;
4121 if ( VectorLength( floodlightRGB ) == 0 ) {
4122 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4135 floodlightDistance = v4;
4136 floodlightIntensity = v5;
4137 floodlightDirectionScale = v6;
4139 floodlighty = qtrue;
4140 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4144 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4147 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4148 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4149 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4151 ColorNormalize( floodlightRGB,floodlightRGB );
4155 FloodLightForSample()
4156 calculates floodlight value for a given sample
4157 once again, kudos to the dirtmapping coder
4160 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4165 float gatherLight, outLight;
4166 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4174 if ( trace == NULL || trace->cluster < 0 ) {
4180 dd = floodLightDistance;
4181 VectorCopy( trace->normal, normal );
4183 /* check if the normal is aligned to the world-up */
4184 if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4185 if ( normal[ 2 ] == 1.0f ) {
4186 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4187 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4189 else if ( normal[ 2 ] == -1.0f ) {
4190 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4191 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4196 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4197 CrossProduct( normal, worldUp, myRt );
4198 VectorNormalize( myRt, myRt );
4199 CrossProduct( myRt, normal, myUp );
4200 VectorNormalize( myUp, myUp );
4203 /* vortex: optimise floodLightLowQuality a bit */
4204 if ( floodLightLowQuality == qtrue ) {
4205 /* iterate through ordered vectors */
4206 for ( i = 0; i < numFloodVectors; i++ )
4207 if ( rand() % 10 != 0 ) {
4213 /* iterate through ordered vectors */
4214 for ( i = 0; i < numFloodVectors; i++ )
4218 /* transform vector into tangent space */
4219 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4220 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4221 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4224 VectorMA( trace->origin, dd, direction, trace->end );
4226 //VectorMA( trace->origin, 1, direction, trace->origin );
4228 SetupTrace( trace );
4229 VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4234 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4235 contribution = 1.0f;
4237 else if ( trace->opaque ) {
4238 VectorSubtract( trace->hit, trace->origin, displacement );
4239 d = VectorLength( displacement );
4241 // d=trace->distance;
4242 //if (d>256) gatherDirt+=1;
4243 contribution = d / dd;
4244 if ( contribution > 1 ) {
4245 contribution = 1.0f;
4248 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4251 gatherLight += contribution;
4256 if ( gatherLight <= 0.0f ) {
4265 gatherLight /= ( sub );
4267 outLight = gatherLight;
4268 if ( outLight > 1.0f ) {
4272 /* return to sender */
4277 FloodLightRawLightmap
4278 lighttracer style ambient occlusion light hack.
4279 Kudos to the dirtmapping author for most of this source.
4280 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4281 VorteX: fixed problems with deluxemapping
4284 // floodlight pass on a lightmap
4285 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4286 int i, x, y, *cluster;
4287 float *origin, *normal, *floodlight, floodLightAmount;
4288 surfaceInfo_t *info;
4291 // float samples, average, *floodlight2;
4293 memset( &trace,0,sizeof( trace_t ) );
4296 trace.testOcclusion = qtrue;
4297 trace.forceSunlight = qfalse;
4298 trace.twoSided = qtrue;
4299 trace.recvShadows = lm->recvShadows;
4300 trace.numSurfaces = lm->numLightSurfaces;
4301 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4302 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4303 trace.testAll = qfalse;
4304 trace.distance = 1024;
4306 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4307 //trace.twoSided = qfalse;
4308 for ( i = 0; i < trace.numSurfaces; i++ )
4311 info = &surfaceInfos[ trace.surfaces[ i ] ];
4313 /* check twosidedness */
4314 if ( info->si->twoSided ) {
4315 trace.twoSided = qtrue;
4320 /* gather floodlight */
4321 for ( y = 0; y < lm->sh; y++ )
4323 for ( x = 0; x < lm->sw; x++ )
4326 cluster = SUPER_CLUSTER( x, y );
4327 origin = SUPER_ORIGIN( x, y );
4328 normal = SUPER_NORMAL( x, y );
4329 floodlight = SUPER_FLOODLIGHT( x, y );
4331 /* set default dirt */
4334 /* only look at mapped luxels */
4335 if ( *cluster < 0 ) {
4340 trace.cluster = *cluster;
4341 VectorCopy( origin, trace.origin );
4342 VectorCopy( normal, trace.normal );
4344 /* get floodlight */
4345 floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4347 /* add floodlight */
4348 floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4349 floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4350 floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4351 floodlight[3] += floodlightDirectionScale;
4355 /* testing no filtering */
4361 for ( y = 0; y < lm->sh; y++ )
4363 for ( x = 0; x < lm->sw; x++ )
4366 cluster = SUPER_CLUSTER( x, y );
4367 floodlight = SUPER_FLOODLIGHT( x, y );
4369 /* filter dirt by adjacency to unmapped luxels */
4370 average = *floodlight;
4372 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4374 if ( sy < 0 || sy >= lm->sh ) {
4378 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4380 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4384 /* get neighboring luxel */
4385 cluster = SUPER_CLUSTER( sx, sy );
4386 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4387 if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4392 average += *floodlight2;
4397 if ( samples <= 0.0f ) {
4403 if ( samples <= 0.0f ) {
4408 *floodlight = average / samples;
4414 void FloodLightRawLightmap( int rawLightmapNum ){
4417 /* bail if this number exceeds the number of raw lightmaps */
4418 if ( rawLightmapNum >= numRawLightmaps ) {
4422 lm = &rawLightmaps[ rawLightmapNum ];
4425 if ( floodlighty && floodlightIntensity ) {
4426 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4430 if ( lm->floodlightIntensity ) {
4431 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4432 numSurfacesFloodlighten += 1;
4436 void FloodlightRawLightmaps(){
4437 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4438 numSurfacesFloodlighten = 0;
4439 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4440 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4444 FloodLightIlluminate()
4445 illuminate floodlight into lightmap luxels
4448 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4449 float *luxel, *floodlight, *deluxel, *normal;
4452 int x, y, lightmapNum;
4454 /* walk lightmaps */
4455 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4458 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4462 /* apply floodlight to each luxel */
4463 for ( y = 0; y < lm->sh; y++ )
4465 for ( x = 0; x < lm->sw; x++ )
4467 /* get floodlight */
4468 floodlight = SUPER_FLOODLIGHT( x, y );
4469 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4474 cluster = SUPER_CLUSTER( x, y );
4476 /* only process mapped luxels */
4477 if ( *cluster < 0 ) {
4481 /* get particulars */
4482 luxel = SUPER_LUXEL( lightmapNum, x, y );
4483 deluxel = SUPER_DELUXEL( x, y );
4485 /* add to lightmap */
4486 luxel[0] += floodlight[0];
4487 luxel[1] += floodlight[1];
4488 luxel[2] += floodlight[2];
4490 if ( luxel[3] == 0 ) {
4494 /* add to deluxemap */
4495 if ( deluxemap && floodlight[3] > 0 ) {
4498 normal = SUPER_NORMAL( x, y );
4499 brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4501 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4502 if ( brightness < 0.00390625f ) {
4503 brightness = 0.00390625f;
4506 VectorScale( normal, brightness, lightvector );
4507 VectorAdd( deluxel, lightvector, deluxel );