GroupFilterTable.h 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Physics/Collision/GroupFilter.h>
  5. #include <Physics/Collision/CollisionGroup.h>
  6. namespace JPH {
  7. /// Implementation of GroupFilter that stores a bit table with one bit per sub shape ID pair to determine if they collide or not
  8. ///
  9. /// The collision rules:
  10. /// - If one of the objects is in the cInvalidGroup the objects will collide
  11. /// - If the objects are in different groups they will collide
  12. /// - If they're in the same group but their collision filter is different they will not collide
  13. /// - If they're in the same group and their collision filters match, we'll use the SubGroupID and the table below:
  14. ///
  15. /// For N = 6 sub shapes the table will look like:
  16. ///
  17. /// group 1 --->
  18. /// group 2 x.....
  19. /// | ox....
  20. /// | oox...
  21. /// V ooox..
  22. /// oooox.
  23. /// ooooox
  24. ///
  25. /// x means group 1 == group 2 and we define this to never collide
  26. /// o is a bit that we have to store
  27. /// . is a bit we don't need to store because the table is symmetric, we take care that group 2 > group 1 always by swapping the elements if needed
  28. ///
  29. /// The total number of bits we need to store is (N * (N - 1)) / 2
  30. class GroupFilterTable final : public GroupFilter
  31. {
  32. JPH_DECLARE_SERIALIZABLE_VIRTUAL(GroupFilterTable)
  33. private:
  34. using GroupID = CollisionGroup::GroupID;
  35. using SubGroupID = CollisionGroup::SubGroupID;
  36. /// Get which bit corresponds to the pair (inSubGroup1, inSubGroup2)
  37. int GetBit(SubGroupID inSubGroup1, SubGroupID inSubGroup2) const
  38. {
  39. JPH_ASSERT(inSubGroup1 != inSubGroup2);
  40. // We store the lower left half only, so swap the inputs when trying to access the top right half
  41. if (inSubGroup1 > inSubGroup2)
  42. swap(inSubGroup1, inSubGroup2);
  43. JPH_ASSERT(inSubGroup2 < mNumSubGroups);
  44. // Calculate at which bit the entry for this pair resides
  45. // We use the fact that a row always starts at inSubGroup2 * (inSubGroup2 - 1) / 2
  46. // (this is the amount of bits needed to store a table of inSubGroup2 entries)
  47. return (inSubGroup2 * (inSubGroup2 - 1)) / 2 + inSubGroup1;
  48. }
  49. public:
  50. /// Constructs the table with inNumSubGroups subgroups, initially all collision pairs are enabled except when the sub group ID is the same
  51. explicit GroupFilterTable(uint inNumSubGroups = 0) :
  52. mNumSubGroups(inNumSubGroups)
  53. {
  54. // By default everything collides
  55. int table_size = ((inNumSubGroups * (inNumSubGroups - 1)) / 2 + 7) / 8;
  56. mTable.resize(table_size, 0xff);
  57. }
  58. /// Copy constructor
  59. GroupFilterTable(const GroupFilterTable &inRHS) : mNumSubGroups(inRHS.mNumSubGroups), mTable(inRHS.mTable) { }
  60. /// Disable collision between two sub groups
  61. void DisableCollision(SubGroupID inSubGroup1, SubGroupID inSubGroup2)
  62. {
  63. int bit = GetBit(inSubGroup1, inSubGroup2);
  64. mTable[bit >> 3] &= (0xff ^ (1 << (bit & 0b111)));
  65. }
  66. /// Enable collision between two sub groups
  67. void EnableCollision(SubGroupID inSubGroup1, SubGroupID inSubGroup2)
  68. {
  69. int bit = GetBit(inSubGroup1, inSubGroup2);
  70. mTable[bit >> 3] |= 1 << (bit & 0b111);
  71. }
  72. /// Checks if two CollisionGroups collide
  73. virtual bool CanCollide(const CollisionGroup &inGroup1, const CollisionGroup &inGroup2) const override
  74. {
  75. // If one of the groups is cInvalidGroup the objects will collide (note that the if following this if will ensure that group2 is not cInvalidGroup)
  76. if (inGroup1.GetGroupID() == CollisionGroup::cInvalidGroup)
  77. return true;
  78. // If the objects are in different groups, they collide
  79. if (inGroup1.GetGroupID() != inGroup2.GetGroupID())
  80. return true;
  81. // If the collision filters do not match, but they're in the same group we ignore the collision
  82. if (inGroup1.GetGroupFilter() != inGroup2.GetGroupFilter())
  83. return false;
  84. // If they are in the same sub group, they don't collide
  85. if (inGroup1.GetSubGroupID() == inGroup2.GetSubGroupID())
  86. return false;
  87. // Test if the bit is set for this group pair
  88. int bit = GetBit(inGroup1.GetSubGroupID(), inGroup2.GetSubGroupID());
  89. return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0;
  90. }
  91. // See: GroupFilter::SaveBinaryState
  92. virtual void SaveBinaryState(StreamOut &inStream) const override;
  93. protected:
  94. // See: GroupFilter::RestoreBinaryState
  95. virtual void RestoreBinaryState(StreamIn &inStream) override;
  96. private:
  97. uint mNumSubGroups; ///< The number of subgroups that this group filter supports
  98. vector<uint8> mTable; ///< The table of bits that indicates which pairs collide
  99. };
  100. } // JPH