Counter Strike : Global Offensive Source Code
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.

1076 lines
43 KiB

  1. //=================================================================================================//
  2. // filename: utlspheretree.h //
  3. // //
  4. // description: A binary tree of spheres - put stuff in it to do fast spatial queries //
  5. // //
  6. //=================================================================================================//
  7. #ifndef _utlspheretree_H_
  8. #define _utlspheretree_H_
  9. #include "mathlib/vector4d.h"
  10. #include "tier1/utlvector.h"
  11. #include "collisionutils.h"
  12. typedef Vector4D Sphere_t;
  13. typedef Vector4D Plane_t;
  14. // Paste the following commented text into your autoexp.dat to get a nifty debugger visualizer for CUtlSphereTree.
  15. // (insert it below [Visualizer] or ";-------------VALVE AUTOEXP AUTOGENERATED BLOCK START----------------" )
  16. // NOTE: you need to define DEBUGGABLE_NODES (on by default in debug) and rebuild to get the full benefit of this.
  17. // This does bog down VS2005 if you expand many levels of the tree - paste �tree.m_Nodes.m_pElements[node]� to sidestep that.
  18. // Also note that names are CASE-INSENSITIVE, so if you have a member 'index' and a function 'Index', it will break :o/
  19. #ifdef _DEBUG
  20. #define DEBUGGABLE_NODES
  21. #endif
  22. /*
  23. ;-------- CUtlSphereTree start ------------------------------------------------------
  24. Vector4D{
  25. preview
  26. ( #( "(", $c.x, ", ", $c.y, ", ", $c.z, ", ", $c.w, ")" ) )
  27. }
  28. CUtlSphereTree::Node{
  29. preview
  30. ( #(
  31. #if ( $c.deepIndex < 0 )
  32. ( #( "leaf data = ", $c.pData, ", bounds(", $c.bounds.x, ", ", $c.bounds.y, ", ", $c.bounds.z, ", ", $c.bounds.w, ")" ) )
  33. #else
  34. ( #( "depth = ", $c.maxDepth, ", bounds(", $c.bounds.x, ", ", $c.bounds.y, ", ", $c.bounds.z, ", ", $c.bounds.w, ")" ) )
  35. ) )
  36. children
  37. ( #(
  38. #if ( $c.deepIndex < 0 )
  39. ( #(
  40. #( leaf data : $c.pData )
  41. ) )
  42. #else
  43. ( #(
  44. #( child[deep] : $c.pTree->m_Nodes.m_pElements[ $c.deepIndex ] ),
  45. #( child[shallow] : $c.pTree->m_Nodes.m_pElements[ $c.shallowIndex ] ),
  46. #( subtree depth : $c.maxDepth )
  47. ) ),
  48. #( node bounds : $c.bounds ),
  49. #( z[raw_members] : [$c,!] )
  50. ) )
  51. }
  52. CUtlSphereTree::NodeRef{
  53. preview
  54. ( #(
  55. #if ( $c.nodeIndex < 0 )
  56. ( #( "Invalid!" ) )
  57. #else
  58. ( #(
  59. #if ( $c.pNodes->m_pElements[ $c.nodeIndex ].deepIndex < 0 )
  60. ( #( "leaf data = ", $c.pNodes->m_pElements[ $c.nodeIndex ].pData, ", bounds(", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.x, ", ", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.y, ", ", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.z, ", ", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.w, ")" ) )
  61. #else
  62. ( #( "depth = ", $c.pNodes->m_pElements[ $c.nodeIndex ].maxDepth, ", bounds(", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.x, ", ", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.y, ", ", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.z, ", ", $c.pNodes->m_pElements[ $c.nodeIndex ].bounds.w, ")" ) )
  63. ) )
  64. ) )
  65. children
  66. ( #(
  67. #if ( $c.nodeIndex < 0 )
  68. ( #( invalid! : $c.nodeIndex ) )
  69. #else
  70. ( #( node : $c.pNodes->m_pElements[ $c.nodeIndex ] ) ),
  71. #( z[raw_members] : [$c,!] )
  72. ) )
  73. }
  74. CUtlSphereTree{
  75. preview
  76. ( #( "treeCount = ", $c.m_NumNodesInTree, ", allocCount = ", $c.m_Nodes.m_Size, ", freelistHead = ", $c.m_FreelistHead ) )
  77. children
  78. ( #(
  79. deep child : $c.m_Nodes.m_pElements[ $c.m_Nodes.m_pElements[0].deepIndex ],
  80. shallow child : $c.m_Nodes.m_pElements[ $c.m_Nodes.m_pElements[0].shallowIndex ],
  81. z[raw_members] : [$c,!]
  82. ) )
  83. }
  84. ;-------- CUtlSphereTree end ------------------------------------------------------
  85. */
  86. class CUtlSphereTree
  87. {
  88. struct Node
  89. {
  90. // Every node either has two children or (if it is a leaf) none
  91. // TODO: shrink the size of Node (pack child node indices and isLeaf flags into an int32, with leaf nodes in a separate vector so we can index 2^16 nodes total)
  92. // TODO: for the case where the caller's data already contains a bounding Sphere_t, we could omit the 20-byte Sphere_t from leaf nodes
  93. // (the void * would need wrapping with a user-provided class that provides a bounding sphere accessor function - or, just provide a pointer to the Sphere_t)
  94. // TODO: determine whether nodes fully containing other nodes happens a lot in practise - if so, it may be worthwhile allowing non-leaf Nodes to hold a pData pointer
  95. Sphere_t bounds;
  96. int deepIndex, shallowIndex;// Not set (-1) for Leaf nodes
  97. const void *pData; // Only set for Leaf nodes
  98. int maxDepth; // The maximum number of steps to a leaf node from here
  99. // (zero for leaf nodes), used to rebalance the tree
  100. #ifdef DEBUGGABLE_NODES
  101. CUtlSphereTree *pTree; // Allows debug pane tree expansion
  102. #endif
  103. };
  104. struct NodeRef
  105. {
  106. // Convenience class to allow access to nodes, stored in the m_Nodes vector, by index
  107. NodeRef( int node, const CUtlVector< Node > &nodes )
  108. : nodeIndex( node ), pNodes( (CUtlVector< Node > *)&nodes ) {}
  109. Node * Ptr( void ) const
  110. {
  111. Assert ( nodeIndex >= 0 );
  112. return ( nodeIndex >= 0 ) ? &(*pNodes)[ nodeIndex ] : (Node *)NULL;
  113. }
  114. Node * operator->( void ) const { return Ptr(); }
  115. Node & operator*( void ) const { return *Ptr(); }
  116. int Index( void ) const { return nodeIndex; }
  117. NodeRef Deep( void ) const { return NodeRef( Ptr()->deepIndex, *pNodes ); }
  118. NodeRef Shallow( void ) const { return NodeRef( Ptr()->shallowIndex, *pNodes ); }
  119. bool IsValid( void ) const { return ( nodeIndex >= 0 ); }
  120. bool IsLeaf( void ) const { return ( Ptr()->deepIndex < 0 ); }
  121. private:
  122. NodeRef( void ) : nodeIndex( -1 ), pNodes( NULL ) { Assert( 0 ); }
  123. int nodeIndex;
  124. CUtlVector< Node > * pNodes; // TODO: find a way to get rid of this, given it's the same for all NodeRefs
  125. };
  126. public:
  127. // Cut
  128. //
  129. // This member class represents a 'cut' of a sphere tree - which is the result of a depth-first traversal
  130. // of the tree in which each node is run through a test function that may return "true", "false" or "partial".
  131. //
  132. // For example:
  133. // - a sphere tree can be 'cut' by a plane
  134. // - the cut is initialised to contain the whole tree
  135. // - the test function is a sphere-plane test
  136. // o nodes which return 'true' (fully in front) are included in the cut and not traversed further
  137. // o nodes which return 'false' (fully behind) are discarded from the cut and not traversed further
  138. // o for nodes which return 'partial' (intersecting) they are discarded and the test recurses to their children
  139. // partial leaf nodes are included in the cut
  140. // - the result (the 'cut') is thus the set of nodes fully/partially in front of the plane,
  141. // subdivided only where necessary to make the result more accurate
  142. //
  143. // In practise, the Cut contains an (unordered) list of sub-tree root node indices.
  144. class Cut
  145. {
  146. friend class CUtlSphereTree;
  147. public:
  148. // Flags to guide how a Cut is to be refined by a set of intersection primitives
  149. enum CutFlags
  150. {
  151. PARTIAL_INTERSECTIONS = 1, // 'Partial' node intersections should be counted
  152. // (for planes, this means include nodes only partially in front of a plane)
  153. OR_INTERSECTIONS = 2 // Passing the intersection test for ANY primitive in the current set constitutes
  154. // a pass ('AND' mode is the default - it requires passing w.r.t ALL primitives)
  155. };
  156. enum CutTestResult
  157. {
  158. CUT_TEST_FAIL = 0,
  159. CUT_TEST_PASS = 1,
  160. CUT_TEST_PARTIAL = 2
  161. };
  162. // Initialize a Cut with an entire sphere tree (i.e. the root node)
  163. Cut( const CUtlSphereTree *pTree );
  164. ~Cut( void );
  165. // Return the user data pointers of the leaf nodes within the Cut. To limit the number of results returned,
  166. // pass 'maxResults > 0' (note that the return value may be more than this).
  167. // This is an efficient way to find all the leaves inside/outside a set of planes.
  168. int GetLeaves( CUtlVector< void * > &leaves, int maxLeaves = 0 ) const;
  169. private:
  170. // Don't use the default constructor
  171. Cut( void ) : m_pTree( NULL ) { Assert( 0 ); };
  172. const CUtlSphereTree *m_pTree; // The associated sphere tree
  173. CUtlVector< int >m_NodeRefs; // Sub-tree root node indices
  174. };
  175. public:
  176. CUtlSphereTree();
  177. virtual ~CUtlSphereTree();
  178. // It is valid to Insert a NULL pData pointer, and it is valid to pass the same pData pointer with
  179. // multiple different bounding spheres, but it is NOT valid to pass the same pointer+sphere pair twice.
  180. // TODO: we can build a better tree if given all the spheres at once (would definitely be a win for non-dynamic scenes)
  181. void Insert( const void *pData, const Sphere_t *bounds );
  182. // Inverse of Insert (make sure to pass the same parameters!)
  183. void Remove( const void *pData, const Sphere_t *bounds );
  184. // Empty the tree (but do not deallocate)
  185. void RemoveAll( void );
  186. // Empty the tree (and deallocate)
  187. void Purge( void );
  188. // Intersect a ray with the spheretree (or a Cut thereof), returning the user data pointers of the leaf nodes intersecting
  189. // the ray. To limit the number of results returned, pass 'maxResults > 0' (note that the return value may be more than this).
  190. // NOTE: these spheres are currently unordered
  191. // TODO: write a function to return the first intersection and an ordered list of intersections (use a priority queue of nodes,
  192. // always processing the top element and emitting leaves - time this and compare it with just quicksorting the results)
  193. int IntersectWithRay( Vector &rayStart, Vector &rayDelta, CUtlVector< void * > &result, int maxResults = 0, CUtlSphereTree::Cut *cut = NULL ) const;
  194. // Intersect a sphere with the spheretree (or a Cut thereof), returning the user data pointers of the leaf nodes intersecting
  195. // the sphere. To limit the number of results returned, pass 'maxResults > 0' (note that the return value may be more than this).
  196. // If 'bPartial' is FALSE, only leaves fully contained within the sphere are returned (otherwise partial overlaps are returned too)
  197. // NOTE: these spheres are currently unordered
  198. // TODO: optionally sort by distance from the sphere centre
  199. int IntersectWithSphere( const Sphere_t &sphere, bool bPartial, CUtlVector< void * > &result, int maxResults = 0, CUtlSphereTree::Cut *cut = NULL ) const;
  200. // Cut the tree (or refine an existing cut) using one or more planes. The output Cut will contain all
  201. // sphere tree nodes which are partially/fully in front of any/all of the planes (see CUtlSphereTree_Cut::CutFlags).
  202. // If outputCut is NULL, then inputCut will be updated.
  203. void CutByPlanes( Cut *outputCut, const Cut *inputCut, int cutFlags, Plane_t *planes, int numPlanes = 1 ) const;
  204. // Use this method to measure the effect of any changes to tree-construction logic. It computes some numbers relating to the efficiency of the tree:
  205. // - averageDepth: the tree's depth, as compared to the depth of a 'perfect' tree (idealDepth)
  206. // - averageRedundancy: the average 'redundancy' (volume ratio) between a node and its parent - this is in [0,1] and correlates to query cost (lower is better, less than 0.4 is good).
  207. // - averageSphereTestCost: estimated average cost of intersecting a sphere (of a given radius) with the tree (uses the redundancy values to compute intersection probabilities)
  208. void MeasureTreeQuality( float &averageDepth, float &idealDepth, float &averageRedundancy, float &averageSphereTestCost, float sphereTestRadius = 0.0f ) const;
  209. private:
  210. // To avoid node/index-shuffling, we add Removed Nodes to a freelist:
  211. void FreelistPush( NodeRef &node );
  212. NodeRef FreelistPop( void );
  213. // We can't hold pointers to Nodes within a CUtlVector, so this util func converts from an index to a NodeRef:
  214. NodeRef Ref( int node ) const { return NodeRef( node, m_Nodes ); }
  215. // Allocates a new node off m_Nodes and init it as a leaf
  216. NodeRef NewNode( const void *pData, const Sphere_t *bounds );
  217. // Allocates a new node off m_Nodes and init it by copying an existing node
  218. NodeRef CopyNode( const NodeRef &nodeToCopy );
  219. // Assign two children to a node (recomputes its 'bounds' and 'maxDepth')
  220. void SetNodeChildren( NodeRef &node, const NodeRef &childA, const NodeRef &childB );
  221. // Recursive function called by Insert()
  222. void FindPathToClosestLeaf_R( NodeRef node, const Vector &pos, const float nodeDist, float &minDist, CUtlVector< int > &path ) const;
  223. // Recursive function called by Remove()
  224. int Remove_R( NodeRef &node, const void *pData, const Sphere_t *bounds );
  225. // Called by Remove_R to identify the Node to remove:
  226. bool NodeHasData( const NodeRef &node, const void *pData, const Sphere_t *bounds );
  227. // Called by Insert() and Remove() to ensure that the subtrees under a node are balanced
  228. void RebalanceSubtrees( NodeRef &node, bool bInserting );
  229. // Is this node's sub-tree balanced? (left and right sub-trees differ in 'maxDepth' by at most 1)
  230. bool IsSubtreeBalanced( const NodeRef &node ) const;
  231. // Useful debug function if there's a bug in tree construction
  232. void ParanoidBalanceCheck_R( const NodeRef &node ) const;
  233. // The basic metric used during insertion to determine the 'cost' of different node-grouping choices
  234. float Cost( const Sphere_t &sphere ) const;
  235. // Computes the composite bounding sphere of two spheres
  236. Sphere_t AddSpheres( const Sphere_t &pA, const Sphere_t &pB ) const;
  237. // Called by RebalanceSubtrees() to determine the optimal rebalance permutation
  238. float ComputePairingCost( const NodeRef &pair1A, const NodeRef &pair1B, const NodeRef &pair2A, const NodeRef *pair2B ) const;
  239. // Called by IntersectWithRay
  240. void IntersectWithRay_R( const NodeRef &node, Vector &rayStart, Vector &rayDelta, CUtlVector< void * > &result, int &count ) const;
  241. // Called by IntersectWithSphere
  242. void IntersectWithSphere_R( const NodeRef &node, const Sphere_t &sphere, const bool bPartial, CUtlVector< void * > &result, int &count, const float distSq ) const;
  243. bool SpheresIntersect( const Sphere_t &A, const Sphere_t &B, float &distSq ) const;
  244. // Called by CutByPlanes
  245. void CutByPlanes_R( Cut *outputCut, const NodeRef &node, int cutFlags, Plane_t *planes, int numPlanes ) const;
  246. Cut::CutTestResult CutByPlanes_Test( const NodeRef &node, int cutFlags, Plane_t *planes, int numPlanes ) const;
  247. // Leaf node list accessor for Cut::GetLeaves:
  248. void GetLeavesUnderNode_R( const NodeRef &node, CUtlVector< void * > &leaves, int &count ) const;
  249. // Called by MeasureTreeQuality
  250. void MeasureTreeQuality_R( const NodeRef &node, const int steps, float &averageDepth, float &averageRedundancy, const float pathRedundancy, float &averageSphereTestCost, const float pathProbability, const float sphereTestRadius ) const;
  251. float ComputeRedundancy( const NodeRef &parent, const NodeRef &child ) const;
  252. float ComputeIntersectionProbability( const NodeRef &parent, const NodeRef &child, const float sphereTestRadius ) const;
  253. // We reference Nodes in this vector by index (the root is always at index zero)
  254. CUtlVector< Node > m_Nodes;
  255. // Removed Nodes are added to a freelist so we don't need to do any index-shuffling
  256. int m_FreelistHead;
  257. // How many nodes are in the tree? (i.e. in m_Nodes but not the freelist)
  258. int m_NumNodesInTree;
  259. // The index of the last inserted node (should always be a leaf, -1 if invalid):
  260. int m_PrevInsertedNode;
  261. };
  262. // ===== CUtlSphereTree implementation =====================================
  263. inline CUtlSphereTree::CUtlSphereTree( void )
  264. {
  265. Purge();
  266. }
  267. inline CUtlSphereTree::~CUtlSphereTree( void )
  268. {
  269. }
  270. inline void CUtlSphereTree::RemoveAll( void )
  271. {
  272. // Empty m_Nodes and our freelist
  273. m_Nodes.RemoveAll();
  274. m_FreelistHead = -1;
  275. m_NumNodesInTree = 0;
  276. m_PrevInsertedNode = -1;
  277. }
  278. inline void CUtlSphereTree::Purge( void )
  279. {
  280. // Empty m_Nodes and our freelist (and deallocate memory)
  281. RemoveAll();
  282. m_Nodes.Purge();
  283. }
  284. inline void CUtlSphereTree::FreelistPush( NodeRef &node )
  285. {
  286. // TODO: update the freelist to use a FreeNodeRef struct which can be traversed with a Debug Visualizer
  287. if ( m_PrevInsertedNode == node.Index() )
  288. m_PrevInsertedNode = -1;
  289. *(int *)node.Ptr() = m_FreelistHead;
  290. m_FreelistHead = node.Index();
  291. Assert( m_NumNodesInTree > 0 );
  292. m_NumNodesInTree--;
  293. }
  294. inline CUtlSphereTree::NodeRef CUtlSphereTree::FreelistPop( void )
  295. {
  296. Assert( m_FreelistHead >= 0 );
  297. NodeRef node = Ref( m_FreelistHead );
  298. if ( m_FreelistHead >= 0 )
  299. m_FreelistHead = *(int *)&m_Nodes[ m_FreelistHead ];
  300. return node;
  301. }
  302. inline CUtlSphereTree::NodeRef CUtlSphereTree::NewNode( const void *pData, const Sphere_t *bounds )
  303. {
  304. // Allocate a new node off m_Nodes (from our freelist if available) and init it as a leaf
  305. Assert( ( m_NumNodesInTree >= 0 ) && ( m_NumNodesInTree <= m_Nodes.Count() ) );
  306. Assert( ( m_NumNodesInTree < m_Nodes.Count() ) == ( m_FreelistHead >= 0 ) );
  307. NodeRef node = ( m_FreelistHead >= 0 ) ? FreelistPop() : Ref( m_Nodes.AddToTail() );
  308. m_NumNodesInTree++;
  309. node->bounds = bounds ? *bounds : Sphere_t(0,0,0,0);
  310. node->deepIndex = -1;
  311. node->shallowIndex = -1;
  312. node->pData = pData;
  313. node->maxDepth = 0;
  314. #ifdef DEBUGGABLE_NODES
  315. node->pTree = this;
  316. #endif
  317. return node;
  318. }
  319. inline CUtlSphereTree::NodeRef CUtlSphereTree::CopyNode( const NodeRef &nodeToCopy )
  320. {
  321. // Allocate a new node off m_Nodes and init it by copying an existing node
  322. NodeRef node = NewNode( NULL, NULL );
  323. *node = *nodeToCopy;
  324. return node;
  325. }
  326. inline void CUtlSphereTree::SetNodeChildren( NodeRef &node, const NodeRef &childA, const NodeRef &childB )
  327. {
  328. bool bChildADeeper = ( childA->maxDepth > childB->maxDepth );
  329. node->deepIndex = bChildADeeper ? childA.Index() : childB.Index();
  330. node->shallowIndex = bChildADeeper ? childB.Index() : childA.Index();
  331. node->bounds = AddSpheres( node.Deep()->bounds, node.Shallow()->bounds );
  332. node->maxDepth = node.Deep()->maxDepth + 1;
  333. node->pData = NULL; // If it was a leaf, it's not any more
  334. }
  335. inline void CUtlSphereTree::Insert( const void *pData, const Sphere_t *bounds )
  336. {
  337. if ( !m_NumNodesInTree )
  338. {
  339. // Special case: first node inserted
  340. NodeRef root = NewNode( pData, bounds );
  341. Assert( root.Index() == 0 ); // The root should always be at index zero
  342. return;
  343. }
  344. // Find the closest leaf to the inserted sphere, and record the path to it
  345. float minDist = FLT_MAX;
  346. const Vector &pos = bounds->AsVector3D();
  347. if ( m_PrevInsertedNode >= 0 )
  348. {
  349. // Use the last inserted node as an upper bound on minDist (~halves the cost of FindPathToClosestLeaf_R)
  350. NodeRef node = Ref( m_PrevInsertedNode );
  351. Assert( node.IsLeaf() );
  352. if ( node.IsLeaf() )
  353. {
  354. float dist = ( node->bounds.AsVector3D() - pos ).Length();
  355. minDist = MAX( dist*1.1f, dist + 0.001f ); // Add slop for paranoia's sake
  356. }
  357. }
  358. CUtlVector< int > path;
  359. float nodeDist = ( Ref( 0 )->bounds.AsVector3D() - pos ).Length();
  360. FindPathToClosestLeaf_R( Ref( 0 ), pos, nodeDist, minDist, path );
  361. Assert( path.Count() && path[ path.Count() - 1 ] == 0 ); // All paths lead to the root!
  362. int bestSibling = 0;
  363. /* TODO: see if this could improve the tree noticeably, without increasing tree-building time too much (reuse the old ChooseInsertionStrategy code)
  364. // Now determine where best to insert the sphere along the recorded path, minimizing summed node volume growth
  365. // (if the sphere is much bigger than the leaf it was paired with, we'll insert it higher up the tree).
  366. float minCost = FLT_MAX;
  367. for ( int i = 0; i < path.Count(); i++ )
  368. {
  369. NodeRef &sibling = Ref( path[ i ] );
  370. // NOTE: Can't rebalance the tree if you pair with a depth 3+ sibling
  371. if ( ( sibling->maxDepth <= 2 ) && ( ComputePairingCost2( sibling, bounds, path ) < minCost ) )
  372. bestSibling = i;
  373. } */
  374. // Insert alongside the chosen sibling - duplicate the sibling and add this copy plus the new node to the original sibling:
  375. NodeRef sibling = Ref( path[ bestSibling ] );
  376. NodeRef newSibling = CopyNode( sibling );
  377. NodeRef newNode = NewNode( pData, bounds );
  378. SetNodeChildren( sibling, newNode, newSibling );
  379. RebalanceSubtrees( sibling, true );
  380. // Remember the last node we inserted
  381. m_PrevInsertedNode = newNode.Index();
  382. for ( int i = ( bestSibling + 1 ); i < path.Count(); i++ )
  383. {
  384. // Update each node's status whose descendants were modified, and rebalance it:
  385. NodeRef &sibling = Ref( path[ i ] );
  386. SetNodeChildren( sibling, sibling.Deep(), sibling.Shallow() );
  387. RebalanceSubtrees( sibling, true );
  388. }
  389. //ParanoidBalanceCheck_R( Ref( 0 ) ); // Use this to help debug Insert/Remove issues
  390. }
  391. inline void CUtlSphereTree::FindPathToClosestLeaf_R( NodeRef node, const Vector &pos, const float nodeDist, float &minDist, CUtlVector< int > &path ) const
  392. {
  393. float prevMinDist = minDist;
  394. if ( node.IsLeaf() )
  395. {
  396. // TODO: should we use radius here? pos could be more 'central' in a larger sphere, while being closer to the centre of a smaller sphere...
  397. if ( nodeDist >= minDist )
  398. return;
  399. // New closest leaf! Start the path again:
  400. path.RemoveAll();
  401. minDist = nodeDist;
  402. }
  403. else
  404. {
  405. float deepDist = ( node.Deep()->bounds.AsVector3D() - pos ).Length();
  406. float shallowDist = ( node.Shallow()->bounds.AsVector3D() - pos ).Length();
  407. if ( ( deepDist - node.Deep()->bounds.w ) < minDist )
  408. FindPathToClosestLeaf_R( node.Deep(), pos, deepDist, minDist, path );
  409. if ( ( shallowDist - node.Shallow()->bounds.w ) < minDist )
  410. FindPathToClosestLeaf_R( node.Shallow(), pos, shallowDist, minDist, path );
  411. }
  412. if ( minDist < prevMinDist )
  413. {
  414. // If minDist improved, we're on the path to the closest leaf, so add ourself to the path:
  415. path.AddToTail( node.Index() );
  416. }
  417. }
  418. inline void CUtlSphereTree::Remove( const void *pData, const Sphere_t *bounds )
  419. {
  420. Assert( m_NumNodesInTree );
  421. if ( !m_NumNodesInTree )
  422. return; // Empty tree
  423. NodeRef root = Ref( 0 );
  424. if ( m_NumNodesInTree == 1 )
  425. {
  426. // Special case: the last node to remove is the root, making the tree empty
  427. Assert( NodeHasData( root, pData, bounds ) );
  428. if ( NodeHasData( root, pData, bounds ) )
  429. FreelistPush( root );
  430. return;
  431. }
  432. // Recurse to find the target node and remove it
  433. int deletedNode;
  434. deletedNode = Remove_R( root, pData, bounds );
  435. Assert( deletedNode != 0 );
  436. //ParanoidBalanceCheck_R( root ); // Use this to help debug Insert/Remove issues
  437. }
  438. inline int CUtlSphereTree::Remove_R( NodeRef &node, const void *pData, const Sphere_t *bounds )
  439. {
  440. // NOTE: it's ok to cache deepChild+shallowChild refs here because no items are removed from m_Nodes during this recursion
  441. NodeRef deepChild = node.Deep(), shallowChild = node.Shallow();
  442. // Is the data owned by one of our direct children?
  443. for ( int i = 0; i < 2; i++ )
  444. {
  445. NodeRef &child = ( i ? shallowChild : deepChild ), otherChild = ( i ? deepChild : shallowChild );
  446. if ( NodeHasData( child, pData, bounds ) )
  447. {
  448. // Found our target - replace node with the other child
  449. *node = *otherChild;
  450. FreelistPush( child );
  451. FreelistPush( otherChild );
  452. return child.Index();
  453. }
  454. }
  455. // Recurse further to find the owner
  456. int deletedNodeIndex = -1;
  457. if ( !deepChild.IsLeaf() )
  458. deletedNodeIndex = Remove_R( deepChild, pData, bounds );
  459. if ( ( deletedNodeIndex < 0 ) && !shallowChild.IsLeaf() )
  460. deletedNodeIndex = Remove_R( shallowChild, pData, bounds );
  461. if ( deletedNodeIndex >= 0 )
  462. {
  463. // One of this node's sub-trees has changed, so update its status and rebalance the tree
  464. SetNodeChildren( node, deepChild, shallowChild );
  465. RebalanceSubtrees( node, false );
  466. }
  467. return deletedNodeIndex;
  468. }
  469. inline bool CUtlSphereTree::NodeHasData( const NodeRef &node, const void *pData, const Sphere_t *bounds )
  470. {
  471. // NOTE: this allows for pData being NULL or the same pData value being used with multiple spheres
  472. // (though results are undefined if you insert the exact same pData/bounds pair twice)
  473. return ( ( node->pData == pData ) && ( node->bounds == *bounds ) );
  474. }
  475. inline void CUtlSphereTree::RebalanceSubtrees( NodeRef &node, bool bInserting )
  476. {
  477. if ( IsSubtreeBalanced( node ) )
  478. return;
  479. // ShallowChild needs to be paired with a node from the deepChild subtree in order to rebalance the tree.
  480. // We can either pair it with a 'nephew' (one of the children of deepChild) or a great-nephew (one of the
  481. // children of the nephews). Not all choices result in a balanced tree, so we first determine which
  482. // choices are valid and then we choose the one with the lowest 'cost'.
  483. NodeRef shallowChild = node.Shallow(), deepChild = node.Deep();
  484. Assert( IsSubtreeBalanced( deepChild ) );
  485. // Put the shallower nephew first
  486. NodeRef nephews[2] = { deepChild.Shallow(), deepChild.Deep() };
  487. Assert( nephews[1]->maxDepth == ( shallowChild->maxDepth + 1 ) );
  488. Assert( IsSubtreeBalanced( nephews[0] ) );
  489. Assert( IsSubtreeBalanced( nephews[1] ) );
  490. // Put the shallower great-nephew pair first, and within each pair put the shallower great-nephew first
  491. NodeRef greatNephews[4] = { nephews[0].Shallow(), nephews[0].Deep(),
  492. nephews[1].Shallow(), nephews[1].Deep() };
  493. int nephewMask = 0x3, greatNephewMask = 0xF;
  494. if ( nephews[0]->maxDepth == nephews[1]->maxDepth )
  495. {
  496. Assert( !bInserting );
  497. Assert( nephews[0]->maxDepth == ( shallowChild->maxDepth + 1 ) );
  498. // The nephews are both fair game for pairing with shallowChild. Determine which great-nephews are viable:
  499. for ( int i = 0; i < 2; i++ )
  500. {
  501. // If a nephew is uneven, only the shallower great-nephew may be paired with shallowChild
  502. if ( greatNephews[ i*2 ]->maxDepth < greatNephews[ i*2 + 1 ]->maxDepth )
  503. greatNephewMask ^= 1 << ( i*2 + 1 );
  504. }
  505. }
  506. else
  507. {
  508. Assert( nephews[0]->maxDepth == shallowChild->maxDepth );
  509. // Only the shallower nephew and the deeper nephew's children may be paired with shallowChild
  510. nephewMask = ( 1 << 0 );
  511. greatNephewMask = ( 1 << 2 ) | ( 1 << 3 );
  512. }
  513. // Now use a cost metric to determine the best choice
  514. float minCost = FLT_MAX;
  515. int bestIndex = -1;
  516. bool nephewBest = true;
  517. for ( int i = 0; i < 2; i++ )
  518. {
  519. if ( nephewMask & (1<<i) )
  520. {
  521. float cost = ComputePairingCost( shallowChild, nephews[i], nephews[i^1], NULL );
  522. if ( cost < minCost )
  523. {
  524. minCost = cost;
  525. bestIndex = i;
  526. }
  527. }
  528. }
  529. for ( int i = 0; i < 4; i++ )
  530. {
  531. if ( greatNephewMask & (1<<i) )
  532. {
  533. int otherNephew = ( i >> 1 ) ^ 1;
  534. float cost = ComputePairingCost( shallowChild, greatNephews[i], nephews[otherNephew], &greatNephews[i^1] );
  535. if ( cost < minCost )
  536. {
  537. minCost = cost;
  538. bestIndex = i;
  539. nephewBest = false;
  540. }
  541. }
  542. }
  543. if ( nephewBest )
  544. {
  545. // Pair shallowChild with one of its nephews (reuse deepChild as their parent), and pull the other nephew up the tree
  546. SetNodeChildren( deepChild, shallowChild, nephews[ bestIndex ] );
  547. SetNodeChildren( node, deepChild, nephews[ bestIndex ^ 1 ] );
  548. }
  549. else
  550. {
  551. // Pair shallowChild with one of its great-nephews, and pair the remaining great-nephew with the other nephew
  552. NodeRef &nephew = nephews[ bestIndex >> 1 ];
  553. NodeRef &otherNephew = nephews[ ( bestIndex >> 1 ) ^ 1 ];
  554. SetNodeChildren( deepChild, otherNephew, greatNephews[ bestIndex ^ 1 ] );
  555. SetNodeChildren( nephew, shallowChild, greatNephews[ bestIndex ] );
  556. SetNodeChildren( node, deepChild, nephew );
  557. }
  558. // The tree under 'node' should now be balanced
  559. Assert( IsSubtreeBalanced( node ) );
  560. Assert( IsSubtreeBalanced( node.Deep() ) );
  561. Assert( IsSubtreeBalanced( node.Shallow() ) );
  562. }
  563. inline bool CUtlSphereTree::IsSubtreeBalanced( const NodeRef &node ) const
  564. {
  565. if ( !node.IsLeaf() )
  566. {
  567. Assert( node.Deep()->maxDepth >= node.Shallow()->maxDepth );
  568. if ( node.Deep()->maxDepth > ( node.Shallow()->maxDepth + 1 ) )
  569. return false;
  570. }
  571. return true;
  572. }
  573. inline float CUtlSphereTree::Cost( const Sphere_t &sphere ) const
  574. {
  575. // We optimize the tree based on total resulting bounding sphere volume:
  576. // - this minimizes average cost in the space occupied by the tree, i.e. the number of tests you have
  577. // to perform to satisfy a given query; on entering a sphere, you need to do 2 tests to pick a child,
  578. // so total cost is 'two tests' times the total volume of (non-leaf) nodes
  579. // - NOTE: this metric works for input spheres which overlap or fully contain each other
  580. // TODO: Volume is the best criterion for a point test ("which leaves does a point intersect?"),
  581. // but area seems better for ray tests and radius for plane tests - are there pathological
  582. // cases where these 3 metrics would differ enough to make Insert choose a different pairing?
  583. // Should be easy to change this and test perf differences on real data...
  584. return sphere.w*sphere.w*sphere.w;
  585. }
  586. inline Sphere_t CUtlSphereTree::AddSpheres( const Sphere_t &pA, const Sphere_t &pB ) const
  587. {
  588. const Vector &aPos = pA.AsVector3D(), &bPos = pB.AsVector3D();
  589. float aRad = pA.w, bRad = pB.w;
  590. Vector delta = aPos - bPos;
  591. float dist = delta.NormalizeInPlace();
  592. if ( dist < fabsf( aRad - bRad ) )
  593. {
  594. // One sphere entirely contains the other
  595. return ( ( aRad > bRad ) ? pA : pB );
  596. }
  597. // Figure out the new sphere's centre+radius, using 'mathematics'
  598. float newRad = ( aRad + bRad + dist ) / 2;
  599. Vector newPos = bPos + delta*( newRad - bRad );
  600. return Sphere_t( newPos.x, newPos.y, newPos.z, newRad );
  601. }
  602. inline float CUtlSphereTree::ComputePairingCost( const NodeRef &pair1A, const NodeRef &pair1B, const NodeRef &pair2A, const NodeRef *pair2B ) const
  603. {
  604. Sphere_t pair1Sphere = AddSpheres( pair1A->bounds, pair1B->bounds );
  605. Sphere_t pair2Sphere = pair2B ? AddSpheres( pair2A->bounds, (*pair2B)->bounds ) : pair2A->bounds;
  606. Sphere_t outerSphere = AddSpheres( pair1Sphere, pair2Sphere );
  607. return ( Cost( pair1Sphere ) + Cost( pair2Sphere ) + Cost( outerSphere ) );
  608. }
  609. inline void CUtlSphereTree::ParanoidBalanceCheck_R( const NodeRef &node ) const
  610. {
  611. if ( node.IsLeaf() )
  612. return;
  613. Assert( IsSubtreeBalanced( node ) );
  614. ParanoidBalanceCheck_R( node.Deep() );
  615. ParanoidBalanceCheck_R( node.Shallow() );
  616. }
  617. inline void CUtlSphereTree::GetLeavesUnderNode_R( const NodeRef &node, CUtlVector< void * > &leaves, int &count ) const
  618. {
  619. if ( node.IsLeaf() )
  620. {
  621. if ( count > 0 )
  622. leaves.AddToTail( (void *)node->pData );
  623. count--;
  624. }
  625. else
  626. {
  627. GetLeavesUnderNode_R( node.Deep(), leaves, count );
  628. GetLeavesUnderNode_R( node.Shallow(), leaves, count );
  629. }
  630. }
  631. inline int CUtlSphereTree::IntersectWithRay( Vector &rayStart, Vector &rayDelta, CUtlVector< void * > &result, int maxResults, CUtlSphereTree::Cut *cut ) const
  632. {
  633. // Returns a list of the indices of the (leaf) nodes intersecting the given ray
  634. // TODO: optionally return the spheres in intersection order, from rayStart to (rayStart+rayDelta)
  635. result.RemoveAll();
  636. if ( !m_NumNodesInTree )
  637. return 0; // Empty tree
  638. // Default to searching the whole tree
  639. Cut completeCut( this );
  640. if ( cut == NULL )
  641. cut = &completeCut;
  642. Assert( cut->m_pTree == this );
  643. if ( cut->m_pTree != this )
  644. return 0;
  645. maxResults = maxResults ? maxResults : 0x7FFFFFFF;
  646. int count = maxResults;
  647. for ( int i = 0; i < cut->m_NodeRefs.Count(); i++ )
  648. {
  649. IntersectWithRay_R( Ref( cut->m_NodeRefs[ i ] ), rayStart, rayDelta, result, count );
  650. }
  651. return ( maxResults - count );
  652. }
  653. // extern int gNumCalls; // Useful for accurately measuring the cost of intersection tests (average num calls should be a low multiple of average tree depth)
  654. inline void CUtlSphereTree::IntersectWithRay_R( const NodeRef &node, Vector &rayStart, Vector &rayDelta, CUtlVector< void * > &result, int & count ) const
  655. {
  656. // TODO: pull the intersection test outside the recursive call, as is done in IntersectWithSphere_R
  657. //gNumCalls++;
  658. float hit1, hit2;
  659. Sphere_t &sphere = node->bounds;
  660. // TODO: In cases where a node overlaps heavily with its children, might it be a win to skip this test
  661. // and assume it passes? (i.e. recurse to the children and test their (similar) bounds instead)
  662. // Might be a clearer win in a SIMD-ified version of the tree (where we can simply omit this node)
  663. if ( !IntersectRayWithSphere( rayStart, rayDelta, sphere.AsVector3D(), sphere.w, &hit1, &hit2 ) )
  664. {
  665. // No intersection
  666. return;
  667. }
  668. if ( node.IsLeaf() )
  669. {
  670. // Leaf node, add it to the result
  671. if ( count > 0 )
  672. result.AddToTail( (void *)node->pData );
  673. count--;
  674. }
  675. else
  676. {
  677. // Non-leaf, recurse to child nodes
  678. IntersectWithRay_R( node.Deep(), rayStart, rayDelta, result, count );
  679. IntersectWithRay_R( node.Shallow(), rayStart, rayDelta, result, count );
  680. }
  681. }
  682. inline int CUtlSphereTree::IntersectWithSphere( const Sphere_t &sphere, bool bPartial, CUtlVector< void * > &result, int maxResults, CUtlSphereTree::Cut *cut ) const
  683. {
  684. // Returns a list of the indices of the (leaf) nodes intersecting the given sphere
  685. // TODO: optionally return the spheres in order of distance from the sphere centre
  686. result.RemoveAll();
  687. if ( !m_NumNodesInTree )
  688. return 0; // Empty tree
  689. // Default to searching the whole tree
  690. Cut completeCut( this );
  691. if ( cut == NULL )
  692. cut = &completeCut;
  693. Assert( cut->m_pTree == this );
  694. if ( cut->m_pTree != this )
  695. return 0;
  696. maxResults = maxResults ? maxResults : 0x7FFFFFFF;
  697. int count = maxResults;
  698. float distSq;
  699. for ( int i = 0; i < cut->m_NodeRefs.Count(); i++ )
  700. {
  701. if ( SpheresIntersect( sphere, Ref( cut->m_NodeRefs[ i ] )->bounds, distSq ) )
  702. IntersectWithSphere_R( Ref( cut->m_NodeRefs[ i ] ), sphere, bPartial, result, count, distSq );
  703. }
  704. return ( maxResults - count );
  705. }
  706. inline void CUtlSphereTree::IntersectWithSphere_R( const NodeRef &node, const Sphere_t &sphere, const bool bPartial, CUtlVector< void * > &result, int &count, const float distSq ) const
  707. {
  708. //gNumCalls++;
  709. if ( node.IsLeaf() )
  710. {
  711. // Leaf node
  712. if ( !bPartial )
  713. {
  714. // sphere must fully contain bounds
  715. Sphere_t &bounds = node->bounds;
  716. if ( sphere.w < bounds.w )
  717. return;
  718. float maxDist = sphere.w - bounds.w;
  719. if ( distSq > ( maxDist*maxDist ) )
  720. return;
  721. }
  722. // Add it to the result
  723. if ( count > 0 )
  724. result.AddToTail( (void *)node->pData );
  725. count--;
  726. }
  727. else
  728. {
  729. // Non-leaf, recurse to child nodes (do the sphere test here to minimize function calls)
  730. // TODO: In cases where a node overlaps heavily with its children, might it be a win to skip this test
  731. // and assume it passes? (i.e. recurse to the children and test their (similar) bounds instead)
  732. // Might be a clearer win in a SIMD-ified version of the tree (where we can simply omit the parent node)
  733. float deepDistSq, shallowDistSq;
  734. if ( SpheresIntersect( sphere, node.Deep()->bounds, deepDistSq ) )
  735. IntersectWithSphere_R( node.Deep(), sphere, bPartial, result, count, deepDistSq );
  736. if ( SpheresIntersect( sphere, node.Shallow()->bounds, shallowDistSq ) )
  737. IntersectWithSphere_R( node.Shallow(), sphere, bPartial, result, count, shallowDistSq );
  738. }
  739. }
  740. inline bool CUtlSphereTree::SpheresIntersect( const Sphere_t &A, const Sphere_t &B, float &distSq ) const
  741. {
  742. Vector delta = A.AsVector3D() - B.AsVector3D();
  743. float radiusSum = A.w + B.w;
  744. distSq = delta.LengthSqr();
  745. return ( distSq <= ( radiusSum*radiusSum ) );
  746. }
  747. inline void CUtlSphereTree::CutByPlanes( Cut *outputCut, const Cut *inputCut, int cutFlags, Plane_t *planes, int numPlanes ) const
  748. {
  749. // TODO: might be faster if this code called one of 4 variants of CutByPlanes_R (i.e. templatize to make 'cutFlags' a compile-time constant)
  750. if ( !m_NumNodesInTree )
  751. return; // Empty tree
  752. Assert( outputCut );
  753. if ( !outputCut )
  754. return;
  755. // Default to searching the whole tree
  756. Cut completeCut( this );
  757. if ( inputCut == NULL )
  758. inputCut = &completeCut;
  759. if ( inputCut == outputCut )
  760. {
  761. // Copy the input/output Cut's contents to a temporary Cut so we don't have to special-case much code
  762. // TODO: it would be quicker to modify the input Cut in-place (given that node order is not important,
  763. // you can process nodes in order, addtotail new items and replace removed items with the last entry)
  764. completeCut.m_NodeRefs.RemoveAll();
  765. completeCut.m_NodeRefs.AddMultipleToTail( inputCut->m_NodeRefs.Count(), inputCut->m_NodeRefs.Base() );
  766. inputCut = &completeCut;
  767. }
  768. // Clear the output before we start
  769. outputCut->m_NodeRefs.RemoveAll();
  770. for ( int i = 0; i < inputCut->m_NodeRefs.Count(); i++ )
  771. {
  772. CutByPlanes_R( outputCut, Ref( inputCut->m_NodeRefs[ i ] ), cutFlags, planes, numPlanes );
  773. }
  774. }
  775. inline void CUtlSphereTree::CutByPlanes_R( Cut *outputCut, const NodeRef &node, int cutFlags, Plane_t *planes, int numPlanes ) const
  776. {
  777. // When refining a Cut w.r.t a set of intersection primitives (here planes), there are two flags to control how this is done:
  778. // PARTIAL_INTERSECTIONS - nodes partially passing the intersection test (partially in front of the plane) are included
  779. // these are only leaf nodes; non-leaf nodes are subdivided if found to be 'partial' (since we do
  780. // not want both a node and its parent to be present in the result)
  781. // OR_INTERSECTIONS - in 'OR' mode, a given node needs to pass the intersection test for ANY intersection primitive
  782. // - in 'AND' mode (the default), it must pass the intersection test for ALL intersection primitives
  783. // (for planes, AND is useful to find 'nodes INSIDE a region' and OR to find 'nodes OUTSIDE a region')
  784. //
  785. // When recursing, we follow these rules for each node:
  786. // AND:
  787. // - fail any plane -> FAIL, end recursion
  788. // - pass all planes -> PASS, add node
  789. // - otherwise (partial for any planes)
  790. // o non-leaf -> recurse to children
  791. // o leaf -> add node if allow partial
  792. // OR:
  793. // - fail all planes -> FAIL, end recursion
  794. // - pass any planes -> PASS, add node
  795. // - partial for any planes (fail for others)
  796. // o non-leaf -> recurse to children
  797. // o leaf -> add node if allow partial
  798. Cut::CutTestResult result = CutByPlanes_Test( node, cutFlags, planes, numPlanes );
  799. switch( result )
  800. {
  801. case Cut::CUT_TEST_PASS:
  802. // The node fully passes the test, add it to the output Cut
  803. outputCut->m_NodeRefs.AddToTail( node.Index() );
  804. return;
  805. case Cut::CUT_TEST_FAIL:
  806. // The node fully fails the test, stop recursion
  807. return;
  808. case Cut::CUT_TEST_PARTIAL:
  809. // The node partially passes the test
  810. if ( node.IsLeaf() )
  811. {
  812. // Add to the result if partial intersections are desired
  813. if ( cutFlags & Cut::PARTIAL_INTERSECTIONS )
  814. outputCut->m_NodeRefs.AddToTail( node.Index() );
  815. }
  816. else
  817. {
  818. // Recurse to the child nodes (since we can't know yet whether they are pass, fail or partial)
  819. CutByPlanes_R( outputCut, node.Deep(), cutFlags, planes, numPlanes );
  820. CutByPlanes_R( outputCut, node.Shallow(), cutFlags, planes, numPlanes );
  821. }
  822. return;
  823. }
  824. }
  825. inline CUtlSphereTree::Cut::CutTestResult CUtlSphereTree::CutByPlanes_Test( const NodeRef &node, int cutFlags, Plane_t *planes, int numPlanes ) const
  826. {
  827. Vector &centre = node->bounds.AsVector3D();
  828. float radius = node->bounds.w;
  829. int numPass = 0, numFail = 0, numPartial = 0;
  830. for ( int i = 0; i < numPlanes; i++ )
  831. {
  832. float dist = centre.Dot( planes[ i ].AsVector3D() ) - planes[ i ].w;
  833. if ( dist >= radius )
  834. {
  835. // Node is fully in front
  836. if ( cutFlags & Cut::OR_INTERSECTIONS )
  837. return Cut::CUT_TEST_PASS; // A single pass is enough for 'OR' mode
  838. numPass++;
  839. }
  840. else if ( dist < -radius )
  841. {
  842. // Node is fully behind
  843. if ( !( cutFlags & Cut::OR_INTERSECTIONS ) )
  844. return Cut::CUT_TEST_FAIL; // A single fail is enough for 'AND' mode
  845. numFail++;
  846. }
  847. else
  848. {
  849. // Node is partially in front
  850. numPartial++;
  851. }
  852. }
  853. if ( cutFlags & Cut::OR_INTERSECTIONS ) // 'OR' mode
  854. {
  855. Assert( numPass == 0 ); // Should have earlied-out above, otherwise
  856. return ( numPartial == 0 ) ? Cut::CUT_TEST_FAIL : Cut::CUT_TEST_PARTIAL;
  857. }
  858. else // 'AND' mode
  859. {
  860. Assert( numFail == 0 ); // Should have earlied-out above, otherwise
  861. return ( numPass == numPlanes ) ? Cut::CUT_TEST_PASS : Cut::CUT_TEST_PARTIAL;
  862. }
  863. }
  864. inline void CUtlSphereTree::MeasureTreeQuality( float &averageDepth, float &idealDepth, float &averageRedundancy, float &averageSphereTestCost, float sphereTestRadius ) const
  865. {
  866. int numLeaves = ( m_NumNodesInTree + 1 ) / 2;
  867. float pathRedundancy = 0;
  868. averageRedundancy = 0;
  869. averageDepth = 0;
  870. float pathProbability = 1;
  871. averageSphereTestCost = 0;
  872. int steps = 0;
  873. // Measure depth and redundancy for all paths through the tree:
  874. MeasureTreeQuality_R( Ref( 0 ), steps, averageDepth, averageRedundancy, pathRedundancy, averageSphereTestCost, pathProbability, sphereTestRadius );
  875. // The more efficient a perfectly tree, the closer its averageRedundancy to 0.0 (1.0 is the worst case, 0.5 is normal)
  876. // NOTE: parent-child redundancy correlates well with sibling overlap
  877. averageRedundancy /= MAX( 1, numLeaves );
  878. // For a well-balanced tree, averageDepth ~= idealDepth
  879. averageDepth /= MAX( 1, numLeaves );
  880. // Compute idealDepth for comparison:
  881. idealDepth = ( log( m_NumNodesInTree + 1.0f ) / log( 2.0f ) ) - 1;
  882. int baseDepth = (int)floor( idealDepth );
  883. int numFullTreeNodes = ( 1 << baseDepth )*2 - 1;
  884. int numHighDepthLeaves = m_NumNodesInTree - numFullTreeNodes;
  885. int numLowDepthLeaves = numLeaves - numHighDepthLeaves;
  886. idealDepth = ( numLowDepthLeaves*baseDepth + numHighDepthLeaves*( baseDepth + 1 ) ) / (float)numLeaves;
  887. }
  888. inline void CUtlSphereTree::MeasureTreeQuality_R( const NodeRef &node, const int steps, float &averageDepth, float &averageRedundancy, const float pathRedundancy, float &averageSphereTestCost, const float pathProbability, const float sphereTestRadius ) const
  889. {
  890. averageSphereTestCost += pathProbability;
  891. if ( !node.IsLeaf() )
  892. {
  893. float deepHitProbability = ComputeIntersectionProbability( node, node.Deep(), sphereTestRadius ), shallowHitProbability = ComputeIntersectionProbability( node, node.Shallow(), sphereTestRadius );
  894. MeasureTreeQuality_R( node.Deep(), steps+1, averageDepth, averageRedundancy, ( pathRedundancy + ComputeRedundancy( node, node.Deep() ) ), averageSphereTestCost, pathProbability*deepHitProbability, sphereTestRadius );
  895. MeasureTreeQuality_R( node.Shallow(), steps+1, averageDepth, averageRedundancy, ( pathRedundancy + ComputeRedundancy( node, node.Shallow() ) ), averageSphereTestCost, pathProbability*shallowHitProbability, sphereTestRadius );
  896. }
  897. else
  898. {
  899. // Leaf
  900. averageDepth += steps;
  901. averageRedundancy += pathRedundancy / steps;
  902. }
  903. }
  904. inline float CUtlSphereTree::ComputeRedundancy( const NodeRef &parent, const NodeRef &child ) const
  905. {
  906. const Sphere_t &parentSphere = parent->bounds, &childSphere = child->bounds;
  907. if ( parentSphere.w == 0 )
  908. return 1;
  909. return ( childSphere.w*childSphere.w*childSphere.w ) / ( parentSphere.w*parentSphere.w*parentSphere.w );
  910. }
  911. inline float CUtlSphereTree::ComputeIntersectionProbability( const NodeRef &parent, const NodeRef &child, const float sphereTestRadius ) const
  912. {
  913. // The probability of a test sphere intersecting a parent's child sphere, GIVEN that it has intersected the parent sphere:
  914. // [NOTE: this math assumes that the child sphere touches the parent sphere's boundary, which is always true for CUtlSphereTree]
  915. float totalRadius = ( parent->bounds.w + sphereTestRadius ), hitRadius = ( child->bounds.w + sphereTestRadius );
  916. if ( totalRadius == 0 )
  917. return 1;
  918. return ( hitRadius*hitRadius*hitRadius ) / ( totalRadius*totalRadius*totalRadius );
  919. }
  920. // ===== CUtlSphereTree_Cut implementation =================================
  921. inline CUtlSphereTree::Cut::Cut( const CUtlSphereTree *pTree )
  922. {
  923. // Initialize a Cut with an entire sphere tree (i.e. the root node)
  924. Assert( pTree );
  925. m_pTree = pTree;
  926. if ( pTree->m_NumNodesInTree )
  927. m_NodeRefs.AddToTail( 0 );
  928. }
  929. inline CUtlSphereTree::Cut::~Cut( void )
  930. {
  931. }
  932. inline int CUtlSphereTree::Cut::GetLeaves( CUtlVector< void * > &leaves, int maxResults ) const
  933. {
  934. leaves.RemoveAll();
  935. maxResults = maxResults ? maxResults : 0x7FFFFFFF;
  936. int count = maxResults;
  937. for ( int i = 0; i < m_NodeRefs.Count(); i++ )
  938. {
  939. m_pTree->GetLeavesUnderNode_R( m_pTree->Ref( m_NodeRefs[ i ] ), leaves, count );
  940. }
  941. return ( maxResults - count );
  942. }
  943. #endif // _utlspheretree_H_