|
|
//=========== Copyright � Valve Corporation, All rights reserved. ============//
//
// Purpose: Mesh class UV parameterization operations.
//
//===========================================================================//
#include "mesh.h"
class CPackNode { public: CPackNode* m_child[2]; Rect_t m_rect; AtlasChart_t* m_pChart;
float m_flTotalW; float m_flTotalH;
public: CPackNode( Rect_t rect, float flTotalW, float flTotalH ); ~CPackNode();
CPackNode *InsertChart( AtlasChart_t* pTexture ); };
class CAtlasPacker { private: int m_nWidth; int m_nHeight;
CPackNode *m_pRootNode;
public: CAtlasPacker(); ~CAtlasPacker();
void Init( int nWidth, int nHeight );
bool InsertChart( AtlasChart_t *pTexture ); };
int SortAtlasCharts( AtlasChart_t* const *pOne, AtlasChart_t* const *pTwo ) { int nSizeOne = MAX( (*pOne)->m_vMaxTextureSize.x, (*pOne)->m_vMaxTextureSize.y ); int nSizeTwo = MAX( (*pTwo)->m_vMaxTextureSize.x, (*pTwo)->m_vMaxTextureSize.y );
if ( nSizeOne < nSizeTwo ) return -1; else if ( nSizeOne > nSizeTwo ) return 1; return 0; }
//--------------------------------------------------------------------------------------
// Pack charts into an atlas. If nAtlasGrow is non-zero, we will attempt to create an atlas
// starting at nAtlasTextureSizeX and growing by nAtlasGrow every time until we eventually
// get an atlas. If nAtlasGrow is 0, then we return the number of charts that didn't
// get atlased.
//--------------------------------------------------------------------------------------
int PackChartsIntoAtlas( AtlasChart_t *pCharts, int nCharts, int nAtlasTextureSizeX, int nAtlasTextureSizeY, int nAtlasGrow ) { // Create a duplicate vector to sort so that the input remains in the same order
CUtlVector<AtlasChart_t*> chartVector; CUtlVector<bool> chartUsed; chartVector.EnsureCount( nCharts ); chartUsed.EnsureCount( nCharts );
for ( int c=0; c<nCharts; ++c ) { chartVector[ c ] = &pCharts[ c ]; chartUsed[ c ] = false; } chartVector.Sort( SortAtlasCharts );
// Try to get the most out of our texture space
bool bTryGrow = ( nAtlasGrow > 0 ); bool bHaveAtlas = false; int nAtlasSizeX = nAtlasTextureSizeX; int nAtlasSizeY = nAtlasTextureSizeY; int nAttempt = 0; int nUnatlased = 0;
while ( !bHaveAtlas ) { Msg( "Atlas Attempt: %d\n", nAttempt ); // assume we have an atlas
bHaveAtlas = true;
// increment and try again
nAtlasSizeX += nAtlasGrow; nAtlasSizeY += nAtlasGrow;
CAtlasPacker m_packer; m_packer.Init( nAtlasSizeX, nAtlasSizeY );
// insert largest first
for ( int t=nCharts-1; t>=0; --t ) { AtlasChart_t *pChart = chartVector[ t ];
if ( !pChart->m_bAtlased ) { if ( m_packer.InsertChart( pChart ) ) { pChart->m_bAtlased = true; } else { if ( bTryGrow ) { bHaveAtlas = false; nAttempt++; break; } else { nUnatlased++; } } } } }
return nUnatlased; }
//CPackNode
CPackNode::CPackNode( Rect_t rect, float flTotalW, float flTotalH ) : m_pChart( NULL ), m_flTotalW( flTotalW ), m_flTotalH( flTotalH ) { m_child[0] = NULL; m_child[1] = NULL; m_rect = rect; }
CPackNode::~CPackNode() { if ( m_child[ 0 ] ) { delete m_child[ 0 ]; } if ( m_child[ 1 ] ) { delete m_child[ 1 ]; } }
CPackNode* CPackNode::InsertChart( AtlasChart_t *pChart ) { int texWidth = (int)ceil( pChart->m_vMaxTextureSize.x ); int texHeight = (int)ceil( pChart->m_vMaxTextureSize.y );
//if we have children, that means we can't insert into this node, try the kids
if ( NULL != m_child[ 0 ] && NULL != m_child[ 1 ] ) { //try the first child
CPackNode* pNewNode = m_child[ 0 ]->InsertChart( pChart ); if(pNewNode) return pNewNode;
//if that didn't work, try the second
pNewNode = m_child[ 1 ]->InsertChart( pChart ); return pNewNode; //if this didn't work it will return NULL
} else //else, see if we can fit it in
{ //if we are a leaf of the tree (m_child[0] and m_child[1] have textures in them,
//then make sure we don't have texture already in here
if ( m_pChart ) return NULL; //if we already have a texture, return NULL
//else, see if we can even fit the lightmap
int width = m_rect.width;// + 1;
int height = m_rect.height;// + 1;
if ( width < texWidth || height < texHeight ) return NULL; //we don't fit!!!
//if we're just the right size, then add the lightmap and we're done
if ( width == texWidth && height == texHeight ) { m_pChart = pChart; //mark this as the texture for the current node
//get the new texture coordinates and put them in the texture
{ m_pChart->m_vAtlasMin.x = m_rect.x / m_flTotalW; m_pChart->m_vAtlasMin.y = m_rect.y / m_flTotalH;
m_pChart->m_vAtlasMax.x = ( m_rect.x + m_rect.width ) / m_flTotalW; m_pChart->m_vAtlasMax.y = ( m_rect.y + m_rect.height ) / m_flTotalH; }
return this; //return us, since we're the right size
}
//if we're not the right size, but we're big enough to hold the lightmap,
//split us up into two nodes
Rect_t rect0,rect1;
int dw = width - texWidth; int dh = height - texHeight;
if( dw > dh ) //split left, right
{ //left rect
rect0.x = m_rect.x; rect0.width = texWidth;// - 1;
rect0.y = m_rect.y; rect0.height = m_rect.height; //right rect
rect1.x = m_rect.x + rect0.width; rect1.width = m_rect.width - rect0.width; rect1.y = m_rect.y; rect1.height = m_rect.height; } else //split up, down
{ //top rect
rect0.x = m_rect.x; rect0.width = m_rect.width; rect0.y = m_rect.y; rect0.height = texHeight;// - 1;
//bottom rect
rect1.x = m_rect.x; rect1.width = m_rect.width; rect1.y = m_rect.y + rect0.height; rect1.height = m_rect.height - rect0.height; } m_child[ 0 ] = new CPackNode( rect0, m_flTotalW, m_flTotalH ); m_child[ 1 ] = new CPackNode( rect1, m_flTotalW, m_flTotalH );
//since we made the first child the size we needed, insert into him.
//this should never fail
return m_child[ 0 ]->InsertChart( pChart ); }
}
//clightmappacker class
CAtlasPacker::CAtlasPacker() { m_nWidth = 0; m_nHeight = 0;
m_pRootNode = NULL; }
CAtlasPacker::~CAtlasPacker() { if ( m_pRootNode ) delete m_pRootNode; }
void CAtlasPacker::Init( int nWidth, int nHeight ) { m_nWidth = nWidth; m_nHeight = nHeight;
Rect_t rect; rect.x = 0; rect.width = nWidth; rect.y = 0; rect.height = nHeight; m_pRootNode = new CPackNode( rect, (float)nWidth, (float)nHeight ); }
bool CAtlasPacker::InsertChart( AtlasChart_t *pChart ) { if( !m_pRootNode ) return false;
CPackNode* pNode = m_pRootNode->InsertChart( pChart );
if(pNode) { return true; }
return false; }
|