// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "MetasoundBasicNode.h" #include "MetasoundFrontend.h" #include "MetasoundFrontendDocument.h" #include "MetasoundFrontendNodeClassRegistry.h" #include "MetasoundLog.h" #include "MetasoundFrontendModuleRegistrationMacros.h" #include "MetasoundNodeInterface.h" #include "Traits/MetasoundNodeConstructorTraits.h" #include "Traits/MetasoundNodeStaticMemberTraits.h" #define UE_API METASOUNDFRONTEND_API // In UE 5.6, registered node are expected to support the constructor signature Constructor(FNodeData, TSharedRef) // Because there are many existing nodes, it may take time to update them. For convenience, the deprecations related to this change are // configurable via a preprocessor macro so that the deprecation warnings do not drown out other compiler errors and warnings. #ifndef UE_METASOUND_DISABLE_5_6_NODE_REGISTRATION_DEPRECATION_WARNINGS #define UE_METASOUND_DISABLE_5_6_NODE_REGISTRATION_DEPRECATION_WARNINGS (0) #endif namespace Metasound::Frontend { namespace NodeRegistrationPrivate { // Utilize base class to reduce template bloat in TNodeRegistryEntry class FNodeRegistryEntryBase : public INodeClassRegistryEntry { public: UE_API FNodeRegistryEntryBase(const Metasound::FNodeClassMetadata& InMetadata, const FModuleInfo& InOwningModuleInfo); virtual ~FNodeRegistryEntryBase() = default; UE_API virtual const FNodeClassInfo& GetClassInfo() const override; UE_API virtual const FMetasoundFrontendClass& GetFrontendClass() const override; // Unhide CreateNode overloads which exist on INodeClassRegistryEntry using INodeClassRegistryEntry::CreateNode; UE_DEPRECATED(5.6, "Node classes should be constructed with FNodeData") UE_API virtual TUniquePtr CreateNode(const FNodeInitData& InInitData) const override; UE_API virtual const TSet* GetImplementedInterfaces() const override; UE_API virtual FVertexInterface GetDefaultVertexInterface() const override; UE_API virtual TInstancedStruct CreateFrontendNodeConfiguration() const override; UE_API virtual bool IsCompatibleNodeConfiguration(TConstStructView InNodeConfiguration) const override; #if WITH_EDITORONLY_DATA UE_API virtual FName GetPluginName() const override; UE_API virtual FName GetModuleName() const override; #endif protected: UE_API TSharedRef GetNodeClassMetadata() const; private: TSharedRef ClassMetadata; FNodeClassInfo ClassInfo; FMetasoundFrontendClass FrontendClass; #if WITH_EDITORONLY_DATA FName PluginName; FName ModuleName; #endif }; template class TNodeRegistryEntryBase : public FNodeRegistryEntryBase { public: // Expose FNodeRegistryEntryBase constructors. using FNodeRegistryEntryBase::FNodeRegistryEntryBase; // Unhide CreateNode overloads which exist on INodeClassRegistryEntry using FNodeRegistryEntryBase::CreateNode; virtual TUniquePtr CreateNode(FNodeData InNodeData) const override { if constexpr(std::is_constructible_v>) { // Prefer construction of nodes using (FNodeData, TShareRef) return MakeUnique(MoveTemp(InNodeData), GetNodeClassMetadata()); } else if constexpr(std::is_constructible_v) { // Some node classes have FNodeClassMetadata declared as static members on // the node class and do not need a separate TSharedRef. return MakeUnique(MoveTemp(InNodeData)); } else { checkNoEntry(); return nullptr; } } }; // A node registry entry which also provides a node extension. template class TNodeRegistryEntry : public TNodeRegistryEntryBase { static_assert(std::is_base_of_v, "Configurations must inherit from FMetaSoundFrontendNodeConfiguration"); public: // Inherit constructor using TNodeRegistryEntryBase::TNodeRegistryEntryBase; virtual TInstancedStruct CreateFrontendNodeConfiguration() const override { return TInstancedStruct::Make(); } virtual bool IsCompatibleNodeConfiguration(TConstStructView InNodeConfiguration) const override { return InNodeConfiguration.GetScriptStruct() == ConfigurationType::StaticStruct(); } }; // A partial template specialization for scenario where no node extension is provided template class TNodeRegistryEntry : public TNodeRegistryEntryBase { public: // Inherit constructor using TNodeRegistryEntryBase::TNodeRegistryEntryBase; }; #if UE_METASOUND_DISABLE_5_6_NODE_REGISTRATION_DEPRECATION_WARNINGS template void TriggerDeprecatedNodeConstructorWarning() {} template void TriggerMissingCreateNodeClassMetadataWarning() {} #else // These only trigger deprecation if the macro UE_METASOUND_DISABLE_5_6_NODE_REGISTRATION_DEPRECATION_WARNINGS is false template UE_DEPRECATED(5.6, "Update the node's constructor to be of the form FMyNode(FNodeData InNodeData, TSharedRef InClassMetadata)") void TriggerDeprecatedNodeConstructorWarning() {} template UE_DEPRECATED(5.6, "Update the node class to include 'static FNodeClassMetadata CreateNodeClassMetadata()`)") void TriggerMissingCreateNodeClassMetadataWarning() {} #endif // Forward declare class FDeprecatedNodeAPIFactory; // FDeprecatedNodeAPIAdapterBase is used as a back compatible shim // for nodes which have not been updated to support the new APIs for node // registration. class FDeprecatedNodeAPIAdapterBase : public FBasicNode { protected: UE_API FDeprecatedNodeAPIAdapterBase(FNodeData InNodeData, TSharedRef InClassMetadata, TUniquePtr InNode); public: virtual ~FDeprecatedNodeAPIAdapterBase() = default; UE_API virtual FOperatorFactorySharedRef GetDefaultOperatorFactory() const override; private: TSharedRef Factory; }; // TDeprecatedNodeAPIAdapter is used as a back compatible shim // for nodes which have not been updated to support the new APIs for node // registration. template class TDeprecatedNodeAPIAdapter : public FDeprecatedNodeAPIAdapterBase { public: TDeprecatedNodeAPIAdapter(FNodeData InNodeData, TSharedRef InClassMetadata) : FDeprecatedNodeAPIAdapterBase(InNodeData, MoveTemp(InClassMetadata), MakeUnique(FNodeInitData{InNodeData.Name, InNodeData.ID})) { if constexpr(TIsOnlyDeprecatedNodeConstructorProvided::Value) { TriggerDeprecatedNodeConstructorWarning(); } } }; UE_API bool RegisterNodeInternal(TUniquePtr&& InEntry); UE_API bool UnregisterNodeInternal(const FNodeClassMetadata& InMetadata); } // namespace NodeRegistrationPrivate template bool RegisterNode(const FNodeClassMetadata& InMetadata, const FModuleInfo& InOwningModuleInfo) { using namespace NodeRegistrationPrivate; if constexpr (TIsOnlyDeprecatedNodeConstructorProvided::Value) { return RegisterNode, ConfigurationType>(InMetadata, InOwningModuleInfo); } else { return NodeRegistrationPrivate::RegisterNodeInternal(MakeUnique>(InMetadata, InOwningModuleInfo)); } } template UE_DEPRECATED(5.7, "Use RegisterNode(...) which provides FModuleInfo") bool RegisterNode(const FNodeClassMetadata& InMetadata) { return RegisterNode(InMetadata, FModuleInfo{}); } template bool RegisterNode(const FModuleInfo& InOwningModuleInfo) { static_assert(::Metasound::TIsNodeConstructorSupported::Value, "In order to be registered as a MetaSound node, the node needs to implement the following public constructor: Constructor(Metasound::FNodeData InNodeData) or Construct(Metasound::FNodeData InNodeData, TSharedRef InMetadata)"); if constexpr(TIsCreateNodeClassMetadataDeclared::Value) { return RegisterNode(TNodeType::CreateNodeClassMetadata(), InOwningModuleInfo); } else { using namespace NodeRegistrationPrivate; TriggerMissingCreateNodeClassMetadataWarning(); // Register a node using a prototype node. FNodeInitData InitData; TUniquePtr Node = MakeUnique(InitData); return RegisterNode(Node->GetMetadata(), InOwningModuleInfo); } } template UE_DEPRECATED(5.7, "Use RegisterNode(...) which provides FModuleInfo") bool RegisterNode() { return RegisterNode(FModuleInfo{}); } template bool UnregisterNode(const FModuleInfo& InOwningModuleInfo) { using namespace NodeRegistrationPrivate; if constexpr(TIsCreateNodeClassMetadataDeclared::Value) { return UnregisterNodeInternal(TNodeType::CreateNodeClassMetadata()); } else { using namespace NodeRegistrationPrivate; TriggerMissingCreateNodeClassMetadataWarning(); // Register a node using a prototype node. FNodeInitData InitData; TUniquePtr Node = MakeUnique(InitData); return UnregisterNodeInternal(Node->GetMetadata()); } } template UE_DEPRECATED(5.7, "Use UnregisterNode(...) which provides FModuleInfo") bool UnregisterNode() { return UnregisterNode(FModuleInfo{}); } } namespace Metasound { // Utility class to ensure that a node class can use the constructor the frontend uses. template struct UE_DEPRECATED(5.6, "Use std::is_constructible instead") ConstructorTakesNodeInitData { // Use SFINAE trick to see if we have a valid constructor: template static uint16 TestForConstructor(decltype(T(::Metasound::FNodeInitData()))*); template static uint8 TestForConstructor(...); static const bool Value = sizeof(TestForConstructor(nullptr)) == sizeof(uint16); }; template UE_DEPRECATED(5.6, "Use Frontend::RegisterNode()") bool RegisterNodeWithFrontend() { return Frontend::RegisterNode(); } template UE_DEPRECATED(5.6, "Use Frontend::RegisterNode(const Metasound::FNodeClassMetadata&)") bool RegisterNodeWithFrontend(const Metasound::FNodeClassMetadata& InMetadata) { return Frontend::RegisterNode(InMetadata); } } #define METASOUND_REGISTER_NODE_AND_CONFIGURATION(NodeClass, ConfigurationClass) \ static_assert(std::is_base_of<::Metasound::INodeBase, NodeClass>::value, "To be registered as a Metasound Node," #NodeClass "need to be a derived class from Metasound::INodeBase, Metasound::INode, or Metasound::FNode."); \ static_assert(::Metasound::TIsNodeConstructorSupported::Value, "In order to be registered as a Metasound Node, " #NodeClass " needs to implement the following public constructor: " #NodeClass "(Metasound::FNodeData InNodeData);"); \ METASOUND_IMPLEMENT_REGISTRATION_ACTION(NodeClass, (::Metasound::Frontend::RegisterNode), (::Metasound::Frontend::UnregisterNode)); #define METASOUND_REGISTER_NODE(NodeClass) METASOUND_REGISTER_NODE_AND_CONFIGURATION(NodeClass, void) /* Macros to help define various FText node fields. */ #if WITH_EDITOR #define METASOUND_LOCTEXT(KEY, NAME_TEXT) LOCTEXT(KEY, NAME_TEXT) #define METASOUND_LOCTEXT_FORMAT(KEY, NAME_TEXT, ...) FText::Format(LOCTEXT(KEY, NAME_TEXT), __VA_ARGS__) #else #define METASOUND_LOCTEXT(KEY, NAME_TEXT) FText{} #define METASOUND_LOCTEXT_FORMAT(KEY, NAME_TEXT, ...) FText{} #endif // WITH_EDITOR #undef UE_API