1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 ydnar: moved to here 2001-02-04
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
55 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
59 /* make a local copy */
60 VectorScale( color, scale, sample );
63 gamma = 1.0f / lightmapGamma;
64 for( i = 0; i < 3; i++ )
66 /* handle negative light */
67 if( sample[ i ] < 0.0f )
74 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
77 if (lightmapExposure == 0)
79 /* clamp with color normalization */
81 if( sample[ 1 ] > max )
83 if( sample[ 2 ] > max )
86 VectorScale( sample, (255.0f / max), sample );
90 inv=1.f/lightmapExposure;
94 if( sample[ 1 ] > max )
96 if( sample[ 2 ] > max )
99 dif = (1- exp(-max * inv) ) * 255;
117 /* compensate for ingame overbrighting/bitshifting */
118 VectorScale( sample, (1.0f / lightmapCompensate), sample );
123 sample[0] = floor(Image_sRGBFloatFromLinearFloat(sample[0] * (1.0 / 255.0)) * 255.0 + 0.5);
124 sample[1] = floor(Image_sRGBFloatFromLinearFloat(sample[1] * (1.0 / 255.0)) * 255.0 + 0.5);
125 sample[2] = floor(Image_sRGBFloatFromLinearFloat(sample[2] * (1.0 / 255.0)) * 255.0 + 0.5);
129 colorBytes[ 0 ] = sample[ 0 ];
130 colorBytes[ 1 ] = sample[ 1 ];
131 colorBytes[ 2 ] = sample[ 2 ];
136 /* -------------------------------------------------------------------------------
138 this section deals with phong shading (normal interpolation across brush faces)
140 ------------------------------------------------------------------------------- */
144 smooths together coincident vertex normals across the bsp
147 #define MAX_SAMPLES 256
148 #define THETA_EPSILON 0.000001
149 #define EQUAL_NORMAL_EPSILON 0.01
151 void SmoothNormals( void )
153 int i, j, k, f, cs, numVerts, numVotes, fOld, start;
154 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
155 bspDrawSurface_t *ds;
159 vec3_t average, diff;
160 int indexes[ MAX_SAMPLES ];
161 vec3_t votes[ MAX_SAMPLES ];
164 /* allocate shade angle table */
165 shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
166 memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
168 /* allocate smoothed table */
169 cs = (numBSPDrawVerts / 8) + 1;
170 smoothed = safe_malloc( cs );
171 memset( smoothed, 0, cs );
173 /* set default shade angle */
174 defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
177 /* run through every surface and flag verts belonging to non-lightmapped surfaces
178 and set per-vertex smoothing angle */
179 for( i = 0; i < numBSPDrawSurfaces; i++ )
182 ds = &bspDrawSurfaces[ i ];
184 /* get shader for shade angle */
185 si = surfaceInfos[ i ].si;
186 if( si->shadeAngleDegrees )
187 shadeAngle = DEG2RAD( si->shadeAngleDegrees );
189 shadeAngle = defaultShadeAngle;
190 if( shadeAngle > maxShadeAngle )
191 maxShadeAngle = shadeAngle;
194 for( j = 0; j < ds->numVerts; j++ )
196 f = ds->firstVert + j;
197 shadeAngles[ f ] = shadeAngle;
198 if( ds->surfaceType == MST_TRIANGLE_SOUP )
199 smoothed[ f >> 3 ] |= (1 << (f & 7));
202 /* ydnar: optional force-to-trisoup */
203 if( trisoup && ds->surfaceType == MST_PLANAR )
205 ds->surfaceType = MST_TRIANGLE_SOUP;
206 ds->lightmapNum[ 0 ] = -3;
210 /* bail if no surfaces have a shade angle */
211 if( maxShadeAngle == 0 )
220 start = I_FloatTime();
222 /* go through the list of vertexes */
223 for( i = 0; i < numBSPDrawVerts; i++ )
226 f = 10 * i / numBSPDrawVerts;
230 Sys_Printf( "%i...", f );
233 /* already smoothed? */
234 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
238 VectorClear( average );
242 /* build a table of coincident vertexes */
243 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
245 /* already smoothed? */
246 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
250 if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
253 /* use smallest shade angle */
254 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
256 /* check shade angle */
257 dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
260 else if( dot < -1.0 )
262 testAngle = acos( dot ) + THETA_EPSILON;
263 if( testAngle >= shadeAngle )
265 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
268 //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
270 /* add to the list */
271 indexes[ numVerts++ ] = j;
274 smoothed[ j >> 3 ] |= (1 << (j & 7));
276 /* see if this normal has already been voted */
277 for( k = 0; k < numVotes; k++ )
279 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
280 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
281 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
282 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
286 /* add a new vote? */
287 if( k == numVotes && numVotes < MAX_SAMPLES )
289 VectorAdd( average, bspDrawVerts[ j ].normal, average );
290 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
295 /* don't average for less than 2 verts */
300 if( VectorNormalize( average, average ) > 0 )
303 for( j = 0; j < numVerts; j++ )
304 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
308 /* free the tables */
313 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
318 /* -------------------------------------------------------------------------------
320 this section deals with phong shaded lightmap tracing
322 ------------------------------------------------------------------------------- */
324 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
328 calculates the st tangent vectors for normalmapping
331 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
338 /* calculate barycentric basis for the triangle */
339 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 ]);
340 if( fabs( bb ) < 0.00000001f )
344 for( i = 0; i < numVerts; i++ )
346 /* calculate s tangent vector */
347 s = dv[ i ]->st[ 0 ] + 10.0f;
348 t = dv[ i ]->st[ 1 ];
349 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
350 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
351 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
353 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
354 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
355 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
357 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
358 VectorNormalize( stv[ i ], stv[ i ] );
360 /* calculate t tangent vector */
361 s = dv[ i ]->st[ 0 ];
362 t = dv[ i ]->st[ 1 ] + 10.0f;
363 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
364 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
365 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
367 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
368 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
369 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
371 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
372 VectorNormalize( ttv[ i ], ttv[ i ] );
375 //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
376 //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
379 /* return to caller */
388 perterbs the normal by the shader's normalmap in tangent space
391 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
398 VectorCopy( dv->normal, pNormal );
400 /* sample normalmap */
401 if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
404 /* remap sampled normal from [0,255] to [-1,-1] */
405 for( i = 0; i < 3; i++ )
406 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
408 /* scale tangent vectors and add to original normal */
409 VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
410 VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
411 VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
413 /* renormalize and return */
414 VectorNormalize( pNormal, pNormal );
421 maps a luxel for triangle bv at
425 #define BOGUS_NUDGE -99999.0f
427 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 ] )
429 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
430 float *luxel, *origin, *normal, d, lightmapSampleOffset;
437 vec4_t sideplane, hostplane;
442 static float nudges[][ 2 ] =
444 //%{ 0, 0 }, /* try center first */
445 { -NUDGE, 0 }, /* left */
446 { NUDGE, 0 }, /* right */
447 { 0, NUDGE }, /* up */
448 { 0, -NUDGE }, /* down */
449 { -NUDGE, NUDGE }, /* left/up */
450 { NUDGE, -NUDGE }, /* right/down */
451 { NUDGE, NUDGE }, /* right/up */
452 { -NUDGE, -NUDGE }, /* left/down */
453 { BOGUS_NUDGE, BOGUS_NUDGE }
457 /* find luxel xy coords (fixme: subtract 0.5?) */
458 x = dv->lightmap[ 0 ][ 0 ];
459 y = dv->lightmap[ 0 ][ 1 ];
462 else if( x >= lm->sw )
466 else if( y >= lm->sh )
469 /* set shader and cluster list */
473 numClusters = info->numSurfaceClusters;
474 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
483 /* get luxel, origin, cluster, and normal */
484 luxel = SUPER_LUXEL( 0, x, y );
485 origin = SUPER_ORIGIN( x, y );
486 normal = SUPER_NORMAL( x, y );
487 cluster = SUPER_CLUSTER( x, y );
489 /* don't attempt to remap occluded luxels for planar surfaces */
490 if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
493 /* only average the normal for premapped luxels */
494 else if( (*cluster) >= 0 )
496 /* do bumpmap calculations */
498 PerturbNormal( dv, si, pNormal, stv, ttv );
500 VectorCopy( dv->normal, pNormal );
502 /* add the additional normal data */
503 VectorAdd( normal, pNormal, normal );
508 /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
512 /* axial lightmap projection */
513 if( lm->vecs != NULL )
515 /* calculate an origin for the sample from the lightmap vectors */
516 VectorCopy( lm->origin, origin );
517 for( i = 0; i < 3; i++ )
519 /* add unless it's the axis, which is taken care of later */
520 if( i == lm->axisNum )
522 origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
525 /* project the origin onto the plane */
526 d = DotProduct( origin, plane ) - plane[ 3 ];
527 d /= plane[ lm->axisNum ];
528 origin[ lm->axisNum ] -= d;
531 /* non axial lightmap projection (explicit xyz) */
533 VectorCopy( dv->xyz, origin );
535 //////////////////////
536 //27's test to make sure samples stay within the triangle boundaries
537 //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
538 //2) if it does, nudge it onto the correct side.
540 if (worldverts!=NULL && lightmapTriangleCheck)
544 VectorCopy(worldverts[j],cverts[j]);
546 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
552 //build plane using 2 edges and a normal
555 VectorCopy(cverts[next],temp);
556 VectorAdd(temp,hostplane,temp);
557 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
559 //planetest sample point
560 e=DotProduct(origin,sideplane);
565 //VectorClear(origin);
566 //Move the sample point back inside triangle bounds
567 origin[0]-=sideplane[0]*(e+1);
568 origin[1]-=sideplane[1]*(e+1);
569 origin[2]-=sideplane[2]*(e+1);
578 ////////////////////////
580 /* planar surfaces have precalculated lightmap vectors for nudging */
581 if( lm->plane != NULL )
583 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
584 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
585 VectorCopy( lm->plane, vecs[ 2 ] );
588 /* non-planar surfaces must calculate them */
592 VectorCopy( plane, vecs[ 2 ] );
594 VectorCopy( dv->normal, vecs[ 2 ] );
595 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
598 /* push the origin off the surface a bit */
600 lightmapSampleOffset = si->lightmapSampleOffset;
602 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
603 if( lm->axisNum < 0 )
604 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
605 else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
606 origin[ lm->axisNum ] -= lightmapSampleOffset;
608 origin[ lm->axisNum ] += lightmapSampleOffset;
610 VectorCopy(origin,origintwo);
611 if(lightmapExtraVisClusterNudge)
613 origintwo[0]+=vecs[2][0];
614 origintwo[1]+=vecs[2][1];
615 origintwo[2]+=vecs[2][2];
619 pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
621 /* another retarded hack, storing nudge count in luxel[ 1 ] */
624 /* point in solid? (except in dark mode) */
625 if( pointCluster < 0 && dark == qfalse )
627 /* nudge the the location around */
629 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
631 /* nudge the vector around a bit */
632 for( i = 0; i < 3; i++ )
634 /* set nudged point*/
635 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
639 /* get pvs cluster */
640 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
641 if( pointCluster >= 0 )
642 VectorCopy( nudged, origin );
647 /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
648 if( pointCluster < 0 && si != NULL && dark == qfalse )
650 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
651 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
652 if( pointCluster >= 0 )
653 VectorCopy( nudged, origin );
658 if( pointCluster < 0 )
660 (*cluster) = CLUSTER_OCCLUDED;
661 VectorClear( origin );
662 VectorClear( normal );
668 //% Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
670 /* do bumpmap calculations */
672 PerturbNormal( dv, si, pNormal, stv, ttv );
674 VectorCopy( dv->normal, pNormal );
676 /* store the cluster and normal */
677 (*cluster) = pointCluster;
678 VectorCopy( pNormal, normal );
680 /* store explicit mapping pass and implicit mapping pass */
695 recursively subdivides a triangle until its edges are shorter
696 than the distance between two luxels (thanks jc :)
699 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 ] )
701 bspDrawVert_t mid, *dv2[ 3 ];
705 /* map the vertexes */
707 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
708 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
709 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
715 float *a, *b, dx, dy, dist, maxDist;
718 /* find the longest edge and split it */
721 for( i = 0; i < 3; i++ )
724 a = dv[ i ]->lightmap[ 0 ];
725 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
728 dx = a[ 0 ] - b[ 0 ];
729 dy = a[ 1 ] - b[ 1 ];
730 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
740 /* try to early out */
741 if( max < 0 || maxDist <= subdivideThreshold ) /* ydnar: was i < 0 instead of max < 0 (?) */
745 /* split the longest edge and map it */
746 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
747 MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
749 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
750 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
752 /* recurse to first triangle */
753 VectorCopy( dv, dv2 );
755 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
757 /* recurse to second triangle */
758 VectorCopy( dv, dv2 );
759 dv2[ (max + 1) % 3 ] = ∣
760 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
767 seed function for MapTriangle_r()
768 requires a cw ordered triangle
771 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
775 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
776 vec3_t worldverts[ 3 ];
779 /* get plane if possible */
780 if( lm->plane != NULL )
782 VectorCopy( lm->plane, plane );
783 plane[ 3 ] = lm->plane[ 3 ];
786 /* otherwise make one from the points */
787 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
790 /* check to see if we need to calculate texture->world tangent vectors */
791 if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
802 VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
803 VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
804 VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
806 /* map the vertexes */
807 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
808 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
809 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
811 /* 2002-11-20: prefer axial triangle edges */
814 /* subdivide the triangle */
815 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
819 for( i = 0; i < 3; i++ )
822 bspDrawVert_t *dv2[ 3 ];
826 a = dv[ i ]->lightmap[ 0 ];
827 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
829 /* make degenerate triangles for mapping edges */
830 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
833 dv2[ 1 ] = dv[ (i + 1) % 3 ];
834 dv2[ 2 ] = dv[ (i + 1) % 3 ];
836 /* map the degenerate triangle */
837 MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
848 recursively subdivides a quad until its edges are shorter
849 than the distance between two luxels
852 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 ] )
854 bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
861 float *a, *b, dx, dy, dist, maxDist;
864 /* find the longest edge and split it */
867 for( i = 0; i < 4; i++ )
870 a = dv[ i ]->lightmap[ 0 ];
871 b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
874 dx = a[ 0 ] - b[ 0 ];
875 dy = a[ 1 ] - b[ 1 ];
876 dist = (dx * dx) + (dy * dy); //% sqrt( (dx * dx) + (dy * dy) );
886 /* try to early out */
887 if( max < 0 || maxDist <= subdivideThreshold )
891 /* we only care about even/odd edges */
894 /* split the longest edges */
895 LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
896 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
898 /* map the vertexes */
899 MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
900 MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
905 /* recurse to first quad */
907 dv2[ 1 ] = &mid[ 0 ];
908 dv2[ 2 ] = &mid[ 1 ];
910 MapQuad_r( lm, info, dv2, plane, stv, ttv );
912 /* recurse to second quad */
913 dv2[ 0 ] = &mid[ 0 ];
916 dv2[ 3 ] = &mid[ 1 ];
917 MapQuad_r( lm, info, dv2, plane, stv, ttv );
923 /* recurse to first quad */
926 dv2[ 2 ] = &mid[ 0 ];
927 dv2[ 3 ] = &mid[ 1 ];
928 MapQuad_r( lm, info, dv2, plane, stv, ttv );
930 /* recurse to second quad */
931 dv2[ 0 ] = &mid[ 1 ];
932 dv2[ 1 ] = &mid[ 0 ];
935 MapQuad_r( lm, info, dv2, plane, stv, ttv );
943 seed function for MapQuad_r()
944 requires a cw ordered triangle quad
947 #define QUAD_PLANAR_EPSILON 0.5f
949 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
953 vec3_t *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
956 /* get plane if possible */
957 if( lm->plane != NULL )
959 VectorCopy( lm->plane, plane );
960 plane[ 3 ] = lm->plane[ 3 ];
963 /* otherwise make one from the points */
964 else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
967 /* 4th point must fall on the plane */
968 dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
969 if( fabs( dist ) > QUAD_PLANAR_EPSILON )
972 /* check to see if we need to calculate texture->world tangent vectors */
973 if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
984 /* map the vertexes */
985 MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
986 MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
987 MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
988 MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
990 /* subdivide the quad */
991 MapQuad_r( lm, info, dv, plane, stv, ttv );
999 maps the locations, normals, and pvs clusters for a raw lightmap
1002 #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)
1004 void MapRawLightmap( int rawLightmapNum )
1006 int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1007 float *luxel, *origin, *normal, samples, radius, pass;
1009 bspDrawSurface_t *ds;
1010 surfaceInfo_t *info;
1011 mesh_t src, *subdivided, *mesh;
1012 bspDrawVert_t *verts, *dv[ 4 ], fake;
1015 /* bail if this number exceeds the number of raw lightmaps */
1016 if( rawLightmapNum >= numRawLightmaps )
1020 lm = &rawLightmaps[ rawLightmapNum ];
1022 /* -----------------------------------------------------------------
1023 map referenced surfaces onto the raw lightmap
1024 ----------------------------------------------------------------- */
1026 /* walk the list of surfaces on this raw lightmap */
1027 for( n = 0; n < lm->numLightSurfaces; n++ )
1029 /* with > 1 surface per raw lightmap, clear occluded */
1032 for( y = 0; y < lm->sh; y++ )
1034 for( x = 0; x < lm->sw; x++ )
1037 cluster = SUPER_CLUSTER( x, y );
1039 *cluster = CLUSTER_UNMAPPED;
1045 num = lightSurfaces[ lm->firstLightSurface + n ];
1046 ds = &bspDrawSurfaces[ num ];
1047 info = &surfaceInfos[ num ];
1049 /* bail if no lightmap to calculate */
1050 if( info->lm != lm )
1056 /* map the surface onto the lightmap origin/cluster/normal buffers */
1057 switch( ds->surfaceType )
1061 verts = yDrawVerts + ds->firstVert;
1063 /* map the triangles */
1064 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1066 for( i = 0; i < ds->numIndexes; i += 3 )
1068 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1069 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1070 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1071 MapTriangle( lm, info, dv, mapNonAxial );
1077 /* make a mesh from the drawsurf */
1078 src.width = ds->patchWidth;
1079 src.height = ds->patchHeight;
1080 src.verts = &yDrawVerts[ ds->firstVert ];
1081 //% subdivided = SubdivideMesh( src, 8, 512 );
1082 subdivided = SubdivideMesh2( src, info->patchIterations );
1084 /* fit it to the curve and remove colinear verts on rows/columns */
1085 PutMeshOnCurve( *subdivided );
1086 mesh = RemoveLinearMeshColumnsRows( subdivided );
1087 FreeMesh( subdivided );
1090 verts = mesh->verts;
1096 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1097 lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1098 lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1099 lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1103 /* map the mesh quads */
1106 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1108 for( y = 0; y < (mesh->height - 1); y++ )
1110 for( x = 0; x < (mesh->width - 1); x++ )
1113 pw[ 0 ] = x + (y * mesh->width);
1114 pw[ 1 ] = x + ((y + 1) * mesh->width);
1115 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1116 pw[ 3 ] = x + 1 + (y * mesh->width);
1117 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1122 /* get drawverts and map first triangle */
1123 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1124 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1125 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1126 MapTriangle( lm, info, dv, mapNonAxial );
1128 /* get drawverts and map second triangle */
1129 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1130 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1131 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1132 MapTriangle( lm, info, dv, mapNonAxial );
1139 for( y = 0; y < (mesh->height - 1); y++ )
1141 for( x = 0; x < (mesh->width - 1); x++ )
1144 pw[ 0 ] = x + (y * mesh->width);
1145 pw[ 1 ] = x + ((y + 1) * mesh->width);
1146 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1147 pw[ 3 ] = x + 1 + (y * mesh->width);
1153 /* attempt to map quad first */
1154 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1155 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1156 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1157 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1158 if( MapQuad( lm, info, dv ) )
1161 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1163 /* get drawverts and map first triangle */
1164 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1165 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1166 MapTriangle( lm, info, dv, mapNonAxial );
1168 /* get drawverts and map second triangle */
1169 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1170 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1171 MapTriangle( lm, info, dv, mapNonAxial );
1187 /* -----------------------------------------------------------------
1188 average and clean up luxel normals
1189 ----------------------------------------------------------------- */
1191 /* walk the luxels */
1192 for( y = 0; y < lm->sh; y++ )
1194 for( x = 0; x < lm->sw; x++ )
1197 luxel = SUPER_LUXEL( 0, x, y );
1198 normal = SUPER_NORMAL( x, y );
1199 cluster = SUPER_CLUSTER( x, y );
1201 /* only look at mapped luxels */
1205 /* the normal data could be the sum of multiple samples */
1206 if( luxel[ 3 ] > 1.0f )
1207 VectorNormalize( normal, normal );
1209 /* mark this luxel as having only one normal */
1214 /* non-planar surfaces stop here */
1215 if( lm->plane == NULL )
1218 /* -----------------------------------------------------------------
1219 map occluded or unuxed luxels
1220 ----------------------------------------------------------------- */
1222 /* walk the luxels */
1223 radius = floor( superSample / 2 );
1224 radius = radius > 0 ? radius : 1.0f;
1226 for( pass = 2.0f; pass <= radius; pass += 1.0f )
1228 for( y = 0; y < lm->sh; y++ )
1230 for( x = 0; x < lm->sw; x++ )
1233 luxel = SUPER_LUXEL( 0, x, y );
1234 normal = SUPER_NORMAL( x, y );
1235 cluster = SUPER_CLUSTER( x, y );
1237 /* only look at unmapped luxels */
1238 if( *cluster != CLUSTER_UNMAPPED )
1241 /* divine a normal and origin from neighboring luxels */
1242 VectorClear( fake.xyz );
1243 VectorClear( fake.normal );
1244 fake.lightmap[ 0 ][ 0 ] = x; //% 0.0001 + x;
1245 fake.lightmap[ 0 ][ 1 ] = y; //% 0.0001 + y;
1247 for( sy = (y - 1); sy <= (y + 1); sy++ )
1249 if( sy < 0 || sy >= lm->sh )
1252 for( sx = (x - 1); sx <= (x + 1); sx++ )
1254 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1257 /* get neighboring luxel */
1258 luxel = SUPER_LUXEL( 0, sx, sy );
1259 origin = SUPER_ORIGIN( sx, sy );
1260 normal = SUPER_NORMAL( sx, sy );
1261 cluster = SUPER_CLUSTER( sx, sy );
1263 /* only consider luxels mapped in previous passes */
1264 if( *cluster < 0 || luxel[ 0 ] >= pass )
1267 /* add its distinctiveness to our own */
1268 VectorAdd( fake.xyz, origin, fake.xyz );
1269 VectorAdd( fake.normal, normal, fake.normal );
1270 samples += luxel[ 3 ];
1275 if( samples == 0.0f )
1279 VectorDivide( fake.xyz, samples, fake.xyz );
1280 //% VectorDivide( fake.normal, samples, fake.normal );
1281 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1284 /* map the fake vert */
1285 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1290 /* -----------------------------------------------------------------
1291 average and clean up luxel normals
1292 ----------------------------------------------------------------- */
1294 /* walk the luxels */
1295 for( y = 0; y < lm->sh; y++ )
1297 for( x = 0; x < lm->sw; x++ )
1300 luxel = SUPER_LUXEL( 0, x, y );
1301 normal = SUPER_NORMAL( x, y );
1302 cluster = SUPER_CLUSTER( x, y );
1304 /* only look at mapped luxels */
1308 /* the normal data could be the sum of multiple samples */
1309 if( luxel[ 3 ] > 1.0f )
1310 VectorNormalize( normal, normal );
1312 /* mark this luxel as having only one normal */
1320 for( y = 0; y < lm->sh; y++ )
1322 for( x = 0; x < lm->sw; x++ )
1327 cluster = SUPER_CLUSTER( x, y );
1328 origin = SUPER_ORIGIN( x, y );
1329 normal = SUPER_NORMAL( x, y );
1330 luxel = SUPER_LUXEL( x, y );
1335 /* check if within the bounding boxes of all surfaces referenced */
1336 ClearBounds( mins, maxs );
1337 for( n = 0; n < lm->numLightSurfaces; n++ )
1340 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1341 TOL = info->sampleSize + 2;
1342 AddPointToBounds( info->mins, mins, maxs );
1343 AddPointToBounds( info->maxs, mins, maxs );
1344 if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1345 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1346 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1351 if( n < lm->numLightSurfaces )
1354 /* report bogus origin */
1355 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",
1356 rawLightmapNum, x, y, *cluster,
1357 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1358 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1359 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1370 sets up dirtmap (ambient occlusion)
1373 #define DIRT_CONE_ANGLE 88 /* degrees */
1374 #define DIRT_NUM_ANGLE_STEPS 16
1375 #define DIRT_NUM_ELEVATION_STEPS 3
1376 #define DIRT_NUM_VECTORS (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1378 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1379 static int numDirtVectors = 0;
1381 void SetupDirt( void )
1384 float angle, elevation, angleStep, elevationStep;
1388 Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1390 /* calculate angular steps */
1391 angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1392 elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1396 for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1398 /* iterate elevation */
1399 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1401 dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1402 dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1403 dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1408 /* emit some statistics */
1409 Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1415 calculates dirt value for a given sample
1418 float DirtForSample( trace_t *trace )
1421 float gatherDirt, outDirt, angle, elevation, ooDepth;
1422 vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1428 if( trace == NULL || trace->cluster < 0 )
1433 ooDepth = 1.0f / dirtDepth;
1434 VectorCopy( trace->normal, normal );
1436 /* check if the normal is aligned to the world-up */
1437 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1439 if( normal[ 2 ] == 1.0f )
1441 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1442 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1444 else if( normal[ 2 ] == -1.0f )
1446 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1447 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1452 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1453 CrossProduct( normal, worldUp, myRt );
1454 VectorNormalize( myRt, myRt );
1455 CrossProduct( myRt, normal, myUp );
1456 VectorNormalize( myUp, myUp );
1459 /* 1 = random mode, 0 (well everything else) = non-random mode */
1463 for( i = 0; i < numDirtVectors; i++ )
1465 /* get random vector */
1466 angle = Random() * DEG2RAD( 360.0f );
1467 elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1468 temp[ 0 ] = cos( angle ) * sin( elevation );
1469 temp[ 1 ] = sin( angle ) * sin( elevation );
1470 temp[ 2 ] = cos( elevation );
1472 /* transform into tangent space */
1473 direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1474 direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1475 direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1478 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1479 SetupTrace( trace );
1483 if( trace->opaque && !(trace->compileFlags & C_SKY) )
1485 VectorSubtract( trace->hit, trace->origin, displacement );
1486 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1492 /* iterate through ordered vectors */
1493 for( i = 0; i < numDirtVectors; i++ )
1495 /* transform vector into tangent space */
1496 direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1497 direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1498 direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1501 VectorMA( trace->origin, dirtDepth, direction, trace->end );
1502 SetupTrace( trace );
1508 VectorSubtract( trace->hit, trace->origin, displacement );
1509 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1515 VectorMA( trace->origin, dirtDepth, normal, trace->end );
1516 SetupTrace( trace );
1522 VectorSubtract( trace->hit, trace->origin, displacement );
1523 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1527 if( gatherDirt <= 0.0f )
1530 /* apply gain (does this even do much? heh) */
1531 outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1532 if( outDirt > 1.0f )
1536 outDirt *= dirtScale;
1537 if( outDirt > 1.0f )
1540 /* return to sender */
1541 return 1.0f - outDirt;
1548 calculates dirty fraction for each luxel
1551 void DirtyRawLightmap( int rawLightmapNum )
1553 int i, x, y, sx, sy, *cluster;
1554 float *origin, *normal, *dirt, *dirt2, average, samples;
1556 surfaceInfo_t *info;
1561 /* bail if this number exceeds the number of raw lightmaps */
1562 if( rawLightmapNum >= numRawLightmaps )
1566 lm = &rawLightmaps[ rawLightmapNum ];
1569 trace.testOcclusion = qtrue;
1570 trace.forceSunlight = qfalse;
1571 trace.recvShadows = lm->recvShadows;
1572 trace.numSurfaces = lm->numLightSurfaces;
1573 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1574 trace.inhibitRadius = 0.0f;
1575 trace.testAll = qfalse;
1577 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1578 trace.twoSided = qfalse;
1579 for( i = 0; i < trace.numSurfaces; i++ )
1582 info = &surfaceInfos[ trace.surfaces[ i ] ];
1584 /* check twosidedness */
1585 if( info->si->twoSided )
1587 trace.twoSided = qtrue;
1593 for( i = 0; i < trace.numSurfaces; i++ )
1596 info = &surfaceInfos[ trace.surfaces[ i ] ];
1598 /* check twosidedness */
1599 if( info->si->noDirty )
1607 for( y = 0; y < lm->sh; y++ )
1609 for( x = 0; x < lm->sw; x++ )
1612 cluster = SUPER_CLUSTER( x, y );
1613 origin = SUPER_ORIGIN( x, y );
1614 normal = SUPER_NORMAL( x, y );
1615 dirt = SUPER_DIRT( x, y );
1617 /* set default dirt */
1620 /* only look at mapped luxels */
1624 /* don't apply dirty on this surface */
1632 trace.cluster = *cluster;
1633 VectorCopy( origin, trace.origin );
1634 VectorCopy( normal, trace.normal );
1637 *dirt = DirtForSample( &trace );
1641 /* testing no filtering */
1645 for( y = 0; y < lm->sh; y++ )
1647 for( x = 0; x < lm->sw; x++ )
1650 cluster = SUPER_CLUSTER( x, y );
1651 dirt = SUPER_DIRT( x, y );
1653 /* filter dirt by adjacency to unmapped luxels */
1656 for( sy = (y - 1); sy <= (y + 1); sy++ )
1658 if( sy < 0 || sy >= lm->sh )
1661 for( sx = (x - 1); sx <= (x + 1); sx++ )
1663 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1666 /* get neighboring luxel */
1667 cluster = SUPER_CLUSTER( sx, sy );
1668 dirt2 = SUPER_DIRT( sx, sy );
1669 if( *cluster < 0 || *dirt2 <= 0.0f )
1678 if( samples <= 0.0f )
1683 if( samples <= 0.0f )
1687 *dirt = average / samples;
1696 calculates the pvs cluster, origin, normal of a sub-luxel
1699 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1701 int i, *cluster, *cluster2;
1702 float *origin, *origin2, *normal; //% , *normal2;
1703 vec3_t originVecs[ 2 ]; //% , normalVecs[ 2 ];
1706 /* calulate x vector */
1707 if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1709 cluster = SUPER_CLUSTER( x, y );
1710 origin = SUPER_ORIGIN( x, y );
1711 //% normal = SUPER_NORMAL( x, y );
1712 cluster2 = SUPER_CLUSTER( x + 1, y );
1713 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1714 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1716 else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1718 cluster = SUPER_CLUSTER( x - 1, y );
1719 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1720 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1721 cluster2 = SUPER_CLUSTER( x, y );
1722 origin2 = SUPER_ORIGIN( x, y );
1723 //% normal2 = SUPER_NORMAL( x, y );
1727 Error( "Spurious lightmap S vector\n" );
1730 VectorSubtract( origin2, origin, originVecs[ 0 ] );
1731 //% VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1733 /* calulate y vector */
1734 if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1736 cluster = SUPER_CLUSTER( x, y );
1737 origin = SUPER_ORIGIN( x, y );
1738 //% normal = SUPER_NORMAL( x, y );
1739 cluster2 = SUPER_CLUSTER( x, y + 1 );
1740 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1741 //% normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1743 else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1745 cluster = SUPER_CLUSTER( x, y - 1 );
1746 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1747 //% normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1748 cluster2 = SUPER_CLUSTER( x, y );
1749 origin2 = SUPER_ORIGIN( x, y );
1750 //% normal2 = SUPER_NORMAL( x, y );
1753 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1755 VectorSubtract( origin2, origin, originVecs[ 1 ] );
1756 //% VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1758 /* calculate new origin */
1759 //% VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1760 //% VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1761 for( i = 0; i < 3; i++ )
1762 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1765 *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1766 if( *sampleCluster < 0 )
1769 /* calculate new normal */
1770 //% VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1771 //% VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1772 //% if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1774 normal = SUPER_NORMAL( x, y );
1775 VectorCopy( normal, sampleNormal );
1783 SubsampleRawLuxel_r()
1784 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1787 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1789 int b, samples, mapped, lighted;
1792 vec3_t deluxel[ 3 ];
1793 vec3_t origin[ 4 ], normal[ 4 ];
1794 float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1795 vec3_t color, direction = { 0, 0, 0 }, total;
1799 if( lightLuxel[ 3 ] >= lightSamples )
1803 VectorClear( total );
1807 /* make 2x2 subsample stamp */
1808 for( b = 0; b < 4; b++ )
1811 VectorCopy( sampleOrigin, origin[ b ] );
1813 /* calculate position */
1814 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1821 /* increment sample count */
1822 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1825 trace->cluster = *cluster;
1826 VectorCopy( origin[ b ], trace->origin );
1827 VectorCopy( normal[ b ], trace->normal );
1831 LightContributionToSample( trace );
1832 if(trace->forceSubsampling > 1.0f)
1834 /* alphashadow: we subsample as deep as we can */
1840 /* add to totals (fixme: make contrast function) */
1841 VectorCopy( trace->color, luxel[ b ] );
1844 VectorCopy( trace->directionContribution, deluxel[ b ] );
1846 VectorAdd( total, trace->color, total );
1847 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1851 /* subsample further? */
1852 if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1853 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1854 lighted != 0 && lighted != mapped )
1856 for( b = 0; b < 4; b++ )
1858 if( cluster[ b ] < 0 )
1860 SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1865 //% VectorClear( color );
1867 VectorCopy( lightLuxel, color );
1870 VectorCopy( lightDeluxel, direction );
1873 for( b = 0; b < 4; b++ )
1875 if( cluster[ b ] < 0 )
1877 VectorAdd( color, luxel[ b ], color );
1880 VectorAdd( direction, deluxel[ b ], direction );
1889 color[ 0 ] /= samples;
1890 color[ 1 ] /= samples;
1891 color[ 2 ] /= samples;
1894 VectorCopy( color, lightLuxel );
1895 lightLuxel[ 3 ] += 1.0f;
1899 direction[ 0 ] /= samples;
1900 direction[ 1 ] /= samples;
1901 direction[ 2 ] /= samples;
1902 VectorCopy( direction, lightDeluxel );
1907 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1908 static void GaussLikeRandom(float sigma, float *x, float *y)
1911 r = Random() * 2 * Q_PI;
1912 *x = sigma * 2.73861278752581783822 * cos(r);
1913 *y = sigma * 2.73861278752581783822 * sin(r);
1920 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
1924 vec3_t origin, normal;
1925 vec3_t total, totaldirection;
1928 VectorClear( total );
1929 VectorClear( totaldirection );
1931 for(b = 0; b < lightSamples; ++b)
1934 VectorCopy( sampleOrigin, origin );
1935 GaussLikeRandom(bias, &dx, &dy);
1937 /* calculate position */
1938 if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
1945 trace->cluster = cluster;
1946 VectorCopy( origin, trace->origin );
1947 VectorCopy( normal, trace->normal );
1949 LightContributionToSample( trace );
1950 VectorAdd( total, trace->color, total );
1953 VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1961 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1962 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1963 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1967 lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1968 lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1969 lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1977 IlluminateRawLightmap()
1978 illuminates the luxels
1981 #define STACK_LL_SIZE (SUPER_LUXEL_SIZE * 64 * 64)
1982 #define LIGHT_LUXEL( x, y ) (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1983 #define LIGHT_DELUXEL( x, y ) (lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
1985 void IlluminateRawLightmap( int rawLightmapNum )
1987 int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1988 int *cluster, *cluster2, mapped, lighted, totalLighted;
1989 size_t llSize, ldSize;
1991 surfaceInfo_t *info;
1992 qboolean filterColor, filterDir;
1994 float *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1995 unsigned char *flag;
1996 float *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
1997 vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
1998 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2000 float stackLightLuxels[ STACK_LL_SIZE ];
2003 /* bail if this number exceeds the number of raw lightmaps */
2004 if( rawLightmapNum >= numRawLightmaps )
2008 lm = &rawLightmaps[ rawLightmapNum ];
2011 trace.testOcclusion = !noTrace;
2012 trace.forceSunlight = qfalse;
2013 trace.recvShadows = lm->recvShadows;
2014 trace.numSurfaces = lm->numLightSurfaces;
2015 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2016 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2018 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2019 trace.twoSided = qfalse;
2020 for( i = 0; i < trace.numSurfaces; i++ )
2023 info = &surfaceInfos[ trace.surfaces[ i ] ];
2025 /* check twosidedness */
2026 if( info->si->twoSided )
2028 trace.twoSided = qtrue;
2033 /* create a culled light list for this raw lightmap */
2034 CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2036 /* -----------------------------------------------------------------
2038 ----------------------------------------------------------------- */
2041 numLuxelsIlluminated += (lm->sw * lm->sh);
2043 /* test debugging state */
2044 if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
2046 /* debug fill the luxels */
2047 for( y = 0; y < lm->sh; y++ )
2049 for( x = 0; x < lm->sw; x++ )
2052 cluster = SUPER_CLUSTER( x, y );
2054 /* only fill mapped luxels */
2058 /* get particulars */
2059 luxel = SUPER_LUXEL( 0, x, y );
2060 origin = SUPER_ORIGIN( x, y );
2061 normal = SUPER_NORMAL( x, y );
2063 /* color the luxel with raw lightmap num? */
2065 VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2067 /* color the luxel with lightmap axis? */
2068 else if( debugAxis )
2070 luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
2071 luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
2072 luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
2075 /* color the luxel with luxel cluster? */
2076 else if( debugCluster )
2077 VectorCopy( debugColors[ *cluster % 12 ], luxel );
2079 /* color the luxel with luxel origin? */
2080 else if( debugOrigin )
2082 VectorSubtract( lm->maxs, lm->mins, temp );
2083 VectorScale( temp, (1.0f / 255.0f), temp );
2084 VectorSubtract( origin, lm->mins, temp2 );
2085 luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2086 luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2087 luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2090 /* color the luxel with the normal */
2091 else if( normalmap )
2093 luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
2094 luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
2095 luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
2098 /* otherwise clear it */
2100 VectorClear( luxel );
2109 /* allocate temporary per-light luxel storage */
2110 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2111 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2112 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2113 lightLuxels = stackLightLuxels;
2115 lightLuxels = safe_malloc( llSize );
2117 lightDeluxels = safe_malloc( ldSize );
2119 lightDeluxels = NULL;
2122 //% memset( lm->superLuxels[ 0 ], 0, llSize );
2124 /* set ambient color */
2125 for( y = 0; y < lm->sh; y++ )
2127 for( x = 0; x < lm->sw; x++ )
2130 cluster = SUPER_CLUSTER( x, y );
2131 luxel = SUPER_LUXEL( 0, x, y );
2132 normal = SUPER_NORMAL( x, y );
2133 deluxel = SUPER_DELUXEL( x, y );
2135 /* blacken unmapped clusters */
2137 VectorClear( luxel );
2142 VectorCopy( ambientColor, luxel );
2145 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2147 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2148 if(brightness < 0.00390625f)
2149 brightness = 0.00390625f;
2151 VectorScale( normal, brightness, deluxel );
2158 /* clear styled lightmaps */
2159 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2160 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2162 if( lm->superLuxels[ lightmapNum ] != NULL )
2163 memset( lm->superLuxels[ lightmapNum ], 0, size );
2166 /* debugging code */
2167 //% if( trace.numLights <= 0 )
2168 //% Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2170 /* walk light list */
2171 for( i = 0; i < trace.numLights; i++ )
2174 trace.light = trace.lights[ i ];
2177 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2179 if( lm->styles[ lightmapNum ] == trace.light->style ||
2180 lm->styles[ lightmapNum ] == LS_NONE )
2184 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2185 if( lightmapNum >= MAX_LIGHTMAPS )
2187 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2192 memset( lightLuxels, 0, llSize );
2194 memset( lightDeluxels, 0, ldSize );
2197 /* determine filter radius */
2198 filterRadius = lm->filterRadius > trace.light->filterRadius
2200 : trace.light->filterRadius;
2201 if( filterRadius < 0.0f )
2202 filterRadius = 0.0f;
2204 /* set luxel filter radius */
2205 luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2206 if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2207 luxelFilterRadius = 1;
2209 /* allocate sampling flags storage */
2210 if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2212 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2213 if(lm->superFlags == NULL)
2214 lm->superFlags = safe_malloc( size );
2215 memset( (void *) lm->superFlags, 0, size );
2218 /* initial pass, one sample per luxel */
2219 for( y = 0; y < lm->sh; y++ )
2221 for( x = 0; x < lm->sw; x++ )
2224 cluster = SUPER_CLUSTER( x, y );
2228 /* get particulars */
2229 lightLuxel = LIGHT_LUXEL( x, y );
2230 lightDeluxel = LIGHT_DELUXEL( x, y );
2231 origin = SUPER_ORIGIN( x, y );
2232 normal = SUPER_NORMAL( x, y );
2233 flag = SUPER_FLAG( x, y );
2236 ////////// 27's temp hack for testing edge clipping ////
2237 if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2239 lightLuxel[ 1 ] = 255;
2240 lightLuxel[ 3 ] = 1.0f;
2246 /* set contribution count */
2247 lightLuxel[ 3 ] = 1.0f;
2250 trace.cluster = *cluster;
2251 VectorCopy( origin, trace.origin );
2252 VectorCopy( normal, trace.normal );
2254 /* get light for this sample */
2255 LightContributionToSample( &trace );
2256 VectorCopy( trace.color, lightLuxel );
2258 /* add the contribution to the deluxemap */
2261 VectorCopy( trace.directionContribution, lightDeluxel );
2264 /* check for evilness */
2265 if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
2268 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2271 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2277 /* don't even bother with everything else if nothing was lit */
2278 if( totalLighted == 0 )
2281 /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2282 /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2283 if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
2286 for( y = 0; y < (lm->sh - 1); y++ )
2288 for( x = 0; x < (lm->sw - 1); x++ )
2293 VectorClear( total );
2295 /* test 2x2 stamp */
2296 for( t = 0; t < 4; t++ )
2298 /* set sample coords */
2299 sx = x + tests[ t ][ 0 ];
2300 sy = y + tests[ t ][ 1 ];
2303 cluster = SUPER_CLUSTER( sx, sy );
2309 flag = SUPER_FLAG( sx, sy );
2310 if(*flag & FLAG_FORCE_SUBSAMPLING)
2312 /* force a lighted/mapped discrepancy so we subsample */
2317 lightLuxel = LIGHT_LUXEL( sx, sy );
2318 VectorAdd( total, lightLuxel, total );
2319 if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2323 /* if total color is under a certain amount, then don't bother subsampling */
2324 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2327 /* if all 4 pixels are either in shadow or light, then don't subsample */
2328 if( lighted != 0 && lighted != mapped )
2330 for( t = 0; t < 4; t++ )
2332 /* set sample coords */
2333 sx = x + tests[ t ][ 0 ];
2334 sy = y + tests[ t ][ 1 ];
2337 cluster = SUPER_CLUSTER( sx, sy );
2340 flag = SUPER_FLAG( sx, sy );
2341 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2343 lightLuxel = LIGHT_LUXEL( sx, sy );
2344 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2345 origin = SUPER_ORIGIN( sx, sy );
2347 /* only subsample shadowed luxels */
2348 //% if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2352 if(lightRandomSamples)
2353 RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2355 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2357 *flag |= FLAG_ALREADY_SUBSAMPLED;
2359 /* debug code to colorize subsampled areas to yellow */
2360 //% luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2361 //% VectorSet( luxel, 255, 204, 0 );
2368 /* tertiary pass, apply dirt map (ambient occlusion) */
2372 for( y = 0; y < lm->sh; y++ )
2374 for( x = 0; x < lm->sw; x++ )
2377 cluster = SUPER_CLUSTER( x, y );
2381 /* get particulars */
2382 lightLuxel = LIGHT_LUXEL( x, y );
2383 dirt = SUPER_DIRT( x, y );
2385 /* scale light value */
2386 VectorScale( lightLuxel, *dirt, lightLuxel );
2391 /* allocate sampling lightmap storage */
2392 if( lm->superLuxels[ lightmapNum ] == NULL )
2394 /* allocate sampling lightmap storage */
2395 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2396 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2397 memset( lm->superLuxels[ lightmapNum ], 0, size );
2401 if( lightmapNum > 0 )
2403 lm->styles[ lightmapNum ] = trace.light->style;
2404 //% Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2407 /* copy to permanent luxels */
2408 for( y = 0; y < lm->sh; y++ )
2410 for( x = 0; x < lm->sw; x++ )
2412 /* get cluster and origin */
2413 cluster = SUPER_CLUSTER( x, y );
2416 origin = SUPER_ORIGIN( x, y );
2419 if( luxelFilterRadius )
2422 VectorClear( averageColor );
2423 VectorClear( averageDir );
2426 /* cheaper distance-based filtering */
2427 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2429 if( sy < 0 || sy >= lm->sh )
2432 for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2434 if( sx < 0 || sx >= lm->sw )
2437 /* get particulars */
2438 cluster = SUPER_CLUSTER( sx, sy );
2441 lightLuxel = LIGHT_LUXEL( sx, sy );
2442 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2445 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2446 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2448 /* scale luxel by filter weight */
2449 VectorScale( lightLuxel, weight, color );
2450 VectorAdd( averageColor, color, averageColor );
2453 VectorScale( lightDeluxel, weight, direction );
2454 VectorAdd( averageDir, direction, averageDir );
2461 if( samples <= 0.0f )
2464 /* scale into luxel */
2465 luxel = SUPER_LUXEL( lightmapNum, x, y );
2468 /* handle negative light */
2469 if( trace.light->flags & LIGHT_NEGATIVE )
2471 luxel[ 0 ] -= averageColor[ 0 ] / samples;
2472 luxel[ 1 ] -= averageColor[ 1 ] / samples;
2473 luxel[ 2 ] -= averageColor[ 2 ] / samples;
2476 /* handle normal light */
2479 luxel[ 0 ] += averageColor[ 0 ] / samples;
2480 luxel[ 1 ] += averageColor[ 1 ] / samples;
2481 luxel[ 2 ] += averageColor[ 2 ] / samples;
2486 /* scale into luxel */
2487 deluxel = SUPER_DELUXEL( x, y );
2488 deluxel[ 0 ] += averageDir[ 0 ] / samples;
2489 deluxel[ 1 ] += averageDir[ 1 ] / samples;
2490 deluxel[ 2 ] += averageDir[ 2 ] / samples;
2497 /* get particulars */
2498 lightLuxel = LIGHT_LUXEL( x, y );
2499 lightDeluxel = LIGHT_DELUXEL( x, y );
2500 luxel = SUPER_LUXEL( lightmapNum, x, y );
2501 deluxel = SUPER_DELUXEL( x, y );
2503 /* handle negative light */
2504 if( trace.light->flags & LIGHT_NEGATIVE )
2505 VectorScale( averageColor, -1.0f, averageColor );
2510 /* handle negative light */
2511 if( trace.light->flags & LIGHT_NEGATIVE )
2512 VectorSubtract( luxel, lightLuxel, luxel );
2514 /* handle normal light */
2516 VectorAdd( luxel, lightLuxel, luxel );
2520 VectorAdd( deluxel, lightDeluxel, deluxel );
2527 /* free temporary luxels */
2528 if( lightLuxels != stackLightLuxels )
2529 free( lightLuxels );
2532 free( lightDeluxels );
2535 /* free light list */
2536 FreeTraceLights( &trace );
2538 /* floodlight pass */
2540 FloodlightIlluminateLightmap(lm);
2544 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2547 if( lm->superLuxels[ lightmapNum ] == NULL )
2550 for( y = 0; y < lm->sh; y++ )
2552 for( x = 0; x < lm->sw; x++ )
2555 cluster = SUPER_CLUSTER( x, y );
2556 //% if( *cluster < 0 )
2559 /* get particulars */
2560 luxel = SUPER_LUXEL( lightmapNum, x, y );
2561 normal = SUPER_NORMAL ( x, y );
2563 luxel[0]=(normal[0]*127)+127;
2564 luxel[1]=(normal[1]*127)+127;
2565 luxel[2]=(normal[2]*127)+127;
2571 /* -----------------------------------------------------------------
2573 ----------------------------------------------------------------- */
2577 /* walk lightmaps */
2578 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2581 if( lm->superLuxels[ lightmapNum ] == NULL )
2584 /* apply dirt to each luxel */
2585 for( y = 0; y < lm->sh; y++ )
2587 for( x = 0; x < lm->sw; x++ )
2590 cluster = SUPER_CLUSTER( x, y );
2591 //% if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2594 /* get particulars */
2595 luxel = SUPER_LUXEL( lightmapNum, x, y );
2596 dirt = SUPER_DIRT( x, y );
2599 VectorScale( luxel, *dirt, luxel );
2603 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2609 /* -----------------------------------------------------------------
2611 ----------------------------------------------------------------- */
2613 /* walk lightmaps */
2614 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2617 if( lm->superLuxels[ lightmapNum ] == NULL )
2620 /* average occluded luxels from neighbors */
2621 for( y = 0; y < lm->sh; y++ )
2623 for( x = 0; x < lm->sw; x++ )
2625 /* get particulars */
2626 cluster = SUPER_CLUSTER( x, y );
2627 luxel = SUPER_LUXEL( lightmapNum, x, y );
2628 deluxel = SUPER_DELUXEL( x, y );
2629 normal = SUPER_NORMAL( x, y );
2631 /* determine if filtering is necessary */
2632 filterColor = qfalse;
2635 (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2636 filterColor = qtrue;
2638 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2641 if( !filterColor && !filterDir )
2644 /* choose seed amount */
2645 VectorClear( averageColor );
2646 VectorClear( averageDir );
2649 /* walk 3x3 matrix */
2650 for( sy = (y - 1); sy <= (y + 1); sy++ )
2652 if( sy < 0 || sy >= lm->sh )
2655 for( sx = (x - 1); sx <= (x + 1); sx++ )
2657 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2660 /* get neighbor's particulars */
2661 cluster2 = SUPER_CLUSTER( sx, sy );
2662 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2663 deluxel2 = SUPER_DELUXEL( sx, sy );
2665 /* ignore unmapped/unlit luxels */
2666 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2667 (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2670 /* add its distinctiveness to our own */
2671 VectorAdd( averageColor, luxel2, averageColor );
2672 samples += luxel2[ 3 ];
2674 VectorAdd( averageDir, deluxel2, averageDir );
2679 if( samples <= 0.0f )
2682 /* dark lightmap seams */
2685 if( lightmapNum == 0 )
2686 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2693 VectorDivide( averageColor, samples, luxel );
2697 VectorDivide( averageDir, samples, deluxel );
2699 /* set cluster to -3 */
2701 *cluster = CLUSTER_FLOODED;
2709 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2712 if( lm->superLuxels[ lightmapNum ] == NULL )
2714 for( y = 0; y < lm->sh; y++ )
2715 for( x = 0; x < lm->sw; x++ )
2718 cluster = SUPER_CLUSTER( x, y );
2719 luxel = SUPER_LUXEL( lightmapNum, x, y );
2720 deluxel = SUPER_DELUXEL( x, y );
2721 if(!luxel || !deluxel || !cluster)
2723 Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2726 else if(*cluster < 0)
2729 // should have neither deluxemap nor lightmap
2731 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2736 // should have both deluxemap and lightmap
2738 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2748 IlluminateVertexes()
2749 light the surface vertexes
2752 #define VERTEX_NUDGE 4.0f
2754 void IlluminateVertexes( int num )
2756 int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2757 int lightmapNum, numAvg;
2758 float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2759 vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2760 bspDrawSurface_t *ds;
2761 surfaceInfo_t *info;
2763 bspDrawVert_t *verts;
2765 float floodLightAmount;
2769 /* get surface, info, and raw lightmap */
2770 ds = &bspDrawSurfaces[ num ];
2771 info = &surfaceInfos[ num ];
2774 /* -----------------------------------------------------------------
2775 illuminate the vertexes
2776 ----------------------------------------------------------------- */
2778 /* calculate vertex lighting for surfaces without lightmaps */
2779 if( lm == NULL || cpmaHack )
2782 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2783 trace.forceSunlight = info->si->forceSunlight;
2784 trace.recvShadows = info->recvShadows;
2785 trace.numSurfaces = 1;
2786 trace.surfaces = #
2787 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2789 /* twosided lighting */
2790 trace.twoSided = info->si->twoSided;
2792 /* make light list for this surface */
2793 CreateTraceLightsForSurface( num, &trace );
2796 verts = yDrawVerts + ds->firstVert;
2798 memset( avgColors, 0, sizeof( avgColors ) );
2800 /* walk the surface verts */
2801 for( i = 0; i < ds->numVerts; i++ )
2803 /* get vertex luxel */
2804 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2806 /* color the luxel with raw lightmap num? */
2808 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2810 /* color the luxel with luxel origin? */
2811 else if( debugOrigin )
2813 VectorSubtract( info->maxs, info->mins, temp );
2814 VectorScale( temp, (1.0f / 255.0f), temp );
2815 VectorSubtract( origin, lm->mins, temp2 );
2816 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2817 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2818 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2821 /* color the luxel with the normal */
2822 else if( normalmap )
2824 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2825 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2826 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2829 /* illuminate the vertex */
2832 /* clear vertex luxel */
2833 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2835 /* try at initial origin */
2836 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2837 if( trace.cluster >= 0 )
2840 VectorCopy( verts[ i ].xyz, trace.origin );
2841 VectorCopy( verts[ i ].normal, trace.normal );
2844 if( dirty && !bouncing )
2845 dirt = DirtForSample( &trace );
2849 /* jal: floodlight */
2850 floodLightAmount = 0.0f;
2851 VectorClear( floodColor );
2852 if( floodlighty && !bouncing )
2854 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2855 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2859 LightingAtSample( &trace, ds->vertexStyles, colors );
2862 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2865 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2867 /* jal: floodlight */
2868 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2871 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2872 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2873 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2877 /* is this sample bright enough? */
2878 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2879 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2880 radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2881 radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2883 /* nudge the sample point around a bit */
2884 for( x = 0; x < 5; x++ )
2886 /* two's complement 0, 1, -1, 2, -2, etc */
2887 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2889 for( y = 0; y < 5; y++ )
2891 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2893 for( z = 0; z < 5; z++ )
2895 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2898 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2899 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2900 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2902 /* try at nudged origin */
2903 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2904 if( trace.cluster < 0 )
2908 if( dirty && !bouncing )
2909 dirt = DirtForSample( &trace );
2913 /* jal: floodlight */
2914 floodLightAmount = 0.0f;
2915 VectorClear( floodColor );
2916 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 ] )
2950 /* add to average? */
2951 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2952 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2953 radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2954 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 */
2972 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2973 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2977 VectorCopy( ambientColor, avgColors[ 0 ] );
2980 /* clean up and store vertex color */
2981 for( i = 0; i < ds->numVerts; i++ )
2983 /* get vertex luxel */
2984 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2986 /* store average in occluded vertexes */
2987 if( radVertLuxel[ 0 ] < 0.0f )
2989 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2991 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2992 VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2995 //% VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
3000 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3003 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3004 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3007 if( bouncing || bounce == 0 || !bounceOnly )
3008 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3009 if( !info->si->noVertexLight )
3010 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 )
3044 /* get luxel coords */
3045 x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3046 y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3049 else if( x >= lm->sw )
3053 else if( y >= lm->sh )
3056 /* get vertex luxels */
3057 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3058 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3060 /* color the luxel with the normal? */
3063 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
3064 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
3065 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
3068 /* color the luxel with surface num? */
3069 else if( debugSurfaces )
3070 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3072 /* divine color from the superluxels */
3075 /* increasing radius */
3076 VectorClear( radVertLuxel );
3078 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3080 /* sample within radius */
3081 for( sy = (y - radius); sy <= (y + radius); sy++ )
3083 if( sy < 0 || sy >= lm->sh )
3086 for( sx = (x - radius); sx <= (x + radius); sx++ )
3088 if( sx < 0 || sx >= lm->sw )
3091 /* get luxel particulars */
3092 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3093 cluster = SUPER_CLUSTER( sx, sy );
3097 /* testing: must be brigher than ambient color */
3098 //% if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3101 /* add its distinctiveness to our own */
3102 VectorAdd( radVertLuxel, luxel, radVertLuxel );
3103 samples += luxel[ 3 ];
3109 if( samples > 0.0f )
3110 VectorDivide( radVertLuxel, samples, radVertLuxel );
3112 VectorCopy( ambientColor, radVertLuxel );
3115 /* store into floating point storage */
3116 VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3117 numVertsIlluminated++;
3119 /* store into bytes (for vertex approximation) */
3120 if( !info->si->noVertexLight )
3121 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3128 /* -------------------------------------------------------------------------------
3130 light optimization (-fast)
3132 creates a list of lights that will affect a surface and stores it in tw
3133 this is to optimize surface lighting by culling out as many of the
3134 lights in the world as possible from further calculation
3136 ------------------------------------------------------------------------------- */
3140 determines opaque brushes in the world and find sky shaders for sunlight calculations
3143 void SetupBrushesFlags( int mask_any, int test_any, int mask_all, int test_all )
3146 unsigned int compileFlags, allCompileFlags;
3149 bspBrushSide_t *side;
3150 bspShader_t *shader;
3155 Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3158 if( opaqueBrushes == NULL )
3159 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3162 memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3163 numOpaqueBrushes = 0;
3165 /* walk the list of worldspawn brushes */
3166 for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3169 b = bspModels[ 0 ].firstBSPBrush + i;
3170 brush = &bspBrushes[ b ];
3172 /* check all sides */
3175 allCompileFlags = ~(0u);
3176 for( j = 0; j < brush->numSides && inside; j++ )
3178 /* do bsp shader calculations */
3179 side = &bspBrushSides[ brush->firstSide + j ];
3180 shader = &bspShaders[ side->shaderNum ];
3182 /* get shader info */
3183 si = ShaderInfoForShaderNull( shader->shader );
3187 /* or together compile flags */
3188 compileFlags |= si->compileFlags;
3189 allCompileFlags &= si->compileFlags;
3192 /* determine if this brush is opaque to light */
3193 if( (compileFlags & mask_any) == test_any && (allCompileFlags & mask_all) == test_all )
3195 opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3201 /* emit some statistics */
3202 Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3204 void SetupBrushes( void )
3206 SetupBrushesFlags(C_TRANSLUCENT, 0, 0, 0);
3213 determines if two clusters are visible to each other using the PVS
3216 qboolean ClusterVisible( int a, int b )
3223 if( a < 0 || b < 0 )
3231 if( numBSPVisBytes <=8 )
3235 /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3236 leafBytes = ((int*) bspVisBytes)[ 1 ];
3237 pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3240 if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3249 borrowed from vlight.c
3252 int PointInLeafNum_r( vec3_t point, int nodenum )
3260 while( nodenum >= 0 )
3262 node = &bspNodes[ nodenum ];
3263 plane = &bspPlanes[ node->planeNum ];
3264 dist = DotProduct( point, plane->normal ) - plane->dist;
3266 nodenum = node->children[ 0 ];
3267 else if( dist < -0.1 )
3268 nodenum = node->children[ 1 ];
3271 leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3272 if( bspLeafs[ leafnum ].cluster != -1 )
3274 nodenum = node->children[ 1 ];
3278 leafnum = -nodenum - 1;
3286 borrowed from vlight.c
3289 int PointInLeafNum( vec3_t point )
3291 return PointInLeafNum_r( point, 0 );
3297 ClusterVisibleToPoint() - ydnar
3298 returns qtrue if point can "see" cluster
3301 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3306 /* get leafNum for point */
3307 pointCluster = ClusterForPoint( point );
3308 if( pointCluster < 0 )
3312 return ClusterVisible( pointCluster, cluster );
3318 ClusterForPoint() - ydnar
3319 returns the pvs cluster for point
3322 int ClusterForPoint( vec3_t point )
3327 /* get leafNum for point */
3328 leafNum = PointInLeafNum( point );
3332 /* return the cluster */
3333 return bspLeafs[ leafNum ].cluster;
3339 ClusterForPointExt() - ydnar
3340 also takes brushes into account for occlusion testing
3343 int ClusterForPointExt( vec3_t point, float epsilon )
3345 int i, j, b, leafNum, cluster;
3348 int *brushes, numBSPBrushes;
3354 /* get leaf for point */
3355 leafNum = PointInLeafNum( point );
3358 leaf = &bspLeafs[ leafNum ];
3360 /* get the cluster */
3361 cluster = leaf->cluster;
3365 /* transparent leaf, so check point against all brushes in the leaf */
3366 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3367 numBSPBrushes = leaf->numBSPLeafBrushes;
3368 for( i = 0; i < numBSPBrushes; i++ )
3372 if( b > maxOpaqueBrush )
3374 brush = &bspBrushes[ b ];
3375 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3378 /* check point against all planes */
3380 for( j = 0; j < brush->numSides && inside; j++ )
3382 plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3383 dot = DotProduct( point, plane->normal );
3389 /* if inside, return bogus cluster */
3394 /* if the point made it this far, it's not inside any opaque brushes */
3401 ClusterForPointExtFilter() - ydnar
3402 adds cluster checking against a list of known valid clusters
3405 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3410 /* get cluster for point */
3411 cluster = ClusterForPointExt( point, epsilon );
3413 /* check if filtering is necessary */
3414 if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3418 for( i = 0; i < numClusters; i++ )
3420 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3431 ShaderForPointInLeaf() - ydnar
3432 checks a point against all brushes in a leaf, returning the shader of the brush
3433 also sets the cumulative surface and content flags for the brush hit
3436 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3441 int *brushes, numBSPBrushes;
3444 bspBrushSide_t *side;
3446 bspShader_t *shader;
3447 int allSurfaceFlags, allContentFlags;
3450 /* clear things out first */
3457 leaf = &bspLeafs[ leafNum ];
3459 /* transparent leaf, so check point against all brushes in the leaf */
3460 brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3461 numBSPBrushes = leaf->numBSPLeafBrushes;
3462 for( i = 0; i < numBSPBrushes; i++ )
3465 brush = &bspBrushes[ brushes[ i ] ];
3467 /* check point against all planes */
3469 allSurfaceFlags = 0;
3470 allContentFlags = 0;
3471 for( j = 0; j < brush->numSides && inside; j++ )
3473 side = &bspBrushSides[ brush->firstSide + j ];
3474 plane = &bspPlanes[ side->planeNum ];
3475 dot = DotProduct( point, plane->normal );
3481 shader = &bspShaders[ side->shaderNum ];
3482 allSurfaceFlags |= shader->surfaceFlags;
3483 allContentFlags |= shader->contentFlags;
3487 /* handle if inside */
3490 /* if there are desired flags, check for same and continue if they aren't matched */
3491 if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3493 if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3496 /* store the cumulative flags and return the brush shader (which is mostly useless) */
3497 *surfaceFlags = allSurfaceFlags;
3498 *contentFlags = allContentFlags;
3499 return brush->shaderNum;
3503 /* if the point made it this far, it's not inside any brushes */
3511 chops a bounding box by the plane defined by origin and normal
3512 returns qfalse if the bounds is entirely clipped away
3514 this is not exactly the fastest way to do this...
3517 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3519 /* FIXME: rewrite this so it doesn't use bloody brushes */
3527 calculates each light's effective envelope,
3528 taking into account brightness, type, and pvs.
3531 #define LIGHT_EPSILON 0.125f
3532 #define LIGHT_NUDGE 2.0f
3534 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3536 int i, x, y, z, x1, y1, z1;
3537 light_t *light, *light2, **owner;
3539 vec3_t origin, dir, mins, maxs;
3540 float radius, intensity;
3541 light_t *buckets[ 256 ];
3544 /* early out for weird cases where there are no lights */
3545 if( lights == NULL )
3549 Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3553 numCulledLights = 0;
3555 while( *owner != NULL )
3560 /* handle negative lights */
3561 if( light->photons < 0.0f || light->add < 0.0f )
3563 light->photons *= -1.0f;
3564 light->add *= -1.0f;
3565 light->flags |= LIGHT_NEGATIVE;
3569 if( light->type == EMIT_SUN )
3573 light->envelope = MAX_WORLD_COORD * 8.0f;
3574 VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3575 VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3578 /* everything else */
3581 /* get pvs cluster for light */
3582 light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3584 /* invalid cluster? */
3585 if( light->cluster < 0 )
3587 /* nudge the sample point around a bit */
3588 for( x = 0; x < 4; x++ )
3590 /* two's complement 0, 1, -1, 2, -2, etc */
3591 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3593 for( y = 0; y < 4; y++ )
3595 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3597 for( z = 0; z < 4; z++ )
3599 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3602 origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3603 origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3604 origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3606 /* try at nudged origin */
3607 light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3608 if( light->cluster < 0 )
3612 VectorCopy( origin, light->origin );
3618 /* only calculate for lights in pvs and outside of opaque brushes */
3619 if( light->cluster >= 0 )
3621 /* set light fast flag */
3623 light->flags |= LIGHT_FAST_TEMP;
3625 light->flags &= ~LIGHT_FAST_TEMP;
3626 if( light->si && light->si->noFast )
3627 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3629 /* clear light envelope */
3630 light->envelope = 0;
3632 /* handle area lights */
3633 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3635 light->envelope = MAX_WORLD_COORD * 8.0f;
3637 /* check for fast mode */
3638 if( (light->flags & LIGHT_FAST) || (light->flags & LIGHT_FAST_TEMP) )
3640 /* ugly hack to calculate extent for area lights, but only done once */
3641 VectorScale( light->normal, -1.0f, dir );
3642 for( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3646 VectorMA( light->origin, radius, light->normal, origin );
3647 factor = PointToPolygonFormFactor( origin, dir, light->w );
3650 if( (factor * light->add) <= light->falloffTolerance )
3652 light->envelope = radius;
3658 intensity = light->photons; /* hopefully not used */
3663 intensity = light->photons;
3667 if( light->envelope <= 0.0f )
3669 /* solve distance for non-distance lights */
3670 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3671 light->envelope = MAX_WORLD_COORD * 8.0f;
3673 else if( (light->flags & LIGHT_FAST) || (light->flags & LIGHT_FAST_TEMP) )
3675 /* solve distance for linear lights */
3676 if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3677 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3680 add = angle * light->photons * linearScale - (dist * light->fade);
3681 T = (light->photons * linearScale) - (dist * light->fade);
3682 T + (dist * light->fade) = (light->photons * linearScale);
3683 dist * light->fade = (light->photons * linearScale) - T;
3684 dist = ((light->photons * linearScale) - T) / light->fade;
3687 /* solve for inverse square falloff */
3689 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3692 add = light->photons / (dist * dist);
3693 T = light->photons / (dist * dist);
3694 T * (dist * dist) = light->photons;
3695 dist = sqrt( light->photons / T );
3700 /* solve distance for linear lights */
3701 if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3702 light->envelope = (intensity * linearScale) / light->fade;
3704 /* can't cull these */
3706 light->envelope = MAX_WORLD_COORD * 8.0f;
3710 /* chop radius against pvs */
3713 ClearBounds( mins, maxs );
3715 /* check all leaves */
3716 for( i = 0; i < numBSPLeafs; i++ )
3719 leaf = &bspLeafs[ i ];
3722 if( leaf->cluster < 0 )
3724 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3727 /* add this leafs bbox to the bounds */
3728 VectorCopy( leaf->mins, origin );
3729 AddPointToBounds( origin, mins, maxs );
3730 VectorCopy( leaf->maxs, origin );
3731 AddPointToBounds( origin, mins, maxs );
3734 /* test to see if bounds encompass light */
3735 for( i = 0; i < 3; i++ )
3737 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3739 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3740 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3741 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3742 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3743 AddPointToBounds( light->origin, mins, maxs );
3747 /* chop the bounds by a plane for area lights and spotlights */
3748 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3749 ChopBounds( mins, maxs, light->origin, light->normal );
3752 VectorCopy( mins, light->mins );
3753 VectorCopy( maxs, light->maxs );
3755 /* reflect bounds around light origin */
3756 //% VectorMA( light->origin, -1.0f, origin, origin );
3757 VectorScale( light->origin, 2, origin );
3758 VectorSubtract( origin, maxs, origin );
3759 AddPointToBounds( origin, mins, maxs );
3760 //% VectorMA( light->origin, -1.0f, mins, origin );
3761 VectorScale( light->origin, 2, origin );
3762 VectorSubtract( origin, mins, origin );
3763 AddPointToBounds( origin, mins, maxs );
3765 /* calculate spherical bounds */
3766 VectorSubtract( maxs, light->origin, dir );
3767 radius = (float) VectorLength( dir );
3769 /* if this radius is smaller than the envelope, then set the envelope to it */
3770 if( radius < light->envelope )
3772 light->envelope = radius;
3773 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3776 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3779 /* add grid/surface only check */
3782 if( !(light->flags & LIGHT_GRID) )
3783 light->envelope = 0.0f;
3787 if( !(light->flags & LIGHT_SURFACES) )
3788 light->envelope = 0.0f;
3793 if( light->cluster < 0 || light->envelope <= 0.0f )
3796 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3798 /* delete the light */
3800 *owner = light->next;
3801 if( light->w != NULL )
3808 /* square envelope */
3809 light->envelope2 = (light->envelope * light->envelope);
3811 /* increment light count */
3814 /* set next light */
3815 owner = &((**owner).next);
3818 /* bucket sort lights by style */
3819 memset( buckets, 0, sizeof( buckets ) );
3821 for( light = lights; light != NULL; light = light2 )
3823 /* get next light */
3824 light2 = light->next;
3826 /* filter into correct bucket */
3827 light->next = buckets[ light->style ];
3828 buckets[ light->style ] = light;
3830 /* if any styled light is present, automatically set nocollapse */
3831 if( light->style != LS_NORMAL )
3835 /* filter back into light list */
3837 for( i = 255; i >= 0; i-- )
3840 for( light = buckets[ i ]; light != NULL; light = light2 )
3842 light2 = light->next;
3843 light->next = lights;
3848 /* emit some statistics */
3849 Sys_Printf( "%9d total lights\n", numLights );
3850 Sys_Printf( "%9d culled lights\n", numCulledLights );
3856 CreateTraceLightsForBounds()
3857 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3860 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3864 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3865 float radius, dist, length;
3868 /* potential pre-setup */
3869 if( numLights == 0 )
3870 SetupEnvelopes( qfalse, fast );
3873 //% 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 ] );
3875 /* allocate the light list */
3876 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3877 trace->numLights = 0;
3879 /* calculate spherical bounds */
3880 VectorAdd( mins, maxs, origin );
3881 VectorScale( origin, 0.5f, origin );
3882 VectorSubtract( maxs, origin, dir );
3883 radius = (float) VectorLength( dir );
3885 /* get length of normal vector */
3886 if( normal != NULL )
3887 length = VectorLength( normal );
3890 normal = nullVector;
3894 /* test each light and see if it reaches the sphere */
3895 /* note: the attenuation code MUST match LightingAtSample() */
3896 for( light = lights; light; light = light->next )
3898 /* check zero sized envelope */
3899 if( light->envelope <= 0 )
3901 lightsEnvelopeCulled++;
3906 if( !(light->flags & flags) )
3909 /* sunlight skips all this nonsense */
3910 if( light->type != EMIT_SUN )
3916 /* check against pvs cluster */
3917 if( numClusters > 0 && clusters != NULL )
3919 for( i = 0; i < numClusters; i++ )
3921 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3926 if( i == numClusters )
3928 lightsClusterCulled++;
3933 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3934 VectorSubtract( light->origin, origin, dir );
3935 dist = VectorLength( dir );
3936 dist -= light->envelope;
3940 lightsEnvelopeCulled++;
3944 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3947 for( i = 0; i < 3; i++ )
3949 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3954 lightsBoundsCulled++;
3960 /* planar surfaces (except twosided surfaces) have a couple more checks */
3961 if( length > 0.0f && trace->twoSided == qfalse )
3963 /* lights coplanar with a surface won't light it */
3964 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3966 lightsPlaneCulled++;
3970 /* check to see if light is behind the plane */
3971 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3973 lightsPlaneCulled++;
3978 /* add this light */
3979 trace->lights[ trace->numLights++ ] = light;
3982 /* make last night null */
3983 trace->lights[ trace->numLights ] = NULL;
3988 void FreeTraceLights( trace_t *trace )
3990 if( trace->lights != NULL )
3991 free( trace->lights );
3997 CreateTraceLightsForSurface()
3998 creates a list of lights that can potentially affect a drawsurface
4001 void CreateTraceLightsForSurface( int num, trace_t *trace )
4004 vec3_t mins, maxs, normal;
4006 bspDrawSurface_t *ds;
4007 surfaceInfo_t *info;
4014 /* get drawsurface and info */
4015 ds = &bspDrawSurfaces[ num ];
4016 info = &surfaceInfos[ num ];
4018 /* get the mins/maxs for the dsurf */
4019 ClearBounds( mins, maxs );
4020 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4021 for( i = 0; i < ds->numVerts; i++ )
4023 dv = &yDrawVerts[ ds->firstVert + i ];
4024 AddPointToBounds( dv->xyz, mins, maxs );
4025 if( !VectorCompare( dv->normal, normal ) )
4026 VectorClear( normal );
4029 /* create the lights for the bounding box */
4030 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4033 /////////////////////////////////////////////////////////////
4035 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4036 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4037 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4038 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4040 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4041 static int numFloodVectors = 0;
4043 void SetupFloodLight( void )
4046 float angle, elevation, angleStep, elevationStep;
4048 double v1,v2,v3,v4,v5,v6;
4051 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4053 /* calculate angular steps */
4054 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4055 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4059 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4061 /* iterate elevation */
4062 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4064 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4065 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4066 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4071 /* emit some statistics */
4072 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4075 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4077 if( value[ 0 ] != '\0' )
4080 v4=floodlightDistance;
4081 v5=floodlightIntensity;
4082 v6=floodlightDirectionScale;
4084 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6);
4086 floodlightRGB[0]=v1;
4087 floodlightRGB[1]=v2;
4088 floodlightRGB[2]=v3;
4090 if (VectorLength(floodlightRGB)==0)
4092 VectorSet(floodlightRGB,0.94,0.94,1.0);
4099 floodlightDistance=v4;
4100 floodlightIntensity=v5;
4101 floodlightDirectionScale=v6;
4103 floodlighty = qtrue;
4104 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4108 VectorSet(floodlightRGB,0.94,0.94,1.0);
4112 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat(floodlightRGB[0]);
4113 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat(floodlightRGB[1]);
4114 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat(floodlightRGB[2]);
4116 ColorNormalize(floodlightRGB,floodlightRGB);
4120 FloodLightForSample()
4121 calculates floodlight value for a given sample
4122 once again, kudos to the dirtmapping coder
4125 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4131 float gatherLight, outLight;
4132 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4140 if( trace == NULL || trace->cluster < 0 )
4145 dd = floodLightDistance;
4146 VectorCopy( trace->normal, normal );
4148 /* check if the normal is aligned to the world-up */
4149 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4151 if( normal[ 2 ] == 1.0f )
4153 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4154 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4156 else if( normal[ 2 ] == -1.0f )
4158 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4159 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4164 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4165 CrossProduct( normal, worldUp, myRt );
4166 VectorNormalize( myRt, myRt );
4167 CrossProduct( myRt, normal, myUp );
4168 VectorNormalize( myUp, myUp );
4171 /* vortex: optimise floodLightLowQuality a bit */
4172 if ( floodLightLowQuality == qtrue )
4174 /* iterate through ordered vectors */
4175 for( i = 0; i < numFloodVectors; i++ )
4176 if (rand()%10 != 0 ) continue;
4180 /* iterate through ordered vectors */
4181 for( i = 0; i < numFloodVectors; i++ )
4185 /* transform vector into tangent space */
4186 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4187 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4188 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4191 VectorMA( trace->origin, dd, direction, trace->end );
4193 //VectorMA( trace->origin, 1, direction, trace->origin );
4195 SetupTrace( trace );
4200 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4204 else if ( trace->opaque )
4206 VectorSubtract( trace->hit, trace->origin, displacement );
4207 d=VectorLength( displacement );
4209 // d=trace->distance;
4210 //if (d>256) gatherDirt+=1;
4212 if (contribution>1) contribution=1.0f;
4214 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4217 gatherLight+=contribution;
4222 if( gatherLight <= 0.0f )
4230 outLight=gatherLight;
4231 if( outLight > 1.0f )
4234 /* return to sender */
4239 FloodLightRawLightmap
4240 lighttracer style ambient occlusion light hack.
4241 Kudos to the dirtmapping author for most of this source.
4242 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4243 VorteX: fixed problems with deluxemapping
4246 // floodlight pass on a lightmap
4247 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4249 int i, x, y, *cluster;
4250 float *origin, *normal, *floodlight, floodLightAmount;
4251 surfaceInfo_t *info;
4254 // float samples, average, *floodlight2;
4256 memset(&trace,0,sizeof(trace_t));
4259 trace.testOcclusion = qtrue;
4260 trace.forceSunlight = qfalse;
4261 trace.twoSided = qtrue;
4262 trace.recvShadows = lm->recvShadows;
4263 trace.numSurfaces = lm->numLightSurfaces;
4264 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4265 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4266 trace.testAll = qfalse;
4267 trace.distance = 1024;
4269 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4270 //trace.twoSided = qfalse;
4271 for( i = 0; i < trace.numSurfaces; i++ )
4274 info = &surfaceInfos[ trace.surfaces[ i ] ];
4276 /* check twosidedness */
4277 if( info->si->twoSided )
4279 trace.twoSided = qtrue;
4284 /* gather floodlight */
4285 for( y = 0; y < lm->sh; y++ )
4287 for( x = 0; x < lm->sw; x++ )
4290 cluster = SUPER_CLUSTER( x, y );
4291 origin = SUPER_ORIGIN( x, y );
4292 normal = SUPER_NORMAL( x, y );
4293 floodlight = SUPER_FLOODLIGHT( x, y );
4295 /* set default dirt */
4298 /* only look at mapped luxels */
4303 trace.cluster = *cluster;
4304 VectorCopy( origin, trace.origin );
4305 VectorCopy( normal, trace.normal );
4307 /* get floodlight */
4308 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4310 /* add floodlight */
4311 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4312 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4313 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4314 floodlight[3] += floodlightDirectionScale;
4318 /* testing no filtering */
4324 for( y = 0; y < lm->sh; y++ )
4326 for( x = 0; x < lm->sw; x++ )
4329 cluster = SUPER_CLUSTER( x, y );
4330 floodlight = SUPER_FLOODLIGHT(x, y );
4332 /* filter dirt by adjacency to unmapped luxels */
4333 average = *floodlight;
4335 for( sy = (y - 1); sy <= (y + 1); sy++ )
4337 if( sy < 0 || sy >= lm->sh )
4340 for( sx = (x - 1); sx <= (x + 1); sx++ )
4342 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4345 /* get neighboring luxel */
4346 cluster = SUPER_CLUSTER( sx, sy );
4347 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4348 if( *cluster < 0 || *floodlight2 <= 0.0f )
4352 average += *floodlight2;
4357 if( samples <= 0.0f )
4362 if( samples <= 0.0f )
4366 *floodlight = average / samples;
4372 void FloodLightRawLightmap( int rawLightmapNum )
4376 /* bail if this number exceeds the number of raw lightmaps */
4377 if( rawLightmapNum >= numRawLightmaps )
4380 lm = &rawLightmaps[ rawLightmapNum ];
4383 if (floodlighty && floodlightIntensity)
4384 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
4387 if (lm->floodlightIntensity)
4389 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4390 numSurfacesFloodlighten += 1;
4394 void FloodlightRawLightmaps()
4396 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4397 numSurfacesFloodlighten = 0;
4398 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4399 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4403 FloodLightIlluminate()
4404 illuminate floodlight into lightmap luxels
4407 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4409 float *luxel, *floodlight, *deluxel, *normal;
4412 int x, y, lightmapNum;
4414 /* walk lightmaps */
4415 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4418 if( lm->superLuxels[ lightmapNum ] == NULL )
4421 /* apply floodlight to each luxel */
4422 for( y = 0; y < lm->sh; y++ )
4424 for( x = 0; x < lm->sw; x++ )
4426 /* get floodlight */
4427 floodlight = SUPER_FLOODLIGHT( x, y );
4428 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4432 cluster = SUPER_CLUSTER( x, y );
4434 /* only process mapped luxels */
4438 /* get particulars */
4439 luxel = SUPER_LUXEL( lightmapNum, x, y );
4440 deluxel = SUPER_DELUXEL( x, y );
4442 /* add to lightmap */
4443 luxel[0]+=floodlight[0];
4444 luxel[1]+=floodlight[1];
4445 luxel[2]+=floodlight[2];
4447 if (luxel[3]==0) luxel[3]=1;
4449 /* add to deluxemap */
4450 if (deluxemap && floodlight[3] > 0)
4454 normal = SUPER_NORMAL( x, y );
4455 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4457 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4458 if(brightness < 0.00390625f)
4459 brightness = 0.00390625f;
4461 VectorScale( normal, brightness, lightvector );
4462 VectorAdd( deluxel, lightvector, deluxel );