aboutsummaryrefslogtreecommitdiff
path: root/mp/src/utils/vrad/leaf_ambient_lighting.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/utils/vrad/leaf_ambient_lighting.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/utils/vrad/leaf_ambient_lighting.cpp')
-rw-r--r--mp/src/utils/vrad/leaf_ambient_lighting.cpp1416
1 files changed, 708 insertions, 708 deletions
diff --git a/mp/src/utils/vrad/leaf_ambient_lighting.cpp b/mp/src/utils/vrad/leaf_ambient_lighting.cpp
index ea26c8c6..3836592e 100644
--- a/mp/src/utils/vrad/leaf_ambient_lighting.cpp
+++ b/mp/src/utils/vrad/leaf_ambient_lighting.cpp
@@ -1,708 +1,708 @@
-//========= 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-4f )
- {
- 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, int 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");
-}
-
+//========= 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-4f )
+ {
+ 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, int 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");
+}
+