Node.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  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. // AZ
  9. #include <AzCore/RTTI/BehaviorContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/std/smart_ptr/make_shared.h>
  13. // Graph Model
  14. #include <GraphModel/Model/Connection.h>
  15. #include <GraphModel/Model/Graph.h>
  16. #include <GraphModel/Model/Node.h>
  17. #include <GraphModel/Model/Slot.h>
  18. namespace GraphModel
  19. {
  20. static constexpr int InvalidExtendableSlot = -1;
  21. void Node::Reflect(AZ::ReflectContext* context)
  22. {
  23. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  24. if (serializeContext)
  25. {
  26. serializeContext->Class<Node>()
  27. ->Version(0)
  28. // m_id isn't reflected because this information is already stored in the Graph's node map
  29. // m_outputDataSlots isn't reflected because its Slot::m_value field is unused
  30. // m_inputEventSlots isn't reflected because its Slot::m_value field is unused
  31. // m_outputEventSlots isn't reflected because its Slot::m_value field is unused
  32. ->Field("m_propertySlots", &Node::m_propertySlots)
  33. ->Field("m_inputDataSlots", &Node::m_inputDataSlots)
  34. ->Field("m_extendableSlots", &Node::m_extendableSlots)
  35. ;
  36. }
  37. }
  38. Node::Node(GraphPtr graph)
  39. : GraphElement(graph)
  40. {
  41. }
  42. void Node::PostLoadSetup(GraphPtr graph, NodeId id)
  43. {
  44. AZ_Assert(nullptr == GetGraph(), "Node isn't freshly loaded.");
  45. AZ_Assert(m_id == INVALID_NODE_ID, "Node isn't freshly loaded.");
  46. m_graph = graph;
  47. m_id = id;
  48. PostLoadSetup();
  49. }
  50. void Node::PostLoadSetup()
  51. {
  52. RegisterSlots();
  53. // Moving slots between input data slot and property slot containers in case the layout of the slot definitions change.
  54. for (const auto& def : m_inputDataSlotDefinitions)
  55. {
  56. for (const auto& slot : m_propertySlots)
  57. {
  58. if (def->GetName() == slot.first.m_name)
  59. {
  60. m_inputDataSlots.insert(slot);
  61. m_propertySlots.erase(slot.first);
  62. break;
  63. }
  64. }
  65. }
  66. for (const auto& def : m_propertySlotDefinitions)
  67. {
  68. for (const auto& slot : m_inputDataSlots)
  69. {
  70. if (def->GetName() == slot.first.m_name)
  71. {
  72. m_propertySlots.insert(slot);
  73. m_inputDataSlots.erase(slot.first);
  74. break;
  75. }
  76. }
  77. }
  78. // Make sure the loaded Slot data aligns with the Node's input slot descriptions
  79. SyncAndSetupSlots(m_propertySlots, m_propertySlotDefinitions);
  80. SyncAndSetupSlots(m_inputDataSlots, m_inputDataSlotDefinitions);
  81. SyncAndSetupExtendableSlots();
  82. // These slots types are a bit different, because they don't actually have any data to be serialized, so instead of SyncAndSetupSlots we need to create them.
  83. CreateSlotData(m_outputDataSlots, m_outputDataSlotDefinitions);
  84. CreateSlotData(m_inputEventSlots, m_inputEventSlotDefinitions);
  85. CreateSlotData(m_outputEventSlots, m_outputEventSlotDefinitions);
  86. [[maybe_unused]] int numExtendableSlots = 0;
  87. for (const auto& slotPair : m_extendableSlots)
  88. {
  89. numExtendableSlots += aznumeric_cast<int>(slotPair.second.size());
  90. }
  91. AZ_Assert(m_allSlots.size() == m_propertySlots.size() + m_inputDataSlots.size() + m_outputDataSlots.size() + m_inputEventSlots.size() + m_outputEventSlots.size() + numExtendableSlots, "Slot counts don't match");
  92. AZ_Assert(m_allSlotDefinitions.size() == m_propertySlotDefinitions.size() + m_inputDataSlotDefinitions.size() + m_outputDataSlotDefinitions.size() + m_inputEventSlotDefinitions.size() + m_outputEventSlotDefinitions.size() + m_extendableSlotDefinitions.size(), "SlotDefinition counts don't match");
  93. ClearCachedData();
  94. }
  95. //! Returns the name that will be displayed as the sub-title of the Node in the UI
  96. const char* Node::GetSubTitle() const
  97. {
  98. return "";
  99. }
  100. void Node::CreateSlotData()
  101. {
  102. AZ_Assert(m_allSlots.empty(), "CreateSlotData() should only be called once after creating a new node.");
  103. CreateSlotData(m_propertySlots, m_propertySlotDefinitions);
  104. CreateSlotData(m_inputDataSlots, m_inputDataSlotDefinitions);
  105. CreateSlotData(m_outputDataSlots, m_outputDataSlotDefinitions);
  106. CreateSlotData(m_inputEventSlots, m_inputEventSlotDefinitions);
  107. CreateSlotData(m_outputEventSlots, m_outputEventSlotDefinitions);
  108. CreateExtendableSlotData();
  109. }
  110. void Node::ClearCachedData()
  111. {
  112. {
  113. AZStd::scoped_lock lock(m_maxInputDepthMutex);
  114. m_maxInputDepth = AZStd::numeric_limits<uint32_t>::max();
  115. }
  116. {
  117. AZStd::scoped_lock lock(m_maxOutputDepthMutex);
  118. m_maxOutputDepth = AZStd::numeric_limits<uint32_t>::max();
  119. }
  120. for (auto& slotPair : m_allSlots)
  121. {
  122. slotPair.second->ClearCachedData();
  123. }
  124. }
  125. void Node::CreateSlotData(SlotMap& slotMap, const SlotDefinitionList& slotDefinitionList)
  126. {
  127. AZ_Assert(slotMap.empty(), "This node isn't freshly initialized");
  128. for (SlotDefinitionPtr slotDefinition : slotDefinitionList)
  129. {
  130. SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition);
  131. slot->SetValue(slotDefinition->GetDefaultValue());
  132. SlotId slotId(slotDefinition->GetName());
  133. auto newEntry = AZStd::make_pair(slotId, slot);
  134. slotMap.insert(newEntry);
  135. m_allSlots.insert(newEntry);
  136. }
  137. }
  138. void Node::CreateExtendableSlotData()
  139. {
  140. for (SlotDefinitionPtr slotDefinition : m_extendableSlotDefinitions)
  141. {
  142. // Skip creating slots for this definition if a set already exists in the map, since we
  143. // use this method to populate slots that have been loaded on existing nodes where
  144. // new slots have been added in addition to creating slots on nodes for the first time
  145. const SlotName& slotName = slotDefinition->GetName();
  146. if (m_extendableSlots.find(slotName) != m_extendableSlots.end())
  147. {
  148. continue;
  149. }
  150. ExtendableSlotSet extendableSet;
  151. // When creating a new node, we need to populate enough extendable slots to satisfy
  152. // the minimum requirement of the definition.
  153. int minimumSlots = slotDefinition->GetMinimumSlots();
  154. for (int i = 0; i < minimumSlots; ++i)
  155. {
  156. SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition, i);
  157. slot->SetValue(slotDefinition->GetDefaultValue());
  158. m_allSlots.insert(AZStd::make_pair(slot->GetSlotId(), slot));
  159. extendableSet.insert(slot);
  160. }
  161. m_extendableSlots.insert(AZStd::make_pair(slotName, extendableSet));
  162. }
  163. }
  164. void Node::SyncAndSetupSlots(SlotMap& slotData, Node::SlotDefinitionList& slotDefinitions)
  165. {
  166. // Do PostLoadSetup to attach each Slot to its SlotDefinition
  167. // Also remove any Slot that doesn't have a corresponding SlotDefinition
  168. for (auto slotDataIter = slotData.begin(); slotDataIter != slotData.end(); /* increment in loop */)
  169. {
  170. const SlotId& slotId = slotDataIter->first;
  171. const SlotName& slotName = slotId.m_name;
  172. SlotPtr slot = slotDataIter->second;
  173. auto slotDefinitionIter = AZStd::find_if(
  174. slotDefinitions.begin(),
  175. slotDefinitions.end(),
  176. [&slotName](SlotDefinitionPtr slotDefinition)
  177. {
  178. return slotDefinition->GetName() == slotName;
  179. });
  180. if (slotDefinitionIter == slotDefinitions.end())
  181. {
  182. AZ_Warning(
  183. GetGraph()->GetSystemName(), false, "Found data for unrecognized slot [%s]. It will be ignored.", slotName.c_str());
  184. slotDataIter = slotData.erase(slotDataIter);
  185. }
  186. else
  187. {
  188. // If PostLoadSetup fails (could be due to type mismatch)
  189. slot->PostLoadSetup(GetGraph(), *slotDefinitionIter);
  190. ++slotDataIter;
  191. }
  192. }
  193. // Make sure all SlotDefinitions have slot data. This would normally happen when the code for a Node class has been changed to add a new slot.
  194. for (SlotDefinitionPtr slotDefinition : slotDefinitions)
  195. {
  196. SlotId slotId(slotDefinition->GetName());
  197. if (slotData.find(slotId) == slotData.end())
  198. {
  199. AZ_Warning(GetGraph()->GetSystemName(), false, "No data found for slot [%s]. It will be filled with default values.", slotDefinition->GetName().c_str());
  200. SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition);
  201. slotData[slotId] = slot;
  202. }
  203. }
  204. m_allSlots.insert(slotData.begin(), slotData.end());
  205. }
  206. void Node::SyncAndSetupExtendableSlots()
  207. {
  208. // Do PostLoadSetup to attach each Slot to its SlotDefinition
  209. // Also remove any Slot that doesn't have a corresponding SlotDefinition
  210. for (auto slotDataIter = m_extendableSlots.begin(); slotDataIter != m_extendableSlots.end(); /* increment in loop */)
  211. {
  212. const SlotName& slotName = slotDataIter->first;
  213. auto slotDefinitionIter = AZStd::find_if(
  214. m_extendableSlotDefinitions.begin(),
  215. m_extendableSlotDefinitions.end(),
  216. [&slotName](SlotDefinitionPtr slotDefinition)
  217. {
  218. return slotDefinition->GetName() == slotName;
  219. });
  220. if (slotDefinitionIter == m_extendableSlotDefinitions.end())
  221. {
  222. AZ_Warning(
  223. GetGraph()->GetSystemName(), false, "Found data for unrecognized slot [%s]. It will be ignored.", slotName.c_str());
  224. slotDataIter = m_extendableSlots.erase(slotDataIter);
  225. }
  226. else
  227. {
  228. for (SlotPtr slot : slotDataIter->second)
  229. {
  230. // If PostLoadSetup fails (could be due to type mismatch)
  231. slot->PostLoadSetup(GetGraph(), *slotDefinitionIter);
  232. m_allSlots.insert(AZStd::make_pair(slot->GetSlotId(), slot));
  233. }
  234. ++slotDataIter;
  235. }
  236. }
  237. // Make sure all SlotDefinitions have slot data. This would normally happen when the code for a Node class has been changed to add a new slot.
  238. CreateExtendableSlotData();
  239. }
  240. //! Returns node type (general by default) which can be overriden for other types, such as wrapper nodes
  241. NodeType Node::GetNodeType() const
  242. {
  243. return NodeType::GeneralNode;
  244. }
  245. NodeId Node::GetId() const
  246. {
  247. return m_id;
  248. }
  249. uint32_t Node::GetMaxInputDepth() const
  250. {
  251. AZStd::scoped_lock lock(m_maxInputDepthMutex);
  252. if (m_maxInputDepth == AZStd::numeric_limits<uint32_t>::max())
  253. {
  254. m_maxInputDepth = 0;
  255. for (const auto& slotPair : m_inputDataSlots)
  256. {
  257. const auto& slot = slotPair.second;
  258. AZ_Assert(slot->GetSlotDirection() == GraphModel::SlotDirection::Input, "Slots in this container must be input slots.");
  259. for (const auto& connection : slot->GetConnections())
  260. {
  261. AZ_Assert(connection->GetSourceNode().get() != this, "This should never be the source node on an input connection.");
  262. AZ_Assert(connection->GetTargetNode().get() == this, "This should always be the target node on an input connection.");
  263. m_maxInputDepth = AZStd::max(m_maxInputDepth, connection->GetSourceNode()->GetMaxInputDepth() + 1);
  264. }
  265. }
  266. }
  267. return m_maxInputDepth;
  268. }
  269. uint32_t Node::GetMaxOutputDepth() const
  270. {
  271. AZStd::scoped_lock lock(m_maxOutputDepthMutex);
  272. if (m_maxOutputDepth == AZStd::numeric_limits<uint32_t>::max())
  273. {
  274. m_maxOutputDepth = 0;
  275. for (const auto& slotPair : m_outputDataSlots)
  276. {
  277. const auto& slot = slotPair.second;
  278. AZ_Assert(slot->GetSlotDirection() == GraphModel::SlotDirection::Output, "Slots in this container must be output slots.");
  279. for (const auto& connection : slot->GetConnections())
  280. {
  281. AZ_Assert(connection->GetSourceNode().get() == this, "This should always be the source node on an output connection.");
  282. AZ_Assert(connection->GetTargetNode().get() != this, "This should never be the target node on an output connection.");
  283. m_maxOutputDepth = AZStd::max(m_maxOutputDepth, connection->GetTargetNode()->GetMaxOutputDepth() + 1);
  284. }
  285. }
  286. }
  287. return m_maxOutputDepth;
  288. }
  289. bool Node::HasSlots() const
  290. {
  291. return !m_allSlots.empty();
  292. }
  293. bool Node::HasInputSlots() const
  294. {
  295. return !m_inputDataSlots.empty() || !m_inputEventSlots.empty();
  296. }
  297. bool Node::HasOutputSlots() const
  298. {
  299. return !m_outputDataSlots.empty() || !m_outputEventSlots.empty();
  300. }
  301. bool Node::HasConnections() const
  302. {
  303. return AZStd::any_of(m_allSlots.begin(), m_allSlots.end(), [](const auto& slotPair) {
  304. const auto& slot = slotPair.second;
  305. return !slot->GetConnections().empty();
  306. });
  307. }
  308. bool Node::HasInputConnections() const
  309. {
  310. return AZStd::any_of(m_inputDataSlots.begin(), m_inputDataSlots.end(), [](const auto& slotPair) {
  311. const auto& slot = slotPair.second;
  312. AZ_Assert(slot->GetSlotDirection() == GraphModel::SlotDirection::Input, "Slots in this container must be input slots.");
  313. return !slot->GetConnections().empty();
  314. });
  315. }
  316. bool Node::HasOutputConnections() const
  317. {
  318. return AZStd::any_of(m_outputDataSlots.begin(), m_outputDataSlots.end(), [](const auto& slotPair) {
  319. const auto& slot = slotPair.second;
  320. AZ_Assert(slot->GetSlotDirection() == GraphModel::SlotDirection::Output, "Slots in this container must be output slots.");
  321. return !slot->GetConnections().empty();
  322. });
  323. }
  324. bool Node::HasInputConnectionFromNode(ConstNodePtr node) const
  325. {
  326. return AZStd::any_of(m_inputDataSlots.begin(), m_inputDataSlots.end(), [&](const auto& slotPair) {
  327. const auto& slot = slotPair.second;
  328. AZ_Assert(slot->GetSlotDirection() == GraphModel::SlotDirection::Input, "Slots in this container must be input slots.");
  329. const auto& connections = slot->GetConnections();
  330. return AZStd::any_of(connections.begin(), connections.end(), [&](const auto& connection) {
  331. AZ_Assert(connection->GetSourceNode().get() != this, "This should never be the source node on an input connection.");
  332. AZ_Assert(connection->GetTargetNode().get() == this, "This should always be the target node on an input connection.");
  333. return connection->GetSourceNode() == node || connection->GetSourceNode()->HasInputConnectionFromNode(node);
  334. });
  335. });
  336. }
  337. bool Node::HasOutputConnectionToNode(ConstNodePtr node) const
  338. {
  339. return AZStd::any_of(m_outputDataSlots.begin(), m_outputDataSlots.end(), [&](const auto& slotPair) {
  340. const auto& slot = slotPair.second;
  341. AZ_Assert(slot->GetSlotDirection() == GraphModel::SlotDirection::Output, "Slots in this container must be output slots.");
  342. const auto& connections = slot->GetConnections();
  343. return AZStd::any_of(connections.begin(), connections.end(), [&](const auto& connection) {
  344. AZ_Assert(connection->GetSourceNode().get() == this, "This should always be the source node on an output connection.");
  345. AZ_Assert(connection->GetTargetNode().get() != this, "This should never be the target node on an output connection.");
  346. return connection->GetTargetNode() == node || connection->GetTargetNode()->HasOutputConnectionToNode(node);
  347. });
  348. });
  349. }
  350. bool Node::Contains(ConstSlotPtr slot) const
  351. {
  352. const auto slotItr = slot ? m_allSlots.find(slot->GetSlotId()) : m_allSlots.end();
  353. return slotItr != m_allSlots.end() && slotItr->second == slot;
  354. }
  355. const Node::SlotDefinitionList& Node::GetSlotDefinitions() const
  356. {
  357. return m_allSlotDefinitions;
  358. }
  359. const Node::SlotMap& Node::GetSlots()
  360. {
  361. return m_allSlots;
  362. }
  363. Node::ConstSlotMap Node::GetSlots() const
  364. {
  365. return ConstSlotMap(m_allSlots.begin(), m_allSlots.end());
  366. }
  367. ConstSlotPtr Node::GetSlot(const SlotId& slotId) const
  368. {
  369. const auto slotItr = m_allSlots.find(slotId);
  370. return slotItr != m_allSlots.end() ? slotItr->second : nullptr;
  371. }
  372. SlotPtr Node::GetSlot(const SlotId& slotId)
  373. {
  374. // Shared const/non-const overload implementation
  375. return AZStd::const_pointer_cast<Slot>(static_cast<const Node*>(this)->GetSlot(slotId));
  376. }
  377. SlotPtr Node::GetSlot(const SlotName& name)
  378. {
  379. SlotId slotId(name);
  380. return GetSlot(slotId);
  381. }
  382. ConstSlotPtr Node::GetSlot(const SlotName& name) const
  383. {
  384. SlotId slotId(name);
  385. return GetSlot(slotId);
  386. }
  387. const Node::ExtendableSlotSet& Node::GetExtendableSlots(const SlotName& name)
  388. {
  389. static Node::ExtendableSlotSet defaultSet;
  390. const auto slotItr = m_extendableSlots.find(name);
  391. return slotItr != m_extendableSlots.end() ? slotItr->second : defaultSet;
  392. }
  393. int Node::GetExtendableSlotCount(const SlotName& name) const
  394. {
  395. const auto slotItr = m_extendableSlots.find(name);
  396. return slotItr != m_extendableSlots.end() ? aznumeric_cast<int>(slotItr->second.size()) : InvalidExtendableSlot;
  397. }
  398. void Node::DeleteSlot(SlotPtr slot)
  399. {
  400. if (CanDeleteSlot(slot))
  401. {
  402. // Remove this slot from our map tracking all slots on the node, as well as the extendable slots
  403. m_allSlots.erase(slot->GetSlotId());
  404. auto extendableSlotsIt = m_extendableSlots.find(slot->GetName());
  405. if (extendableSlotsIt != m_extendableSlots.end())
  406. {
  407. extendableSlotsIt->second.erase(slot);
  408. }
  409. ClearCachedData();
  410. }
  411. }
  412. bool Node::CanDeleteSlot(ConstSlotPtr slot) const
  413. {
  414. // Only extendable slots can be removed
  415. if (slot->SupportsExtendability())
  416. {
  417. // Only allow this slot to be deleted if there are more than the required minimum
  418. const int currentNumSlots = GetExtendableSlotCount(slot->GetName());
  419. return currentNumSlots >= 0 && currentNumSlots > slot->GetMinimumSlots();
  420. }
  421. return false;
  422. }
  423. bool Node::CanExtendSlot(SlotDefinitionPtr slotDefinition) const
  424. {
  425. if (slotDefinition->SupportsExtendability())
  426. {
  427. // Only allow this slot to extended if we haven't reached the maximum
  428. const int currentNumSlots = GetExtendableSlotCount(slotDefinition->GetName());
  429. return currentNumSlots >= 0 && currentNumSlots < slotDefinition->GetMaximumSlots();
  430. }
  431. return false;
  432. }
  433. SlotPtr Node::AddExtendedSlot(const SlotName& slotName)
  434. {
  435. SlotDefinitionPtr slotDefinition;
  436. for (auto definition : m_extendableSlotDefinitions)
  437. {
  438. if (definition->GetName() == slotName)
  439. {
  440. slotDefinition = definition;
  441. break;
  442. }
  443. }
  444. if (!slotDefinition)
  445. {
  446. AZ_Assert(false, "No slot definitions with registered slotName");
  447. return nullptr;
  448. }
  449. if (!CanExtendSlot(slotDefinition))
  450. {
  451. return nullptr;
  452. }
  453. // Find the existing slots (if any) for this definition, so that we can set the subId
  454. // for the newly created slot
  455. auto extendableIt = m_extendableSlots.find(slotName);
  456. AZ_Assert(extendableIt != m_extendableSlots.end(), "Extendable slot definition name should always exist in the mapping.");
  457. int newSubId = 0;
  458. ExtendableSlotSet& currentSlots = extendableIt->second;
  459. if (!currentSlots.empty())
  460. {
  461. newSubId = (*currentSlots.rbegin())->GetSlotId().m_subId + 1;
  462. }
  463. SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition, newSubId);
  464. slot->SetValue(slotDefinition->GetDefaultValue());
  465. m_allSlots.insert(AZStd::make_pair(slot->GetSlotId(), slot));
  466. currentSlots.insert(slot);
  467. ClearCachedData();
  468. return slot;
  469. }
  470. void Node::RegisterSlot(SlotDefinitionPtr slotDefinition, SlotDefinitionList& slotDefinitionList)
  471. {
  472. AssertPointerIsNew(slotDefinition, m_allSlotDefinitions);
  473. AssertPointerIsNew(slotDefinition, m_propertySlotDefinitions);
  474. AssertPointerIsNew(slotDefinition, m_inputDataSlotDefinitions);
  475. AssertPointerIsNew(slotDefinition, m_outputDataSlotDefinitions);
  476. AssertPointerIsNew(slotDefinition, m_extendableSlotDefinitions);
  477. AssertNameIsNew(slotDefinition, m_allSlotDefinitions);
  478. AssertNameIsNew(slotDefinition, m_propertySlotDefinitions);
  479. AssertNameIsNew(slotDefinition, m_inputDataSlotDefinitions);
  480. AssertNameIsNew(slotDefinition, m_outputDataSlotDefinitions);
  481. AssertNameIsNew(slotDefinition, m_extendableSlotDefinitions);
  482. // Only check the target list because we could allow the same display name for an input and an output.
  483. // Only check if DisplayName is not empty, because Name will be used for display in that case.
  484. if (!slotDefinition->GetDisplayName().empty())
  485. {
  486. AssertDisplayNameIsNew(slotDefinition, slotDefinitionList);
  487. }
  488. slotDefinitionList.push_back(slotDefinition);
  489. m_allSlotDefinitions.push_back(slotDefinition);
  490. }
  491. void Node::RegisterSlot(SlotDefinitionPtr slotDefinition)
  492. {
  493. // [GFX TODO] CJS Consider merging SlotDirection and SlotType into a single enum so we can use switch statements.
  494. if (slotDefinition->SupportsExtendability())
  495. {
  496. RegisterSlot(slotDefinition, m_extendableSlotDefinitions);
  497. }
  498. else if (slotDefinition->Is(SlotDirection::Input, SlotType::Data))
  499. {
  500. RegisterSlot(slotDefinition, m_inputDataSlotDefinitions);
  501. }
  502. else if (slotDefinition->Is(SlotDirection::Output, SlotType::Data))
  503. {
  504. RegisterSlot(slotDefinition, m_outputDataSlotDefinitions);
  505. }
  506. else if (slotDefinition->Is(SlotDirection::Input, SlotType::Property))
  507. {
  508. RegisterSlot(slotDefinition, m_propertySlotDefinitions);
  509. }
  510. else if (slotDefinition->Is(SlotDirection::Input, SlotType::Event))
  511. {
  512. RegisterSlot(slotDefinition, m_inputEventSlotDefinitions);
  513. }
  514. else if (slotDefinition->Is(SlotDirection::Output, SlotType::Event))
  515. {
  516. RegisterSlot(slotDefinition, m_outputEventSlotDefinitions);
  517. }
  518. else
  519. {
  520. AZ_Assert(false, "Unsupported slot configuration");
  521. }
  522. }
  523. void Node::AssertPointerIsNew([[maybe_unused]] SlotDefinitionPtr newSlotDefinition, [[maybe_unused]] const SlotDefinitionList& existingSlotDefinitions) const
  524. {
  525. #if defined(AZ_ENABLE_TRACING)
  526. auto slotItr = AZStd::find(existingSlotDefinitions.begin(), existingSlotDefinitions.end(), newSlotDefinition);
  527. AZ_Assert(slotItr == existingSlotDefinitions.end(), "This slot has already been registered");
  528. #endif
  529. }
  530. void Node::AssertNameIsNew([[maybe_unused]] SlotDefinitionPtr newSlotDefinition, [[maybe_unused]] const SlotDefinitionList& existingSlotDefinitions) const
  531. {
  532. #if defined(AZ_ENABLE_TRACING)
  533. auto slotItr = AZStd::find_if(existingSlotDefinitions.begin(), existingSlotDefinitions.end(),
  534. [newSlotDefinition](SlotDefinitionPtr existingSlotDefinition) { return newSlotDefinition->GetName() == existingSlotDefinition->GetName(); });
  535. AZ_Assert(slotItr == existingSlotDefinitions.end(), "Another slot with name [%s] already exists", newSlotDefinition->GetName().c_str());
  536. #endif
  537. }
  538. void Node::AssertDisplayNameIsNew([[maybe_unused]] SlotDefinitionPtr newSlotDefinition, [[maybe_unused]] const SlotDefinitionList& existingSlotDefinitions) const
  539. {
  540. #if defined(AZ_ENABLE_TRACING)
  541. auto slotItr = AZStd::find_if(existingSlotDefinitions.begin(), existingSlotDefinitions.end(),
  542. [newSlotDefinition](SlotDefinitionPtr existingSlotDefinition) { return newSlotDefinition->GetDisplayName() == existingSlotDefinition->GetDisplayName(); });
  543. AZ_Assert(slotItr == existingSlotDefinitions.end(), "Another slot with display name [%s] already exists", newSlotDefinition->GetDisplayName().c_str());
  544. #endif
  545. }
  546. } // namespace GraphModel