SoftBodySkinnedConstraintTest.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2024 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Tests/SoftBody/SoftBodySkinnedConstraintTest.h>
  6. #include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
  7. #include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>
  8. #include <Utils/SoftBodyCreator.h>
  9. #include <Layers.h>
  10. #include <Renderer/DebugRendererImp.h>
  11. #include <Application/DebugUI.h>
  12. JPH_IMPLEMENT_RTTI_VIRTUAL(SoftBodySkinnedConstraintTest)
  13. {
  14. JPH_ADD_BASE_CLASS(SoftBodySkinnedConstraintTest, Test)
  15. }
  16. Array<Mat44> SoftBodySkinnedConstraintTest::GetWorldSpacePose(float inTime) const
  17. {
  18. Array<Mat44> pose;
  19. pose.resize(cNumJoints);
  20. // Create local space pose
  21. pose[0] = Mat44::sTranslation(Vec3(0.0f, cBodyPosY, -0.5f * (cNumVerticesZ - 1) * cVertexSpacing));
  22. for (int i = 1; i < cNumJoints; ++i)
  23. {
  24. float amplitude = 0.25f * min(inTime, 2.0f); // Fade effect in over time
  25. Mat44 rotation = Mat44::sRotationX(amplitude * Sin(0.25f * JPH_PI * i + 2.0f * inTime));
  26. Mat44 translation = Mat44::sTranslation(Vec3(0, 0, (cNumVerticesZ - 1) * cVertexSpacing / (cNumJoints - 1)));
  27. pose[i] = rotation * translation;
  28. }
  29. // Convert to world space
  30. for (int i = 1; i < cNumJoints; ++i)
  31. pose[i] = pose[i - 1] * pose[i];
  32. return pose;
  33. }
  34. void SoftBodySkinnedConstraintTest::SkinVertices(bool inHardSkinAll)
  35. {
  36. RMat44 com = mBody->GetCenterOfMassTransform();
  37. // Make pose relative to the center of mass of the body
  38. Array<Mat44> pose = GetWorldSpacePose(mTime);
  39. Mat44 offset = com.InversedRotationTranslation().ToMat44();
  40. for (Mat44 &m : pose)
  41. m = offset * m;
  42. SoftBodyMotionProperties *mp = static_cast<SoftBodyMotionProperties *>(mBody->GetMotionProperties());
  43. mp->SetEnableSkinConstraints(sEnableSkinConstraints);
  44. mp->SetSkinnedMaxDistanceMultiplier(sMaxDistanceMultiplier);
  45. mp->SkinVertices(com, pose.data(), cNumJoints, inHardSkinAll, *mTempAllocator);
  46. }
  47. void SoftBodySkinnedConstraintTest::Initialize()
  48. {
  49. CreateFloor();
  50. // Where we'll place the body
  51. RVec3 body_translation(0.0f, cBodyPosY, 0);
  52. // Make first and last row kinematic
  53. auto inv_mass = [](uint, uint inZ) { return inZ == 0 || inZ == cNumVerticesZ - 1? 0.0f : 1.0f; };
  54. Ref<SoftBodySharedSettings> settings = SoftBodyCreator::CreateCloth(cNumVerticesX, cNumVerticesZ, cVertexSpacing, inv_mass);
  55. // Make edges soft
  56. for (SoftBodySharedSettings::Edge &e : settings->mEdgeConstraints)
  57. e.mCompliance = 1.0e-3f;
  58. // Create inverse bind matrices by moving the bind pose to the center of mass space for the body
  59. Array<Mat44> bind_pose = GetWorldSpacePose(0.0f);
  60. Mat44 offset = Mat44::sTranslation(Vec3(-body_translation));
  61. for (Mat44 &m : bind_pose)
  62. m = offset * m;
  63. for (int i = 0; i < cNumJoints; ++i)
  64. settings->mInvBindMatrices.push_back(SoftBodySharedSettings::InvBind(i, bind_pose[i].Inversed()));
  65. // Create skinned vertices
  66. auto get_vertex = [](uint inX, uint inZ) { return inX + inZ * cNumVerticesX; };
  67. for (int z = 0; z < cNumVerticesZ; ++z)
  68. for (int x = 0; x < cNumVerticesX; ++x)
  69. {
  70. uint vertex_idx = get_vertex(x, z);
  71. SoftBodySharedSettings::Skinned skinned(vertex_idx, settings->mVertices[vertex_idx].mInvMass > 0.0f? 2.0f : 0.0f, 0.1f, 40.0f);
  72. // Find closest joints
  73. int closest_joint = -1, prev_closest_joint = -1;
  74. float closest_joint_dist = FLT_MAX, prev_closest_joint_dist = FLT_MAX;
  75. for (int i = 0; i < cNumJoints; ++i)
  76. {
  77. float dist = abs(settings->mVertices[vertex_idx].mPosition.z - bind_pose[i].GetTranslation().GetZ());
  78. if (dist < closest_joint_dist)
  79. {
  80. prev_closest_joint = closest_joint;
  81. prev_closest_joint_dist = closest_joint_dist;
  82. closest_joint = i;
  83. closest_joint_dist = dist;
  84. }
  85. else if (dist < prev_closest_joint_dist)
  86. {
  87. prev_closest_joint = i;
  88. prev_closest_joint_dist = dist;
  89. }
  90. }
  91. if (closest_joint_dist == 0.0f)
  92. {
  93. // Hard skin to closest joint
  94. skinned.mWeights[0] = SoftBodySharedSettings::SkinWeight(closest_joint, 1.0f);
  95. }
  96. else
  97. {
  98. // Skin to two closest joints
  99. skinned.mWeights[0] = SoftBodySharedSettings::SkinWeight(closest_joint, 1.0f / closest_joint_dist);
  100. skinned.mWeights[1] = SoftBodySharedSettings::SkinWeight(prev_closest_joint, 1.0f / prev_closest_joint_dist);
  101. skinned.NormalizeWeights();
  102. }
  103. settings->mSkinnedConstraints.push_back(skinned);
  104. }
  105. // Calculate the information needed for skinned constraints
  106. settings->CalculateSkinnedConstraintNormals();
  107. // Create the body
  108. SoftBodyCreationSettings cloth(settings, body_translation, Quat::sIdentity(), Layers::MOVING);
  109. mBody = mBodyInterface->CreateSoftBody(cloth);
  110. mBodyInterface->AddBody(mBody->GetID(), EActivation::Activate);
  111. // Initially hard skin all vertices to the pose
  112. SkinVertices(true);
  113. }
  114. void SoftBodySkinnedConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  115. {
  116. // Draw the pose pre step
  117. Array<Mat44> pose = GetWorldSpacePose(mTime);
  118. for (int i = 1; i < cNumJoints; ++i)
  119. {
  120. mDebugRenderer->DrawArrow(RVec3(pose[i - 1].GetTranslation()), RVec3(pose[i].GetTranslation()), Color::sGreen, 0.1f);
  121. mDebugRenderer->DrawCoordinateSystem(RMat44(pose[i]), 0.5f);
  122. }
  123. // Update time
  124. mTime += inParams.mDeltaTime;
  125. // Calculate skinned vertices but do not hard skin them
  126. SkinVertices(false);
  127. }
  128. void SoftBodySkinnedConstraintTest::SaveState(StateRecorder &inStream) const
  129. {
  130. inStream.Write(mTime);
  131. }
  132. void SoftBodySkinnedConstraintTest::RestoreState(StateRecorder &inStream)
  133. {
  134. inStream.Read(mTime);
  135. }
  136. void SoftBodySkinnedConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  137. {
  138. inUI->CreateCheckBox(inSubMenu, "Enable Skin Constraints", sEnableSkinConstraints, [](UICheckBox::EState inState) { sEnableSkinConstraints = inState == UICheckBox::STATE_CHECKED; });
  139. inUI->CreateSlider(inSubMenu, "Max Distance Multiplier", sMaxDistanceMultiplier, 0.0f, 10.0f, 0.1f, [](float inValue) { sMaxDistanceMultiplier = inValue; });
  140. }