BlendTreeSimulatedObjectNodeTests.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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 <EMotionFX/Source/ActorInstance.h>
  9. #include <EMotionFX/Source/Actor.h>
  10. #include <EMotionFX/Source/TransformData.h>
  11. #include <EMotionFX/Source/Pose.h>
  12. #include <EMotionFX/Source/AnimGraph.h>
  13. #include <EMotionFX/Source/AnimGraphInstance.h>
  14. #include <EMotionFX/Source/AnimGraphStateMachine.h>
  15. #include <EMotionFX/Source/AnimGraphBindPoseNode.h>
  16. #include <EMotionFX/Source/BlendTree.h>
  17. #include <EMotionFX/Source/BlendTreeFinalNode.h>
  18. #include <EMotionFX/Source/BlendTreeParameterNode.h>
  19. #include <EMotionFX/Source/BlendTreeSimulatedObjectNode.h>
  20. #include <EMotionFX/Source/EMotionFXManager.h>
  21. #include <EMotionFX/Source/SimulatedObjectSetup.h>
  22. #include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
  23. #include <EMotionFX/Source/Parameter/ParameterFactory.h>
  24. #include <Tests/JackGraphFixture.h>
  25. #include <Tests/Matchers.h>
  26. #include <string>
  27. #include <vector>
  28. namespace EMotionFX
  29. {
  30. class BlendTreeSimulatedObjectNodeFixture
  31. : public JackGraphFixture
  32. {
  33. public:
  34. enum
  35. {
  36. UpperLeg = 0,
  37. LowerLeg = 1,
  38. Foot = 2
  39. };
  40. void CreateSimulatedObject(SimulatedObjectSetup* simSetup, const AZStd::string& simObjectName, const AZStd::vector<AZStd::string>& jointNames)
  41. {
  42. SimulatedObject* simObject = simSetup->AddSimulatedObject(simObjectName);
  43. const Skeleton* skeleton = m_actor->GetSkeleton();
  44. ASSERT_EQ(jointNames.size(), 3);
  45. for (size_t i= 0; i < 3; ++i)
  46. {
  47. size_t jointIndex = InvalidIndex;
  48. const Node* node = skeleton->FindNodeAndIndexByName(jointNames[i].c_str(), jointIndex);
  49. ASSERT_NE(node, nullptr);
  50. m_jointIndices[i] = jointIndex;
  51. SimulatedJoint* simJoint = simObject->AddSimulatedJoint(jointIndex);
  52. simJoint->SetStiffness(0.0f);
  53. if (i == UpperLeg)
  54. {
  55. simJoint->SetPinned(true);
  56. }
  57. }
  58. }
  59. void ConstructGraph() override
  60. {
  61. JackGraphFixture::ConstructGraph();
  62. SimulatedObjectSetup* simSetup = m_actor->GetSimulatedObjectSetup().get();
  63. ASSERT_NE(simSetup, nullptr);
  64. CreateSimulatedObject(simSetup, "leftLeg", {"l_upLeg", "l_loLeg", "l_ankle"});
  65. CreateSimulatedObject(simSetup, "rightLeg", {"r_upLeg", "r_loLeg", "r_ankle"});
  66. //---------------------------------------------
  67. // Create a weight parameter.
  68. m_weightParameter = static_cast<FloatSliderParameter*>(ParameterFactory::Create(azrtti_typeid<FloatSliderParameter>()));
  69. m_weightParameter->SetName("Weight");
  70. m_weightParameter->SetDefaultValue(1.0f);
  71. m_animGraph->AddParameter(m_weightParameter);
  72. // Create the blend tree.
  73. BlendTree* blendTree = aznew BlendTree();
  74. m_animGraph->GetRootStateMachine()->AddChildNode(blendTree);
  75. m_animGraph->GetRootStateMachine()->SetEntryState(blendTree);
  76. // Add a final node.
  77. BlendTreeFinalNode* finalNode = aznew BlendTreeFinalNode();
  78. blendTree->AddChildNode(finalNode);
  79. // Add the simulated object node.
  80. m_simNode = aznew BlendTreeSimulatedObjectNode();
  81. m_simNode->SetName("SimObjectNode");
  82. m_simNode->SetSimulatedObjectNames({});
  83. blendTree->AddChildNode(m_simNode);
  84. finalNode->AddConnection(m_simNode, BlendTreeSimulatedObjectNode::OUTPUTPORT_POSE, BlendTreeFinalNode::INPUTPORT_POSE);
  85. // Create the parameter node.
  86. m_parameterNode = aznew BlendTreeParameterNode();
  87. blendTree->AddChildNode(m_parameterNode);
  88. AnimGraphBindPoseNode* bindPoseNode = aznew AnimGraphBindPoseNode();
  89. blendTree->AddChildNode(bindPoseNode);
  90. m_simNode->AddConnection(bindPoseNode, AnimGraphBindPoseNode::OUTPUTPORT_RESULT, BlendTreeSimulatedObjectNode::INPUTPORT_POSE);
  91. // Connect the weight parameter to the weight of the simulated object node.
  92. m_simNode->AddUnitializedConnection(m_parameterNode, /* Weight parameter port */ 0, BlendTreeSimulatedObjectNode::INPUTPORT_ACTIVE);
  93. }
  94. void SetActiveObjects(const AZStd::vector<AZStd::string>& activeObjects)
  95. {
  96. m_simNode->SetSimulatedObjectNames(activeObjects);
  97. m_simNode->InvalidateUniqueData(m_animGraphInstance);
  98. }
  99. protected:
  100. FloatSliderParameter* m_weightParameter = nullptr;
  101. BlendTreeSimulatedObjectNode* m_simNode = nullptr;
  102. BlendTreeParameterNode* m_parameterNode = nullptr;
  103. size_t m_jointIndices[3] { InvalidIndex, InvalidIndex, InvalidIndex };
  104. };
  105. TEST_F(BlendTreeSimulatedObjectNodeFixture, TransformsCheck)
  106. {
  107. SetActiveObjects({"leftLeg"});
  108. // Get bind pose positions in world space.
  109. m_actorInstance->GetTransformData()->MakeBindPoseTransformsUnique(); // We do this as otherwise we can't get world transforms.
  110. const Pose& bindPose = *m_actorInstance->GetTransformData()->GetBindPose();
  111. // Process 1000 frames at 60 fps.
  112. const Pose& currentPose = *m_actorInstance->GetTransformData()->GetCurrentPose();
  113. for (size_t frame = 0; frame < 1000; ++frame)
  114. {
  115. GetEMotionFX().Update(1.0f / 60.0f);
  116. for (size_t joint = 0; joint < 3; ++joint)
  117. {
  118. const AZ::Vector3& jointPos = currentPose.GetWorldSpaceTransform(m_jointIndices[joint]).m_position;
  119. const AZ::Vector3& jointBindPos = bindPose.GetWorldSpaceTransform(m_jointIndices[joint]).m_position;
  120. ASSERT_TRUE((jointPos - jointBindPos).GetLength() <= 0.01f); // Make sure we didn't move too far from the bind pose.
  121. ASSERT_TRUE(AZ::IsClose(currentPose.GetWorldSpaceTransform(m_jointIndices[joint]).m_rotation.GetLength(), 1.0f, 0.001f)); // Make sure we have a unit quaternion.
  122. }
  123. }
  124. }
  125. TEST_F(BlendTreeSimulatedObjectNodeFixture, ActiveObjectsZero)
  126. {
  127. SetActiveObjects({});
  128. const BlendTreeSimulatedObjectNode::UniqueData* uniqueData = static_cast<const BlendTreeSimulatedObjectNode::UniqueData*>(m_simNode->FindOrCreateUniqueNodeData(m_animGraphInstance));
  129. ASSERT_NE(uniqueData, nullptr);
  130. ASSERT_EQ(uniqueData->m_simulations.size(), 2);
  131. ASSERT_NE(uniqueData->m_simulations[0], nullptr);
  132. EXPECT_EQ(uniqueData->m_simulations[0]->m_solver.GetNumSprings(), 2);
  133. EXPECT_EQ(uniqueData->m_simulations[0]->m_solver.GetNumParticles(), 3);
  134. ASSERT_NE(uniqueData->m_simulations[1], nullptr);
  135. EXPECT_EQ(uniqueData->m_simulations[1]->m_solver.GetNumSprings(), 2);
  136. EXPECT_EQ(uniqueData->m_simulations[1]->m_solver.GetNumParticles(), 3);
  137. }
  138. TEST_F(BlendTreeSimulatedObjectNodeFixture, ActiveObjectsOne)
  139. {
  140. SetActiveObjects({"leftLeg"});
  141. const BlendTreeSimulatedObjectNode::UniqueData* uniqueData = static_cast<const BlendTreeSimulatedObjectNode::UniqueData*>(m_simNode->FindOrCreateUniqueNodeData(m_animGraphInstance));
  142. ASSERT_NE(uniqueData, nullptr);
  143. ASSERT_EQ(uniqueData->m_simulations.size(), 1);
  144. ASSERT_NE(uniqueData->m_simulations[0], nullptr);
  145. EXPECT_EQ(uniqueData->m_simulations[0]->m_solver.GetNumSprings(), 2);
  146. EXPECT_EQ(uniqueData->m_simulations[0]->m_solver.GetNumParticles(), 3);
  147. }
  148. TEST_F(BlendTreeSimulatedObjectNodeFixture, ActiveObjectsTwo)
  149. {
  150. SetActiveObjects({"leftLeg", "rightLeg"});
  151. const BlendTreeSimulatedObjectNode::UniqueData* uniqueData = static_cast<const BlendTreeSimulatedObjectNode::UniqueData*>(m_simNode->FindOrCreateUniqueNodeData(m_animGraphInstance));
  152. ASSERT_NE(uniqueData, nullptr);
  153. ASSERT_EQ(uniqueData->m_simulations.size(), 2);
  154. ASSERT_NE(uniqueData->m_simulations[0], nullptr);
  155. EXPECT_EQ(uniqueData->m_simulations[0]->m_solver.GetNumSprings(), 2);
  156. EXPECT_EQ(uniqueData->m_simulations[0]->m_solver.GetNumParticles(), 3);
  157. ASSERT_NE(uniqueData->m_simulations[1], nullptr);
  158. EXPECT_EQ(uniqueData->m_simulations[1]->m_solver.GetNumSprings(), 2);
  159. EXPECT_EQ(uniqueData->m_simulations[1]->m_solver.GetNumParticles(), 3);
  160. }
  161. } // end namespace EMotionFX