You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.0 KiB
170 lines
4.0 KiB
5 years ago
|
//========= 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();
|
||
|
}
|
||
|
}
|