3
0

SyncingSystemTests.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 <AzCore/std/containers/array.h>
  9. #include <EMotionFX/Source/AnimGraph.h>
  10. #include <EMotionFX/Source/AnimGraphMotionNode.h>
  11. #include <EMotionFX/Source/BlendTree.h>
  12. #include <EMotionFX/Source/BlendTreeBlend2Node.h>
  13. #include <EMotionFX/Source/Motion.h>
  14. #include <EMotionFX/Source/MotionEventTable.h>
  15. #include <EMotionFX/Source/MotionSet.h>
  16. #include <EMotionFX/Source/MotionData/UniformMotionData.h>
  17. #include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
  18. #include <EMotionFX/Source/Parameter/ParameterFactory.h>
  19. #include <Tests/AnimGraphFixture.h>
  20. #include <Tests/TestAssetCode/MotionEvent.h>
  21. namespace EMotionFX
  22. {
  23. struct SyncParam
  24. {
  25. void (*m_eventFactoryA)(MotionEventTrack* track) = MakeNoEvents;
  26. void (*m_eventFactoryB)(MotionEventTrack* track) = MakeNoEvents;
  27. // 2.0 seconds of simulation, 0.1 increments, 21 playtimes
  28. AZStd::array<float, 21> m_expectedPlayTimeA {};
  29. AZStd::array<float, 21> m_expectedPlayTimeB {};
  30. // Expected play times will be calculated based on motion event and duration from AnimGraphNode::SyncUsingSyncTracks().
  31. AnimGraphObject::ESyncMode m_syncMode = AnimGraphObject::ESyncMode::SYNCMODE_DISABLED;
  32. float m_weightParam = 0.0f;
  33. bool m_reverseMotion = false;
  34. };
  35. class SyncingSystemFixture
  36. : public AnimGraphFixture
  37. , public ::testing::WithParamInterface<SyncParam>
  38. {
  39. public:
  40. void ConstructGraph() override
  41. {
  42. const SyncParam param = GetParam();
  43. m_syncMode = param.m_syncMode;
  44. AnimGraphFixture::ConstructGraph();
  45. m_blendTreeAnimGraph = AnimGraphFactory::Create<OneBlendTreeNodeAnimGraph>();
  46. m_rootStateMachine = m_blendTreeAnimGraph->GetRootStateMachine();
  47. m_blendTree = m_blendTreeAnimGraph->GetBlendTreeNode();
  48. /*
  49. Inside blend tree:
  50. +-------------+
  51. |m_motionNodeA|--------+
  52. +-------------+ |
  53. |
  54. +-------------+ +------->+------------+ +---------+
  55. |m_motionNodeB|---------------->|m_blend2Node|------>|finalNode|
  56. +-------------+ +------->+------------+ +---------+
  57. |
  58. +-----------------+ |
  59. |m_weightParamNode|----+
  60. +-----------------+
  61. */
  62. // Create parameter for animgraph
  63. Parameter* parameter = ParameterFactory::Create(azrtti_typeid<FloatSliderParameter>());
  64. parameter->SetName("blendWeight");
  65. m_blendTreeAnimGraph->AddParameter(parameter);
  66. // Create nodes in animgraph
  67. m_motionNodeA = aznew AnimGraphMotionNode();
  68. m_motionNodeB = aznew AnimGraphMotionNode();
  69. m_blend2Node = aznew BlendTreeBlend2Node();
  70. BlendTreeParameterNode* parameterNode = aznew BlendTreeParameterNode();
  71. BlendTreeFinalNode* finalNode = aznew BlendTreeFinalNode();
  72. m_blendTree->AddChildNode(m_motionNodeA);
  73. m_blendTree->AddChildNode(m_motionNodeB);
  74. m_blendTree->AddChildNode(parameterNode);
  75. m_blendTree->AddChildNode(m_blend2Node);
  76. m_blendTree->AddChildNode(finalNode);
  77. // Connect the nodes in animgraph
  78. m_blend2Node->AddConnection(m_motionNodeA, AnimGraphMotionNode::PORTID_OUTPUT_POSE, BlendTreeBlend2Node::PORTID_INPUT_POSE_A);
  79. m_blend2Node->AddConnection(m_motionNodeB, AnimGraphMotionNode::PORTID_OUTPUT_POSE, BlendTreeBlend2Node::PORTID_INPUT_POSE_B);
  80. m_blend2Node->AddUnitializedConnection(parameterNode, 0, BlendTreeBlend2Node::INPUTPORT_WEIGHT);
  81. finalNode->AddConnection(m_blend2Node, BlendTreeBlend2Node::PORTID_OUTPUT_POSE, BlendTreeFinalNode::PORTID_INPUT_POSE);
  82. m_blend2Node->SetSyncMode(m_syncMode);
  83. m_blendTreeAnimGraph->InitAfterLoading();
  84. }
  85. void SetUp() override
  86. {
  87. AnimGraphFixture::SetUp();
  88. m_animGraphInstance->Destroy();
  89. m_animGraphInstance = m_blendTreeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet);
  90. // Add motion to motion nodes
  91. Motion* motionA = aznew Motion("testSkeletalMotionA");
  92. Motion* motionB = aznew Motion("testSkeletalMotionB");
  93. UniformMotionData* dataA = aznew UniformMotionData();
  94. UniformMotionData* dataB = aznew UniformMotionData();
  95. UniformMotionData::InitSettings settings;
  96. settings.m_numSamples = 2;
  97. settings.m_sampleRate = 1.0f;
  98. dataA->Init(settings);
  99. settings.m_sampleRate = 0.5f;
  100. dataB->Init(settings);
  101. motionA->SetMotionData(dataA);
  102. motionB->SetMotionData(dataB);
  103. EXPECT_FLOAT_EQ(motionA->GetDuration(), 1.0f);
  104. EXPECT_FLOAT_EQ(motionB->GetDuration(), 2.0f);
  105. motionA->GetEventTable()->AutoCreateSyncTrack(motionA);
  106. motionB->GetEventTable()->AutoCreateSyncTrack(motionB);
  107. m_syncTrackA = motionA->GetEventTable()->GetSyncTrack();
  108. m_syncTrackB = motionB->GetEventTable()->GetSyncTrack();
  109. MotionSet::MotionEntry* motionEntryA = aznew MotionSet::MotionEntry(motionA->GetName(), motionA->GetName(), motionA);
  110. MotionSet::MotionEntry* motionEntryB = aznew MotionSet::MotionEntry(motionB->GetName(), motionB->GetName(), motionB);
  111. m_motionSet->AddMotionEntry(motionEntryA);
  112. m_motionSet->AddMotionEntry(motionEntryB);
  113. m_motionNodeA->AddMotionId("testSkeletalMotionA");
  114. m_motionNodeB->AddMotionId("testSkeletalMotionB");
  115. }
  116. public:
  117. AnimGraphObject::ESyncMode m_syncMode;
  118. AnimGraphMotionNode* m_motionNodeA = nullptr;
  119. AnimGraphMotionNode* m_motionNodeB = nullptr;
  120. BlendTreeBlend2Node* m_blend2Node = nullptr;
  121. BlendTree* m_blendTree = nullptr;
  122. AnimGraphSyncTrack* m_syncTrackA = nullptr;
  123. AnimGraphSyncTrack* m_syncTrackB = nullptr;
  124. };
  125. // Play speed test for different sync modes with different weight on blend2Node
  126. TEST_P(SyncingSystemFixture, SyncingSystemPlaySpeedTests)
  127. {
  128. const SyncParam param = GetParam();
  129. param.m_eventFactoryA(m_syncTrackA);
  130. param.m_eventFactoryB(m_syncTrackB);
  131. GetEMotionFX().Update(0.0f);
  132. MCore::AttributeFloat* weightParam = m_animGraphInstance->GetParameterValueChecked<MCore::AttributeFloat>(0);
  133. weightParam->SetValue(param.m_weightParam);
  134. // Test reverse motion
  135. m_motionNodeA->SetReverse(param.m_reverseMotion);
  136. m_motionNodeB->SetReverse(param.m_reverseMotion);
  137. uint32 playTimeIndex = 0;
  138. const float tolerance = 0.00001f;
  139. Simulate(2.0f/*simulationTime*/, 10.0f/*expectedFps*/, 0.0f/*fpsVariance*/,
  140. /*preCallback*/[]([[maybe_unused]] AnimGraphInstance* animGraphInstance) {},
  141. /*postCallback*/[]([[maybe_unused]] AnimGraphInstance* animGraphInstance) {},
  142. /*preUpdateCallback*/[](AnimGraphInstance*, float, float, int) {},
  143. /*postUpdateCallback*/[this, &playTimeIndex, &tolerance, &param](AnimGraphInstance* animGraphInstance, [[maybe_unused]] float time, [[maybe_unused]] float timeDelta, [[maybe_unused]] int frame)
  144. {
  145. const float motionPlaySpeedA = m_motionNodeA->ExtractCustomPlaySpeed(animGraphInstance);
  146. const float durationA = m_motionNodeA->GetDuration(animGraphInstance);
  147. const float statePlaySpeedA = m_motionNodeA->GetPlaySpeed(animGraphInstance);
  148. const float motionPlaySpeedB = m_motionNodeB->ExtractCustomPlaySpeed(animGraphInstance);
  149. const float durationB = m_motionNodeB->GetDuration(animGraphInstance);
  150. const float statePlaySpeedB = m_motionNodeB->GetPlaySpeed(animGraphInstance);
  151. if (m_blend2Node->GetSyncMode() == AnimGraphObject::SYNCMODE_DISABLED)
  152. {
  153. // We don't blend playspeeds when sync is disabled, the motion nodes should keep their playspeeds.
  154. EXPECT_EQ(motionPlaySpeedA, statePlaySpeedA) << "Motion playspeeds should match the set playspeed in the motion node throughout blending.";
  155. EXPECT_EQ(motionPlaySpeedB, statePlaySpeedB) << "Motion playspeeds should match the set playspeed in the motion node throughout blending.";
  156. }
  157. else if(m_blend2Node->GetSyncMode() == AnimGraphObject::SYNCMODE_CLIPBASED)
  158. {
  159. float factorA;
  160. float factorB;
  161. float interpolatedSpeedA;
  162. AZStd::tie(interpolatedSpeedA, factorA, factorB) = AnimGraphNode::SyncPlaySpeeds(
  163. motionPlaySpeedA, durationA, motionPlaySpeedB, durationB, param.m_weightParam);
  164. EXPECT_FLOAT_EQ(statePlaySpeedA, interpolatedSpeedA * factorA) << "Motion playspeeds should match the set playspeed in the motion node throughout blending.";
  165. }
  166. else if(m_blend2Node->GetSyncMode() == AnimGraphObject::SYNCMODE_TRACKBASED)
  167. {
  168. const float motionPlayTimeA = m_motionNodeA->GetCurrentPlayTime(animGraphInstance);
  169. const float motionPlayTimeB = m_motionNodeB->GetCurrentPlayTime(animGraphInstance);
  170. EXPECT_NEAR(motionPlayTimeA, param.m_expectedPlayTimeA[playTimeIndex], tolerance) << "Motion node A playtime should match the expected playtime.";
  171. EXPECT_NEAR(motionPlayTimeB, param.m_expectedPlayTimeB[playTimeIndex], tolerance) << "Motion node B playtime should match the expected playtime.";
  172. playTimeIndex++;
  173. }
  174. }
  175. );
  176. }
  177. std::vector<SyncParam> SyncTestData
  178. {
  179. {
  180. /*.eventFactoryA =*/ MakeNoEvents,
  181. /*.eventFactoryB =*/ MakeNoEvents,
  182. /*.expectedPlayTimeA =*/ {},
  183. /*.expectedPlayTimeB =*/ {},
  184. /*.syncMode =*/ AnimGraphObject::ESyncMode::SYNCMODE_DISABLED,
  185. /*.weightParam =*/ 0.0f,
  186. /*.reverseMotion =*/ true
  187. },
  188. {
  189. /*.eventFactoryA =*/ MakeNoEvents,
  190. /*.eventFactoryB =*/ MakeNoEvents,
  191. /*.expectedPlayTimeA =*/ {},
  192. /*.expectedPlayTimeB =*/ {},
  193. /*.syncMode =*/ AnimGraphObject::ESyncMode::SYNCMODE_CLIPBASED,
  194. /*.weightParam =*/ 0.25f,
  195. /*.reverseMotion =*/ false
  196. },
  197. {
  198. /*.eventFactoryA =*/ MakeNoEvents,
  199. /*.eventFactoryB =*/ MakeOneEvent,
  200. /*.expectedPlayTimeA =*/ {},
  201. /*.expectedPlayTimeB =*/ {},
  202. /*.syncMode =*/ AnimGraphObject::ESyncMode::SYNCMODE_CLIPBASED,
  203. /*.weightParam =*/ 0.5f,
  204. /*.reverseMotion =*/ true
  205. },
  206. {
  207. /*.eventFactoryA =*/ MakeOneEvent,
  208. /*.eventFactoryB =*/ MakeOneEvent,
  209. /*.expectedPlayTimeA =*/ { 1.0f, 0.0625f, 0.125f, 0.1875f, 0.25f, 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, 0.9375f, 1.0f, 0.0625f, 0.125f, 0.1875f, 0.25f },
  210. /*.expectedPlayTimeB =*/ { 1.75f, 1.875f, 2.0f, 0.125f, 0.25f, 0.375f, 0.5f, 0.625f, 0.75f, 0.875f, 1.0f, 1.125f, 1.25f, 1.375f, 1.5f, 1.625f, 1.75f, 1.875f, 2.0f, 0.125f, 0.25f },
  211. /*.syncMode =*/ AnimGraphObject::ESyncMode::SYNCMODE_TRACKBASED,
  212. /*.weightParam =*/ 0.75f,
  213. /*.reverseMotion =*/ false
  214. },
  215. {
  216. /*.eventFactoryA =*/ MakeOneEvent,
  217. /*.eventFactoryB =*/ MakeTwoEvents,
  218. /*.expectedPlayTimeA =*/ { 1.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f },
  219. /*.expectedPlayTimeB =*/ { 0.625f, 0.725f, 0.325f, 0.425f, 0.525f, 0.625f, 0.725f, 0.325f, 0.425f, 0.525f, 0.625f, 0.725f, 0.325f, 0.425f, 0.525f, 0.625f, 0.725f, 0.325f, 0.425f, 0.525f, 0.625f },
  220. /*.syncMode =*/ AnimGraphObject::ESyncMode::SYNCMODE_TRACKBASED,
  221. /*.weightParam =*/ 1.0f,
  222. /*.reverseMotion =*/ true
  223. },
  224. {
  225. /*.eventFactoryA =*/ MakeOneEvent,
  226. /*.eventFactoryB =*/ MakeThreeEvents,
  227. /*.expectedPlayTimeA =*/ { 1.0f, 0.15f, 0.3f, 0.45f, 0.6f, 0.75f, 0.9f, 0.05f, 0.2f, 0.35f, 0.5f, 0.65f, 0.8f, 0.95f, 0.1f, 0.25f, 0.4f, 0.55f, 0.7f, 0.85f, 1.0f },
  228. /*.expectedPlayTimeB =*/ { 0.625f, 0.7f, 0.275f, 0.35f, 0.425f, 0.5f, 0.575f, 0.65f, 0.725f, 0.3f, 0.375f, 0.45f, 0.525f, 0.6f, 0.675f, 0.75f, 0.325f, 0.4f, 0.475f, 0.55f, 0.625f },
  229. /*.syncMode =*/ AnimGraphObject::ESyncMode::SYNCMODE_TRACKBASED,
  230. /*.weightParam =*/ 0.5f,
  231. /*.reverseMotion =*/ false
  232. },
  233. {
  234. /*.eventFactoryA =*/ MakeTwoEvents,
  235. /*.eventFactoryB =*/ MakeThreeEvents,
  236. /*.expectedPlayTimeA =*/ { 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.4875f, 0.575f, 0.6625f, 0.75f, 0.8375f, 0.9375f, 0.0375f, 0.1375f, 0.2375f, 0.3375f, 0.4375f },
  237. /*.expectedPlayTimeB =*/ { 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.35f, 1.55f, 1.725f, 1.9f, 0.075f, 0.25f, 0.3375f, 0.4375f, 0.5375f, 0.6375f, 0.7375f, 0.8375f, 0.9375f },
  238. /*.syncMode =*/ AnimGraphObject::ESyncMode::SYNCMODE_TRACKBASED,
  239. /*.weightParam =*/ 0.25f,
  240. /*.reverseMotion =*/ true
  241. }
  242. };
  243. INSTANTIATE_TEST_CASE_P(SyncingSystem, SyncingSystemFixture,
  244. ::testing::ValuesIn(SyncTestData));
  245. } // end namespace EMotionFX