SkeletonMapperTest.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <TestFramework.h>
  4. #include <Tests/Rig/SkeletonMapperTest.h>
  5. #include <Jolt/Physics/StateRecorder.h>
  6. #include <Jolt/ObjectStream/ObjectStreamIn.h>
  7. #include <Renderer/DebugRendererImp.h>
  8. #include <Layers.h>
  9. #include <Utils/Log.h>
  10. #include <Application/DebugUI.h>
  11. JPH_IMPLEMENT_RTTI_VIRTUAL(SkeletonMapperTest)
  12. {
  13. JPH_ADD_BASE_CLASS(SkeletonMapperTest, Test)
  14. }
  15. SkeletonMapperTest::~SkeletonMapperTest()
  16. {
  17. mRagdoll->RemoveFromPhysicsSystem();
  18. }
  19. void SkeletonMapperTest::Initialize()
  20. {
  21. // Floor
  22. CreateFloor();
  23. // Load ragdoll
  24. mRagdollSettings = RagdollLoader::sLoad("Assets/Human.tof", EMotionType::Dynamic);
  25. // Create ragdoll
  26. mRagdoll = mRagdollSettings->CreateRagdoll(0, 0, mPhysicsSystem);
  27. mRagdoll->AddToPhysicsSystem(EActivation::Activate);
  28. // Load neutral animation for ragdoll
  29. Ref<SkeletalAnimation> neutral_ragdoll;
  30. if (!ObjectStreamIn::sReadObject("Assets/Human/neutral.tof", neutral_ragdoll))
  31. FatalError("Could not open neutral animation");
  32. // Load animation skeleton
  33. Ref<Skeleton> animation_skeleton;
  34. if (!ObjectStreamIn::sReadObject("Assets/Human/skeleton_hd.tof", animation_skeleton))
  35. FatalError("Could not open skeleton_hd");
  36. animation_skeleton->CalculateParentJointIndices();
  37. // Load neutral animation
  38. Ref<SkeletalAnimation> neutral_animation;
  39. if (!ObjectStreamIn::sReadObject("Assets/Human/neutral_hd.tof", neutral_animation))
  40. FatalError("Could not open neutral_hd animation");
  41. // Load test animation
  42. if (!ObjectStreamIn::sReadObject("Assets/Human/jog_hd.tof", mAnimation))
  43. FatalError("Could not open jog_hd animation");
  44. // Initialize pose
  45. mAnimatedPose.SetSkeleton(animation_skeleton);
  46. mRagdollPose.SetSkeleton(mRagdollSettings->GetSkeleton());
  47. // Calculate neutral poses and initialize skeleton mapper
  48. neutral_ragdoll->Sample(0.0f, mRagdollPose);
  49. mRagdollPose.CalculateJointMatrices();
  50. neutral_animation->Sample(0.0f, mAnimatedPose);
  51. mAnimatedPose.CalculateJointMatrices();
  52. mRagdollToAnimated.Initialize(mRagdollPose.GetSkeleton(), mRagdollPose.GetJointMatrices().data(), mAnimatedPose.GetSkeleton(), mAnimatedPose.GetJointMatrices().data());
  53. // Optionally lock translations (this can be used to prevent ragdolls from stretching)
  54. // Try wildly dragging the ragdoll by the head (using spacebar) to see how the ragdoll stretches under stress
  55. if (sLockTranslations)
  56. mRagdollToAnimated.LockAllTranslations(mAnimatedPose.GetSkeleton(), mAnimatedPose.GetJointMatrices().data());
  57. // Calculate initial pose and set it
  58. CalculateRagdollPose();
  59. mRagdoll->SetPose(mRagdollPose);
  60. }
  61. void SkeletonMapperTest::CalculateRagdollPose()
  62. {
  63. // Sample new animated pose
  64. mAnimation->Sample(mTime, mAnimatedPose);
  65. mAnimatedPose.CalculateJointMatrices();
  66. // Map to ragdoll pose
  67. mRagdollToAnimated.MapReverse(mAnimatedPose.GetJointMatrices().data(), mRagdollPose.GetJointMatrices().data());
  68. mRagdollPose.CalculateJointStates();
  69. }
  70. void SkeletonMapperTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  71. {
  72. // Update time
  73. mTime += inParams.mDeltaTime;
  74. // Drive the ragdoll pose and drive motors to reach it
  75. CalculateRagdollPose();
  76. mRagdoll->DriveToPoseUsingMotors(mRagdollPose);
  77. #ifdef JPH_DEBUG_RENDERER
  78. // Draw animated skeleton
  79. mAnimatedPose.Draw(*inParams.mPoseDrawSettings, mDebugRenderer);
  80. mDebugRenderer->DrawText3D(mAnimatedPose.GetJointMatrix(0).GetTranslation(), "Animated", Color::sWhite, 0.2f);
  81. // Draw mapped skeleton
  82. Mat44 offset = Mat44::sTranslation(Vec3(1.0f, 0, 0));
  83. mRagdollPose.Draw(*inParams.mPoseDrawSettings, mDebugRenderer, offset);
  84. mDebugRenderer->DrawText3D(offset * mAnimatedPose.GetJointMatrix(0).GetTranslation(), "Reverse Mapped", Color::sWhite, 0.2f);
  85. #endif // JPH_DEBUG_RENDERER
  86. // Get ragdoll pose in model space
  87. Array<Mat44> pose1_model(mRagdollPose.GetJointCount());
  88. mRagdoll->GetPose(pose1_model.data());
  89. // Get animated pose in local space
  90. Array<Mat44> pose2_local(mAnimatedPose.GetJointCount());
  91. mAnimatedPose.CalculateLocalSpaceJointMatrices(pose2_local.data());
  92. // Map ragdoll to animated pose, filling in the extra joints using the local space animated pose
  93. SkeletonPose pose2_world;
  94. pose2_world.SetSkeleton(mAnimatedPose.GetSkeleton());
  95. mRagdollToAnimated.Map(pose1_model.data(), pose2_local.data(), pose2_world.GetJointMatrices().data());
  96. #ifdef JPH_DEBUG_RENDERER
  97. // Draw mapped pose
  98. pose2_world.Draw(*inParams.mPoseDrawSettings, mDebugRenderer, offset);
  99. mDebugRenderer->DrawText3D(offset * pose2_world.GetJointMatrix(1).GetTranslation(), "Mapped", Color::sWhite, 0.2f);
  100. #endif // JPH_DEBUG_RENDERER
  101. }
  102. void SkeletonMapperTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  103. {
  104. inUI->CreateCheckBox(inSubMenu, "Lock Translations", sLockTranslations, [this](UICheckBox::EState inState) { sLockTranslations = inState == UICheckBox::STATE_CHECKED; RestartTest(); });
  105. }
  106. void SkeletonMapperTest::SaveState(StateRecorder &inStream) const
  107. {
  108. inStream.Write(mTime);
  109. }
  110. void SkeletonMapperTest::RestoreState(StateRecorder &inStream)
  111. {
  112. inStream.Read(mTime);
  113. }