JointLimitOptimizer.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzFramework/Physics/Common/PhysicsJoint.h>
  9. #include <EMotionFX/CommandSystem/Source/JointLimitCommands.h>
  10. #include <EMotionFX/Source/Motion.h>
  11. #include <EMotionFX/Source/MotionData/MotionData.h>
  12. #include <EMotionFX/Source/MotionManager.h>
  13. #include <EMotionFX/Source/Node.h>
  14. #include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
  15. #include <Editor/AnimGraphEditorBus.h>
  16. #include <Editor/Plugins/Ragdoll/JointLimitOptimizer.h>
  17. #include <Editor/Plugins/Ragdoll/JointLimitRotationManipulators.h>
  18. #include <MCore/Source/Command.h>
  19. #include <MCore/Source/MCoreCommandManager.h>
  20. namespace EMotionFX
  21. {
  22. void OptimizeJointLimits(const PhysicsSetupManipulatorData& physicsSetupManipulatorData)
  23. {
  24. if (!physicsSetupManipulatorData.m_actor || !physicsSetupManipulatorData.m_node ||
  25. !physicsSetupManipulatorData.m_jointConfiguration)
  26. {
  27. EMStudio::GetNotificationWindowManager()->CreateNotificationWindow(
  28. EMStudio::NotificationWindow::TYPE_ERROR, "Joint limit optimization <font color=red>failed</font>");
  29. return;
  30. }
  31. auto* jointHelpersInterface = AZ::Interface<AzPhysics::JointHelpersInterface>::Get();
  32. auto* editorJointHelpersInterface = AZ::Interface<AzPhysics::EditorJointHelpersInterface>::Get();
  33. if (!jointHelpersInterface || !editorJointHelpersInterface)
  34. {
  35. EMStudio::GetNotificationWindowManager()->CreateNotificationWindow(
  36. EMStudio::NotificationWindow::TYPE_ERROR,
  37. "Joint limit optimization <font color=red>failed</font> due to missing physics joint interface");
  38. return;
  39. }
  40. // get initial guess to start the solver from
  41. const AZ::Vector3 boneDirection =
  42. GetBoneDirection(physicsSetupManipulatorData.m_actor->GetSkeleton(), physicsSetupManipulatorData.m_node);
  43. const Pose* bindPose = physicsSetupManipulatorData.m_actor->GetSkeleton()->GetBindPose();
  44. const Transform& nodeBindTransform = bindPose->GetModelSpaceTransform(physicsSetupManipulatorData.m_node->GetNodeIndex());
  45. const Transform& parentBindTransform = physicsSetupManipulatorData.m_node->GetParentNode()
  46. ? bindPose->GetModelSpaceTransform(physicsSetupManipulatorData.m_node->GetParentIndex())
  47. : Transform::CreateIdentity();
  48. const AZ::Quaternion& nodeBindRotationWorld = nodeBindTransform.m_rotation;
  49. const AZ::Quaternion& parentBindRotationWorld = parentBindTransform.m_rotation;
  50. AZStd::unique_ptr<AzPhysics::JointConfiguration> jointInitialConfiguration =
  51. jointHelpersInterface->ComputeInitialJointLimitConfiguration(
  52. physicsSetupManipulatorData.m_jointConfiguration->RTTI_GetType(),
  53. parentBindRotationWorld,
  54. nodeBindRotationWorld,
  55. boneDirection,
  56. {});
  57. if (!jointInitialConfiguration)
  58. {
  59. EMStudio::GetNotificationWindowManager()->CreateNotificationWindow(
  60. EMStudio::NotificationWindow::TYPE_ERROR,
  61. "Computing initial configuration for joint limit optimization <font color=red>failed</font>");
  62. return;
  63. }
  64. // check how many motions are available to help decide how many samples to take per motion
  65. MotionManager& motionManager = EMotionFX::GetMotionManager();
  66. const size_t numMotionSets = motionManager.GetNumMotionSets();
  67. int numMotions = 0;
  68. for (size_t motionSetIndex = 0; motionSetIndex < numMotionSets; motionSetIndex++)
  69. {
  70. const MotionSet* motionSet = motionManager.GetMotionSet(motionSetIndex);
  71. const MotionSet::MotionEntries& motionEntries = motionSet->GetMotionEntries();
  72. for (const auto& motionEntryPair : motionEntries)
  73. {
  74. MotionSet::MotionEntry* motionEntry = motionEntryPair.second;
  75. motionSet->LoadMotion(motionEntry);
  76. const Motion* motion = motionEntry->GetMotion();
  77. if (!motion || !motion->GetMotionData())
  78. {
  79. continue;
  80. }
  81. const AZ::Outcome<size_t> jointIndexOutcome =
  82. motion->GetMotionData()->FindJointIndexByName(physicsSetupManipulatorData.m_node->GetNameString());
  83. numMotions += jointIndexOutcome.IsSuccess();
  84. }
  85. }
  86. if (numMotions == 0)
  87. {
  88. EMStudio::GetNotificationWindowManager()->CreateNotificationWindow(
  89. EMStudio::NotificationWindow::TYPE_WARNING,
  90. "Please ensure a motion set is loaded in order to perform joint limit optimization");
  91. return;
  92. }
  93. // now do the actual sampling of rotation values
  94. const int numSamplesPerMotion = AZ::GetMin(JointLimitOptimizerMaxSamplesPerMotion, JointLimitOptimizerTotalSamples / numMotions);
  95. const int sampleCount = numMotions * numSamplesPerMotion;
  96. AZStd::vector<AZ::Quaternion> localRotationSamples;
  97. localRotationSamples.reserve(sampleCount);
  98. for (size_t motionSetIndex = 0; motionSetIndex < numMotionSets; motionSetIndex++)
  99. {
  100. const MotionSet* motionSet = motionManager.GetMotionSet(motionSetIndex);
  101. const MotionSet::MotionEntries& motionEntries = motionSet->GetMotionEntries();
  102. for (const auto& motionEntryPair : motionEntries)
  103. {
  104. MotionSet::MotionEntry* motionEntry = motionEntryPair.second;
  105. motionSet->LoadMotion(motionEntry);
  106. const Motion* motion = motionEntry->GetMotion();
  107. if (!motion || !motion->GetMotionData())
  108. {
  109. continue;
  110. }
  111. const MotionData* motionData = motion->GetMotionData();
  112. const AZ::Outcome<size_t> jointIndexOutcome =
  113. motionData->FindJointIndexByName(physicsSetupManipulatorData.m_node->GetNameString());
  114. if (jointIndexOutcome.IsSuccess())
  115. {
  116. const float duration = motion->GetDuration();
  117. for (int sampleIndex = 0; sampleIndex < numSamplesPerMotion; sampleIndex++)
  118. {
  119. localRotationSamples.push_back(motionData->SampleJointRotation(
  120. (sampleIndex * duration) / aznumeric_cast<float>(numSamplesPerMotion), jointIndexOutcome.GetValue()));
  121. }
  122. }
  123. }
  124. }
  125. // perform the optimization
  126. AZStd::unique_ptr<AzPhysics::JointConfiguration> optimizedJointLimit =
  127. editorJointHelpersInterface->ComputeOptimalJointLimit(jointInitialConfiguration.get(), localRotationSamples);
  128. if (!optimizedJointLimit)
  129. {
  130. EMStudio::GetNotificationWindowManager()->CreateNotificationWindow(
  131. EMStudio::NotificationWindow::TYPE_ERROR, "Joint limit optimization <font color=red>failed</font>");
  132. return;
  133. }
  134. MCore::CommandGroup commandGroup;
  135. commandGroup.SetGroupName("Adjust joint limit");
  136. const AZ::u32 actorId = physicsSetupManipulatorData.m_actor->GetID();
  137. const AZStd::string& nodeName = physicsSetupManipulatorData.m_node->GetNameString();
  138. CommandAdjustJointLimit* command = aznew CommandAdjustJointLimit(actorId, nodeName);
  139. commandGroup.AddCommand(command);
  140. command->SetOldJointConfiguration(physicsSetupManipulatorData.m_jointConfiguration);
  141. command->SetJointConfiguration(optimizedJointLimit.get());
  142. AZStd::string result;
  143. CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result);
  144. commandGroup.Clear();
  145. }
  146. } // namespace EMotionFX