SoftBodyTests.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2024 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include "UnitTestFramework.h"
  5. #include "PhysicsTestContext.h"
  6. #include "Layers.h"
  7. #include <Jolt/Physics/SoftBody/SoftBodySharedSettings.h>
  8. #include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
  9. #include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>
  10. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  11. TEST_SUITE("SoftBodyTests")
  12. {
  13. TEST_CASE("TestBendConstraint")
  14. {
  15. // Possible values for x3
  16. const Float3 x3_values[] = {
  17. Float3(0, 0, 1), // forming flat plane
  18. Float3(0, 0, -1), // overlapping
  19. Float3(0, 1, 0), // 90 degrees concave
  20. Float3(0, -1, 0), // 90 degrees convex
  21. Float3(0, 1, 1), // 45 degrees concave
  22. Float3(0, -1, -1) // 135 degrees convex
  23. };
  24. for (const Float3 &x3 : x3_values)
  25. {
  26. PhysicsTestContext c;
  27. PhysicsSystem *s = c.GetSystem();
  28. BodyInterface &bi = s->GetBodyInterface();
  29. // Create settings
  30. Ref<SoftBodySharedSettings> shared_settings = new SoftBodySharedSettings;
  31. /* Create two triangles with a shared edge, x3 = free, the rest is locked
  32. x2
  33. e1/ \e3
  34. / \
  35. x0----x1
  36. \ e0 /
  37. e2\ /e4
  38. x3
  39. */
  40. SoftBodySharedSettings::Vertex v;
  41. v.mPosition = Float3(-1, 0, 0);
  42. v.mInvMass = 0;
  43. shared_settings->mVertices.push_back(v);
  44. v.mPosition = Float3(1, 0, 0);
  45. shared_settings->mVertices.push_back(v);
  46. v.mPosition = Float3(0, 0, -1);
  47. shared_settings->mVertices.push_back(v);
  48. v.mPosition = x3;
  49. v.mInvMass = 1;
  50. shared_settings->mVertices.push_back(v);
  51. // Create the 2 triangles
  52. shared_settings->AddFace(SoftBodySharedSettings::Face(0, 1, 2));
  53. shared_settings->AddFace(SoftBodySharedSettings::Face(0, 3, 1));
  54. // Create edge and dihedral constraints
  55. SoftBodySharedSettings::VertexAttributes va;
  56. va.mShearCompliance = FLT_MAX;
  57. va.mBendCompliance = 0;
  58. shared_settings->CreateConstraints(&va, 1, SoftBodySharedSettings::EBendType::Dihedral);
  59. // Optimize the settings
  60. shared_settings->Optimize();
  61. // Create the soft body
  62. SoftBodyCreationSettings sb_settings(shared_settings, RVec3::sZero(), Quat::sIdentity(), Layers::MOVING);
  63. sb_settings.mGravityFactor = 0.0f;
  64. sb_settings.mAllowSleeping = false;
  65. sb_settings.mUpdatePosition = false;
  66. Body &body = *bi.CreateSoftBody(sb_settings);
  67. bi.AddBody(body.GetID(), EActivation::Activate);
  68. SoftBodyMotionProperties *mp = static_cast<SoftBodyMotionProperties *>(body.GetMotionProperties());
  69. // Test 4 angles to see if there are singularities (the dot product between the triangles has the same value for 2 configurations)
  70. for (float angle : { 0.0f, 90.0f, 180.0f, 270.0f })
  71. {
  72. // Perturb x3
  73. Vec3 perturbed_x3(x3);
  74. mp->GetVertex(3).mPosition = 0.5f * (Mat44::sRotationX(DegreesToRadians(angle)) * perturbed_x3);
  75. // Simulate
  76. c.Simulate(0.25f);
  77. // Should return to the original position
  78. CHECK_APPROX_EQUAL(mp->GetVertex(3).mPosition, Vec3(x3), 1.0e-3f);
  79. }
  80. }
  81. }
  82. // Test that applying a force to a soft body and rigid body of the same mass has the same effect
  83. TEST_CASE("TestApplyForce")
  84. {
  85. PhysicsTestContext c;
  86. PhysicsSystem *s = c.GetSystem();
  87. BodyInterface &bi = s->GetBodyInterface();
  88. // Soft body cube
  89. SoftBodyCreationSettings sb_box_settings(SoftBodySharedSettings::sCreateCube(6, 0.2f), RVec3::sZero(), Quat::sIdentity(), Layers::MOVING);
  90. sb_box_settings.mGravityFactor = 0.0f;
  91. sb_box_settings.mLinearDamping = 0.0f;
  92. Body &sb_box = *bi.CreateSoftBody(sb_box_settings);
  93. BodyID sb_id = sb_box.GetID();
  94. bi.AddBody(sb_id, EActivation::Activate);
  95. constexpr float cMass = 216; // 6 * 6 * 6 * 1 kg
  96. CHECK_APPROX_EQUAL(sb_box.GetMotionProperties()->GetInverseMass(), 1.0f / cMass);
  97. // Rigid body cube of same size and mass
  98. const RVec3 cRBBoxPos(0, 2, 0);
  99. BodyCreationSettings rb_box_settings(new BoxShape(Vec3::sReplicate(0.5f)), cRBBoxPos, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
  100. rb_box_settings.mGravityFactor = 0.0f;
  101. rb_box_settings.mLinearDamping = 0.0f;
  102. rb_box_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
  103. rb_box_settings.mMassPropertiesOverride.mMass = cMass;
  104. Body &rb_box = *bi.CreateBody(rb_box_settings);
  105. BodyID rb_id = rb_box.GetID();
  106. bi.AddBody(rb_id, EActivation::Activate);
  107. // Simulate for 3 seconds while applying the same force
  108. constexpr int cNumSteps = 180;
  109. const Vec3 cForce(10000.0f, 0, 0);
  110. for (int i = 0; i < cNumSteps; ++i)
  111. {
  112. bi.AddForce(sb_id, cForce, EActivation::Activate);
  113. bi.AddForce(rb_id, cForce, EActivation::Activate);
  114. c.SimulateSingleStep();
  115. }
  116. // Check that the rigid body moved as expected
  117. const float cTotalTime = cNumSteps * c.GetStepDeltaTime();
  118. const Vec3 cAcceleration = cForce / cMass;
  119. const RVec3 cExpectedPos = c.PredictPosition(cRBBoxPos, Vec3::sZero(), cAcceleration, cTotalTime);
  120. CHECK_APPROX_EQUAL(rb_box.GetPosition(), cExpectedPos);
  121. const Vec3 cExpectedVel = cAcceleration * cTotalTime;
  122. CHECK_APPROX_EQUAL(rb_box.GetLinearVelocity(), cExpectedVel, 1.0e-3f);
  123. CHECK_APPROX_EQUAL(rb_box.GetAngularVelocity(), Vec3::sZero());
  124. // Check that the soft body moved within 1% of that
  125. const RVec3 cExpectedPosSB = cExpectedPos - cRBBoxPos;
  126. CHECK_APPROX_EQUAL(sb_box.GetPosition(), cExpectedPosSB, 0.01f * cExpectedPosSB.Length());
  127. CHECK_APPROX_EQUAL(sb_box.GetLinearVelocity(), cExpectedVel, 2.0e-3f);
  128. CHECK_APPROX_EQUAL(sb_box.GetAngularVelocity(), Vec3::sZero(), 0.01f);
  129. }
  130. }