//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
# include "vrad.h"
# include "leaf_ambient_lighting.h"
# include "bsplib.h"
# include "vraddetailprops.h"
# include "mathlib/anorms.h"
# include "pacifier.h"
# include "coordsize.h"
# include "vstdlib/random.h"
# include "bsptreedata.h"
# include "messbuf.h"
# include "vmpi.h"
# include "vmpi_distribute_work.h"
static TableVector g_BoxDirections [ 6 ] =
{
{ 1 , 0 , 0 } ,
{ - 1 , 0 , 0 } ,
{ 0 , 1 , 0 } ,
{ 0 , - 1 , 0 } ,
{ 0 , 0 , 1 } ,
{ 0 , 0 , - 1 } ,
} ;
static void ComputeAmbientFromSurface ( dface_t * surfID , dworldlight_t * pSkylight ,
Vector & radcolor )
{
if ( ! surfID )
return ;
texinfo_t * pTexInfo = & texinfo [ surfID - > texinfo ] ;
// If we hit the sky, use the sky ambient
if ( pTexInfo - > flags & SURF_SKY )
{
if ( pSkylight )
{
// add in sky ambient
VectorCopy ( pSkylight - > intensity , radcolor ) ;
}
}
else
{
Vector reflectivity = dtexdata [ pTexInfo - > texdata ] . reflectivity ;
VectorMultiply ( radcolor , reflectivity , radcolor ) ;
}
}
// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
float Engine_WorldLightAngle ( const dworldlight_t * wl , const Vector & lnormal , const Vector & snormal , const Vector & delta )
{
float dot , dot2 ;
Assert ( wl - > type = = emit_surface ) ;
dot = DotProduct ( snormal , delta ) ;
if ( dot < 0 )
return 0 ;
dot2 = - DotProduct ( delta , lnormal ) ;
if ( dot2 < = ON_EPSILON / 10 )
return 0 ; // behind light surface
return dot * dot2 ;
}
// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
float Engine_WorldLightDistanceFalloff ( const dworldlight_t * wl , const Vector & delta )
{
Assert ( wl - > type = = emit_surface ) ;
// Cull out stuff that's too far
if ( wl - > radius ! = 0 )
{
if ( DotProduct ( delta , delta ) > ( wl - > radius * wl - > radius ) )
return 0.0f ;
}
return InvRSquared ( delta ) ;
}
void AddEmitSurfaceLights ( const Vector & vStart , Vector lightBoxColor [ 6 ] )
{
fltx4 fractionVisible ;
FourVectors vStart4 , wlOrigin4 ;
vStart4 . DuplicateVector ( vStart ) ;
for ( int iLight = 0 ; iLight < * pNumworldlights ; iLight + + )
{
dworldlight_t * wl = & dworldlights [ iLight ] ;
// Should this light even go in the ambient cubes?
if ( ! ( wl - > flags & DWL_FLAGS_INAMBIENTCUBE ) )
continue ;
Assert ( wl - > type = = emit_surface ) ;
// Can this light see the point?
wlOrigin4 . DuplicateVector ( wl - > origin ) ;
TestLine ( vStart4 , wlOrigin4 , & fractionVisible ) ;
if ( ! TestSignSIMD ( CmpGtSIMD ( fractionVisible , Four_Zeros ) ) )
continue ;
// Add this light's contribution.
Vector vDelta = wl - > origin - vStart ;
float flDistanceScale = Engine_WorldLightDistanceFalloff ( wl , vDelta ) ;
Vector vDeltaNorm = vDelta ;
VectorNormalize ( vDeltaNorm ) ;
float flAngleScale = Engine_WorldLightAngle ( wl , wl - > normal , vDeltaNorm , vDeltaNorm ) ;
float ratio = flDistanceScale * flAngleScale * SubFloat ( fractionVisible , 0 ) ;
if ( ratio = = 0 )
continue ;
for ( int i = 0 ; i < 6 ; i + + )
{
float t = DotProduct ( g_BoxDirections [ i ] , vDeltaNorm ) ;
if ( t > 0 )
{
lightBoxColor [ i ] + = wl - > intensity * ( t * ratio ) ;
}
}
}
}
void ComputeAmbientFromSphericalSamples ( int iThread , const Vector & vStart , Vector lightBoxColor [ 6 ] )
{
// Figure out the color that rays hit when shot out from this position.
Vector radcolor [ NUMVERTEXNORMALS ] ;
float tanTheta = tan ( VERTEXNORMAL_CONE_INNER_ANGLE ) ;
for ( int i = 0 ; i < NUMVERTEXNORMALS ; i + + )
{
Vector vEnd = vStart + g_anorms [ i ] * ( COORD_EXTENT * 1.74 ) ;
// Now that we've got a ray, see what surface we've hit
Vector lightStyleColors [ MAX_LIGHTSTYLES ] ;
lightStyleColors [ 0 ] . Init ( ) ; // We only care about light style 0 here.
CalcRayAmbientLighting ( iThread , vStart , vEnd , tanTheta , lightStyleColors ) ;
radcolor [ i ] = lightStyleColors [ 0 ] ;
}
// accumulate samples into radiant box
for ( int j = 6 ; - - j > = 0 ; )
{
float t = 0 ;
lightBoxColor [ j ] . Init ( ) ;
for ( int i = 0 ; i < NUMVERTEXNORMALS ; i + + )
{
float c = DotProduct ( g_anorms [ i ] , g_BoxDirections [ j ] ) ;
if ( c > 0 )
{
t + = c ;
lightBoxColor [ j ] + = radcolor [ i ] * c ;
}
}
lightBoxColor [ j ] * = 1 / t ;
}
// Now add direct light from the emit_surface lights. These go in the ambient cube because
// there are a ton of them and they are often so dim that they get filtered out by r_worldlightmin.
AddEmitSurfaceLights ( vStart , lightBoxColor ) ;
}
bool IsLeafAmbientSurfaceLight ( dworldlight_t * wl )
{
static const float g_flWorldLightMinEmitSurface = 0.005f ;
static const float g_flWorldLightMinEmitSurfaceDistanceRatio = ( InvRSquared ( Vector ( 0 , 0 , 512 ) ) ) ;
if ( wl - > type ! = emit_surface )
return false ;
if ( wl - > style ! = 0 )
return false ;
float intensity = max ( wl - > intensity [ 0 ] , wl - > intensity [ 1 ] ) ;
intensity = max ( intensity , wl - > intensity [ 2 ] ) ;
return ( intensity * g_flWorldLightMinEmitSurfaceDistanceRatio ) < g_flWorldLightMinEmitSurface ;
}
class CLeafSampler
{
public :
CLeafSampler ( int iThread ) : m_iThread ( iThread ) { }
// Generate a random point in the leaf's bounding volume
// reject any points that aren't actually in the leaf
// do a couple of tracing heuristics to eliminate points that are inside detail brushes
// or underneath displacement surfaces in the leaf
// return once we have a valid point, use the center if one can't be computed quickly
void GenerateLeafSamplePosition ( int leafIndex , const CUtlVector < dplane_t > & leafPlanes , Vector & samplePosition )
{
dleaf_t * pLeaf = dleafs + leafIndex ;
float dx = pLeaf - > maxs [ 0 ] - pLeaf - > mins [ 0 ] ;
float dy = pLeaf - > maxs [ 1 ] - pLeaf - > mins [ 1 ] ;
float dz = pLeaf - > maxs [ 2 ] - pLeaf - > mins [ 2 ] ;
bool bValid = false ;
for ( int i = 0 ; i < 1000 & & ! bValid ; i + + )
{
samplePosition . x = pLeaf - > mins [ 0 ] + m_random . RandomFloat ( 0 , dx ) ;
samplePosition . y = pLeaf - > mins [ 1 ] + m_random . RandomFloat ( 0 , dy ) ;
samplePosition . z = pLeaf - > mins [ 2 ] + m_random . RandomFloat ( 0 , dz ) ;
bValid = true ;
for ( int j = leafPlanes . Count ( ) ; - - j > = 0 & & bValid ; )
{
float d = DotProduct ( leafPlanes [ j ] . normal , samplePosition ) - leafPlanes [ j ] . dist ;
if ( d < DIST_EPSILON )
{
// not inside the leaf, try again
bValid = false ;
break ;
}
}
if ( ! bValid )
continue ;
for ( int j = 0 ; j < 6 ; j + + )
{
Vector start = samplePosition ;
int axis = j % 3 ;
start [ axis ] = ( j < 3 ) ? pLeaf - > mins [ axis ] : pLeaf - > maxs [ axis ] ;
float t ;
Vector normal ;
CastRayInLeaf ( m_iThread , samplePosition , start , leafIndex , & t , & normal ) ;
if ( t = = 0.0f )
{
// inside a func_detail, try again.
bValid = false ;
break ;
}
if ( t ! = 1.0f )
{
Vector delta = start - samplePosition ;
if ( DotProduct ( delta , normal ) > 0 )
{
// hit backside of displacement, try again.
bValid = false ;
break ;
}
}
}
}
if ( ! bValid )
{
// didn't generate a valid sample point, just use the center of the leaf bbox
samplePosition = ( Vector ( pLeaf - > mins [ 0 ] , pLeaf - > mins [ 1 ] , pLeaf - > mins [ 2 ] ) + Vector ( pLeaf - > maxs [ 0 ] , pLeaf - > maxs [ 1 ] , pLeaf - > maxs [ 2 ] ) ) * 0.5f ;
}
}
private :
int m_iThread ;
CUniformRandomStream m_random ;
} ;
// gets a list of the planes pointing into a leaf
void GetLeafBoundaryPlanes ( CUtlVector < dplane_t > & list , int leafIndex )
{
list . RemoveAll ( ) ;
int nodeIndex = leafparents [ leafIndex ] ;
int child = - ( leafIndex + 1 ) ;
while ( nodeIndex > = 0 )
{
dnode_t * pNode = dnodes + nodeIndex ;
dplane_t * pNodePlane = dplanes + pNode - > planenum ;
if ( pNode - > children [ 0 ] = = child )
{
// front side
list . AddToTail ( * pNodePlane ) ;
}
else
{
// back side
int plane = list . AddToTail ( ) ;
list [ plane ] . dist = - pNodePlane - > dist ;
list [ plane ] . normal = - pNodePlane - > normal ;
list [ plane ] . type = pNodePlane - > type ;
}
child = nodeIndex ;
nodeIndex = nodeparents [ child ] ;
}
}
// this stores each sample of the ambient lighting
struct ambientsample_t
{
Vector pos ;
Vector cube [ 6 ] ;
} ;
// add the sample to the list. If we exceed the maximum number of samples, the worst sample will
// be discarded. This has the effect of converging on the best samples when enough are added.
void AddSampleToList ( CUtlVector < ambientsample_t > & list , const Vector & samplePosition , Vector * pCube )
{
const int MAX_SAMPLES = 16 ;
int index = list . AddToTail ( ) ;
list [ index ] . pos = samplePosition ;
for ( int i = 0 ; i < 6 ; i + + )
{
list [ index ] . cube [ i ] = pCube [ i ] ;
}
if ( list . Count ( ) < = MAX_SAMPLES )
return ;
int nearestNeighborIndex = 0 ;
float nearestNeighborDist = FLT_MAX ;
float nearestNeighborTotal = 0 ;
for ( int i = 0 ; i < list . Count ( ) ; i + + )
{
int closestIndex = 0 ;
float closestDist = FLT_MAX ;
float totalDC = 0 ;
for ( int j = 0 ; j < list . Count ( ) ; j + + )
{
if ( j = = i )
continue ;
float dist = ( list [ i ] . pos - list [ j ] . pos ) . Length ( ) ;
float maxDC = 0 ;
for ( int k = 0 ; k < 6 ; k + + )
{
// color delta is computed per-component, per cube side
for ( int s = 0 ; s < 3 ; s + + )
{
float dc = fabs ( list [ i ] . cube [ k ] [ s ] - list [ j ] . cube [ k ] [ s ] ) ;
maxDC = max ( maxDC , dc ) ;
}
totalDC + = maxDC ;
}
// need a measurable difference in color or we'll just rely on position
if ( maxDC < 1e-4 f )
{
maxDC = 0 ;
}
else if ( maxDC > 1.0f )
{
maxDC = 1.0f ;
}
// selection criteria is 10% distance, 90% color difference
// choose samples that fill the space (large distance from each other)
// and have largest color variation
float distanceFactor = 0.1f + ( maxDC * 0.9f ) ;
dist * = distanceFactor ;
// find the "closest" sample to this one
if ( dist < closestDist )
{
closestDist = dist ;
closestIndex = j ;
}
}
// the sample with the "closest" neighbor is rejected
if ( closestDist < nearestNeighborDist | | ( closestDist = = nearestNeighborDist & & totalDC < nearestNeighborTotal ) )
{
nearestNeighborDist = closestDist ;
nearestNeighborIndex = i ;
}
}
list . FastRemove ( nearestNeighborIndex ) ;
}
// max number of units in gamma space of per-side delta
int CubeDeltaGammaSpace ( Vector * pCube0 , Vector * pCube1 )
{
int maxDelta = 0 ;
// do this comparison in gamma space to try and get a perceptual basis for the compare
for ( int i = 0 ; i < 6 ; i + + )
{
for ( int j = 0 ; j < 3 ; j + + )
{
int val0 = LinearToScreenGamma ( pCube0 [ i ] [ j ] ) ;
int val1 = LinearToScreenGamma ( pCube1 [ i ] [ j ] ) ;
int delta = abs ( val0 - val1 ) ;
if ( delta > maxDelta )
maxDelta = delta ;
}
}
return maxDelta ;
}
// reconstruct the ambient lighting for a leaf at the given position in worldspace
// optionally skip one of the entries in the list
void Mod_LeafAmbientColorAtPos ( Vector * pOut , const Vector & pos , const CUtlVector < ambientsample_t > & list , int skipIndex )
{
for ( int i = 0 ; i < 6 ; i + + )
{
pOut [ i ] . Init ( ) ;
}
float totalFactor = 0 ;
for ( int i = 0 ; i < list . Count ( ) ; i + + )
{
if ( i = = skipIndex )
continue ;
// do an inverse squared distance weighted average of the samples to reconstruct
// the original function
float dist = ( list [ i ] . pos - pos ) . LengthSqr ( ) ;
float factor = 1.0f / ( dist + 1.0f ) ;
totalFactor + = factor ;
for ( int j = 0 ; j < 6 ; j + + )
{
pOut [ j ] + = list [ i ] . cube [ j ] * factor ;
}
}
for ( int i = 0 ; i < 6 ; i + + )
{
pOut [ i ] * = ( 1.0f / totalFactor ) ;
}
}
// this samples the lighting at each sample and removes any unnecessary samples
void CompressAmbientSampleList ( CUtlVector < ambientsample_t > & list )
{
Vector testCube [ 6 ] ;
for ( int i = 0 ; i < list . Count ( ) ; i + + )
{
if ( list . Count ( ) > 1 )
{
Mod_LeafAmbientColorAtPos ( testCube , list [ i ] . pos , list , i ) ;
if ( CubeDeltaGammaSpace ( testCube , list [ i ] . cube ) < 3 )
{
list . FastRemove ( i ) ;
i - - ;
}
}
}
}
// basically this is an intersection routine that returns a distance between the boxes
float AABBDistance ( const Vector & mins0 , const Vector & maxs0 , const Vector & mins1 , const Vector & maxs1 )
{
Vector delta ;
for ( int i = 0 ; i < 3 ; i + + )
{
float greatestMin = max ( mins0 [ i ] , mins1 [ i ] ) ;
float leastMax = min ( maxs0 [ i ] , maxs1 [ i ] ) ;
delta [ i ] = ( greatestMin < leastMax ) ? 0 : ( leastMax - greatestMin ) ;
}
return delta . Length ( ) ;
}
// build a list of leaves from a query
class CLeafList : public ISpatialLeafEnumerator
{
public :
virtual bool EnumerateLeaf ( int leaf , intp context )
{
m_list . AddToTail ( leaf ) ;
return true ;
}
CUtlVector < int > m_list ;
} ;
// conver short[3] to vector
static void LeafBounds ( int leafIndex , Vector & mins , Vector & maxs )
{
for ( int i = 0 ; i < 3 ; i + + )
{
mins [ i ] = dleafs [ leafIndex ] . mins [ i ] ;
maxs [ i ] = dleafs [ leafIndex ] . maxs [ i ] ;
}
}
// returns the index of the nearest leaf with ambient samples
int NearestNeighborWithLight ( int leafID )
{
Vector mins , maxs ;
LeafBounds ( leafID , mins , maxs ) ;
Vector size = maxs - mins ;
CLeafList leafList ;
ToolBSPTree ( ) - > EnumerateLeavesInBox ( mins - size , maxs + size , & leafList , 0 ) ;
float bestDist = FLT_MAX ;
int bestIndex = leafID ;
for ( int i = 0 ; i < leafList . m_list . Count ( ) ; i + + )
{
int testIndex = leafList . m_list [ i ] ;
if ( ! g_pLeafAmbientIndex - > Element ( testIndex ) . ambientSampleCount )
continue ;
Vector testMins , testMaxs ;
LeafBounds ( testIndex , testMins , testMaxs ) ;
float dist = AABBDistance ( mins , maxs , testMins , testMaxs ) ;
if ( dist < bestDist )
{
bestDist = dist ;
bestIndex = testIndex ;
}
}
return bestIndex ;
}
// maps a float to a byte fraction between min & max
static byte Fixed8Fraction ( float t , float tMin , float tMax )
{
if ( tMax < = tMin )
return 0 ;
float frac = RemapValClamped ( t , tMin , tMax , 0.0f , 255.0f ) ;
return byte ( frac + 0.5f ) ;
}
CUtlVector < CUtlVector < ambientsample_t > > g_LeafAmbientSamples ;
void ComputeAmbientForLeaf ( int iThread , int leafID , CUtlVector < ambientsample_t > & list )
{
CUtlVector < dplane_t > leafPlanes ;
CLeafSampler sampler ( iThread ) ;
GetLeafBoundaryPlanes ( leafPlanes , leafID ) ;
list . RemoveAll ( ) ;
// this heuristic tries to generate at least one sample per volume (chosen to be similar to the size of a player) in the space
int xSize = ( dleafs [ leafID ] . maxs [ 0 ] - dleafs [ leafID ] . mins [ 0 ] ) / 32 ;
int ySize = ( dleafs [ leafID ] . maxs [ 1 ] - dleafs [ leafID ] . mins [ 1 ] ) / 32 ;
int zSize = ( dleafs [ leafID ] . maxs [ 2 ] - dleafs [ leafID ] . mins [ 2 ] ) / 64 ;
xSize = max ( xSize , 1 ) ;
ySize = max ( xSize , 1 ) ;
zSize = max ( xSize , 1 ) ;
// generate update 128 candidate samples, always at least one sample
int volumeCount = xSize * ySize * zSize ;
if ( g_bFastAmbient )
{
// save compute time, only do one sample
volumeCount = 1 ;
}
int sampleCount = clamp ( volumeCount , 1 , 128 ) ;
if ( dleafs [ leafID ] . contents & CONTENTS_SOLID )
{
// don't generate any samples in solid leaves
// NOTE: We copy the nearest non-solid leaf sample pointers into this leaf at the end
return ;
}
Vector cube [ 6 ] ;
for ( int i = 0 ; i < sampleCount ; i + + )
{
// compute each candidate sample and add to the list
Vector samplePosition ;
sampler . GenerateLeafSamplePosition ( leafID , leafPlanes , samplePosition ) ;
ComputeAmbientFromSphericalSamples ( iThread , samplePosition , cube ) ;
// note this will remove the least valuable sample once the limit is reached
AddSampleToList ( list , samplePosition , cube ) ;
}
// remove any samples that can be reconstructed with the remaining data
CompressAmbientSampleList ( list ) ;
}
static void ThreadComputeLeafAmbient ( int iThread , void * pUserData )
{
CUtlVector < ambientsample_t > list ;
while ( 1 )
{
int leafID = GetThreadWork ( ) ;
if ( leafID = = - 1 )
break ;
list . RemoveAll ( ) ;
ComputeAmbientForLeaf ( iThread , leafID , list ) ;
// copy to the output array
g_LeafAmbientSamples [ leafID ] . SetCount ( list . Count ( ) ) ;
for ( int i = 0 ; i < list . Count ( ) ; i + + )
{
g_LeafAmbientSamples [ leafID ] . Element ( i ) = list . Element ( i ) ;
}
}
}
void VMPI_ProcessLeafAmbient ( int iThread , uint64 iLeaf , MessageBuffer * pBuf )
{
CUtlVector < ambientsample_t > list ;
ComputeAmbientForLeaf ( iThread , ( int ) iLeaf , list ) ;
VMPI_SetCurrentStage ( " EncodeLeafAmbientResults " ) ;
// Encode the results.
int nSamples = list . Count ( ) ;
pBuf - > write ( & nSamples , sizeof ( nSamples ) ) ;
if ( nSamples )
{
pBuf - > write ( list . Base ( ) , list . Count ( ) * sizeof ( ambientsample_t ) ) ;
}
}
//-----------------------------------------------------------------------------
// Called on the master when a worker finishes processing a static prop.
//-----------------------------------------------------------------------------
void VMPI_ReceiveLeafAmbientResults ( uint64 leafID , MessageBuffer * pBuf , int iWorker )
{
// Decode the results.
int nSamples ;
pBuf - > read ( & nSamples , sizeof ( nSamples ) ) ;
g_LeafAmbientSamples [ leafID ] . SetCount ( nSamples ) ;
if ( nSamples )
{
pBuf - > read ( g_LeafAmbientSamples [ leafID ] . Base ( ) , nSamples * sizeof ( ambientsample_t ) ) ;
}
}
void ComputePerLeafAmbientLighting ( )
{
// Figure out which lights should go in the per-leaf ambient cubes.
int nInAmbientCube = 0 ;
int nSurfaceLights = 0 ;
for ( int i = 0 ; i < * pNumworldlights ; i + + )
{
dworldlight_t * wl = & dworldlights [ i ] ;
if ( IsLeafAmbientSurfaceLight ( wl ) )
wl - > flags | = DWL_FLAGS_INAMBIENTCUBE ;
else
wl - > flags & = ~ DWL_FLAGS_INAMBIENTCUBE ;
if ( wl - > type = = emit_surface )
+ + nSurfaceLights ;
if ( wl - > flags & DWL_FLAGS_INAMBIENTCUBE )
+ + nInAmbientCube ;
}
Msg ( " %d of %d (%d%% of) surface lights went in leaf ambient cubes. \n " , nInAmbientCube , nSurfaceLights , nSurfaceLights ? ( ( nInAmbientCube * 100 ) / nSurfaceLights ) : 0 ) ;
g_LeafAmbientSamples . SetCount ( numleafs ) ;
if ( g_bUseMPI )
{
// Distribute the work among the workers.
VMPI_SetCurrentStage ( " ComputeLeafAmbientLighting " ) ;
DistributeWork ( numleafs , VMPI_DISTRIBUTEWORK_PACKETID , VMPI_ProcessLeafAmbient , VMPI_ReceiveLeafAmbientResults ) ;
}
else
{
RunThreadsOn ( numleafs , true , ThreadComputeLeafAmbient ) ;
}
// now write out the data
Msg ( " Writing leaf ambient... " ) ;
g_pLeafAmbientIndex - > RemoveAll ( ) ;
g_pLeafAmbientLighting - > RemoveAll ( ) ;
g_pLeafAmbientIndex - > SetCount ( numleafs ) ;
g_pLeafAmbientLighting - > EnsureCapacity ( numleafs * 4 ) ;
for ( int leafID = 0 ; leafID < numleafs ; leafID + + )
{
const CUtlVector < ambientsample_t > & list = g_LeafAmbientSamples [ leafID ] ;
g_pLeafAmbientIndex - > Element ( leafID ) . ambientSampleCount = list . Count ( ) ;
if ( ! list . Count ( ) )
{
g_pLeafAmbientIndex - > Element ( leafID ) . firstAmbientSample = 0 ;
}
else
{
g_pLeafAmbientIndex - > Element ( leafID ) . firstAmbientSample = g_pLeafAmbientLighting - > Count ( ) ;
// compute the samples in disk format. Encode the positions in 8-bits using leaf bounds fractions
for ( int i = 0 ; i < list . Count ( ) ; i + + )
{
int outIndex = g_pLeafAmbientLighting - > AddToTail ( ) ;
dleafambientlighting_t & light = g_pLeafAmbientLighting - > Element ( outIndex ) ;
light . x = Fixed8Fraction ( list [ i ] . pos . x , dleafs [ leafID ] . mins [ 0 ] , dleafs [ leafID ] . maxs [ 0 ] ) ;
light . y = Fixed8Fraction ( list [ i ] . pos . y , dleafs [ leafID ] . mins [ 1 ] , dleafs [ leafID ] . maxs [ 1 ] ) ;
light . z = Fixed8Fraction ( list [ i ] . pos . z , dleafs [ leafID ] . mins [ 2 ] , dleafs [ leafID ] . maxs [ 2 ] ) ;
light . pad = 0 ;
for ( int side = 0 ; side < 6 ; side + + )
{
VectorToColorRGBExp32 ( list [ i ] . cube [ side ] , light . cube . m_Color [ side ] ) ;
}
}
}
}
for ( int i = 0 ; i < numleafs ; i + + )
{
// UNDONE: Do this dynamically in the engine instead. This will allow us to sample across leaf
// boundaries always which should improve the quality of lighting in general
if ( g_pLeafAmbientIndex - > Element ( i ) . ambientSampleCount = = 0 )
{
if ( ! ( dleafs [ i ] . contents & CONTENTS_SOLID ) )
{
Msg ( " Bad leaf ambient for leaf %d \n " , i ) ;
}
int refLeaf = NearestNeighborWithLight ( i ) ;
g_pLeafAmbientIndex - > Element ( i ) . ambientSampleCount = 0 ;
g_pLeafAmbientIndex - > Element ( i ) . firstAmbientSample = refLeaf ;
}
}
Msg ( " done \n " ) ;
}