//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #if IS_WINDOWS_PC #define WIN32_LEAN_AND_MEAN #include #endif #include "bitmap/texturepacker.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CTexturePacker::CTexturePacker( int texWidth, int texHeight, int pixelGap ) { m_PageWidth = texWidth; m_PageHeight = texHeight; m_PixelGap = pixelGap; Clear(); } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CTexturePacker::~CTexturePacker() { // remove all existing data m_Tree.RemoveAll(); } //----------------------------------------------------------------------------- // Purpose: Resets the tree //----------------------------------------------------------------------------- void CTexturePacker::Clear() { // remove all existing data m_Tree.RemoveAll(); // Add root item, everything else is procedurally generated int rootIndex = m_Tree.InsertChildAfter( m_Tree.InvalidIndex(), m_Tree.InvalidIndex() ); TreeEntry_t &topNode = m_Tree[rootIndex]; topNode.rc.x = 0; topNode.rc.y = 0; topNode.rc.width = m_PageWidth; topNode.rc.height = m_PageHeight; topNode.bInUse = false; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CTexturePacker::IsLeaf( int nodeIndex ) { int leftChildIndex = m_Tree.FirstChild( nodeIndex ); if ( leftChildIndex == m_Tree.InvalidIndex() ) { return true; } else if ( m_Tree.NextSibling( leftChildIndex ) == m_Tree.InvalidIndex() ) { return true; } return false; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CTexturePacker::IsLeftChild( int nodeIndexParent, int nodeIndexChild ) { int leftChildIndex = m_Tree.FirstChild( nodeIndexParent ); if ( leftChildIndex == nodeIndexChild ) { return true; } else { return false; } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CTexturePacker::IsRightChild( int nodeIndexParent, int nodeIndexChild ) { return !IsLeftChild( nodeIndexParent, nodeIndexChild ); } #ifdef _PS3 // Remove some annoying GCC warnings by specializing these template functions to remove a redundant (i>=0) clause template <> inline bool CUtlNTree::IsValidIndex( short unsigned int i ) const { return (i < m_MaxElementIndex); } template <> inline bool CUtlNTree::IsInTree( short unsigned int i ) const { return (i < m_MaxElementIndex) && (InternalNode(i).m_PrevSibling != i); } #endif // _PS3 //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- int CTexturePacker::InsertRect( const Rect_t& texRect, int nodeIndex ) { if ( nodeIndex == -1 ) { nodeIndex = m_Tree.Root(); Assert( nodeIndex != m_Tree.InvalidIndex() ); } if ( !IsLeaf( nodeIndex) ) // not a leaf { // try inserting under left child int leftChildIndex = m_Tree.FirstChild( nodeIndex ); int newIndex = InsertRect( texRect, leftChildIndex ); if ( m_Tree.IsValidIndex( newIndex ) ) { return newIndex; } // no room, insert under right child int rightChildIndex = m_Tree.NextSibling( leftChildIndex ); return InsertRect( texRect, rightChildIndex ); } else { if ( m_Tree[nodeIndex].bInUse ) // there is already a glpyh here { return -1; } int cacheSlotWidth = m_Tree[nodeIndex].rc.width; int cacheSlotHeight = m_Tree[nodeIndex].rc.height; if ( ( texRect.width > cacheSlotWidth ) || ( texRect.height > cacheSlotHeight ) ) { // if this node's box is too small, return return -1; } if ( ( texRect.width == cacheSlotWidth) && ( texRect.height == cacheSlotHeight ) ) { // if we're just right, accept m_Tree[nodeIndex].bInUse = true; return nodeIndex; } // otherwise, gotta split this node and create some kids // decide which way to split int dw = cacheSlotWidth - texRect.width; int dh = cacheSlotHeight - texRect.height; int leftChildIndex; if ( dw > dh ) // split along x { leftChildIndex = m_Tree.InsertChildAfter( nodeIndex, m_Tree.InvalidIndex() ); Assert( IsLeftChild( nodeIndex, leftChildIndex ) ); TreeEntry_t &leftChild = m_Tree[leftChildIndex]; leftChild.rc.x = m_Tree[nodeIndex].rc.x; leftChild.rc.y = m_Tree[nodeIndex].rc.y; leftChild.rc.width = texRect.width; leftChild.rc.height = cacheSlotHeight; leftChild.bInUse = false; int rightChildIndex = m_Tree.InsertChildAfter( nodeIndex, leftChildIndex ); Assert( IsRightChild( nodeIndex, rightChildIndex ) ); TreeEntry_t &rightChild = m_Tree[rightChildIndex]; rightChild.rc.x = m_Tree[nodeIndex].rc.x + texRect.width + m_PixelGap; rightChild.rc.y = m_Tree[nodeIndex].rc.y; rightChild.rc.width = dw - m_PixelGap; rightChild.rc.height = cacheSlotHeight; rightChild.bInUse = false; Assert( m_Tree.Parent( leftChildIndex ) == m_Tree.Parent( rightChildIndex ) ); Assert( m_Tree.Parent( leftChildIndex ) == nodeIndex ); } else // split along y { leftChildIndex = m_Tree.InsertChildAfter( nodeIndex, m_Tree.InvalidIndex() ); Assert( IsLeftChild( nodeIndex, leftChildIndex ) ); TreeEntry_t &leftChild = m_Tree[leftChildIndex]; leftChild.rc.x = m_Tree[nodeIndex].rc.x; leftChild.rc.y = m_Tree[nodeIndex].rc.y; leftChild.rc.width = cacheSlotWidth; leftChild.rc.height = texRect.height; leftChild.bInUse = false; int rightChildIndex = m_Tree.InsertChildAfter( nodeIndex, leftChildIndex ); Assert( IsRightChild( nodeIndex, rightChildIndex ) ); TreeEntry_t &rightChild = m_Tree[rightChildIndex]; rightChild.rc.x = m_Tree[nodeIndex].rc.x; rightChild.rc.y = m_Tree[nodeIndex].rc.y + texRect.height + m_PixelGap; rightChild.rc.width = cacheSlotWidth; rightChild.rc.height = dh - m_PixelGap; rightChild.bInUse = false; Assert( m_Tree.Parent( leftChildIndex ) == m_Tree.Parent( rightChildIndex ) ); Assert( m_Tree.Parent( leftChildIndex ) == nodeIndex ); } // insert into first child we created return InsertRect( texRect, leftChildIndex ); } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CTexturePacker::RemoveRect( int nodeIndex ) { if ( !m_Tree.IsInTree( nodeIndex ) ) { return false; } m_Tree[nodeIndex].bInUse = false; // If its a leaf, see if its peer is empty, if it is the split can go away. if ( IsLeaf( nodeIndex) ) // a leaf { int parentIndex = m_Tree.Parent( nodeIndex ); // Is this node the left child? if ( nodeIndex == m_Tree.FirstChild( parentIndex ) ) { // Get the index of the right child int peerIndex = m_Tree.NextSibling( nodeIndex ); if ( IsLeaf( peerIndex ) && !m_Tree[peerIndex].bInUse ) { // Both children are leaves and neither is in use, remove the split here. m_Tree.Remove( nodeIndex ); m_Tree.Remove( peerIndex ); } } else // Its the right child { // Get the index of the left child int peerIndex = m_Tree.FirstChild( parentIndex ); Assert( nodeIndex == m_Tree.NextSibling( peerIndex ) ); if ( IsLeaf( peerIndex ) && !m_Tree[peerIndex].bInUse ) { // Both children are leaves and neither is in use, remove the split here. m_Tree.Remove( nodeIndex ); m_Tree.Remove( peerIndex ); } } } return true; }