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( fastpoint && (light->flags != EMIT_AREA) )
3627 light->flags |= LIGHT_FAST_TEMP;
3628 if( light->si && light->si->noFast )
3629 light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3631 /* clear light envelope */
3632 light->envelope = 0;
3634 /* handle area lights */
3635 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3637 light->envelope = MAX_WORLD_COORD * 8.0f;
3639 /* check for fast mode */
3640 if( (light->flags & LIGHT_FAST) || (light->flags & LIGHT_FAST_TEMP) )
3642 /* ugly hack to calculate extent for area lights, but only done once */
3643 VectorScale( light->normal, -1.0f, dir );
3644 for( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3648 VectorMA( light->origin, radius, light->normal, origin );
3649 factor = PointToPolygonFormFactor( origin, dir, light->w );
3652 if( (factor * light->add) <= light->falloffTolerance )
3654 light->envelope = radius;
3660 intensity = light->photons; /* hopefully not used */
3665 intensity = light->photons;
3669 if( light->envelope <= 0.0f )
3671 /* solve distance for non-distance lights */
3672 if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3673 light->envelope = MAX_WORLD_COORD * 8.0f;
3675 else if( (light->flags & LIGHT_FAST) || (light->flags & LIGHT_FAST_TEMP) )
3677 /* solve distance for linear lights */
3678 if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3679 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3682 add = angle * light->photons * linearScale - (dist * light->fade);
3683 T = (light->photons * linearScale) - (dist * light->fade);
3684 T + (dist * light->fade) = (light->photons * linearScale);
3685 dist * light->fade = (light->photons * linearScale) - T;
3686 dist = ((light->photons * linearScale) - T) / light->fade;
3689 /* solve for inverse square falloff */
3691 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3694 add = light->photons / (dist * dist);
3695 T = light->photons / (dist * dist);
3696 T * (dist * dist) = light->photons;
3697 dist = sqrt( light->photons / T );
3702 /* solve distance for linear lights */
3703 if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3704 light->envelope = (intensity * linearScale) / light->fade;
3706 /* can't cull these */
3708 light->envelope = MAX_WORLD_COORD * 8.0f;
3712 /* chop radius against pvs */
3715 ClearBounds( mins, maxs );
3717 /* check all leaves */
3718 for( i = 0; i < numBSPLeafs; i++ )
3721 leaf = &bspLeafs[ i ];
3724 if( leaf->cluster < 0 )
3726 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3729 /* add this leafs bbox to the bounds */
3730 VectorCopy( leaf->mins, origin );
3731 AddPointToBounds( origin, mins, maxs );
3732 VectorCopy( leaf->maxs, origin );
3733 AddPointToBounds( origin, mins, maxs );
3736 /* test to see if bounds encompass light */
3737 for( i = 0; i < 3; i++ )
3739 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3741 //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3742 //% mins[ 0 ], mins[ 1 ], mins[ 2 ],
3743 //% maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3744 //% numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3745 AddPointToBounds( light->origin, mins, maxs );
3749 /* chop the bounds by a plane for area lights and spotlights */
3750 if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3751 ChopBounds( mins, maxs, light->origin, light->normal );
3754 VectorCopy( mins, light->mins );
3755 VectorCopy( maxs, light->maxs );
3757 /* reflect bounds around light origin */
3758 //% VectorMA( light->origin, -1.0f, origin, origin );
3759 VectorScale( light->origin, 2, origin );
3760 VectorSubtract( origin, maxs, origin );
3761 AddPointToBounds( origin, mins, maxs );
3762 //% VectorMA( light->origin, -1.0f, mins, origin );
3763 VectorScale( light->origin, 2, origin );
3764 VectorSubtract( origin, mins, origin );
3765 AddPointToBounds( origin, mins, maxs );
3767 /* calculate spherical bounds */
3768 VectorSubtract( maxs, light->origin, dir );
3769 radius = (float) VectorLength( dir );
3771 /* if this radius is smaller than the envelope, then set the envelope to it */
3772 if( radius < light->envelope )
3774 light->envelope = radius;
3775 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3778 //% Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3781 /* add grid/surface only check */
3784 if( !(light->flags & LIGHT_GRID) )
3785 light->envelope = 0.0f;
3789 if( !(light->flags & LIGHT_SURFACES) )
3790 light->envelope = 0.0f;
3795 if( light->cluster < 0 || light->envelope <= 0.0f )
3798 //% Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3800 /* delete the light */
3802 *owner = light->next;
3803 if( light->w != NULL )
3810 /* square envelope */
3811 light->envelope2 = (light->envelope * light->envelope);
3813 /* increment light count */
3816 /* set next light */
3817 owner = &((**owner).next);
3820 /* bucket sort lights by style */
3821 memset( buckets, 0, sizeof( buckets ) );
3823 for( light = lights; light != NULL; light = light2 )
3825 /* get next light */
3826 light2 = light->next;
3828 /* filter into correct bucket */
3829 light->next = buckets[ light->style ];
3830 buckets[ light->style ] = light;
3832 /* if any styled light is present, automatically set nocollapse */
3833 if( light->style != LS_NORMAL )
3837 /* filter back into light list */
3839 for( i = 255; i >= 0; i-- )
3842 for( light = buckets[ i ]; light != NULL; light = light2 )
3844 light2 = light->next;
3845 light->next = lights;
3850 /* emit some statistics */
3851 Sys_Printf( "%9d total lights\n", numLights );
3852 Sys_Printf( "%9d culled lights\n", numCulledLights );
3858 CreateTraceLightsForBounds()
3859 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3862 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3866 vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3867 float radius, dist, length;
3870 /* potential pre-setup */
3871 if( numLights == 0 )
3872 SetupEnvelopes( qfalse, fast );
3875 //% 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 ] );
3877 /* allocate the light list */
3878 trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3879 trace->numLights = 0;
3881 /* calculate spherical bounds */
3882 VectorAdd( mins, maxs, origin );
3883 VectorScale( origin, 0.5f, origin );
3884 VectorSubtract( maxs, origin, dir );
3885 radius = (float) VectorLength( dir );
3887 /* get length of normal vector */
3888 if( normal != NULL )
3889 length = VectorLength( normal );
3892 normal = nullVector;
3896 /* test each light and see if it reaches the sphere */
3897 /* note: the attenuation code MUST match LightingAtSample() */
3898 for( light = lights; light; light = light->next )
3900 /* check zero sized envelope */
3901 if( light->envelope <= 0 )
3903 lightsEnvelopeCulled++;
3908 if( !(light->flags & flags) )
3911 /* sunlight skips all this nonsense */
3912 if( light->type != EMIT_SUN )
3918 /* check against pvs cluster */
3919 if( numClusters > 0 && clusters != NULL )
3921 for( i = 0; i < numClusters; i++ )
3923 if( ClusterVisible( light->cluster, clusters[ i ] ) )
3928 if( i == numClusters )
3930 lightsClusterCulled++;
3935 /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3936 VectorSubtract( light->origin, origin, dir );
3937 dist = VectorLength( dir );
3938 dist -= light->envelope;
3942 lightsEnvelopeCulled++;
3946 /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3949 for( i = 0; i < 3; i++ )
3951 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3956 lightsBoundsCulled++;
3962 /* planar surfaces (except twosided surfaces) have a couple more checks */
3963 if( length > 0.0f && trace->twoSided == qfalse )
3965 /* lights coplanar with a surface won't light it */
3966 if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3968 lightsPlaneCulled++;
3972 /* check to see if light is behind the plane */
3973 if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3975 lightsPlaneCulled++;
3980 /* add this light */
3981 trace->lights[ trace->numLights++ ] = light;
3984 /* make last night null */
3985 trace->lights[ trace->numLights ] = NULL;
3990 void FreeTraceLights( trace_t *trace )
3992 if( trace->lights != NULL )
3993 free( trace->lights );
3999 CreateTraceLightsForSurface()
4000 creates a list of lights that can potentially affect a drawsurface
4003 void CreateTraceLightsForSurface( int num, trace_t *trace )
4006 vec3_t mins, maxs, normal;
4008 bspDrawSurface_t *ds;
4009 surfaceInfo_t *info;
4016 /* get drawsurface and info */
4017 ds = &bspDrawSurfaces[ num ];
4018 info = &surfaceInfos[ num ];
4020 /* get the mins/maxs for the dsurf */
4021 ClearBounds( mins, maxs );
4022 VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4023 for( i = 0; i < ds->numVerts; i++ )
4025 dv = &yDrawVerts[ ds->firstVert + i ];
4026 AddPointToBounds( dv->xyz, mins, maxs );
4027 if( !VectorCompare( dv->normal, normal ) )
4028 VectorClear( normal );
4031 /* create the lights for the bounding box */
4032 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4035 /////////////////////////////////////////////////////////////
4037 #define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
4038 #define FLOODLIGHT_NUM_ANGLE_STEPS 16
4039 #define FLOODLIGHT_NUM_ELEVATION_STEPS 4
4040 #define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
4042 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4043 static int numFloodVectors = 0;
4045 void SetupFloodLight( void )
4048 float angle, elevation, angleStep, elevationStep;
4050 double v1,v2,v3,v4,v5,v6;
4053 Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4055 /* calculate angular steps */
4056 angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4057 elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4061 for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4063 /* iterate elevation */
4064 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4066 floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4067 floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4068 floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4073 /* emit some statistics */
4074 Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4077 value = ValueForKey( &entities[ 0 ], "_floodlight" );
4079 if( value[ 0 ] != '\0' )
4082 v4=floodlightDistance;
4083 v5=floodlightIntensity;
4084 v6=floodlightDirectionScale;
4086 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6);
4088 floodlightRGB[0]=v1;
4089 floodlightRGB[1]=v2;
4090 floodlightRGB[2]=v3;
4092 if (VectorLength(floodlightRGB)==0)
4094 VectorSet(floodlightRGB,0.94,0.94,1.0);
4101 floodlightDistance=v4;
4102 floodlightIntensity=v5;
4103 floodlightDirectionScale=v6;
4105 floodlighty = qtrue;
4106 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4110 VectorSet(floodlightRGB,0.94,0.94,1.0);
4114 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat(floodlightRGB[0]);
4115 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat(floodlightRGB[1]);
4116 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat(floodlightRGB[2]);
4118 ColorNormalize(floodlightRGB,floodlightRGB);
4122 FloodLightForSample()
4123 calculates floodlight value for a given sample
4124 once again, kudos to the dirtmapping coder
4127 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
4133 float gatherLight, outLight;
4134 vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4142 if( trace == NULL || trace->cluster < 0 )
4147 dd = floodLightDistance;
4148 VectorCopy( trace->normal, normal );
4150 /* check if the normal is aligned to the world-up */
4151 if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
4153 if( normal[ 2 ] == 1.0f )
4155 VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4156 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4158 else if( normal[ 2 ] == -1.0f )
4160 VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4161 VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4166 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4167 CrossProduct( normal, worldUp, myRt );
4168 VectorNormalize( myRt, myRt );
4169 CrossProduct( myRt, normal, myUp );
4170 VectorNormalize( myUp, myUp );
4173 /* vortex: optimise floodLightLowQuality a bit */
4174 if ( floodLightLowQuality == qtrue )
4176 /* iterate through ordered vectors */
4177 for( i = 0; i < numFloodVectors; i++ )
4178 if (rand()%10 != 0 ) continue;
4182 /* iterate through ordered vectors */
4183 for( i = 0; i < numFloodVectors; i++ )
4187 /* transform vector into tangent space */
4188 direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4189 direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4190 direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4193 VectorMA( trace->origin, dd, direction, trace->end );
4195 //VectorMA( trace->origin, 1, direction, trace->origin );
4197 SetupTrace( trace );
4202 if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
4206 else if ( trace->opaque )
4208 VectorSubtract( trace->hit, trace->origin, displacement );
4209 d=VectorLength( displacement );
4211 // d=trace->distance;
4212 //if (d>256) gatherDirt+=1;
4214 if (contribution>1) contribution=1.0f;
4216 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4219 gatherLight+=contribution;
4224 if( gatherLight <= 0.0f )
4232 outLight=gatherLight;
4233 if( outLight > 1.0f )
4236 /* return to sender */
4241 FloodLightRawLightmap
4242 lighttracer style ambient occlusion light hack.
4243 Kudos to the dirtmapping author for most of this source.
4244 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4245 VorteX: fixed problems with deluxemapping
4248 // floodlight pass on a lightmap
4249 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4251 int i, x, y, *cluster;
4252 float *origin, *normal, *floodlight, floodLightAmount;
4253 surfaceInfo_t *info;
4256 // float samples, average, *floodlight2;
4258 memset(&trace,0,sizeof(trace_t));
4261 trace.testOcclusion = qtrue;
4262 trace.forceSunlight = qfalse;
4263 trace.twoSided = qtrue;
4264 trace.recvShadows = lm->recvShadows;
4265 trace.numSurfaces = lm->numLightSurfaces;
4266 trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4267 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4268 trace.testAll = qfalse;
4269 trace.distance = 1024;
4271 /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4272 //trace.twoSided = qfalse;
4273 for( i = 0; i < trace.numSurfaces; i++ )
4276 info = &surfaceInfos[ trace.surfaces[ i ] ];
4278 /* check twosidedness */
4279 if( info->si->twoSided )
4281 trace.twoSided = qtrue;
4286 /* gather floodlight */
4287 for( y = 0; y < lm->sh; y++ )
4289 for( x = 0; x < lm->sw; x++ )
4292 cluster = SUPER_CLUSTER( x, y );
4293 origin = SUPER_ORIGIN( x, y );
4294 normal = SUPER_NORMAL( x, y );
4295 floodlight = SUPER_FLOODLIGHT( x, y );
4297 /* set default dirt */
4300 /* only look at mapped luxels */
4305 trace.cluster = *cluster;
4306 VectorCopy( origin, trace.origin );
4307 VectorCopy( normal, trace.normal );
4309 /* get floodlight */
4310 floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4312 /* add floodlight */
4313 floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4314 floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4315 floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4316 floodlight[3] += floodlightDirectionScale;
4320 /* testing no filtering */
4326 for( y = 0; y < lm->sh; y++ )
4328 for( x = 0; x < lm->sw; x++ )
4331 cluster = SUPER_CLUSTER( x, y );
4332 floodlight = SUPER_FLOODLIGHT(x, y );
4334 /* filter dirt by adjacency to unmapped luxels */
4335 average = *floodlight;
4337 for( sy = (y - 1); sy <= (y + 1); sy++ )
4339 if( sy < 0 || sy >= lm->sh )
4342 for( sx = (x - 1); sx <= (x + 1); sx++ )
4344 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4347 /* get neighboring luxel */
4348 cluster = SUPER_CLUSTER( sx, sy );
4349 floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4350 if( *cluster < 0 || *floodlight2 <= 0.0f )
4354 average += *floodlight2;
4359 if( samples <= 0.0f )
4364 if( samples <= 0.0f )
4368 *floodlight = average / samples;
4374 void FloodLightRawLightmap( int rawLightmapNum )
4378 /* bail if this number exceeds the number of raw lightmaps */
4379 if( rawLightmapNum >= numRawLightmaps )
4382 lm = &rawLightmaps[ rawLightmapNum ];
4385 if (floodlighty && floodlightIntensity)
4386 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
4389 if (lm->floodlightIntensity)
4391 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4392 numSurfacesFloodlighten += 1;
4396 void FloodlightRawLightmaps()
4398 Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4399 numSurfacesFloodlighten = 0;
4400 RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4401 Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4405 FloodLightIlluminate()
4406 illuminate floodlight into lightmap luxels
4409 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4411 float *luxel, *floodlight, *deluxel, *normal;
4414 int x, y, lightmapNum;
4416 /* walk lightmaps */
4417 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4420 if( lm->superLuxels[ lightmapNum ] == NULL )
4423 /* apply floodlight to each luxel */
4424 for( y = 0; y < lm->sh; y++ )
4426 for( x = 0; x < lm->sw; x++ )
4428 /* get floodlight */
4429 floodlight = SUPER_FLOODLIGHT( x, y );
4430 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4434 cluster = SUPER_CLUSTER( x, y );
4436 /* only process mapped luxels */
4440 /* get particulars */
4441 luxel = SUPER_LUXEL( lightmapNum, x, y );
4442 deluxel = SUPER_DELUXEL( x, y );
4444 /* add to lightmap */
4445 luxel[0]+=floodlight[0];
4446 luxel[1]+=floodlight[1];
4447 luxel[2]+=floodlight[2];
4449 if (luxel[3]==0) luxel[3]=1;
4451 /* add to deluxemap */
4452 if (deluxemap && floodlight[3] > 0)
4456 normal = SUPER_NORMAL( x, y );
4457 brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4459 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4460 if(brightness < 0.00390625f)
4461 brightness = 0.00390625f;
4463 VectorScale( normal, brightness, lightvector );
4464 VectorAdd( deluxel, lightvector, deluxel );