/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include AZ_DEFINE_BUDGET(NodePaletteModel); namespace { // Various Helper Methods bool IsDeprecated(const AZ::AttributeArray& attributes) { bool isDeprecated{}; if (auto isDeprecatedAttributePtr = AZ::FindAttribute(AZ::Script::Attributes::Deprecated, attributes)) { AZ::AttributeReader(nullptr, isDeprecatedAttributePtr).Read(isDeprecated); } return isDeprecated; } bool ShouldExcludeFromNodeList(const AZ::Edit::AttributeData* excludeAttributeData, const AZ::Uuid& typeId) { if (excludeAttributeData) { AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List | AZ::Script::Attributes::ExcludeFlags::ListOnly; if (typeId == AzToolsFramework::Components::EditorComponentBase::TYPEINFO_Uuid()) { return true; } return (static_cast(excludeAttributeData->Get(nullptr)) & exclusionFlags) != 0; // warning C4800: 'AZ::u64': forcing value to bool 'true' or 'false' (performance warning) } return false; } bool HasExcludeFromNodeListAttribute(const AZ::SerializeContext* serializeContext, const AZ::Uuid& typeId) { const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId); if (classData && classData->m_editData) { if (auto editorElementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData)) { if (auto excludeAttribute = editorElementData->FindAttribute(AZ::Script::Attributes::ExcludeFrom)) { auto excludeAttributeData = azdynamic_cast*>(excludeAttribute); return excludeAttributeData && ShouldExcludeFromNodeList(excludeAttributeData, typeId); } } } return false; } // Checks for and returns the Category attribute from an AZ::AttributeArray AZStd::string GetCategoryPath(const AZ::AttributeArray& attributes, const AZ::BehaviorContext& behaviorContext) { AZStd::string retVal; AZ::Attribute* categoryAttribute = AZ::FindAttribute(AZ::Script::Attributes::Category, attributes); if (categoryAttribute) { AZ::AttributeReader(nullptr, categoryAttribute).Read(retVal, behaviorContext); } return retVal; } bool IsExplicitOverload(const AZ::BehaviorMethod& method) { return AZ::FindAttribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, method.m_attributes) != nullptr; } void RegisterMethod ( ScriptCanvasEditor::NodePaletteModel& nodePaletteModel , const AZ::BehaviorContext& behaviorContext , const AZStd::string& categoryPath , const AZ::BehaviorClass* behaviorClass , const AZStd::string& name , const AZ::BehaviorMethod& method , ScriptCanvas::PropertyStatus propertyStatus , bool isOverloaded) { AZ_PROFILE_SCOPE(NodePaletteModel, "RegisterMethod"); if (IsDeprecated(method.m_attributes)) { return; } if (behaviorClass && !isOverloaded) { auto excludeMethodAttributeData = azdynamic_cast*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, method.m_attributes)); if (ShouldExcludeFromNodeList(excludeMethodAttributeData, behaviorClass->m_azRtti ? behaviorClass->m_azRtti->GetTypeId() : behaviorClass->m_typeId)) { return; } } const auto isExposableOutcome = ScriptCanvas::IsExposable(method); if (!isExposableOutcome.IsSuccess()) { AZ_Warning("ScriptCanvas", false, "Unable to expose method: %s to ScriptCanvas because: %s", method.m_name.data(), isExposableOutcome.GetError().data()); return; } // If the reflected method returns an AZ::Event, reflect it to the SerializeContext if (AZ::MethodReturnsAzEventByReferenceOrPointer(method)) { const AZ::BehaviorParameter* resultParameter = method.GetResult(); ScriptCanvas::ReflectEventTypeOnDemand(resultParameter->m_typeId, resultParameter->m_name, resultParameter->m_azRtti); } nodePaletteModel.RegisterClassNode(categoryPath, behaviorClass ? behaviorClass->m_name : "", name, &method, &behaviorContext, propertyStatus, isOverloaded); } void RegisterGlobalMethod(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorMethod& behaviorMethod) { AZ_PROFILE_SCOPE(NodePaletteModel, "RegisterGlobalMethod"); const auto isExposableOutcome = ScriptCanvas::IsExposable(behaviorMethod); if (!isExposableOutcome.IsSuccess()) { AZ_Warning("ScriptCanvas", false, "Unable to expose method: %s to ScriptCanvas because: %s", behaviorMethod.m_name.c_str(), isExposableOutcome.GetError().data()); return; } if (!AZ::Internal::IsInScope(behaviorMethod.m_attributes, AZ::Script::Attributes::ScopeFlags::Common)) { return; // skip this method } // If the reflected method returns an AZ::Event, reflect it to the SerializeContext if (AZ::MethodReturnsAzEventByReferenceOrPointer(behaviorMethod)) { const AZ::BehaviorParameter* resultParameter = behaviorMethod.GetResult(); ScriptCanvas::ReflectEventTypeOnDemand(resultParameter->m_typeId, resultParameter->m_name, resultParameter->m_azRtti); } nodePaletteModel.RegisterMethodNode(behaviorContext, behaviorMethod); } //! Retrieve the list of EBuses t hat should not be exposed in the ScriptCanvasEditor Node Palette AZStd::unordered_set GetEBusExcludeSet(const AZ::BehaviorContext& behaviorContext) { AZ_PROFILE_SCOPE(NodePaletteModel, "GetEBusExcludeSet"); // We will skip buses that are ONLY registered on classes that derive from EditorComponentBase, // because they don't have a runtime implementation. Buses such as the TransformComponent which // is implemented by both an EditorComponentBase derived class and a Component derived class // will still appear AZStd::unordered_set skipBuses; AZStd::unordered_set potentialSkipBuses; AZStd::unordered_set nonSkipBuses; for (const auto& classIter : behaviorContext.m_classes) { const AZ::BehaviorClass* behaviorClass = classIter.second; if (IsDeprecated(behaviorClass->m_attributes)) { continue; } // Only bind Behavior Classes marked with the Scope type of Launcher if (!AZ::Internal::IsInScope(behaviorClass->m_attributes, AZ::Script::Attributes::ScopeFlags::Launcher)) { continue; // skip this class } // Check for "ExcludeFrom" attribute for ScriptCanvas auto excludeClassAttributeData = azdynamic_cast*>( AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes)); // We don't want to show any components, since there isn't anything we can do with them // from ScriptCanvas since we use buses to communicate to everything. if (ShouldExcludeFromNodeList(excludeClassAttributeData, behaviorClass->m_azRtti ? behaviorClass->m_azRtti->GetTypeId() : behaviorClass->m_typeId)) { for (const auto& requestBus : behaviorClass->m_requestBuses) { skipBuses.insert(AZ::Crc32(requestBus.c_str())); } continue; } auto baseClass = AZStd::find(behaviorClass->m_baseClasses.begin(), behaviorClass->m_baseClasses.end(), AzToolsFramework::Components::EditorComponentBase::TYPEINFO_Uuid()); if (baseClass != behaviorClass->m_baseClasses.end()) { for (const auto& requestBus : behaviorClass->m_requestBuses) { potentialSkipBuses.insert(AZ::Crc32(requestBus.c_str())); } } // If the Ebus does not inherit from EditorComponentBase then do not skip it else { for (const auto& requestBus : behaviorClass->m_requestBuses) { nonSkipBuses.insert(AZ::Crc32(requestBus.c_str())); } } } // Add buses which are not on the non-skip list to the skipBuses set for (auto potentialSkipBus : potentialSkipBuses) { if (nonSkipBuses.find(potentialSkipBus) == nonSkipBuses.end()) { skipBuses.insert(potentialSkipBus); } } return skipBuses; } //! Register all nodes populated into the ScriptCanvas NodeRegistry for each class derived //! from the ScriptCanvas LibraryDefinition class void PopulateScriptCanvasDerivedNodes(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::SerializeContext& serializeContext) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateScriptCanvasDerivedNodes"); // Get all the types. auto EnumerateLibraryDefintionNodes = [&nodePaletteModel, &serializeContext]( const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool { ScriptCanvasEditor::CategoryInformation categoryInfo; AZStd::string categoryPath = classData->m_editData ? classData->m_editData->m_name : classData->m_name; if (classData->m_editData) { auto editorElementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData); if (editorElementData) { if (auto categoryAttribute = editorElementData->FindAttribute(AZ::Edit::Attributes::Category)) { if (auto categoryAttributeData = azdynamic_cast*>(categoryAttribute)) { categoryPath = categoryAttributeData->Get(nullptr); } } if (auto categoryStyleAttribute = editorElementData->FindAttribute(AZ::Edit::Attributes::CategoryStyle)) { if (auto categoryAttributeData = azdynamic_cast*>(categoryStyleAttribute)) { categoryInfo.m_styleOverride = categoryAttributeData->Get(nullptr); } } if (auto titlePaletteAttribute = editorElementData->FindAttribute(ScriptCanvas::Attributes::Node::TitlePaletteOverride)) { if (auto categoryAttributeData = azdynamic_cast*>(titlePaletteAttribute)) { categoryInfo.m_paletteOverride = categoryAttributeData->Get(nullptr); } } } } nodePaletteModel.RegisterCategoryInformation(categoryPath, categoryInfo); // Children for (auto& node : ScriptCanvas::Library::LibraryDefinition::GetNodes(classData->m_typeId)) { if (HasExcludeFromNodeListAttribute(&serializeContext, node.first)) { continue; } // Pass in the associated class data so we can do more intensive lookups? const AZ::SerializeContext::ClassData* nodeClassData = serializeContext.FindClassData(node.first); if (nodeClassData == nullptr) { continue; } // Skip over some of our more dynamic nodes that we want to populate using different means else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } else { nodePaletteModel.RegisterCustomNode(categoryPath, node.first, node.second, nodeClassData); } } return true; }; const AZ::TypeId& libraryDefTypeId = azrtti_typeid(); serializeContext.EnumerateDerived(EnumerateLibraryDefintionNodes, libraryDefTypeId, libraryDefTypeId); } void PopulateVariablePalette() { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateVariablePalette"); auto dataRegistry = ScriptCanvas::GetDataRegistry(); for (auto& type : dataRegistry->m_creatableTypes) { if (!type.second.m_isTransient) { ScriptCanvasEditor::VariablePaletteRequestBus::Broadcast(&ScriptCanvasEditor::VariablePaletteRequests::RegisterVariableType, type.first); } } } void PopulateBehaviorContextGlobalMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateBehaviorContextGlobalMethods"); // BehaviorMethods are not associated with a class // therefore the Uuid is set to Null const AZ::Uuid behaviorMethodUuid = AZ::Uuid::CreateNull(); for (const auto& [methodName, behaviorMethod] : behaviorContext.m_methods) { // Skip behavior methods that are deprecated if (behaviorMethod == nullptr || IsDeprecated(behaviorMethod->m_attributes)) { continue; } // Check for "ExcludeFrom" attribute for ScriptCanvas auto excludeMethodAttributeData = azrtti_cast*>( AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorMethod->m_attributes)); if (ShouldExcludeFromNodeList(excludeMethodAttributeData, behaviorMethodUuid)) { continue; } RegisterGlobalMethod(nodePaletteModel, behaviorContext, *behaviorMethod); } } //! Iterates over all Properties directly reflected to the BehaviorContext instance //! and registers there Getter/Setter methods to the NodePaletteModel void PopulateBehaviorContextGlobalProperties(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateBehaviorContextGlobalProperties"); const AZ::Uuid behaviorMethodUuid = AZ::Uuid::CreateNull(); for (const auto& [propertyName, behaviorProperty] : behaviorContext.m_properties) { // Skip behavior properties that are deprecated if (behaviorProperty == nullptr || IsDeprecated(behaviorProperty->m_attributes)) { continue; } // Check for "ExcludeFrom" attribute for ScriptCanvas auto excludePropertyAttributeData = azrtti_cast*>( AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorProperty->m_attributes)); if (ShouldExcludeFromNodeList(excludePropertyAttributeData, behaviorMethodUuid)) { continue; } if (behaviorProperty->m_getter && !behaviorProperty->m_setter) { nodePaletteModel.RegisterGlobalConstant(behaviorContext, behaviorProperty , *behaviorProperty->m_getter); } else { if (behaviorProperty->m_getter) { RegisterGlobalMethod(nodePaletteModel, behaviorContext, *behaviorProperty->m_getter); } if (behaviorProperty->m_setter) { RegisterGlobalMethod(nodePaletteModel, behaviorContext, *behaviorProperty->m_setter); } } } } void PopulateBehaviorContextClassMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateBehaviorContextClassMethods"); AZ::SerializeContext* serializeContext{}; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); for (const auto& classIter : behaviorContext.m_classes) { const AZ::BehaviorClass* behaviorClass = classIter.second; if (IsDeprecated(behaviorClass->m_attributes)) { continue; } if (auto excludeFromPointer = AZ::FindAttribute(AZ::ScriptCanvasAttributes::Internal::ImplementedAsNodeGeneric, behaviorClass->m_attributes)) { continue; } if (auto excludeFromPointer = AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes)) { AZ::Script::Attributes::ExcludeFlags excludeFlags{}; AZ::AttributeReader(nullptr, excludeFromPointer).Read(excludeFlags); if ((excludeFlags & (AZ::Script::Attributes::ExcludeFlags::List | AZ::Script::Attributes::ExcludeFlags::ListOnly)) != 0) { continue; } } if (!AZ::Internal::IsInScope(behaviorClass->m_attributes, AZ::Script::Attributes::ScopeFlags::Launcher)) { continue; } // Objects and Object methods { AZStd::string categoryPath; GraphCanvas::TranslationKey key; key << "BehaviorClass" << behaviorClass->m_name.c_str() << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); categoryPath = details.m_category; if (categoryPath.empty()) { categoryPath = GetCategoryPath(behaviorClass->m_attributes, behaviorContext); } auto dataRegistry = ScriptCanvas::GetDataRegistry(); ScriptCanvas::Data::Type type = dataRegistry->m_typeIdTraitMap[ScriptCanvas::Data::eType::BehaviorContextObject].m_dataTraits.GetSCType(behaviorClass->m_typeId); if (type.IsValid()) { if (dataRegistry->m_creatableTypes.contains(type)) { ScriptCanvasEditor::VariablePaletteRequestBus::Broadcast(&ScriptCanvasEditor::VariablePaletteRequests::RegisterVariableType, type); } } AZStd::string classNamePretty(classIter.first); AZ::Attribute* prettyNameAttribute = AZ::FindAttribute(AZ::ScriptCanvasAttributes::PrettyName, behaviorClass->m_attributes); if (prettyNameAttribute) { AZ::AttributeReader(nullptr, prettyNameAttribute).Read(classNamePretty, behaviorContext); } if (categoryPath.empty()) { if (classNamePretty.empty()) { categoryPath = classNamePretty; } else { categoryPath = "Other"; } } categoryPath.append("/"); if (details.m_name.empty()) { categoryPath.append(classNamePretty.c_str()); } else { categoryPath.append(details.m_name.c_str()); } for (auto property : behaviorClass->m_properties) { if (property.second->m_getter) { RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_getter, ScriptCanvas::PropertyStatus::Getter, behaviorClass->IsMethodOverloaded(property.first)); } if (property.second->m_setter) { RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_setter, ScriptCanvas::PropertyStatus::Setter, behaviorClass->IsMethodOverloaded(property.first)); } } for (auto methodIter : behaviorClass->m_methods) { if (!IsExplicitOverload(*methodIter.second)) { // Respect the exclusion flags auto attributeData = azdynamic_cast*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, methodIter.second->m_attributes)); if (ShouldExcludeFromNodeList(attributeData , {})) { continue; } RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, methodIter.first, *methodIter.second, ScriptCanvas::PropertyStatus::None, behaviorClass->IsMethodOverloaded(methodIter.first)); } } } } } void PopulateBehaviorContextOverloadedMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateBehaviorContextOverloadedMethods"); for (const AZ::ExplicitOverloadInfo& explicitOverload : behaviorContext.m_explicitOverloads) { RegisterMethod(nodePaletteModel, behaviorContext, explicitOverload.m_categoryPath, nullptr, explicitOverload.m_name, *explicitOverload.m_overloads.begin()->first, ScriptCanvas::PropertyStatus::None, true); } } void PopulateBehaviorContextEBusHandler(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorEBus& behaviorEbus) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateBehaviorContextEBusHandler"); if (AZ::ScopedBehaviorEBusHandler handler{ behaviorEbus }; handler) { auto excludeEbusAttributeData = azdynamic_cast*>( AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorEbus.m_attributes)); if (ShouldExcludeFromNodeList(excludeEbusAttributeData, handler->RTTI_GetType())) { return; } const AZ::BehaviorEBusHandler::EventArray& events(handler->GetEvents()); if (!events.empty()) { GraphCanvas::TranslationKey key; key << "EBusHandler" << behaviorEbus.m_name.c_str() << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); AZStd::string categoryPath = details.m_category.empty() ? GetCategoryPath(behaviorEbus.m_attributes, behaviorContext) : details.m_category; // Treat the EBusHandler name as a Category key in order to allow multiple buses to be merged into a single Category. { if (!categoryPath.empty()) { categoryPath.append("/"); } else { categoryPath = "Other/"; } if (!details.m_name.empty()) { categoryPath.append(details.m_name.c_str()); } else if (categoryPath.contains("Other")) { // Use the BehaviorEBus name to categorize within the 'Other' category categoryPath.append(behaviorEbus.m_name.c_str()); } } for (const auto& event : events) { nodePaletteModel.RegisterEBusHandlerNodeModelInformation(categoryPath.c_str(), behaviorEbus.m_name, event.m_name, ScriptCanvas::EBusBusId(behaviorEbus.m_name), event); } } } } void PopulateBehaviorContextEBusEventMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorEBus& behaviorEbus) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateBehaviorContextEBusEventMethods"); if (!behaviorEbus.m_events.empty()) { GraphCanvas::TranslationKey key; key << "EBusSender" << behaviorEbus.m_name.c_str() << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); AZStd::string categoryPath = details.m_category.empty() ? GetCategoryPath(behaviorEbus.m_attributes, behaviorContext) : details.m_category; // Parent AZStd::string displayName = details.m_name; // Treat the EBus name as a Category key in order to allow multiple buses to be merged into a single Category. if (!categoryPath.empty()) { categoryPath.append("/"); } else { categoryPath = "Other/"; } if (!details.m_name.empty()) { categoryPath.append(details.m_name.c_str()); } else if (categoryPath.contains("Other")) { // Use the behavior EBus name to categorize within the 'Other' category categoryPath.append(behaviorEbus.m_name.c_str()); } ScriptCanvasEditor::CategoryInformation ebusCategoryInformation; ebusCategoryInformation.m_tooltip = details.m_tooltip; nodePaletteModel.RegisterCategoryInformation(categoryPath, ebusCategoryInformation); for (auto event : behaviorEbus.m_events) { if (IsDeprecated(event.second.m_attributes)) { continue; } auto excludeEventAttributeData = azdynamic_cast*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, event.second.m_attributes)); if (ShouldExcludeFromNodeList(excludeEventAttributeData, AZ::Uuid::CreateNull())) { continue; // skip this event } const bool isOverload{ false }; // overloaded events are not trivially supported nodePaletteModel.RegisterEBusSenderNodeModelInformation(categoryPath, behaviorEbus.m_name, event.first, ScriptCanvas::EBusBusId(behaviorEbus.m_name.c_str()), ScriptCanvas::EBusEventId(event.first.c_str()), event.second, ScriptCanvas::PropertyStatus::None, isOverload); } } } void PopulateBehaviorContextEBuses(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateBehaviorContextEBuses"); AZStd::unordered_set skipBuses = GetEBusExcludeSet(behaviorContext); for (const auto& [ebusName, behaviorEbus] : behaviorContext.m_ebuses) { if (behaviorEbus == nullptr) { continue; } auto skipBusIterator = skipBuses.find(AZ::Crc32(ebusName)); if (skipBusIterator != skipBuses.end()) { continue; } // Skip buses mapped by their deprecated name (usually duplicates) if (ebusName == behaviorEbus->m_deprecatedName) { continue; } // Only bind Behavior Buses marked with the Scope type of Launcher if (!AZ::Internal::IsInScope(behaviorEbus->m_attributes, AZ::Script::Attributes::ScopeFlags::Launcher)) { continue; // skip this bus } if (IsDeprecated(behaviorEbus->m_attributes)) { continue; } auto excludeEbusAttributeData = azdynamic_cast*>( AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorEbus->m_attributes)); if (ShouldExcludeFromNodeList(excludeEbusAttributeData, AZ::Uuid::CreateNull())) { continue; } if (auto runtimeEbusAttributePtr = AZ::FindAttribute(AZ::RuntimeEBusAttribute, behaviorEbus->m_attributes)) { bool isRuntimeEbus = false; AZ::AttributeReader(nullptr, runtimeEbusAttributePtr).Read(isRuntimeEbus); if (isRuntimeEbus) { continue; } } // EBus Handler PopulateBehaviorContextEBusHandler(nodePaletteModel, behaviorContext, *behaviorEbus); // EBus Sender PopulateBehaviorContextEBusEventMethods(nodePaletteModel, behaviorContext, *behaviorEbus); } } // Helper function for populating the node palette model. // Pulled out just to make the tabbing a bit nicer, since it's a huge method. void PopulateNodePaletteModel(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel) { AZ_PROFILE_SCOPE(NodePaletteModel, "PopulateNodePaletteModel"); AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); AZ::BehaviorContext* behaviorContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext); AZ_Assert(serializeContext, "Could not find SerializeContext. Aborting Palette Creation."); AZ_Assert(behaviorContext, "Could not find BehaviorContext. Aborting Palette Creation."); if (serializeContext == nullptr || behaviorContext == nullptr) { return; } // Populates the NodePalette with each ScriptCanvas LibraryDefinition derived class // static InitNodeRegistry() function PopulateScriptCanvasDerivedNodes(nodePaletteModel, *serializeContext); // Populates the VariablePalette with type registered with the ScriptCanvas DataRegistry PopulateVariablePalette(); // Populates the NodePalette with Behavior Class method nodes PopulateBehaviorContextClassMethods(nodePaletteModel, *behaviorContext); // Populates the NodePalette with BehaviorContext methods overloaded on the same name PopulateBehaviorContextOverloadedMethods(nodePaletteModel, *behaviorContext); // Populates the NodePalette with EBus Event method nodes and EBus Event handler nodes PopulateBehaviorContextEBuses(nodePaletteModel, *behaviorContext); // Populates the NodePalette with Methods reflected directly on the BehaviorContext PopulateBehaviorContextGlobalMethods(nodePaletteModel, *behaviorContext); // Populates the NodePalette with Properties reflected directly on the BehaviorContext PopulateBehaviorContextGlobalProperties(nodePaletteModel, *behaviorContext); } } namespace ScriptCanvasEditor { //////////////////////////////// // NodePaletteModelInformation //////////////////////////////// void NodePaletteModelInformation::PopulateTreeItem(GraphCanvas::NodePaletteTreeItem& treeItem) const { if (!m_toolTip.empty()) { treeItem.SetToolTip(m_toolTip.c_str()); } if (!m_styleOverride.empty()) { treeItem.SetStyleOverride(m_styleOverride.c_str()); } if (!m_titlePaletteOverride.empty()) { const bool forceSet = true; treeItem.SetTitlePalette(m_titlePaletteOverride.c_str(), forceSet); } } ///////////////////// // NodePaletteModel ///////////////////// NodePaletteModel::NodePaletteModel() : m_paletteId(AZ::Entity::MakeId()) { UpgradeNotificationsBus::Handler::BusConnect(); } NodePaletteModel::~NodePaletteModel() { UpgradeNotificationsBus::Handler::BusDisconnect(); DisconnectLambdas(); ClearRegistry(); } NodePaletteId NodePaletteModel::GetNotificationId() const { return m_paletteId; } void NodePaletteModel::AssignAssetModel(AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel) { m_assetModel = assetModel; if (m_assetModel) { TraverseTree(); ConnectLambdas(); } } void NodePaletteModel::ConnectLambdas() { { auto connection = QObject::connect(m_assetModel, &QAbstractItemModel::rowsInserted, [this](const QModelIndex& parentIndex, int first, int last) { this->OnRowsInserted(parentIndex, first, last); }); m_lambdaConnections.emplace_back(connection); } { auto connection = QObject::connect(m_assetModel, &QAbstractItemModel::rowsAboutToBeRemoved, [this](const QModelIndex& parentIndex, int first, int last) { this->OnRowsAboutToBeRemoved(parentIndex, first, last); }); m_lambdaConnections.emplace_back(connection); } } void NodePaletteModel::DisconnectLambdas() { for (auto connection : m_lambdaConnections) { QObject::disconnect(connection); } } void NodePaletteModel::RepopulateModel() { AZ_PROFILE_FUNCTION(ScriptCanvas); ClearRegistry(); PopulateNodePaletteModel((*this)); if (m_assetModel) { TraverseTree(); } NodePaletteModelNotificationBus::Event(m_paletteId, &NodePaletteModelNotifications::OnAssetModelRepopulated); } void NodePaletteModel::RegisterCustomNode(AZStd::string_view categoryPath, const AZ::Uuid& uuid, AZStd::string_view name, const AZ::SerializeContext::ClassData* classData) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterCustomNode"); ScriptCanvas::NodeTypeIdentifier nodeIdentifier = ScriptCanvas::NodeUtils::ConstructCustomNodeIdentifier(uuid); auto mapIter = m_registeredNodes.find(nodeIdentifier); if (mapIter == m_registeredNodes.end()) { CustomNodeModelInformation* customNodeInformation = aznew CustomNodeModelInformation(); customNodeInformation->m_nodeIdentifier = nodeIdentifier; customNodeInformation->m_typeId = uuid; customNodeInformation->m_displayName = name; customNodeInformation->m_categoryPath = categoryPath; bool isDeprecated(false); if (classData && classData->m_editData && classData->m_editData->m_name) { GraphCanvas::TranslationKey key; key << "ScriptCanvas::Node" << classData->m_typeId.ToString().c_str() << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); if (details.m_name.empty()) { details.m_name = classData->m_editData->m_name; details.m_tooltip = classData->m_editData->m_description; } customNodeInformation->m_displayName = details.m_name; customNodeInformation->m_toolTip = details.m_tooltip; if (!details.m_category.empty()) { customNodeInformation->m_categoryPath = details.m_category; } if (customNodeInformation->m_displayName.empty()) { customNodeInformation->m_displayName = classData->m_editData->m_name; } auto editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData); if (editorDataElement) { if (auto categoryStyleAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::CategoryStyle)) { if (auto categoryAttributeData = azdynamic_cast*>(categoryStyleAttribute)) { if (categoryAttributeData->Get(nullptr)) { customNodeInformation->m_styleOverride = categoryAttributeData->Get(nullptr); } } } if (auto titlePaletteAttribute = editorDataElement->FindAttribute(ScriptCanvas::Attributes::Node::TitlePaletteOverride)) { if (auto titlePaletteAttributeData = azdynamic_cast*>(titlePaletteAttribute)) { if (titlePaletteAttributeData->Get(nullptr)) { customNodeInformation->m_titlePaletteOverride = titlePaletteAttributeData->Get(nullptr); } } } if (auto deprecatedAttribute = editorDataElement->FindAttribute(AZ::Script::Attributes::Deprecated)) { if (auto deprecatedAttributeData = azdynamic_cast*>(deprecatedAttribute)) { isDeprecated = deprecatedAttributeData->Get(nullptr); } } if (customNodeInformation->m_toolTip.empty() && classData->m_editData->m_description) { customNodeInformation->m_toolTip = classData->m_editData->m_description; } } } if (!isDeprecated) { m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, customNodeInformation)); } else { delete customNodeInformation; } } } void NodePaletteModel::RegisterClassNode ( const AZStd::string& categoryPath , const AZStd::string& methodClass , const AZStd::string& methodName , const AZ::BehaviorMethod* , const AZ::BehaviorContext* , ScriptCanvas::PropertyStatus propertyStatus , bool isOverload) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterClassNode"); ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructMethodOverloadedNodeIdentifier(methodName) : ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(methodClass, methodName, propertyStatus); auto registerIter = m_registeredNodes.find(nodeIdentifier); if (registerIter == m_registeredNodes.end()) { MethodNodeModelInformation* methodModelInformation = aznew MethodNodeModelInformation(); methodModelInformation->m_isOverload = isOverload; methodModelInformation->m_nodeIdentifier = nodeIdentifier; methodModelInformation->m_classMethod = methodClass; methodModelInformation->m_methodName = methodName; methodModelInformation->m_propertyStatus = propertyStatus; methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette"; GraphCanvas::TranslationKey catkey; catkey << "BehaviorClass" << methodClass.c_str() << "details"; GraphCanvas::TranslationRequests::Details catdetails; GraphCanvas::TranslationRequestBus::BroadcastResult(catdetails, &GraphCanvas::TranslationRequests::GetDetails, catkey, catdetails); GraphCanvas::TranslationKey key; AZStd::string context; AZStd::string updatedMethodName; if (propertyStatus != ScriptCanvas::PropertyStatus::None) { updatedMethodName = (propertyStatus == ScriptCanvas::PropertyStatus::Getter) ? "Get" : "Set"; context = (propertyStatus == ScriptCanvas::PropertyStatus::Getter) ? "Getter" : "Setter"; } updatedMethodName += methodName; key << "BehaviorClass" << methodClass << "methods" << updatedMethodName << context << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); methodModelInformation->m_displayName = details.m_name.empty() ? updatedMethodName : details.m_name; methodModelInformation->m_toolTip = details.m_tooltip; methodModelInformation->m_categoryPath = categoryPath; if (methodModelInformation->m_categoryPath.empty()) { methodModelInformation->m_categoryPath = "Other"; } m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, methodModelInformation)); } } void NodePaletteModel::RegisterGlobalConstant(const AZ::BehaviorContext&, const AZ::BehaviorProperty* behaviorProperty, const AZ::BehaviorMethod& behaviorMethod) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterGlobalConstant"); // Construct Node Identifier using the BehaviorMethod name and the ScriptCanvas Method typeid ScriptCanvas::NodeTypeIdentifier nodeIdentifier = ScriptCanvas::NodeUtils::ConstructGlobalMethodNodeIdentifier(behaviorMethod.m_name); // Register the methodModelInformation if not already registered if (auto registerIter = m_registeredNodes.find(nodeIdentifier); registerIter == m_registeredNodes.end()) { auto methodModelInformation = AZStd::make_unique(); methodModelInformation->m_nodeIdentifier = nodeIdentifier; methodModelInformation->m_methodName = behaviorMethod.m_name; methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette"; AZStd::string name = behaviorProperty->m_name; AZ::StringFunc::Replace(name, "::Getter", ""); AZ::StringFunc::Replace(name, "::Setter", ""); GraphCanvas::TranslationKey key; key << "Constant" << name << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); methodModelInformation->m_displayName = details.m_name; methodModelInformation->m_toolTip = details.m_tooltip; methodModelInformation->m_categoryPath = details.m_category; if (methodModelInformation->m_categoryPath.empty()) { methodModelInformation->m_categoryPath = "Constants"; } m_registeredNodes.emplace(nodeIdentifier, methodModelInformation.release()); } } void NodePaletteModel::RegisterMethodNode(const AZ::BehaviorContext&, const AZ::BehaviorMethod& behaviorMethod) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterMethodNode"); // Construct Node Identifier using the BehaviorMethod name and the ScriptCanvas Method typeid ScriptCanvas::NodeTypeIdentifier nodeIdentifier = ScriptCanvas::NodeUtils::ConstructGlobalMethodNodeIdentifier(behaviorMethod.m_name); // Register the methodModelInformation if not already registered if (auto registerIter = m_registeredNodes.find(nodeIdentifier); registerIter == m_registeredNodes.end()) { auto methodModelInformation = AZStd::make_unique(); methodModelInformation->m_methodName = behaviorMethod.m_name; methodModelInformation->m_nodeIdentifier = nodeIdentifier; methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette"; GraphCanvas::TranslationKey key; key << "BehaviorMethod" << behaviorMethod.m_name.c_str() << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); methodModelInformation->m_displayName = details.m_name.empty() ? behaviorMethod.m_name : details.m_name; methodModelInformation->m_toolTip = details.m_tooltip.empty() ? "" : details.m_tooltip; methodModelInformation->m_categoryPath = details.m_category.empty() ? "Behavior Context: Global Methods" : details.m_category; m_registeredNodes.emplace(nodeIdentifier, methodModelInformation.release()); } } void NodePaletteModel::RegisterEBusHandlerNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const AZ::BehaviorEBusHandler::BusForwarderEvent& forwardEvent) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterEBusHandlerNodeModelInformation"); ScriptCanvas::NodeTypeIdentifier nodeIdentifier = ScriptCanvas::NodeUtils::ConstructEBusEventReceiverIdentifier(busId, forwardEvent.m_eventId); auto nodeIter = m_registeredNodes.find(nodeIdentifier); if (nodeIter == m_registeredNodes.end()) { EBusHandlerNodeModelInformation* handlerInformation = aznew EBusHandlerNodeModelInformation(); handlerInformation->m_titlePaletteOverride = "HandlerNodeTitlePalette"; handlerInformation->m_categoryPath = categoryPath; handlerInformation->m_nodeIdentifier = nodeIdentifier; handlerInformation->m_busName = busName; handlerInformation->m_eventName = eventName; handlerInformation->m_busId = busId; handlerInformation->m_eventId = forwardEvent.m_eventId; GraphCanvas::TranslationKey key; key << "EBusHandler" << busName << "methods" << eventName << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); handlerInformation->m_displayName = details.m_name.empty() ? eventName : details.m_name.c_str(); handlerInformation->m_toolTip = details.m_tooltip.empty() ? "" : details.m_tooltip; m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, handlerInformation)); } } void NodePaletteModel::RegisterEBusSenderNodeModelInformation ( AZStd::string_view categoryPath , AZStd::string_view busName , AZStd::string_view eventName , const ScriptCanvas::EBusBusId& busId , const ScriptCanvas::EBusEventId& eventId , const AZ::BehaviorEBusEventSender& sender , ScriptCanvas::PropertyStatus propertyStatus , bool isOverload) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterEBusSenderNodeModelInformation"); ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructEBusEventSenderOverloadedIdentifier(busId, eventId) : ScriptCanvas::NodeUtils::ConstructEBusEventSenderIdentifier(busId, eventId); auto nodeIter = m_registeredNodes.find(nodeIdentifier); if (nodeIter == m_registeredNodes.end()) { EBusSenderNodeModelInformation* senderInformation = aznew EBusSenderNodeModelInformation(); senderInformation->m_isOverload = isOverload; senderInformation->m_titlePaletteOverride = "MethodNodeTitlePalette"; senderInformation->m_categoryPath = categoryPath; senderInformation->m_nodeIdentifier = nodeIdentifier; senderInformation->m_propertyStatus = propertyStatus; senderInformation->m_busName = busName; senderInformation->m_eventName = eventName; senderInformation->m_busId = busId; senderInformation->m_eventId = eventId; GraphCanvas::TranslationKey key; key << "EBusSender" << busName << "methods" << eventName << "details"; GraphCanvas::TranslationRequests::Details details; GraphCanvas::TranslationRequestBus::BroadcastResult(details, &GraphCanvas::TranslationRequests::GetDetails, key, details); senderInformation->m_displayName = details.m_name.empty() ? eventName : details.m_name.c_str(); senderInformation->m_toolTip = details.m_tooltip.empty() ? "" : details.m_tooltip; auto safeRegister = [](AZ::BehaviorMethod* method) { if (method && AZ::MethodReturnsAzEventByReferenceOrPointer(*method)) { const AZ::BehaviorParameter* resultParameter = method->GetResult(); ScriptCanvas::ReflectEventTypeOnDemand(resultParameter->m_typeId, resultParameter->m_name, resultParameter->m_azRtti); } }; safeRegister(sender.m_event); safeRegister(sender.m_broadcast); m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, senderInformation)); } } AZStd::vector NodePaletteModel::RegisterScriptEvent(ScriptEvents::ScriptEventsAsset* scriptEventAsset) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterScriptEvent"); const ScriptEvents::ScriptEvent& scriptEvent = scriptEventAsset->m_definition; ScriptCanvas::EBusBusId busId = scriptEventAsset->GetBusId(); AZStd::string category = scriptEvent.GetCategory(); auto methods = scriptEvent.GetMethods(); AZStd::vector identifiers; // Each event has a handler and a receiver identifiers.reserve(methods.size() * 2); for (const auto& method : methods) { ScriptCanvas::EBusEventId eventId = method.GetEventId(); ScriptCanvas::NodeTypeIdentifier senderIdentifier = ScriptCanvas::NodeUtils::ConstructSendScriptEventIdentifier(busId, eventId); ScriptCanvas::NodeTypeIdentifier receiverIdentifier = ScriptCanvas::NodeUtils::ConstructScriptEventReceiverIdentifier(busId, eventId); ScriptEventHandlerNodeModelInformation* handlerInformation = aznew ScriptEventHandlerNodeModelInformation(); handlerInformation->m_titlePaletteOverride = "HandlerNodeTitlePalette"; handlerInformation->m_busName = scriptEvent.GetName(); handlerInformation->m_eventName = method.GetName(); handlerInformation->m_displayName = method.GetName(); handlerInformation->m_categoryPath = scriptEvent.GetCategory(); handlerInformation->m_busId = busId; handlerInformation->m_eventId = eventId; handlerInformation->m_nodeIdentifier = receiverIdentifier; m_registeredNodes.emplace(AZStd::make_pair(receiverIdentifier, handlerInformation)); ScriptEventSenderNodeModelInformation* senderInformation = aznew ScriptEventSenderNodeModelInformation(); senderInformation->m_titlePaletteOverride = "MethodNodeTitlePalette"; senderInformation->m_busName = scriptEvent.GetName(); senderInformation->m_eventName = method.GetName(); senderInformation->m_displayName = method.GetName(); senderInformation->m_categoryPath = scriptEvent.GetCategory(); senderInformation->m_busId = busId; senderInformation->m_eventId = eventId; senderInformation->m_nodeIdentifier = senderIdentifier; m_registeredNodes.emplace(AZStd::make_pair(senderIdentifier, senderInformation)); m_assetMapping.insert(AZStd::make_pair(scriptEventAsset->GetId(), senderIdentifier)); m_assetMapping.insert(AZStd::make_pair(scriptEventAsset->GetId(), receiverIdentifier)); identifiers.emplace_back(senderIdentifier); identifiers.emplace_back(receiverIdentifier); } return identifiers; } void NodePaletteModel::RegisterCategoryInformation(const AZStd::string& category, const CategoryInformation& categoryInformation) { auto categoryIter = m_categoryInformation.find(category); if (categoryIter == m_categoryInformation.end()) { m_categoryInformation[category] = categoryInformation; } } const CategoryInformation* NodePaletteModel::FindCategoryInformation(const AZStd::string& categoryStyle) const { auto categoryIter = m_categoryInformation.find(categoryStyle); if (categoryIter != m_categoryInformation.end()) { return &(categoryIter->second); } return nullptr; } const CategoryInformation* NodePaletteModel::FindBestCategoryInformation(AZStd::string_view categoryView) const { const CategoryInformation* bestCategoryFit = nullptr; auto categoryIter = m_categoryInformation.find(categoryView); size_t offset = AZStd::string_view::npos; AZStd::string_view categoryTrail = categoryView; while (categoryIter == m_categoryInformation.end() && !categoryTrail.empty()) { size_t seperator = categoryTrail.find_last_of('/', offset); if (seperator == AZStd::string_view::npos) { categoryTrail = {}; } else { categoryTrail = categoryTrail.substr(0, seperator - 1); categoryIter = m_categoryInformation.find(categoryTrail); } } if (categoryIter != m_categoryInformation.end()) { bestCategoryFit = &(categoryIter->second); } return bestCategoryFit; } const NodePaletteModelInformation* NodePaletteModel::FindNodePaletteInformation(const ScriptCanvas::NodeTypeIdentifier& nodeType) const { auto registryIter = m_registeredNodes.find(nodeType); if (registryIter != m_registeredNodes.end()) { return registryIter->second; } return nullptr; } const NodePaletteModel::NodePaletteRegistry& NodePaletteModel::GetNodeRegistry() const { return m_registeredNodes; } GraphCanvas::GraphCanvasTreeItem* NodePaletteModel::CreateCategoryNode(AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvas::GraphCanvasTreeItem* parentItem) const { GraphCanvas::NodePaletteTreeItem* treeItem = parentItem->CreateChildNode(categoryName, ScriptCanvasEditor::AssetEditorId); const CategoryInformation* categoryInformation = FindCategoryInformation(categoryPath); if (categoryInformation) { if (!categoryInformation->m_tooltip.empty()) { treeItem->SetToolTip(categoryInformation->m_tooltip.c_str()); } if (!categoryInformation->m_paletteOverride.empty()) { treeItem->SetTitlePalette(categoryInformation->m_paletteOverride.c_str()); } if (!categoryInformation->m_styleOverride.empty()) { treeItem->SetStyleOverride(categoryInformation->m_styleOverride.c_str()); } } return treeItem; } void NodePaletteModel::OnRowsInserted(const QModelIndex& parentIndex, int first, int last) { for (int i = first; i <= last; ++i) { QModelIndex modelIndex = m_assetModel->index(i, 0, parentIndex); QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex); AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast(sourceIndex.internalPointer()); auto nodeTypeIdentifiers = ProcessAsset(entry); for (auto nodeTypeIdentifier : nodeTypeIdentifiers) { auto nodeIter = m_registeredNodes.find(nodeTypeIdentifier); if (nodeIter != m_registeredNodes.end()) { NodePaletteModelNotificationBus::Event(m_paletteId, &NodePaletteModelNotifications::OnAssetNodeAdded, nodeIter->second); } } } } void NodePaletteModel::OnRowsAboutToBeRemoved(const QModelIndex& parentIndex, int first, int last) { for (int i = first; i <= last; ++i) { QModelIndex modelIndex = m_assetModel->index(first, 0, parentIndex); QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex); const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast(sourceIndex.internalPointer()); if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product) { const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast(entry); if (productEntry) { RemoveAsset(productEntry->GetAssetId()); } } } } void NodePaletteModel::TraverseTree(QModelIndex index) { QModelIndex sourceIndex = m_assetModel->mapToSource(index); AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast(sourceIndex.internalPointer()); ProcessAsset(entry); int rowCount = m_assetModel->rowCount(index); for (int i = 0; i < rowCount; ++i) { QModelIndex nextIndex = m_assetModel->index(i, 0, index); TraverseTree(nextIndex); } } AZStd::vector NodePaletteModel::ProcessAsset(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) { AZ_PROFILE_SCOPE(NodePaletteModel, "NodePaletteModel::RegisterScriptEvent"); AZStd::lock_guard myLocker(m_mutex); if (entry) { if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product) { const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast(entry); if (productEntry->GetAssetType() == azrtti_typeid()) { const AZ::Data::AssetId& assetId = productEntry->GetAssetId(); auto busAsset = AZ::Data::AssetManager::Instance().GetAsset(assetId, azrtti_typeid(), AZ::Data::AssetLoadBehavior::PreLoad); busAsset.BlockUntilLoadComplete(); if (busAsset.IsReady()) { ScriptEvents::ScriptEventsAsset* data = busAsset.GetAs(); return RegisterScriptEvent(data); } else { AZ_TracePrintf("NodePaletteModel", "Could not refresh node palette properly, the asset failed to load correctly."); } } } } return AZStd::vector(); } void NodePaletteModel::RemoveAsset(const AZ::Data::AssetId& assetId) { auto mapRange = m_assetMapping.equal_range(assetId); for (auto rangeIter = mapRange.first; rangeIter != mapRange.second; ++rangeIter) { auto nodeIter = m_registeredNodes.find(rangeIter->second); if (nodeIter != m_registeredNodes.end()) { NodePaletteModelNotificationBus::Event(m_paletteId, &NodePaletteModelNotifications::OnAssetNodeRemoved, nodeIter->second); delete nodeIter->second; m_registeredNodes.erase(nodeIter); } } } void NodePaletteModel::ClearRegistry() { for (auto& mapPair : m_registeredNodes) { delete mapPair.second; } m_registeredNodes.clear(); m_categoryInformation.clear(); } }