diff options
Diffstat (limited to 'materialsystem/cmatlightmaps.cpp')
| -rw-r--r-- | materialsystem/cmatlightmaps.cpp | 2191 |
1 files changed, 2191 insertions, 0 deletions
diff --git a/materialsystem/cmatlightmaps.cpp b/materialsystem/cmatlightmaps.cpp new file mode 100644 index 0000000..fe2df86 --- /dev/null +++ b/materialsystem/cmatlightmaps.cpp @@ -0,0 +1,2191 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "pch_materialsystem.h" + +#define MATSYS_INTERNAL + +#include "cmatlightmaps.h" + +#include "colorspace.h" +#include "IHardwareConfigInternal.h" + +#include "cmaterialsystem.h" + +// NOTE: This must be the last file included!!! +#include "tier0/memdbgon.h" +#include "bitmap/float_bm.h" + +static ConVar mat_lightmap_pfms( "mat_lightmap_pfms", "0", FCVAR_MATERIAL_SYSTEM_THREAD, "Outputs .pfm files containing lightmap data for each lightmap page when a level exits." ); // Write PFM files for each lightmap page in the game directory when exiting a level + +#define USE_32BIT_LIGHTMAPS_ON_360 //uncomment to use 32bit lightmaps, be sure to keep this in sync with the same #define in stdshaders/lightmappedgeneric_ps2_3_x.h + +#ifdef _X360 +#define X360_USE_SIMD_LIGHTMAP +#endif + +//----------------------------------------------------------------------------- + +inline IMaterialInternal* CMatLightmaps::GetCurrentMaterialInternal() const +{ + return GetMaterialSystem()->GetRenderContextInternal()->GetCurrentMaterialInternal(); +} + +inline void CMatLightmaps::SetCurrentMaterialInternal(IMaterialInternal* pCurrentMaterial) +{ + return GetMaterialSystem()->GetRenderContextInternal()->SetCurrentMaterialInternal( pCurrentMaterial ); +} + +inline IMaterialInternal *CMatLightmaps::GetMaterialInternal( MaterialHandle_t idx ) const +{ + return GetMaterialSystem()->GetMaterialInternal( idx ); +} + +inline const IMatRenderContextInternal *CMatLightmaps::GetRenderContextInternal() const +{ + return GetMaterialSystem()->GetRenderContextInternal(); +} + +inline IMatRenderContextInternal *CMatLightmaps::GetRenderContextInternal() +{ + return GetMaterialSystem()->GetRenderContextInternal(); +} + +inline const CMaterialDict *CMatLightmaps::GetMaterialDict() const +{ + return GetMaterialSystem()->GetMaterialDict(); +} + +inline CMaterialDict *CMatLightmaps::GetMaterialDict() +{ + return GetMaterialSystem()->GetMaterialDict(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CMatLightmaps::CMatLightmaps() +{ + m_currentWhiteLightmapMaterial = NULL; + m_pLightmapPages = NULL; + m_NumLightmapPages = 0; + m_numSortIDs = 0; + m_nUpdatingLightmapsStackDepth = 0; + m_nLockedLightmap = -1; + m_pLightmapDataPtrArray = NULL; + m_eLightmapsState = STATE_DEFAULT; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatLightmaps::Shutdown( ) +{ + // Clean up all lightmaps + CleanupLightmaps(); +} + +//----------------------------------------------------------------------------- +// Assign enumeration IDs to all materials +//----------------------------------------------------------------------------- +void CMatLightmaps::EnumerateMaterials( void ) +{ + // iterate in sorted order + int id = 0; + for (MaterialHandle_t i = GetMaterialDict()->FirstMaterial(); i != GetMaterialDict()->InvalidMaterial(); i = GetMaterialDict()->NextMaterial(i) ) + { + GetMaterialInternal(i)->SetEnumerationID( id ); + ++id; + } +} + + +//----------------------------------------------------------------------------- +// Gets the maximum lightmap page size... +//----------------------------------------------------------------------------- +int CMatLightmaps::GetMaxLightmapPageWidth() const +{ + // FIXME: It's unclear which we want here. + // It doesn't drastically increase primitives per DrawIndexedPrimitive + // call at the moment to increase it, so let's not for now. + + // If we're using dynamic textures though, we want bigger that's for sure. + // The tradeoff here is how much memory we waste if we don't fill the lightmap + + // We need to go to 512x256 textures because that's the only way bumped + // lighting on displacements can work given the 128x128 allowance.. + int nWidth = 512; + if ( nWidth > HardwareConfig()->MaxTextureWidth() ) + nWidth = HardwareConfig()->MaxTextureWidth(); + + return nWidth; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMatLightmaps::GetMaxLightmapPageHeight() const +{ + int nHeight = 256; + + if ( nHeight > HardwareConfig()->MaxTextureHeight() ) + nHeight = HardwareConfig()->MaxTextureHeight(); + + return nHeight; +} + + +//----------------------------------------------------------------------------- +// Returns the lightmap page size +//----------------------------------------------------------------------------- +void CMatLightmaps::GetLightmapPageSize( int lightmapPageID, int *pWidth, int *pHeight ) const +{ + switch( lightmapPageID ) + { + default: + Assert( lightmapPageID >= 0 && lightmapPageID < GetNumLightmapPages() ); + *pWidth = m_pLightmapPages[lightmapPageID].m_Width; + *pHeight = m_pLightmapPages[lightmapPageID].m_Height; + break; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED: + *pWidth = *pHeight = 1; + AssertOnce( !"Can't use CMatLightmaps to get properties of MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED" ); + break; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE: + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP: + *pWidth = *pHeight = 1; + break; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMatLightmaps::GetLightmapWidth( int lightmapPageID ) const +{ + switch( lightmapPageID ) + { + default: + Assert( lightmapPageID >= 0 && lightmapPageID < GetNumLightmapPages() ); + return m_pLightmapPages[lightmapPageID].m_Width; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED: + AssertOnce( !"Can't use CMatLightmaps to get properties of MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED" ); + return 1; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE: + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP: + return 1; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMatLightmaps::GetLightmapHeight( int lightmapPageID ) const +{ + switch( lightmapPageID ) + { + default: + Assert( lightmapPageID >= 0 && lightmapPageID < GetNumLightmapPages() ); + return m_pLightmapPages[lightmapPageID].m_Height; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED: + AssertOnce( !"Can't use CMatLightmaps to get properties of MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED" ); + return 1; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE: + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP: + return 1; + } +} + + +//----------------------------------------------------------------------------- +// Clean up lightmap pages. +//----------------------------------------------------------------------------- +void CMatLightmaps::CleanupLightmaps() +{ + if ( mat_lightmap_pfms.GetBool()) + { + // Write PFM files containing lightmap data for this page + for (int lightmap = 0; lightmap < GetNumLightmapPages(); lightmap++) + { + if ((NULL != m_pLightmapDataPtrArray) && (NULL != m_pLightmapDataPtrArray[lightmap])) + { + char szPFMFileName[MAX_PATH]; + + sprintf(szPFMFileName, "Lightmap-Page-%d.pfm", lightmap); + m_pLightmapDataPtrArray[lightmap]->WritePFM(szPFMFileName); + } + } + } + + // Remove the lightmap data bitmap representations + if (m_pLightmapDataPtrArray) + { + int i; + for( i = 0; i < GetNumLightmapPages(); i++ ) + { + delete m_pLightmapDataPtrArray[i]; + } + + delete [] m_pLightmapDataPtrArray; + m_pLightmapDataPtrArray = NULL; + } + + // delete old lightmap pages + if( m_pLightmapPages ) + { + int i; + for( i = 0; i < GetNumLightmapPages(); i++ ) + { + g_pShaderAPI->DeleteTexture( m_LightmapPageTextureHandles[i] ); + } + delete [] m_pLightmapPages; + m_pLightmapPages = 0; + } + + m_NumLightmapPages = 0; +} + +//----------------------------------------------------------------------------- +// Resets the lightmap page info for each material +//----------------------------------------------------------------------------- +void CMatLightmaps::ResetMaterialLightmapPageInfo( void ) +{ + for (MaterialHandle_t i = GetMaterialDict()->FirstMaterial(); i != GetMaterialDict()->InvalidMaterial(); i = GetMaterialDict()->NextMaterial(i) ) + { + IMaterialInternal *pMaterial = GetMaterialInternal(i); + pMaterial->SetMinLightmapPageID( 9999 ); + pMaterial->SetMaxLightmapPageID( -9999 ); + pMaterial->SetNeedsWhiteLightmap( false ); + } +} + +//----------------------------------------------------------------------------- +// This is called before any lightmap allocations take place +//----------------------------------------------------------------------------- +void CMatLightmaps::BeginLightmapAllocation() +{ + // delete old lightmap pages + CleanupLightmaps(); + + m_ImagePackers.RemoveAll(); + int i = m_ImagePackers.AddToTail(); + m_ImagePackers[i].Reset( 0, GetMaxLightmapPageWidth(), GetMaxLightmapPageHeight() ); + + SetCurrentMaterialInternal(0); + m_currentWhiteLightmapMaterial = 0; + m_numSortIDs = 0; + + // need to set the min and max sorting id number for each material to + // a default value that basically means that it hasn't been used yet. + ResetMaterialLightmapPageInfo(); + + EnumerateMaterials(); +} + + +//----------------------------------------------------------------------------- +// Allocates space in the lightmaps; must be called after BeginLightmapAllocation +//----------------------------------------------------------------------------- +int CMatLightmaps::AllocateLightmap( int width, int height, + int offsetIntoLightmapPage[2], + IMaterial *iMaterial ) +{ + IMaterialInternal *pMaterial = static_cast<IMaterialInternal *>( iMaterial ); + if ( !pMaterial ) + { + Warning( "Programming error: CMatRenderContext::AllocateLightmap: NULL material\n" ); + return m_numSortIDs; + } + pMaterial = pMaterial->GetRealTimeVersion(); //always work with the real time versions of materials internally + + // material change + int i; + int nPackCount = m_ImagePackers.Count(); + if ( GetCurrentMaterialInternal() != pMaterial ) + { + // If this happens, then we need to close out all image packers other than + // the last one so as to produce as few sort IDs as possible + for ( i = nPackCount - 1; --i >= 0; ) + { + // NOTE: We *must* use the order preserving one here so the remaining one + // is the last lightmap + m_ImagePackers.Remove( i ); + --nPackCount; + } + + // If it's not the first material, increment the sort id + if (GetCurrentMaterialInternal()) + { + m_ImagePackers[0].IncrementSortId( ); + ++m_numSortIDs; + } + + SetCurrentMaterialInternal(pMaterial); + + // This assertion guarantees we don't see the same material twice in this loop. + Assert( pMaterial->GetMinLightmapPageID( ) > pMaterial->GetMaxLightmapPageID() ); + + // NOTE: We may not use this lightmap page, but we might + // we won't know for sure until the next material is passed in. + // So, for now, we're going to forcibly add the current lightmap + // page to this material so the sort IDs work out correctly. + GetCurrentMaterialInternal()->SetMinLightmapPageID( GetNumLightmapPages() ); + GetCurrentMaterialInternal()->SetMaxLightmapPageID( GetNumLightmapPages() ); + } + + // Try to add it to any of the current images... + bool bAdded = false; + for ( i = 0; i < nPackCount; ++i ) + { + bAdded = m_ImagePackers[i].AddBlock( width, height, &offsetIntoLightmapPage[0], &offsetIntoLightmapPage[1] ); + if ( bAdded ) + break; + } + + if ( !bAdded ) + { + ++m_numSortIDs; + i = m_ImagePackers.AddToTail(); + m_ImagePackers[i].Reset( m_numSortIDs, GetMaxLightmapPageWidth(), GetMaxLightmapPageHeight() ); + ++m_NumLightmapPages; + if ( !m_ImagePackers[i].AddBlock( width, height, &offsetIntoLightmapPage[0], &offsetIntoLightmapPage[1] ) ) + { + Error( "MaterialSystem_Interface_t::AllocateLightmap: lightmap (%dx%d) too big to fit in page (%dx%d)\n", + width, height, GetMaxLightmapPageWidth(), GetMaxLightmapPageHeight() ); + } + + // Add this lightmap to the material... + GetCurrentMaterialInternal()->SetMaxLightmapPageID( GetNumLightmapPages() ); + } + + return m_ImagePackers[i].GetSortId(); +} + +// UNDONE: This needs testing, but it appears as though creating these textures managed +// results in huge stalls whenever they are locked for modify. +// That makes sense given the d3d docs, but these have been flagged as managed for quite some time. +#define DYNAMIC_TEXTURES_NO_BACKING 1 + +void CMatLightmaps::EndLightmapAllocation() +{ + // count the last page that we were on.if it wasn't + // and count the last sortID that we were on + m_NumLightmapPages++; + m_numSortIDs++; + + m_firstDynamicLightmap = m_NumLightmapPages; + // UNDONE: Until we start using the separate dynamic lighting textures don't allocate them + // NOTE: Enable this if we want to stop locking the base lightmaps and instead only lock update + // these completely dynamic pages +// m_NumLightmapPages += COUNT_DYNAMIC_LIGHTMAP_PAGES; + m_dynamic.Init(); + + // Compute the dimensions of the last lightmap + int lastLightmapPageWidth, lastLightmapPageHeight; + int nLastIdx = m_ImagePackers.Count(); + m_ImagePackers[nLastIdx - 1].GetMinimumDimensions( &lastLightmapPageWidth, &lastLightmapPageHeight ); + m_ImagePackers.Purge(); + + m_pLightmapPages = new LightmapPageInfo_t[GetNumLightmapPages()]; + Assert( m_pLightmapPages ); + + if ( mat_lightmap_pfms.GetBool()) + { + // This array will be used to write PFM files full of lightmap data + m_pLightmapDataPtrArray = new FloatBitMap_t*[GetNumLightmapPages()]; + } + + int i; + m_LightmapPageTextureHandles.EnsureCapacity( GetNumLightmapPages() ); + for ( i = 0; i < GetNumLightmapPages(); i++ ) + { + // Compute lightmap dimensions + bool lastStaticLightmap = ( i == (m_firstDynamicLightmap-1)); + m_pLightmapPages[i].m_Width = (unsigned short)(lastStaticLightmap ? lastLightmapPageWidth : GetMaxLightmapPageWidth()); + m_pLightmapPages[i].m_Height = (unsigned short)(lastStaticLightmap ? lastLightmapPageHeight : GetMaxLightmapPageHeight()); + m_pLightmapPages[i].m_Flags = 0; + + AllocateLightmapTexture( i ); + + if ( mat_lightmap_pfms.GetBool()) + { + // Initialize the pointers to lightmap data + m_pLightmapDataPtrArray[i] = NULL; + } + } +} + +//----------------------------------------------------------------------------- +// Allocate lightmap textures +//----------------------------------------------------------------------------- +void CMatLightmaps::AllocateLightmapTexture( int lightmap ) +{ + bool bUseDynamicTextures = HardwareConfig()->PreferDynamicTextures(); + + int flags = bUseDynamicTextures ? TEXTURE_CREATE_DYNAMIC : TEXTURE_CREATE_MANAGED; + + m_LightmapPageTextureHandles.EnsureCount( lightmap + 1 ); + + char debugName[256]; + Q_snprintf( debugName, sizeof( debugName ), "[lightmap %d]", lightmap ); + + ImageFormat imageFormat; + switch ( HardwareConfig()->GetHDRType() ) + { + default: + Assert( 0 ); + // fall through. + + case HDR_TYPE_NONE: +#if !defined( _X360 ) + imageFormat = IMAGE_FORMAT_RGBA8888; + flags |= TEXTURE_CREATE_SRGB; +#else + imageFormat = IMAGE_FORMAT_LINEAR_RGBA8888; +#endif + break; + + case HDR_TYPE_INTEGER: +#if !defined( _X360 ) + imageFormat = IMAGE_FORMAT_RGBA16161616; +#else +# if ( defined( USE_32BIT_LIGHTMAPS_ON_360 ) ) + imageFormat = IMAGE_FORMAT_LINEAR_RGBA8888; +# else + imageFormat = IMAGE_FORMAT_LINEAR_RGBA16161616; +# endif +#endif + break; + + case HDR_TYPE_FLOAT: + imageFormat = IMAGE_FORMAT_RGBA16161616F; + break; + } + + switch ( m_eLightmapsState ) + { + case STATE_DEFAULT: + // Allow allocations in default state + { + m_LightmapPageTextureHandles[lightmap] = g_pShaderAPI->CreateTexture( + GetLightmapWidth(lightmap), GetLightmapHeight(lightmap), 1, + imageFormat, + 1, 1, flags, debugName, TEXTURE_GROUP_LIGHTMAP ); // don't mipmap lightmaps + + // Load up the texture data + g_pShaderAPI->ModifyTexture( m_LightmapPageTextureHandles[lightmap] ); + g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR ); + g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR ); + + if ( !bUseDynamicTextures ) + { + g_pShaderAPI->TexSetPriority( 1 ); + } + + // Blat out the lightmap bits + InitLightmapBits( lightmap ); + } + break; + + case STATE_RELEASED: + // Not assigned m_LightmapPageTextureHandles[lightmap]; + DevMsg( "AllocateLightmapTexture(%d) in released lightmap state (STATE_RELEASED), delayed till \"Restore\".\n", lightmap ); + return; + + default: + // Not assigned m_LightmapPageTextureHandles[lightmap]; + Warning( "AllocateLightmapTexture(%d) in unknown lightmap state (%d), skipped.\n", lightmap, m_eLightmapsState ); + Assert( !"AllocateLightmapTexture(?) in unknown lightmap state (?)" ); + return; + } +} + + +int CMatLightmaps::AllocateWhiteLightmap( IMaterial *iMaterial ) +{ + IMaterialInternal *pMaterial = static_cast<IMaterialInternal *>( iMaterial ); + if( !pMaterial ) + { + Warning( "Programming error: CMatRenderContext::AllocateWhiteLightmap: NULL material\n" ); + return m_numSortIDs; + } + pMaterial = pMaterial->GetRealTimeVersion(); //always work with the real time versions of materials internally + + if ( !m_currentWhiteLightmapMaterial || ( m_currentWhiteLightmapMaterial != pMaterial ) ) + { + if ( !GetCurrentMaterialInternal() && !m_currentWhiteLightmapMaterial ) + { + // don't increment if this is the very first material (ie. no lightmaps + // allocated with AllocateLightmap + // Assert( 0 ); + } + else + { + // material change + m_numSortIDs++; +#if 0 + char buf[128]; + Q_snprintf( buf, sizeof( buf ), "AllocateWhiteLightmap: m_numSortIDs = %d %s\n", m_numSortIDs, pMaterial->GetName() ); + OutputDebugString( buf ); +#endif + } +// Warning( "%d material: \"%s\" lightmapPageID: -1\n", m_numSortIDs, pMaterial->GetName() ); + m_currentWhiteLightmapMaterial = pMaterial; + pMaterial->SetNeedsWhiteLightmap( true ); + } + + return m_numSortIDs; +} + +//----------------------------------------------------------------------------- +// Releases/restores lightmap pages +//----------------------------------------------------------------------------- +void CMatLightmaps::ReleaseLightmapPages() +{ + switch ( m_eLightmapsState ) + { + case STATE_DEFAULT: + // Allow release in default state only + break; + + default: + Warning( "ReleaseLightmapPages is expected in STATE_DEFAULT, current state = %d, discarded.\n", m_eLightmapsState ); + Assert( !"ReleaseLightmapPages is expected in STATE_DEFAULT" ); + return; + } + + for( int i = 0; i < GetNumLightmapPages(); i++ ) + { + g_pShaderAPI->DeleteTexture( m_LightmapPageTextureHandles[i] ); + } + + // We are now in released state + m_eLightmapsState = STATE_RELEASED; +} + +void CMatLightmaps::RestoreLightmapPages() +{ + switch ( m_eLightmapsState ) + { + case STATE_RELEASED: + // Allow restore in released state only + break; + + default: + Warning( "RestoreLightmapPages is expected in STATE_RELEASED, current state = %d, discarded.\n", m_eLightmapsState ); + Assert( !"RestoreLightmapPages is expected in STATE_RELEASED" ); + return; + } + + // Switch to default state to allow allocations + m_eLightmapsState = STATE_DEFAULT; + + for( int i = 0; i < GetNumLightmapPages(); i++ ) + { + AllocateLightmapTexture( i ); + } +} + + +//----------------------------------------------------------------------------- +// This initializes the lightmap bits +//----------------------------------------------------------------------------- +void CMatLightmaps::InitLightmapBits( int lightmap ) +{ + VPROF_( "CMatLightmaps::InitLightmapBits", 1, VPROF_BUDGETGROUP_DLIGHT_RENDERING, false, 0 ); + int width = GetLightmapWidth(lightmap); + int height = GetLightmapHeight(lightmap); + + CPixelWriter writer; + + g_pShaderAPI->ModifyTexture( m_LightmapPageTextureHandles[lightmap] ); + if ( !g_pShaderAPI->TexLock( 0, 0, 0, 0, width, height, writer ) ) + return; + + // Debug mode, make em green checkerboard + if ( writer.IsUsingFloatFormat() ) + { + for ( int j = 0; j < height; ++j ) + { + writer.Seek( 0, j ); + for ( int k = 0; k < width; ++k ) + { +#ifndef _DEBUG + writer.WritePixel( 1.0f, 1.0f, 1.0f ); +#else // _DEBUG + if( ( j + k ) & 1 ) + { + writer.WritePixelF( 0.0f, 1.0f, 0.0f ); + } + else + { + writer.WritePixelF( 0.0f, 0.0f, 0.0f ); + } +#endif // _DEBUG + } + } + } + else + { + for ( int j = 0; j < height; ++j ) + { + writer.Seek( 0, j ); + for ( int k = 0; k < width; ++k ) + { +#ifndef _DEBUG + // note: make this white to find multisample centroid sampling problems. + // writer.WritePixel( 255, 255, 255 ); + writer.WritePixel( 0, 0, 0 ); +#else // _DEBUG + if ( ( j + k ) & 1 ) + { + writer.WritePixel( 0, 255, 0 ); + } + else + { + writer.WritePixel( 0, 0, 0 ); + } +#endif // _DEBUG + } + } + } + + g_pShaderAPI->TexUnlock(); +} + +bool CMatLightmaps::LockLightmap( int lightmap ) +{ +// Warning( "locking lightmap page: %d\n", lightmap ); + VPROF_INCREMENT_COUNTER( "lightmap fullpage texlock", 1 ); + if( m_nLockedLightmap != -1 ) + { + g_pShaderAPI->TexUnlock(); + } + g_pShaderAPI->ModifyTexture( m_LightmapPageTextureHandles[lightmap] ); + int pageWidth = m_pLightmapPages[lightmap].m_Width; + int pageHeight = m_pLightmapPages[lightmap].m_Height; + if (!g_pShaderAPI->TexLock( 0, 0, 0, 0, pageWidth, pageHeight, m_LightmapPixelWriter )) + { + Assert( 0 ); + return false; + } + m_nLockedLightmap = lightmap; + return true; +} + +Vector4D ConvertLightmapColorToRGBScale( const float *lightmapColor ) +{ + Vector4D result; + + float fScale = lightmapColor[0]; + for( int i = 1; i != 3; ++i ) + { + if( lightmapColor[i] > fScale ) + fScale = lightmapColor[i]; + } + + fScale = ceil( fScale * (255.0f/16.0f) ) * (16.0f/255.0f); + fScale = min( fScale, 16.0f ); + + float fInvScale = 1.0f / fScale; + + for( int i = 0; i != 3; ++i ) + { + result[i] = lightmapColor[i] * fInvScale; + result[i] = ceil( result[i] * 255.0f ) * (1.0f/255.0f); + result[i] = min( result[i], 1.0f ); + } + + fScale /= 16.0f; + + result.w = fScale; + + return result; +} + +#ifdef _X360 +// SIMD version of above +// input numbers from pSrc are on the domain [0..16] +// output is RGBA +// ignores contents of w channel of input +// the shader does this: rOut = Rin * Ain * 16.0f +// where Rin is [0..1], a float computed from a byte value [0..255] +// Ain is therefore the brightest channel (say R) divided by 16 and quantized +// Rin is computed from pSrc->r by dividing by Ain +// this outputs RGBa where RGB are [0..255] and a is the shader's scaling factor (also 0..255) +// +// WARNING - this code appears to be vulnerable to a compiler bug. Be very careful modifying and be +// sure to test +fltx4 ConvertLightmapColorToRGBScale( FLTX4 lightmapColor ) +{ + + static const fltx4 vTwoFiftyFive = {255.0f, 255.0f, 255.0f, 255.0f}; + static const fltx4 FourPoint1s = { 0.1, 0.1, 0.1, 0.1 }; + static const fltx4 vTwoFiftyFiveOverSixteen = {255.0f / 16.0f, 255.0f / 16.0f, 255.0f / 16.0f, 255.0f / 16.0f}; + // static const fltx4 vSixteenOverTwoFiftyFive = { 16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f }; + + + // find the highest color value in lightmapColor and replicate it + fltx4 scale = FindHighestSIMD3( lightmapColor ); + fltx4 minscale = FindLowestSIMD3( lightmapColor ); + fltx4 fl4OutofRange = OrSIMD( CmpGeSIMD( scale, Four_Ones ), CmpLeSIMD( scale, FourPoint1s ) ); + fl4OutofRange = OrSIMD( fl4OutofRange, CmpGtSIMD( minscale, MulSIMD( Four_PointFives, scale ) ) ); + + // scale needs to be divided by 16 (because the shader multiplies it by 16) + // then mapped to 0..255 and quantized. + scale = __vrfip(MulSIMD(scale, vTwoFiftyFiveOverSixteen)); // scale = ceil(scale * 255/16) + + fltx4 result = MulSIMD(vTwoFiftyFive, lightmapColor); // start the scale cooking on the final result + + fltx4 invScale = ReciprocalEstSIMD(scale); // invScale = (16/255)(1/scale). may be +inf + invScale = MulSIMD(invScale, vTwoFiftyFiveOverSixteen); // take the quantizing factor back out + // of the inverse scale (one less + // dependent op if you do it this way) + + // scale the input channels + // compute so the numbers are all 0..255 ints. (if one happens to + // be 256 due to numerical error in the reciprocation, the unsigned-saturate + // store we'll use later on will bake it back down to 255) + result = MulSIMD(result, invScale); + + // now, output -- + // if the input color was nonzero, slip the scale into return value's w + // component and return. If the input was zero, return zero. + + result = MaskedAssign( + fl4OutofRange, + SetWSIMD( result, scale ), + SetWSIMD( MulSIMD( lightmapColor, vTwoFiftyFive ), vTwoFiftyFiveOverSixteen ) ); + return result; +} +#endif + + +// write bumped lightmap update to LDR 8-bit lightmap +void CMatLightmaps::BumpedLightmapBitsToPixelWriter_LDR( float* pFloatImage, float *pFloatImageBump1, float *pFloatImageBump2, + float *pFloatImageBump3, int pLightmapSize[2], int pOffsetIntoLightmapPage[2], FloatBitMap_t *pfmOut ) +{ + const int nLightmapSize0 = pLightmapSize[0]; + const int nLightmap0WriterSizeBytes = nLightmapSize0 * m_LightmapPixelWriter.GetPixelSize(); + const int nRewindToNextPixel = -( ( nLightmap0WriterSizeBytes * 3 ) - m_LightmapPixelWriter.GetPixelSize() ); + + for( int t = 0; t < pLightmapSize[1]; t++ ) + { + int srcTexelOffset = ( sizeof( Vector4D ) / sizeof( float ) ) * ( 0 + t * nLightmapSize0 ); + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + + for( int s = 0; s < nLightmapSize0; + s++, m_LightmapPixelWriter.SkipBytes(nRewindToNextPixel),srcTexelOffset += (sizeof(Vector4D)/sizeof(float))) + { + unsigned char color[4][3]; + + ColorSpace::LinearToBumpedLightmap( &pFloatImage[srcTexelOffset], + &pFloatImageBump1[srcTexelOffset], &pFloatImageBump2[srcTexelOffset], + &pFloatImageBump3[srcTexelOffset], + color[0], color[1], color[2], color[3] ); + + unsigned char alpha = RoundFloatToByte( pFloatImage[srcTexelOffset+3] * 255.0f ); + m_LightmapPixelWriter.WritePixelNoAdvance( color[0][0], color[0][1], color[0][2], alpha ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( color[1][0], color[1][1], color[1][2], alpha ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( color[2][0], color[2][1], color[2][2], alpha ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( color[3][0], color[3][1], color[3][2], alpha ); + } + } + if ( pfmOut ) + { + for( int t = 0; t < pLightmapSize[1]; t++ ) + { + int srcTexelOffset = ( sizeof( Vector4D ) / sizeof( float ) ) * ( 0 + t * nLightmapSize0 ); + for( int s = 0; s < nLightmapSize0; s++,srcTexelOffset += (sizeof(Vector4D)/sizeof(float))) + { + unsigned char color[4][3]; + + ColorSpace::LinearToBumpedLightmap( &pFloatImage[srcTexelOffset], + &pFloatImageBump1[srcTexelOffset], &pFloatImageBump2[srcTexelOffset], + &pFloatImageBump3[srcTexelOffset], + color[0], color[1], color[2], color[3] ); + + unsigned char alpha = RoundFloatToByte( pFloatImage[srcTexelOffset+3] * 255.0f ); + // Write data to the bitmapped represenations so that PFM files can be written + PixRGBAF pixelData; + pixelData.Red = color[0][0]; + pixelData.Green = color[0][1]; + pixelData.Blue = color[0][2]; + pixelData.Alpha = alpha; + pfmOut->WritePixelRGBAF( pOffsetIntoLightmapPage[0] + s, pOffsetIntoLightmapPage[1] + t, pixelData); + } + } + + } +} + +// write bumped lightmap update to HDR float lightmap +void CMatLightmaps::BumpedLightmapBitsToPixelWriter_HDRF( float* pFloatImage, float *pFloatImageBump1, float *pFloatImageBump2, + float *pFloatImageBump3, int pLightmapSize[2], int pOffsetIntoLightmapPage[2], FloatBitMap_t *pfmOut ) +{ + if ( IsX360() ) + { + // 360 does not support HDR float mode + Assert( 0 ); + return; + } + + Assert( !pfmOut ); // unsupported in this mode + + const int nLightmapSize0 = pLightmapSize[0]; + const int nLightmap0WriterSizeBytes = nLightmapSize0 * m_LightmapPixelWriter.GetPixelSize(); + const int nRewindToNextPixel = -( ( nLightmap0WriterSizeBytes * 3 ) - m_LightmapPixelWriter.GetPixelSize() ); + + for( int t = 0; t < pLightmapSize[1]; t++ ) + { + int srcTexelOffset = ( sizeof( Vector4D ) / sizeof( float ) ) * ( 0 + t * nLightmapSize0 ); + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + + for( int s = 0; + s < nLightmapSize0; + s++, m_LightmapPixelWriter.SkipBytes(nRewindToNextPixel),srcTexelOffset += (sizeof(Vector4D)/sizeof(float))) + { + m_LightmapPixelWriter.WritePixelNoAdvanceF( pFloatImage[srcTexelOffset], pFloatImage[srcTexelOffset+1], + pFloatImage[srcTexelOffset+2], pFloatImage[srcTexelOffset+3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvanceF( pFloatImageBump1[srcTexelOffset], pFloatImageBump1[srcTexelOffset+1], + pFloatImageBump1[srcTexelOffset+2], pFloatImage[srcTexelOffset+3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvanceF( pFloatImageBump2[srcTexelOffset], pFloatImageBump2[srcTexelOffset+1], + pFloatImageBump2[srcTexelOffset+2], pFloatImage[srcTexelOffset+3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvanceF( pFloatImageBump3[srcTexelOffset], pFloatImageBump3[srcTexelOffset+1], + pFloatImageBump3[srcTexelOffset+2], pFloatImage[srcTexelOffset+3] ); + } + } +} + +#ifdef _X360 +#pragma optimize("u", on) +#endif + + +#ifdef _X360 + +namespace { + // pack a pixel into BGRA8888 and return it with the data packed into the w component +FORCEINLINE fltx4 PackPixel_BGRA8888( FLTX4 rgba ) +{ + // this happens to be in an order such that we can use the handy builtin packing op + // clamp to 0..255 (coz it might have leaked over) + static const fltx4 vTwoFiftyFive = {255.0f, 255.0f, 255.0f, 255.0f}; + + // the magic number such that when mul-accummulated against rbga, + // gets us a representation 3.0 + (r)*2^-22 -- puts the bits at + // the bottom of the float + static const XMVECTOR PackScale = { (1.0f / (FLOAT)(1 << 22)), (1.0f / (FLOAT)(1 << 22)), (1.0f / (FLOAT)(1 << 22)), (1.0f / (FLOAT)(1 << 22))}; // 255.0f / (FLOAT)(1 << 22) + static const XMVECTOR Three = {3.0f, 3.0f, 3.0f, 3.0f}; + + fltx4 N = MinSIMD(vTwoFiftyFive, rgba); + + N = __vmaddfp(N, PackScale, Three); + N = __vpkd3d(N, N, VPACK_D3DCOLOR, VPACK_32, 0); // pack into w word + return N; +} + +// A small store-gather buffer used in the +// BumpedLightmapBitsToPixelWriter_HDRI_BGRA_X360(). +// The store-gather buffers. Hopefully these will live in the L1 +// cache, which will make writing to them, then to memory, faster +// than just using __stvewx to write directly into WC memory +// one noncontiguous float at a time. (If there weren't a huge +// compiler bug with __stvewx in the Apr07 XDK, that might not +// be the case.) +struct ALIGN128 CPixelWriterStoreGather +{ + enum { + kRows = 4, + kWordsPerRow = 32, + }; + + ALIGN128 uint32 m_data[kRows][kWordsPerRow]; // four rows of bgra data, aligned to 4 cache lines. dwords so memcpy works better. + int m_wordsGathered; + int m_bytesBetweenWriterRows; // the number of bytes spacing the maps inside the writer from each other + // if we weren't gathering, we'd SkipBytes this many between the base map, bump1, etc. + + // write four rows, as SIMD registers, into the buffers + inline void write( CPixelWriter * RESTRICT pLightmapPixelWriter, FLTX4 row0, FLTX4 row1, FLTX4 row2, FLTX4 row3 ) RESTRICT + { + // if full, commit + Assert(m_wordsGathered <= kWordsPerRow); + AssertMsg((m_wordsGathered & 3) == 0, "Don't call CPixelWriterStoreGather::write after ::writeJustX"); // single-word writes have misaligned me + if (m_wordsGathered >= kWordsPerRow) + { + commitWhenFull(pLightmapPixelWriter); + } + + XMStoreVector4A( &m_data[0][m_wordsGathered], row0 ); + XMStoreVector4A( &m_data[1][m_wordsGathered], row1 ); + XMStoreVector4A( &m_data[2][m_wordsGathered], row2 ); + XMStoreVector4A( &m_data[3][m_wordsGathered], row3 ); + + m_wordsGathered += 4 ; // four words per simd vec + } + + // pluck the w component out of each of the rows, and store it into the gather buffer. Don't + // call the other write function after calling this. + inline void writeJustW( CPixelWriter * RESTRICT pLightmapPixelWriter, FLTX4 row0, FLTX4 row1, FLTX4 row2, FLTX4 row3 ) RESTRICT + { + // if full, commit + Assert(m_wordsGathered <= kWordsPerRow); + if (m_wordsGathered >= kWordsPerRow) + { + commitWhenFull(pLightmapPixelWriter); + } + + // for each fltx4, splat out x and then use the __stvewx to store + // whichever word happens to align with the float pointer through + // that pointer. + + __stvewx(__vspltw(row0, 3), &m_data[0][m_wordsGathered], 0 ); + __stvewx(__vspltw(row1, 3), &m_data[1][m_wordsGathered], 0 ); + __stvewx(__vspltw(row2, 3), &m_data[2][m_wordsGathered], 0 ); + __stvewx(__vspltw(row3, 3), &m_data[3][m_wordsGathered], 0 ); + + m_wordsGathered += 1 ; // only stored one word + } + + // Commit my buffers to the pixelwriter's memory, and advance its + // pointer. + void commit(CPixelWriter * RESTRICT pLightmapPixelWriter) RESTRICT + { + if (m_wordsGathered > 0) + { + unsigned char* RESTRICT pWriteInto = pLightmapPixelWriter->GetCurrentPixel(); + // we have to use memcpy because we're writing to non-cacheable memory, + // but we can't even assume that the addresses we're writing to are + // vector-aligned. +#ifdef memcpy // if someone's overriden the intrinsic, complain +#pragma error("You have overridden memcpy(), which is an XBOX360 intrinsic. This function will not behave optimally.") +#endif + + memcpy(pWriteInto, m_data[0], m_wordsGathered * sizeof(uint32)); + pWriteInto += m_bytesBetweenWriterRows; + memcpy(pWriteInto, m_data[1], m_wordsGathered * sizeof(uint32)); + pWriteInto += m_bytesBetweenWriterRows; + memcpy(pWriteInto, m_data[2], m_wordsGathered * sizeof(uint32)); + pWriteInto += m_bytesBetweenWriterRows; + memcpy(pWriteInto, m_data[3], m_wordsGathered * sizeof(uint32)); + + pLightmapPixelWriter->SkipBytes(m_wordsGathered * sizeof(uint32)); + m_wordsGathered = 0; + } + } + + // like commit, but the version we use when we know we're full. + // Takes advantage of better compile-time generation for + // memcpy. + void commitWhenFull(CPixelWriter * RESTRICT pLightmapPixelWriter) RESTRICT + { + unsigned char* RESTRICT pWriteInto = pLightmapPixelWriter->GetCurrentPixel(); + // we have to use memcpy because we're writing to non-cacheable memory, + // but we can't even assume that the addresses we're writing to are + // vector-aligned. +#ifdef memcpy // if someone's overriden the intrinsic, complain +#pragma error("You have overridden memcpy(), which is an XBOX360 intrinsic. This function will not behave optimally.") +#endif + + // if we're full, use compile-time known version of + // mempcy to take advantage of its ability to generate + // inline code. In fact, use the dword-aligned + // version so that we use the 64-bit writing funcs. + Assert( m_wordsGathered == kWordsPerRow ); + COMPILE_TIME_ASSERT((kWordsPerRow & 3) == 0); // the number of words per row has to be a multiple of four + + memcpy(pWriteInto, reinterpret_cast<uint64* RESTRICT>(m_data[0]), kWordsPerRow * sizeof(uint32)); + pWriteInto += m_bytesBetweenWriterRows; + memcpy(pWriteInto, reinterpret_cast<uint64* RESTRICT>(m_data[1]), kWordsPerRow * sizeof(uint32)); + pWriteInto += m_bytesBetweenWriterRows; + memcpy(pWriteInto, reinterpret_cast<uint64* RESTRICT>(m_data[2]), kWordsPerRow * sizeof(uint32)); + pWriteInto += m_bytesBetweenWriterRows; + memcpy(pWriteInto, reinterpret_cast<uint64* RESTRICT>(m_data[3]), kWordsPerRow * sizeof(uint32)); + + pLightmapPixelWriter->SkipBytes(m_wordsGathered * sizeof(uint32)); + m_wordsGathered = 0; + } + + // parameter: space between bump pages in the pixelwriter + CPixelWriterStoreGather(int writerSizeBytes) : m_wordsGathered(0), m_bytesBetweenWriterRows(writerSizeBytes) {}; + +}; +} + + +// this is a function for specifically writing bumped BGRA lightmaps -- in order for it +// to be properly scheduled, I needed to break out the inline functions. Also, +// to make the write-combined memory more efficient (and work around a bug in the +// April 2007 XDK), we need to store-gather our writes on the cache before blasting +// them out to write-combined memory. We can't simply write from the SIMD registers +// into the pixelwriter's data, because the difference between the output rows, +// eg nLightmap0WriterSizeBytes[0], might not be a multiple of 16. Unaligned stores +// to non-cacheable memory cause an alignment exception. +static void BumpedLightmapBitsToPixelWriter_HDRI_BGRA_X360( float* RESTRICT pFloatImage, float * RESTRICT pFloatImageBump1, float * RESTRICT pFloatImageBump2, + float * RESTRICT pFloatImageBump3, int pLightmapSize[2], int pOffsetIntoLightmapPage[2], FloatBitMap_t *pfmOut, + CPixelWriter * RESTRICT m_LightmapPixelWriter) +{ + AssertMsg(m_LightmapPixelWriter->GetPixelSize() == 4, "BGRA format is no longer four bytes long? This is unsupported on 360, and probably immoral as well."); + const int nLightmap0WriterSizeBytes = pLightmapSize[0] * 4 /*m_LightmapPixelWriter->GetPixelSize()*/; + // const int nRewindToNextPixel = -( ( nLightmap0WriterSizeBytes * 3 ) - 4 ); + + // assert that 1 * 4 = 4 + COMPILE_TIME_ASSERT(sizeof( Vector4D ) == sizeof(float) * 4); + + AssertMsg(!pfmOut, "Runtime conversion of lightmaps to files is no longer supported on 360.\n"); + + + // The store-gather buffers. Hopefully these will live in the L1 + // cache, which will make writing to them, then to memory, faster + // than just using __stvewx to write directly into WC memory + // one noncontiguous float at a time. (If there weren't a huge + // compiler bug with __stvewx in the Apr07 XDK, that might not + // be the case.) + CPixelWriterStoreGather storeGather(nLightmap0WriterSizeBytes); + + for( int t = 0; t < pLightmapSize[1]; t++ ) + { +#define FOUR (sizeof( Vector4D ) / sizeof( float )) // make explicit when we're incrementing by length of a 4dvec + int srcTexelOffset = ( FOUR ) * ( 0 + t * pLightmapSize[0] ); + m_LightmapPixelWriter->Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + + // Our code works best when we can process luxels in groups of four. So, + // figure out how many four-luxel groups we can process, + // then do them in groups, then process the remainder. + unsigned int groupsOfFourLimit = (((unsigned int)pLightmapSize[0]) & ~3); + + // we want to hang on to this index when we're done with groups so we can do the remainder. + unsigned int s; // counts the number of luxels processed + for( s = 0; + s < groupsOfFourLimit; + s += 4, srcTexelOffset += 4 * ( FOUR )) + { + static const fltx4 vSixteen = {16.0f, 16.0f, 16.0f, 16.0f}; + // the store-gather simds + fltx4 outBaseMap = Four_Zeros, outBump1 = Four_Zeros, outBump2 = Four_Zeros, outBump3 = Four_Zeros; + // we'll read four at a time + fltx4 vFloatImage[4], vFloatImageBump1[4], vFloatImageBump2[4], vFloatImageBump3[4]; + + + // stripe these loads to cause less ERAT thrashing + vFloatImage[0] = LoadUnalignedSIMD(pFloatImage + srcTexelOffset ); + vFloatImage[1] = LoadUnalignedSIMD(pFloatImage + srcTexelOffset + 4 ); + vFloatImage[2] = LoadUnalignedSIMD(pFloatImage + srcTexelOffset + 8 ); + vFloatImage[3] = LoadUnalignedSIMD(pFloatImage + srcTexelOffset + 12 ); + + vFloatImageBump1[0] = LoadUnalignedSIMD(pFloatImageBump1 + srcTexelOffset ); + vFloatImageBump1[1] = LoadUnalignedSIMD(pFloatImageBump1 + srcTexelOffset + 4 ); + vFloatImageBump1[2] = LoadUnalignedSIMD(pFloatImageBump1 + srcTexelOffset + 8 ); + vFloatImageBump1[3] = LoadUnalignedSIMD(pFloatImageBump1 + srcTexelOffset + 12 ); + + vFloatImageBump2[0] = LoadUnalignedSIMD(pFloatImageBump2 + srcTexelOffset ); + vFloatImageBump2[1] = LoadUnalignedSIMD(pFloatImageBump2 + srcTexelOffset + 4 ); + vFloatImageBump2[2] = LoadUnalignedSIMD(pFloatImageBump2 + srcTexelOffset + 8 ); + vFloatImageBump2[3] = LoadUnalignedSIMD(pFloatImageBump2 + srcTexelOffset + 12 ); + + vFloatImageBump3[0] = LoadUnalignedSIMD(pFloatImageBump3 + srcTexelOffset ); + vFloatImageBump3[1] = LoadUnalignedSIMD(pFloatImageBump3 + srcTexelOffset + 4 ); + vFloatImageBump3[2] = LoadUnalignedSIMD(pFloatImageBump3 + srcTexelOffset + 8 ); + vFloatImageBump3[3] = LoadUnalignedSIMD(pFloatImageBump3 + srcTexelOffset + 12 ); + + // perform an arcane averaging operation upon the bump map values + // (todo: make this not an inline so it will schedule better -- inlining is + // done by the linker, which is too late for operation scheduling) + ColorSpace::LinearToBumpedLightmap( vFloatImage[0], vFloatImageBump1[0], + vFloatImageBump2[0], vFloatImageBump3[0], + // transform "in place": + vFloatImage[0], vFloatImageBump1[0], + vFloatImageBump2[0], vFloatImageBump3[0] ); + ColorSpace::LinearToBumpedLightmap( vFloatImage[1], vFloatImageBump1[1], + vFloatImageBump2[1], vFloatImageBump3[1], + // transform "in place": + vFloatImage[1], vFloatImageBump1[1], + vFloatImageBump2[1], vFloatImageBump3[1] ); + ColorSpace::LinearToBumpedLightmap( vFloatImage[2], vFloatImageBump1[2], + vFloatImageBump2[2], vFloatImageBump3[2], + // transform "in place": + vFloatImage[2], vFloatImageBump1[2], + vFloatImageBump2[2], vFloatImageBump3[2] ); + ColorSpace::LinearToBumpedLightmap( vFloatImage[3], vFloatImageBump1[3], + vFloatImageBump2[3], vFloatImageBump3[3], + // transform "in place": + vFloatImage[3], vFloatImageBump1[3], + vFloatImageBump2[3], vFloatImageBump3[3] ); + + + // convert each color to RGB scaled. + // DO NOT! make this into a for loop. The (April07 XDK) compiler + // in fact DOES NOT unroll them, and will perform very naive + // scheduling if you try. + + // clamp to 0..16 float + vFloatImage[0] = MinSIMD(vFloatImage[0], vSixteen); + vFloatImageBump1[0] = MinSIMD(vFloatImageBump1[0], vSixteen); + vFloatImageBump2[0] = MinSIMD(vFloatImageBump2[0], vSixteen); + vFloatImageBump3[0] = MinSIMD(vFloatImageBump3[0], vSixteen); + + vFloatImage[1] = MinSIMD(vFloatImage[1], vSixteen); + vFloatImageBump1[1] = MinSIMD(vFloatImageBump1[1], vSixteen); + vFloatImageBump2[1] = MinSIMD(vFloatImageBump2[1], vSixteen); + vFloatImageBump3[1] = MinSIMD(vFloatImageBump3[1], vSixteen); + + vFloatImage[2] = MinSIMD(vFloatImage[2], vSixteen); + vFloatImageBump1[2] = MinSIMD(vFloatImageBump1[2], vSixteen); + vFloatImageBump2[2] = MinSIMD(vFloatImageBump2[2], vSixteen); + vFloatImageBump3[2] = MinSIMD(vFloatImageBump3[2], vSixteen); + + vFloatImage[3] = MinSIMD(vFloatImage[3], vSixteen); + vFloatImageBump1[3] = MinSIMD(vFloatImageBump1[3], vSixteen); + vFloatImageBump2[3] = MinSIMD(vFloatImageBump2[3], vSixteen); + vFloatImageBump3[3] = MinSIMD(vFloatImageBump3[3], vSixteen); + + + // compute the scaling factor, place it in w, and + // scale the rest by it. Obliterates whatever was + // already in alpha. + // This code is why it is important to not use a for + // loop: you need to let the compiler keep the value + // on registers (which it can't do if you use a + // variable indexed array) and interleave the + // inlined instructions. + + vFloatImage[0] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImage[0]) ); + vFloatImageBump1[0] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump1[0]) ); + vFloatImageBump2[0] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump2[0]) ); + vFloatImageBump3[0] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump3[0]) ); + + vFloatImage[1] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImage[1]) ); + vFloatImageBump1[1] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump1[1]) ); + vFloatImageBump2[1] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump2[1]) ); + vFloatImageBump3[1] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump3[1]) ); + + vFloatImage[2] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImage[2]) ); + vFloatImageBump1[2] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump1[2]) ); + vFloatImageBump2[2] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump2[2]) ); + vFloatImageBump3[2] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump3[2]) ); + + vFloatImage[3] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImage[3]) ); + vFloatImageBump1[3] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump1[3]) ); + vFloatImageBump2[3] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump2[3]) ); + vFloatImageBump3[3] = PackPixel_BGRA8888( ConvertLightmapColorToRGBScale(vFloatImageBump3[3]) ); + + // Each of the registers above contains one RGBA 32-bit struct + // in their w word. So, combine them such that each of the assignees + // below contains four RGBAs, in xyzw order (big-endian). + + outBaseMap = __vrlimi(outBaseMap, vFloatImage[0], 8, 3 ); // insert into x + outBump1 = __vrlimi(outBump1, vFloatImageBump1[0], 8, 3 ); // insert into x + outBump2 = __vrlimi(outBump2, vFloatImageBump2[0], 8, 3 ); // insert into x + outBump3 = __vrlimi(outBump3, vFloatImageBump3[0], 8, 3 ); // insert into x + + outBaseMap = __vrlimi(outBaseMap, vFloatImage[1], 4, 2 ); // insert into y + outBump1 = __vrlimi(outBump1, vFloatImageBump1[1], 4, 2 ); // insert into y + outBump2 = __vrlimi(outBump2, vFloatImageBump2[1], 4, 2 ); // insert into y + outBump3 = __vrlimi(outBump3, vFloatImageBump3[1], 4, 2 ); // insert into y + + outBaseMap = __vrlimi(outBaseMap, vFloatImage[2], 2, 1 ); // insert into z + outBump1 = __vrlimi(outBump1, vFloatImageBump1[2], 2, 1 ); // insert into z + outBump2 = __vrlimi(outBump2, vFloatImageBump2[2], 2, 1 ); // insert into z + outBump3 = __vrlimi(outBump3, vFloatImageBump3[2], 2, 1 ); // insert into z + + outBaseMap = __vrlimi(outBaseMap, vFloatImage[3], 1, 0 ); // insert into w + outBump1 = __vrlimi(outBump1, vFloatImageBump1[3], 1, 0 ); // insert into w + outBump2 = __vrlimi(outBump2, vFloatImageBump2[3], 1, 0 ); // insert into w + outBump3 = __vrlimi(outBump3, vFloatImageBump3[3], 1, 0 ); // insert into w + + // push the data through the store-gather buffer. + storeGather.write(m_LightmapPixelWriter, outBaseMap, outBump1, outBump2, outBump3); + + } + + // Once here, make sure we've committed any leftover changes, then process + // the remainders singly. + storeGather.commit(m_LightmapPixelWriter); + + for( ; // s is where it should be from the loop above + s < (unsigned int) pLightmapSize[0]; + s++, + // m_LightmapPixelWriter->SkipBytes(nRewindToNextPixel), // now handled by store-gather + srcTexelOffset += ( FOUR )) + { + + static const fltx4 vSixteen = {16.0f, 16.0f, 16.0f, 16.0f}; + fltx4 vColor[4]; + fltx4 vFloatImage = LoadUnalignedSIMD(&pFloatImage[srcTexelOffset]); + fltx4 vFloatImageBump1 = LoadUnalignedSIMD(&pFloatImageBump1[srcTexelOffset]); + fltx4 vFloatImageBump2 = LoadUnalignedSIMD(&pFloatImageBump2[srcTexelOffset]); + fltx4 vFloatImageBump3 = LoadUnalignedSIMD(&pFloatImageBump3[srcTexelOffset]); + + // perform an arcane averaging operation upon the bump map values + ColorSpace::LinearToBumpedLightmap( vFloatImage, + vFloatImageBump1, vFloatImageBump2, + vFloatImageBump3, + vColor[0], vColor[1], vColor[2], vColor[3] ); + + // convert each color to RGB scaled. + // DO NOT! make this into a for loop. The (April07 XDK) compiler + // in fact DOES NOT unroll them, and will perform very naive + // scheduling if you try. + + // clamp to 0..16 float + vColor[0] = MinSIMD(vColor[0], vSixteen); + vColor[1] = MinSIMD(vColor[1], vSixteen); + vColor[2] = MinSIMD(vColor[2], vSixteen); + vColor[3] = MinSIMD(vColor[3], vSixteen); + + // compute the scaling factor, place it in w, and + // scale the rest by it. Obliterates whatever was + // already in alpha. + // This code is why it is important to not use a for + // loop: you need to let the compiler interleave the + // inlined instructions. + vColor[0] = ConvertLightmapColorToRGBScale( vColor[0] ); + vColor[1] = ConvertLightmapColorToRGBScale( vColor[1] ); + vColor[2] = ConvertLightmapColorToRGBScale( vColor[2] ); + vColor[3] = ConvertLightmapColorToRGBScale( vColor[3] ); + + +#ifdef X360_DOUBLECHECK_LIGHTMAPS + unsigned short color[4][4]; + + ColorSpace::LinearToBumpedLightmap( &pFloatImage[srcTexelOffset], + &pFloatImageBump1[srcTexelOffset], &pFloatImageBump2[srcTexelOffset], + &pFloatImageBump3[srcTexelOffset], + color[0], color[1], color[2], color[3] ); + unsigned short alpha = ColorSpace::LinearToUnsignedShort( pFloatImage[srcTexelOffset+3], 16 ); + color[0][3] = color[1][3] = color[2][3] = color[3][3] = alpha; + + if( IsX360() ) + { + for( int i = 0; i != 4; ++i ) + { + Vector4D vRGBScale; + + vRGBScale.x = color[i][0] * (16.0f / 65535.0f); + vRGBScale.y = color[i][1] * (16.0f / 65535.0f); + vRGBScale.z = color[i][2] * (16.0f / 65535.0f); + vRGBScale = ConvertLightmapColorToRGBScale( &vRGBScale.x ); + color[i][0] = RoundFloatToByte( vRGBScale.x * 255.0f ); + color[i][1] = RoundFloatToByte( vRGBScale.y * 255.0f ); + color[i][2] = RoundFloatToByte( vRGBScale.z * 255.0f ); + color[i][3] = RoundFloatToByte( vRGBScale.w * 255.0f ); + } + } + + /* + for (int ii = 0; ii < 4; ++ii) + { + uint32 pack = (PackPixel_BGRA8888( vColor[ii] ).u[3]); + if (color[ii][3] != 0) + Assert( color[ii][0] == (pack & 0xFF0000) >> 16 && + color[ii][1] == (pack & 0xFF00) >> 8 && + color[ii][2] == (pack & 0xFF) && + color[ii][3] == (pack & 0xFF000000) >> 24 ); + } + */ + +#endif + + + vColor[0] = PackPixel_BGRA8888( vColor[0] ); + vColor[1] = PackPixel_BGRA8888( vColor[1] ); + vColor[2] = PackPixel_BGRA8888( vColor[2] ); + vColor[3] = PackPixel_BGRA8888( vColor[3] ); + + storeGather.writeJustW(m_LightmapPixelWriter, vColor[0], vColor[1], vColor[2], vColor[3] ); + + /* // here is the old way of writing pixels: + // now we store-gather this + m_LightmapPixelWriter->WritePixelNoAdvance_BGRA8888( vColor[0] ); + Assert(*reinterpret_cast<unsigned int *>(m_LightmapPixelWriter->GetCurrentPixel()) == PackPixel_BGRA8888( vColor[0] ).u[3] ); + void * RESTRICT pBits = m_LightmapPixelWriter->SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter->WritePixelNoAdvance_BGRA8888( vColor[1], pBits ); + Assert(*reinterpret_cast<unsigned int *>(m_LightmapPixelWriter->GetCurrentPixel()) == PackPixel_BGRA8888( vColor[1] ).u[3] ); + pBits = m_LightmapPixelWriter->SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter->WritePixelNoAdvance_BGRA8888( vColor[2], pBits ); + Assert(*reinterpret_cast<unsigned int *>(m_LightmapPixelWriter->GetCurrentPixel()) == PackPixel_BGRA8888( vColor[2] ).u[3] ); + pBits = m_LightmapPixelWriter->SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter->WritePixelNoAdvance_BGRA8888( vColor[3], pBits ); + Assert(*reinterpret_cast<unsigned int *>(m_LightmapPixelWriter->GetCurrentPixel()) == PackPixel_BGRA8888( vColor[3] ).u[3] ); + + m_LightmapPixelWriter->SkipBytes(nRewindToNextPixel); + */ + } + + storeGather.commit(m_LightmapPixelWriter); + + } +} + +#endif // _X360 + +// write bumped lightmap update to HDR integer lightmap +void CMatLightmaps::BumpedLightmapBitsToPixelWriter_HDRI( float* RESTRICT pFloatImage, float * RESTRICT pFloatImageBump1, float * RESTRICT pFloatImageBump2, + float * RESTRICT pFloatImageBump3, int pLightmapSize[2], int pOffsetIntoLightmapPage[2], FloatBitMap_t *pfmOut ) RESTRICT +{ + const int nLightmapSize0 = pLightmapSize[0]; + const int nLightmap0WriterSizeBytes = nLightmapSize0 * m_LightmapPixelWriter.GetPixelSize(); + const int nRewindToNextPixel = -( ( nLightmap0WriterSizeBytes * 3 ) - m_LightmapPixelWriter.GetPixelSize() ); + + if( m_LightmapPixelWriter.IsUsingFloatFormat() ) + { + AssertMsg(!IsX360(), "Tried to use a floating-point pixel format for lightmaps on 360, which is not supported."); + if (!IsX360()) + { + for( int t = 0; t < pLightmapSize[1]; t++ ) + { + int srcTexelOffset = ( sizeof( Vector4D ) / sizeof( float ) ) * ( 0 + t * nLightmapSize0 ); + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + + for( int s = 0; + s < nLightmapSize0; + s++, m_LightmapPixelWriter.SkipBytes(nRewindToNextPixel),srcTexelOffset += (sizeof(Vector4D)/sizeof(float))) + { + unsigned short color[4][4]; + + ColorSpace::LinearToBumpedLightmap( &pFloatImage[srcTexelOffset], + &pFloatImageBump1[srcTexelOffset], &pFloatImageBump2[srcTexelOffset], + &pFloatImageBump3[srcTexelOffset], + color[0], color[1], color[2], color[3] ); + float alpha = pFloatImage[srcTexelOffset+3]; + Assert( alpha >= 0.0f && alpha <= 1.0f ); + color[0][3] = color[1][3] = color[2][3] = color[3][3] = alpha; + + float toFloat = ( 1.0f / ( float )( 1 << 16 ) ); + + /* // This code is now a can't-happen, because we do not allow float formats on 360. +#if ( defined( USE_32BIT_LIGHTMAPS_ON_360 ) ) + if( IsX360() ) + { + for( int i = 0; i != 4; ++i ) + { + Vector4D vRGBScale; + + vRGBScale.x = color[i][0] * (16.0f / 65535.0f); + vRGBScale.y = color[i][1] * (16.0f / 65535.0f); + vRGBScale.z = color[i][2] * (16.0f / 65535.0f); + vRGBScale = ConvertLightmapColorToRGBScale( &vRGBScale.x ); + color[i][0] = RoundFloatToByte( vRGBScale.x * 255.0f ); + color[i][1] = RoundFloatToByte( vRGBScale.y * 255.0f ); + color[i][2] = RoundFloatToByte( vRGBScale.z * 255.0f ); + color[i][3] = RoundFloatToByte( vRGBScale.w * 255.0f ); + } + + toFloat = ( 1.0f / ( float )( 1 << 8 ) ); + } +#endif + */ + + m_LightmapPixelWriter.WritePixelNoAdvanceF( toFloat * color[0][0], toFloat * color[0][1], toFloat * color[0][2], toFloat * color[0][3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvanceF( toFloat * color[1][0], toFloat * color[1][1], toFloat * color[1][2], toFloat * color[1][3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvanceF( toFloat * color[2][0], toFloat * color[2][1], toFloat * color[2][2], toFloat * color[2][3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvanceF( toFloat * color[3][0], toFloat * color[3][1], toFloat * color[3][2], toFloat * color[3][3] ); + } + } + } + } + else + { +#ifndef X360_USE_SIMD_LIGHTMAP + for( int t = 0; t < pLightmapSize[1]; t++ ) + { + int srcTexelOffset = ( sizeof( Vector4D ) / sizeof( float ) ) * ( 0 + t * nLightmapSize0 ); + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + + for( int s = 0; + s < nLightmapSize0; + s++, m_LightmapPixelWriter.SkipBytes(nRewindToNextPixel),srcTexelOffset += (sizeof(Vector4D)/sizeof(float))) + { + unsigned short color[4][4]; + + ColorSpace::LinearToBumpedLightmap( &pFloatImage[srcTexelOffset], + &pFloatImageBump1[srcTexelOffset], &pFloatImageBump2[srcTexelOffset], + &pFloatImageBump3[srcTexelOffset], + color[0], color[1], color[2], color[3] ); + unsigned short alpha = ColorSpace::LinearToUnsignedShort( pFloatImage[srcTexelOffset+3], 16 ); + color[0][3] = color[1][3] = color[2][3] = color[3][3] = alpha; + +#if ( defined( USE_32BIT_LIGHTMAPS_ON_360 ) ) + if( IsX360() ) + { + for( int i = 0; i != 4; ++i ) + { + Vector4D vRGBScale; + + vRGBScale.x = color[i][0] * (16.0f / 65535.0f); + vRGBScale.y = color[i][1] * (16.0f / 65535.0f); + vRGBScale.z = color[i][2] * (16.0f / 65535.0f); + vRGBScale = ConvertLightmapColorToRGBScale( &vRGBScale.x ); + color[i][0] = RoundFloatToByte( vRGBScale.x * 255.0f ); + color[i][1] = RoundFloatToByte( vRGBScale.y * 255.0f ); + color[i][2] = RoundFloatToByte( vRGBScale.z * 255.0f ); + color[i][3] = RoundFloatToByte( vRGBScale.w * 255.0f ); + } + } +#endif + m_LightmapPixelWriter.WritePixelNoAdvance( color[0][0], color[0][1], color[0][2], color[0][3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( color[1][0], color[1][1], color[1][2], color[1][3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( color[2][0], color[2][1], color[2][2], color[2][3] ); + + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( color[3][0], color[3][1], color[3][2], color[3][3] ); + + // Write data to the bitmapped represenations so that PFM files can be written + if ( pfmOut ) + { + PixRGBAF pixelData; + pixelData.Red = color[0][0]; + pixelData.Green = color[0][1]; + pixelData.Blue = color[0][2]; + pixelData.Alpha = alpha; + pfmOut->WritePixelRGBAF(pOffsetIntoLightmapPage[0] + s, pOffsetIntoLightmapPage[1] + t, pixelData); + } + } + } +#else + // this is an optimized XBOX implementation. For a clearer + // presentation of the algorithm, see the PC implementation + // above. + // First check for the most common case, using an efficient + // branch rather than a switch: + if (m_LightmapPixelWriter.GetFormat() == IMAGE_FORMAT_LINEAR_BGRA8888) + { + // broken out into a static to make things more readable + // and be nicer to the instruction cache + BumpedLightmapBitsToPixelWriter_HDRI_BGRA_X360( pFloatImage, pFloatImageBump1, pFloatImageBump2, + pFloatImageBump3, pLightmapSize, pOffsetIntoLightmapPage, pfmOut, &m_LightmapPixelWriter ); + } + else + { // This case should actually never be hit -- we do not use RGBA. + for( int t = 0; t < pLightmapSize[1]; t++ ) + { + // assert that 1 * 4 = 4 + COMPILE_TIME_ASSERT(sizeof( Vector4D ) == sizeof(float) * 4); +#define FOUR (sizeof( Vector4D ) / sizeof( float )) // in case this ever changes + int srcTexelOffset = ( FOUR ) * ( 0 + t * nLightmapSize0 ); + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + + for( int s = 0; + s < nLightmapSize0; + s++, m_LightmapPixelWriter.SkipBytes(nRewindToNextPixel),srcTexelOffset += ( FOUR )) + { + + static const fltx4 vSixteen = {16.0f, 16.0f, 16.0f, 16.0f}; + fltx4 vColor[4]; + fltx4 vFloatImage = LoadUnalignedSIMD(&pFloatImage[srcTexelOffset]); + fltx4 vFloatImageBump1 = LoadUnalignedSIMD(&pFloatImageBump1[srcTexelOffset]); + fltx4 vFloatImageBump2 = LoadUnalignedSIMD(&pFloatImageBump2[srcTexelOffset]); + fltx4 vFloatImageBump3 = LoadUnalignedSIMD(&pFloatImageBump3[srcTexelOffset]); + + // perform an arcane averaging operation upon the bump map values + ColorSpace::LinearToBumpedLightmap( vFloatImage, + vFloatImageBump1, vFloatImageBump2, + vFloatImageBump3, + vColor[0], vColor[1], vColor[2], vColor[3] ); + + // convert each color to RGB scaled. + // DO NOT! make this into a for loop. The (April07 XDK) compiler + // in fact DOES NOT unroll them, and will perform very naive + // scheduling if you try. + + // clamp to 0..16 float + vColor[0] = MinSIMD(vColor[0], vSixteen); + vColor[1] = MinSIMD(vColor[1], vSixteen); + vColor[2] = MinSIMD(vColor[2], vSixteen); + vColor[3] = MinSIMD(vColor[3], vSixteen); + + // compute the scaling factor, transform the RGB, + // and place the scale in w. Obliterates whatever was + // already in alpha. + // This code is why it is important to not use a for + // loop: you need to let the compiler interleave the + // inlined instructions. + vColor[0] = ConvertLightmapColorToRGBScale( vColor[0] ); + vColor[1] = ConvertLightmapColorToRGBScale( vColor[1] ); + vColor[2] = ConvertLightmapColorToRGBScale( vColor[2] ); + vColor[3] = ConvertLightmapColorToRGBScale( vColor[3] ); + + + m_LightmapPixelWriter.WritePixelNoAdvance( vColor[0] ); + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( vColor[1] ); + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( vColor[2] ); + m_LightmapPixelWriter.SkipBytes( nLightmap0WriterSizeBytes ); + m_LightmapPixelWriter.WritePixelNoAdvance( vColor[3] ); + + AssertMsg(!pfmOut, "Runtime conversion of lightmaps to files is no longer supported on 360.\n"); + + // Write data to the bitmapped represenations so that PFM files can be written + if ( pfmOut ) + { + Warning("**************************************************\n" + "Lightmap output to files on 360 HAS BEEN DISABLED.\n" + "A grave error has just occurred.\n" + "**************************************************\n"); + DebuggerBreakIfDebugging(); + /* + PixRGBAF pixelData; + pixelData.Red = color[0][0]; + pixelData.Green = color[0][1]; + pixelData.Blue = color[0][2]; + pixelData.Alpha = alpha; + pfmOut->WritePixelRGBAF(pOffsetIntoLightmapPage[0] + s, pOffsetIntoLightmapPage[1] + t, pixelData); + */ + } + } + } + } +#endif + } +} + + +void CMatLightmaps::LightmapBitsToPixelWriter_LDR( float* pFloatImage, int pLightmapSize[2], int pOffsetIntoLightmapPage[2], FloatBitMap_t *pfmOut ) +{ + // non-HDR lightmap processing + float *pSrc = pFloatImage; + for( int t = 0; t < pLightmapSize[1]; ++t ) + { + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + for( int s = 0; s < pLightmapSize[0]; ++s, pSrc += (sizeof(Vector4D)/sizeof(*pSrc)) ) + { + unsigned char color[4]; + ColorSpace::LinearToLightmap( color, pSrc ); + color[3] = RoundFloatToByte( pSrc[3] * 255.0f ); + m_LightmapPixelWriter.WritePixel( color[0], color[1], color[2], color[3] ); + + if ( pfmOut ) + { + // Write data to the bitmapped represenations so that PFM files can be written + PixRGBAF pixelData; + pixelData.Red = color[0]; + pixelData.Green = color[1]; + pixelData.Blue = color[2]; + pixelData.Alpha = color[3]; + pfmOut->WritePixelRGBAF( pOffsetIntoLightmapPage[0] + s, pOffsetIntoLightmapPage[1] + t, pixelData ); + } + } + } +} + + +void CMatLightmaps::LightmapBitsToPixelWriter_HDRF( float* pFloatImage, int pLightmapSize[2], int pOffsetIntoLightmapPage[2], FloatBitMap_t *pfmOut ) +{ + if ( IsX360() ) + { + // 360 does not support HDR float + Assert( 0 ); + return; + } + + // float HDR lightmap processing + float *pSrc = pFloatImage; + for ( int t = 0; t < pLightmapSize[1]; ++t ) + { + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + for ( int s = 0; s < pLightmapSize[0]; ++s, pSrc += (sizeof(Vector4D)/sizeof(*pSrc)) ) + { + m_LightmapPixelWriter.WritePixelF( pSrc[0], pSrc[1], pSrc[2], pSrc[3] ); + } + } +} + +// numbers come in on the domain [0..16] +void CMatLightmaps::LightmapBitsToPixelWriter_HDRI( float* RESTRICT pFloatImage, int pLightmapSize[2], int pOffsetIntoLightmapPage[2], FloatBitMap_t * RESTRICT pfmOut ) +{ +#ifndef X360_USE_SIMD_LIGHTMAP + // PC code (and old, pre-SIMD xbox version -- unshippably slow) + if ( m_LightmapPixelWriter.IsUsingFloatFormat() ) + { + // integer HDR lightmap processing + float *pSrc = pFloatImage; + for ( int t = 0; t < pLightmapSize[1]; ++t ) + { + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + for ( int s = 0; s < pLightmapSize[0]; ++s, pSrc += (sizeof(Vector4D)/sizeof(*pSrc)) ) + { + int r, g, b, a; + + r = ColorSpace::LinearFloatToCorrectedShort( pSrc[0] ); + g = ColorSpace::LinearFloatToCorrectedShort( pSrc[1] ); + b = ColorSpace::LinearFloatToCorrectedShort( pSrc[2] ); + a = ColorSpace::LinearToUnsignedShort( pSrc[3], 16 ); + + float toFloat = ( 1.0f / ( float )( 1 << 16 ) ); + +#if ( defined( USE_32BIT_LIGHTMAPS_ON_360 ) ) + if( IsX360() ) + { + Vector4D vRGBScale; + + vRGBScale.x = r * (16.0f / 65535.0f); + vRGBScale.y = g * (16.0f / 65535.0f); + vRGBScale.z = b * (16.0f / 65535.0f); + vRGBScale = ConvertLightmapColorToRGBScale( &vRGBScale.x ); + + r = RoundFloatToByte( vRGBScale.x * 255.0f ); + g = RoundFloatToByte( vRGBScale.y * 255.0f ); + b = RoundFloatToByte( vRGBScale.z * 255.0f ); + a = RoundFloatToByte( vRGBScale.w * 255.0f ); + + toFloat = ( 1.0f / ( float )( 1 << 8 ) ); + } + +#endif + Assert( pSrc[3] >= 0.0f && pSrc[3] <= 1.0f ); + m_LightmapPixelWriter.WritePixelF( r * toFloat, g * toFloat, b * toFloat, pSrc[3] ); + } + } + } + else + { + // integer HDR lightmap processing + float *pSrc = pFloatImage; + for ( int t = 0; t < pLightmapSize[1]; ++t ) + { + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + for ( int s = 0; s < pLightmapSize[0]; ++s, pSrc += (sizeof(Vector4D)/sizeof(*pSrc)) ) + { + int r, g, b, a; + + r = ColorSpace::LinearFloatToCorrectedShort( pSrc[0] ); + g = ColorSpace::LinearFloatToCorrectedShort( pSrc[1] ); + b = ColorSpace::LinearFloatToCorrectedShort( pSrc[2] ); + a = ColorSpace::LinearToUnsignedShort( pSrc[3], 16 ); + +#if ( defined( USE_32BIT_LIGHTMAPS_ON_360 ) ) + if( IsX360() ) + { + Vector4D vRGBScale; + + vRGBScale.x = r * (16.0f / 65535.0f); + vRGBScale.y = g * (16.0f / 65535.0f); + vRGBScale.z = b * (16.0f / 65535.0f); + vRGBScale = ConvertLightmapColorToRGBScale( &vRGBScale.x ); + + r = RoundFloatToByte( vRGBScale.x * 255.0f ); + g = RoundFloatToByte( vRGBScale.y * 255.0f ); + b = RoundFloatToByte( vRGBScale.z * 255.0f ); + a = RoundFloatToByte( vRGBScale.w * 255.0f ); + } +#endif + m_LightmapPixelWriter.WritePixel( r, g, b, a ); + + if ( pfmOut ) + { + // Write data to the bitmapped represenations so that PFM files can be written + PixRGBAF pixelData; + pixelData.Red = pSrc[0]; + pixelData.Green = pSrc[1]; + pixelData.Blue = pSrc[2]; + pixelData.Alpha = pSrc[3]; + pfmOut->WritePixelRGBAF( pOffsetIntoLightmapPage[0] + s, pOffsetIntoLightmapPage[1] + t, pixelData ); + } + } + } + } +#else + // XBOX360 code + if ( m_LightmapPixelWriter.IsUsingFloatFormat() ) + { + if( IsX360() ) + { + AssertMsg( false, "Float-format pixel writers do not exist on x360." ); + } + else + { // This code is here as an example only, in case floating point + // format is restored to 360. + + // integer HDR lightmap processing + float * RESTRICT pSrc = pFloatImage; + for ( int t = 0; t < pLightmapSize[1]; ++t ) + { + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + for ( int s = 0; s < pLightmapSize[0]; ++s, pSrc += (sizeof(Vector4D)/sizeof(*pSrc)) ) + { + int r, g, b, a; + + r = ColorSpace::LinearFloatToCorrectedShort( pSrc[0] ); + g = ColorSpace::LinearFloatToCorrectedShort( pSrc[1] ); + b = ColorSpace::LinearFloatToCorrectedShort( pSrc[2] ); + a = ColorSpace::LinearToUnsignedShort( pSrc[3], 16 ); + + float toFloat = ( 1.0f / ( float )( 1 << 16 ) ); + +#if ( defined( USE_32BIT_LIGHTMAPS_ON_360 ) ) + if( IsX360() ) + { + Vector4D vRGBScale; + + vRGBScale.x = r * (16.0f / 65535.0f); + vRGBScale.y = g * (16.0f / 65535.0f); + vRGBScale.z = b * (16.0f / 65535.0f); + vRGBScale = ConvertLightmapColorToRGBScale( &vRGBScale.x ); + + r = RoundFloatToByte( vRGBScale.x * 255.0f ); + g = RoundFloatToByte( vRGBScale.y * 255.0f ); + b = RoundFloatToByte( vRGBScale.z * 255.0f ); + a = RoundFloatToByte( vRGBScale.w * 255.0f ); + + toFloat = ( 1.0f / ( float )( 1 << 8 ) ); + } + +#endif + Assert( pSrc[3] >= 0.0f && pSrc[3] <= 1.0f ); + m_LightmapPixelWriter.WritePixelF( r * toFloat, g * toFloat, b * toFloat, pSrc[3] ); + } + } + } + } + else + { + // This is the fast X360 pathway. + + // integer HDR lightmap processing + float * RESTRICT pSrc = pFloatImage; + // Assert((reinterpret_cast<unsigned int>(pSrc) & 15) == 0); // 16-byte aligned? + COMPILE_TIME_ASSERT(sizeof(Vector4D)/sizeof(*pSrc) == 4); // assert that 1 * 4 = 4 +#ifndef USE_32BIT_LIGHTMAPS_ON_360 +#pragma error("This function only supports 32 bit lightmaps.") +#endif + + // input numbers from pSrc are on the domain [0..+inf] + // we clamp them to the range [0..16] + // output is RGBA + // the shader does this: rOut = Rin * Ain * 16.0f + // where Rin is [0..1], a float computed from a byte value [0..255] + // Ain is therefore the brightest channel (say R) divided by 16 and quantized + // Rin is computed from pSrc->r by dividing by Ain + + // rather than switching inside WritePixel for each different format, + // thus causing a 23-cycle pipeline clear for every pixel, we'll + // branch on the format here. That will allow us to unroll the inline + // pixel write functions differently depending on their different + // latencies. + + Assert(!pfmOut); // should never happen on 360. +#ifndef ALLOW_PFM_OUTPUT_ON_360 + if ( pfmOut ) + { + Warning("*****************************************\n" + "Lightmap output on 360 HAS BEEN DISABLED.\n" + "A grave error has just occurred.\n" + "*****************************************\n"); + } +#endif + + // switch once, here, outside the loop, rather than + // switching inside each pixel. Switches are not fast + // on x360: they are usually implemented as jumps + // through function tables, which have a 24-cycle + // stall. + switch (m_LightmapPixelWriter.GetFormat()) + { + // note: format names are low-order-byte first. + case IMAGE_FORMAT_RGBA8888: + case IMAGE_FORMAT_LINEAR_RGBA8888: + { + for ( int t = 0; t < pLightmapSize[1]; ++t ) + { + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + for ( int s = 0; s < pLightmapSize[0]; ++s, pSrc += 4 ) + { + static const fltx4 vSixteen = {16.0f, 16.0f, 16.0f, 16.0f}; + fltx4 rgba = LoadUnalignedSIMD(pSrc); + + // clamp to 0..16 float + rgba = MinSIMD(rgba, vSixteen); + // compute the scaling factor, place it in w, and + // scale the rest by it. + rgba = ConvertLightmapColorToRGBScale( rgba ); + // rgba is now float 0..255 in each component + m_LightmapPixelWriter.WritePixelNoAdvance_RGBA8888(rgba); + + + /* // not supported on X360 + if ( pfmOut ) + { + // Write data to the bitmapped represenations so that PFM files can be written + PixRGBAF pixelData; + XMStoreVector4(&pixelData,rgba); + pfmOut->WritePixelRGBAF( pOffsetIntoLightmapPage[0] + s, pOffsetIntoLightmapPage[1] + t, pixelData ); + } + */ + } + } + break; + } + + case IMAGE_FORMAT_BGRA8888: // NOTE! : the low order bits are first in this naming convention. + case IMAGE_FORMAT_LINEAR_BGRA8888: + { + for ( int t = 0; t < pLightmapSize[1]; ++t ) + { + m_LightmapPixelWriter.Seek( pOffsetIntoLightmapPage[0], pOffsetIntoLightmapPage[1] + t ); + for ( int s = 0; s < pLightmapSize[0]; ++s, pSrc += 4 ) + { + static const fltx4 vSixteen = {16.0f, 16.0f, 16.0f, 16.0f}; + fltx4 rgba = LoadUnalignedSIMD(pSrc); + + // clamp to 0..16 float + rgba = MinSIMD(rgba, vSixteen); + // compute the scaling factor, place it in w, and + // scale the rest by it. + rgba = ConvertLightmapColorToRGBScale( rgba ); + // rgba is now float 0..255 in each component + m_LightmapPixelWriter.WritePixelNoAdvance_BGRA8888(rgba); + // forcibly advance + m_LightmapPixelWriter.SkipBytes(4); + + /* // not supported on X360 + if ( pfmOut ) + { + // Write data to the bitmapped represenations so that PFM files can be written + PixRGBAF pixelData; + XMStoreVector4(&pixelData,rgba); + pfmOut->WritePixelRGBAF( pOffsetIntoLightmapPage[0] + s, pOffsetIntoLightmapPage[1] + t, pixelData ); + } + */ + } + } + break; + } + + default: + AssertMsg1(false,"Unsupported pixel format %d while writing lightmaps!", m_LightmapPixelWriter.GetFormat() ); + Warning("Unsupported pixel format used in lightmap. Lightmaps could not be downloaded.\n"); + break; + } + } +#endif +} + +void CMatLightmaps::BeginUpdateLightmaps( void ) +{ + CMatCallQueue *pCallQueue = GetMaterialSystem()->GetRenderContextInternal()->GetCallQueueInternal(); + if ( pCallQueue ) + { + pCallQueue->QueueCall( this, &CMatLightmaps::BeginUpdateLightmaps ); + return; + } + + m_nUpdatingLightmapsStackDepth++; +} + +void CMatLightmaps::EndUpdateLightmaps( void ) +{ + CMatCallQueue *pCallQueue = GetMaterialSystem()->GetRenderContextInternal()->GetCallQueueInternal(); + if ( pCallQueue ) + { + pCallQueue->QueueCall( this, &CMatLightmaps::EndUpdateLightmaps ); + return; + } + + m_nUpdatingLightmapsStackDepth--; + Assert( m_nUpdatingLightmapsStackDepth >= 0 ); + if( m_nUpdatingLightmapsStackDepth <= 0 && m_nLockedLightmap != -1 ) + { + g_pShaderAPI->TexUnlock(); + m_nLockedLightmap = -1; + } +} + +int CMatLightmaps::AllocateDynamicLightmap( int lightmapSize[2], int *pOutOffsetIntoPage, int frameID ) +{ + // check frameID, fail if current + for ( int i = 0; i < COUNT_DYNAMIC_LIGHTMAP_PAGES; i++ ) + { + int dynamicIndex = (m_dynamic.currentDynamicIndex + i) % COUNT_DYNAMIC_LIGHTMAP_PAGES; + int lightmapPageIndex = m_firstDynamicLightmap + dynamicIndex; + if ( m_dynamic.lightmapLockFrame[dynamicIndex] != frameID ) + { + m_dynamic.lightmapLockFrame[dynamicIndex] = frameID; + m_dynamic.imagePackers[dynamicIndex].Reset( 0, m_pLightmapPages[lightmapPageIndex].m_Width, m_pLightmapPages[lightmapPageIndex].m_Height ); + } + + if ( m_dynamic.imagePackers[dynamicIndex].AddBlock( lightmapSize[0], lightmapSize[1], &pOutOffsetIntoPage[0], &pOutOffsetIntoPage[1] ) ) + { + return lightmapPageIndex; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Updates the lightmap +//----------------------------------------------------------------------------- +void CMatLightmaps::UpdateLightmap( int lightmapPageID, int lightmapSize[2], + int offsetIntoLightmapPage[2], + float *pFloatImage, float *pFloatImageBump1, + float *pFloatImageBump2, float *pFloatImageBump3 ) +{ + VPROF( "CMatRenderContext::UpdateLightmap" ); + + bool hasBump = false; + int uSize = 1; + FloatBitMap_t *pfmOut = NULL; + if ( pFloatImageBump1 && pFloatImageBump2 && pFloatImageBump3 ) + { + hasBump = true; + uSize = 4; + } + + if ( lightmapPageID >= GetNumLightmapPages() || lightmapPageID < 0 ) + { + Error( "MaterialSystem_Interface_t::UpdateLightmap lightmapPageID=%d out of range\n", lightmapPageID ); + return; + } + bool bDynamic = IsDynamicLightmap(lightmapPageID); + + if ( bDynamic ) + { + int dynamicIndex = lightmapPageID-m_firstDynamicLightmap; + Assert(dynamicIndex < COUNT_DYNAMIC_LIGHTMAP_PAGES); + m_dynamic.currentDynamicIndex = (dynamicIndex + 1) % COUNT_DYNAMIC_LIGHTMAP_PAGES; + } + + if ( mat_lightmap_pfms.GetBool()) + { + // Allocate and initialize lightmap data that will be written to a PFM file + if (NULL == m_pLightmapDataPtrArray[lightmapPageID]) + { + m_pLightmapDataPtrArray[lightmapPageID] = new FloatBitMap_t(m_pLightmapPages[lightmapPageID].m_Width, m_pLightmapPages[lightmapPageID].m_Height); + m_pLightmapDataPtrArray[lightmapPageID]->Clear(0, 0, 0, 1); + } + pfmOut = m_pLightmapDataPtrArray[lightmapPageID]; + } + + // NOTE: Change how the lock is taking place if you ever change how bumped + // lightmaps are put into the page. Right now, we assume that they're all + // added to the right of the original lightmap. + bool bLockSubRect; + { + VPROF_( "Locking lightmaps", 2, VPROF_BUDGETGROUP_DLIGHT_RENDERING, false, 0 ); // vprof scope + + bLockSubRect = m_nUpdatingLightmapsStackDepth <= 0 && !bDynamic; + if( bLockSubRect ) + { + VPROF_INCREMENT_COUNTER( "lightmap subrect texlock", 1 ); + g_pShaderAPI->ModifyTexture( m_LightmapPageTextureHandles[lightmapPageID] ); + if (!g_pShaderAPI->TexLock( 0, 0, offsetIntoLightmapPage[0], offsetIntoLightmapPage[1], + lightmapSize[0] * uSize, lightmapSize[1], m_LightmapPixelWriter )) + { + return; + } + } + else if( lightmapPageID != m_nLockedLightmap ) + { + if ( !LockLightmap( lightmapPageID ) ) + { + ExecuteNTimes( 10, Warning( "Failed to lock lightmap\n" ) ); + return; + } + } + } + + int subRectOffset[2] = {0,0}; + + { + // account for the part spent in math: + VPROF_( "LightmapBitsToPixelWriter", 2, VPROF_BUDGETGROUP_DLIGHT_RENDERING, false, 0 ); + if ( hasBump ) + { + switch( HardwareConfig()->GetHDRType() ) + { + case HDR_TYPE_NONE: + BumpedLightmapBitsToPixelWriter_LDR( pFloatImage, pFloatImageBump1, pFloatImageBump2, pFloatImageBump3, + lightmapSize, bLockSubRect ? subRectOffset : offsetIntoLightmapPage, pfmOut ); + break; + case HDR_TYPE_INTEGER: + BumpedLightmapBitsToPixelWriter_HDRI( pFloatImage, pFloatImageBump1, pFloatImageBump2, pFloatImageBump3, + lightmapSize, bLockSubRect ? subRectOffset : offsetIntoLightmapPage, pfmOut ); + break; + case HDR_TYPE_FLOAT: + BumpedLightmapBitsToPixelWriter_HDRF( pFloatImage, pFloatImageBump1, pFloatImageBump2, pFloatImageBump3, + lightmapSize, bLockSubRect ? subRectOffset : offsetIntoLightmapPage, pfmOut ); + break; + } + } + else + { + switch ( HardwareConfig()->GetHDRType() ) + { + case HDR_TYPE_NONE: + LightmapBitsToPixelWriter_LDR( pFloatImage, lightmapSize, bLockSubRect ? subRectOffset : offsetIntoLightmapPage, pfmOut ); + break; + + case HDR_TYPE_INTEGER: + LightmapBitsToPixelWriter_HDRI( pFloatImage, lightmapSize, bLockSubRect ? subRectOffset : offsetIntoLightmapPage, pfmOut ); + break; + + case HDR_TYPE_FLOAT: + LightmapBitsToPixelWriter_HDRF( pFloatImage, lightmapSize, bLockSubRect ? subRectOffset : offsetIntoLightmapPage, pfmOut ); + break; + + default: + Assert( 0 ); + break; + } + } + } + + if( bLockSubRect ) + { + VPROF_( "Unlocking Lightmaps", 2, VPROF_BUDGETGROUP_DLIGHT_RENDERING, false, 0 ); + g_pShaderAPI->TexUnlock(); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMatLightmaps::GetNumSortIDs( void ) +{ + return m_numSortIDs; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatLightmaps::ComputeSortInfo( MaterialSystem_SortInfo_t* pInfo, int& sortId, bool alpha ) +{ + int lightmapPageID; + + for ( MaterialHandle_t i = GetMaterialDict()->FirstMaterial(); i != GetMaterialDict()->InvalidMaterial(); i = GetMaterialDict()->NextMaterial(i) ) + { + IMaterialInternal* pMaterial = GetMaterialInternal(i); + + if ( pMaterial->GetMinLightmapPageID() > pMaterial->GetMaxLightmapPageID() ) + { + continue; + } + + // const IMaterialVar *pTransVar = pMaterial->GetMaterialProperty( MATERIAL_PROPERTY_OPACITY ); + // if( ( !alpha && ( pTransVar->GetIntValue() == MATERIAL_TRANSLUCENT ) ) || + // ( alpha && !( pTransVar->GetIntValue() == MATERIAL_TRANSLUCENT ) ) ) + // { + // return true; + // } + + +// Warning( "sort stuff: %s %s\n", material->GetName(), bAlpha ? "alpha" : "not alpha" ); + + // fill in the lightmapped materials + for ( lightmapPageID = pMaterial->GetMinLightmapPageID(); + lightmapPageID <= pMaterial->GetMaxLightmapPageID(); ++lightmapPageID ) + { + pInfo[sortId].material = pMaterial->GetQueueFriendlyVersion(); + pInfo[sortId].lightmapPageID = lightmapPageID; +#if 0 + char buf[128]; + Q_snprintf( buf, sizeof( buf ), "ComputeSortInfo: %s lightmapPageID: %d sortID: %d\n", pMaterial->GetName(), lightmapPageID, sortId ); + OutputDebugString( buf ); +#endif + ++sortId; + } + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatLightmaps::ComputeWhiteLightmappedSortInfo( MaterialSystem_SortInfo_t* pInfo, int& sortId, bool alpha ) +{ + for (MaterialHandle_t i = GetMaterialDict()->FirstMaterial(); i != GetMaterialDict()->InvalidMaterial(); i = GetMaterialDict()->NextMaterial(i) ) + { + IMaterialInternal* pMaterial = GetMaterialInternal(i); + + // fill in the lightmapped materials that are actually used by this level + if( pMaterial->GetNeedsWhiteLightmap() && + ( pMaterial->GetReferenceCount() > 0 ) ) + { + // const IMaterialVar *pTransVar = pMaterial->GetMaterialProperty( MATERIAL_PROPERTY_OPACITY ); + // if( ( !alpha && ( pTransVar->GetIntValue() == MATERIAL_TRANSLUCENT ) ) || + // ( alpha && !( pTransVar->GetIntValue() == MATERIAL_TRANSLUCENT ) ) ) + // { + // return true; + // } + + pInfo[sortId].material = pMaterial->GetQueueFriendlyVersion(); + if( pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ) ) + { + pInfo[sortId].lightmapPageID = MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP; + } + else + { + pInfo[sortId].lightmapPageID = MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE; + } + + sortId++; + } + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatLightmaps::GetSortInfo( MaterialSystem_SortInfo_t *pSortInfoArray ) +{ + // sort non-alpha blended materials first + int sortId = 0; + ComputeSortInfo( pSortInfoArray, sortId, false ); + ComputeWhiteLightmappedSortInfo( pSortInfoArray, sortId, false ); + Assert( m_numSortIDs == sortId ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatLightmaps::EnableLightmapFiltering( bool enabled ) +{ + int i; + for( i = 0; i < GetNumLightmapPages(); i++ ) + { + g_pShaderAPI->ModifyTexture( m_LightmapPageTextureHandles[i] ); + if( enabled ) + { + g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_LINEAR ); + g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_LINEAR ); + } + else + { + g_pShaderAPI->TexMinFilter( SHADER_TEXFILTERMODE_NEAREST ); + g_pShaderAPI->TexMagFilter( SHADER_TEXFILTERMODE_NEAREST ); + } + } +} + + |