// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Interfaces/MetasoundFrontendSourceInterface.h" #include "Internationalization/Text.h" #include "MetasoundBuilderInterface.h" #include "MetasoundDataFactory.h" #include "MetasoundEnvironment.h" #include "MetasoundExecutableOperator.h" #include "MetasoundFacade.h" #include "MetasoundLog.h" #include "MetasoundNodeInterface.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundOperatorInterface.h" #include "MetasoundParamHelper.h" #include "MetasoundPrimitives.h" #include "MetasoundTrigger.h" #include "MetasoundVertex.h" #include #define LOCTEXT_NAMESPACE "MetasoundFrontend" namespace Metasound { namespace MetasoundArrayNodesPrivate { // Convenience function for make FNodeClassMetadata of array nodes. METASOUNDFRONTEND_API FNodeClassMetadata CreateArrayNodeClassMetadata(const FName& InDataTypeName, const FName& InOperatorName, const FText& InDisplayName, const FText& InDescription, const FVertexInterface& InDefaultInterface, int32 MajorVersion=1, int32 MinorVersion=0, bool bIsDeprecated=false); // Retrieve the ElementType from an ArrayType template struct TArrayElementType { // Default implementation has Type. }; // ElementType specialization for TArray types. template struct TArrayElementType> { using Type = ElementType; }; } namespace ArrayNodeVertexNames { static const FLazyName InputInitialArrayName = TEXT("Array"); #if WITH_EDITOR static const FText InputInitialArrayTooltip = LOCTEXT("InitialArrayTooltip", "Initial Array"); static const FText InputInitialArrayDisplayName = LOCTEXT("InitialArrayDisplayName", "Init Array"); #else static const FText InputInitialArrayTooltip = FText::GetEmpty(); static const FText InputInitialArrayDisplayName = FText::GetEmpty(); #endif METASOUND_PARAM(InputArray, "Array", "Input Array.") METASOUND_PARAM(InputLeftArray, "Left Array", "Input Left Array.") METASOUND_PARAM(InputRightArray, "Right Array", "Input Right Array.") METASOUND_PARAM(InputTriggerGet, "Trigger", "Trigger to get value.") METASOUND_PARAM(InputTriggerSet, "Trigger", "Trigger to set value.") METASOUND_PARAM(InputIndex, "Index", "Index in Array.") METASOUND_PARAM(InputStartIndex, "Start Index", "First index to include.") METASOUND_PARAM(InputEndIndex, "End Index", "Last index to include.") METASOUND_PARAM(InputValue, "Value", "Value to set.") METASOUND_PARAM(OutputNum, "Num", "Number of elements in the array.") METASOUND_PARAM(OutputValue, "Element", "Value of element at array index.") METASOUND_PARAM(OutputArrayConcat, "Array", "Array after concatenation.") METASOUND_PARAM(OutputArraySet, "Array", "Array after setting.") METASOUND_PARAM(OutputArraySubset, "Array", "Subset of input array.") METASOUND_PARAM(OutputLastIndex, "Last Index", "Last index of the array.") }; /** TArrayNumOperator gets the number of elements in an Array. The operator * uses the FNodeFacade and defines the vertex, metadata and vertex interface * statically on the operator class. */ template class TArrayNumOperator : public TExecutableOperator> { public: using FArrayDataReadReference = TDataReadReference; // Declare the vertex interface static const FVertexInterface& GetDefaultInterface() { using namespace ArrayNodeVertexNames; static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputArray)) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputNum)) ) ); return DefaultInterface; } static const FNodeClassMetadata& GetNodeInfo() { auto CreateNodeClassMetadata = []() -> FNodeClassMetadata { const FName DataTypeName = GetMetasoundDataTypeName(); const FName OperatorName = TEXT("Num"); const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT("ArrayOpArrayNumDisplayNamePattern", "Num ({0})", GetMetasoundDataTypeDisplayText()); const FText NodeDescription = METASOUND_LOCTEXT("ArrayOpArrayNumDescription", "Number of elements in the array"); const FVertexInterface NodeInterface = GetDefaultInterface(); FNodeClassMetadata NodeClassMetadata = MetasoundArrayNodesPrivate::CreateArrayNodeClassMetadata(DataTypeName, OperatorName, NodeDisplayName, NodeDescription, NodeInterface); NodeClassMetadata.Keywords.Append({ METASOUND_LOCTEXT("ArrayNumKeyword_Length", "length"), METASOUND_LOCTEXT("ArrayNumKeyword_Size", "size"), }); return NodeClassMetadata; }; static const FNodeClassMetadata Metadata = CreateNodeClassMetadata(); return Metadata; } static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace ArrayNodeVertexNames; using namespace MetasoundArrayNodesPrivate; const FInputVertexInterfaceData& InputData = InParams.InputData; // Get the input array or construct an empty one. FArrayDataReadReference Array = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputArray), InParams.OperatorSettings); return MakeUnique(Array); } TArrayNumOperator(FArrayDataReadReference InArray) : Array(InArray) , Num(TDataWriteReference::CreateNew()) { // Initialize value for downstream nodes. *Num = Array->Num(); } virtual ~TArrayNumOperator() = default; virtual FDataReferenceCollection GetInputs() const override { checkNoEntry(); return {}; } virtual FDataReferenceCollection GetOutputs() const override { checkNoEntry(); return {}; } virtual void BindInputs(FInputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputArray), Array); } virtual void BindOutputs(FOutputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputNum), Num); } void Execute() { *Num = Array->Num(); } void Reset(const IOperator::FResetParams& InParams) { Execute(); } private: FArrayDataReadReference Array; TDataWriteReference Num; }; template using TArrayNumNode = TNodeFacade>; /** TArrayGetOperator copies a value from the array to the output when * a trigger occurs. Initially, the output value is default constructed and * will remain that way until until a trigger is encountered. */ template class TArrayGetOperator : public TExecutableOperator> { public: using FArrayDataReadReference = TDataReadReference; using ElementType = typename MetasoundArrayNodesPrivate::TArrayElementType::Type; static const FVertexInterface& GetDefaultInterface() { using namespace ArrayNodeVertexNames; static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputTriggerGet)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputArray)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputIndex)) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputValue)) ) ); return DefaultInterface; } static const FNodeClassMetadata& GetNodeInfo() { auto CreateNodeClassMetadata = []() -> FNodeClassMetadata { const FName DataTypeName = GetMetasoundDataTypeName(); const FName OperatorName = TEXT("Get"); const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT("ArrayOpArrayGetDisplayNamePattern", "Get ({0})", GetMetasoundDataTypeDisplayText()); const FText NodeDescription = METASOUND_LOCTEXT("ArrayOpArrayGetDescription", "Get element at index in array."); const FVertexInterface NodeInterface = GetDefaultInterface(); return MetasoundArrayNodesPrivate::CreateArrayNodeClassMetadata(DataTypeName, OperatorName, NodeDisplayName, NodeDescription, NodeInterface); }; static const FNodeClassMetadata Metadata = CreateNodeClassMetadata(); return Metadata; } struct FInitParams { TDataReadReference Trigger; FArrayDataReadReference Array; TDataReadReference Index; #if WITH_METASOUND_DEBUG_ENVIRONMENT FString GraphName; #endif }; static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace ArrayNodeVertexNames; using namespace MetasoundArrayNodesPrivate; const FInputVertexInterfaceData& InputData = InParams.InputData; // Input Trigger TDataReadReference Trigger = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputTriggerGet), InParams.OperatorSettings); // Input Array FArrayDataReadReference Array = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputArray), InParams.OperatorSettings); // Input Index TDataReadReference Index = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputIndex), InParams.OperatorSettings); #if WITH_METASOUND_DEBUG_ENVIRONMENT FString GraphName; if (InParams.Environment.Contains(Frontend::SourceInterface::Environment::GraphName)) { GraphName = InParams.Environment.GetValue(Frontend::SourceInterface::Environment::GraphName); } #endif // WITH_METASOUND_DEBUG_ENVIRONMENT FInitParams OperatorInitParams { Trigger , Array , Index #if WITH_METASOUND_DEBUG_ENVIRONMENT , GraphName #endif // WITH_METASOUND_DEBUG_ENVIRONMENT }; return MakeUnique(InParams.OperatorSettings, MoveTemp(OperatorInitParams)); } TArrayGetOperator(const FOperatorSettings& InSettings, FInitParams&& InParams) : Trigger(InParams.Trigger) , Array(InParams.Array) , Index(InParams.Index) , Value(TDataWriteReferenceFactory::CreateExplicitArgs(InSettings)) #if WITH_METASOUND_DEBUG_ENVIRONMENT , GraphName(InParams.GraphName) #endif // WITH_METASOUND_DEBUG_ENVIRONMENT { const int32 IndexValue = *Index; const ArrayType& ArrayRef = *Array; if ((IndexValue >= 0) && (IndexValue < ArrayRef.Num())) { *Value = ArrayRef[IndexValue]; } } virtual ~TArrayGetOperator() = default; virtual FDataReferenceCollection GetInputs() const override { checkNoEntry(); return {}; } virtual FDataReferenceCollection GetOutputs() const override { checkNoEntry(); return {}; } virtual void BindInputs(FInputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputTriggerGet), Trigger); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputArray), Array); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputIndex), Index); } virtual void BindOutputs(FOutputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputValue), Value); } void Execute() { // Only perform get on trigger. if (*Trigger) { const int32 IndexValue = *Index; const ArrayType& ArrayRef = *Array; if ((IndexValue >= 0) && (IndexValue < ArrayRef.Num())) { *Value = ArrayRef[IndexValue]; } #if WITH_METASOUND_DEBUG_ENVIRONMENT else { UE_LOG(LogMetaSound, Warning, TEXT("Attempt to get value at invalid index [ArraySize:%d, Index:%d] in MetaSound Graph \"%s\"."), ArrayRef.Num(), IndexValue, *GraphName); } #endif // WITH_METASOUND_DEBUG_ENVIRONMENT } } void Reset(const IOperator::FResetParams& InParams) { const int32 IndexValue = *Index; const ArrayType& ArrayRef = *Array; if ((IndexValue >= 0) && (IndexValue < ArrayRef.Num())) { *Value = ArrayRef[IndexValue]; } else { *Value = TDataTypeFactory::CreateExplicitArgs(InParams.OperatorSettings); } } private: TDataReadReference Trigger; FArrayDataReadReference Array; TDataReadReference Index; TDataWriteReference Value; #if WITH_METASOUND_DEBUG_ENVIRONMENT FString GraphName; #endif // WITH_METASOUND_DEBUG_ENVIRONMENT }; template using TArrayGetNode = TNodeFacade>; /** TArraySetOperator sets an element in an array to a specific value. */ template class TArraySetOperator : public TExecutableOperator> { public: using FArrayDataReadReference = TDataReadReference; using FArrayDataWriteReference = TDataWriteReference; using ElementType = typename MetasoundArrayNodesPrivate::TArrayElementType::Type; static const FVertexInterface& GetDefaultInterface() { using namespace ArrayNodeVertexNames; static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputTriggerSet)), TInputDataVertex(InputInitialArrayName, FDataVertexMetadata { InputInitialArrayTooltip, InputInitialArrayDisplayName }), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputIndex)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputValue)) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputArraySet)) ) ); return DefaultInterface; } static const FNodeClassMetadata& GetNodeInfo() { auto CreateNodeClassMetadata = []() -> FNodeClassMetadata { const FName DataTypeName = GetMetasoundDataTypeName(); const FName OperatorName = TEXT("Set"); const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT("ArrayOpArraySetDisplayNamePattern", "Set ({0})", GetMetasoundDataTypeDisplayText()); const FText NodeDescription = METASOUND_LOCTEXT("ArrayOpArraySetDescription", "Set element at index in array."); const FVertexInterface NodeInterface = GetDefaultInterface(); return MetasoundArrayNodesPrivate::CreateArrayNodeClassMetadata(DataTypeName, OperatorName, NodeDisplayName, NodeDescription, NodeInterface); }; static const FNodeClassMetadata Metadata = CreateNodeClassMetadata(); return Metadata; } struct FInitParams { TDataReadReference Trigger; FArrayDataReadReference InitArray; FArrayDataWriteReference Array; TDataReadReference Index; TDataReadReference Value; #if WITH_METASOUND_DEBUG_ENVIRONMENT FString GraphName; #endif // WITH_METASOUND_DEBUG_ENVIRONMENT }; static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace ArrayNodeVertexNames; using namespace MetasoundArrayNodesPrivate; const FInputVertexInterfaceData& InputData = InParams.InputData; TDataReadReference Trigger = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputTriggerSet), InParams.OperatorSettings); FArrayDataReadReference InitArray = InputData.GetOrCreateDefaultDataReadReference(InputInitialArrayName, InParams.OperatorSettings); FArrayDataWriteReference Array = TDataWriteReferenceFactory::CreateExplicitArgs(InParams.OperatorSettings, *InitArray); TDataReadReference Index = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputIndex), InParams.OperatorSettings); TDataReadReference Value = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputValue), InParams.OperatorSettings); #if WITH_METASOUND_DEBUG_ENVIRONMENT FString GraphName; if (InParams.Environment.Contains(Frontend::SourceInterface::Environment::GraphName)) { GraphName = InParams.Environment.GetValue(Frontend::SourceInterface::Environment::GraphName); } #endif // WITH_METASOUND_DEBUG_ENVIRONMENT FInitParams OperatorInitParams { Trigger , InitArray , Array , Index , Value #if WITH_METASOUND_DEBUG_ENVIRONMENT , GraphName #endif // WITH_METASOUND_DEBUG_ENVIRONMENT }; return MakeUnique(InParams.OperatorSettings, MoveTemp(OperatorInitParams)); } TArraySetOperator(const FOperatorSettings& InSettings, FInitParams&& InParams) : OperatorSettings(InSettings) , Trigger(InParams.Trigger) , InitArray(InParams.InitArray) , Array(InParams.Array) , Index(InParams.Index) , Value(InParams.Value) #if WITH_METASOUND_DEBUG_ENVIRONMENT , GraphName(InParams.GraphName) #endif // WITH_METASOUND_DEBUG_ENVIRONMENT { } virtual ~TArraySetOperator() = default; virtual FDataReferenceCollection GetInputs() const override { checkNoEntry(); return {}; } virtual FDataReferenceCollection GetOutputs() const override { checkNoEntry(); return {}; } virtual void BindInputs(FInputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputTriggerSet), Trigger); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputArray), InitArray); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputIndex), Index); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputValue), Value); } virtual void BindOutputs(FOutputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputArraySet), Array); } void Execute() { if (*Trigger) { const int32 IndexValue = *Index; ArrayType& ArrayRef = *Array; if ((IndexValue >= 0) && (IndexValue < ArrayRef.Num())) { ArrayRef[IndexValue] = *Value; } #if WITH_METASOUND_DEBUG_ENVIRONMENT else { UE_LOG(LogMetaSound, Warning, TEXT("Attempt to set value at invalid index [ArraySize:%d, Index:%d] in MetaSound Graph \"%s\"."), ArrayRef.Num(), IndexValue, *GraphName); } #endif // WITH_METASOUND_DEBUG_ENVIRONMENT } } void Reset(const IOperator::FResetParams& Inparams) { *Array = *InitArray; } private: FOperatorSettings OperatorSettings; TDataReadReference Trigger; FArrayDataReadReference InitArray; FArrayDataWriteReference Array; TDataReadReference Index; TDataReadReference Value; #if WITH_METASOUND_DEBUG_ENVIRONMENT FString GraphName; #endif // WITH_METASOUND_DEBUG_ENVIRONMENT }; template using TArraySetNode = TNodeFacade>; /** TArrayConcatOperator concatenates two arrays on trigger. */ template class TArrayConcatOperator : public TExecutableOperator> { public: using FArrayDataReadReference = TDataReadReference; using FArrayDataWriteReference = TDataWriteReference; using ElementType = typename MetasoundArrayNodesPrivate::TArrayElementType::Type; static const FVertexInterface& GetDefaultInterface() { using namespace ArrayNodeVertexNames; static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputTriggerGet)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputLeftArray)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputRightArray)) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputArrayConcat)) ) ); return DefaultInterface; } static const FNodeClassMetadata& GetNodeInfo() { auto CreateNodeClassMetadata = []() -> FNodeClassMetadata { const FName DataTypeName = GetMetasoundDataTypeName(); const FName OperatorName = TEXT("Concat"); const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT("ArrayOpArrayConcatDisplayNamePattern", "Concatenate ({0})", GetMetasoundDataTypeDisplayText()); const FText NodeDescription = METASOUND_LOCTEXT("ArrayOpArrayConcatDescription", "Concatenates two arrays on trigger."); const FVertexInterface NodeInterface = GetDefaultInterface(); FNodeClassMetadata NodeClassMetadata = MetasoundArrayNodesPrivate::CreateArrayNodeClassMetadata(DataTypeName, OperatorName, NodeDisplayName, NodeDescription, NodeInterface); NodeClassMetadata.Keywords.Append({ METASOUND_LOCTEXT("ArrayConcatKeyword_Append", "append"), }); return NodeClassMetadata; }; static const FNodeClassMetadata Metadata = CreateNodeClassMetadata(); return Metadata; } static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace ArrayNodeVertexNames; using namespace MetasoundArrayNodesPrivate; const FInputVertexInterfaceData& InputData = InParams.InputData; TDataReadReference Trigger = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputTriggerGet), InParams.OperatorSettings); FArrayDataReadReference LeftArray = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputLeftArray), InParams.OperatorSettings); FArrayDataReadReference RightArray = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputRightArray), InParams.OperatorSettings); return MakeUnique(Trigger, LeftArray, RightArray); } TArrayConcatOperator(TDataReadReference InTrigger, FArrayDataReadReference InLeftArray, FArrayDataReadReference InRightArray) : Trigger(InTrigger) , LeftArray(InLeftArray) , RightArray(InRightArray) , OutArray(TDataWriteReference::CreateNew()) { } virtual ~TArrayConcatOperator() = default; virtual FDataReferenceCollection GetInputs() const override { checkNoEntry(); return {}; } virtual FDataReferenceCollection GetOutputs() const override { checkNoEntry(); return {}; } virtual void BindInputs(FInputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputTriggerGet), Trigger); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputLeftArray), LeftArray); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputRightArray), RightArray); } virtual void BindOutputs(FOutputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputArrayConcat), OutArray); } void Execute() { if (*Trigger) { *OutArray = *LeftArray; OutArray->Append(*RightArray); } } void Reset(const IOperator::FResetParams& InParams) { OutArray->Reset(); } private: TDataReadReference Trigger; FArrayDataReadReference LeftArray; FArrayDataReadReference RightArray; FArrayDataWriteReference OutArray; }; template using TArrayConcatNode = TNodeFacade>; /** TArraySubsetOperator slices an array on trigger. */ template class TArraySubsetOperator : public TExecutableOperator> { public: using FArrayDataReadReference = TDataReadReference; using FArrayDataWriteReference = TDataWriteReference; using ElementType = typename MetasoundArrayNodesPrivate::TArrayElementType::Type; static const FVertexInterface& GetDefaultInterface() { using namespace ArrayNodeVertexNames; static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputTriggerGet)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputArray)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputStartIndex)), TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputEndIndex)) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputArraySubset)) ) ); return DefaultInterface; } static const FNodeClassMetadata& GetNodeInfo() { auto CreateNodeClassMetadata = []() -> FNodeClassMetadata { const FName DataTypeName = GetMetasoundDataTypeName(); const FName OperatorName = TEXT("Subset"); const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT("ArrayOpArraySubsetDisplayNamePattern", "Subset ({0})", GetMetasoundDataTypeDisplayText()); const FText NodeDescription = METASOUND_LOCTEXT("ArrayOpArraySubsetDescription", "Subset array on trigger."); const FVertexInterface NodeInterface = GetDefaultInterface(); FNodeClassMetadata NodeClassMetadata = MetasoundArrayNodesPrivate::CreateArrayNodeClassMetadata(DataTypeName, OperatorName, NodeDisplayName, NodeDescription, NodeInterface); NodeClassMetadata.Keywords.Append({ METASOUND_LOCTEXT("ArraySubsetKeyword_Slice", "slice"), }); return NodeClassMetadata; }; static const FNodeClassMetadata Metadata = CreateNodeClassMetadata(); return Metadata; } static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace ArrayNodeVertexNames; using namespace MetasoundArrayNodesPrivate; const FInputVertexInterfaceData& InputData = InParams.InputData; TDataReadReference Trigger = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputTriggerGet), InParams.OperatorSettings); FArrayDataReadReference InArray = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputArray), InParams.OperatorSettings); TDataReadReference StartIndex = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputStartIndex), InParams.OperatorSettings); TDataReadReference EndIndex = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputEndIndex), InParams.OperatorSettings); TDataWriteReference OutputArray = TDataWriteReferenceFactory::CreateExplicitArgs(InParams.OperatorSettings); return MakeUnique(Trigger, InArray, StartIndex, EndIndex, OutputArray); } TArraySubsetOperator(TDataReadReference InTrigger, FArrayDataReadReference InInputArray, TDataReadReference InStartIndex, TDataReadReference InEndIndex, TDataWriteReference InOutputArray) : Trigger(InTrigger) , InputArray(InInputArray) , StartIndex(InStartIndex) , EndIndex(InEndIndex) , OutputArray(InOutputArray) { } virtual ~TArraySubsetOperator() = default; virtual FDataReferenceCollection GetInputs() const override { checkNoEntry(); return {}; } virtual FDataReferenceCollection GetOutputs() const override { checkNoEntry(); return {}; } virtual void BindInputs(FInputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputTriggerGet), Trigger); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputArray), InputArray); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputStartIndex), StartIndex); InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputEndIndex), EndIndex); } virtual void BindOutputs(FOutputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputArraySubset), OutputArray); } void Execute() { if (*Trigger) { OutputArray->Reset(); const ArrayType& InputArrayRef = *InputArray; const int32 StartIndexValue = FMath::Max(0, *StartIndex); const int32 EndIndexValue = FMath::Min(InputArrayRef.Num(), *EndIndex + 1); if (StartIndexValue < EndIndexValue) { const int32 Num = EndIndexValue - StartIndexValue; OutputArray->Append(&InputArrayRef[StartIndexValue], Num); } } } void Reset(const IOperator::FResetParams& InParams) { OutputArray->Reset(); } private: TDataReadReference Trigger; FArrayDataReadReference InputArray; TDataReadReference StartIndex; TDataReadReference EndIndex; FArrayDataWriteReference OutputArray; }; template using TArraySubsetNode = TNodeFacade>; /** TArrayLastIndex gets last index of an array. */ template class TArrayLastIndexOperator : public TExecutableOperator> { public: using FArrayDataReadReference = TDataReadReference; // Declare the vertex interface static const FVertexInterface& GetDefaultInterface() { using namespace ArrayNodeVertexNames; static const FVertexInterface DefaultInterface( FInputVertexInterface( TInputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(InputArray)) ), FOutputVertexInterface( TOutputDataVertex(METASOUND_GET_PARAM_NAME_AND_METADATA(OutputLastIndex)) ) ); return DefaultInterface; } static const FNodeClassMetadata& GetNodeInfo() { auto CreateNodeClassMetadata = []() -> FNodeClassMetadata { const FName DataTypeName = GetMetasoundDataTypeName(); const FName OperatorName = TEXT("GetLastIndex"); const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT("ArrayOpArrayLastIndexDisplayNamePattern", "Get Last Index ({0})", GetMetasoundDataTypeDisplayText()); const FText NodeDescription = METASOUND_LOCTEXT("ArrayOpArrayLastIndexDescription", "Last index of the array"); const FVertexInterface NodeInterface = GetDefaultInterface(); return MetasoundArrayNodesPrivate::CreateArrayNodeClassMetadata(DataTypeName, OperatorName, NodeDisplayName, NodeDescription, NodeInterface); }; static const FNodeClassMetadata Metadata = CreateNodeClassMetadata(); return Metadata; } static TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using namespace ArrayNodeVertexNames; using namespace MetasoundArrayNodesPrivate; const FInputVertexInterfaceData& InputData = InParams.InputData; // Get the input array or construct an empty one. FArrayDataReadReference Array = InputData.GetOrCreateDefaultDataReadReference(METASOUND_GET_PARAM_NAME(InputArray), InParams.OperatorSettings); return MakeUnique(Array); } TArrayLastIndexOperator(FArrayDataReadReference InArray) : Array(InArray) , LastIndex(TDataWriteReference::CreateNew()) { // Initialize value for downstream nodes. *LastIndex = Array->Num() - 1; } virtual ~TArrayLastIndexOperator() = default; virtual void BindInputs(FInputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(InputArray), Array); } virtual void BindOutputs(FOutputVertexInterfaceData& InVertexData) override { using namespace ArrayNodeVertexNames; InVertexData.BindReadVertex(METASOUND_GET_PARAM_NAME(OutputLastIndex), LastIndex); } void Execute() { *LastIndex = Array->Num() - 1; } void Reset(const IOperator::FResetParams& InParams) { Execute(); } private: FArrayDataReadReference Array; TDataWriteReference LastIndex; }; template using TArrayLastIndexNode = TNodeFacade>; } // namespace Metasound #undef LOCTEXT_NAMESPACE