SensorTests.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include "UnitTestFramework.h"
  4. #include "PhysicsTestContext.h"
  5. #include "Layers.h"
  6. #include "LoggingContactListener.h"
  7. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  8. TEST_SUITE("SensorTests")
  9. {
  10. using LogEntry = LoggingContactListener::LogEntry;
  11. using EType = LoggingContactListener::EType;
  12. TEST_CASE("TestDynamicVsSensor")
  13. {
  14. PhysicsTestContext c;
  15. c.ZeroGravity();
  16. // Register listener
  17. LoggingContactListener listener;
  18. c.GetSystem()->SetContactListener(&listener);
  19. // Sensor
  20. BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(1)), Vec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
  21. sensor_settings.mIsSensor = true;
  22. BodyID sensor_id = c.GetBodyInterface().CreateAndAddBody(sensor_settings, EActivation::DontActivate);
  23. // Dynamic body moving downwards
  24. Body &dynamic = c.CreateBox(Vec3(0, 2, 0), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, Vec3::sReplicate(0.5f));
  25. dynamic.SetLinearVelocity(Vec3(0, -1, 0));
  26. // After a single step the dynamic object should not have touched the sensor yet
  27. c.SimulateSingleStep();
  28. CHECK(listener.GetEntryCount() == 0);
  29. // After half a second we should be touching the sensor
  30. c.Simulate(0.5f);
  31. CHECK(listener.Contains(EType::Add, dynamic.GetID(), sensor_id));
  32. listener.Clear();
  33. // The next step we require that the contact persists
  34. c.SimulateSingleStep();
  35. CHECK(listener.Contains(EType::Persist, dynamic.GetID(), sensor_id));
  36. listener.Clear();
  37. // After 3 more seconds we should have left the sensor at the bottom side
  38. c.Simulate(3.0f + c.GetDeltaTime());
  39. CHECK(listener.Contains(EType::Remove, dynamic.GetID(), sensor_id));
  40. }
  41. TEST_CASE("TestKinematicVsSensor")
  42. {
  43. PhysicsTestContext c;
  44. // Register listener
  45. LoggingContactListener listener;
  46. c.GetSystem()->SetContactListener(&listener);
  47. // Sensor
  48. BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(1)), Vec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
  49. sensor_settings.mIsSensor = true;
  50. BodyID sensor_id = c.GetBodyInterface().CreateAndAddBody(sensor_settings, EActivation::DontActivate);
  51. // Kinematic body moving downwards
  52. Body &kinematic = c.CreateBox(Vec3(0, 2, 0), Quat::sIdentity(), EMotionType::Kinematic, EMotionQuality::Discrete, Layers::MOVING, Vec3::sReplicate(0.5f));
  53. kinematic.SetLinearVelocity(Vec3(0, -1, 0));
  54. // After a single step the kinematic object should not have touched the sensor yet
  55. c.SimulateSingleStep();
  56. CHECK(listener.GetEntryCount() == 0);
  57. // After half a second we should be touching the sensor
  58. c.Simulate(0.5f);
  59. CHECK(listener.Contains(EType::Add, kinematic.GetID(), sensor_id));
  60. listener.Clear();
  61. // The next step we require that the contact persists
  62. c.SimulateSingleStep();
  63. CHECK(listener.Contains(EType::Persist, kinematic.GetID(), sensor_id));
  64. listener.Clear();
  65. // After 3 more seconds we should have left the sensor at the bottom side
  66. c.Simulate(3.0f + c.GetDeltaTime());
  67. CHECK(listener.Contains(EType::Remove, kinematic.GetID(), sensor_id));
  68. }
  69. TEST_CASE("TestDynamicSleepingVsStaticSensor")
  70. {
  71. PhysicsTestContext c;
  72. // Register listener
  73. LoggingContactListener listener;
  74. c.GetSystem()->SetContactListener(&listener);
  75. // Sensor
  76. BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(1)), Vec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
  77. sensor_settings.mIsSensor = true;
  78. Body &sensor = *c.GetBodyInterface().CreateBody(sensor_settings);
  79. c.GetBodyInterface().AddBody(sensor.GetID(), EActivation::DontActivate);
  80. // Floor
  81. Body &floor = c.CreateFloor();
  82. // Dynamic body on floor (make them penetrate)
  83. Body &dynamic = c.CreateBox(Vec3(0, 0.5f - c.GetSystem()->GetPhysicsSettings().mMaxPenetrationDistance, 0), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, Vec3::sReplicate(0.5f), EActivation::DontActivate);
  84. // After a single step (because the object is sleeping) there should not be a contact
  85. c.SimulateSingleStep();
  86. CHECK(listener.GetEntryCount() == 0);
  87. // Activate the body
  88. c.GetBodyInterface().ActivateBody(dynamic.GetID());
  89. // After a single step we should have detected the collision with the floor and the sensor
  90. c.SimulateSingleStep();
  91. CHECK(listener.GetEntryCount() == 4);
  92. CHECK(listener.Contains(EType::Validate, floor.GetID(), dynamic.GetID()));
  93. CHECK(listener.Contains(EType::Add, floor.GetID(), dynamic.GetID()));
  94. CHECK(listener.Contains(EType::Validate, sensor.GetID(), dynamic.GetID()));
  95. CHECK(listener.Contains(EType::Add, sensor.GetID(), dynamic.GetID()));
  96. listener.Clear();
  97. // After a second the body should have gone to sleep and the contacts should have been removed
  98. c.Simulate(1.0f);
  99. CHECK(!dynamic.IsActive());
  100. CHECK(listener.Contains(EType::Remove, floor.GetID(), dynamic.GetID()));
  101. CHECK(listener.Contains(EType::Remove, sensor.GetID(), dynamic.GetID()));
  102. }
  103. TEST_CASE("TestDynamicSleepingVsKinematicSensor")
  104. {
  105. PhysicsTestContext c;
  106. // Register listener
  107. LoggingContactListener listener;
  108. c.GetSystem()->SetContactListener(&listener);
  109. // Kinematic sensor that is active (so will keep detecting contacts with sleeping bodies)
  110. BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(1)), Vec3::sZero(), Quat::sIdentity(), EMotionType::Kinematic, Layers::NON_MOVING);
  111. sensor_settings.mIsSensor = true;
  112. Body &sensor = *c.GetBodyInterface().CreateBody(sensor_settings);
  113. c.GetBodyInterface().AddBody(sensor.GetID(), EActivation::Activate);
  114. // Floor
  115. Body &floor = c.CreateFloor();
  116. // Dynamic body on floor (make them penetrate)
  117. Body &dynamic = c.CreateBox(Vec3(0, 0.5f - c.GetSystem()->GetPhysicsSettings().mMaxPenetrationDistance, 0), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, Vec3::sReplicate(0.5f), EActivation::DontActivate);
  118. // After a single step, there should be a contact with the sensor only (the sensor is active)
  119. c.SimulateSingleStep();
  120. CHECK(listener.GetEntryCount() == 2);
  121. CHECK(listener.Contains(EType::Validate, sensor.GetID(), dynamic.GetID()));
  122. CHECK(listener.Contains(EType::Add, sensor.GetID(), dynamic.GetID()));
  123. listener.Clear();
  124. // The sensor should be in its own island
  125. CHECK(sensor.GetMotionProperties()->GetIslandIndexInternal() != Body::cInactiveIndex);
  126. CHECK(dynamic.GetMotionProperties()->GetIslandIndexInternal() == Body::cInactiveIndex);
  127. // Activate the body
  128. c.GetBodyInterface().ActivateBody(dynamic.GetID());
  129. // After a single step we should have detected collision with the floor and the collision with the sensor should have persisted
  130. c.SimulateSingleStep();
  131. CHECK(listener.GetEntryCount() == 3);
  132. CHECK(listener.Contains(EType::Validate, floor.GetID(), dynamic.GetID()));
  133. CHECK(listener.Contains(EType::Add, floor.GetID(), dynamic.GetID()));
  134. CHECK(listener.Contains(EType::Persist, sensor.GetID(), dynamic.GetID()));
  135. listener.Clear();
  136. // The sensor should not be part of the same island as the dynamic body (they won't interact, so this is not needed)
  137. CHECK(sensor.GetMotionProperties()->GetIslandIndexInternal() != Body::cInactiveIndex);
  138. CHECK(dynamic.GetMotionProperties()->GetIslandIndexInternal() != Body::cInactiveIndex);
  139. CHECK(sensor.GetMotionProperties()->GetIslandIndexInternal() != dynamic.GetMotionProperties()->GetIslandIndexInternal());
  140. // After a second the body should have gone to sleep and the contacts with the floor should have been removed, but not with the sensor
  141. c.Simulate(1.0f);
  142. CHECK(!dynamic.IsActive());
  143. CHECK(listener.Contains(EType::Remove, floor.GetID(), dynamic.GetID()));
  144. CHECK(!listener.Contains(EType::Remove, sensor.GetID(), dynamic.GetID()));
  145. }
  146. }