ContactListenerTest.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Tests/General/ContactListenerTest.h>
  6. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  7. #include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
  8. #include <Jolt/Physics/Collision/Shape/SphereShape.h>
  9. #include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
  10. #include <Jolt/Physics/Collision/EstimateCollisionResponse.h>
  11. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  12. #include <Layers.h>
  13. #include <Renderer/DebugRendererImp.h>
  14. JPH_IMPLEMENT_RTTI_VIRTUAL(ContactListenerTest)
  15. {
  16. JPH_ADD_BASE_CLASS(ContactListenerTest, Test)
  17. }
  18. void ContactListenerTest::Initialize()
  19. {
  20. // Floor
  21. CreateFloor();
  22. RefConst<Shape> box_shape = new BoxShape(Vec3(0.5f, 1.0f, 2.0f));
  23. // Dynamic body 1, this body will have restitution 1 for new contacts and restitution 0 for persisting contacts
  24. Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box_shape, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
  25. body1.SetAllowSleeping(false);
  26. mBodyInterface->AddBody(body1.GetID(), EActivation::Activate);
  27. // Dynamic body 2
  28. Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box_shape, RVec3(5, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
  29. body2.SetAllowSleeping(false);
  30. mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
  31. // Dynamic body 3
  32. Body &body3 = *mBodyInterface->CreateBody(BodyCreationSettings(new SphereShape(2.0f), RVec3(10, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
  33. body3.SetAllowSleeping(false);
  34. mBodyInterface->AddBody(body3.GetID(), EActivation::Activate);
  35. // Dynamic body 4
  36. Ref<StaticCompoundShapeSettings> compound_shape = new StaticCompoundShapeSettings;
  37. compound_shape->AddShape(Vec3::sZero(), Quat::sIdentity(), new CapsuleShape(5, 1));
  38. compound_shape->AddShape(Vec3(0, -5, 0), Quat::sIdentity(), new SphereShape(2));
  39. compound_shape->AddShape(Vec3(0, 5, 0), Quat::sIdentity(), new SphereShape(2));
  40. Body &body4 = *mBodyInterface->CreateBody(BodyCreationSettings(compound_shape, RVec3(15, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
  41. body4.SetAllowSleeping(false);
  42. mBodyInterface->AddBody(body4.GetID(), EActivation::Activate);
  43. // Dynamic body 5, a cube with a bigger cube surrounding it that acts as a sensor
  44. Ref<StaticCompoundShapeSettings> compound_shape2 = new StaticCompoundShapeSettings;
  45. compound_shape2->AddShape(Vec3::sZero(), Quat::sIdentity(), new BoxShape(Vec3::sReplicate(1)));
  46. compound_shape2->AddShape(Vec3::sZero(), Quat::sIdentity(), new BoxShape(Vec3::sReplicate(2))); // This will become a sensor in the contact callback
  47. BodyCreationSettings bcs5(compound_shape2, RVec3(20, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
  48. bcs5.mUseManifoldReduction = false; // Needed in order to prevent the physics system from combining contacts between sensor and non-sensor sub shapes
  49. Body &body5 = *mBodyInterface->CreateBody(bcs5);
  50. body5.SetAllowSleeping(false);
  51. mBodyInterface->AddBody(body5.GetID(), EActivation::Activate);
  52. // Store bodies for later use
  53. mBody[0] = &body1;
  54. mBody[1] = &body2;
  55. mBody[2] = &body3;
  56. mBody[3] = &body4;
  57. mBody[4] = &body5;
  58. }
  59. void ContactListenerTest::PostPhysicsUpdate(float inDeltaTime)
  60. {
  61. // Check that predicted velocities match actual velocities
  62. lock_guard lock(mPredictedVelocitiesMutex);
  63. for (const PredictedVelocity &v : mPredictedVelocities)
  64. {
  65. BodyLockRead body_lock(mPhysicsSystem->GetBodyLockInterface(), v.mBodyID);
  66. if (body_lock.Succeeded())
  67. {
  68. const Body &body = body_lock.GetBody();
  69. Vec3 linear_velocity = body.GetLinearVelocity();
  70. Vec3 angular_velocity = body.GetAngularVelocity();
  71. float diff_v = (v.mLinearVelocity - linear_velocity).Length();
  72. float diff_w = (v.mAngularVelocity - angular_velocity).Length();
  73. if (diff_v > 1.0e-3f || diff_w > 1.0e-3f)
  74. Trace("Mispredicted collision for body: %08x, v=%s, w=%s, predicted_v=%s, predicted_w=%s, diff_v=%f, diff_w=%f",
  75. body.GetID().GetIndex(),
  76. ConvertToString(linear_velocity).c_str(), ConvertToString(angular_velocity).c_str(),
  77. ConvertToString(v.mLinearVelocity).c_str(), ConvertToString(v.mAngularVelocity).c_str(),
  78. (double)diff_v,
  79. (double)diff_w);
  80. }
  81. }
  82. mPredictedVelocities.clear();
  83. }
  84. ValidateResult ContactListenerTest::OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult)
  85. {
  86. // Body 1 and 2 should never collide
  87. return ((&inBody1 == mBody[0] && &inBody2 == mBody[1]) || (&inBody1 == mBody[1] && &inBody2 == mBody[0]))? ValidateResult::RejectAllContactsForThisBodyPair : ValidateResult::AcceptAllContactsForThisBodyPair;
  88. }
  89. void ContactListenerTest::MakeBody5PartialSensor(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
  90. {
  91. // Make the 2nd shape of body 5 a sensor
  92. SubShapeID body5_subshape_1 = StaticCast<CompoundShape>(mBody[4]->GetShape())->GetSubShapeIDFromIndex(1, SubShapeIDCreator()).GetID();
  93. if ((&inBody1 == mBody[4] && inManifold.mSubShapeID1 == body5_subshape_1)
  94. || (&inBody2 == mBody[4] && inManifold.mSubShapeID2 == body5_subshape_1))
  95. {
  96. Trace("Sensor contact detected between body %08x and body %08x", inBody1.GetID().GetIndexAndSequenceNumber(), inBody2.GetID().GetIndexAndSequenceNumber());
  97. ioSettings.mIsSensor = true;
  98. }
  99. }
  100. void ContactListenerTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
  101. {
  102. // Make body 1 bounce only when a new contact point is added but not when it is persisted (its restitution is normally 0)
  103. if (&inBody1 == mBody[0] || &inBody2 == mBody[0])
  104. {
  105. JPH_ASSERT(ioSettings.mCombinedRestitution == 0.0f);
  106. ioSettings.mCombinedRestitution = 1.0f;
  107. }
  108. MakeBody5PartialSensor(inBody1, inBody2, inManifold, ioSettings);
  109. // Estimate the contact impulses.
  110. CollisionEstimationResult result;
  111. EstimateCollisionResponse(inBody1, inBody2, inManifold, result, ioSettings.mCombinedFriction, ioSettings.mCombinedRestitution);
  112. // Trace the result
  113. String impulses_str;
  114. for (const CollisionEstimationResult::Impulse &impulse : result.mImpulses)
  115. impulses_str += StringFormat("(%f, %f, %f) ", (double)impulse.mContactImpulse, (double)impulse.mFrictionImpulse1, (double)impulse.mFrictionImpulse2);
  116. Trace("Estimated velocity after collision, body1: %08x, v=%s, w=%s, body2: %08x, v=%s, w=%s, impulses: %s",
  117. inBody1.GetID().GetIndex(), ConvertToString(result.mLinearVelocity1).c_str(), ConvertToString(result.mAngularVelocity1).c_str(),
  118. inBody2.GetID().GetIndex(), ConvertToString(result.mLinearVelocity2).c_str(), ConvertToString(result.mAngularVelocity2).c_str(),
  119. impulses_str.c_str());
  120. // Log predicted velocities
  121. {
  122. lock_guard lock(mPredictedVelocitiesMutex);
  123. mPredictedVelocities.push_back({ inBody1.GetID(), result.mLinearVelocity1, result.mAngularVelocity1 });
  124. mPredictedVelocities.push_back({ inBody2.GetID(), result.mLinearVelocity2, result.mAngularVelocity2 });
  125. }
  126. }
  127. void ContactListenerTest::OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
  128. {
  129. MakeBody5PartialSensor(inBody1, inBody2, inManifold, ioSettings);
  130. }