SoftBodySkinnedConstraintTest.cpp 5.3 KB

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