// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "MetasoundBuilderInterface.h" #include "MetasoundBuildError.h" #include "MetasoundNode.h" #include "MetasoundNodeInterface.h" #include "MetasoundOperatorInterface.h" #include "MetasoundDataFactory.h" #include "MetasoundDataReference.h" #include "MetasoundExecutableOperator.h" #include "MetasoundFrontend.h" #include #define LOCTEXT_NAMESPACE "MetasoundFrontend" namespace Metasound { // This convenience node can be registered and will invoke static_cast(FromDataType) every time it is executed. template class TAutoConverterNode : public FNode { static_assert(std::is_convertible::value, "Tried to create an auto converter node between two types we can't static_cast between."); public: static const FString& GetInputName() { static const FString InputName = GetMetasoundDataTypeString(); return InputName; } static const FString& GetOutputName() { static const FString OutputName = GetMetasoundDataTypeString(); return OutputName; } static FVertexInterface DeclareVertexInterface() { return FVertexInterface( FInputVertexInterface( TInputDataVertexModel(GetInputName(), FText::GetEmpty()) ), FOutputVertexInterface( TOutputDataVertexModel(GetOutputName(), FText::GetEmpty()) ) ); } static const FNodeClassMetadata& GetAutoConverterNodeMetadata() { auto InitNodeInfo = []() -> FNodeClassMetadata { const FString& InputDisplayName = GetInputName(); const FString& OutputDisplayName = GetOutputName(); FNodeDisplayStyle DisplayStyle; DisplayStyle.bShowName = false; DisplayStyle.bShowInputNames = false; DisplayStyle.bShowOutputNames = false; FNodeClassMetadata Info; Info.ClassName = { TEXT("Convert"), GetMetasoundDataTypeName(), GetMetasoundDataTypeName() }; Info.MajorVersion = 1; Info.MinorVersion = 0; Info.DisplayName = FText::Format(LOCTEXT("Metasound_AutoConverterNodeDisplayNameFormat", "{0} to {1}"), FText::FromName(GetMetasoundDataTypeName()), FText::FromName(GetMetasoundDataTypeName())); Info.Description = LOCTEXT("Metasound_ConverterNodeDescription", "Converts between two different data types."); Info.Author = PluginAuthor; Info.DisplayStyle = DisplayStyle; Info.PromptIfMissing = PluginNodeMissingPrompt; Info.DefaultInterface = DeclareVertexInterface(); Info.CategoryHierarchy = { LOCTEXT("Metasound_ConvertNodeCategory", "Conversions") }; Info.Keywords = { "Convert", GetMetasoundDataTypeName(), GetMetasoundDataTypeName()}; return Info; }; static const FNodeClassMetadata Info = InitNodeInfo(); return Info; } private: /** FConverterOperator converts from "FromDataType" to "ToDataType" using * a implicit conversion operators. */ class FConverterOperator : public TExecutableOperator { public: FConverterOperator(TDataReadReference InFromDataReference, TDataWriteReference InToDataReference) : FromData(InFromDataReference) , ToData(InToDataReference) { } virtual ~FConverterOperator() {} virtual FDataReferenceCollection GetInputs() const override { FDataReferenceCollection Inputs; Inputs.AddDataReadReference(GetInputName(), FromData); return Inputs; } virtual FDataReferenceCollection GetOutputs() const override { FDataReferenceCollection Outputs; Outputs.AddDataReadReference(GetOutputName(), ToData); return Outputs; } void Execute() { *ToData = static_cast(*FromData); } private: TDataReadReference FromData; TDataWriteReference ToData; }; /** FConverterOperatorFactory creates an operator which converts from * "FromDataType" to "ToDataType". */ class FCoverterOperatorFactory : public IOperatorFactory { public: FCoverterOperatorFactory() = default; virtual TUniquePtr CreateOperator(const FCreateOperatorParams& InParams, FBuildErrorArray& OutErrors) override { TDataWriteReference WriteReference = TDataWriteReferenceFactory::CreateAny(InParams.OperatorSettings); if (!InParams.InputDataReferences.ContainsDataReadReference(GetInputName())) { if (ensure(InParams.Node.GetVertexInterface().ContainsInputVertex(GetInputName()))) { // There should be something hooked up to the converter node. Report it as an error. FInputDataDestination Dest(InParams.Node, InParams.Node.GetVertexInterface().GetInputVertex(GetInputName())); AddBuildError(OutErrors, Dest); } // We can still build something even though there is an error. TDataReadReference ReadReference = TDataReadReferenceFactory::CreateAny(InParams.OperatorSettings); return MakeUnique(ReadReference, WriteReference); } TDataReadReference ReadReference = InParams.InputDataReferences.GetDataReadReference(GetInputName()); return MakeUnique(ReadReference, WriteReference); } }; public: TAutoConverterNode(const FNodeInitData& InInitData) : FNode(InInitData.InstanceName, InInitData.InstanceID, GetAutoConverterNodeMetadata()) , Interface(DeclareVertexInterface()) , Factory(MakeOperatorFactoryRef()) { } virtual ~TAutoConverterNode() = default; virtual const FVertexInterface& GetVertexInterface() const override { return Interface; } virtual bool SetVertexInterface(const FVertexInterface& InInterface) override { return Interface == InInterface; } virtual bool IsVertexInterfaceSupported(const FVertexInterface& InInterface) const override { return Interface == InInterface; } virtual FOperatorFactorySharedRef GetDefaultOperatorFactory() const override { return Factory; } private: FVertexInterface Interface; FOperatorFactorySharedRef Factory; }; } #undef LOCTEXT_NAMESPACE