AnimGraphReferenceNodeTests.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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 <AnimGraphFixture.h>
  9. #include <Integration/Assets/AnimGraphAsset.h>
  10. #include <MCore/Source/AttributeFloat.h>
  11. #include <AzCore/Asset/AssetManager.h>
  12. #include <EMotionFX/Source/AnimGraph.h>
  13. #include <EMotionFX/Source/AnimGraphBindPoseNode.h>
  14. #include <EMotionFX/Source/AnimGraphReferenceNode.h>
  15. #include <EMotionFX/Source/AnimGraphStateMachine.h>
  16. #include <EMotionFX/Source/BlendTree.h>
  17. #include <EMotionFX/Source/BlendTreeFinalNode.h>
  18. #include <EMotionFX/Source/BlendTreeParameterNode.h>
  19. #include <EMotionFX/Source/BlendTreeTransformNode.h>
  20. #include <EMotionFX/Source/EMotionFXManager.h>
  21. #include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
  22. #include <EMotionFX/Source/Parameter/ParameterFactory.h>
  23. #include <Tests/TestAssetCode/AnimGraphAssetFactory.h>
  24. #include <Tests/TestAssetCode/AnimGraphFactory.h>
  25. #include <Tests/Printers.h>
  26. namespace EMotionFX
  27. {
  28. // The smallest possible graph that contains a reference node
  29. class JustAReferenceNodeGraph
  30. : public EmptyAnimGraph
  31. {
  32. public:
  33. AZ_CLASS_ALLOCATOR(JustAReferenceNodeGraph, AnimGraphAllocator)
  34. JustAReferenceNodeGraph()
  35. {
  36. /*
  37. +--Root State---------------------------------------------+
  38. | |
  39. | +-Blend Tree----------------------------------------+ |
  40. | | | |
  41. | | +-Reference Node----+----->+-Final Node------+ | |
  42. | | +-------------------+ +-----------------+ | |
  43. | | | |
  44. | +---------------------------------------------------+ |
  45. +---------------------------------------------------------+
  46. */
  47. m_referenceNode = aznew AnimGraphReferenceNode();
  48. m_referenceNode->SetAnimGraph(this);
  49. m_referenceNode->SetName("ReferenceNodeInParentGraph");
  50. auto* finalNode = aznew BlendTreeFinalNode();
  51. finalNode->SetName("BlendTreeFinalNodeParentGraph");
  52. finalNode->AddUnitializedConnection(m_referenceNode, AnimGraphReferenceNode::PORTID_OUTPUT_POSE, BlendTreeFinalNode::PORTID_INPUT_POSE);
  53. m_blendTree = aznew BlendTree();
  54. m_blendTree->SetName("BlendTreeInParentGraph");
  55. m_blendTree->AddChildNode(m_referenceNode);
  56. m_blendTree->AddChildNode(finalNode);
  57. m_blendTree->SetFinalNodeId(finalNode->GetId());
  58. GetRootStateMachine()->AddChildNode(m_blendTree);
  59. GetRootStateMachine()->SetEntryState(m_blendTree);
  60. }
  61. BlendTree* GetBlendTree() const { return m_blendTree; }
  62. AnimGraphReferenceNode* GetReferenceNode() const { return m_referenceNode; }
  63. private:
  64. BlendTree* m_blendTree = nullptr;
  65. AnimGraphReferenceNode* m_referenceNode = nullptr;
  66. };
  67. class ReferenceNodeWithParameterGraph
  68. : public JustAReferenceNodeGraph
  69. {
  70. public:
  71. AZ_CLASS_ALLOCATOR(ReferenceNodeWithParameterGraph, AnimGraphAllocator)
  72. ReferenceNodeWithParameterGraph()
  73. {
  74. /*
  75. +--Root State---------------------------------------------------------------------+
  76. | |
  77. | +-Blend Tree----------------------------------------------------------------+ |
  78. | | | |
  79. | | +-ParameterNode---+---->+-Reference Node----+----->+-Final Node------+ | |
  80. | | +-----------------+ +-------------------+ +-----------------+ | |
  81. | | | |
  82. | +---------------------------------------------------------------------------+ |
  83. +---------------------------------------------------------------------------------+
  84. */
  85. m_parameter = static_cast<FloatSliderParameter*>(ParameterFactory::Create(azrtti_typeid<FloatSliderParameter>()));
  86. AddParameter(m_parameter);
  87. BlendTreeParameterNode* parameterNode = aznew BlendTreeParameterNode();
  88. parameterNode->SetName("ParameterNodeInParentGraph");
  89. GetBlendTree()->AddChildNode(parameterNode);
  90. GetReferenceNode()->AddUnitializedConnection(parameterNode, 0, 0);
  91. }
  92. FloatSliderParameter* GetParameter() const { return m_parameter; }
  93. private:
  94. FloatSliderParameter* m_parameter = nullptr;
  95. };
  96. // An AnimGraph that will apply a transform based on a float parameter value
  97. class BlendTreeTransformNodeAnimGraph
  98. : public EmptyAnimGraph
  99. {
  100. public:
  101. AZ_CLASS_ALLOCATOR(BlendTreeTransformNodeAnimGraph, AnimGraphAllocator)
  102. BlendTreeTransformNodeAnimGraph()
  103. {
  104. /*
  105. +--Root State--------------------------------------------------------------+
  106. | |
  107. | +-Blend Tree---------------------------------------------------------+ |
  108. | | | |
  109. | | +-Parameter Node--+--->+-Transform Node--+-->+-Final Node-----+ | |
  110. | | +-----------------+ +-----------------+ +-----------------+ | |
  111. | | | |
  112. | +--------------------------------------------------------------------+ |
  113. +--------------------------------------------------------------------------+
  114. */
  115. BlendTreeParameterNode* parameterNode = aznew BlendTreeParameterNode();
  116. parameterNode->SetName("ParameterNodeInReferenceGraph");
  117. m_transformNode = aznew BlendTreeTransformNode();
  118. m_transformNode->SetName("BlendTreeTransformNodeInReferenceGraph");
  119. m_transformNode->AddUnitializedConnection(parameterNode, 0, BlendTreeTransformNode::PORTID_INPUT_TRANSLATE_AMOUNT);
  120. m_transformNode->SetMinTranslation(AZ::Vector3::CreateZero());
  121. m_transformNode->SetMaxTranslation(AZ::Vector3::CreateAxisX(10.0f));
  122. m_transformNode->SetTargetNodeName("rootJoint"); // From the SimpleJointChain actor
  123. BlendTreeFinalNode* finalNode = aznew BlendTreeFinalNode();
  124. finalNode->SetName("BlendTreeFinalNodeInReferenceGraph");
  125. finalNode->AddUnitializedConnection(m_transformNode, BlendTreeTransformNode::PORTID_OUTPUT_POSE, BlendTreeFinalNode::PORTID_INPUT_POSE);
  126. BlendTree* blendTree = aznew BlendTree();
  127. blendTree->SetName("BlendTreeInReferenceGraph");
  128. blendTree->AddChildNode(m_transformNode);
  129. blendTree->AddChildNode(finalNode);
  130. blendTree->AddChildNode(parameterNode);
  131. GetRootStateMachine()->AddChildNode(blendTree);
  132. GetRootStateMachine()->SetEntryState(blendTree);
  133. m_parameter = static_cast<FloatSliderParameter*>(ParameterFactory::Create(azrtti_typeid<FloatSliderParameter>()));
  134. AddParameter(m_parameter);
  135. }
  136. BlendTreeTransformNode* GetTransformNode() const { return m_transformNode; }
  137. FloatSliderParameter* GetParameter() const { return m_parameter; }
  138. private:
  139. BlendTreeTransformNode* m_transformNode = nullptr;
  140. FloatSliderParameter* m_parameter = nullptr;
  141. };
  142. // Add a reference node without any asset in it
  143. class AnimGraphReferenceNodeBaseTests : public AnimGraphFixture
  144. {
  145. public:
  146. void ConstructGraph() override
  147. {
  148. m_animGraph = AnimGraphFactory::Create<JustAReferenceNodeGraph>();
  149. m_rootStateMachine = m_animGraph->GetRootStateMachine();
  150. }
  151. BlendTree* GetBlendTree() const
  152. {
  153. return static_cast<JustAReferenceNodeGraph*>(m_animGraph.get())->GetBlendTree();
  154. }
  155. AnimGraphReferenceNode* GetReferenceNode() const
  156. {
  157. return static_cast<JustAReferenceNodeGraph*>(m_animGraph.get())->GetReferenceNode();
  158. }
  159. };
  160. // Basic test that just evaluates the node. Since the node is not doing anything,
  161. // The pose should not be affected.
  162. TEST_F(AnimGraphReferenceNodeBaseTests, VerifyRootTransform)
  163. {
  164. Evaluate();
  165. EXPECT_EQ(GetOutputTransform(), Transform::CreateIdentity());
  166. }
  167. // Add a reference node with an empty asset
  168. class AnimGraphReferenceNodeWithAssetTests : public AnimGraphReferenceNodeBaseTests
  169. {
  170. public:
  171. void ConstructGraph() override
  172. {
  173. AnimGraphReferenceNodeBaseTests::ConstructGraph();
  174. auto animGraphAsset = ConstructReferenceGraphAsset();
  175. GetReferenceNode()->SetAnimGraphAsset(animGraphAsset);
  176. GetReferenceNode()->OnAssetReady(animGraphAsset);
  177. }
  178. virtual AZ::Data::Asset<Integration::AnimGraphAsset> ConstructReferenceGraphAsset()
  179. {
  180. AZ::Data::Asset<Integration::AnimGraphAsset> referenceAnimGraphAsset = AnimGraphAssetFactory::Create(AZ::Data::AssetId("{E8FBAEF1-CBC5-43C2-83C8-9F8812857494}"), AnimGraphFactory::Create<EmptyAnimGraph>());
  181. m_referenceAnimGraph = referenceAnimGraphAsset.Get()->GetAnimGraph();
  182. m_referenceAnimGraph->InitAfterLoading();
  183. return referenceAnimGraphAsset;
  184. }
  185. AnimGraph* m_referenceAnimGraph = nullptr;
  186. };
  187. // Load an empty anim graph into the reference node
  188. TEST_F(AnimGraphReferenceNodeWithAssetTests, VerifyRootTransform)
  189. {
  190. Evaluate();
  191. EXPECT_EQ(GetOutputTransform(), Transform::CreateIdentity());
  192. }
  193. class AnimGraphReferenceNodeWithContentsTests : public AnimGraphReferenceNodeWithAssetTests
  194. {
  195. public:
  196. void ConstructGraph() override
  197. {
  198. auto animGraph = AnimGraphFactory::Create<ReferenceNodeWithParameterGraph>();
  199. m_rootStateMachine = animGraph->GetRootStateMachine();
  200. m_parameter = animGraph->GetParameter();
  201. m_animGraph = AZStd::move(animGraph);
  202. m_referencedAsset = ConstructReferenceGraphAsset();
  203. }
  204. AZ::Data::Asset<Integration::AnimGraphAsset> ConstructReferenceGraphAsset() override
  205. {
  206. AZ::Data::Asset<Integration::AnimGraphAsset> referenceAnimGraphAsset = AnimGraphAssetFactory::Create(AZ::Data::AssetId("{E8FBAEF1-CBC5-43C2-83C8-9F8812857494}"), AnimGraphFactory::Create<BlendTreeTransformNodeAnimGraph>());
  207. m_referenceAnimGraph = referenceAnimGraphAsset.Get()->GetAnimGraph();
  208. m_referenceAnimGraph->InitAfterLoading();
  209. m_transformNode = static_cast<BlendTreeTransformNodeAnimGraph*>(m_referenceAnimGraph)->GetTransformNode();
  210. return referenceAnimGraphAsset;
  211. }
  212. void TearDown() override
  213. {
  214. m_referencedAsset.Release();
  215. AnimGraphReferenceNodeWithAssetTests::TearDown();
  216. }
  217. AZ::Data::Asset<Integration::AnimGraphAsset> m_referencedAsset;
  218. Parameter* m_parameter = nullptr;
  219. BlendTreeTransformNode* m_transformNode = nullptr;
  220. };
  221. TEST_F(AnimGraphReferenceNodeWithContentsTests, VerifyRootTransform)
  222. {
  223. GetReferenceNode()->SetAnimGraphAsset(m_referencedAsset);
  224. GetReferenceNode()->OnAssetReady(m_referencedAsset);
  225. GetEMotionFX().Update(0.0f);
  226. EXPECT_EQ(Transform::CreateIdentity(), GetOutputTransform());
  227. static_cast<MCore::AttributeFloat*>(m_animGraphInstance->GetParameterValue(static_cast<uint32>(m_animGraph->FindParameterIndex(m_parameter).GetValue())))->SetValue(1.0f);
  228. GetEMotionFX().Update(0.0f);
  229. EXPECT_EQ(Transform::CreateIdentity() * AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)), GetOutputTransform());
  230. }
  231. class AnimGraphWithNestedReferencesTests
  232. : public AnimGraphFixture
  233. {
  234. public:
  235. void ConstructGraph() override
  236. {
  237. m_animGraph = AnimGraphFactory::Create<ReferenceNodeWithParameterGraph>();
  238. m_rootStateMachine = m_animGraph->GetRootStateMachine();
  239. m_secondLevelAsset = AnimGraphAssetFactory::Create(AZ::Data::AssetId("{5B05769E-2532-4B1E-A37B-E8CCB303E797}"), AnimGraphFactory::Create<ReferenceNodeWithParameterGraph>());
  240. auto thirdLevel = AnimGraphAssetFactory::Create(AZ::Data::AssetId("{2D605BAF-5C71-44AE-884F-89338AD49F03}"), AnimGraphFactory::Create<ReferenceNodeWithParameterGraph>());
  241. auto bottomLevel = AnimGraphAssetFactory::Create(AZ::Data::AssetId("{C23E2C8D-72C0-4EDE-BB37-48993A3EE83D}"), AnimGraphFactory::Create<BlendTreeTransformNodeAnimGraph>());
  242. m_secondLevelAsset->GetAnimGraph()->InitAfterLoading();
  243. thirdLevel->GetAnimGraph()->InitAfterLoading();
  244. bottomLevel->GetAnimGraph()->InitAfterLoading();
  245. auto thirdReferenceNode = static_cast<ReferenceNodeWithParameterGraph*>(thirdLevel.Get()->GetAnimGraph())->GetReferenceNode();
  246. thirdReferenceNode->SetAnimGraphAsset(bottomLevel);
  247. thirdReferenceNode->OnAssetReady(bottomLevel);
  248. auto secondReferenceNode = static_cast<ReferenceNodeWithParameterGraph*>(m_secondLevelAsset.Get()->GetAnimGraph())->GetReferenceNode();
  249. secondReferenceNode->SetAnimGraphAsset(thirdLevel);
  250. secondReferenceNode->OnAssetReady(thirdLevel);
  251. m_topLevelParameter = static_cast<ReferenceNodeWithParameterGraph*>(m_animGraph.get())->GetParameter();
  252. }
  253. void TearDown() override
  254. {
  255. m_secondLevelAsset.Release();
  256. AnimGraphFixture::TearDown();
  257. }
  258. AZ::Data::Asset<Integration::AnimGraphAsset> m_secondLevelAsset{};
  259. Parameter* m_topLevelParameter = nullptr;
  260. };
  261. TEST_F(AnimGraphWithNestedReferencesTests, VerifyRootTransform)
  262. {
  263. // The AnimGraphFixture doesn't call InitAfterLoading until after
  264. // ConstructGraph() is done, and these bits have to run after
  265. // InitAfterLoading()
  266. auto firstReferenceNode = static_cast<ReferenceNodeWithParameterGraph*>(m_animGraph.get())->GetReferenceNode();
  267. firstReferenceNode->SetAnimGraphAsset(m_secondLevelAsset);
  268. firstReferenceNode->OnAssetReady(m_secondLevelAsset);
  269. GetEMotionFX().Update(0.0f);
  270. EXPECT_EQ(Transform::CreateIdentity(), GetOutputTransform());
  271. // Changing this one parameter value should change it through all 3
  272. // layers of reference nodes, down to the referenced Transform node
  273. static_cast<MCore::AttributeFloat*>(m_animGraphInstance->GetParameterValue(static_cast<uint32>(m_animGraph->FindParameterIndex(m_topLevelParameter).GetValue())))->SetValue(1.0f);
  274. GetEMotionFX().Update(0.0f);
  275. EXPECT_EQ(Transform::CreateIdentity() * AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)), GetOutputTransform());
  276. }
  277. ///////////////////////////////////////////////////////////////////////////
  278. class AnimGraphReferenceNodeDeferredInitTests
  279. : public AnimGraphFixture
  280. {
  281. public:
  282. void ConstructGraph() override
  283. {
  284. /*
  285. +-Root state machine--------------------------------------------+
  286. | |
  287. | +------------+ +---------------+ +----------+ |
  288. | =>| BindPose |------>| ReferenceNode |------>| EndState | |
  289. | +------------+ +---------------+ +----------+ |
  290. | |
  291. +---------------------------------------------------------------+
  292. +-Root state machine (referenceNode)----------------------------+
  293. | |
  294. | +---------------+ +----------+ |
  295. | =>| RefBindPose |------>| endState | |
  296. | +---------------+ +----------+ |
  297. | |
  298. +---------------------------------------------------------------+
  299. */
  300. AnimGraphFixture::ConstructGraph();
  301. AnimGraphBindPoseNode* entryState = aznew AnimGraphBindPoseNode();
  302. entryState->SetName("StateA");
  303. m_rootStateMachine->AddChildNode(entryState);
  304. m_rootStateMachine->SetEntryState(entryState);
  305. m_referenceNode = aznew AnimGraphReferenceNode();
  306. m_referenceNode->SetName("StateB (Reference)");
  307. m_rootStateMachine->AddChildNode(m_referenceNode);
  308. AddTransitionWithTimeCondition(entryState, m_referenceNode, 1.0f, 1.0f);
  309. AnimGraphBindPoseNode* endState = aznew AnimGraphBindPoseNode();
  310. endState->SetName("StateC");
  311. m_rootStateMachine->AddChildNode(endState);
  312. AddTransitionWithTimeCondition(m_referenceNode, endState, 1.0f, 1.0f);
  313. AnimGraph* referenceAnimGraph = CreateReferenceGraph();
  314. AZ::Data::Asset<Integration::AnimGraphAsset> animGraphAsset = AZ::Data::AssetManager::Instance().CreateAsset<Integration::AnimGraphAsset>(AZ::Data::AssetId("{E8FBAEF1-CBC5-43C2-83C8-9F8812857494}"));
  315. animGraphAsset.GetAs<Integration::AnimGraphAsset>()->SetData(referenceAnimGraph);
  316. m_referenceNode->SetAnimGraphAsset(animGraphAsset);
  317. referenceAnimGraph->InitAfterLoading();
  318. m_referenceNode->SetAnimGraph(m_animGraph.get());
  319. m_referenceNode->OnAssetReady(animGraphAsset);
  320. }
  321. AnimGraph* CreateReferenceGraph()
  322. {
  323. AnimGraph* referenceAnimGraph = aznew AnimGraph();
  324. AnimGraphStateMachine* referenceRootSM = aznew AnimGraphStateMachine();
  325. referenceAnimGraph->SetRootStateMachine(referenceRootSM);
  326. AnimGraphBindPoseNode* referenceEntryState = aznew AnimGraphBindPoseNode();
  327. referenceEntryState->SetName("RefEntryState");
  328. referenceRootSM->AddChildNode(referenceEntryState);
  329. referenceRootSM->SetEntryState(referenceEntryState);
  330. AnimGraphBindPoseNode* referenceEndState = aznew AnimGraphBindPoseNode();
  331. referenceEndState->SetName("RefEndState");
  332. referenceRootSM->AddChildNode(referenceEndState);
  333. AddTransitionWithTimeCondition(referenceEntryState, referenceEndState, 1.0f, 1.0f);
  334. return referenceAnimGraph;
  335. }
  336. void TearDown() override
  337. {
  338. m_referenceNode->GetReferencedAnimGraphAsset().Release();
  339. AnimGraphFixture::TearDown();
  340. }
  341. public:
  342. AnimGraphReferenceNode* m_referenceNode = nullptr;
  343. };
  344. TEST_F(AnimGraphReferenceNodeDeferredInitTests, DeferredReferenceGraphTest)
  345. {
  346. const size_t numObjects = m_animGraph->GetNumObjects();
  347. EXPECT_EQ(numObjects, m_animGraphInstance->GetNumUniqueObjectDatas())
  348. << "There should be a unique data placeholder for each anim graph object.";
  349. EXPECT_EQ(m_animGraphInstance->CalcNumAllocatedUniqueDatas(), 0)
  350. << "Unique datas should not be allocated yet.";
  351. // Entry state active, conditions are watching.
  352. GetEMotionFX().Update(0.0f);
  353. EXPECT_EQ(m_animGraphInstance->CalcNumAllocatedUniqueDatas(), 3)
  354. << "Exactly 3 unique datas should be allocated now, the root state machine, the entry state (StateA) as well as the time condition.";
  355. // Transitioning from entry to reference state.
  356. GetEMotionFX().Update(1.5f);
  357. EXPECT_EQ(m_animGraphInstance->CalcNumAllocatedUniqueDatas(), 6)
  358. << "As we're transitioning, unique datas from the root SM, State A (entry node), the transition (A->B) + condition, State B and the new condition of B->C as the count-down timer already started as soon as B gets activated.";
  359. const AnimGraphReferenceNode::UniqueData* referenceNodeUniqueData = static_cast<AnimGraphReferenceNode::UniqueData*>(m_animGraphInstance->GetUniqueObjectData(m_referenceNode->GetObjectIndex()));
  360. EXPECT_NE(referenceNodeUniqueData, nullptr)
  361. << "Unique data for reference node should have already been allocated, as we're transitioning into the node.";
  362. const AnimGraphInstance* referenceAnimGraphInstance = referenceNodeUniqueData->m_referencedAnimGraphInstance;
  363. EXPECT_NE(referenceAnimGraphInstance, nullptr)
  364. << "Anim graph instance for reference node should be created already, as we're transitioning into the reference node.";
  365. EXPECT_EQ(referenceAnimGraphInstance->CalcNumAllocatedUniqueDatas(), 3)
  366. << "Exactly 3 unique datas should be allocated in the reference instance now, the root state machine, the entry state (RefEntryState) as well as the time condition.";
  367. // The reference node state machine transitions into the end state.
  368. GetEMotionFX().Update(1.0f);
  369. EXPECT_EQ(referenceAnimGraphInstance->CalcNumAllocatedUniqueDatas(), 5)
  370. << "The transition as well as the end state unique datas are now also allocated.";
  371. const AnimGraph* refAnimGraph = referenceAnimGraphInstance->GetAnimGraph();
  372. EXPECT_EQ(referenceAnimGraphInstance->CalcNumAllocatedUniqueDatas(), refAnimGraph->GetNumObjects())
  373. << "All objects should have their unique datas allocated now.";
  374. // The root state machine transitioned into the end state.
  375. GetEMotionFX().Update(1.0f);
  376. EXPECT_EQ(m_animGraphInstance->CalcNumAllocatedUniqueDatas(), 8)
  377. << "The last transition as well as the end state of the root state machine unique datas should now be allocated.";
  378. EXPECT_EQ(m_animGraphInstance->CalcNumAllocatedUniqueDatas(), numObjects)
  379. << "We should have reached all states, transitions and conditions.";
  380. }
  381. ///////////////////////////////////////////////////////////////////////////
  382. using AnimGraphReferenceNodeCircularDependencyDetectionTests = SystemComponentFixture;
  383. TEST_F(AnimGraphReferenceNodeCircularDependencyDetectionTests, CircularDependencyDetectionTest)
  384. {
  385. AZ::Data::Asset<Integration::AnimGraphAsset> assetA = AnimGraphAssetFactory::Create(AZ::Data::AssetId("{1CB9DC29-5063-4F0B-BF31-4610C8E683EA}"), AnimGraphFactory::Create<JustAReferenceNodeGraph>());
  386. AZ::Data::Asset<Integration::AnimGraphAsset> assetB = AnimGraphAssetFactory::Create(AZ::Data::AssetId("{4EE7A2F6-5982-4DBE-8F66-03BEB456520A}"), AnimGraphFactory::Create<JustAReferenceNodeGraph>());
  387. AnimGraph* animGraphA = assetA->GetAnimGraph();
  388. AnimGraph* animGraphB = assetB->GetAnimGraph();
  389. animGraphA->InitAfterLoading();
  390. animGraphB->InitAfterLoading();
  391. AnimGraphReferenceNode* refNodeA = static_cast<AnimGraphReferenceNode*>(animGraphA->GetRootStateMachine()->GetChildNode(0)->GetChildNode(0));
  392. AnimGraphReferenceNode* refNodeB = static_cast<AnimGraphReferenceNode*>(animGraphB->GetRootStateMachine()->GetChildNode(0)->GetChildNode(0));
  393. refNodeA->SetAnimGraphAsset(assetB);
  394. refNodeB->SetAnimGraphAsset(assetA);
  395. refNodeA->OnAssetReady(assetB);
  396. refNodeB->OnAssetReady(assetA);
  397. // Cycle detection for AnimGraphs with no instances only works when
  398. // we're in editor mode
  399. GetEMotionFX().SetIsInEditorMode(true);
  400. AZStd::unordered_set<const AnimGraphNode*> nodes;
  401. EXPECT_TRUE(animGraphA->GetRootStateMachine()->RecursiveDetectCycles(nodes));
  402. GetEMotionFX().SetIsInEditorMode(false);
  403. // Break the cyclic reference to allow memory to be released
  404. refNodeA->SetAnimGraphAsset({});
  405. }
  406. } // end namespace EMotionFX