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.

348 lines
10 KiB

  1. //========= Copyright � Valve Corporation, All rights reserved. ============//
  2. #include "feagglomerator.h"
  3. #include "bitvec.h"
  4. CFeAgglomerator::CFeAgglomerator( uint nReserveNodes )
  5. {
  6. m_Clusters.EnsureCapacity( nReserveNodes * 2 );
  7. m_Clusters.SetCount( nReserveNodes );
  8. m_Clusters.FillWithValue( NULL ); // client needs to set all the nodes
  9. /*
  10. for ( uint i = 0; i < nReserveNodes; ++i )
  11. {
  12. m_Clusters[ i ] = new CCluster;
  13. }
  14. */
  15. }
  16. CFeAgglomerator::~CFeAgglomerator()
  17. {
  18. m_Clusters.PurgeAndDeleteElements();
  19. }
  20. CFeAgglomerator::CCluster::CCluster( int nIndex, int nChildLeafs ) :m_nParent( -1 ), m_nIndex( nIndex ), m_nChildLeafs( nChildLeafs )
  21. {
  22. m_nChild[ 0 ] = -1;
  23. m_nChild[ 1 ] = -1;
  24. }
  25. bool CFeAgglomerator::CCluster::HasLinks()const
  26. {
  27. return m_Links.Count() > 0;
  28. }
  29. float CFeAgglomerator::CCluster::GetBestCost()const
  30. {
  31. if ( HasLinks() )
  32. {
  33. return m_Links.ElementAtHead().m_flCost;
  34. }
  35. else
  36. {
  37. return FLT_MAX;
  38. }
  39. }
  40. void CFeAgglomerator::CCluster::RemoveLink( CCluster *pOtherCluster )
  41. {
  42. for ( int i = 0; i < m_Links.Count(); ++i )
  43. {
  44. if ( m_Links.Element( i ).m_pOtherCluster == pOtherCluster )
  45. {
  46. m_Links.RemoveAt( i );
  47. return;
  48. }
  49. }
  50. Assert( !"Not found" );
  51. }
  52. const CFeAgglomerator::CLink *CFeAgglomerator::CCluster::FindLink( CCluster *pOtherCluster )
  53. {
  54. for ( int i = 0; i < m_Links.Count(); ++i )
  55. {
  56. const CLink &link = m_Links.Element( i );
  57. if ( link.m_pOtherCluster == pOtherCluster )
  58. {
  59. return &link;
  60. }
  61. }
  62. return NULL;
  63. }
  64. //
  65. // The cost of a node of the tree is the probability of its collision with another bounding box, times number of points to test.
  66. // The probably of collision is the probability of their minkowski sum containing the origin, which is proportional to the volume of the minkowski sum.
  67. // It boils down to guessing the size of the other bounding box. Having no better heuristc, we can just say it'll probably be a box of average size 12x12x12 or something
  68. //
  69. float CFeAgglomerator::CCluster::ComputeCost( const Vector &vSize, int nChildLeafs )
  70. {
  71. return ( vSize.x + 12 ) * ( vSize.y + 12 ) * ( vSize.z + 12 ) * nChildLeafs;
  72. }
  73. void CFeAgglomerator::CCluster::AddLink( CCluster *pOtherCluster )
  74. {
  75. #ifdef _DEBUG
  76. for ( int i = 0; i < m_Links.Count(); i++ )
  77. {
  78. Assert( m_Links.Element( i ).m_pOtherCluster != pOtherCluster );
  79. }
  80. #endif
  81. CLink link;
  82. link.m_pOtherCluster = pOtherCluster;
  83. //
  84. // Note: GetSurfaceArea() is not a valid heuristic for cost here. E.g. 2 horizontal points will have 0 cost, which is clearly wrong
  85. // (m_Aabb + pOtherCluster->m_Aabb ).GetSurfaceArea()
  86. //
  87. link.m_flCost = ComputeCost( ( m_Aabb + pOtherCluster->m_Aabb ).GetSize(), m_nChildLeafs + pOtherCluster->m_nChildLeafs );
  88. m_Links.Insert( link );
  89. }
  90. void CFeAgglomerator::AddLink( CCluster* pCluster0, CCluster *pCluster1, ClustersPriorityQueue_t &queue )
  91. {
  92. float flBestDist0 = pCluster0->GetBestCost();
  93. float flBestDist1 = pCluster1->GetBestCost();
  94. pCluster0->AddLink( pCluster1 );
  95. pCluster1->AddLink( pCluster0 );
  96. float flNewBestDist0 = pCluster0->GetBestCost();
  97. float flNewBestDist1 = pCluster1->GetBestCost();
  98. if ( flNewBestDist0 != flBestDist0 )
  99. {
  100. queue.RevaluateElement( pCluster0->m_nPriorityIndex );
  101. }
  102. if ( flNewBestDist1 != flBestDist1 )
  103. {
  104. queue.RevaluateElement( pCluster1->m_nPriorityIndex );
  105. }
  106. }
  107. // register a link between the nodes.
  108. // Call this to register all links between all nodes before building agglomerated clusters
  109. void CFeAgglomerator::LinkNodes( int nNode0, int nNode1 )
  110. {
  111. if ( nNode0 == nNode1 )
  112. return;
  113. CCluster* pCluster0 = m_Clusters[ nNode0 ];
  114. CCluster *pCluster1 = m_Clusters[ nNode1 ];
  115. if ( pCluster0->FindLink( pCluster1 ) )
  116. {
  117. AssertDbg( pCluster1->FindLink( pCluster0 ) );
  118. }
  119. else
  120. {
  121. // link not duplicated, create new link
  122. pCluster0->AddLink( pCluster1 );
  123. pCluster1->AddLink( pCluster0 );
  124. }
  125. }
  126. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  127. // Build agglomerated clusters; cannot call this function twice, because it will try to agglomerate clusters at all levels (nodes to root) the 2nd time
  128. //
  129. void CFeAgglomerator::Build( bool bSingleRoot )
  130. {
  131. ClustersPriorityQueue_t queue;
  132. for ( int i = 0; i < m_Clusters.Count(); ++i )
  133. {
  134. queue.Insert( m_Clusters[ i ] );
  135. }
  136. Process( queue );
  137. // create links of all-with-all
  138. // this will be ultra-slow if we have degenerate case (like 1000 small disconnected pieces of cloth)
  139. if ( bSingleRoot && queue.Count() > 1 )
  140. {
  141. for ( int i = queue.Count(); i-- > 1; )
  142. {
  143. for ( int j = i; j-- > 0; )
  144. {
  145. queue.Element( i )->AddLink( queue.Element( j ) );
  146. queue.Element( j )->AddLink( queue.Element( i ) );
  147. }
  148. }
  149. // the old queue order is destroyed; create a new queue (we could re-heapify the old queue
  150. ClustersPriorityQueue_t queue2;
  151. for ( int i = queue.Count(); i-- > 0; )
  152. {
  153. queue2.Insert( queue.Element( i ) );
  154. }
  155. Process( queue2 );
  156. Assert( queue2.Count() == 1 );
  157. }
  158. }
  159. void CFeAgglomerator::Validate( ClustersPriorityQueue_t *pQueue )
  160. {
  161. #ifdef _DEBUG
  162. if ( pQueue )
  163. {
  164. Assert( pQueue->IsHeapified() );
  165. CVarBitVec used( m_Clusters.Count() );
  166. for ( int i = 0; i < pQueue->Count(); ++i )
  167. {
  168. int nCluster = pQueue->Element( i )->m_nIndex;
  169. Assert( !used.IsBitSet( nCluster ) );
  170. used.Set( nCluster );
  171. }
  172. for ( int i = 0; i < m_Clusters.Count(); ++i )
  173. {
  174. CCluster *pCluster = m_Clusters[ i ];
  175. if ( !used.IsBitSet( i ) )
  176. {
  177. Assert( pCluster->m_Links.Count() == 0 );
  178. }
  179. }
  180. }
  181. for ( int nIndex = 0; nIndex < m_Clusters.Count(); ++nIndex )
  182. {
  183. CCluster *pThisCluster = m_Clusters[ nIndex ];
  184. Assert( pThisCluster->m_nIndex == nIndex );
  185. if ( pThisCluster->m_nChild[ 0 ] < 0 )
  186. {
  187. Assert( pThisCluster->m_nChild[ 1 ] < 0 && pThisCluster->m_nChildLeafs == 1 );
  188. }
  189. else
  190. {
  191. Assert( m_Clusters[ pThisCluster->m_nChild[ 0 ] ]->m_nChildLeafs + m_Clusters[ pThisCluster->m_nChild[ 1 ] ]->m_nChildLeafs == pThisCluster->m_nChildLeafs );
  192. }
  193. ClusterLinkQueue_t &links = pThisCluster->m_Links;
  194. Assert( links.IsHeapified() );
  195. CVarBitVec used( m_Clusters.Count() );
  196. for ( int i = 0; i < links.Count(); ++i )
  197. {
  198. CLink *pThisLink = &links.Element( i );
  199. CCluster *pOtherCluster = pThisLink->m_pOtherCluster;
  200. const CLink *pOtherLink = pOtherCluster->FindLink( pThisCluster );
  201. Assert( pOtherLink );
  202. Assert( pOtherLink->m_pOtherCluster == pThisCluster && pThisLink->m_flCost == pOtherLink->m_flCost ); // can't have a link with self & the cost of link should be the same from both sides
  203. Assert( !used.IsBitSet( pOtherCluster->m_nIndex ) );// can't have 2 links to the same cluster
  204. used.Set( pOtherCluster->m_nIndex );
  205. }
  206. }
  207. #endif
  208. }
  209. void CFeAgglomerator::Process( ClustersPriorityQueue_t &queue )
  210. {
  211. while ( queue.Count() > 0 && queue.ElementAtHead()->HasLinks() )
  212. {
  213. Validate( &queue );
  214. // remove the clusters we're merging from priority queue
  215. CCluster *pChild[ 2 ];
  216. pChild[ 0 ] = queue.ElementAtHead();
  217. pChild[ 1 ] = queue.ElementAtHead()->m_Links.ElementAtHead().m_pOtherCluster;
  218. // remove the children from the queue
  219. Assert( pChild[ 0 ]->m_nPriorityIndex == 0 );
  220. queue.RemoveAtHead(); // removing pChild[0]
  221. queue.RemoveAt( pChild[ 1 ]->m_nPriorityIndex );
  222. pChild[ 0 ]->m_nPriorityIndex = -1;
  223. pChild[ 1 ]->m_nPriorityIndex = -1;
  224. // make the new cluster, link and compute its distances to nearest clusters
  225. int nParentIndex = m_Clusters.AddToTail();
  226. CCluster *pParent = new CCluster( nParentIndex, pChild[ 0 ]->m_nChildLeafs + pChild[ 1 ]->m_nChildLeafs );
  227. m_Clusters[ nParentIndex ] = pParent ;
  228. pParent->m_Aabb = pChild[ 0 ]->m_Aabb + pChild[ 1 ]->m_Aabb;
  229. pParent->m_nChild[ 0 ] = pChild[ 0 ]->m_nIndex;
  230. pParent->m_nChild[ 1 ] = pChild[ 1 ]->m_nIndex;
  231. pChild[ 0 ]->m_nParent = nParentIndex;
  232. pChild[ 1 ]->m_nParent = nParentIndex;
  233. CUtlVectorFixedGrowable< CCluster*, 8 > reAdd;
  234. CVarBitVec skipAddLink( m_Clusters.Count() );
  235. // remove all links to the children, replace them with links to the parent
  236. {
  237. ClusterLinkQueue_t &links = pChild[ 0 ]->m_Links;
  238. for ( int i = 0; i < links.Count(); ++i )
  239. {
  240. CCluster *pOther = links.Element( i ).m_pOtherCluster;
  241. Assert( pOther != pChild[ 0 ] );
  242. if ( pOther == pChild[ 1 ] )
  243. continue; // just skip the connection to the other child
  244. Assert( pOther->m_nPriorityIndex >= 0 );
  245. // we see this cluster for the first time
  246. float flOldDistance = pOther->GetBestCost();
  247. pOther->RemoveLink( pChild[ 0 ] );
  248. pOther->AddLink( pParent );
  249. pParent->AddLink( pOther );
  250. skipAddLink.Set( pOther->m_nIndex );
  251. float flNewDistance = pOther->GetBestCost();
  252. if ( flOldDistance != flNewDistance )
  253. {
  254. reAdd.AddToTail( pOther );
  255. queue.RemoveAt( pOther->m_nPriorityIndex );
  256. pOther->m_nPriorityIndex = -1;
  257. }
  258. }
  259. links.Purge(); // the links don't matter any more, we can free the memory
  260. }
  261. {
  262. ClusterLinkQueue_t &links = pChild[ 1 ]->m_Links;
  263. for ( int i = 0; i < links.Count(); ++i )
  264. {
  265. CCluster *pOther = links.Element( i ).m_pOtherCluster;
  266. Assert( pOther != pChild[ 1 ] );
  267. if ( pOther == pChild[ 0 ] )
  268. continue; // just skip the connection to the other child
  269. if ( pOther->m_nPriorityIndex >= 0 )
  270. {
  271. // we see this cluster for the first time
  272. float flOldDistance = pOther->GetBestCost();
  273. pOther->RemoveLink( pChild[ 1 ] );
  274. // if we saw this pOther already, and didn't remove it from the queue, then we marked it as added
  275. if ( !skipAddLink.IsBitSet( pOther->m_nIndex ) )
  276. {
  277. pOther->AddLink( pParent );
  278. pParent->AddLink( pOther );
  279. }
  280. float flNewDistance = pOther->GetBestCost();
  281. if ( flOldDistance != flNewDistance )
  282. {
  283. queue.RevaluateElement( pOther->m_nPriorityIndex ); // no need to remove, this is the first and last edit of this other element
  284. }
  285. }
  286. else
  287. {
  288. // we've seen this cluster before within this loop, just remove the link; we already added the new link to the new parent
  289. pOther->RemoveLink( pChild[ 1 ] );
  290. }
  291. }
  292. links.Purge(); // the links don't matter any more, we can free the memory
  293. }
  294. for ( int nCluster = 0; nCluster < reAdd.Count(); ++nCluster )
  295. {
  296. queue.Insert( reAdd[nCluster] );
  297. }
  298. queue.Insert( pParent );
  299. Validate( &queue );
  300. }
  301. for ( int i = 0; i < queue.Count(); ++i )
  302. {
  303. Assert( !queue.Element( i )->HasLinks() );
  304. }
  305. }