GroupFilterTable.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Physics/Collision/GroupFilter.h>
  5. #include <Jolt/Physics/Collision/CollisionGroup.h>
  6. JPH_NAMESPACE_BEGIN
  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. /// Check if the collision between two subgroups is enabled
  73. inline bool IsCollisionEnabled(SubGroupID inSubGroup1, SubGroupID inSubGroup2) const
  74. {
  75. // Test if the bit is set for this group pair
  76. int bit = GetBit(inSubGroup1, inSubGroup2);
  77. return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0;
  78. }
  79. /// Checks if two CollisionGroups collide
  80. virtual bool CanCollide(const CollisionGroup &inGroup1, const CollisionGroup &inGroup2) const override
  81. {
  82. // 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)
  83. if (inGroup1.GetGroupID() == CollisionGroup::cInvalidGroup)
  84. return true;
  85. // If the objects are in different groups, they collide
  86. if (inGroup1.GetGroupID() != inGroup2.GetGroupID())
  87. return true;
  88. // If the collision filters do not match, but they're in the same group we ignore the collision
  89. if (inGroup1.GetGroupFilter() != inGroup2.GetGroupFilter())
  90. return false;
  91. // If they are in the same sub group, they don't collide
  92. if (inGroup1.GetSubGroupID() == inGroup2.GetSubGroupID())
  93. return false;
  94. // Check the bit table
  95. return IsCollisionEnabled(inGroup1.GetSubGroupID(), inGroup2.GetSubGroupID());
  96. }
  97. // See: GroupFilter::SaveBinaryState
  98. virtual void SaveBinaryState(StreamOut &inStream) const override;
  99. protected:
  100. // See: GroupFilter::RestoreBinaryState
  101. virtual void RestoreBinaryState(StreamIn &inStream) override;
  102. private:
  103. uint mNumSubGroups; ///< The number of subgroups that this group filter supports
  104. Array<uint8> mTable; ///< The table of bits that indicates which pairs collide
  105. };
  106. JPH_NAMESPACE_END