SimCollideBodyVsBodyTest.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2025 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Tests/General/SimCollideBodyVsBodyTest.h>
  6. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  7. #include <Jolt/Physics/Collision/Shape/MeshShape.h>
  8. #include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
  9. #include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
  10. #include <Jolt/Physics/Collision/CollisionDispatch.h>
  11. #include <Jolt/Physics/Collision/CollideShapeVsShapePerLeaf.h>
  12. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  13. #include <Layers.h>
  14. #include <Renderer/DebugRendererImp.h>
  15. JPH_IMPLEMENT_RTTI_VIRTUAL(SimCollideBodyVsBodyTest)
  16. {
  17. JPH_ADD_BASE_CLASS(SimCollideBodyVsBodyTest, Test)
  18. }
  19. template <class LeafCollector>
  20. static void sCollideBodyVsBodyPerBody(const Body &inBody1, const Body &inBody2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, CollideShapeSettings &ioCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
  21. {
  22. if (inBody1.IsSensor() || inBody2.IsSensor())
  23. {
  24. // A sensor will return max 1 hit per body pair
  25. LeafCollector collector;
  26. SubShapeIDCreator part1, part2;
  27. CollisionDispatch::sCollideShapeVsShape(inBody1.GetShape(), inBody2.GetShape(), Vec3::sOne(), Vec3::sOne(), inCenterOfMassTransform1, inCenterOfMassTransform2, part1, part2, ioCollideShapeSettings, collector);
  28. if (collector.HadHit())
  29. ioCollector.AddHit(collector.mHit);
  30. }
  31. else
  32. {
  33. // If not a sensor: fall back to the default
  34. PhysicsSystem::sDefaultSimCollideBodyVsBody(inBody1, inBody2, inCenterOfMassTransform1, inCenterOfMassTransform2, ioCollideShapeSettings, ioCollector, inShapeFilter);
  35. }
  36. }
  37. template <class LeafCollector>
  38. static void sCollideBodyVsBodyPerLeaf(const Body &inBody1, const Body &inBody2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, CollideShapeSettings &ioCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
  39. {
  40. if (inBody1.IsSensor() || inBody2.IsSensor())
  41. {
  42. // A sensor will return 1 hit per leaf shape pair
  43. SubShapeIDCreator part1, part2;
  44. CollideShapeVsShapePerLeaf<LeafCollector>(inBody1.GetShape(), inBody2.GetShape(), Vec3::sOne(), Vec3::sOne(), inCenterOfMassTransform1, inCenterOfMassTransform2, part1, part2, ioCollideShapeSettings, ioCollector, inShapeFilter);
  45. }
  46. else
  47. {
  48. // If not a sensor: fall back to the default
  49. PhysicsSystem::sDefaultSimCollideBodyVsBody(inBody1, inBody2, inCenterOfMassTransform1, inCenterOfMassTransform2, ioCollideShapeSettings, ioCollector, inShapeFilter);
  50. }
  51. }
  52. void SimCollideBodyVsBodyTest::Initialize()
  53. {
  54. // Create pyramid with flat top
  55. MeshShapeSettings pyramid;
  56. pyramid.mTriangleVertices = { Float3(1, 0, 1), Float3(1, 0, -1), Float3(-1, 0, -1), Float3(-1, 0, 1), Float3(0.1f, 1, 0.1f), Float3(0.1f, 1, -0.1f), Float3(-0.1f, 1, -0.1f), Float3(-0.1f, 1, 0.1f) };
  57. pyramid.mIndexedTriangles = { IndexedTriangle(0, 1, 4), IndexedTriangle(4, 1, 5), IndexedTriangle(1, 2, 5), IndexedTriangle(2, 6, 5), IndexedTriangle(2, 3, 6), IndexedTriangle(3, 7, 6), IndexedTriangle(3, 0, 7), IndexedTriangle(0, 4, 7), IndexedTriangle(4, 5, 6), IndexedTriangle(4, 6, 7) };
  58. pyramid.SetEmbedded();
  59. // Create floor of many pyramids
  60. StaticCompoundShapeSettings compound;
  61. for (int x = -10; x <= 10; ++x)
  62. for (int z = -10; z <= 10; ++z)
  63. compound.AddShape(Vec3(x * 2.0f, 0, z * 2.0f), Quat::sIdentity(), &pyramid);
  64. compound.SetEmbedded();
  65. mBodyInterface->CreateAndAddBody(BodyCreationSettings(&compound, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
  66. // A kinematic sensor that also detects static bodies
  67. BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(10.0f)), RVec3(0, 5, 0), Quat::sIdentity(), EMotionType::Kinematic, Layers::MOVING); // Put in a layer that collides with static
  68. sensor_settings.mIsSensor = true;
  69. sensor_settings.mCollideKinematicVsNonDynamic = true;
  70. sensor_settings.mUseManifoldReduction = false;
  71. mSensorID = mBodyInterface->CreateAndAddBody(sensor_settings, EActivation::Activate);
  72. // Dynamic bodies
  73. for (int i = 0; i < 10; ++i)
  74. mBodyIDs.push_back(mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(0.1f, 0.5f, 0.2f)), RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate));
  75. }
  76. void SimCollideBodyVsBodyTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  77. {
  78. // Update time
  79. mTime += inParams.mDeltaTime;
  80. const char *mode_string = nullptr;
  81. int mode = int(mTime / 3.0f) % 5;
  82. switch (mode)
  83. {
  84. default:
  85. mode_string = "Sensor: Collect all contact points";
  86. mPhysicsSystem->SetSimCollideBodyVsBody(&PhysicsSystem::sDefaultSimCollideBodyVsBody);
  87. break;
  88. case 1:
  89. mode_string = "Sensor: Collect any contact point per body";
  90. mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerBody<AnyHitCollisionCollector<CollideShapeCollector>>);
  91. break;
  92. case 2:
  93. mode_string = "Sensor: Collect deepest contact point per body";
  94. mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerBody<ClosestHitCollisionCollector<CollideShapeCollector>>);
  95. break;
  96. case 3:
  97. mode_string = "Sensor: Collect any contact point per leaf shape";
  98. mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerLeaf<AnyHitCollisionCollector<CollideShapeCollector>>);
  99. break;
  100. case 4:
  101. mode_string = "Sensor: Collect deepest contact point per leaf shape";
  102. mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerLeaf<ClosestHitCollisionCollector<CollideShapeCollector>>);
  103. break;
  104. }
  105. DebugRenderer::sInstance->DrawText3D(RVec3(0, 5, 0), mode_string);
  106. // If the mode changes
  107. if (mode != mPrevMode)
  108. {
  109. // Start all bodies from the top
  110. for (int i = 0; i < (int)mBodyIDs.size(); ++i)
  111. {
  112. BodyID id = mBodyIDs[i];
  113. mBodyInterface->SetPositionRotationAndVelocity(id, RVec3(-4.9_r + i * 1.0_r, 5.0_r, 0), Quat::sIdentity(), Vec3::sZero(), Vec3::sZero());
  114. mBodyInterface->ActivateBody(id);
  115. }
  116. // Invalidate collisions with sensor to refresh contacts
  117. mBodyInterface->InvalidateContactCache(mSensorID);
  118. mPrevMode = mode;
  119. }
  120. }
  121. void SimCollideBodyVsBodyTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
  122. {
  123. if (!inBody1.IsSensor())
  124. {
  125. mDebugRenderer->DrawWirePolygon(RMat44::sTranslation(inManifold.mBaseOffset), inManifold.mRelativeContactPointsOn1, Color::sGreen, 0.01f);
  126. Vec3 average = Vec3::sZero();
  127. for (const Vec3 &p : inManifold.mRelativeContactPointsOn1)
  128. average += p;
  129. average /= (float)inManifold.mRelativeContactPointsOn1.size();
  130. mDebugRenderer->DrawArrow(inManifold.mBaseOffset + average, inManifold.mBaseOffset + average - inManifold.mWorldSpaceNormal, Color::sYellow, 0.1f);
  131. }
  132. if (!inBody2.IsSensor())
  133. {
  134. mDebugRenderer->DrawWirePolygon(RMat44::sTranslation(inManifold.mBaseOffset), inManifold.mRelativeContactPointsOn2, Color::sGreen, 0.01f);
  135. Vec3 average = Vec3::sZero();
  136. for (const Vec3 &p : inManifold.mRelativeContactPointsOn2)
  137. average += p;
  138. average /= (float)inManifold.mRelativeContactPointsOn2.size();
  139. mDebugRenderer->DrawArrow(inManifold.mBaseOffset + average, inManifold.mBaseOffset + average + inManifold.mWorldSpaceNormal, Color::sYellow, 0.1f);
  140. }
  141. }
  142. void SimCollideBodyVsBodyTest::OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
  143. {
  144. OnContactAdded(inBody1, inBody2, inManifold, ioSettings);
  145. }
  146. void SimCollideBodyVsBodyTest::SaveState(StateRecorder &inStream) const
  147. {
  148. inStream.Write(mPrevMode);
  149. inStream.Write(mTime);
  150. }
  151. void SimCollideBodyVsBodyTest::RestoreState(StateRecorder &inStream)
  152. {
  153. inStream.Read(mPrevMode);
  154. inStream.Read(mTime);
  155. }