JointSwingLimitManipulators.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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/Configuration/JointConfiguration.h>
  9. #include <AzFramework/Viewport/ViewportColors.h>
  10. #include <AzFramework/Viewport/ViewportConstants.h>
  11. #include <AzToolsFramework/Manipulators/ManipulatorManager.h>
  12. #include <AzToolsFramework/Manipulators/ManipulatorView.h>
  13. #include <EMotionFX/CommandSystem/Source/JointLimitCommands.h>
  14. #include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
  15. #include <Editor/Plugins/Ragdoll/JointLimitRotationManipulators.h>
  16. #include <Editor/Plugins/Ragdoll/JointSwingLimitManipulators.h>
  17. #include <Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h>
  18. namespace EMotionFX
  19. {
  20. static constexpr float ManipulatorOffsetX = 0.2f; // manipulator position offset along the joint parent frame's X axis
  21. static constexpr float ManipulatorScale = 400.0f; // scaling factor between linear position of manipulator and swing limit in degrees
  22. static constexpr float ManipulatorInverseScale = 1.0f / ManipulatorScale;
  23. void JointSwingLimitManipulators::Setup(const PhysicsSetupManipulatorData& physicsSetupManipulatorData)
  24. {
  25. m_physicsSetupManipulatorData = physicsSetupManipulatorData;
  26. if (!m_physicsSetupManipulatorData.HasJointLimit())
  27. {
  28. return;
  29. }
  30. AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
  31. AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, m_viewportId);
  32. m_debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
  33. // swing limit Y manipulator
  34. m_swingYManipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity());
  35. m_swingYManipulator->SetAxis(AZ::Vector3::CreateAxisZ());
  36. m_swingYManipulator->Register(EMStudio::g_animManipulatorManagerId);
  37. {
  38. AzToolsFramework::ManipulatorViews views;
  39. views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(
  40. AzFramework::ViewportColors::DefaultManipulatorHandleColor, AzFramework::ViewportConstants::DefaultManipulatorHandleSize));
  41. m_swingYManipulator->SetViews(AZStd::move(views));
  42. }
  43. m_swingYManipulator->InstallLeftMouseDownCallback(
  44. [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
  45. {
  46. BeginEditing();
  47. });
  48. m_swingYManipulator->InstallMouseMoveCallback(
  49. [this](const AzToolsFramework::LinearManipulator::Action& action)
  50. {
  51. if (m_jointSwingLimitState.m_swingLimitY.has_value())
  52. {
  53. const float newSwingLimitY = ManipulatorScale * action.LocalPosition().GetZ();
  54. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("SwingLimitY"), newSwingLimitY);
  55. // get the value again, in case it is different from the value set due to validation
  56. AZStd::optional<float> validatedSwingLimitY =
  57. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitY"));
  58. if (validatedSwingLimitY.has_value())
  59. {
  60. m_swingYManipulator->SetLocalPosition(
  61. AZ::Vector3(ManipulatorOffsetX, 0.0f, ManipulatorInverseScale * validatedSwingLimitY.value()));
  62. }
  63. InvalidateEditorValues();
  64. }
  65. });
  66. m_swingYManipulator->InstallLeftMouseUpCallback(
  67. [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
  68. {
  69. EndEditing();
  70. });
  71. // swing limit Z manipulator
  72. m_swingZManipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity());
  73. m_swingZManipulator->SetAxis(AZ::Vector3::CreateAxisY());
  74. m_swingZManipulator->Register(EMStudio::g_animManipulatorManagerId);
  75. {
  76. AzToolsFramework::ManipulatorViews views;
  77. views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(
  78. AzFramework::ViewportColors::DefaultManipulatorHandleColor, AzFramework::ViewportConstants::DefaultManipulatorHandleSize));
  79. m_swingZManipulator->SetViews(AZStd::move(views));
  80. }
  81. m_swingZManipulator->InstallLeftMouseDownCallback(
  82. [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
  83. {
  84. BeginEditing();
  85. });
  86. m_swingZManipulator->InstallMouseMoveCallback(
  87. [this](const AzToolsFramework::LinearManipulator::Action& action)
  88. {
  89. if (m_jointSwingLimitState.m_swingLimitZ.has_value())
  90. {
  91. const float newSwingLimitZ = ManipulatorScale * action.LocalPosition().GetY();
  92. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("SwingLimitZ"), newSwingLimitZ);
  93. // get the value again, in case it is different from the value set due to validation
  94. AZStd::optional<float> validatedSwingLimitZ =
  95. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitZ"));
  96. if (validatedSwingLimitZ.has_value())
  97. {
  98. m_swingZManipulator->SetLocalPosition(
  99. AZ::Vector3(ManipulatorOffsetX, ManipulatorInverseScale * validatedSwingLimitZ.value(), 0.0f));
  100. }
  101. InvalidateEditorValues();
  102. }
  103. });
  104. m_swingZManipulator->InstallLeftMouseUpCallback(
  105. [this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
  106. {
  107. EndEditing();
  108. });
  109. Refresh();
  110. AZ::TickBus::Handler::BusConnect();
  111. PhysicsSetupManipulatorRequestBus::Handler::BusConnect();
  112. m_adjustJointLimitCallback = AZStd::make_unique<PhysicsSetupManipulatorCommandCallback>(this, false);
  113. EMStudio::GetCommandManager()->RegisterCommandCallback("AdjustJointLimit", m_adjustJointLimitCallback.get());
  114. }
  115. void JointSwingLimitManipulators::Refresh()
  116. {
  117. if (m_physicsSetupManipulatorData.HasJointLimit())
  118. {
  119. const AZ::Transform parentWorldTransform = m_physicsSetupManipulatorData.GetJointParentFrameWorld();
  120. AZStd::optional<float> swingLimitY =
  121. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitY"));
  122. if (swingLimitY.has_value())
  123. {
  124. m_swingYManipulator->SetSpace(parentWorldTransform);
  125. m_swingYManipulator->SetLocalPosition(AZ::Vector3(ManipulatorOffsetX, 0.0f, ManipulatorInverseScale * swingLimitY.value()));
  126. }
  127. AZStd::optional<float> swingLimitZ =
  128. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitZ"));
  129. if (swingLimitZ.has_value())
  130. {
  131. m_swingZManipulator->SetSpace(parentWorldTransform);
  132. m_swingZManipulator->SetLocalPosition(AZ::Vector3(ManipulatorOffsetX, ManipulatorInverseScale * swingLimitZ.value(), 0.0f));
  133. }
  134. }
  135. }
  136. void JointSwingLimitManipulators::Teardown()
  137. {
  138. if (!m_physicsSetupManipulatorData.HasJointLimit())
  139. {
  140. return;
  141. }
  142. EMStudio::GetCommandManager()->RemoveCommandCallback(m_adjustJointLimitCallback.get(), false);
  143. m_adjustJointLimitCallback.reset();
  144. PhysicsSetupManipulatorRequestBus::Handler::BusDisconnect();
  145. AZ::TickBus::Handler::BusDisconnect();
  146. if (m_swingYManipulator)
  147. {
  148. m_swingYManipulator->Unregister();
  149. }
  150. if (m_swingZManipulator)
  151. {
  152. m_swingZManipulator->Unregister();
  153. }
  154. m_swingYManipulator.reset();
  155. m_swingZManipulator.reset();
  156. m_debugDisplay = nullptr;
  157. }
  158. void JointSwingLimitManipulators::ResetValues()
  159. {
  160. if (m_physicsSetupManipulatorData.HasJointLimit())
  161. {
  162. BeginEditing();
  163. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("SwingLimitY"), 45.0f);
  164. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("SwingLimitZ"), 45.0f);
  165. EndEditing();
  166. Refresh();
  167. }
  168. }
  169. void JointSwingLimitManipulators::InvalidateEditorValues()
  170. {
  171. if (m_physicsSetupManipulatorData.m_jointLimitWidget)
  172. {
  173. m_physicsSetupManipulatorData.m_jointLimitWidget->InvalidateValues();
  174. }
  175. }
  176. void JointSwingLimitManipulators::OnUnderlyingPropertiesChanged()
  177. {
  178. Refresh();
  179. }
  180. void JointSwingLimitManipulators::BeginEditing()
  181. {
  182. m_jointSwingLimitState.m_swingLimitY =
  183. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitY"));
  184. m_jointSwingLimitState.m_swingLimitZ =
  185. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitZ"));
  186. CreateCommandAdjustJointLimit(m_commandGroup, m_physicsSetupManipulatorData);
  187. }
  188. void JointSwingLimitManipulators::EndEditing()
  189. {
  190. ExecuteCommandAdjustJointLimit(m_commandGroup, m_physicsSetupManipulatorData);
  191. }
  192. void JointSwingLimitManipulators::OnTick([[maybe_unused]] float delta, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
  193. {
  194. if (!m_debugDisplay || !m_physicsSetupManipulatorData.HasJointLimit())
  195. {
  196. return;
  197. }
  198. AZStd::optional<float> swingLimitY = m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitY"));
  199. AZStd::optional<float> swingLimitZ = m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("SwingLimitZ"));
  200. if (!swingLimitY.has_value() && !swingLimitZ.has_value())
  201. {
  202. return;
  203. }
  204. AZ::u32 previousState = m_debugDisplay->GetState();
  205. m_debugDisplay->CullOff();
  206. m_debugDisplay->SetColor(AZ::Colors::White);
  207. m_debugDisplay->PushMatrix(m_physicsSetupManipulatorData.GetJointParentFrameWorld());
  208. if (swingLimitY.has_value())
  209. {
  210. m_debugDisplay->DrawLine(
  211. AZ::Vector3(ManipulatorOffsetX, 0.0f, 0.0f),
  212. AZ::Vector3(ManipulatorOffsetX, 0.0f, ManipulatorInverseScale * swingLimitY.value()));
  213. }
  214. if (swingLimitZ.has_value())
  215. {
  216. m_debugDisplay->DrawLine(
  217. AZ::Vector3(ManipulatorOffsetX, 0.0f, 0.0f),
  218. AZ::Vector3(ManipulatorOffsetX, ManipulatorInverseScale * swingLimitZ.value(), 0.0f));
  219. }
  220. m_debugDisplay->PopMatrix();
  221. m_debugDisplay->SetState(previousState);
  222. }
  223. } // namespace EMotionFX