// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "MetasoundBuilderInterface.h" #include "MetasoundNode.h" #include "MetasoundNodeInterface.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundOperatorInterface.h" #include "MetasoundDataReference.h" #include "MetasoundExecutableOperator.h" #include "MetasoundParamHelper.h" #include "MetasoundRouter.h" #include "MetasoundVertex.h" #include #define LOCTEXT_NAMESPACE "MetasoundFrontend" namespace Metasound { namespace SendVertexNames { METASOUND_PARAM(AddressInput, "Address", "Address") } template class TSendNode : public FNode { public: static const FVertexName& GetSendInputName() { static const FVertexName& SendInput = GetMetasoundDataTypeName(); return SendInput; } static FVertexInterface DeclareVertexInterface() { using namespace SendVertexNames; static const FDataVertexMetadata AddressInputMetadata { FText::GetEmpty() // description , METASOUND_GET_PARAM_DISPLAYNAME(AddressInput) // display name }; return FVertexInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME(AddressInput), AddressInputMetadata), TInputDataVertex(GetSendInputName(), FDataVertexMetadata{ FText::GetEmpty() }) ), FOutputVertexInterface( ) ); } static const FNodeClassMetadata& GetNodeInfo() { auto InitNodeInfo = []() -> FNodeClassMetadata { const FVertexName& InputName = GetSendInputName(); FNodeClassMetadata Info; Info.ClassName = { "Send", GetMetasoundDataTypeName(), FName() }; Info.MajorVersion = 1; Info.MinorVersion = 0; Info.DisplayName = METASOUND_LOCTEXT_FORMAT("Metasound_SendNodeDisplayNameFormat", "Send {0}", GetMetasoundDataTypeDisplayText()); Info.Description = METASOUND_LOCTEXT("Metasound_SendNodeDescription", "Sends data from a send node with the same name."); Info.Author = PluginAuthor; Info.PromptIfMissing = PluginNodeMissingPrompt; Info.DefaultInterface = DeclareVertexInterface(); Info.CategoryHierarchy = { METASOUND_LOCTEXT("Metasound_TransmissionNodeCategory", "Transmission") }; Info.Keywords = { }; // Then send & receive nodes do not work as expected, particularly // around multiple-consumer scenarios. Deprecate them to avoid // metasound assets from relying on send & receive nodes. Info.bDeprecated = true; return Info; }; static const FNodeClassMetadata Info = InitNodeInfo(); return Info; } private: class TSendOperator : public TExecutableOperator { public: TSendOperator(TDataReadReference InInputData, TDataReadReference InSendAddress, const FOperatorSettings& InOperatorSettings) : InputData(InInputData) , SendAddress(InSendAddress) , CachedSendAddress(*InSendAddress) , CachedSenderParams({InOperatorSettings, 0.0f}) , Sender(nullptr) { Sender = CreateNewSender(); } virtual ~TSendOperator() { ResetSenderAndCleanupChannel(); } virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override { using namespace SendVertexNames; InOutVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(AddressInput), SendAddress); InOutVertexData.BindReadVertex(GetSendInputName(), InputData); } virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override { } void Execute() { if (*SendAddress != CachedSendAddress) { ResetSenderAndCleanupChannel(); CachedSendAddress = *SendAddress; Sender = CreateNewSender(); check(Sender.IsValid()); } Sender->Push(*InputData); } void Reset(const IOperator::FResetParams& InParams) { ResetSenderAndCleanupChannel(); CachedSendAddress = *SendAddress; Sender = CreateNewSender(); check(Sender.IsValid()); } private: FSendAddress GetSendAddressWithDataType(const FSendAddress& InAddress) const { // The data type of a send address is inferred by the underlying // data type of this node. A full send address, including the data type, // cannot be constructed from a literal. return FSendAddress{ InAddress.GetChannelName(), GetMetasoundDataTypeName(), InAddress.GetInstanceID() }; } TSenderPtr CreateNewSender() const { if (ensure(SendAddress->GetDataType().IsNone() || (GetMetasoundDataTypeName() == SendAddress->GetDataType()))) { return FDataTransmissionCenter::Get().RegisterNewSender(GetSendAddressWithDataType(*SendAddress), CachedSenderParams); } return TSenderPtr(nullptr); } void ResetSenderAndCleanupChannel() { Sender.Reset(); FDataTransmissionCenter::Get().UnregisterDataChannelIfUnconnected(GetSendAddressWithDataType(CachedSendAddress)); } TDataReadReference InputData; TDataReadReference SendAddress; FSendAddress CachedSendAddress; FSenderInitParams CachedSenderParams; TSenderPtr Sender; }; class FSendOperatorFactory : public IOperatorFactory { public: FSendOperatorFactory() = default; virtual TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) override { using namespace SendVertexNames; if (InParams.InputData.IsVertexBound(GetSendInputName())) { return MakeUnique(InParams.InputData.GetDataReadReference(GetSendInputName()), InParams.InputData.GetOrConstructDataReadReference(METASOUND_GET_PARAM_NAME(AddressInput)), InParams.OperatorSettings ); } else { // No input hook up to send, so this node can no-op return MakeUnique(); } } }; public: TSendNode(const FNodeInitData& InInitData) : FNode(InInitData.InstanceName, InInitData.InstanceID, GetNodeInfo()) , Factory(MakeOperatorFactoryRef()) { } virtual ~TSendNode() = default; virtual FOperatorFactorySharedRef GetDefaultOperatorFactory() const override { return Factory; } private: FOperatorFactorySharedRef Factory; }; } #undef LOCTEXT_NAMESPACE