ContactListenerTest.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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
  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. // Store bodies for later use
  44. mBody[0] = &body1;
  45. mBody[1] = &body2;
  46. mBody[2] = &body3;
  47. mBody[3] = &body4;
  48. }
  49. void ContactListenerTest::PostPhysicsUpdate(float inDeltaTime)
  50. {
  51. // Check that predicted velocities match actual velocities
  52. lock_guard lock(mPredictedVelocitiesMutex);
  53. for (const PredictedVelocity &v : mPredictedVelocities)
  54. {
  55. BodyLockRead body_lock(mPhysicsSystem->GetBodyLockInterface(), v.mBodyID);
  56. if (body_lock.Succeeded())
  57. {
  58. const Body &body = body_lock.GetBody();
  59. Vec3 linear_velocity = body.GetLinearVelocity();
  60. Vec3 angular_velocity = body.GetAngularVelocity();
  61. float diff_v = (v.mLinearVelocity - linear_velocity).Length();
  62. float diff_w = (v.mAngularVelocity - angular_velocity).Length();
  63. if (diff_v > 1.0e-3f || diff_w > 1.0e-3f)
  64. Trace("Mispredicted collision for body: %08x, v=%s, w=%s, predicted_v=%s, predicted_w=%s, diff_v=%f, diff_w=%f",
  65. body.GetID().GetIndex(),
  66. ConvertToString(linear_velocity).c_str(), ConvertToString(angular_velocity).c_str(),
  67. ConvertToString(v.mLinearVelocity).c_str(), ConvertToString(v.mAngularVelocity).c_str(),
  68. (double)diff_v,
  69. (double)diff_w);
  70. }
  71. }
  72. mPredictedVelocities.clear();
  73. }
  74. ValidateResult ContactListenerTest::OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult)
  75. {
  76. // Body 1 and 2 should never collide
  77. return ((&inBody1 == mBody[0] && &inBody2 == mBody[1]) || (&inBody1 == mBody[1] && &inBody2 == mBody[0]))? ValidateResult::RejectAllContactsForThisBodyPair : ValidateResult::AcceptAllContactsForThisBodyPair;
  78. }
  79. void ContactListenerTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
  80. {
  81. // Make body 1 bounce only when a new contact point is added but not when it is persisted (its restitution is normally 0)
  82. if (&inBody1 == mBody[0] || &inBody2 == mBody[0])
  83. {
  84. JPH_ASSERT(ioSettings.mCombinedRestitution == 0.0f);
  85. ioSettings.mCombinedRestitution = 1.0f;
  86. }
  87. // Estimate the contact impulses.
  88. CollisionEstimationResult result;
  89. EstimateCollisionResponse(inBody1, inBody2, inManifold, result, ioSettings.mCombinedFriction, ioSettings.mCombinedRestitution);
  90. // Trace the result
  91. String impulses_str;
  92. for (const CollisionEstimationResult::Impulse &impulse : result.mImpulses)
  93. impulses_str += StringFormat("(%f, %f, %f) ", (double)impulse.mContactImpulse, (double)impulse.mFrictionImpulse1, (double)impulse.mFrictionImpulse2);
  94. Trace("Estimated velocity after collision, body1: %08x, v=%s, w=%s, body2: %08x, v=%s, w=%s, impulses: %s",
  95. inBody1.GetID().GetIndex(), ConvertToString(result.mLinearVelocity1).c_str(), ConvertToString(result.mAngularVelocity1).c_str(),
  96. inBody2.GetID().GetIndex(), ConvertToString(result.mLinearVelocity2).c_str(), ConvertToString(result.mAngularVelocity2).c_str(),
  97. impulses_str.c_str());
  98. // Log predicted velocities
  99. {
  100. lock_guard lock(mPredictedVelocitiesMutex);
  101. mPredictedVelocities.push_back({ inBody1.GetID(), result.mLinearVelocity1, result.mAngularVelocity1 });
  102. mPredictedVelocities.push_back({ inBody2.GetID(), result.mLinearVelocity2, result.mAngularVelocity2 });
  103. }
  104. }