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.

151 lines
5.7 KiB

  1. //========= Copyright � Valve Corporation, All rights reserved. ============//
  2. //
  3. // Agglomerative clustering algorithm variant suitable for FE deformable body collision detection
  4. // Clusters given set of points, with given connectivity, bottom-up (agglomerative clustering, see http://en.wikipedia.org/wiki/Hierarchical_clustering).
  5. //
  6. // All connected elements are first merged into a few big clusters. When there's no more connectivity left, brute-force N^3 aggomerative clustering algorithm
  7. // merges the remaining few elements into a large tree. This formulation is especially invented to work with FeModelBuilder : the bigger clusters correspond
  8. // to multiple disconnected pieces of cloth that can move about freely without destroying their respective cluster spacial coherence. Within those clusters,
  9. // the nodes correspond to connected pieces of cloth, so that the sub-clusters do not expand uncontrollably during simulation.
  10. //
  11. // All in all, it's much simpler and hopefully faster than the classical agglomerative clustering, because it takes domain information into account
  12. //
  13. #ifndef FE_AGGLOMERATOR_HDR
  14. #define FE_AGGLOMERATOR_HDR
  15. ////////////////////////////////////////////////////////////////////////////
  16. //
  17. // Agglomerative clustering can be solved in N^3. If we only limit ourselves to m links to check, it may be faster
  18. // We can use an unordered list/vector to store connectivity or distances between clusters, or we can use a small tree sorted by Distance
  19. // We can also keep all clusters in a heap, to make searching the "best" cluster O(1) and deleting clusters O(lnN)
  20. //
  21. // To maintain links between clusters, if we keep them in heaps, we can just lazily delete outdated links to clusters that have been agglomerated into bigger clusters
  22. // We can simply insert new links as we go, and when we need to find the "Cluster closest to given cluster", we'll just remove outdated links from the top of the heap until we find a non-outdated link
  23. //
  24. // To maintain the priority queue of all clusters (sorted by the "closest cluster distance" property), we can, again, lazily delete agglomerated clusters as they come to the top of the heap.
  25. //
  26. // This way, we can improve O( N^2m + Nm^2 ) algorithm (with lists) to O( N m ln m ) (with heaps)
  27. // To simplify, we can use only the global heap of clusters to find the best cluster in O(1) instead of O(N), and unordered list for the links between clusters, making it O( Nm^2 )
  28. // for cloth, typical N~= 400, m ~= 8..40, so the difference between O(N m lnm ) and O(Nm^2) is a factor of 4..10
  29. //
  30. // NOTE: it's much cleaner to use RB trees (or something like that) in place of heaps above. Lazy-delete nodes in heaps will mean the same cluster will be in the heap in multiple places, and we need to keep track of outdated entries
  31. // Also, it's cleaner (but slower) to use heaps and update them (by percolating the corresponding elements when clusters change connectivity)
  32. //
  33. #include "tier1/utlpriorityqueue.h"
  34. #include "mathlib/aabb.h"
  35. class CFeAgglomerator
  36. {
  37. public:
  38. CFeAgglomerator( uint nReserveNodes );
  39. ~CFeAgglomerator();
  40. enum ConstEnum_t { INVALID_INDEX = -1 };
  41. class CCluster;
  42. class CLink
  43. {
  44. public:
  45. CCluster *m_pOtherCluster;
  46. // the metric of cost of this node, proportional to the probability of collision with a feature-sized object
  47. float m_flCost;
  48. };
  49. struct LinkLessFunc_t
  50. {
  51. bool operator()( const CLink &lhs, const CLink &rhs, bool( *lessFuncPtr )( CLink const&, CLink const& ) )
  52. {
  53. return lhs.m_flCost > rhs.m_flCost;
  54. /*
  55. if ( lhs.m_flDistance > rhs.m_flDistance )
  56. return true;
  57. if ( lhs.m_flDistance == rhs.m_flDistance )
  58. return lhs.m_pOtherCluster < rhs.m_pOtherCluster;
  59. return false;
  60. */
  61. }
  62. };
  63. typedef CUtlPriorityQueue< CLink, LinkLessFunc_t >ClusterLinkQueue_t;
  64. class CCluster
  65. {
  66. public:
  67. int m_nIndex;
  68. int m_nPriorityIndex;
  69. int m_nChildLeafs;
  70. AABB_t m_Aabb;
  71. int m_nParent;
  72. int m_nChild[ 2 ];
  73. ClusterLinkQueue_t m_Links;
  74. public:
  75. CCluster( int nIndex, int nLeafCount );
  76. bool HasLinks()const;
  77. float GetBestCost()const;
  78. void RemoveLink( CCluster *pOtherCluster );
  79. const CLink *FindLink( CCluster *pOtherCluster );
  80. float ComputeCost( const Vector &vSize, int nChildLeafs );
  81. void AddLink( CCluster *pOtherCluster );
  82. };
  83. struct ClusterLessFunc_t
  84. {
  85. bool operator()( const CCluster *pLeft, const CCluster *pRight, bool( lessFuncPtr )( CCluster*const&, CCluster*const& ) )
  86. {
  87. return pLeft->GetBestCost() > pRight->GetBestCost();
  88. }
  89. };
  90. struct ClusterSetIndexFunc_t
  91. {
  92. inline static void SetIndex( CCluster* heapElement, int nNewIndex )
  93. {
  94. heapElement->m_nPriorityIndex = nNewIndex;
  95. }
  96. };
  97. int GetClusterCount() const { return m_Clusters.Count(); }
  98. CCluster *GetCluster( int nIndex ) const { return m_Clusters[ nIndex ]; }
  99. public:
  100. // Call this to register all links between all nodes before building agglomerated clusters
  101. CCluster* SetNode( int nIndex, const Vector &origin )
  102. {
  103. if ( !m_Clusters[ nIndex ] )
  104. {
  105. ( m_Clusters[ nIndex ] = new CCluster( nIndex, 1 ) )->m_Aabb.SetToPoint( origin );
  106. }
  107. else
  108. {
  109. m_Clusters[ nIndex ]->m_Aabb.SetToPoint( origin );
  110. }
  111. return m_Clusters[ nIndex ];
  112. }
  113. typedef CUtlPriorityQueue< CCluster*, ClusterLessFunc_t, CUtlMemory< CCluster* >, ClusterSetIndexFunc_t > ClustersPriorityQueue_t;
  114. void LinkNodes( int nNode0, int nNode1 ); // call before building priority queue
  115. void Build( bool bSingleRoot );
  116. protected:
  117. void Process( ClustersPriorityQueue_t &queue );
  118. void Validate( ClustersPriorityQueue_t *pQueue = NULL );
  119. void AddLink( CCluster* pCluster0, CCluster *pCluster1, ClustersPriorityQueue_t &queue );
  120. protected:
  121. // the first N clusters are the original nodes that have no children,
  122. // the next N-1 (or less) clusters are the parents
  123. CUtlVector< CCluster* > m_Clusters;
  124. };
  125. #endif //FE_AGGLOMERATOR_HDR