DynamicNodeManager.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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 <Atom/RPI.Edit/Common/AssetUtils.h>
  9. #include <Atom/RPI.Edit/Common/JsonUtils.h>
  10. #include <Atom/RPI.Reflect/System/AnyAsset.h>
  11. #include <AtomToolsFramework/Graph/DynamicNode/DynamicNode.h>
  12. #include <AtomToolsFramework/Graph/DynamicNode/DynamicNodeManager.h>
  13. #include <AtomToolsFramework/Graph/DynamicNode/DynamicNodePaletteItem.h>
  14. #include <AtomToolsFramework/Util/Util.h>
  15. #include <AzCore/IO/FileIO.h>
  16. #include <AzCore/IO/SystemFile.h>
  17. #include <AzCore/Serialization/Json/JsonUtils.h>
  18. #include <AzCore/Serialization/Utils.h>
  19. #include <AzCore/std/algorithm.h>
  20. #include <AzCore/std/sort.h>
  21. #include <AzCore/std/string/regex.h>
  22. #include <AzFramework/StringFunc/StringFunc.h>
  23. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  24. #include <GraphCanvas/Widgets/NodePalette/TreeItems/IconDecoratedNodePaletteTreeItem.h>
  25. #include <GraphModel/Integration/NodePalette/GraphCanvasNodePaletteItems.h>
  26. namespace AtomToolsFramework
  27. {
  28. void DynamicNodeManager::Reflect(AZ::ReflectContext* context)
  29. {
  30. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  31. {
  32. serializeContext->Class<DynamicNodeManager>()
  33. ->Version(0)
  34. ;
  35. }
  36. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  37. {
  38. behaviorContext->EBus<DynamicNodeManagerRequestBus>("DynamicNodeManagerRequestBus")
  39. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  40. ->Attribute(AZ::Script::Attributes::Category, "Editor")
  41. ->Attribute(AZ::Script::Attributes::Module, "atomtools")
  42. ->Event("LoadConfigFiles", &DynamicNodeManagerRequestBus::Events::LoadConfigFiles)
  43. ->Event("RegisterConfig", &DynamicNodeManagerRequestBus::Events::RegisterConfig)
  44. ->Event("GetConfigById", &DynamicNodeManagerRequestBus::Events::GetConfigById)
  45. ->Event("Clear", &DynamicNodeManagerRequestBus::Events::Clear)
  46. ->Event("CreateNodeById", &DynamicNodeManagerRequestBus::Events::CreateNodeById)
  47. ->Event("CreateNodeByName", &DynamicNodeManagerRequestBus::Events::CreateNodeByName)
  48. ;
  49. }
  50. }
  51. DynamicNodeManager::DynamicNodeManager(const AZ::Crc32& toolId)
  52. : m_toolId(toolId)
  53. {
  54. DynamicNodeManagerRequestBus::Handler::BusConnect(m_toolId);
  55. }
  56. DynamicNodeManager::~DynamicNodeManager()
  57. {
  58. DynamicNodeManagerRequestBus::Handler::BusDisconnect();
  59. }
  60. void DynamicNodeManager::RegisterDataTypes(const GraphModel::DataTypeList& dataTypes)
  61. {
  62. m_registeredDataTypes.insert(m_registeredDataTypes.end(), dataTypes.begin(), dataTypes.end());
  63. }
  64. GraphModel::DataTypeList DynamicNodeManager::GetRegisteredDataTypes()
  65. {
  66. return m_registeredDataTypes;
  67. }
  68. void DynamicNodeManager::LoadConfigFiles(const AZStd::string& extension)
  69. {
  70. AZ_TracePrintf("DynamicNodeManager", "Load %s config files started.", extension.c_str());
  71. // Load and register all discovered dynamic node configuration
  72. for (const auto& configPath : GetPathsInSourceFoldersMatchingExtension(extension))
  73. {
  74. DynamicNodeConfig config;
  75. if (config.Load(configPath))
  76. {
  77. // Automatically fill missing display names and descriptions if they were not specified in the config file
  78. config.AutoFillMissingData();
  79. AZ_TracePrintf_IfTrue(
  80. "DynamicNodeManager", IsNodeConfigLoggingEnabled(), "DynamicNodeConfig \"%s\" loaded.\n", configPath.c_str());
  81. RegisterConfig(config);
  82. }
  83. }
  84. AZ_TracePrintf("DynamicNodeManager", "Load %s config files finished.", extension.c_str());
  85. }
  86. bool DynamicNodeManager::RegisterConfig(const DynamicNodeConfig& config)
  87. {
  88. AZ_TracePrintf_IfTrue(
  89. "DynamicNodeManager", IsNodeConfigLoggingEnabled(), "DynamicNodeConfig \"%s\" registering.\n", config.m_id.ToFixedString().c_str());
  90. if (!ValidateSlotConfigVec(config.m_id, config.m_inputSlots) ||
  91. !ValidateSlotConfigVec(config.m_id, config.m_outputSlots) ||
  92. !ValidateSlotConfigVec(config.m_id, config.m_propertySlots))
  93. {
  94. AZ_Error("DynamicNodeManager", false, "DynamicNodeConfig \"%s\" could not be registered.", config.m_id.ToFixedString().c_str());
  95. return false;
  96. }
  97. if (m_nodeConfigMap.find(config.m_id) != m_nodeConfigMap.end())
  98. {
  99. AZ_Error("DynamicNodeManager", false, "DynamicNodeConfig with id \"%s\" is already registered.", config.m_id.ToFixedString().c_str());
  100. return false;
  101. }
  102. m_nodeConfigMap[config.m_id] = config;
  103. AZ_TracePrintf_IfTrue(
  104. "DynamicNodeManager", IsNodeConfigLoggingEnabled(), "DynamicNodeConfig \"%s\" registered.\n", config.m_id.ToFixedString().c_str());
  105. return true;
  106. }
  107. DynamicNodeConfig DynamicNodeManager::GetConfigById(const AZ::Uuid& configId) const
  108. {
  109. auto configItr = m_nodeConfigMap.find(configId);
  110. if (configItr != m_nodeConfigMap.end())
  111. {
  112. return configItr->second;
  113. }
  114. AZ_Error("DynamicNodeManager", false, "DynamicNodeConfig \"%s\" could not be found.", configId.ToFixedString().c_str());
  115. return DynamicNodeConfig();
  116. }
  117. void DynamicNodeManager::Clear()
  118. {
  119. m_nodeConfigMap.clear();
  120. }
  121. GraphCanvas::GraphCanvasTreeItem* DynamicNodeManager::CreateNodePaletteTree() const
  122. {
  123. auto rootItem = aznew GraphCanvas::NodePaletteTreeItem("Root", m_toolId);
  124. AZStd::unordered_map<AZStd::string, GraphCanvas::GraphCanvasTreeItem*> categoryMap;
  125. categoryMap[""] = rootItem;
  126. // Create the node palette tree by traversing the configuration container, registering any unique categories and child items
  127. for (const auto& configPair : m_nodeConfigMap)
  128. {
  129. const auto& config = configPair.second;
  130. auto categoryItr = categoryMap.find(config.m_category);
  131. if (categoryItr == categoryMap.end())
  132. {
  133. // Creating a new node palette tree item category and setting the title palette based on the 1st dynamic node config added
  134. auto categoryTreeItem =
  135. rootItem->CreateChildNode<GraphCanvas::IconDecoratedNodePaletteTreeItem>(config.m_category, m_toolId);
  136. categoryTreeItem->SetTitlePalette(config.m_titlePaletteName);
  137. categoryItr = categoryMap.emplace(config.m_category, categoryTreeItem).first;
  138. }
  139. categoryItr->second->CreateChildNode<DynamicNodePaletteItem>(m_toolId, config);
  140. }
  141. GraphModelIntegration::AddCommonNodePaletteUtilities(rootItem, m_toolId);
  142. return rootItem;
  143. }
  144. GraphModel::NodePtr DynamicNodeManager::CreateNodeById(GraphModel::GraphPtr graph, const AZ::Uuid& configId)
  145. {
  146. const auto configItr = m_nodeConfigMap.find(configId);
  147. if (configItr != m_nodeConfigMap.end())
  148. {
  149. return AZStd::make_shared<DynamicNode>(graph, m_toolId, configItr->first);
  150. }
  151. return GraphModel::NodePtr();
  152. }
  153. GraphModel::NodePtr DynamicNodeManager::CreateNodeByName(GraphModel::GraphPtr graph, const AZStd::string& name)
  154. {
  155. const auto configItr = AZStd::find_if(
  156. m_nodeConfigMap.begin(),
  157. m_nodeConfigMap.end(),
  158. [&name](const auto& configPair)
  159. {
  160. return AZ::StringFunc::Equal(name, configPair.second.m_title);
  161. });
  162. if (configItr != m_nodeConfigMap.end())
  163. {
  164. return AZStd::make_shared<DynamicNode>(graph, m_toolId, configItr->first);
  165. }
  166. return GraphModel::NodePtr();
  167. }
  168. void DynamicNodeManager::RegisterEditDataForSetting(const AZStd::string& settingName, const AZ::Edit::ElementData& editData)
  169. {
  170. m_editDataForSettingName[settingName] = editData;
  171. }
  172. AZStd::vector<AZStd::string> DynamicNodeManager::GetRegisteredEditDataSettingNames() const
  173. {
  174. AZStd::vector<AZStd::string> names;
  175. names.reserve(m_editDataForSettingName.size());
  176. for (const auto& editDataPair : m_editDataForSettingName)
  177. {
  178. names.push_back(editDataPair.first);
  179. }
  180. return names;
  181. }
  182. const AZ::Edit::ElementData* DynamicNodeManager::GetEditDataForSetting(const AZStd::string& settingName) const
  183. {
  184. for (const auto& editDataPair : m_editDataForSettingName)
  185. {
  186. if (AZ::StringFunc::Equal(editDataPair.first, settingName))
  187. {
  188. return &editDataPair.second;
  189. }
  190. }
  191. return nullptr;
  192. }
  193. bool DynamicNodeManager::ValidateSlotConfig(
  194. [[maybe_unused]] const AZ::Uuid& configId, const DynamicNodeSlotConfig& slotConfig) const
  195. {
  196. if (slotConfig.m_supportedDataTypeRegex.empty())
  197. {
  198. AZ_Error(
  199. "DynamicNodeManager",
  200. false,
  201. "DynamicNodeConfig \"%s\" could not be validated because DynamicNodeSlotConfig \"%s\" has no supported data types.",
  202. configId.ToFixedString().c_str(),
  203. slotConfig.m_displayName.c_str());
  204. return false;
  205. }
  206. AZStd::regex supportedDataTypeRegex(slotConfig.m_supportedDataTypeRegex, AZStd::regex::flag_type::icase);
  207. if (!AZStd::any_of(
  208. m_registeredDataTypes.begin(),
  209. m_registeredDataTypes.end(),
  210. [&](const auto& dataType)
  211. {
  212. return AZStd::regex_match(dataType->GetCppName(), supportedDataTypeRegex) ||
  213. AZStd::regex_match(dataType->GetDisplayName(), supportedDataTypeRegex);
  214. }))
  215. {
  216. AZ_Error(
  217. "DynamicNodeManager",
  218. false,
  219. "DynamicNodeConfig \"%s\" could not be validated because DynamicNodeSlotConfig \"%s\" does not match any registered data type."
  220. "types.",
  221. configId.ToFixedString().c_str(),
  222. slotConfig.m_displayName.c_str());
  223. return false;
  224. }
  225. return true;
  226. }
  227. bool DynamicNodeManager::ValidateSlotConfigVec(
  228. [[maybe_unused]] const AZ::Uuid& configId, const AZStd::vector<DynamicNodeSlotConfig>& slotConfigVec) const
  229. {
  230. for (const auto& slotConfig : slotConfigVec)
  231. {
  232. if (!ValidateSlotConfig(configId, slotConfig))
  233. {
  234. AZ_Error(
  235. "DynamicNodeManager",
  236. false,
  237. "DynamicNodeConfig \"%s\" could not be validated because DynamicNodeSlotConfig \"%s\" could not be validated.",
  238. configId.ToFixedString().c_str(),
  239. slotConfig.m_displayName.c_str());
  240. return false;
  241. }
  242. }
  243. return true;
  244. }
  245. bool DynamicNodeManager::IsNodeConfigLoggingEnabled() const
  246. {
  247. return GetSettingsValue("/O3DE/AtomToolsFramework/DynamicNodeManager/NodeConfigLoggingEnabled", false);
  248. }
  249. } // namespace AtomToolsFramework