diff options
Diffstat (limited to 'materialsystem/imagepacker.cpp')
| -rw-r--r-- | materialsystem/imagepacker.cpp | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/materialsystem/imagepacker.cpp b/materialsystem/imagepacker.cpp new file mode 100644 index 0000000..882639f --- /dev/null +++ b/materialsystem/imagepacker.cpp @@ -0,0 +1,169 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "imagepacker.h" +#include "materialsystem_global.h" +#include "IHardwareConfigInternal.h" + +// NOTE: This has to be the last file included +#include "tier0/memdbgon.h" + +float CImagePacker::GetEfficiency( void ) +{ + return ( float )m_AreaUsed / ( float )( m_MaxLightmapWidth * CeilPow2( m_MinimumHeight ) ); +} + +bool CImagePacker::Reset( int nSortId, int maxLightmapWidth, int maxLightmapHeight ) +{ + int i; + + Assert( maxLightmapWidth <= MAX_MAX_LIGHTMAP_WIDTH ); + + m_MaxLightmapWidth = maxLightmapWidth; + m_MaxLightmapHeight = maxLightmapHeight; + + m_MaxBlockWidth = maxLightmapWidth + 1; + m_MaxBlockHeight = maxLightmapHeight + 1; + + m_nSortID = nSortId; + + m_AreaUsed = 0; + m_MinimumHeight = -1; + for( i = 0; i < m_MaxLightmapWidth; i++ ) + { + m_pLightmapWavefront[i] = -1; + } + return true; +} + +inline int CImagePacker::GetMaxYIndex( int firstX, int width ) +{ + int maxY = -1; + int maxYIndex = 0; + for( int x = firstX; x < firstX + width; ++x ) + { + // NOTE: Want the equals here since we'll never be able to fit + // in between the multiple instances of maxY + if( m_pLightmapWavefront[x] >= maxY ) + { + maxY = m_pLightmapWavefront[x]; + maxYIndex = x; + } + } + return maxYIndex; +} + +//#define ADD_ONE_TEXEL_BORDER + +bool CImagePacker::AddBlock( int width, int height, + int *returnX, int *returnY ) +{ +#ifdef ADD_ONE_TEXEL_BORDER + width += 2; + height += 2; + width = clamp( width, m_MaxLightmapWidth ); + height = clamp( height, m_MaxLightmapHeight ); +#endif + + // If we've already determined that a block this big couldn't fit + // then blow off checking again... + if ( ( width >= m_MaxBlockWidth ) && ( height >= m_MaxBlockHeight ) ) + return false; + + int bestX = -1; + int maxYIdx; + int outerX = 0; + int outerMinY = m_MaxLightmapHeight; + int lastX = m_MaxLightmapWidth - width; + int lastMaxYVal = -2; + while (outerX <= lastX) + { + // Skip all tiles that have the last Y value, these + // aren't going to change our min Y value + if (m_pLightmapWavefront[outerX] == lastMaxYVal) + { + ++outerX; + continue; + } + + maxYIdx = GetMaxYIndex( outerX, width ); + lastMaxYVal = m_pLightmapWavefront[maxYIdx]; + if (outerMinY > lastMaxYVal) + { + outerMinY = lastMaxYVal; + bestX = outerX; + + // Early out for the first row... +// if (outerMinY == -1) +// break; + } + outerX = maxYIdx + 1; + } + + if( bestX == -1 ) + { + // If we failed to add it, remember the block size that failed + // *only if both dimensions are smaller*!! + // Just because a 1x10 block failed, doesn't mean a 10x1 block will fail + if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) ) + { + m_MaxBlockWidth = width; + m_MaxBlockHeight = height; + } + + return false; + } + + // Set the return positions for the block. + *returnX = bestX; + *returnY = outerMinY + 1; + + // Check if it actually fit height-wise. + // hack + // if( *returnY + height > maxLightmapHeight ) + if( *returnY + height >= m_MaxLightmapHeight - 1 ) + { + if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) ) + { + m_MaxBlockWidth = width; + m_MaxBlockHeight = height; + } + + return false; + } + + // It fit! + // Keep up with the smallest possible size for the image so far. + if( *returnY + height > m_MinimumHeight ) + m_MinimumHeight = *returnY + height; + + // Update the wavefront info. + int x; + for( x = bestX; x < bestX + width; x++ ) + { + m_pLightmapWavefront[x] = outerMinY + height; + } + + // AddBlockToLightmapImage( *returnX, *returnY, width, height ); + m_AreaUsed += width * height; +#ifdef ADD_ONE_TEXEL_BORDER + *returnX++; + *returnY++; +#endif + return true; +} + +void CImagePacker::GetMinimumDimensions( int *pReturnWidth, int *pReturnHeight ) +{ + *pReturnWidth = CeilPow2( m_MaxLightmapWidth ); + *pReturnHeight = CeilPow2( m_MinimumHeight ); + + int aspect = *pReturnWidth / *pReturnHeight; + if (aspect > HardwareConfig()->MaxTextureAspectRatio()) + { + *pReturnHeight = *pReturnWidth / HardwareConfig()->MaxTextureAspectRatio(); + } +} |