diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/utils/vrad/incremental.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/utils/vrad/incremental.cpp')
| -rw-r--r-- | mp/src/utils/vrad/incremental.cpp | 1532 |
1 files changed, 766 insertions, 766 deletions
diff --git a/mp/src/utils/vrad/incremental.cpp b/mp/src/utils/vrad/incremental.cpp index 9dd877e0..ad46d04d 100644 --- a/mp/src/utils/vrad/incremental.cpp +++ b/mp/src/utils/vrad/incremental.cpp @@ -1,766 +1,766 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "incremental.h"
-#include "lightmap.h"
-
-
-
-static bool g_bFileError = false;
-
-
-// -------------------------------------------------------------------------------- //
-// Static helpers.
-// -------------------------------------------------------------------------------- //
-
-static bool CompareLights( dworldlight_t *a, dworldlight_t *b )
-{
- static float flEpsilon = 1e-7;
-
- bool a1 = VectorsAreEqual( a->origin, b->origin, flEpsilon );
- bool a2 = VectorsAreEqual( a->intensity, b->intensity, 1.1f ); // intensities are huge numbers
- bool a3 = VectorsAreEqual( a->normal, b->normal, flEpsilon );
- bool a4 = fabs( a->constant_attn - b->constant_attn ) < flEpsilon;
- bool a5 = fabs( a->linear_attn - b->linear_attn ) < flEpsilon;
- bool a6 = fabs( a->quadratic_attn - b->quadratic_attn ) < flEpsilon;
- bool a7 = fabs( float( a->flags - b->flags ) ) < flEpsilon;
- bool a8 = fabs( a->stopdot - b->stopdot ) < flEpsilon;
- bool a9 = fabs( a->stopdot2 - b->stopdot2 ) < flEpsilon;
- bool a10 = fabs( a->exponent - b->exponent ) < flEpsilon;
- bool a11 = fabs( a->radius - b->radius ) < flEpsilon;
-
- return a1 && a2 && a3 && a4 && a5 && a6 && a7 && a8 && a9 && a10 && a11;
-}
-
-
-long FileOpen( char const *pFilename, bool bRead )
-{
- g_bFileError = false;
- return (long)g_pFileSystem->Open( pFilename, bRead ? "rb" : "wb" );
-}
-
-
-void FileClose( long fp )
-{
- if( fp )
- g_pFileSystem->Close( (FILE*)fp );
-}
-
-
-// Returns true if there was an error reading from the file.
-bool FileError()
-{
- return g_bFileError;
-}
-
-static inline void FileRead( long fp, void *pOut, int size )
-{
- if( g_bFileError || g_pFileSystem->Read( pOut, size, (FileHandle_t)fp ) != size )
- {
- g_bFileError = true;
- memset( pOut, 0, size );
- }
-}
-
-
-template<class T>
-static inline void FileRead( long fp, T &out )
-{
- FileRead( fp, &out, sizeof(out) );
-}
-
-
-static inline void FileWrite( long fp, void const *pData, int size )
-{
- if( g_bFileError || g_pFileSystem->Write( pData, size, (FileHandle_t)fp ) != size )
- {
- g_bFileError = true;
- }
-}
-
-
-template<class T>
-static inline void FileWrite( long fp, T out )
-{
- FileWrite( fp, &out, sizeof(out) );
-}
-
-
-IIncremental* GetIncremental()
-{
- static CIncremental inc;
- return &inc;
-}
-
-
-// -------------------------------------------------------------------------------- //
-// CIncremental.
-// -------------------------------------------------------------------------------- //
-
-CIncremental::CIncremental()
-{
- m_TotalMemory = 0;
- m_pIncrementalFilename = NULL;
- m_pBSPFilename = NULL;
- m_bSuccessfulRun = false;
-}
-
-
-CIncremental::~CIncremental()
-{
-}
-
-
-bool CIncremental::Init( char const *pBSPFilename, char const *pIncrementalFilename )
-{
- m_pBSPFilename = pBSPFilename;
- m_pIncrementalFilename = pIncrementalFilename;
- return true;
-}
-
-
-bool CIncremental::PrepareForLighting()
-{
- if( !m_pBSPFilename )
- return false;
-
- // Clear the touched faces list.
- m_FacesTouched.SetSize( numfaces );
- memset( m_FacesTouched.Base(), 0, numfaces );
-
- // If we haven't done a complete successful run yet, then we either haven't
- // loaded the lights, or a run was aborted and our lights are half-done so we
- // should reload them.
- if( !m_bSuccessfulRun )
- LoadIncrementalFile();
-
- // unmatched = a list of the lights we have
- CUtlLinkedList<int,int> unmatched;
- for( int i=m_Lights.Head(); i != m_Lights.InvalidIndex(); i = m_Lights.Next(i) )
- unmatched.AddToTail( i );
-
- // Match the light lists and get rid of lights that we already have all the data for.
- directlight_t *pNext;
- directlight_t **pPrev = &activelights;
- for( directlight_t *dl=activelights; dl != NULL; dl = pNext )
- {
- pNext = dl->next;
-
- //float flClosest = 3000000000;
- //CIncLight *pClosest = 0;
-
- // Look for this light in our light list.
- int iNextUnmatched, iUnmatched;
- for( iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = iNextUnmatched )
- {
- iNextUnmatched = unmatched.Next( iUnmatched );
-
- CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
-
- //float flTest = (pLight->m_Light.origin - dl->light.origin).Length();
- //if( flTest < flClosest )
- //{
- // flClosest = flTest;
- // pClosest = pLight;
- //}
-
- if( CompareLights( &dl->light, &pLight->m_Light ) )
- {
- unmatched.Remove( iUnmatched );
-
- // Ok, we have this light's data already, yay!
- // Get rid of it from the active light list.
- *pPrev = dl->next;
- free( dl );
- dl = 0;
- break;
- }
- }
-
- //bool bTest=false;
- //if(bTest)
- // CompareLights( &dl->light, &pClosest->m_Light );
-
- if( iUnmatched == unmatched.InvalidIndex() )
- pPrev = &dl->next;
- }
-
- // Remove any of our lights that were unmatched.
- for( int iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = unmatched.Next( iUnmatched ) )
- {
- CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
-
- // First tag faces that it touched so they get recomposited.
- for( unsigned short iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
- {
- m_FacesTouched[ pLight->m_LightFaces[iFace]->m_FaceIndex ] = 1;
- }
-
- delete pLight;
- m_Lights.Remove( unmatched[iUnmatched] );
- }
-
- // Now add a light structure for each new light.
- AddLightsForActiveLights();
-
- return true;
-}
-
-
-bool CIncremental::ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader )
-{
- int version;
- FileRead( fp, version );
- if( version != INCREMENTALFILE_VERSION )
- return false;
-
- int nFaces;
- FileRead( fp, nFaces );
-
- pHeader->m_FaceLightmapSizes.SetSize( nFaces );
- FileRead( fp, pHeader->m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
-
- return !FileError();
-}
-
-
-bool CIncremental::WriteIncrementalHeader( long fp )
-{
- int version = INCREMENTALFILE_VERSION;
- FileWrite( fp, version );
-
- int nFaces = numfaces;
- FileWrite( fp, nFaces );
-
- CIncrementalHeader hdr;
- hdr.m_FaceLightmapSizes.SetSize( nFaces );
-
- for( int i=0; i < nFaces; i++ )
- {
- hdr.m_FaceLightmapSizes[i].m_Width = g_pFaces[i].m_LightmapTextureSizeInLuxels[0];
- hdr.m_FaceLightmapSizes[i].m_Height = g_pFaces[i].m_LightmapTextureSizeInLuxels[1];
- }
-
- FileWrite( fp, hdr.m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
-
- return !FileError();
-}
-
-
-bool CIncremental::IsIncrementalFileValid()
-{
- long fp = FileOpen( m_pIncrementalFilename, true );
- if( !fp )
- return false;
-
- bool bValid = false;
- CIncrementalHeader hdr;
- if( ReadIncrementalHeader( fp, &hdr ) )
- {
- // If the number of faces is the same and their lightmap sizes are the same,
- // then this file is considered a legitimate incremental file.
- if( hdr.m_FaceLightmapSizes.Count() == numfaces )
- {
- int i;
- for( i=0; i < numfaces; i++ )
- {
- if( hdr.m_FaceLightmapSizes[i].m_Width != g_pFaces[i].m_LightmapTextureSizeInLuxels[0] ||
- hdr.m_FaceLightmapSizes[i].m_Height != g_pFaces[i].m_LightmapTextureSizeInLuxels[1] )
- {
- break;
- }
- }
-
- // Were all faces valid?
- if( i == numfaces )
- bValid = true;
- }
- }
-
- FileClose( fp );
- return bValid && !FileError();
-}
-
-
-void CIncremental::AddLightToFace(
- IncrementalLightID lightID,
- int iFace,
- int iSample,
- int lmSize,
- float dot,
- int iThread )
-{
- // If we're not being used, don't do anything.
- if( !m_pIncrementalFilename )
- return;
-
- CIncLight *pLight = m_Lights[lightID];
-
- // Check for the 99.99% case in which the face already exists.
- CLightFace *pFace;
- if( pLight->m_pCachedFaces[iThread] &&
- pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
- {
- pFace = pLight->m_pCachedFaces[iThread];
- }
- else
- {
- bool bNew;
-
- EnterCriticalSection( &pLight->m_CS );
- pFace = pLight->FindOrCreateLightFace( iFace, lmSize, &bNew );
- LeaveCriticalSection( &pLight->m_CS );
-
- pLight->m_pCachedFaces[iThread] = pFace;
-
- if( bNew )
- m_TotalMemory += pFace->m_LightValues.Count() * sizeof( pFace->m_LightValues[0] );
- }
-
- // Add this into the light's data.
- pFace->m_LightValues[iSample].m_Dot = dot;
-}
-
-
-unsigned short DecodeCharOrShort( CUtlBuffer *pIn )
-{
- unsigned short val = pIn->GetUnsignedChar();
- if( val & 0x80 )
- {
- val = ((val & 0x7F) << 8) | pIn->GetUnsignedChar();
- }
-
- return val;
-}
-
-
-void EncodeCharOrShort( CUtlBuffer *pBuf, unsigned short val )
-{
- if( (val & 0xFF80) == 0 )
- {
- pBuf->PutUnsignedChar( (unsigned char)val );
- }
- else
- {
- if( val > 32767 )
- val = 32767;
-
- pBuf->PutUnsignedChar( (val >> 8) | 0x80 );
- pBuf->PutUnsignedChar( val & 0xFF );
- }
-}
-
-
-void DecompressLightData( CUtlBuffer *pIn, CUtlVector<CLightValue> *pOut )
-{
- int iOut = 0;
- while( pIn->TellGet() < pIn->TellPut() )
- {
- unsigned char runLength = pIn->GetUnsignedChar();
- unsigned short usVal = DecodeCharOrShort( pIn );
-
- while( runLength > 0 )
- {
- --runLength;
-
- pOut->Element(iOut).m_Dot = usVal;
- ++iOut;
- }
- }
-}
-
-#ifdef _WIN32
-#pragma warning (disable:4701)
-#endif
-
-void CompressLightData(
- CLightValue const *pValues,
- int nValues,
- CUtlBuffer *pBuf )
-{
- unsigned char runLength=0;
- unsigned short flLastValue;
-
- for( int i=0; i < nValues; i++ )
- {
- unsigned short flCurValue = (unsigned short)pValues[i].m_Dot;
-
- if( i == 0 )
- {
- flLastValue = flCurValue;
- runLength = 1;
- }
- else if( flCurValue == flLastValue && runLength < 255 )
- {
- ++runLength;
- }
- else
- {
- pBuf->PutUnsignedChar( runLength );
- EncodeCharOrShort( pBuf, flLastValue );
-
- flLastValue = flCurValue;
- runLength = 1;
- }
- }
-
- // Write the end..
- if( runLength )
- {
- pBuf->PutUnsignedChar( runLength );
- EncodeCharOrShort( pBuf, flLastValue );
- }
-}
-
-#ifdef _WIN32
-#pragma warning (default:4701)
-#endif
-
-void MultiplyValues( CUtlVector<CLightValue> &values, float scale )
-{
- for( int i=0; i < values.Count(); i++ )
- values[i].m_Dot *= scale;
-}
-
-
-void CIncremental::FinishFace(
- IncrementalLightID lightID,
- int iFace,
- int iThread )
-{
- CIncLight *pLight = m_Lights[lightID];
-
- // Check for the 99.99% case in which the face already exists.
- CLightFace *pFace;
- if( pLight->m_pCachedFaces[iThread] && pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
- {
- pFace = pLight->m_pCachedFaces[iThread];
-
- // Compress the data.
- MultiplyValues( pFace->m_LightValues, pLight->m_flMaxIntensity );
-
- pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
- CompressLightData(
- pFace->m_LightValues.Base(),
- pFace->m_LightValues.Count(),
- &pFace->m_CompressedData );
-
-#if 0
- // test decompression
- CUtlVector<CLightValue> test;
- test.SetSize( 2048 );
- pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
- DecompressLightData( &pFace->m_CompressedData, &test );
-#endif
-
- if( pFace->m_CompressedData.TellPut() == 0 )
- {
- // No contribution.. delete this face from the light.
- EnterCriticalSection( &pLight->m_CS );
- pLight->m_LightFaces.Remove( pFace->m_LightFacesIndex );
- delete pFace;
- LeaveCriticalSection( &pLight->m_CS );
- }
- else
- {
- // Discard the uncompressed data.
- pFace->m_LightValues.Purge();
- m_FacesTouched[ pFace->m_FaceIndex ] = 1;
- }
- }
-}
-
-
-bool CIncremental::Finalize()
-{
- // If we're not being used, don't do anything.
- if( !m_pIncrementalFilename || !m_pBSPFilename )
- return false;
-
- CUtlVector<CFaceLightList> faceLights;
- LinkLightsToFaces( faceLights );
-
- Vector faceLight[(MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2)];
- CUtlVector<CLightValue> faceLightValues;
- faceLightValues.SetSize( (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) );
-
- // Only update the faces we've touched.
- for( int facenum = 0; facenum < numfaces; facenum++ )
- {
- if( !m_FacesTouched[facenum] || !faceLights[facenum].Count() )
- continue;
-
- int w = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[0]+1;
- int h = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[1]+1;
- int nLuxels = w * h;
- assert( nLuxels <= sizeof(faceLight) / sizeof(faceLight[0]) );
-
- // Clear the lighting for this face.
- memset( faceLight, 0, nLuxels * sizeof(Vector) );
-
- // Composite all the light contributions.
- for( int iFace=0; iFace < faceLights[facenum].Count(); iFace++ )
- {
- CLightFace *pFace = faceLights[facenum][iFace];
-
- pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
- DecompressLightData( &pFace->m_CompressedData, &faceLightValues );
-
- for( int iSample=0; iSample < nLuxels; iSample++ )
- {
- float flDot = faceLightValues[iSample].m_Dot;
- if( flDot )
- {
- VectorMA(
- faceLight[iSample],
- flDot / pFace->m_pLight->m_flMaxIntensity,
- pFace->m_pLight->m_Light.intensity,
- faceLight[iSample] );
- }
- }
- }
-
- // Convert to the floating-point representation in the BSP file.
- Vector *pSrc = faceLight;
- unsigned char *pDest = &(*pdlightdata)[ g_pFaces[facenum].lightofs ];
-
- for( int iSample=0; iSample < nLuxels; iSample++ )
- {
- VectorToColorRGBExp32( *pSrc, *( ColorRGBExp32 *)pDest );
- pDest += 4;
- pSrc++;
- }
- }
-
- m_bSuccessfulRun = true;
- return true;
-}
-
-
-void CIncremental::GetFacesTouched( CUtlVector<unsigned char> &touched )
-{
- touched.CopyArray( m_FacesTouched.Base(), m_FacesTouched.Count() );
-}
-
-
-bool CIncremental::Serialize()
-{
- if( !SaveIncrementalFile() )
- return false;
-
- WriteBSPFile( (char*)m_pBSPFilename );
- return true;
-}
-
-
-void CIncremental::Term()
-{
- m_Lights.PurgeAndDeleteElements();
- m_TotalMemory = 0;
-}
-
-
-void CIncremental::AddLightsForActiveLights()
-{
- // Create our lights.
- for( directlight_t *dl=activelights; dl != NULL; dl = dl->next )
- {
- CIncLight *pLight = new CIncLight;
- dl->m_IncrementalID = m_Lights.AddToTail( pLight );
-
- // Copy the light information.
- pLight->m_Light = dl->light;
- pLight->m_flMaxIntensity = max( dl->light.intensity[0], max( dl->light.intensity[1], dl->light.intensity[2] ) );
- }
-}
-
-
-bool CIncremental::LoadIncrementalFile()
-{
- Term();
-
- if( !IsIncrementalFileValid() )
- return false;
-
- long fp = FileOpen( m_pIncrementalFilename, true );
- if( !fp )
- return false;
-
- // Read the header.
- CIncrementalHeader hdr;
- if( !ReadIncrementalHeader( fp, &hdr ) )
- {
- FileClose( fp );
- return false;
- }
-
-
- // Read the lights.
- int nLights;
- FileRead( fp, nLights );
- for( int iLight=0; iLight < nLights; iLight++ )
- {
- CIncLight *pLight = new CIncLight;
- m_Lights.AddToTail( pLight );
-
- FileRead( fp, pLight->m_Light );
- pLight->m_flMaxIntensity =
- max( pLight->m_Light.intensity.x,
- max( pLight->m_Light.intensity.y, pLight->m_Light.intensity.z ) );
-
- int nFaces;
- FileRead( fp, nFaces );
- assert( nFaces < 70000 );
-
- for( int iFace=0; iFace < nFaces; iFace++ )
- {
- CLightFace *pFace = new CLightFace;
- pLight->m_LightFaces.AddToTail( pFace );
-
- pFace->m_pLight = pLight;
- FileRead( fp, pFace->m_FaceIndex );
-
- int dataSize;
- FileRead( fp, dataSize );
-
- pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
- while( dataSize )
- {
- --dataSize;
-
- unsigned char ucData;
- FileRead( fp, ucData );
-
- pFace->m_CompressedData.PutUnsignedChar( ucData );
- }
- }
- }
-
-
- FileClose( fp );
- return !FileError();
-}
-
-
-bool CIncremental::SaveIncrementalFile()
-{
- long fp = FileOpen( m_pIncrementalFilename, false );
- if( !fp )
- return false;
-
- if( !WriteIncrementalHeader( fp ) )
- {
- FileClose( fp );
- return false;
- }
-
- // Write the lights.
- int nLights = m_Lights.Count();
- FileWrite( fp, nLights );
- for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
- {
- CIncLight *pLight = m_Lights[iLight];
-
- FileWrite( fp, pLight->m_Light );
-
- int nFaces = pLight->m_LightFaces.Count();
- FileWrite( fp, nFaces );
- for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
- {
- CLightFace *pFace = pLight->m_LightFaces[iFace];
-
- FileWrite( fp, pFace->m_FaceIndex );
-
- int dataSize = pFace->m_CompressedData.TellPut();
- FileWrite( fp, dataSize );
-
- pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
- while( dataSize )
- {
- --dataSize;
- FileWrite( fp, pFace->m_CompressedData.GetUnsignedChar() );
- }
- }
- }
-
-
- FileClose( fp );
- return !FileError();
-}
-
-
-void CIncremental::LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights )
-{
- faceLights.SetSize( numfaces );
-
- for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
- {
- CIncLight *pLight = m_Lights[iLight];
-
- for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
- {
- CLightFace *pFace = pLight->m_LightFaces[iFace];
-
- if( m_FacesTouched[pFace->m_FaceIndex] )
- faceLights[ pFace->m_FaceIndex ].AddToTail( pFace );
- }
- }
-}
-
-
-// ------------------------------------------------------------------ //
-// CIncLight
-// ------------------------------------------------------------------ //
-
-CIncLight::CIncLight()
-{
- memset( m_pCachedFaces, 0, sizeof(m_pCachedFaces) );
- InitializeCriticalSection( &m_CS );
-}
-
-
-CIncLight::~CIncLight()
-{
- m_LightFaces.PurgeAndDeleteElements();
- DeleteCriticalSection( &m_CS );
-}
-
-
-CLightFace* CIncLight::FindOrCreateLightFace( int iFace, int lmSize, bool *bNew )
-{
- if( bNew )
- *bNew = false;
-
-
- // Look for it.
- for( int i=m_LightFaces.Head(); i != m_LightFaces.InvalidIndex(); i=m_LightFaces.Next(i) )
- {
- CLightFace *pFace = m_LightFaces[i];
-
- if( pFace->m_FaceIndex == iFace )
- {
- assert( pFace->m_LightValues.Count() == lmSize );
- return pFace;
- }
- }
-
- // Ok, create one.
- CLightFace *pFace = new CLightFace;
- pFace->m_LightFacesIndex = m_LightFaces.AddToTail( pFace );
- pFace->m_pLight = this;
-
- pFace->m_FaceIndex = iFace;
- pFace->m_LightValues.SetSize( lmSize );
- memset( pFace->m_LightValues.Base(), 0, sizeof( CLightValue ) * lmSize );
-
- if( bNew )
- *bNew = true;
-
- return pFace;
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "incremental.h" +#include "lightmap.h" + + + +static bool g_bFileError = false; + + +// -------------------------------------------------------------------------------- // +// Static helpers. +// -------------------------------------------------------------------------------- // + +static bool CompareLights( dworldlight_t *a, dworldlight_t *b ) +{ + static float flEpsilon = 1e-7; + + bool a1 = VectorsAreEqual( a->origin, b->origin, flEpsilon ); + bool a2 = VectorsAreEqual( a->intensity, b->intensity, 1.1f ); // intensities are huge numbers + bool a3 = VectorsAreEqual( a->normal, b->normal, flEpsilon ); + bool a4 = fabs( a->constant_attn - b->constant_attn ) < flEpsilon; + bool a5 = fabs( a->linear_attn - b->linear_attn ) < flEpsilon; + bool a6 = fabs( a->quadratic_attn - b->quadratic_attn ) < flEpsilon; + bool a7 = fabs( float( a->flags - b->flags ) ) < flEpsilon; + bool a8 = fabs( a->stopdot - b->stopdot ) < flEpsilon; + bool a9 = fabs( a->stopdot2 - b->stopdot2 ) < flEpsilon; + bool a10 = fabs( a->exponent - b->exponent ) < flEpsilon; + bool a11 = fabs( a->radius - b->radius ) < flEpsilon; + + return a1 && a2 && a3 && a4 && a5 && a6 && a7 && a8 && a9 && a10 && a11; +} + + +long FileOpen( char const *pFilename, bool bRead ) +{ + g_bFileError = false; + return (long)g_pFileSystem->Open( pFilename, bRead ? "rb" : "wb" ); +} + + +void FileClose( long fp ) +{ + if( fp ) + g_pFileSystem->Close( (FILE*)fp ); +} + + +// Returns true if there was an error reading from the file. +bool FileError() +{ + return g_bFileError; +} + +static inline void FileRead( long fp, void *pOut, int size ) +{ + if( g_bFileError || g_pFileSystem->Read( pOut, size, (FileHandle_t)fp ) != size ) + { + g_bFileError = true; + memset( pOut, 0, size ); + } +} + + +template<class T> +static inline void FileRead( long fp, T &out ) +{ + FileRead( fp, &out, sizeof(out) ); +} + + +static inline void FileWrite( long fp, void const *pData, int size ) +{ + if( g_bFileError || g_pFileSystem->Write( pData, size, (FileHandle_t)fp ) != size ) + { + g_bFileError = true; + } +} + + +template<class T> +static inline void FileWrite( long fp, T out ) +{ + FileWrite( fp, &out, sizeof(out) ); +} + + +IIncremental* GetIncremental() +{ + static CIncremental inc; + return &inc; +} + + +// -------------------------------------------------------------------------------- // +// CIncremental. +// -------------------------------------------------------------------------------- // + +CIncremental::CIncremental() +{ + m_TotalMemory = 0; + m_pIncrementalFilename = NULL; + m_pBSPFilename = NULL; + m_bSuccessfulRun = false; +} + + +CIncremental::~CIncremental() +{ +} + + +bool CIncremental::Init( char const *pBSPFilename, char const *pIncrementalFilename ) +{ + m_pBSPFilename = pBSPFilename; + m_pIncrementalFilename = pIncrementalFilename; + return true; +} + + +bool CIncremental::PrepareForLighting() +{ + if( !m_pBSPFilename ) + return false; + + // Clear the touched faces list. + m_FacesTouched.SetSize( numfaces ); + memset( m_FacesTouched.Base(), 0, numfaces ); + + // If we haven't done a complete successful run yet, then we either haven't + // loaded the lights, or a run was aborted and our lights are half-done so we + // should reload them. + if( !m_bSuccessfulRun ) + LoadIncrementalFile(); + + // unmatched = a list of the lights we have + CUtlLinkedList<int,int> unmatched; + for( int i=m_Lights.Head(); i != m_Lights.InvalidIndex(); i = m_Lights.Next(i) ) + unmatched.AddToTail( i ); + + // Match the light lists and get rid of lights that we already have all the data for. + directlight_t *pNext; + directlight_t **pPrev = &activelights; + for( directlight_t *dl=activelights; dl != NULL; dl = pNext ) + { + pNext = dl->next; + + //float flClosest = 3000000000; + //CIncLight *pClosest = 0; + + // Look for this light in our light list. + int iNextUnmatched, iUnmatched; + for( iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = iNextUnmatched ) + { + iNextUnmatched = unmatched.Next( iUnmatched ); + + CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ]; + + //float flTest = (pLight->m_Light.origin - dl->light.origin).Length(); + //if( flTest < flClosest ) + //{ + // flClosest = flTest; + // pClosest = pLight; + //} + + if( CompareLights( &dl->light, &pLight->m_Light ) ) + { + unmatched.Remove( iUnmatched ); + + // Ok, we have this light's data already, yay! + // Get rid of it from the active light list. + *pPrev = dl->next; + free( dl ); + dl = 0; + break; + } + } + + //bool bTest=false; + //if(bTest) + // CompareLights( &dl->light, &pClosest->m_Light ); + + if( iUnmatched == unmatched.InvalidIndex() ) + pPrev = &dl->next; + } + + // Remove any of our lights that were unmatched. + for( int iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = unmatched.Next( iUnmatched ) ) + { + CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ]; + + // First tag faces that it touched so they get recomposited. + for( unsigned short iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) ) + { + m_FacesTouched[ pLight->m_LightFaces[iFace]->m_FaceIndex ] = 1; + } + + delete pLight; + m_Lights.Remove( unmatched[iUnmatched] ); + } + + // Now add a light structure for each new light. + AddLightsForActiveLights(); + + return true; +} + + +bool CIncremental::ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader ) +{ + int version; + FileRead( fp, version ); + if( version != INCREMENTALFILE_VERSION ) + return false; + + int nFaces; + FileRead( fp, nFaces ); + + pHeader->m_FaceLightmapSizes.SetSize( nFaces ); + FileRead( fp, pHeader->m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces ); + + return !FileError(); +} + + +bool CIncremental::WriteIncrementalHeader( long fp ) +{ + int version = INCREMENTALFILE_VERSION; + FileWrite( fp, version ); + + int nFaces = numfaces; + FileWrite( fp, nFaces ); + + CIncrementalHeader hdr; + hdr.m_FaceLightmapSizes.SetSize( nFaces ); + + for( int i=0; i < nFaces; i++ ) + { + hdr.m_FaceLightmapSizes[i].m_Width = g_pFaces[i].m_LightmapTextureSizeInLuxels[0]; + hdr.m_FaceLightmapSizes[i].m_Height = g_pFaces[i].m_LightmapTextureSizeInLuxels[1]; + } + + FileWrite( fp, hdr.m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces ); + + return !FileError(); +} + + +bool CIncremental::IsIncrementalFileValid() +{ + long fp = FileOpen( m_pIncrementalFilename, true ); + if( !fp ) + return false; + + bool bValid = false; + CIncrementalHeader hdr; + if( ReadIncrementalHeader( fp, &hdr ) ) + { + // If the number of faces is the same and their lightmap sizes are the same, + // then this file is considered a legitimate incremental file. + if( hdr.m_FaceLightmapSizes.Count() == numfaces ) + { + int i; + for( i=0; i < numfaces; i++ ) + { + if( hdr.m_FaceLightmapSizes[i].m_Width != g_pFaces[i].m_LightmapTextureSizeInLuxels[0] || + hdr.m_FaceLightmapSizes[i].m_Height != g_pFaces[i].m_LightmapTextureSizeInLuxels[1] ) + { + break; + } + } + + // Were all faces valid? + if( i == numfaces ) + bValid = true; + } + } + + FileClose( fp ); + return bValid && !FileError(); +} + + +void CIncremental::AddLightToFace( + IncrementalLightID lightID, + int iFace, + int iSample, + int lmSize, + float dot, + int iThread ) +{ + // If we're not being used, don't do anything. + if( !m_pIncrementalFilename ) + return; + + CIncLight *pLight = m_Lights[lightID]; + + // Check for the 99.99% case in which the face already exists. + CLightFace *pFace; + if( pLight->m_pCachedFaces[iThread] && + pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace ) + { + pFace = pLight->m_pCachedFaces[iThread]; + } + else + { + bool bNew; + + EnterCriticalSection( &pLight->m_CS ); + pFace = pLight->FindOrCreateLightFace( iFace, lmSize, &bNew ); + LeaveCriticalSection( &pLight->m_CS ); + + pLight->m_pCachedFaces[iThread] = pFace; + + if( bNew ) + m_TotalMemory += pFace->m_LightValues.Count() * sizeof( pFace->m_LightValues[0] ); + } + + // Add this into the light's data. + pFace->m_LightValues[iSample].m_Dot = dot; +} + + +unsigned short DecodeCharOrShort( CUtlBuffer *pIn ) +{ + unsigned short val = pIn->GetUnsignedChar(); + if( val & 0x80 ) + { + val = ((val & 0x7F) << 8) | pIn->GetUnsignedChar(); + } + + return val; +} + + +void EncodeCharOrShort( CUtlBuffer *pBuf, unsigned short val ) +{ + if( (val & 0xFF80) == 0 ) + { + pBuf->PutUnsignedChar( (unsigned char)val ); + } + else + { + if( val > 32767 ) + val = 32767; + + pBuf->PutUnsignedChar( (val >> 8) | 0x80 ); + pBuf->PutUnsignedChar( val & 0xFF ); + } +} + + +void DecompressLightData( CUtlBuffer *pIn, CUtlVector<CLightValue> *pOut ) +{ + int iOut = 0; + while( pIn->TellGet() < pIn->TellPut() ) + { + unsigned char runLength = pIn->GetUnsignedChar(); + unsigned short usVal = DecodeCharOrShort( pIn ); + + while( runLength > 0 ) + { + --runLength; + + pOut->Element(iOut).m_Dot = usVal; + ++iOut; + } + } +} + +#ifdef _WIN32 +#pragma warning (disable:4701) +#endif + +void CompressLightData( + CLightValue const *pValues, + int nValues, + CUtlBuffer *pBuf ) +{ + unsigned char runLength=0; + unsigned short flLastValue; + + for( int i=0; i < nValues; i++ ) + { + unsigned short flCurValue = (unsigned short)pValues[i].m_Dot; + + if( i == 0 ) + { + flLastValue = flCurValue; + runLength = 1; + } + else if( flCurValue == flLastValue && runLength < 255 ) + { + ++runLength; + } + else + { + pBuf->PutUnsignedChar( runLength ); + EncodeCharOrShort( pBuf, flLastValue ); + + flLastValue = flCurValue; + runLength = 1; + } + } + + // Write the end.. + if( runLength ) + { + pBuf->PutUnsignedChar( runLength ); + EncodeCharOrShort( pBuf, flLastValue ); + } +} + +#ifdef _WIN32 +#pragma warning (default:4701) +#endif + +void MultiplyValues( CUtlVector<CLightValue> &values, float scale ) +{ + for( int i=0; i < values.Count(); i++ ) + values[i].m_Dot *= scale; +} + + +void CIncremental::FinishFace( + IncrementalLightID lightID, + int iFace, + int iThread ) +{ + CIncLight *pLight = m_Lights[lightID]; + + // Check for the 99.99% case in which the face already exists. + CLightFace *pFace; + if( pLight->m_pCachedFaces[iThread] && pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace ) + { + pFace = pLight->m_pCachedFaces[iThread]; + + // Compress the data. + MultiplyValues( pFace->m_LightValues, pLight->m_flMaxIntensity ); + + pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); + CompressLightData( + pFace->m_LightValues.Base(), + pFace->m_LightValues.Count(), + &pFace->m_CompressedData ); + +#if 0 + // test decompression + CUtlVector<CLightValue> test; + test.SetSize( 2048 ); + pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + DecompressLightData( &pFace->m_CompressedData, &test ); +#endif + + if( pFace->m_CompressedData.TellPut() == 0 ) + { + // No contribution.. delete this face from the light. + EnterCriticalSection( &pLight->m_CS ); + pLight->m_LightFaces.Remove( pFace->m_LightFacesIndex ); + delete pFace; + LeaveCriticalSection( &pLight->m_CS ); + } + else + { + // Discard the uncompressed data. + pFace->m_LightValues.Purge(); + m_FacesTouched[ pFace->m_FaceIndex ] = 1; + } + } +} + + +bool CIncremental::Finalize() +{ + // If we're not being used, don't do anything. + if( !m_pIncrementalFilename || !m_pBSPFilename ) + return false; + + CUtlVector<CFaceLightList> faceLights; + LinkLightsToFaces( faceLights ); + + Vector faceLight[(MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2)]; + CUtlVector<CLightValue> faceLightValues; + faceLightValues.SetSize( (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) ); + + // Only update the faces we've touched. + for( int facenum = 0; facenum < numfaces; facenum++ ) + { + if( !m_FacesTouched[facenum] || !faceLights[facenum].Count() ) + continue; + + int w = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[0]+1; + int h = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[1]+1; + int nLuxels = w * h; + assert( nLuxels <= sizeof(faceLight) / sizeof(faceLight[0]) ); + + // Clear the lighting for this face. + memset( faceLight, 0, nLuxels * sizeof(Vector) ); + + // Composite all the light contributions. + for( int iFace=0; iFace < faceLights[facenum].Count(); iFace++ ) + { + CLightFace *pFace = faceLights[facenum][iFace]; + + pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + DecompressLightData( &pFace->m_CompressedData, &faceLightValues ); + + for( int iSample=0; iSample < nLuxels; iSample++ ) + { + float flDot = faceLightValues[iSample].m_Dot; + if( flDot ) + { + VectorMA( + faceLight[iSample], + flDot / pFace->m_pLight->m_flMaxIntensity, + pFace->m_pLight->m_Light.intensity, + faceLight[iSample] ); + } + } + } + + // Convert to the floating-point representation in the BSP file. + Vector *pSrc = faceLight; + unsigned char *pDest = &(*pdlightdata)[ g_pFaces[facenum].lightofs ]; + + for( int iSample=0; iSample < nLuxels; iSample++ ) + { + VectorToColorRGBExp32( *pSrc, *( ColorRGBExp32 *)pDest ); + pDest += 4; + pSrc++; + } + } + + m_bSuccessfulRun = true; + return true; +} + + +void CIncremental::GetFacesTouched( CUtlVector<unsigned char> &touched ) +{ + touched.CopyArray( m_FacesTouched.Base(), m_FacesTouched.Count() ); +} + + +bool CIncremental::Serialize() +{ + if( !SaveIncrementalFile() ) + return false; + + WriteBSPFile( (char*)m_pBSPFilename ); + return true; +} + + +void CIncremental::Term() +{ + m_Lights.PurgeAndDeleteElements(); + m_TotalMemory = 0; +} + + +void CIncremental::AddLightsForActiveLights() +{ + // Create our lights. + for( directlight_t *dl=activelights; dl != NULL; dl = dl->next ) + { + CIncLight *pLight = new CIncLight; + dl->m_IncrementalID = m_Lights.AddToTail( pLight ); + + // Copy the light information. + pLight->m_Light = dl->light; + pLight->m_flMaxIntensity = max( dl->light.intensity[0], max( dl->light.intensity[1], dl->light.intensity[2] ) ); + } +} + + +bool CIncremental::LoadIncrementalFile() +{ + Term(); + + if( !IsIncrementalFileValid() ) + return false; + + long fp = FileOpen( m_pIncrementalFilename, true ); + if( !fp ) + return false; + + // Read the header. + CIncrementalHeader hdr; + if( !ReadIncrementalHeader( fp, &hdr ) ) + { + FileClose( fp ); + return false; + } + + + // Read the lights. + int nLights; + FileRead( fp, nLights ); + for( int iLight=0; iLight < nLights; iLight++ ) + { + CIncLight *pLight = new CIncLight; + m_Lights.AddToTail( pLight ); + + FileRead( fp, pLight->m_Light ); + pLight->m_flMaxIntensity = + max( pLight->m_Light.intensity.x, + max( pLight->m_Light.intensity.y, pLight->m_Light.intensity.z ) ); + + int nFaces; + FileRead( fp, nFaces ); + assert( nFaces < 70000 ); + + for( int iFace=0; iFace < nFaces; iFace++ ) + { + CLightFace *pFace = new CLightFace; + pLight->m_LightFaces.AddToTail( pFace ); + + pFace->m_pLight = pLight; + FileRead( fp, pFace->m_FaceIndex ); + + int dataSize; + FileRead( fp, dataSize ); + + pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); + while( dataSize ) + { + --dataSize; + + unsigned char ucData; + FileRead( fp, ucData ); + + pFace->m_CompressedData.PutUnsignedChar( ucData ); + } + } + } + + + FileClose( fp ); + return !FileError(); +} + + +bool CIncremental::SaveIncrementalFile() +{ + long fp = FileOpen( m_pIncrementalFilename, false ); + if( !fp ) + return false; + + if( !WriteIncrementalHeader( fp ) ) + { + FileClose( fp ); + return false; + } + + // Write the lights. + int nLights = m_Lights.Count(); + FileWrite( fp, nLights ); + for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) ) + { + CIncLight *pLight = m_Lights[iLight]; + + FileWrite( fp, pLight->m_Light ); + + int nFaces = pLight->m_LightFaces.Count(); + FileWrite( fp, nFaces ); + for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) ) + { + CLightFace *pFace = pLight->m_LightFaces[iFace]; + + FileWrite( fp, pFace->m_FaceIndex ); + + int dataSize = pFace->m_CompressedData.TellPut(); + FileWrite( fp, dataSize ); + + pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + while( dataSize ) + { + --dataSize; + FileWrite( fp, pFace->m_CompressedData.GetUnsignedChar() ); + } + } + } + + + FileClose( fp ); + return !FileError(); +} + + +void CIncremental::LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights ) +{ + faceLights.SetSize( numfaces ); + + for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) ) + { + CIncLight *pLight = m_Lights[iLight]; + + for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) ) + { + CLightFace *pFace = pLight->m_LightFaces[iFace]; + + if( m_FacesTouched[pFace->m_FaceIndex] ) + faceLights[ pFace->m_FaceIndex ].AddToTail( pFace ); + } + } +} + + +// ------------------------------------------------------------------ // +// CIncLight +// ------------------------------------------------------------------ // + +CIncLight::CIncLight() +{ + memset( m_pCachedFaces, 0, sizeof(m_pCachedFaces) ); + InitializeCriticalSection( &m_CS ); +} + + +CIncLight::~CIncLight() +{ + m_LightFaces.PurgeAndDeleteElements(); + DeleteCriticalSection( &m_CS ); +} + + +CLightFace* CIncLight::FindOrCreateLightFace( int iFace, int lmSize, bool *bNew ) +{ + if( bNew ) + *bNew = false; + + + // Look for it. + for( int i=m_LightFaces.Head(); i != m_LightFaces.InvalidIndex(); i=m_LightFaces.Next(i) ) + { + CLightFace *pFace = m_LightFaces[i]; + + if( pFace->m_FaceIndex == iFace ) + { + assert( pFace->m_LightValues.Count() == lmSize ); + return pFace; + } + } + + // Ok, create one. + CLightFace *pFace = new CLightFace; + pFace->m_LightFacesIndex = m_LightFaces.AddToTail( pFace ); + pFace->m_pLight = this; + + pFace->m_FaceIndex = iFace; + pFace->m_LightValues.SetSize( lmSize ); + memset( pFace->m_LightValues.Base(), 0, sizeof( CLightValue ) * lmSize ); + + if( bNew ) + *bNew = true; + + return pFace; +} + + |