GroupFilterTable.h 4.9 KB

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