JointTwistLimitManipulators.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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 <AzToolsFramework/Manipulators/ManipulatorManager.h>
  11. #include <AzToolsFramework/Manipulators/ManipulatorView.h>
  12. #include <EMotionFX/CommandSystem/Source/JointLimitCommands.h>
  13. #include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
  14. #include <Editor/Plugins/Ragdoll/JointLimitRotationManipulators.h>
  15. #include <Editor/Plugins/Ragdoll/JointTwistLimitManipulators.h>
  16. #include <Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h>
  17. namespace EMotionFX
  18. {
  19. static const float ManipulatorAxisLength = 0.2f;
  20. static const float ManipulatorRadius = 0.5f;
  21. static const float ManipulatorWidth = 0.05f;
  22. static const float ManipulatorQuadWidth = 0.1f;
  23. float GetAngleDeltaDegrees(const AzToolsFramework::AngularManipulator::Action& action)
  24. {
  25. float angleDeltaRadians = 0.0f;
  26. AZ::Vector3 axis = AZ::Vector3::CreateZero();
  27. action.m_current.m_delta.ConvertToAxisAngle(axis, angleDeltaRadians);
  28. // reverse the direction of the angle if the axis has been flipped by ConvertToAxisAngle
  29. if (axis.GetX() < 0.0f)
  30. {
  31. angleDeltaRadians = -angleDeltaRadians;
  32. }
  33. return AZ::RadToDeg(angleDeltaRadians);
  34. }
  35. void JointTwistLimitManipulators::Setup(const PhysicsSetupManipulatorData& physicsSetupManipulatorData)
  36. {
  37. m_physicsSetupManipulatorData = physicsSetupManipulatorData;
  38. if (!m_physicsSetupManipulatorData.HasJointLimit())
  39. {
  40. return;
  41. }
  42. AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
  43. AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, m_viewportId);
  44. m_debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
  45. const AZ::Transform parentWorldTransform = m_physicsSetupManipulatorData.GetJointParentFrameWorld();
  46. // lower limit manipulator
  47. m_twistLimitLowerManipulator = AzToolsFramework::AngularManipulator::MakeShared(parentWorldTransform);
  48. m_twistLimitLowerManipulator->SetAxis(AZ::Vector3::CreateAxisX());
  49. m_twistLimitLowerManipulator->SetLocalPosition(AZ::Vector3::CreateAxisX(0.5f * ManipulatorAxisLength));
  50. m_twistLimitLowerManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
  51. *m_twistLimitLowerManipulator, AzPhysics::JointVisualizationDefaults::ColorFirst, ManipulatorRadius, ManipulatorWidth,
  52. AzToolsFramework::DrawHalfDottedCircle));
  53. m_twistLimitLowerManipulator->InstallLeftMouseDownCallback(
  54. [this]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action)
  55. {
  56. BeginEditing();
  57. });
  58. m_twistLimitLowerManipulator->InstallMouseMoveCallback(
  59. [this](const AzToolsFramework::AngularManipulator::Action& action)
  60. {
  61. if (!m_jointTwistLimitState.m_twistLimitLower.has_value())
  62. {
  63. return;
  64. }
  65. float angleDeltaDegrees = GetAngleDeltaDegrees(action);
  66. float newTwistLimitLower = m_jointTwistLimitState.m_twistLimitLower.value() + angleDeltaDegrees;
  67. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("TwistLimitLower"), newTwistLimitLower);
  68. InvalidateEditorValues();
  69. });
  70. m_twistLimitLowerManipulator->InstallLeftMouseUpCallback(
  71. [this]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action)
  72. {
  73. EndEditing();
  74. });
  75. m_twistLimitLowerManipulator->Register(EMStudio::g_animManipulatorManagerId);
  76. // upper limit manipulator
  77. m_twistLimitUpperManipulator = AzToolsFramework::AngularManipulator::MakeShared(parentWorldTransform);
  78. m_twistLimitUpperManipulator->SetAxis(AZ::Vector3::CreateAxisX());
  79. m_twistLimitUpperManipulator->SetLocalPosition(AZ::Vector3::CreateAxisX(-0.5f * ManipulatorAxisLength));
  80. m_twistLimitUpperManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
  81. *m_twistLimitUpperManipulator, AzPhysics::JointVisualizationDefaults::ColorSecond, ManipulatorRadius, ManipulatorWidth,
  82. AzToolsFramework::DrawHalfDottedCircle));
  83. m_twistLimitUpperManipulator->InstallLeftMouseDownCallback(
  84. [this]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action)
  85. {
  86. BeginEditing();
  87. });
  88. m_twistLimitUpperManipulator->InstallMouseMoveCallback(
  89. [this](const AzToolsFramework::AngularManipulator::Action& action)
  90. {
  91. if (!m_jointTwistLimitState.m_twistLimitUpper.has_value())
  92. {
  93. return;
  94. }
  95. float angleDeltaDegrees = GetAngleDeltaDegrees(action);
  96. float newTwistLimitUpper = m_jointTwistLimitState.m_twistLimitUpper.value() + angleDeltaDegrees;
  97. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("TwistLimitUpper"), newTwistLimitUpper);
  98. InvalidateEditorValues();
  99. });
  100. m_twistLimitUpperManipulator->InstallLeftMouseUpCallback(
  101. [this]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action)
  102. {
  103. EndEditing();
  104. });
  105. m_twistLimitUpperManipulator->Register(EMStudio::g_animManipulatorManagerId);
  106. AZ::TickBus::Handler::BusConnect();
  107. PhysicsSetupManipulatorRequestBus::Handler::BusConnect();
  108. m_adjustJointLimitCallback = AZStd::make_unique<PhysicsSetupManipulatorCommandCallback>(this, false);
  109. EMStudio::GetCommandManager()->RegisterCommandCallback("AdjustJointLimit", m_adjustJointLimitCallback.get());
  110. }
  111. void JointTwistLimitManipulators::Refresh()
  112. {
  113. if (m_physicsSetupManipulatorData.HasJointLimit())
  114. {
  115. const AZ::Transform parentWorldTransform = m_physicsSetupManipulatorData.GetJointParentFrameWorld();
  116. m_twistLimitLowerManipulator->SetSpace(parentWorldTransform);
  117. m_twistLimitUpperManipulator->SetSpace(parentWorldTransform);
  118. }
  119. }
  120. void JointTwistLimitManipulators::Teardown()
  121. {
  122. EMStudio::GetCommandManager()->RemoveCommandCallback(m_adjustJointLimitCallback.get(), false);
  123. m_adjustJointLimitCallback.reset();
  124. PhysicsSetupManipulatorRequestBus::Handler::BusDisconnect();
  125. AZ::TickBus::Handler::BusDisconnect();
  126. if (m_twistLimitLowerManipulator)
  127. {
  128. m_twistLimitLowerManipulator->Unregister();
  129. }
  130. if (m_twistLimitUpperManipulator)
  131. {
  132. m_twistLimitUpperManipulator->Unregister();
  133. }
  134. m_twistLimitLowerManipulator.reset();
  135. m_twistLimitUpperManipulator.reset();
  136. m_debugDisplay = nullptr;
  137. }
  138. void JointTwistLimitManipulators::ResetValues()
  139. {
  140. if (m_physicsSetupManipulatorData.HasJointLimit())
  141. {
  142. BeginEditing();
  143. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("TwistLimitLower"), -45.0f);
  144. m_physicsSetupManipulatorData.m_jointConfiguration->SetPropertyValue(AZ::Name("TwistLimitUpper"), 45.0f);
  145. EndEditing();
  146. Refresh();
  147. }
  148. }
  149. void JointTwistLimitManipulators::OnTick([[maybe_unused]] float delta, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
  150. {
  151. if (!m_debugDisplay || !m_physicsSetupManipulatorData.HasJointLimit())
  152. {
  153. return;
  154. }
  155. AZStd::optional<float> twistLimitLower =
  156. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("TwistLimitLower"));
  157. AZStd::optional<float> twistLimitUpper =
  158. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("TwistLimitUpper"));
  159. if (!twistLimitLower.has_value() && !twistLimitUpper.has_value())
  160. {
  161. return;
  162. }
  163. const AZStd::array<AZ::Vector3, 4> points = {
  164. AZ::Vector3(-0.5f * ManipulatorAxisLength, 0.0f, 0.0f),
  165. AZ::Vector3(0.5f * ManipulatorAxisLength, 0.0f, 0.0f),
  166. AZ::Vector3(0.5f * ManipulatorAxisLength, ManipulatorQuadWidth, 0.0f),
  167. AZ::Vector3(-0.5f * ManipulatorAxisLength, ManipulatorQuadWidth, 0.0f)
  168. };
  169. AZ::u32 previousState = m_debugDisplay->GetState();
  170. m_debugDisplay->CullOff();
  171. m_debugDisplay->SetAlpha(AzPhysics::JointVisualizationDefaults::Alpha);
  172. m_debugDisplay->PushMatrix(m_physicsSetupManipulatorData.GetJointParentFrameWorld());
  173. if (twistLimitLower.has_value())
  174. {
  175. AZ::Quaternion twistLimitLowerRotation =
  176. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(twistLimitLower.value()));
  177. AZ::Transform twistLimitLowerTM = AZ::Transform::CreateFromQuaternion(twistLimitLowerRotation);
  178. m_debugDisplay->PushMatrix(twistLimitLowerTM);
  179. m_debugDisplay->SetColor(AzPhysics::JointVisualizationDefaults::ColorFirst);
  180. m_debugDisplay->DrawQuad(points[0], points[1], points[2], points[3]);
  181. m_debugDisplay->PopMatrix();
  182. }
  183. if (twistLimitUpper.has_value())
  184. {
  185. AZ::Quaternion twistLimitUpperRotation =
  186. AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(twistLimitUpper.value()));
  187. AZ::Transform twistLimitUpperTM = AZ::Transform::CreateFromQuaternion(twistLimitUpperRotation);
  188. m_debugDisplay->PushMatrix(twistLimitUpperTM);
  189. m_debugDisplay->SetColor(AzPhysics::JointVisualizationDefaults::ColorSecond);
  190. m_debugDisplay->DrawQuad(points[0], points[1], points[2], points[3]);
  191. m_debugDisplay->PopMatrix();
  192. }
  193. m_debugDisplay->PopMatrix();
  194. m_debugDisplay->SetState(previousState);
  195. }
  196. void JointTwistLimitManipulators::OnUnderlyingPropertiesChanged()
  197. {
  198. Refresh();
  199. }
  200. void JointTwistLimitManipulators::BeginEditing()
  201. {
  202. m_jointTwistLimitState.m_twistLimitLower =
  203. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("TwistLimitLower"));
  204. m_jointTwistLimitState.m_twistLimitUpper =
  205. m_physicsSetupManipulatorData.m_jointConfiguration->GetPropertyValue(AZ::Name("TwistLimitUpper"));
  206. CreateCommandAdjustJointLimit(m_commandGroup, m_physicsSetupManipulatorData);
  207. }
  208. void JointTwistLimitManipulators::EndEditing()
  209. {
  210. ExecuteCommandAdjustJointLimit(m_commandGroup, m_physicsSetupManipulatorData);
  211. }
  212. void JointTwistLimitManipulators::InvalidateEditorValues()
  213. {
  214. if (m_physicsSetupManipulatorData.m_jointLimitWidget)
  215. {
  216. m_physicsSetupManipulatorData.m_jointLimitWidget->InvalidateValues();
  217. }
  218. }
  219. } // namespace EMotionFX