//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $Revision: $ // $NoKeywords: $ // // The BSP tree leaf data system // //=============================================================================// #include "basetypes.h" #include "bsptreedata.h" #include "utllinkedlist.h" #include "utlvector.h" #include "tier0/dbg.h" #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // The BSP tree leaf data system //----------------------------------------------------------------------------- class CBSPTreeData : public IBSPTreeData, public ISpatialLeafEnumerator { public: // constructor, destructor CBSPTreeData(); virtual ~CBSPTreeData(); // Methods of IBSPTreeData void Init( ISpatialQuery* pBSPTree ); void Shutdown(); BSPTreeDataHandle_t Insert( int userId, Vector const& mins, Vector const& maxs ); void Remove( BSPTreeDataHandle_t handle ); void ElementMoved( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ); // Enumerate elements in a particular leaf bool EnumerateElementsInLeaf( int leaf, IBSPTreeDataEnumerator* pEnum, int context ); // For convenience, enumerates the leaves along a ray, box, etc. bool EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ); bool EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ); bool EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ); bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ); // methods of IBSPLeafEnumerator bool EnumerateLeaf( int leaf, int context ); // Is the element in any leaves at all? bool IsElementInTree( BSPTreeDataHandle_t handle ) const; private: // Creates a new handle BSPTreeDataHandle_t NewHandle( int userId ); // Adds a handle to the list of handles void AddHandleToLeaf( int leaf, BSPTreeDataHandle_t handle ); // insert, remove handles from leaves void InsertIntoTree( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ); void RemoveFromTree( BSPTreeDataHandle_t handle ); // Returns the number of elements in a leaf int CountElementsInLeaf( int leaf ); private: // All the information associated with a particular handle struct HandleInfo_t { int m_UserId; // Client-defined id unsigned short m_LeafList; // What leafs is it in? }; // The leaf contains an index into a list of elements struct Leaf_t { unsigned short m_FirstElement; }; // The handle knows about the leaves it lies in struct HandleInLeaf_t { int m_Leaf; // what leaf is the handle in? unsigned short m_LeafElementIndex; // what's the m_LeafElements index of the entry? }; // Stores data associated with each leaf. CUtlVector< Leaf_t > m_Leaf; // Stores all unique handles CUtlLinkedList< HandleInfo_t, unsigned short > m_Handles; // Maintains the list of all handles in a particular leaf CUtlLinkedList< BSPTreeDataHandle_t, unsigned short, true > m_LeafElements; // Maintains the list of all leaves a particular handle spans CUtlLinkedList< HandleInLeaf_t, unsigned short, true > m_HandleLeafList; // Interface to BSP tree ISpatialQuery* m_pBSPTree; }; //----------------------------------------------------------------------------- // Class factory //----------------------------------------------------------------------------- IBSPTreeData* CreateBSPTreeData() { return new CBSPTreeData; } void DestroyBSPTreeData( IBSPTreeData* pTreeData ) { if (pTreeData) delete pTreeData; } //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- CBSPTreeData::CBSPTreeData() { } CBSPTreeData::~CBSPTreeData() { } //----------------------------------------------------------------------------- // Level init, shutdown //----------------------------------------------------------------------------- void CBSPTreeData::Init( ISpatialQuery* pBSPTree ) { Assert( pBSPTree ); m_pBSPTree = pBSPTree; m_Handles.EnsureCapacity( 1024 ); m_LeafElements.EnsureCapacity( 1024 ); m_HandleLeafList.EnsureCapacity( 1024 ); // Add all the leaves we'll need int leafCount = m_pBSPTree->LeafCount(); m_Leaf.EnsureCapacity( leafCount ); Leaf_t newLeaf; newLeaf.m_FirstElement = m_LeafElements.InvalidIndex(); while ( --leafCount >= 0 ) { m_Leaf.AddToTail( newLeaf ); } } void CBSPTreeData::Shutdown() { m_Handles.Purge(); m_LeafElements.Purge(); m_HandleLeafList.Purge(); m_Leaf.Purge(); } //----------------------------------------------------------------------------- // Creates a new handle //----------------------------------------------------------------------------- BSPTreeDataHandle_t CBSPTreeData::NewHandle( int userId ) { BSPTreeDataHandle_t handle = m_Handles.AddToTail(); m_Handles[handle].m_UserId = userId; m_Handles[handle].m_LeafList = m_HandleLeafList.InvalidIndex(); return handle; } //----------------------------------------------------------------------------- // Add/remove handle //----------------------------------------------------------------------------- BSPTreeDataHandle_t CBSPTreeData::Insert( int userId, Vector const& mins, Vector const& maxs ) { BSPTreeDataHandle_t handle = NewHandle( userId ); InsertIntoTree( handle, mins, maxs ); return handle; } void CBSPTreeData::Remove( BSPTreeDataHandle_t handle ) { if (!m_Handles.IsValidIndex(handle)) return; RemoveFromTree( handle ); m_Handles.Free( handle ); } //----------------------------------------------------------------------------- // Adds a handle to a leaf //----------------------------------------------------------------------------- void CBSPTreeData::AddHandleToLeaf( int leaf, BSPTreeDataHandle_t handle ) { // Got to a leaf baby! Add the handle to the leaf's list of elements unsigned short leafElement = m_LeafElements.Alloc( true ); if (m_Leaf[leaf].m_FirstElement != m_LeafElements.InvalidIndex() ) m_LeafElements.LinkBefore( m_Leaf[leaf].m_FirstElement, leafElement ); m_Leaf[leaf].m_FirstElement = leafElement; m_LeafElements[leafElement] = handle; // Insert the leaf into the handles's list of leaves unsigned short handleElement = m_HandleLeafList.Alloc( true ); if (m_Handles[handle].m_LeafList != m_HandleLeafList.InvalidIndex() ) m_HandleLeafList.LinkBefore( m_Handles[handle].m_LeafList, handleElement ); m_Handles[handle].m_LeafList = handleElement; m_HandleLeafList[handleElement].m_Leaf = leaf; m_HandleLeafList[handleElement].m_LeafElementIndex = leafElement; } //----------------------------------------------------------------------------- // Inserts an element into the tree //----------------------------------------------------------------------------- bool CBSPTreeData::EnumerateLeaf( int leaf, int context ) { BSPTreeDataHandle_t handle = (BSPTreeDataHandle_t)context; AddHandleToLeaf( leaf, handle ); return true; } void CBSPTreeData::InsertIntoTree( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ) { m_pBSPTree->EnumerateLeavesInBox( mins, maxs, this, handle ); } //----------------------------------------------------------------------------- // Removes an element from the tree //----------------------------------------------------------------------------- void CBSPTreeData::RemoveFromTree( BSPTreeDataHandle_t handle ) { // Iterate over the list of all leaves the handle is in unsigned short i = m_Handles[handle].m_LeafList; while (i != m_HandleLeafList.InvalidIndex()) { int leaf = m_HandleLeafList[i].m_Leaf; unsigned short leafElement = m_HandleLeafList[i].m_LeafElementIndex; // Unhook the handle from the leaf handle list if (leafElement == m_Leaf[leaf].m_FirstElement) m_Leaf[leaf].m_FirstElement = m_LeafElements.Next(leafElement); m_LeafElements.Free(leafElement); unsigned short prevNode = i; i = m_HandleLeafList.Next(i); m_HandleLeafList.Free(prevNode); } m_Handles[handle].m_LeafList = m_HandleLeafList.InvalidIndex(); } //----------------------------------------------------------------------------- // Call this when the element moves //----------------------------------------------------------------------------- void CBSPTreeData::ElementMoved( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ) { if (handle != TREEDATA_INVALID_HANDLE) { RemoveFromTree( handle ); InsertIntoTree( handle, mins, maxs ); } } //----------------------------------------------------------------------------- // Is the element in any leaves at all? //----------------------------------------------------------------------------- bool CBSPTreeData::IsElementInTree( BSPTreeDataHandle_t handle ) const { return m_Handles[handle].m_LeafList != m_HandleLeafList.InvalidIndex(); } //----------------------------------------------------------------------------- // Enumerate elements in a particular leaf //----------------------------------------------------------------------------- int CBSPTreeData::CountElementsInLeaf( int leaf ) { int i; int nCount = 0; for( i = m_Leaf[leaf].m_FirstElement; i != m_LeafElements.InvalidIndex(); i = m_LeafElements.Next(i) ) { ++nCount; } return nCount; } //----------------------------------------------------------------------------- // Enumerate elements in a particular leaf //----------------------------------------------------------------------------- bool CBSPTreeData::EnumerateElementsInLeaf( int leaf, IBSPTreeDataEnumerator* pEnum, int context ) { #ifdef DBGFLAG_ASSERT // The enumeration method better damn well not change this list... int nCount = CountElementsInLeaf(leaf); #endif unsigned short idx = m_Leaf[leaf].m_FirstElement; while (idx != m_LeafElements.InvalidIndex()) { BSPTreeDataHandle_t handle = m_LeafElements[idx]; if (!pEnum->EnumerateElement( m_Handles[handle].m_UserId, context )) { Assert( CountElementsInLeaf(leaf) == nCount ); return false; } idx = m_LeafElements.Next(idx); } Assert( CountElementsInLeaf(leaf) == nCount ); return true; } //----------------------------------------------------------------------------- // For convenience, enumerates the leaves along a ray, box, etc. //----------------------------------------------------------------------------- bool CBSPTreeData::EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ) { return m_pBSPTree->EnumerateLeavesAtPoint( pt, pEnum, context ); } bool CBSPTreeData::EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ) { return m_pBSPTree->EnumerateLeavesInBox( mins, maxs, pEnum, context ); } bool CBSPTreeData::EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ) { return m_pBSPTree->EnumerateLeavesInSphere( center, radius, pEnum, context ); } bool CBSPTreeData::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ) { return m_pBSPTree->EnumerateLeavesAlongRay( ray, pEnum, context ); }