ObjectLayerPairFilterMaskTests.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2023 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include "UnitTestFramework.h"
  5. #include "Layers.h"
  6. #include "LoggingContactListener.h"
  7. #include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h>
  8. #include <Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h>
  9. #include <Jolt/Physics/Collision/ObjectLayerPairFilterMask.h>
  10. #include <Jolt/Physics/PhysicsSystem.h>
  11. #include <Jolt/Core/JobSystemSingleThreaded.h>
  12. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  13. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  14. TEST_SUITE("ObjectLayerPairFilterMaskTests")
  15. {
  16. TEST_CASE("ObjectLayerPairFilterMaskTest")
  17. {
  18. // Some example layers
  19. constexpr uint32 FilterDefault = 1;
  20. constexpr uint32 FilterStatic = 2;
  21. constexpr uint32 FilterDebris = 4;
  22. constexpr uint32 FilterSensor = 8;
  23. constexpr uint32 FilterAll = FilterDefault | FilterStatic | FilterDebris | FilterSensor;
  24. ObjectLayerPairFilterMask pair_filter;
  25. ObjectLayer layer1 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterDefault, FilterAll);
  26. ObjectLayer layer2 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterStatic, FilterAll);
  27. CHECK(pair_filter.ShouldCollide(layer1, layer2));
  28. CHECK(pair_filter.ShouldCollide(layer2, layer1));
  29. layer1 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterDefault, FilterStatic);
  30. layer2 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterStatic, FilterDefault);
  31. CHECK(pair_filter.ShouldCollide(layer1, layer2));
  32. CHECK(pair_filter.ShouldCollide(layer2, layer1));
  33. layer1 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterDefault, FilterDefault);
  34. layer2 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterStatic, FilterDefault);
  35. CHECK(!pair_filter.ShouldCollide(layer1, layer2));
  36. CHECK(!pair_filter.ShouldCollide(layer2, layer1));
  37. layer1 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterDefault, FilterStatic);
  38. layer2 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterStatic, FilterStatic);
  39. CHECK(!pair_filter.ShouldCollide(layer1, layer2));
  40. CHECK(!pair_filter.ShouldCollide(layer2, layer1));
  41. layer1 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterDefault | FilterDebris, FilterAll);
  42. layer2 = ObjectLayerPairFilterMask::sGetObjectLayer(FilterStatic, FilterStatic);
  43. CHECK(!pair_filter.ShouldCollide(layer1, layer2));
  44. CHECK(!pair_filter.ShouldCollide(layer2, layer1));
  45. BroadPhaseLayerInterfaceMask bp_interface(4);
  46. bp_interface.ConfigureLayer(BroadPhaseLayer(0), FilterDefault, 0); // Default goes to 0
  47. bp_interface.ConfigureLayer(BroadPhaseLayer(1), FilterStatic, FilterSensor); // Static but not sensor goes to 1
  48. bp_interface.ConfigureLayer(BroadPhaseLayer(2), FilterStatic, 0); // Everything else static goes to 2
  49. // Last layer is for everything else
  50. CHECK(bp_interface.GetBroadPhaseLayer(ObjectLayerPairFilterMask::sGetObjectLayer(FilterDefault)) == BroadPhaseLayer(0));
  51. CHECK(bp_interface.GetBroadPhaseLayer(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll)) == BroadPhaseLayer(0));
  52. CHECK(bp_interface.GetBroadPhaseLayer(ObjectLayerPairFilterMask::sGetObjectLayer(FilterStatic)) == BroadPhaseLayer(1));
  53. CHECK(bp_interface.GetBroadPhaseLayer(ObjectLayerPairFilterMask::sGetObjectLayer(FilterStatic | FilterSensor)) == BroadPhaseLayer(2));
  54. CHECK(bp_interface.GetBroadPhaseLayer(ObjectLayerPairFilterMask::sGetObjectLayer(FilterDebris)) == BroadPhaseLayer(3));
  55. ObjectVsBroadPhaseLayerFilterMask bp_filter(bp_interface);
  56. CHECK(bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterDefault), BroadPhaseLayer(0)));
  57. CHECK(!bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterDefault), BroadPhaseLayer(1)));
  58. CHECK(!bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterDefault), BroadPhaseLayer(2)));
  59. CHECK(bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterDefault), BroadPhaseLayer(3)));
  60. CHECK(!bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterStatic), BroadPhaseLayer(0)));
  61. CHECK(bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterStatic), BroadPhaseLayer(1)));
  62. CHECK(bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterStatic), BroadPhaseLayer(2)));
  63. CHECK(bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterStatic), BroadPhaseLayer(3)));
  64. CHECK(!bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterSensor), BroadPhaseLayer(0)));
  65. CHECK(!bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterSensor), BroadPhaseLayer(1)));
  66. CHECK(!bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterSensor), BroadPhaseLayer(2)));
  67. CHECK(bp_filter.ShouldCollide(ObjectLayerPairFilterMask::sGetObjectLayer(FilterAll, FilterSensor), BroadPhaseLayer(3)));
  68. }
  69. TEST_CASE("ThreeFloorTest")
  70. {
  71. // Define the group bits
  72. constexpr uint32 GROUP_STATIC = 1;
  73. constexpr uint32 GROUP_FLOOR1 = 2;
  74. constexpr uint32 GROUP_FLOOR2 = 4;
  75. constexpr uint32 GROUP_FLOOR3 = 8;
  76. constexpr uint32 GROUP_ALL = GROUP_STATIC | GROUP_FLOOR1 | GROUP_FLOOR2 | GROUP_FLOOR3;
  77. ObjectLayerPairFilterMask pair_filter;
  78. constexpr uint NUM_BROAD_PHASE_LAYERS = 2;
  79. BroadPhaseLayer BP_LAYER_STATIC(0);
  80. BroadPhaseLayer BP_LAYER_DYNAMIC(1);
  81. BroadPhaseLayerInterfaceMask bp_interface(NUM_BROAD_PHASE_LAYERS);
  82. bp_interface.ConfigureLayer(BP_LAYER_STATIC, GROUP_STATIC, 0); // Anything that has the static bit set goes into the static broadphase layer
  83. bp_interface.ConfigureLayer(BP_LAYER_DYNAMIC, GROUP_FLOOR1 | GROUP_FLOOR2 | GROUP_FLOOR3, 0); // Anything that has one of the floor bits set goes into the dynamic broadphase layer
  84. ObjectVsBroadPhaseLayerFilterMask bp_filter(bp_interface);
  85. PhysicsSystem system;
  86. system.Init(1024, 0, 1024, 1024, bp_interface, bp_filter, pair_filter);
  87. BodyInterface &body_interface = system.GetBodyInterface();
  88. // Create 3 floors, each colliding with a different group
  89. RefConst<Shape> floor_shape = new BoxShape(Vec3(10, 0.1f, 10));
  90. BodyID ground = body_interface.CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(20, 0.1f, 20)), RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_STATIC, GROUP_FLOOR1)), EActivation::DontActivate);
  91. BodyID floor1 = body_interface.CreateAndAddBody(BodyCreationSettings(floor_shape, RVec3(0, 2, 0), Quat::sIdentity(), EMotionType::Static, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_STATIC, GROUP_FLOOR1)), EActivation::DontActivate);
  92. BodyID floor2 = body_interface.CreateAndAddBody(BodyCreationSettings(floor_shape, RVec3(0, 4, 0), Quat::sIdentity(), EMotionType::Static, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_STATIC, GROUP_FLOOR2)), EActivation::DontActivate);
  93. BodyID floor3 = body_interface.CreateAndAddBody(BodyCreationSettings(floor_shape, RVec3(0, 6, 0), Quat::sIdentity(), EMotionType::Static, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_STATIC, GROUP_FLOOR3)), EActivation::DontActivate);
  94. // Create dynamic bodies, each colliding with a different floor
  95. RefConst<Shape> box_shape = new BoxShape(Vec3::sReplicate(0.5f));
  96. BodyID dynamic_floor1 = body_interface.CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(0, 8, 0), Quat::sIdentity(), EMotionType::Dynamic, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_FLOOR1, GROUP_ALL)), EActivation::Activate);
  97. BodyID dynamic_floor2 = body_interface.CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(0, 9, 0), Quat::sIdentity(), EMotionType::Dynamic, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_FLOOR2, GROUP_ALL)), EActivation::Activate);
  98. BodyID dynamic_floor3 = body_interface.CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_FLOOR3, GROUP_ALL)), EActivation::Activate);
  99. BodyID dynamic_ground = body_interface.CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(15, 8, 0), Quat::sIdentity(), EMotionType::Dynamic, ObjectLayerPairFilterMask::sGetObjectLayer(GROUP_FLOOR1, GROUP_ALL)), EActivation::Activate);
  100. // Start listening to collision events
  101. LoggingContactListener listener;
  102. system.SetContactListener(&listener);
  103. // Simulate long enough for all objects to fall on the ground
  104. TempAllocatorImpl allocator(4 * 1024 * 1024);
  105. JobSystemSingleThreaded job_system(cMaxPhysicsJobs);
  106. for (int i = 0; i < 100; ++i)
  107. system.Update(1.0f/ 60.0f, 1, &allocator, &job_system);
  108. // Dynamic 1 should rest on floor 1
  109. CHECK(listener.Contains(LoggingContactListener::EType::Add, dynamic_floor1, floor1));
  110. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor1, floor2));
  111. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor1, floor3));
  112. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor1, ground));
  113. float tolerance = 1.1f * system.GetPhysicsSettings().mPenetrationSlop;
  114. CHECK_APPROX_EQUAL(body_interface.GetPosition(dynamic_floor1), RVec3(0, 2.6_r, 0), tolerance);
  115. // Dynamic 2 should rest on floor 2
  116. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor2, floor1));
  117. CHECK(listener.Contains(LoggingContactListener::EType::Add, dynamic_floor2, floor2));
  118. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor2, floor3));
  119. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor2, ground));
  120. CHECK_APPROX_EQUAL(body_interface.GetPosition(dynamic_floor2), RVec3(0, 4.6_r, 0), tolerance);
  121. // Dynamic 3 should rest on floor 3
  122. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor3, floor1));
  123. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor3, floor2));
  124. CHECK(listener.Contains(LoggingContactListener::EType::Add, dynamic_floor3, floor3));
  125. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_floor3, ground));
  126. CHECK_APPROX_EQUAL(body_interface.GetPosition(dynamic_floor3), RVec3(0, 6.6_r, 0), tolerance);
  127. // Dynamic 4 should rest on the ground floor
  128. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_ground, floor1));
  129. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_ground, floor2));
  130. CHECK(!listener.Contains(LoggingContactListener::EType::Add, dynamic_ground, floor3));
  131. CHECK(listener.Contains(LoggingContactListener::EType::Add, dynamic_ground, ground));
  132. CHECK_APPROX_EQUAL(body_interface.GetPosition(dynamic_ground), RVec3(15, 0.6_r, 0), tolerance);
  133. }
  134. }