RagdollCommandTests.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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/ShapeConfiguration.h"
  9. #include <ActorFixture.h>
  10. #include <AzFramework/Physics/Ragdoll.h>
  11. #include <MCore/Source/CommandGroup.h>
  12. #include <EMotionFX/Source/Actor.h>
  13. #include <EMotionFX/CommandSystem/Source/CommandManager.h>
  14. #include <EMotionFX/CommandSystem/Source/RagdollCommands.h>
  15. #include <EMotionFX/CommandSystem/Source/ColliderCommands.h>
  16. #include <Tests/D6JointLimitConfiguration.h>
  17. #include <Tests/Mocks/PhysicsSystem.h>
  18. namespace EMotionFX
  19. {
  20. class RagdollCommandTests : public ActorFixture
  21. {
  22. public:
  23. void SetUp() override
  24. {
  25. using ::testing::_;
  26. ActorFixture::SetUp();
  27. D6JointLimitConfiguration::Reflect(GetSerializeContext());
  28. EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeIds)
  29. .WillRepeatedly(testing::Return(AZStd::vector<AZ::TypeId>{ azrtti_typeid<D6JointLimitConfiguration>() }));
  30. EXPECT_CALL(m_jointHelpers, GetSupportedJointTypeId(_))
  31. .WillRepeatedly(
  32. [](AzPhysics::JointType jointType) -> AZStd::optional<const AZ::TypeId>
  33. {
  34. if (jointType == AzPhysics::JointType::D6Joint)
  35. {
  36. return azrtti_typeid<D6JointLimitConfiguration>();
  37. }
  38. return AZStd::nullopt;
  39. });
  40. EXPECT_CALL(m_jointHelpers, ComputeInitialJointLimitConfiguration(_, _, _, _, _))
  41. .WillRepeatedly([]([[maybe_unused]] const AZ::TypeId& jointLimitTypeId,
  42. [[maybe_unused]] const AZ::Quaternion& parentWorldRotation,
  43. [[maybe_unused]] const AZ::Quaternion& childWorldRotation,
  44. [[maybe_unused]] const AZ::Vector3& axis,
  45. [[maybe_unused]] const AZStd::vector<AZ::Quaternion>& exampleLocalRotations)
  46. { return AZStd::make_unique<D6JointLimitConfiguration>(); });
  47. }
  48. protected:
  49. Physics::MockJointHelpersInterface m_jointHelpers;
  50. };
  51. AZStd::vector<AZStd::string> GetRagdollJointNames(const Actor* actor)
  52. {
  53. const PhysicsSetup* physicsSetup = actor->GetPhysicsSetup().get();
  54. const Physics::RagdollConfiguration& ragdollConfig = physicsSetup->GetRagdollConfig();
  55. AZStd::vector<AZStd::string> result;
  56. result.reserve(ragdollConfig.m_nodes.size());
  57. for (const auto& nodeConfig : ragdollConfig.m_nodes)
  58. {
  59. result.emplace_back(nodeConfig.m_debugName);
  60. }
  61. return result;
  62. }
  63. TEST_F(RagdollCommandTests, AddJointLowerInHierarchy)
  64. {
  65. AZStd::string result;
  66. CommandSystem::CommandManager commandManager;
  67. MCore::CommandGroup commandGroup;
  68. const AZStd::vector<AZStd::string> jointsToLeftShoulder {
  69. "jack_root",
  70. "Bip01__pelvis",
  71. "spine1",
  72. "spine2",
  73. "spine3",
  74. "l_shldr",
  75. };
  76. const AZStd::vector<AZStd::string> jointsToLeftHand {
  77. "jack_root",
  78. "Bip01__pelvis",
  79. "spine1",
  80. "spine2",
  81. "spine3",
  82. "l_shldr",
  83. "l_upArm",
  84. "l_loArm",
  85. "l_hand",
  86. };
  87. CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup);
  88. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  89. EXPECT_THAT(
  90. GetRagdollJointNames(GetActor()),
  91. testing::UnorderedPointwise(
  92. StrEq(),
  93. jointsToLeftShoulder
  94. )
  95. );
  96. const AZStd::string serializedBeforeHandAdded = SerializePhysicsSetup(GetActor());
  97. // Adding l_hand should add l_upArm and l_loArm as well
  98. commandGroup.RemoveAllCommands();
  99. CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
  100. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  101. const AZStd::string serializedAfterHandAdded = SerializePhysicsSetup(GetActor());
  102. EXPECT_THAT(
  103. GetRagdollJointNames(GetActor()),
  104. testing::UnorderedPointwise(
  105. StrEq(),
  106. jointsToLeftHand
  107. )
  108. );
  109. EXPECT_TRUE(commandManager.Undo(result)) << result.c_str();
  110. EXPECT_THAT(
  111. GetRagdollJointNames(GetActor()),
  112. testing::UnorderedPointwise(
  113. StrEq(),
  114. jointsToLeftShoulder
  115. )
  116. );
  117. EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedBeforeHandAdded));
  118. EXPECT_TRUE(commandManager.Redo(result)) << result.c_str();
  119. EXPECT_THAT(
  120. GetRagdollJointNames(GetActor()),
  121. testing::UnorderedPointwise(
  122. StrEq(),
  123. jointsToLeftHand
  124. )
  125. );
  126. EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedAfterHandAdded));
  127. }
  128. TEST_F(RagdollCommandTests, AddJointHigherInHierarchy)
  129. {
  130. AZStd::string result;
  131. CommandSystem::CommandManager commandManager;
  132. MCore::CommandGroup commandGroup;
  133. const AZStd::vector<AZStd::string> jointsToLeftHand {
  134. "jack_root",
  135. "Bip01__pelvis",
  136. "spine1",
  137. "spine2",
  138. "spine3",
  139. "l_shldr",
  140. "l_upArm",
  141. "l_loArm",
  142. "l_hand",
  143. };
  144. CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
  145. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  146. EXPECT_THAT(
  147. GetRagdollJointNames(GetActor()),
  148. testing::UnorderedPointwise(
  149. StrEq(),
  150. jointsToLeftHand
  151. )
  152. );
  153. // l_shldr should already be in the ragdoll, so adding it should do nothing
  154. commandGroup.RemoveAllCommands();
  155. CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup);
  156. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  157. EXPECT_THAT(
  158. GetRagdollJointNames(GetActor()),
  159. testing::UnorderedPointwise(
  160. StrEq(),
  161. jointsToLeftHand
  162. )
  163. );
  164. // Undo here undoes the addition of l_hand
  165. EXPECT_TRUE(commandManager.Undo(result)) << result.c_str();
  166. EXPECT_TRUE(GetRagdollJointNames(GetActor()).empty());
  167. EXPECT_TRUE(commandManager.Redo(result)) << result.c_str();
  168. EXPECT_THAT(
  169. GetRagdollJointNames(GetActor()),
  170. testing::UnorderedPointwise(
  171. StrEq(),
  172. jointsToLeftHand
  173. )
  174. );
  175. }
  176. TEST_F(RagdollCommandTests, AddJointAddsAllTheWayToTheRoot)
  177. {
  178. AZStd::string result;
  179. CommandSystem::CommandManager commandManager;
  180. MCore::CommandGroup commandGroup;
  181. const AZStd::vector<AZStd::string> jointsToLeftHand {
  182. "jack_root",
  183. "Bip01__pelvis",
  184. "spine1",
  185. "spine2",
  186. "spine3",
  187. "l_shldr",
  188. "l_upArm",
  189. "l_loArm",
  190. "l_hand",
  191. };
  192. // Add a joint to the ragdoll that does not make a chain all the way to the root
  193. EXPECT_TRUE(commandManager.ExecuteCommand(aznew CommandAddRagdollJoint(GetActor()->GetID(), "l_shldr"), result)) << result.c_str();
  194. EXPECT_THAT(
  195. GetRagdollJointNames(GetActor()),
  196. testing::UnorderedPointwise(StrEq(), AZStd::vector<AZStd::string>{"l_shldr"})
  197. );
  198. CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
  199. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  200. EXPECT_THAT(
  201. GetRagdollJointNames(GetActor()),
  202. testing::UnorderedPointwise(StrEq(), jointsToLeftHand)
  203. );
  204. }
  205. TEST_F(RagdollCommandTests, RemoveJointRemovesChildren)
  206. {
  207. AZStd::string result;
  208. CommandSystem::CommandManager commandManager;
  209. MCore::CommandGroup commandGroup;
  210. const AZStd::vector<AZStd::string> jointsToSpine3 {
  211. "jack_root",
  212. "Bip01__pelvis",
  213. "spine1",
  214. "spine2",
  215. "spine3",
  216. };
  217. // Add joints from the root to the left hand
  218. CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
  219. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  220. // Removing the left shoulder should remove the elbow, wrist, and hand
  221. // as well
  222. commandGroup.RemoveAllCommands();
  223. CommandRagdollHelpers::RemoveJointsFromRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup);
  224. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  225. EXPECT_THAT(
  226. GetRagdollJointNames(GetActor()),
  227. testing::UnorderedPointwise(StrEq(), jointsToSpine3)
  228. );
  229. }
  230. TEST_F(RagdollCommandTests, RemoveJointThenUndoRestoresColliders)
  231. {
  232. AZStd::string result;
  233. CommandSystem::CommandManager commandManager;
  234. MCore::CommandGroup commandGroup;
  235. CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
  236. EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
  237. const AZStd::string serializedBeforeSphereAdded = SerializePhysicsSetup(GetActor());
  238. CommandColliderHelpers::AddCollider(GetActor()->GetID(), "l_hand",
  239. PhysicsSetup::Ragdoll, azrtti_typeid<Physics::SphereShapeConfiguration>());
  240. const AZStd::string serializedAfterSphereAdded = SerializePhysicsSetup(GetActor());
  241. EXPECT_THAT(serializedAfterSphereAdded, ::testing::Not(StrEq(serializedBeforeSphereAdded)));
  242. EXPECT_TRUE(commandManager.Undo(result)) << result.c_str();
  243. EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedBeforeSphereAdded));
  244. EXPECT_TRUE(commandManager.Redo(result)) << result.c_str();
  245. EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedAfterSphereAdded));
  246. }
  247. } // namespace EMotionFX