2020-08-26 14:16:05 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
# include "CoreMinimal.h"
# include "MetasoundBuilderInterface.h"
2020-10-26 13:39:01 -04:00
# include "MetasoundBuildError.h"
2020-11-24 15:24:29 -04:00
# include "MetasoundNode.h"
2020-08-26 14:16:05 -04:00
# include "MetasoundNodeInterface.h"
2022-02-10 18:36:47 -05:00
# include "MetasoundNodeRegistrationMacro.h"
2020-08-26 14:16:05 -04:00
# include "MetasoundOperatorInterface.h"
2020-11-04 14:26:37 -04:00
# include "MetasoundDataFactory.h"
2020-08-26 14:16:05 -04:00
# include "MetasoundDataReference.h"
# include "MetasoundExecutableOperator.h"
2021-01-23 12:59:01 -04:00
# include "MetasoundFrontend.h"
2021-08-09 15:13:40 -04:00
# include "MetasoundFrontendNodesCategories.h"
2021-09-13 14:14:37 -04:00
# include "MetasoundVertex.h"
2020-08-26 14:16:05 -04:00
# include <type_traits>
2020-11-24 15:24:29 -04:00
# define LOCTEXT_NAMESPACE "MetasoundFrontend"
2020-08-26 14:16:05 -04:00
2021-09-13 14:14:37 -04:00
2020-08-26 14:16:05 -04:00
namespace Metasound
{
2021-12-06 14:40:38 -05:00
// Determines whether an auto converter node will be registered to convert
// between two types.
template < typename TFromDataType , typename TToDataType >
struct TIsAutoConvertible
{
static constexpr bool bIsConvertible = std : : is_convertible < TFromDataType , TToDataType > : : value ;
// Handle case of converting enums to/from integers.
static constexpr bool bIsIntToEnumConversion = std : : is_same < int32 , TFromDataType > : : value & & TEnumTraits < TToDataType > : : bIsEnum ;
static constexpr bool bIsEnumToIntConversion = TEnumTraits < TFromDataType > : : bIsEnum & & std : : is_same < int32 , TToDataType > : : value ;
static constexpr bool Value = bIsConvertible | | bIsIntToEnumConversion | | bIsEnumToIntConversion ;
} ;
// This convenience node can be registered and will invoke static_cast<ToDataType>(FromDataType) every time it is executed,
// with a special case for enum <-> int32 conversions.
2020-08-26 14:16:05 -04:00
template < typename FromDataType , typename ToDataType >
2020-11-24 15:24:29 -04:00
class TAutoConverterNode : public FNode
2020-08-26 14:16:05 -04:00
{
2021-12-06 14:40:38 -05:00
static_assert ( TIsAutoConvertible < FromDataType , ToDataType > : : Value ,
" Tried to create an auto converter node between two types we can't static_cast between. " ) ;
2020-08-26 14:16:05 -04:00
public :
2021-09-13 14:14:37 -04:00
static const FVertexName & GetInputName ( )
2020-08-26 14:16:05 -04:00
{
2021-09-13 14:14:37 -04:00
static const FVertexName InputName = GetMetasoundDataTypeName < FromDataType > ( ) ;
2020-08-26 14:16:05 -04:00
return InputName ;
}
2021-09-13 14:14:37 -04:00
static const FVertexName & GetOutputName ( )
2020-08-26 14:16:05 -04:00
{
2021-09-13 14:14:37 -04:00
static const FVertexName OutputName = GetMetasoundDataTypeName < ToDataType > ( ) ;
2020-08-26 14:16:05 -04:00
return OutputName ;
}
2020-11-24 15:24:29 -04:00
static FVertexInterface DeclareVertexInterface ( )
{
2022-02-10 18:36:47 -05:00
static const FText InputDesc = METASOUND_LOCTEXT_FORMAT ( " AutoConvDisplayNamePatternFrom " , " Input {0} value. " , GetMetasoundDataTypeDisplayText < FromDataType > ( ) ) ;
static const FText OutputDesc = METASOUND_LOCTEXT_FORMAT ( " AutoConvDisplayNamePatternTo " , " Output {0} value. " , GetMetasoundDataTypeDisplayText < ToDataType > ( ) ) ;
2021-12-06 14:40:38 -05:00
2020-11-24 15:24:29 -04:00
return FVertexInterface (
FInputVertexInterface (
2022-03-31 16:49:59 -04:00
TInputDataVertex < FromDataType > ( GetInputName ( ) , FDataVertexMetadata { InputDesc } )
2020-11-24 15:24:29 -04:00
) ,
FOutputVertexInterface (
2022-03-31 16:49:59 -04:00
TOutputDataVertex < ToDataType > ( GetOutputName ( ) , FDataVertexMetadata { OutputDesc } )
2020-11-24 15:24:29 -04:00
)
) ;
}
2021-01-28 19:02:51 -04:00
static const FNodeClassMetadata & GetAutoConverterNodeMetadata ( )
2020-11-24 15:24:29 -04:00
{
2021-01-28 19:02:51 -04:00
auto InitNodeInfo = [ ] ( ) - > FNodeClassMetadata
2020-11-24 15:24:29 -04:00
{
2021-01-23 12:59:01 -04:00
FNodeDisplayStyle DisplayStyle ;
DisplayStyle . bShowName = false ;
2021-12-06 14:40:38 -05:00
DisplayStyle . ImageName = TEXT ( " MetasoundEditor.Graph.Node.Conversion " ) ;
2021-01-23 12:59:01 -04:00
DisplayStyle . bShowInputNames = false ;
DisplayStyle . bShowOutputNames = false ;
2021-12-06 14:40:38 -05:00
const FText FromTypeText = GetMetasoundDataTypeDisplayText < FromDataType > ( ) ;
const FText ToTypeText = GetMetasoundDataTypeDisplayText < ToDataType > ( ) ;
2021-01-28 19:02:51 -04:00
FNodeClassMetadata Info ;
2021-02-08 15:58:01 -04:00
Info . ClassName = { TEXT ( " Convert " ) , GetMetasoundDataTypeName < ToDataType > ( ) , GetMetasoundDataTypeName < FromDataType > ( ) } ;
2020-11-24 15:24:29 -04:00
Info . MajorVersion = 1 ;
Info . MinorVersion = 0 ;
2022-02-10 18:36:47 -05:00
Info . DisplayName = METASOUND_LOCTEXT_FORMAT ( " Metasound_AutoConverterNodeDisplayNameFormat " , " {0} to {1} " , FromTypeText , ToTypeText ) ;
Info . Description = METASOUND_LOCTEXT_FORMAT ( " Metasound_AutoConverterNodeDescriptionNameFormat " , " Converts from {0} to {1}. " , FromTypeText , ToTypeText ) ;
2020-11-24 15:24:29 -04:00
Info . Author = PluginAuthor ;
2021-01-23 12:59:01 -04:00
Info . DisplayStyle = DisplayStyle ;
2020-11-24 15:24:29 -04:00
Info . PromptIfMissing = PluginNodeMissingPrompt ;
Info . DefaultInterface = DeclareVertexInterface ( ) ;
2021-08-09 15:13:40 -04:00
Info . CategoryHierarchy . Emplace ( NodeCategories : : Conversions ) ;
2021-12-06 14:40:38 -05:00
if ( TEnumTraits < FromDataType > : : bIsEnum | | TEnumTraits < ToDataType > : : bIsEnum )
{
Info . CategoryHierarchy . Emplace ( NodeCategories : : EnumConversions ) ;
}
2021-08-09 15:13:40 -04:00
Info . Keywords =
{
2022-02-10 18:36:47 -05:00
METASOUND_LOCTEXT ( " MetasoundConvertKeyword " , " Convert " ) ,
2021-12-06 14:40:38 -05:00
GetMetasoundDataTypeDisplayText < FromDataType > ( ) ,
GetMetasoundDataTypeDisplayText < ToDataType > ( )
2021-08-09 15:13:40 -04:00
} ;
2020-11-24 15:24:29 -04:00
return Info ;
} ;
2021-01-28 19:02:51 -04:00
static const FNodeClassMetadata Info = InitNodeInfo ( ) ;
2020-11-24 15:24:29 -04:00
return Info ;
}
2020-08-26 14:16:05 -04:00
private :
2020-10-26 13:39:01 -04:00
/** FConverterOperator converts from "FromDataType" to "ToDataType" using
2021-12-06 14:40:38 -05:00
* a implicit conversion operators .
2020-10-26 13:39:01 -04:00
*/
2020-08-26 14:16:05 -04:00
class FConverterOperator : public TExecutableOperator < FConverterOperator >
{
2021-12-06 14:40:38 -05:00
public :
2020-08-26 14:16:05 -04:00
2021-12-06 14:40:38 -05:00
FConverterOperator ( TDataReadReference < FromDataType > InFromDataReference , TDataWriteReference < ToDataType > InToDataReference )
: FromData ( InFromDataReference )
, ToData ( InToDataReference )
{
2022-01-07 15:09:59 -05:00
Execute ( ) ;
2021-12-06 14:40:38 -05:00
}
virtual ~ FConverterOperator ( ) { }
virtual FDataReferenceCollection GetInputs ( ) const override
{
FDataReferenceCollection Inputs ;
Inputs . AddDataReadReference < FromDataType > ( GetInputName ( ) , FromData ) ;
return Inputs ;
}
virtual FDataReferenceCollection GetOutputs ( ) const override
{
FDataReferenceCollection Outputs ;
Outputs . AddDataReadReference < ToDataType > ( GetOutputName ( ) , ToData ) ;
return Outputs ;
}
void Execute ( )
{
// enum -> int32
if constexpr ( TIsAutoConvertible < FromDataType , ToDataType > : : bIsEnumToIntConversion )
2020-08-26 14:16:05 -04:00
{
2021-12-06 14:40:38 -05:00
// Convert from enum wrapper to inner enum type, then to int
typename TEnumTraits < FromDataType > : : InnerType InnerEnum = static_cast < typename TEnumTraits < FromDataType > : : InnerType > ( * FromData ) ;
* ToData = static_cast < ToDataType > ( InnerEnum ) ;
2020-08-26 14:16:05 -04:00
}
2021-12-06 14:40:38 -05:00
// int32 -> enum
else if constexpr ( TIsAutoConvertible < FromDataType , ToDataType > : : bIsIntToEnumConversion )
2020-08-26 14:16:05 -04:00
{
2021-12-06 14:40:38 -05:00
const int32 FromInt = * FromData ;
// Convert from int to inner enum type
typename TEnumTraits < ToDataType > : : InnerType InnerEnum = static_cast < typename TEnumTraits < ToDataType > : : InnerType > ( FromInt ) ;
2020-08-26 14:16:05 -04:00
2021-12-06 14:40:38 -05:00
// Update tracking for previous int value we tried to convert, used to prevent log spam if it's an invalid enum value
if ( FromInt ! = PreviousIntValueForEnumConversion )
{
PreviousIntValueForEnumConversion = FromInt ;
bHasLoggedInvalidEnum = false ;
}
2020-08-26 14:16:05 -04:00
2021-12-06 14:40:38 -05:00
// If int value is invalid for this enum, return enum default value
TOptional < FName > EnumName = ToDataType : : ToName ( InnerEnum ) ;
if ( ! EnumName . IsSet ( ) )
{
if ( ! bHasLoggedInvalidEnum )
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " Cannot convert int32 value '%d' to enum type '%s'. No valid corresponding enum value exists, so returning enum default value instead. " ) , FromInt , * GetMetasoundDataTypeDisplayText < ToDataType > ( ) . ToString ( ) ) ;
bHasLoggedInvalidEnum = true ;
}
* ToData = static_cast < ToDataType > ( TEnumTraits < ToDataType > : : DefaultValue ) ;
}
else
{
// Convert from inner enum type to int
* ToData = static_cast < ToDataType > ( InnerEnum ) ;
}
}
else
2020-08-26 14:16:05 -04:00
{
* ToData = static_cast < ToDataType > ( * FromData ) ;
}
2021-12-06 14:40:38 -05:00
}
2020-08-26 14:16:05 -04:00
private :
TDataReadReference < FromDataType > FromData ;
TDataWriteReference < ToDataType > ToData ;
2021-12-06 14:40:38 -05:00
// To prevent log spam, keep track of whether we've logged an invalid enum value being converted already
// and the previous int value (need both bool and int for the initial case)
bool bHasLoggedInvalidEnum = false ;
int32 PreviousIntValueForEnumConversion = 0 ;
2020-08-26 14:16:05 -04:00
} ;
2020-10-26 13:39:01 -04:00
/** FConverterOperatorFactory creates an operator which converts from
* " FromDataType " to " ToDataType " .
*/
2020-08-26 14:16:05 -04:00
class FCoverterOperatorFactory : public IOperatorFactory
{
public :
FCoverterOperatorFactory ( ) = default ;
2022-05-19 14:18:38 -04:00
virtual TUniquePtr < IOperator > CreateOperator ( const FBuildOperatorParams & InParams , FBuildResults & OutResults ) override
2020-08-26 14:16:05 -04:00
{
2020-11-04 14:26:37 -04:00
TDataWriteReference < ToDataType > WriteReference = TDataWriteReferenceFactory < ToDataType > : : CreateAny ( InParams . OperatorSettings ) ;
2020-10-26 13:39:01 -04:00
2021-09-13 14:14:37 -04:00
const FVertexName & InputName = GetInputName ( ) ;
2022-05-19 14:18:38 -04:00
const bool bContainsRef = InParams . InputData . IsVertexBound ( InputName ) ;
2021-05-28 14:09:45 -04:00
if ( bContainsRef )
2020-08-26 14:16:05 -04:00
{
2022-05-19 14:18:38 -04:00
TDataReadReference < FromDataType > ReadReference = InParams . InputData . GetDataReadReference < FromDataType > ( InputName ) ;
2021-05-28 14:09:45 -04:00
return MakeUnique < FConverterOperator > ( ReadReference , WriteReference ) ;
}
2020-10-26 13:39:01 -04:00
2021-05-28 14:09:45 -04:00
if constexpr ( TIsParsable < FromDataType > : : Value )
{
2020-11-04 14:26:37 -04:00
TDataReadReference < FromDataType > ReadReference = TDataReadReferenceFactory < FromDataType > : : CreateAny ( InParams . OperatorSettings ) ;
2020-10-26 13:39:01 -04:00
return MakeUnique < FConverterOperator > ( ReadReference , WriteReference ) ;
2020-08-26 14:16:05 -04:00
}
2021-05-28 14:09:45 -04:00
// Converter node requires parsable reference if input not connected. Report as an error.
if ( ensure ( InParams . Node . GetVertexInterface ( ) . ContainsInputVertex ( InputName ) ) )
{
FInputDataDestination Dest ( InParams . Node , InParams . Node . GetVertexInterface ( ) . GetInputVertex ( GetInputName ( ) ) ) ;
2022-05-10 16:51:39 -04:00
AddBuildError < FMissingInputDataReferenceError > ( OutResults . Errors , Dest ) ;
2021-05-28 14:09:45 -04:00
}
2020-10-26 13:39:01 -04:00
2021-05-28 14:09:45 -04:00
return TUniquePtr < IOperator > ( nullptr ) ;
2020-08-26 14:16:05 -04:00
}
} ;
public :
TAutoConverterNode ( const FNodeInitData & InInitData )
2021-01-28 19:02:51 -04:00
: FNode ( InInitData . InstanceName , InInitData . InstanceID , GetAutoConverterNodeMetadata ( ) )
2020-08-26 14:16:05 -04:00
, Interface ( DeclareVertexInterface ( ) )
, Factory ( MakeOperatorFactoryRef < FCoverterOperatorFactory > ( ) )
{
}
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 ;
} ;
2021-09-13 14:14:37 -04:00
} // namespace Metasound
2020-08-26 14:16:05 -04:00
# undef LOCTEXT_NAMESPACE