//========= Copyright © Valve Corporation, All rights reserved. ============// #ifndef DYNAMIC_TREE_HDR #define DYNAMIC_TREE_HDR #include "vector.h" #include "aabb.h" #include "utlvector.h" // This class implements a dynamic AABB tree and allows node insertion, removal and updates. // The user needs to provide an AABB on construction and will receive a unique identifier // to update and remove the node later. On construction you can also associate some arbitrary // user data. The tree is build using the SAH and uses AVL rotations for balancing. Nodes are // managed in a free list and no pointers are returned. This allows for fast allocations and // we can still grow the tree. Note that no memory is released destruction. // If you have a large number of proxies and all are moving each frame it is recommend to // insert and inflated AABB and only trigger and update if the original AABB moved out of the // fat AABB in the tree. This approach is very successfully used in the Rubikon broadphase. // Casting: // Ray, sphere and box casting uses a simple callback mechanism. // The callback signature is: float f( pUserData, vRayStart, vRayDelta, flBestT ); // The callback mechanism allows us to implement any-, closest-, and // all hit(s) queries in one function. We expect from the client to // return the following for this to work: // Any: t = 0 (if hit something) // Closest: 0 < t < 1 // All: t = 1 (always) // Queries: // * The dynamic tree supports sphere and box queries to find all proxies intersected by // the specified volume. // * The dynamic tree supports 'closest proxy' queries //-------------------------------------------------------------------------------------------------- // Proxy vector //-------------------------------------------------------------------------------------------------- typedef CUtlVectorFixedGrowable< int32, 512 > CProxyVector; //-------------------------------------------------------------------------------------------------- // Dynamic tree //-------------------------------------------------------------------------------------------------- class CDynamicTree { public: // Construction CDynamicTree(); // Proxy interface int ProxyCount() const; int32 CreateProxy( const AABB_t& bounds, void* pUserData = NULL ); void* DestroyProxy( int32 nProxyId ); void MoveProxy( int32 nProxyId, const AABB_t& bounds ); void* GetUserData( int32 nProxyId ) const; AABB_t GetBounds( int32 nProxyId ) const; // Casting template < typename Functor > void CastRay( const Vector& vRayStart, const Vector &vRayDelta, Functor& callback ) const; template< typename Functor > void CastSphere( const Vector& vRayStart, const Vector& vRayDelta, float flRadius, Functor& callback ) const; template< typename Functor > void CastBox( const Vector& vRayStart, const Vector& vRayDelta, const Vector& vExtent, Functor& callback ) const; // Queries void Query( CProxyVector& proxies, const AABB_t& aabb ) const; void Query( CProxyVector& proxies, const Vector& vCenter, float flRadius ) const; // Returns the distance to the closest proxy; FLT_MAX if no proxies were // found (i.e. your tree is empty) float ClosestProxies( CProxyVector& proxies, const Vector &vQuery ) const; private: // Implementation enum { NULL_NODE = -1, STACK_DEPTH = 64 }; void InsertLeaf( int32 nLeaf ); void RemoveLeaf( int32 nLeaf ); void AdjustAncestors( int32 nNode ); int32 Balance( int32 nNode ); struct Ray_t { Ray_t() {} Ray_t( const Vector& vStart, const Vector& vEnd ) { vOrigin = vStart; vDelta = vEnd - vStart; // Pre-compute inverse vDeltaInv.x = vDelta.x != 0.0f ? 1.0f / vDelta.x : FLT_MAX; vDeltaInv.y = vDelta.y != 0.0f ? 1.0f / vDelta.y : FLT_MAX; vDeltaInv.z = vDelta.z != 0.0f ? 1.0f / vDelta.z : FLT_MAX; } Vector vOrigin; Vector vDelta; Vector vDeltaInv; }; AABB_t Inflate( const AABB_t& aabb, float flExtent ) const; AABB_t Inflate( const AABB_t& aabb, const Vector& vExtent ) const; void ClipRay( const Ray_t& ray, const AABB_t& aabb, float& flMinT, float& flMaxT ) const; struct Node_t { AABB_t m_Bounds; int32 m_nHeight; int32 m_nParent; int32 m_nChild1; int32 m_nChild2; void* m_pUserData; FORCEINLINE bool IsLeaf() const { return m_nChild1 == NULL_NODE; } }; class CNodePool { public: // Construction / Destruction CNodePool(); ~CNodePool(); // Memory management void Clear(); void Reserve( int nCapacity ); int32 Alloc(); void Free( int32 id ); // Accessors FORCEINLINE Node_t& operator[]( int32 id ) { AssertDbg( 0 <= id && id < m_nCapacity ); return m_pObjects[ id ]; } FORCEINLINE const Node_t& operator[]( int32 id ) const { AssertDbg( 0 <= id && id < m_nCapacity ); return m_pObjects[ id ]; } private: int m_nCapacity; Node_t* m_pObjects; int32 m_nNext; }; // Data members int32 m_nRoot; int m_nProxyCount; CNodePool m_NodePool; }; #include "dynamictree.inl" #endif