AnimGraphCopyPasteTests.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  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/CommandSystem/Source/AnimGraphConnectionCommands.h>
  9. #include <EMotionFX/CommandSystem/Source/AnimGraphConditionCommands.h>
  10. #include <EMotionFX/CommandSystem/Source/AnimGraphNodeCommands.h>
  11. #include <EMotionFX/CommandSystem/Source/AnimGraphTriggerActionCommands.h>
  12. #include <EMotionFX/CommandSystem/Source/CommandManager.h>
  13. #include <EMotionFX/Source/AnimGraphBindPoseNode.h>
  14. #include <EMotionFX/Source/AnimGraphMotionNode.h>
  15. #include <EMotionFX/Source/AnimGraphObjectFactory.h>
  16. #include <EMotionFX/Source/AnimGraphParameterAction.h>
  17. #include <EMotionFX/Source/AnimGraphFollowerParameterAction.h>
  18. #include <EMotionFX/Source/AnimGraphStateMachine.h>
  19. #include <EMotionFX/Source/AnimGraphStateTransition.h>
  20. #include <EMotionFX/Source/AnimGraphSymbolicFollowerParameterAction.h>
  21. #include <EMotionFX/Source/BlendTreeBlendNNode.h>
  22. #include <MCore/Source/CommandGroup.h>
  23. #include <MCore/Source/ReflectionSerializer.h>
  24. #include <Tests/AnimGraphFixture.h>
  25. namespace EMotionFX
  26. {
  27. class AnimGraphSimpleCopyPasteFixture
  28. : public AnimGraphFixture
  29. , public ::testing::WithParamInterface<bool>
  30. {
  31. public:
  32. void ConstructGraph() override
  33. {
  34. AnimGraphFixture::ConstructGraph();
  35. /*
  36. +---+ +---+
  37. | A |--Actions-->| B |
  38. +---+ +---+
  39. +---+
  40. | C |
  41. +---+
  42. */
  43. m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
  44. m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
  45. m_stateA = m_motionNodeAnimGraph->GetMotionNodeA();
  46. m_stateB = m_motionNodeAnimGraph->GetMotionNodeB();
  47. m_stateC = aznew AnimGraphStateMachine();
  48. m_stateC->SetName("C");
  49. m_rootStateMachine->AddChildNode(m_stateC);
  50. m_transition = AddTransition(m_stateA, m_stateB, 1.0f);
  51. m_motionNodeAnimGraph->InitAfterLoading();
  52. }
  53. bool CompareParameterAction(AnimGraphTriggerAction* actionA, AnimGraphTriggerAction* actionB)
  54. {
  55. AnimGraphParameterAction* paramterActionA = static_cast<AnimGraphParameterAction*>(actionA);
  56. AnimGraphParameterAction* paramterActionB = static_cast<AnimGraphParameterAction*>(actionB);
  57. return (paramterActionA->GetTriggerValue() == paramterActionB->GetTriggerValue())
  58. && (paramterActionA->GetParameterName() == paramterActionB->GetParameterName());
  59. }
  60. void TearDown() override
  61. {
  62. if (m_animGraphInstance)
  63. {
  64. m_animGraphInstance->Destroy();
  65. m_animGraphInstance = nullptr;
  66. }
  67. m_motionNodeAnimGraph.reset();
  68. AnimGraphFixture::TearDown();
  69. }
  70. AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
  71. AnimGraphNode* m_stateA = nullptr;
  72. AnimGraphNode* m_stateB = nullptr;
  73. AnimGraphStateMachine* m_stateC = nullptr;
  74. AnimGraphStateTransition* m_transition = nullptr;
  75. };
  76. TEST_P(AnimGraphSimpleCopyPasteFixture, AnimGraphCopyPasteTests_CopyAndPasteTransitionActions)
  77. {
  78. CommandSystem::CommandManager commandManager;
  79. AZStd::string result;
  80. MCore::CommandGroup commandGroup;
  81. const bool cutMode = GetParam();
  82. // 1. Add transition actions.
  83. CommandSystem::AddTransitionAction(m_transition, azrtti_typeid<AnimGraphParameterAction>());
  84. CommandSystem::AddTransitionAction(m_transition, azrtti_typeid<AnimGraphFollowerParameterAction>());
  85. CommandSystem::AddTransitionAction(m_transition, azrtti_typeid<AnimGraphSymbolicFollowerParameterAction>());
  86. EXPECT_EQ(3, m_transition->GetTriggerActionSetup().GetNumActions()) << "There should be exactly three transition actions.";
  87. // 2. Cut & paste both states
  88. AZStd::vector<EMotionFX::AnimGraphNode*> nodesToCopy;
  89. nodesToCopy.emplace_back(m_stateA);
  90. nodesToCopy.emplace_back(m_stateB);
  91. CommandSystem::AnimGraphCopyPasteData copyPasteData;
  92. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  93. /*targetParentNode=*/m_rootStateMachine,
  94. nodesToCopy,
  95. /*posX=*/0,
  96. /*posY=*/0,
  97. /*cutMode=*/cutMode,
  98. copyPasteData,
  99. /*ignoreTopLevelConnections=*/false);
  100. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  101. if (cutMode)
  102. {
  103. EXPECT_EQ(1, m_rootStateMachine->GetNumTransitions())
  104. << "As we only had one transition before the cut & paste operation, there should be exactly one now, too.";
  105. AnimGraphStateTransition* newTransition = m_rootStateMachine->GetTransition(0);
  106. const TriggerActionSetup& actionSetup = newTransition->GetTriggerActionSetup();
  107. EXPECT_EQ(3, actionSetup.GetNumActions()) << "There should be three transition actions again.";
  108. }
  109. else
  110. {
  111. const size_t numTransitions = m_rootStateMachine->GetNumTransitions();
  112. EXPECT_EQ(2, numTransitions)
  113. << "After copy & paste, there should be two transitions.";
  114. for (size_t i = 0; i < numTransitions; ++i)
  115. {
  116. AnimGraphStateTransition* transition = m_rootStateMachine->GetTransition(i);
  117. const TriggerActionSetup& actionSetup = transition->GetTriggerActionSetup();
  118. EXPECT_EQ(3, actionSetup.GetNumActions()) << "There should be three transition actions for both transitions.";
  119. }
  120. }
  121. }
  122. ///////////////////////////////////////////////////////////////////////////
  123. class AnimGraphTransitionConditionCopyPasteFixture
  124. : public AnimGraphFixture
  125. , public ::testing::WithParamInterface<bool>
  126. {
  127. public:
  128. void ConstructGraph() override
  129. {
  130. AnimGraphFixture::ConstructGraph();
  131. m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
  132. m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
  133. m_stateA = m_motionNodeAnimGraph->GetMotionNodeA();
  134. m_stateB = m_motionNodeAnimGraph->GetMotionNodeB();
  135. m_transition = AddTransition(m_stateA, m_stateB, 1.0f);
  136. m_motionNodeAnimGraph->InitAfterLoading();
  137. }
  138. AZStd::vector<AZ::TypeId> GetConditionTypeIds() const
  139. {
  140. AZStd::vector<AZ::TypeId> result;
  141. AnimGraphObjectFactory objectFactory;
  142. const AZStd::vector<EMotionFX::AnimGraphObject*>& objectPrototypes = objectFactory.GetUiObjectPrototypes();
  143. for (EMotionFX::AnimGraphObject* objectPrototype : objectPrototypes)
  144. {
  145. EMotionFX::AnimGraphTransitionCondition* conditionPrototype = azdynamic_cast<EMotionFX::AnimGraphTransitionCondition*>(objectPrototype);
  146. if (conditionPrototype &&
  147. (azrtti_typeid<>(conditionPrototype) != azrtti_typeid<AnimGraphTransitionCondition>()))
  148. {
  149. AZ::TypeId type = azrtti_typeid<>(conditionPrototype);
  150. AZ::Debug::Platform::OutputToDebugger({}, AZStd::string::format("Condition: Name=%s, Type=%s\n", conditionPrototype->GetPaletteName(), type.ToString<AZStd::string>().c_str()).c_str());
  151. result.emplace_back(azrtti_typeid<>(conditionPrototype));
  152. }
  153. }
  154. return result;
  155. }
  156. void VerifyTransition(AnimGraphStateTransition* transition)
  157. {
  158. const AZStd::vector<AZ::TypeId> conditionTypeIds = GetConditionTypeIds();
  159. const size_t numConditionTypes = conditionTypeIds.size();
  160. const size_t numConditions = transition->GetNumConditions();
  161. EXPECT_EQ(numConditions, numConditionTypes) << "We should have a condition for each prototype type.";
  162. for (size_t i = 0; i < numConditions; ++i)
  163. {
  164. const EMotionFX::AnimGraphTransitionCondition* condition = transition->GetCondition(i);
  165. EXPECT_EQ(azrtti_typeid<>(condition), conditionTypeIds[i])
  166. << "The conditions on the transition should have the same order as the prototypes.";
  167. }
  168. }
  169. void VerifyAfterOperation()
  170. {
  171. const AZStd::vector<AZ::TypeId> conditionTypeIds = GetConditionTypeIds();
  172. const bool cutMode = GetParam();
  173. if (cutMode)
  174. {
  175. EXPECT_EQ(m_rootStateMachine->GetNumTransitions(), 1)
  176. << "As we only had one transition before the cut & paste operation, there should be exactly one now, too.";
  177. AnimGraphStateTransition* newTransition = m_rootStateMachine->GetTransition(0);
  178. VerifyTransition(newTransition);
  179. }
  180. else
  181. {
  182. const size_t numTransitions = m_rootStateMachine->GetNumTransitions();
  183. EXPECT_EQ(2, numTransitions)
  184. << "After copy & paste, there should be two transitions.";
  185. for (size_t i = 0; i < numTransitions; ++i)
  186. {
  187. AnimGraphStateTransition* transition = m_rootStateMachine->GetTransition(i);
  188. VerifyTransition(transition);
  189. }
  190. }
  191. }
  192. void TearDown() override
  193. {
  194. if (m_animGraphInstance)
  195. {
  196. m_animGraphInstance->Destroy();
  197. m_animGraphInstance = nullptr;
  198. }
  199. m_motionNodeAnimGraph.reset();
  200. AnimGraphFixture::TearDown();
  201. }
  202. AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
  203. AnimGraphNode* m_stateA = nullptr;
  204. AnimGraphNode* m_stateB = nullptr;
  205. AnimGraphStateTransition* m_transition = nullptr;
  206. };
  207. TEST_P(AnimGraphTransitionConditionCopyPasteFixture, AnimGraphCopyPasteTests_CopyAndPasteTransitionConditions)
  208. {
  209. CommandSystem::CommandManager commandManager;
  210. AZStd::string result;
  211. MCore::CommandGroup commandGroup;
  212. const bool cutMode = GetParam();
  213. // 1. Add transition conditions.
  214. const AZStd::vector<AZ::TypeId> conditionTypeIds = GetConditionTypeIds();
  215. EXPECT_TRUE(conditionTypeIds.size() > 0) << "There are no transition conditions registered in the object factory.";
  216. for (const AZ::TypeId& conditionTypeId : conditionTypeIds)
  217. {
  218. CommandSystem::CommandAddTransitionCondition* addConditionCommand = aznew CommandSystem::CommandAddTransitionCondition(
  219. m_motionNodeAnimGraph->GetID(), m_transition->GetId(), conditionTypeId);
  220. EXPECT_TRUE(commandManager.ExecuteCommand(addConditionCommand, result)) << result.c_str();
  221. }
  222. VerifyTransition(m_transition);
  223. // 2. Copy/cut & paste both states
  224. AZStd::vector<EMotionFX::AnimGraphNode*> nodesToCopy;
  225. nodesToCopy.emplace_back(m_stateA);
  226. nodesToCopy.emplace_back(m_stateB);
  227. CommandSystem::AnimGraphCopyPasteData copyPasteData;
  228. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  229. /*targetParentNode=*/m_rootStateMachine,
  230. nodesToCopy,
  231. /*posX=*/0,
  232. /*posY=*/0,
  233. /*cutMode=*/cutMode,
  234. copyPasteData,
  235. /*ignoreTopLevelConnections=*/false);
  236. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  237. VerifyAfterOperation();
  238. // 3. Undo.
  239. EXPECT_TRUE(commandManager.Undo(result)) << result.c_str();
  240. EXPECT_EQ(m_rootStateMachine->GetNumTransitions(), 1) << "We should be back at only the original transition again.";
  241. VerifyTransition(m_rootStateMachine->GetTransition(0));
  242. // 4. Redo.
  243. EXPECT_TRUE(commandManager.Redo(result)) << result.c_str();
  244. VerifyAfterOperation();
  245. }
  246. INSTANTIATE_TEST_CASE_P(AnimGraphCopyPasteTests,
  247. AnimGraphTransitionConditionCopyPasteFixture,
  248. ::testing::Bool());
  249. ///////////////////////////////////////////////////////////////////////////
  250. TEST_P(AnimGraphSimpleCopyPasteFixture, AnimGraphCopyPasteTests_TransitionIds)
  251. {
  252. CommandSystem::CommandManager commandManager;
  253. AZStd::string result;
  254. MCore::CommandGroup commandGroup;
  255. const bool cutMode = GetParam();
  256. const AnimGraphConnectionId oldtransitionId = m_transition->GetId();
  257. AZStd::vector<EMotionFX::AnimGraphNode*> nodesToCopy;
  258. nodesToCopy.emplace_back(m_stateA);
  259. nodesToCopy.emplace_back(m_stateB);
  260. CommandSystem::AnimGraphCopyPasteData copyPasteData;
  261. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  262. /*targetParentNode=*/m_rootStateMachine,
  263. nodesToCopy,
  264. /*posX=*/0,
  265. /*posY=*/0,
  266. /*cutMode=*/cutMode,
  267. copyPasteData,
  268. /*ignoreTopLevelConnections=*/false);
  269. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  270. if (cutMode)
  271. {
  272. EXPECT_EQ(1, m_rootStateMachine->GetNumTransitions())
  273. << "As we only had one transition before the cut & paste operation, there should be exactly one now, too.";
  274. AnimGraphStateTransition* newTransition = m_rootStateMachine->GetTransition(0);
  275. EXPECT_TRUE(newTransition->GetId() == oldtransitionId) << "There cut & pasted transition should have the same id.";
  276. }
  277. else
  278. {
  279. const size_t numTransitions = m_rootStateMachine->GetNumTransitions();
  280. EXPECT_EQ(2, numTransitions)
  281. << "After copy & paste, there should be two transitions.";
  282. for (size_t i = 0; i < numTransitions; ++i)
  283. {
  284. AnimGraphStateTransition* transition = m_rootStateMachine->GetTransition(i);
  285. if (transition == m_transition)
  286. {
  287. continue;
  288. }
  289. EXPECT_FALSE(transition->GetId() == oldtransitionId) << "There copied transition should have another id. Transition ids need to be unique.";
  290. }
  291. }
  292. }
  293. TEST_P(AnimGraphSimpleCopyPasteFixture, AnimGraphCopyPasteTests_CopyAndPasteToAStateMachine)
  294. {
  295. CommandSystem::CommandManager commandManager;
  296. AZStd::string result;
  297. MCore::CommandGroup commandGroup;
  298. const bool cutMode = GetParam();
  299. // 1. Copy the nodeA and nodeB to nodeC(statemachine)
  300. AZStd::vector<EMotionFX::AnimGraphNode*> nodesToCopy;
  301. nodesToCopy.emplace_back(m_stateA);
  302. nodesToCopy.emplace_back(m_stateB);
  303. CommandSystem::AnimGraphCopyPasteData copyPasteData;
  304. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  305. /*targetParentNode=*/m_stateC,
  306. nodesToCopy,
  307. /*posX=*/0,
  308. /*posY=*/0,
  309. /*cutMode=*/cutMode,
  310. copyPasteData,
  311. /*ignoreTopLevelConnections=*/false);
  312. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  313. if (cutMode)
  314. {
  315. EXPECT_EQ(1, m_rootStateMachine->GetNumChildNodes());
  316. EXPECT_EQ(0, m_rootStateMachine->GetNumTransitions());
  317. EXPECT_EQ(2, m_stateC->GetNumChildNodes());
  318. EXPECT_EQ(1, m_stateC->GetNumTransitions());
  319. EXPECT_EQ("A", m_stateC->GetChildNode(0)->GetNameString());
  320. EXPECT_EQ("B", m_stateC->GetChildNode(1)->GetNameString());
  321. }
  322. else
  323. {
  324. EXPECT_EQ(3, m_rootStateMachine->GetNumChildNodes());
  325. EXPECT_EQ(1, m_rootStateMachine->GetNumTransitions());
  326. EXPECT_EQ(2, m_stateC->GetNumChildNodes());
  327. EXPECT_EQ(1, m_stateC->GetNumTransitions());
  328. }
  329. /* After 1. Cut == true
  330. +--------------------------+
  331. | C |
  332. | +---+ +---+ |
  333. | | A2|--Actions-->| B2| |
  334. | +---+ +---+ |
  335. | |
  336. +--------------------------+
  337. */
  338. /* After 1. Cut == false
  339. +---+ +---+
  340. | A |--Actions-->| B |
  341. +---+ +---+
  342. +--------------------------+
  343. | C |
  344. | +---+ +---+ |
  345. | | A2|--Actions-->| B2| |
  346. | +---+ +---+ |
  347. | |
  348. +--------------------------+
  349. */
  350. // 2. Copy and paste the nodeC(state machine).
  351. commandGroup.Clear();
  352. nodesToCopy.clear();
  353. nodesToCopy.emplace_back(m_stateC);
  354. CommandSystem::AnimGraphCopyPasteData copyPasteData2;
  355. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  356. /*targetParentNode=*/m_rootStateMachine,
  357. nodesToCopy,
  358. /*posX=*/0,
  359. /*posY=*/0,
  360. /*cutMode=*/false,
  361. copyPasteData2,
  362. /*ignoreTopLevelConnections=*/false);
  363. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  364. if (cutMode)
  365. {
  366. EXPECT_EQ(2, m_rootStateMachine->GetNumChildNodes());
  367. EXPECT_EQ(0, m_rootStateMachine->GetNumTransitions());
  368. EXPECT_EQ(6, m_rootStateMachine->RecursiveCalcNumNodes());
  369. }
  370. else
  371. {
  372. EXPECT_EQ(4, m_rootStateMachine->GetNumChildNodes());
  373. EXPECT_EQ(1, m_rootStateMachine->GetNumTransitions());
  374. EXPECT_EQ(8, m_rootStateMachine->RecursiveCalcNumNodes());
  375. }
  376. }
  377. TEST_P(AnimGraphSimpleCopyPasteFixture, AnimGraphCopyPasteTests_TriggerActions)
  378. {
  379. CommandSystem::CommandManager commandManager;
  380. AZStd::string result;
  381. MCore::CommandGroup commandGroup;
  382. const bool cutMode = GetParam();
  383. // Add transition actions to the node.
  384. AnimGraphParameterAction* action1 = aznew AnimGraphParameterAction();
  385. action1->SetParameterName("action1Param");
  386. action1->SetTriggerValue(5.8f);
  387. AnimGraphParameterAction* action2 = aznew AnimGraphParameterAction();
  388. action2->SetParameterName("action2Param");
  389. action2->SetTriggerValue(8.5f);
  390. TriggerActionSetup& actionSetup = m_stateA->GetTriggerActionSetup();
  391. actionSetup.AddAction(action1);
  392. actionSetup.AddAction(action2);
  393. m_stateA->InitTriggerActions();
  394. AZStd::vector<EMotionFX::AnimGraphNode*> nodesToCopy;
  395. nodesToCopy.emplace_back(m_stateA);
  396. CommandSystem::AnimGraphCopyPasteData copyPasteData;
  397. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  398. /*targetParentNode=*/m_rootStateMachine,
  399. nodesToCopy,
  400. /*posX=*/0,
  401. /*posY=*/0,
  402. /*cutMode=*/cutMode,
  403. copyPasteData,
  404. /*ignoreTopLevelConnections=*/false);
  405. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  406. if (cutMode)
  407. {
  408. EXPECT_EQ(3, m_rootStateMachine->GetNumChildNodes()) << "After the cut + copy, the total node number remain the same.";
  409. AnimGraphNode* copiedNode = m_rootStateMachine->GetChildNode(2);
  410. const TriggerActionSetup& actionSetup2 = copiedNode->GetTriggerActionSetup();
  411. EXPECT_EQ(2, actionSetup2.GetNumActions());
  412. AnimGraphParameterAction* action1_1 = static_cast<AnimGraphParameterAction*>(actionSetup2.GetAction(0));
  413. AnimGraphParameterAction* action2_1 = static_cast<AnimGraphParameterAction*>(actionSetup2.GetAction(1));
  414. EXPECT_TRUE(action1_1 && action2_1) << "After cut/copy, the new action setup should has two parameter actions.";
  415. EXPECT_TRUE(action1_1->GetParameterName() == "action1Param");
  416. EXPECT_EQ(action1_1->GetTriggerValue(), 5.8f);
  417. EXPECT_TRUE(action2_1->GetParameterName() == "action2Param");
  418. EXPECT_EQ(action2_1->GetTriggerValue(), 8.5f);
  419. }
  420. else
  421. {
  422. EXPECT_EQ(4, m_rootStateMachine->GetNumChildNodes()) << "After the copy, the total node number should increase by one.";
  423. AnimGraphNode* copiedNode = m_rootStateMachine->GetChildNode(3);
  424. EXPECT_NE(copiedNode, m_stateA) << "Make sure the fourth node is the newly copied node, not the original node.";
  425. const TriggerActionSetup& actionSetup2 = copiedNode->GetTriggerActionSetup();
  426. EXPECT_EQ(2, actionSetup2.GetNumActions());
  427. EXPECT_TRUE(CompareParameterAction(actionSetup2.GetAction(0), action1)) << "After copy, the action should be the same as the original node.";
  428. EXPECT_TRUE(CompareParameterAction(actionSetup2.GetAction(1), action2)) << "After copy, the action should be the same as the original node.";
  429. }
  430. }
  431. INSTANTIATE_TEST_CASE_P(AnimGraphCopyPasteTests,
  432. AnimGraphSimpleCopyPasteFixture,
  433. ::testing::Bool());
  434. ///////////////////////////////////////////////////////////////////////////
  435. class AnimGraphCopyPasteFixture_CanBeInterruptedBy
  436. : public AnimGraphFixture
  437. , public ::testing::WithParamInterface<bool>
  438. {
  439. public:
  440. void ConstructGraph() override
  441. {
  442. AnimGraphFixture::ConstructGraph();
  443. /*
  444. +---+ +---+
  445. | A |---->| B |
  446. +---+ +---+
  447. |
  448. v
  449. +---+
  450. | C |
  451. +---+
  452. */
  453. m_motionNodeAnimGraph = AnimGraphFactory::Create<TwoMotionNodeAnimGraph>();
  454. m_rootStateMachine = m_motionNodeAnimGraph->GetRootStateMachine();
  455. m_stateA = m_motionNodeAnimGraph->GetMotionNodeA();
  456. m_stateB = m_motionNodeAnimGraph->GetMotionNodeB();
  457. m_stateC = aznew AnimGraphMotionNode();
  458. m_stateC->SetName("C");
  459. m_rootStateMachine->AddChildNode(m_stateC);
  460. m_transitionAB = AddTransition(m_stateA, m_stateB, 1.0f);
  461. m_transitionAC = AddTransition(m_stateA, m_stateC, 1.0f);
  462. AZStd::vector<AnimGraphConnectionId> canBeInterruptedBy = { m_transitionAC->GetId() };
  463. m_transitionAB->SetCanBeInterruptedBy(canBeInterruptedBy);
  464. m_motionNodeAnimGraph->InitAfterLoading();
  465. }
  466. void TearDown() override
  467. {
  468. if (m_animGraphInstance)
  469. {
  470. m_animGraphInstance->Destroy();
  471. m_animGraphInstance = nullptr;
  472. }
  473. m_motionNodeAnimGraph.reset();
  474. AnimGraphFixture::TearDown();
  475. }
  476. public:
  477. AZStd::unique_ptr<TwoMotionNodeAnimGraph> m_motionNodeAnimGraph;
  478. AnimGraphNode* m_stateA = nullptr;
  479. AnimGraphNode* m_stateB = nullptr;
  480. AnimGraphNode* m_stateC = nullptr;
  481. AnimGraphStateTransition* m_transitionAB = nullptr;
  482. AnimGraphStateTransition* m_transitionAC = nullptr;
  483. };
  484. TEST_P(AnimGraphCopyPasteFixture_CanBeInterruptedBy, CopyCanBeInterruptedWithTransitionIds)
  485. {
  486. CommandSystem::CommandManager commandManager;
  487. AZStd::string result;
  488. MCore::CommandGroup commandGroup;
  489. const bool cutMode = GetParam();
  490. AZStd::vector<EMotionFX::AnimGraphNode*> nodesToCopy = { m_stateA, m_stateB, m_stateC };
  491. CommandSystem::AnimGraphCopyPasteData copyPasteData;
  492. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  493. /*targetParentNode=*/m_rootStateMachine,
  494. nodesToCopy,
  495. /*posX=*/0,
  496. /*posY=*/0,
  497. /*cutMode=*/cutMode,
  498. copyPasteData,
  499. /*ignoreTopLevelConnections=*/false);
  500. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  501. // Check if the can be interrupted by other transition ids are valid.
  502. size_t numTransitionsChecked = 0;
  503. const size_t numTransitions = m_rootStateMachine->GetNumTransitions();
  504. for (size_t i = 0; i < numTransitions; ++i)
  505. {
  506. const AnimGraphStateTransition* transition = m_rootStateMachine->GetTransition(i);
  507. const AZStd::vector<AZ::u64>& canBeInterruptedByTransitionIds = transition->GetCanBeInterruptedByTransitionIds();
  508. if (!canBeInterruptedByTransitionIds.empty())
  509. {
  510. for (AZ::u64 interruptionCandidateTransitionId : canBeInterruptedByTransitionIds)
  511. {
  512. const AnimGraphStateTransition* interruptionCandidate = m_rootStateMachine->FindTransitionById(interruptionCandidateTransitionId);
  513. EXPECT_TRUE(interruptionCandidate != nullptr) <<
  514. "In case the interruption transition candidate cannot be found something is wrong with the transition id relinking when copy/cut & pasting.";
  515. if (interruptionCandidate)
  516. {
  517. EXPECT_FALSE(interruptionCandidate == transition) <<
  518. "The interruption candidate cannot be the interruption itself. Something went wrong with the transition id relinking.";
  519. EXPECT_TRUE((transition->GetSourceNode() == interruptionCandidate->GetSourceNode()) || transition->GetIsWildcardTransition() || interruptionCandidate->GetIsWildcardTransition()) <<
  520. "The source nodes of the transition and the interruption candidate have to be the same, unless either of them is a wildcard.";
  521. }
  522. }
  523. numTransitionsChecked++;
  524. }
  525. }
  526. if (cutMode)
  527. {
  528. EXPECT_EQ(2, numTransitions) << "There should be exactly the same amount of transitions as before the operation.";
  529. EXPECT_EQ(1, numTransitionsChecked) << "Only one transition should hold interruption candidates.";
  530. }
  531. else
  532. {
  533. EXPECT_EQ(4, numTransitions) << "After copy & paste, there should be four transitions.";
  534. EXPECT_EQ(2, numTransitionsChecked) << "Two transitions should hold interruption candidates.";
  535. }
  536. }
  537. INSTANTIATE_TEST_CASE_P(CopyPasteTests,
  538. AnimGraphCopyPasteFixture_CanBeInterruptedBy,
  539. ::testing::Bool());
  540. ///////////////////////////////////////////////////////////////////////////
  541. class AnimGraphCopyPasteFixture_NodeTriggerValue
  542. : public AnimGraphFixture
  543. , public ::testing::WithParamInterface<bool>
  544. {
  545. public:
  546. void ConstructGraph() override
  547. {
  548. AnimGraphFixture::ConstructGraph();
  549. m_blendTreeAnimGraph = AnimGraphFactory::Create<OneBlendTreeNodeAnimGraph>();
  550. m_rootStateMachine = m_blendTreeAnimGraph->GetRootStateMachine();
  551. m_blendTree = m_blendTreeAnimGraph->GetBlendTreeNode();
  552. m_blendTree->SetName("TestBlendTree");
  553. /*
  554. +---------+
  555. |bindPoseA|----+
  556. +---------+ | +------+ +-----+
  557. +--->|blendN|------>|final|
  558. +--->| | +-----+
  559. +---------+ | +------+
  560. |bindPoseB|----+
  561. +---------+
  562. +------------+
  563. |TestBindPose|
  564. +------------+
  565. */
  566. BlendTreeFinalNode* finalNode = aznew BlendTreeFinalNode();
  567. m_bindPoseNodeA = aznew AnimGraphBindPoseNode();
  568. m_bindPoseNodeB = aznew AnimGraphBindPoseNode();
  569. m_testBindPoseNode = aznew AnimGraphBindPoseNode();
  570. m_testConnection = AZStd::make_unique<EMotionFX::BlendTreeConnection>(m_testBindPoseNode, AnimGraphBindPoseNode::PORTID_OUTPUT_POSE, BlendTreeBlendNNode::PORTID_INPUT_POSE_2);
  571. m_blendNNode = aznew BlendTreeBlendNNode();
  572. m_bindPoseNodeA->SetName("A");
  573. m_bindPoseNodeB->SetName("B");
  574. m_testBindPoseNode->SetName("TestBindPoseNode");
  575. m_blendNNode->SetName("C");
  576. finalNode->SetName("D");
  577. m_blendTree->AddChildNode(m_bindPoseNodeA);
  578. m_blendTree->AddChildNode(m_bindPoseNodeB);
  579. m_blendTree->AddChildNode(m_testBindPoseNode);
  580. m_blendTree->AddChildNode(m_blendNNode);
  581. m_blendTree->AddChildNode(finalNode);
  582. m_blendNNode->AddConnection(m_bindPoseNodeA, AnimGraphBindPoseNode::PORTID_OUTPUT_POSE, BlendTreeBlendNNode::PORTID_INPUT_POSE_0);
  583. m_blendNNode->AddConnection(m_bindPoseNodeB, AnimGraphBindPoseNode::PORTID_OUTPUT_POSE, BlendTreeBlendNNode::PORTID_INPUT_POSE_1);
  584. finalNode->AddConnection(m_blendNNode, BlendTreeBlendNNode::PORTID_OUTPUT_POSE, BlendTreeFinalNode::PORTID_INPUT_POSE);
  585. m_blendNNode->UpdateParamWeights();
  586. m_blendNNode->SetParamWeightsEquallyDistributed(-1.0f, 1.0f);
  587. m_blendTreeAnimGraph->InitAfterLoading();
  588. }
  589. void SetUp() override
  590. {
  591. AnimGraphFixture::SetUp();
  592. m_animGraphInstance->Destroy();
  593. m_animGraphInstance = m_blendTreeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet);
  594. }
  595. public:
  596. AZStd::unique_ptr<EMotionFX::BlendTreeConnection> m_testConnection;
  597. AnimGraphBindPoseNode* m_bindPoseNodeA = nullptr;
  598. AnimGraphBindPoseNode* m_bindPoseNodeB = nullptr;
  599. AnimGraphBindPoseNode* m_testBindPoseNode = nullptr;
  600. BlendTreeBlendNNode* m_blendNNode = nullptr;
  601. BlendTree* m_blendTree = nullptr;
  602. };
  603. TEST_P(AnimGraphCopyPasteFixture_NodeTriggerValue, PastedNodesHaveSameTriggerValue)
  604. {
  605. CommandSystem::CommandManager commandManager;
  606. AZStd::string result;
  607. MCore::CommandGroup commandGroup;
  608. const bool cutMode = GetParam();
  609. AZStd::vector<EMotionFX::AnimGraphNode*> nodesToCopy = { m_bindPoseNodeA, m_bindPoseNodeB, m_blendNNode };
  610. CommandSystem::AnimGraphCopyPasteData copyPasteData;
  611. // Copy and paste m_bindPoseNodeA, m_bindPoseNodeB, m_blendNNode in the blend tree.
  612. CommandSystem::ConstructCopyAnimGraphNodesCommandGroup(&commandGroup,
  613. /*targetParentNode=*/m_blendTree,
  614. nodesToCopy,
  615. /*posX=*/0,
  616. /*posY=*/0,
  617. /*cutMode=*/cutMode,
  618. copyPasteData,
  619. /*ignoreTopLevelConnections=*/false);
  620. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  621. const AZStd::vector<EMotionFX::BlendNParamWeight> &originalNodeParamWeights = m_blendNNode->GetParamWeights();
  622. const size_t numParamWeights = originalNodeParamWeights.size();
  623. if (cutMode)
  624. {
  625. // Node 0(bindPoseA), 1(bindPoseB), 3(blendN), 4(final) are the existing nodes.
  626. EXPECT_EQ(5, m_blendTree->GetNumChildNodes()) << "After cut and paste, the total node number of nodes in the blend tree should stay the same.";
  627. // After cut and paste, node 0, 1, 2 should be placed in index 2, 3, 4.
  628. // Node 3(test bind pose node) is now placed in index 0.
  629. EXPECT_TRUE(strcmp(m_blendTree->GetChildNode(0)->GetName(), "TestBindPoseNode") == 0) << "Test bind pose node should now place in index 0.";
  630. BlendTreeBlendNNode* cutBlendNNode = azrtti_cast<BlendTreeBlendNNode*>(m_blendTree->GetChildNode(4));
  631. // Check the ports are properly connected among the pasted nodes.
  632. EXPECT_TRUE(cutBlendNNode->CheckIfIsInputPortConnected(BlendTreeBlendNNode::INPUTPORT_POSE_0));
  633. EXPECT_TRUE(cutBlendNNode->CheckIfIsInputPortConnected(BlendTreeBlendNNode::INPUTPORT_POSE_1));
  634. const AZStd::vector<EMotionFX::BlendNParamWeight> &cutNodeParamWeights = cutBlendNNode->GetParamWeights();
  635. EXPECT_TRUE(cutNodeParamWeights.size() == numParamWeights) << "Number of cut and pasted parameter weights should be the same as the number of original parameter weights.";
  636. for (uint32 i = 0; i < numParamWeights; i++)
  637. {
  638. EXPECT_TRUE(originalNodeParamWeights[i].GetWeightRange() == cutNodeParamWeights[i].GetWeightRange()) <<
  639. "Parameter weights in the cut and pasted blend n node should be equal to the parameter weights in original blend n node.";
  640. }
  641. }
  642. else
  643. {
  644. EXPECT_EQ(8, m_blendTree->GetNumChildNodes()) << "After copy and paste, the total node number of nodes in the blend tree should increase by 3.";
  645. BlendTreeBlendNNode* copiedBlendNNode = azrtti_cast<BlendTreeBlendNNode*> (m_blendTree->GetChildNode(7));
  646. EXPECT_TRUE(copiedBlendNNode->CheckIfIsInputPortConnected(BlendTreeBlendNNode::INPUTPORT_POSE_0));
  647. EXPECT_TRUE(copiedBlendNNode->CheckIfIsInputPortConnected(BlendTreeBlendNNode::INPUTPORT_POSE_1));
  648. const AZStd::vector<EMotionFX::BlendNParamWeight> &copiedNodeParamWeights = copiedBlendNNode->GetParamWeights();
  649. EXPECT_TRUE(copiedNodeParamWeights.size() == numParamWeights) << "Number of copied parameter weights should be the same as the number of original parameter weights.";
  650. for (uint32 i = 0; i < numParamWeights; i++)
  651. {
  652. EXPECT_TRUE(originalNodeParamWeights[i].GetWeightRange() == copiedNodeParamWeights[i].GetWeightRange()) <<
  653. "Parameter weights in copied blend n node should be equal to the parameter weights in original blend n node.";
  654. }
  655. }
  656. BlendTreeBlendNNode* testBlendNNode = azrtti_cast<BlendTreeBlendNNode*> (m_blendTree->GetChildNode(cutMode ? 4:7));
  657. const AZStd::vector<EMotionFX::BlendNParamWeight> &testNodeParamWeights = testBlendNNode->GetParamWeights();
  658. commandGroup.Clear();
  659. CommandSystem::CreateNodeConnection(&commandGroup, testBlendNNode, m_testConnection.get());
  660. EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(commandGroup, result));
  661. EXPECT_TRUE(testBlendNNode->HasConnectionAtInputPort(BlendTreeBlendNNode::PORTID_INPUT_POSE_2)) <<
  662. "New connection should be created.";
  663. for (uint32 i = 0; i < m_blendNNode->GetParamWeights().size(); i++)
  664. {
  665. EXPECT_TRUE(originalNodeParamWeights[i].GetWeightRange() == testNodeParamWeights[i].GetWeightRange()) <<
  666. "Existing parameter weights should not be affected by adding a new connection.";
  667. }
  668. // Adding a new connection will call update param for blend N node,
  669. // the new connection will have the same value as its previous connection.
  670. EXPECT_TRUE(testNodeParamWeights[2].GetWeightRange() == 1) <<
  671. "New connection's parameter weight should be the weight value of 1.";
  672. }
  673. INSTANTIATE_TEST_CASE_P(AnimGraphCopyPasteTests,
  674. AnimGraphCopyPasteFixture_NodeTriggerValue,
  675. ::testing::Bool());
  676. } // namespace EMotionFX