2021-01-13 10:48:59 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundFrontendTransform.h"
2021-06-23 20:08:21 -04:00
# include "Algo/Transform.h"
2022-08-19 12:14:31 -04:00
# include "Interfaces/MetasoundFrontendInterface.h"
2023-03-13 17:23:05 -04:00
# include "Interfaces/MetasoundFrontendInterfaceRegistry.h"
2022-08-16 16:19:43 -04:00
# include "MetasoundAccessPtr.h"
2021-07-27 15:36:03 -04:00
# include "MetasoundAssetBase.h"
2021-08-04 21:02:58 -04:00
# include "MetasoundFrontendDocument.h"
2022-08-16 16:19:43 -04:00
# include "MetasoundFrontendDocumentController.h"
2023-06-20 13:12:39 -04:00
# include "MetasoundFrontendDocumentIdGenerator.h"
2021-07-27 15:36:03 -04:00
# include "MetasoundFrontendRegistries.h"
2021-06-23 20:08:21 -04:00
# include "MetasoundFrontendSearchEngine.h"
2021-06-08 10:52:31 -04:00
# include "MetasoundLog.h"
2022-01-18 18:06:06 -05:00
# include "MetasoundTrace.h"
2021-06-16 11:21:13 -04:00
# include "Misc/App.h"
2021-01-13 10:48:59 -04:00
namespace Metasound
{
namespace Frontend
{
2022-03-16 19:27:27 -04:00
namespace DocumentTransform
{
2023-10-11 18:37:59 -04:00
void LogAutoUpdateWarning ( const FString & LogMessage )
{
// These should eventually move back to warning on cook
// but are temporarily downgraded to prevent
// warnings on things like unused test content from
// blocking code checkins
if ( IsRunningCookCommandlet ( ) )
{
UE_LOG ( LogMetaSound , Display , TEXT ( " %s " ) , * LogMessage ) ;
}
else
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " %s " ) , * LogMessage ) ;
}
}
2022-03-16 19:27:27 -04:00
# if WITH_EDITOR
2022-03-17 16:56:13 -04:00
FGetNodeDisplayNameProjection NodeDisplayNameProjection ;
2022-03-16 19:27:27 -04:00
2022-03-17 16:56:13 -04:00
void RegisterNodeDisplayNameProjection ( FGetNodeDisplayNameProjection & & InNameProjection )
2022-03-16 19:27:27 -04:00
{
2022-03-17 16:56:13 -04:00
NodeDisplayNameProjection = MoveTemp ( InNameProjection ) ;
2022-03-16 19:27:27 -04:00
}
2022-03-17 16:56:13 -04:00
FGetNodeDisplayNameProjectionRef GetNodeDisplayNameProjection ( )
2022-03-16 19:27:27 -04:00
{
2022-03-17 16:56:13 -04:00
return NodeDisplayNameProjection ;
2022-03-16 19:27:27 -04:00
}
# endif // WITH_EDITOR
} // namespace DocumentTransform
2022-08-16 16:19:43 -04:00
bool IDocumentTransform : : Transform ( FMetasoundFrontendDocument & InOutDocument ) const
{
FDocumentAccessPtr DocAccessPtr = MakeAccessPtr < FDocumentAccessPtr > ( InOutDocument . AccessPoint , InOutDocument ) ;
return Transform ( FDocumentController : : CreateDocumentHandle ( DocAccessPtr ) ) ;
}
2022-03-16 19:27:27 -04:00
2023-09-06 12:54:02 -04:00
bool INodeTransform : : Transform ( const FGuid & InNodeID , FMetaSoundFrontendDocumentBuilder & OutBuilder ) const
2023-09-05 17:54:02 -04:00
{
return false ;
}
bool INodeTransform : : Transform ( FMetasoundFrontendNode & InOutNode ) const
{
return false ;
}
2021-11-22 15:55:50 -05:00
FModifyRootGraphInterfaces : : FModifyRootGraphInterfaces ( const TArray < FMetasoundFrontendInterface > & InInterfacesToRemove , const TArray < FMetasoundFrontendInterface > & InInterfacesToAdd )
: InterfacesToRemove ( InInterfacesToRemove )
, InterfacesToAdd ( InInterfacesToAdd )
2021-06-23 20:08:21 -04:00
{
2021-11-22 15:55:50 -05:00
Init ( ) ;
}
FModifyRootGraphInterfaces : : FModifyRootGraphInterfaces ( const TArray < FMetasoundFrontendVersion > & InInterfaceVersionsToRemove , const TArray < FMetasoundFrontendVersion > & InInterfaceVersionsToAdd )
{
Algo : : Transform ( InInterfaceVersionsToRemove , InterfacesToRemove , [ ] ( const FMetasoundFrontendVersion & Version )
{
FMetasoundFrontendInterface Interface ;
const bool bFromInterfaceFound = IInterfaceRegistry : : Get ( ) . FindInterface ( GetInterfaceRegistryKey ( Version ) , Interface ) ;
if ( ! ensureAlways ( bFromInterfaceFound ) )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Failed to find interface '%s' to remove " ) , * Version . ToString ( ) ) ;
}
return Interface ;
} ) ;
Algo : : Transform ( InInterfaceVersionsToAdd , InterfacesToAdd , [ ] ( const FMetasoundFrontendVersion & Version )
{
FMetasoundFrontendInterface Interface ;
const bool bToInterfaceFound = IInterfaceRegistry : : Get ( ) . FindInterface ( GetInterfaceRegistryKey ( Version ) , Interface ) ;
if ( ! ensureAlways ( bToInterfaceFound ) )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Failed to find interface '%s' to add " ) , * Version . ToString ( ) ) ;
}
return Interface ;
} ) ;
Init ( ) ;
}
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2021-11-22 15:55:50 -05:00
void FModifyRootGraphInterfaces : : SetDefaultNodeLocations ( bool bInSetDefaultNodeLocations )
{
bSetDefaultNodeLocations = bInSetDefaultNodeLocations ;
}
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2021-11-22 15:55:50 -05:00
2021-12-10 20:37:31 -05:00
void FModifyRootGraphInterfaces : : SetNamePairingFunction ( const TFunction < bool ( FName , FName ) > & InNamePairingFunction )
2021-11-22 15:55:50 -05:00
{
2021-12-10 20:37:31 -05:00
// Reinit required to rebuild list of pairs
Init ( & InNamePairingFunction ) ;
}
2023-05-04 18:12:21 -04:00
bool FModifyRootGraphInterfaces : : AddMissingVertices ( FGraphHandle GraphHandle ) const
{
for ( const FInputData & InputData : InputsToAdd )
{
const FMetasoundFrontendClassInput & InputToAdd = InputData . Input ;
GraphHandle - > AddInputVertex ( InputToAdd ) ;
}
for ( const FOutputData & OutputData : OutputsToAdd )
{
const FMetasoundFrontendClassOutput & OutputToAdd = OutputData . Output ;
GraphHandle - > AddOutputVertex ( OutputToAdd ) ;
}
return ! InputsToAdd . IsEmpty ( ) | | ! OutputsToAdd . IsEmpty ( ) ;
}
2021-12-10 20:37:31 -05:00
void FModifyRootGraphInterfaces : : Init ( const TFunction < bool ( FName , FName ) > * InNamePairingFunction )
{
InputsToRemove . Reset ( ) ;
InputsToAdd . Reset ( ) ;
OutputsToRemove . Reset ( ) ;
OutputsToAdd . Reset ( ) ;
PairedInputs . Reset ( ) ;
PairedOutputs . Reset ( ) ;
2021-11-22 15:55:50 -05:00
for ( const FMetasoundFrontendInterface & FromInterface : InterfacesToRemove )
{
InputsToRemove . Append ( FromInterface . Inputs ) ;
OutputsToRemove . Append ( FromInterface . Outputs ) ;
}
2022-02-25 09:45:49 -05:00
// This function combines all the inputs of all interfaces into one input list and ptrs to their originating interfaces.
// The interface ptr will be used to query the interface for required validations on inputs. Interfaces define required inputs (and possibly other validation requirements).
2021-11-22 15:55:50 -05:00
for ( const FMetasoundFrontendInterface & ToInterface : InterfacesToAdd )
{
2022-02-25 09:45:49 -05:00
TArray < FInputData > NewInputDataArray ;
for ( const FMetasoundFrontendClassInput & Input : ToInterface . Inputs )
{
FInputData NewData ;
NewData . Input = Input ;
NewData . InputInterface = & ToInterface ;
NewInputDataArray . Add ( NewData ) ;
}
InputsToAdd . Append ( NewInputDataArray ) ;
TArray < FOutputData > NewOutputDataArray ;
for ( const FMetasoundFrontendClassOutput & Output : ToInterface . Outputs )
{
FOutputData NewData ;
NewData . Output = Output ;
NewData . OutputInterface = & ToInterface ;
NewOutputDataArray . Add ( NewData ) ;
}
OutputsToAdd . Append ( NewOutputDataArray ) ;
2021-11-22 15:55:50 -05:00
}
2021-06-23 20:08:21 -04:00
// Iterate in reverse to allow removal from `InputsToAdd`
for ( int32 AddIndex = InputsToAdd . Num ( ) - 1 ; AddIndex > = 0 ; AddIndex - - )
{
2022-02-25 09:45:49 -05:00
const FMetasoundFrontendClassVertex & VertexToAdd = InputsToAdd [ AddIndex ] . Input ;
2021-06-23 20:08:21 -04:00
2021-12-10 20:37:31 -05:00
const int32 RemoveIndex = InputsToRemove . IndexOfByPredicate ( [ & ] ( const FMetasoundFrontendClassVertex & VertexToRemove )
2021-11-22 15:55:50 -05:00
{
2021-12-10 20:37:31 -05:00
if ( VertexToAdd . TypeName ! = VertexToRemove . TypeName )
{
return false ;
}
if ( InNamePairingFunction & & * InNamePairingFunction )
{
return ( * InNamePairingFunction ) ( VertexToAdd . Name , VertexToRemove . Name ) ;
}
FName ParamA ;
FName ParamB ;
FName Namespace ;
VertexToAdd . SplitName ( Namespace , ParamA ) ;
VertexToRemove . SplitName ( Namespace , ParamB ) ;
return ParamA = = ParamB ;
2021-11-22 15:55:50 -05:00
} ) ;
2021-06-23 20:08:21 -04:00
if ( INDEX_NONE ! = RemoveIndex )
{
2022-02-25 09:45:49 -05:00
PairedInputs . Add ( FVertexPair { InputsToRemove [ RemoveIndex ] , InputsToAdd [ AddIndex ] . Input } ) ;
2021-06-23 20:08:21 -04:00
InputsToRemove . RemoveAtSwap ( RemoveIndex ) ;
InputsToAdd . RemoveAtSwap ( AddIndex ) ;
}
}
// Iterate in reverse to allow removal from `OutputsToAdd`
for ( int32 AddIndex = OutputsToAdd . Num ( ) - 1 ; AddIndex > = 0 ; AddIndex - - )
{
2022-02-25 09:45:49 -05:00
const FMetasoundFrontendClassVertex & VertexToAdd = OutputsToAdd [ AddIndex ] . Output ;
2021-06-23 20:08:21 -04:00
2021-12-10 20:37:31 -05:00
const int32 RemoveIndex = OutputsToRemove . IndexOfByPredicate ( [ & ] ( const FMetasoundFrontendClassVertex & VertexToRemove )
{
if ( VertexToAdd . TypeName ! = VertexToRemove . TypeName )
2021-06-23 20:08:21 -04:00
{
2021-12-10 20:37:31 -05:00
return false ;
}
if ( InNamePairingFunction & & * InNamePairingFunction )
{
return ( * InNamePairingFunction ) ( VertexToAdd . Name , VertexToRemove . Name ) ;
}
FName ParamA ;
FName ParamB ;
FName Namespace ;
VertexToAdd . SplitName ( Namespace , ParamA ) ;
VertexToRemove . SplitName ( Namespace , ParamB ) ;
return ParamA = = ParamB ;
} ) ;
2021-06-23 20:08:21 -04:00
if ( INDEX_NONE ! = RemoveIndex )
{
2023-05-08 15:47:42 -04:00
PairedOutputs . Add ( FVertexPair { OutputsToRemove [ RemoveIndex ] , OutputsToAdd [ AddIndex ] . Output } ) ;
2021-06-23 20:08:21 -04:00
OutputsToRemove . RemoveAtSwap ( RemoveIndex ) ;
OutputsToAdd . RemoveAtSwap ( AddIndex ) ;
}
2023-05-04 18:12:21 -04:00
}
2021-06-23 20:08:21 -04:00
}
2023-05-04 18:12:21 -04:00
bool FModifyRootGraphInterfaces : : RemoveUnsupportedVertices ( FGraphHandle GraphHandle ) const
2021-06-23 20:08:21 -04:00
{
// Remove unsupported inputs
for ( const FMetasoundFrontendClassVertex & InputToRemove : InputsToRemove )
{
2021-11-22 15:55:50 -05:00
if ( const FMetasoundFrontendClassInput * ClassInput = GraphHandle - > FindClassInputWithName ( InputToRemove . Name ) . Get ( ) )
2021-06-23 20:08:21 -04:00
{
if ( FMetasoundFrontendClassInput : : IsFunctionalEquivalent ( * ClassInput , InputToRemove ) )
{
2021-11-22 15:55:50 -05:00
GraphHandle - > RemoveInputVertex ( InputToRemove . Name ) ;
2021-06-23 20:08:21 -04:00
}
}
}
2023-05-04 18:12:21 -04:00
// Remove unsupported outputs
2021-06-23 20:08:21 -04:00
for ( const FMetasoundFrontendClassVertex & OutputToRemove : OutputsToRemove )
{
2021-11-22 15:55:50 -05:00
if ( const FMetasoundFrontendClassOutput * ClassOutput = GraphHandle - > FindClassOutputWithName ( OutputToRemove . Name ) . Get ( ) )
2021-06-23 20:08:21 -04:00
{
if ( FMetasoundFrontendClassOutput : : IsFunctionalEquivalent ( * ClassOutput , OutputToRemove ) )
{
2021-11-22 15:55:50 -05:00
GraphHandle - > RemoveOutputVertex ( OutputToRemove . Name ) ;
2021-06-23 20:08:21 -04:00
}
}
}
2023-05-04 18:12:21 -04:00
return ! InputsToRemove . IsEmpty ( ) | | ! OutputsToRemove . IsEmpty ( ) ;
}
2021-08-04 21:02:58 -04:00
2023-05-04 18:12:21 -04:00
bool FModifyRootGraphInterfaces : : SwapPairedVertices ( FGraphHandle GraphHandle ) const
{
2021-06-23 20:08:21 -04:00
for ( const FVertexPair & InputPair : PairedInputs )
{
const FMetasoundFrontendClassVertex & OriginalVertex = InputPair . Get < 0 > ( ) ;
FMetasoundFrontendClassInput NewVertex = InputPair . Get < 1 > ( ) ;
// Cache off node locations and connections to push to new node
TMap < FGuid , FVector2D > Locations ;
TArray < FInputHandle > ConnectedInputs ;
2021-11-22 15:55:50 -05:00
if ( const FMetasoundFrontendClassInput * ClassInput = GraphHandle - > FindClassInputWithName ( OriginalVertex . Name ) . Get ( ) )
2021-06-23 20:08:21 -04:00
{
if ( FMetasoundFrontendVertex : : IsFunctionalEquivalent ( * ClassInput , OriginalVertex ) )
{
NewVertex . DefaultLiteral = ClassInput - > DefaultLiteral ;
2021-11-22 15:55:50 -05:00
NewVertex . NodeID = ClassInput - > NodeID ;
FNodeHandle OriginalInputNode = GraphHandle - > GetInputNodeWithName ( OriginalVertex . Name ) ;
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2021-06-23 20:08:21 -04:00
Locations = OriginalInputNode - > GetNodeStyle ( ) . Display . Locations ;
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2021-06-23 20:08:21 -04:00
2021-10-12 21:21:22 -04:00
FOutputHandle OriginalInputNodeOutput = OriginalInputNode - > GetOutputWithVertexName ( OriginalVertex . Name ) ;
ConnectedInputs = OriginalInputNodeOutput - > GetConnectedInputs ( ) ;
2021-11-22 15:55:50 -05:00
GraphHandle - > RemoveInputVertex ( OriginalVertex . Name ) ;
2021-06-23 20:08:21 -04:00
}
}
2021-11-22 15:55:50 -05:00
FNodeHandle NewInputNode = GraphHandle - > AddInputVertex ( NewVertex ) ;
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2021-06-23 20:08:21 -04:00
// Copy prior node locations
2021-09-13 14:14:37 -04:00
if ( ! Locations . IsEmpty ( ) )
2021-06-23 20:08:21 -04:00
{
FMetasoundFrontendNodeStyle Style = NewInputNode - > GetNodeStyle ( ) ;
Style . Display . Locations = Locations ;
NewInputNode - > SetNodeStyle ( Style ) ;
}
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2021-06-23 20:08:21 -04:00
// Copy prior node connections
2021-10-12 21:21:22 -04:00
FOutputHandle OutputHandle = NewInputNode - > GetOutputWithVertexName ( NewVertex . Name ) ;
for ( FInputHandle & ConnectedInput : ConnectedInputs )
2021-06-23 20:08:21 -04:00
{
2021-10-12 21:21:22 -04:00
OutputHandle - > Connect ( * ConnectedInput ) ;
2021-06-23 20:08:21 -04:00
}
}
// Swap paired outputs.
for ( const FVertexPair & OutputPair : PairedOutputs )
{
const FMetasoundFrontendClassVertex & OriginalVertex = OutputPair . Get < 0 > ( ) ;
2021-11-22 15:55:50 -05:00
FMetasoundFrontendClassVertex NewVertex = OutputPair . Get < 1 > ( ) ;
2021-06-23 20:08:21 -04:00
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2021-06-23 20:08:21 -04:00
// Cache off node locations to push to new node
// Default add output node to origin.
2022-02-10 15:07:39 -05:00
TMap < FGuid , FVector2D > Locations ;
2023-05-04 18:12:21 -04:00
Locations . Add ( FGuid ( ) , FVector2D { 0.f , 0.f } ) ;
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
FOutputHandle ConnectedOutput = IOutputController : : GetInvalidHandle ( ) ;
2021-11-22 15:55:50 -05:00
if ( const FMetasoundFrontendClassOutput * ClassOutput = GraphHandle - > FindClassOutputWithName ( OriginalVertex . Name ) . Get ( ) )
2021-06-23 20:08:21 -04:00
{
if ( FMetasoundFrontendVertex : : IsFunctionalEquivalent ( * ClassOutput , OriginalVertex ) )
{
2021-11-22 15:55:50 -05:00
NewVertex . NodeID = ClassOutput - > NodeID ;
2022-01-26 18:11:52 -05:00
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2022-01-26 18:11:52 -05:00
// Interface members do not serialize text to avoid localization
// mismatches between assets and interfaces defined in code.
NewVertex . Metadata . SetSerializeText ( false ) ;
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2022-01-26 18:11:52 -05:00
2021-11-22 15:55:50 -05:00
FNodeHandle OriginalOutputNode = GraphHandle - > GetOutputNodeWithName ( OriginalVertex . Name ) ;
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2021-06-23 20:08:21 -04:00
Locations = OriginalOutputNode - > GetNodeStyle ( ) . Display . Locations ;
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2021-10-12 21:21:22 -04:00
FInputHandle Input = OriginalOutputNode - > GetInputWithVertexName ( OriginalVertex . Name ) ;
ConnectedOutput = Input - > GetConnectedOutput ( ) ;
2021-11-22 15:55:50 -05:00
GraphHandle - > RemoveOutputVertex ( OriginalVertex . Name ) ;
2021-06-23 20:08:21 -04:00
}
}
2021-11-22 15:55:50 -05:00
FNodeHandle NewOutputNode = GraphHandle - > AddOutputVertex ( NewVertex ) ;
2021-06-23 20:08:21 -04:00
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2021-06-23 20:08:21 -04:00
if ( Locations . Num ( ) > 0 )
{
FMetasoundFrontendNodeStyle Style = NewOutputNode - > GetNodeStyle ( ) ;
Style . Display . Locations = Locations ;
NewOutputNode - > SetNodeStyle ( Style ) ;
}
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2021-06-23 20:08:21 -04:00
// Copy prior node connections
2021-10-12 21:21:22 -04:00
FInputHandle InputHandle = NewOutputNode - > GetInputWithVertexName ( NewVertex . Name ) ;
ConnectedOutput - > Connect ( * InputHandle ) ;
2021-06-23 20:08:21 -04:00
}
2023-05-04 18:12:21 -04:00
return ! PairedInputs . IsEmpty ( ) | | ! PairedOutputs . IsEmpty ( ) ;
}
bool FModifyRootGraphInterfaces : : Transform ( FDocumentHandle InDocument ) const
{
bool bDidEdit = false ;
FGraphHandle GraphHandle = InDocument - > GetRootGraph ( ) ;
if ( ensure ( GraphHandle - > IsValid ( ) ) )
{
bDidEdit | = UpdateInterfacesInternal ( InDocument ) ;
const bool bAddedVertices = AddMissingVertices ( GraphHandle ) ;
bDidEdit | = bAddedVertices ;
bDidEdit | = SwapPairedVertices ( GraphHandle ) ;
bDidEdit | = RemoveUnsupportedVertices ( GraphHandle ) ;
2023-05-10 20:28:39 -04:00
# if WITH_EDITORONLY_DATA
2023-05-08 15:47:42 -04:00
if ( bAddedVertices & & bSetDefaultNodeLocations )
2023-05-04 18:12:21 -04:00
{
UpdateAddedVertexNodePositions ( GraphHandle ) ;
}
2023-05-10 20:28:39 -04:00
# endif // WITH_EDITORONLY_DATA
2023-05-04 18:12:21 -04:00
}
2021-06-23 20:08:21 -04:00
return bDidEdit ;
}
2023-03-07 17:01:52 -05:00
bool FModifyRootGraphInterfaces : : Transform ( FMetasoundFrontendDocument & InOutDocument ) const
{
// TODO: Swap implementation to not use access pointers/controllers
FDocumentAccessPtr DocAccessPtr = MakeAccessPtr < FDocumentAccessPtr > ( InOutDocument . AccessPoint , InOutDocument ) ;
return Transform ( FDocumentController : : CreateDocumentHandle ( DocAccessPtr ) ) ;
}
2023-05-04 18:12:21 -04:00
bool FModifyRootGraphInterfaces : : UpdateInterfacesInternal ( FDocumentHandle DocumentHandle ) const
{
for ( const FMetasoundFrontendInterface & Interface : InterfacesToRemove )
{
DocumentHandle - > RemoveInterfaceVersion ( Interface . Version ) ;
}
for ( const FMetasoundFrontendInterface & Interface : InterfacesToAdd )
{
DocumentHandle - > AddInterfaceVersion ( Interface . Version ) ;
}
return ! InterfacesToRemove . IsEmpty ( ) | | ! InterfacesToAdd . IsEmpty ( ) ;
}
2023-05-10 20:28:39 -04:00
# if WITH_EDITORONLY_DATA
2023-05-04 18:12:21 -04:00
void FModifyRootGraphInterfaces : : UpdateAddedVertexNodePositions ( FGraphHandle GraphHandle ) const
{
auto SortAndPlaceMemberNodes = [ & GraphHandle ] ( EMetasoundFrontendClassType ClassType , TSet < FName > & AddedNames , TFunctionRef < int32 ( const FVertexName & ) > InGetSortOrder )
{
// Add graph member nodes by sort order
TSortedMap < int32 , FNodeHandle > SortOrderToName ;
GraphHandle - > IterateNodes ( [ & GraphHandle , & SortOrderToName , & InGetSortOrder ] ( FNodeHandle NodeHandle )
{
const int32 Index = InGetSortOrder ( NodeHandle - > GetNodeName ( ) ) ;
SortOrderToName . Add ( Index , NodeHandle ) ;
} , ClassType ) ;
// Prime the first location as an offset prior to an existing location (as provided by a swapped member)
// to avoid placing away from user's active area if possible.
FVector2D NextLocation = { 0.0f , 0.0f } ;
{
int32 NumBeforeDefined = 1 ;
2024-01-12 00:52:57 -05:00
for ( const TPair < int32 , FNodeHandle > & Pair : SortOrderToName ) //-V1078
2023-05-04 18:12:21 -04:00
{
const FConstNodeHandle & NodeHandle = Pair . Value ;
const FName NodeName = NodeHandle - > GetNodeName ( ) ;
if ( AddedNames . Contains ( NodeName ) )
{
NumBeforeDefined + + ;
}
else
{
const TMap < FGuid , FVector2D > & Locations = NodeHandle - > GetNodeStyle ( ) . Display . Locations ;
if ( ! Locations . IsEmpty ( ) )
{
for ( const TPair < FGuid , FVector2D > & Location : Locations )
{
NextLocation = Location . Value - ( NumBeforeDefined * DisplayStyle : : NodeLayout : : DefaultOffsetY ) ;
break ;
}
break ;
}
}
}
}
// Iterate through sorted map in sequence, slotting in new locations after existing swapped nodes with predefined locations.
2024-01-12 00:52:57 -05:00
for ( TPair < int32 , FNodeHandle > & Pair : SortOrderToName ) //-V1078
2023-05-04 18:12:21 -04:00
{
FNodeHandle & NodeHandle = Pair . Value ;
const FName NodeName = NodeHandle - > GetNodeName ( ) ;
if ( AddedNames . Contains ( NodeName ) )
{
FMetasoundFrontendNodeStyle NewStyle = NodeHandle - > GetNodeStyle ( ) ;
NewStyle . Display . Locations . Add ( FGuid ( ) , NextLocation ) ;
NextLocation + = DisplayStyle : : NodeLayout : : DefaultOffsetY ;
NodeHandle - > SetNodeStyle ( NewStyle ) ;
}
else
{
for ( const TPair < FGuid , FVector2D > & Location : NodeHandle - > GetNodeStyle ( ) . Display . Locations )
{
NextLocation = Location . Value + DisplayStyle : : NodeLayout : : DefaultOffsetY ;
}
}
}
} ;
// Sort/Place Inputs
{
TSet < FName > AddedNames ;
Algo : : Transform ( InputsToAdd , AddedNames , [ ] ( const FInputData & InputData ) { return InputData . Input . Name ; } ) ;
auto GetInputSortOrder = [ & GraphHandle ] ( const FVertexName & InVertexName ) { return GraphHandle - > GetSortOrderIndexForInput ( InVertexName ) ; } ;
SortAndPlaceMemberNodes ( EMetasoundFrontendClassType : : Input , AddedNames , GetInputSortOrder ) ;
}
// Sort/Place Outputs
{
TSet < FName > AddedNames ;
Algo : : Transform ( OutputsToAdd , AddedNames , [ ] ( const FOutputData & OutputData ) { return OutputData . Output . Name ; } ) ;
auto GetOutputSortOrder = [ & GraphHandle ] ( const FVertexName & InVertexName ) { return GraphHandle - > GetSortOrderIndexForOutput ( InVertexName ) ; } ;
SortAndPlaceMemberNodes ( EMetasoundFrontendClassType : : Output , AddedNames , GetOutputSortOrder ) ;
}
}
# endif // WITH_EDITOR
2021-11-22 15:55:50 -05:00
bool FUpdateRootGraphInterface : : Transform ( FDocumentHandle InDocument ) const
2021-01-13 10:48:59 -04:00
{
2021-06-23 20:08:21 -04:00
bool bDidEdit = false ;
2021-11-22 15:55:50 -05:00
if ( ! ensure ( InDocument - > IsValid ( ) ) )
2021-01-13 10:48:59 -04:00
{
2021-06-23 20:08:21 -04:00
return bDidEdit ;
}
2021-11-22 15:55:50 -05:00
// Find registered target interface.
FMetasoundFrontendInterface TargetInterface ;
bool bFoundTargetInterface = ISearchEngine : : Get ( ) . FindInterfaceWithHighestVersion ( InterfaceVersion . Name , TargetInterface ) ;
if ( ! bFoundTargetInterface )
2021-06-23 20:08:21 -04:00
{
2023-04-28 16:43:19 -04:00
UE_LOG ( LogMetaSound , Warning , TEXT ( " Could not check for interface updates. Target interface is not registered [InterfaceVersion:%s] when attempting to update root graph of asset (%s). Ensure that the module which registers the interface has been loaded before the asset is loaded. " ) , * InterfaceVersion . ToString ( ) , * OwningAssetName ) ;
2021-01-13 10:48:59 -04:00
return false ;
}
2021-11-22 15:55:50 -05:00
if ( TargetInterface . Version = = InterfaceVersion )
{
return false ;
}
2021-01-13 10:48:59 -04:00
2021-06-23 20:08:21 -04:00
// Attempt to upgrade
2021-11-22 15:55:50 -05:00
TArray < const IInterfaceRegistryEntry * > UpgradePath ;
GetUpdatePathForDocument ( InterfaceVersion , TargetInterface . Version , UpgradePath ) ;
return UpdateDocumentInterface ( UpgradePath , InDocument ) ;
2021-06-23 20:08:21 -04:00
}
2021-11-22 15:55:50 -05:00
void FUpdateRootGraphInterface : : GetUpdatePathForDocument ( const FMetasoundFrontendVersion & InCurrentVersion , const FMetasoundFrontendVersion & InTargetVersion , TArray < const IInterfaceRegistryEntry * > & OutUpgradePath ) const
2021-06-23 20:08:21 -04:00
{
if ( InCurrentVersion . Name = = InTargetVersion . Name )
{
2021-11-22 15:55:50 -05:00
// Get all associated registered interfaces
TArray < FMetasoundFrontendVersion > RegisteredVersions = ISearchEngine : : Get ( ) . FindAllRegisteredInterfacesWithName ( InTargetVersion . Name ) ;
2021-06-23 20:08:21 -04:00
// Filter registry entries that exist between current version and target version
auto FilterRegistryEntries = [ & InCurrentVersion , & InTargetVersion ] ( const FMetasoundFrontendVersion & InVersion )
2021-01-13 10:48:59 -04:00
{
2021-06-23 20:08:21 -04:00
const bool bIsGreaterThanCurrent = InVersion . Number > InCurrentVersion . Number ;
const bool bIsLessThanOrEqualToTarget = InVersion . Number < = InTargetVersion . Number ;
return bIsGreaterThanCurrent & & bIsLessThanOrEqualToTarget ;
} ;
RegisteredVersions = RegisteredVersions . FilterByPredicate ( FilterRegistryEntries ) ;
// sort registry entries to create an ordered upgrade path.
RegisteredVersions . Sort ( ) ;
// Get registry entries from registry keys.
auto GetRegistryEntry = [ ] ( const FMetasoundFrontendVersion & InVersion )
{
2021-11-22 15:55:50 -05:00
FInterfaceRegistryKey Key = GetInterfaceRegistryKey ( InVersion ) ;
return IInterfaceRegistry : : Get ( ) . FindInterfaceRegistryEntry ( Key ) ;
2021-06-23 20:08:21 -04:00
} ;
Algo : : Transform ( RegisteredVersions , OutUpgradePath , GetRegistryEntry ) ;
}
}
2021-11-22 15:55:50 -05:00
bool FUpdateRootGraphInterface : : UpdateDocumentInterface ( const TArray < const IInterfaceRegistryEntry * > & InUpgradePath , FDocumentHandle InDocument ) const
2021-06-23 20:08:21 -04:00
{
2021-11-22 15:55:50 -05:00
const FMetasoundFrontendVersionNumber * LastVersionUpdated = nullptr ;
for ( const IInterfaceRegistryEntry * Entry : InUpgradePath )
2021-06-23 20:08:21 -04:00
{
if ( ensure ( nullptr ! = Entry ) )
{
2021-11-22 15:55:50 -05:00
if ( Entry - > UpdateRootGraphInterface ( InDocument ) )
2021-03-31 00:23:34 -04:00
{
2021-11-22 15:55:50 -05:00
LastVersionUpdated = & Entry - > GetInterface ( ) . Version . Number ;
2021-03-31 00:23:34 -04:00
}
}
2021-06-23 20:08:21 -04:00
}
2021-11-22 15:55:50 -05:00
if ( LastVersionUpdated )
2021-06-23 20:08:21 -04:00
{
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
const FString AssetName = * InDocument - > GetRootGraphClass ( ) . Metadata . GetDisplayName ( ) . ToString ( ) ;
# else
const FString AssetName = * InDocument - > GetRootGraphClass ( ) . Metadata . GetClassName ( ) . ToString ( ) ;
# endif // !WITH_EDITOR
2021-11-22 15:55:50 -05:00
UE_LOG ( LogMetaSound , Display , TEXT ( " Asset '%s' interface '%s' updated: '%s' --> '%s' " ) ,
2022-02-10 15:07:39 -05:00
* AssetName ,
2021-11-23 19:07:34 -05:00
* InterfaceVersion . Name . ToString ( ) ,
2021-11-22 15:55:50 -05:00
* InterfaceVersion . Number . ToString ( ) ,
* LastVersionUpdated - > ToString ( ) ) ;
return true ;
2021-06-23 20:08:21 -04:00
}
2021-11-22 15:55:50 -05:00
return false ;
2021-01-13 10:48:59 -04:00
}
2021-05-28 14:09:45 -04:00
2021-07-27 15:36:03 -04:00
bool FAutoUpdateRootGraph : : Transform ( FDocumentHandle InDocument ) const
{
2022-01-18 18:06:06 -05:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( FAutoUpdateRootGraph : : Transform ) ;
2021-07-27 15:36:03 -04:00
bool bDidEdit = false ;
2021-08-18 15:16:57 -04:00
FMetasoundAssetBase * PresetReferencedMetaSoundAsset = nullptr ;
2021-07-27 15:36:03 -04:00
TArray < TPair < FNodeHandle , FMetasoundFrontendVersionNumber > > NodesToUpdate ;
FGraphHandle RootGraph = InDocument - > GetRootGraph ( ) ;
2022-02-09 12:52:07 -05:00
const bool bIsPreset = RootGraph - > GetGraphPresetOptions ( ) . bIsPreset ;
2021-07-27 15:36:03 -04:00
RootGraph - > IterateNodes ( [ & ] ( FNodeHandle NodeHandle )
{
using namespace Metasound : : Frontend ;
2021-10-12 21:21:22 -04:00
const FMetasoundFrontendClassMetadata & ClassMetadata = NodeHandle - > GetClassMetadata ( ) ;
2023-10-31 14:24:48 -04:00
const FNodeRegistryKey RegistryKey ( ClassMetadata ) ;
2021-10-12 21:21:22 -04:00
2023-10-31 14:24:48 -04:00
if ( FMetasoundAssetBase * ReferencedMetaSoundAsset = IMetaSoundAssetManager : : GetChecked ( ) . TryLoadAssetFromKey ( RegistryKey ) )
2021-10-12 21:21:22 -04:00
{
2023-10-31 14:24:48 -04:00
if ( bIsPreset )
{
PresetReferencedMetaSoundAsset = ReferencedMetaSoundAsset ;
}
}
else
{
if ( bIsPreset )
2021-11-24 15:45:43 -05:00
{
2022-01-27 00:45:28 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " Auto-Updating preset '%s' failed: Referenced class '%s' missing. " ) , * DebugAssetPath , * ClassMetadata . GetClassName ( ) . ToString ( ) ) ;
2023-10-31 14:24:48 -04:00
return ;
2021-11-24 15:45:43 -05:00
}
2021-10-12 21:21:22 -04:00
}
2021-08-09 15:13:40 -04:00
FClassInterfaceUpdates InterfaceUpdates ;
2022-01-31 15:53:54 -05:00
if ( ! NodeHandle - > CanAutoUpdate ( InterfaceUpdates ) )
2021-07-27 15:36:03 -04:00
{
return ;
}
2022-04-25 16:59:41 -04:00
// Check if a updated minor version exists.
FMetasoundFrontendClass ClassWithHighestMinorVersion ;
2023-10-31 14:24:48 -04:00
const bool bFoundClassInSearchEngine = Frontend : : ISearchEngine : : Get ( ) . FindClassWithHighestMinorVersion ( ClassMetadata . GetClassName ( ) , ClassMetadata . GetVersion ( ) . Major , ClassWithHighestMinorVersion ) ;
2022-04-25 16:59:41 -04:00
if ( bFoundClassInSearchEngine & & ( ClassWithHighestMinorVersion . Metadata . GetVersion ( ) > ClassMetadata . GetVersion ( ) ) )
2021-07-27 15:36:03 -04:00
{
2022-04-25 16:59:41 -04:00
const FMetasoundFrontendVersionNumber UpdateVersion = ClassWithHighestMinorVersion . Metadata . GetVersion ( ) ;
2022-01-27 00:45:28 -05:00
UE_LOG ( LogMetaSound , Display , TEXT ( " Auto-Updating '%s' node class '%s': Newer version '%s' found. " ) , * DebugAssetPath , * ClassMetadata . GetClassName ( ) . ToString ( ) , * UpdateVersion . ToString ( ) ) ;
2022-01-31 15:53:54 -05:00
NodesToUpdate . Add ( TPair < FNodeHandle , FMetasoundFrontendVersionNumber > ( NodeHandle , UpdateVersion ) ) ;
2021-07-27 15:36:03 -04:00
}
2021-08-09 15:13:40 -04:00
else if ( InterfaceUpdates . ContainsChanges ( ) )
2021-07-27 15:36:03 -04:00
{
2022-04-25 16:59:41 -04:00
const FMetasoundFrontendVersionNumber UpdateVersion = ClassMetadata . GetVersion ( ) ;
2022-01-27 00:45:28 -05:00
UE_LOG ( LogMetaSound , Display , TEXT ( " Auto-Updating '%s' node class '%s (%s)': Interface change detected. " ) , * DebugAssetPath , * ClassMetadata . GetClassName ( ) . ToString ( ) , * UpdateVersion . ToString ( ) ) ;
2022-01-31 15:53:54 -05:00
NodesToUpdate . Add ( TPair < FNodeHandle , FMetasoundFrontendVersionNumber > ( NodeHandle , UpdateVersion ) ) ;
2021-07-27 15:36:03 -04:00
}
2022-01-31 15:53:54 -05:00
// Only update the node at this point if editor data is loaded. If it isn't and their are no interface
// changes but auto-update returned it was eligible, then the auto-update only contains cosmetic changes.
# if WITH_EDITORONLY_DATA
else
{
2022-04-25 16:59:41 -04:00
NodesToUpdate . Add ( TPair < FNodeHandle , FMetasoundFrontendVersionNumber > ( NodeHandle , ClassMetadata . GetVersion ( ) ) ) ;
2022-01-31 15:53:54 -05:00
}
# endif // WITH_EDITORONLY_DATA
2021-07-27 15:36:03 -04:00
} , EMetasoundFrontendClassType : : External ) ;
2021-08-18 15:16:57 -04:00
if ( PresetReferencedMetaSoundAsset )
2021-07-27 15:36:03 -04:00
{
2022-02-09 12:52:07 -05:00
if ( bIsPreset )
2021-10-12 21:21:22 -04:00
{
2021-11-24 15:45:43 -05:00
bDidEdit | = FRebuildPresetRootGraph ( PresetReferencedMetaSoundAsset - > GetDocumentHandle ( ) ) . Transform ( InDocument ) ;
if ( bDidEdit )
2021-10-12 21:21:22 -04:00
{
2022-02-09 12:52:07 -05:00
FMetasoundFrontendClassMetadata PresetMetadata = InDocument - > GetRootGraphClass ( ) . Metadata ;
PresetMetadata . SetType ( EMetasoundFrontendClassType : : External ) ;
2023-10-12 16:37:45 -04:00
const FNodeRegistryKey RegistryKey ( PresetMetadata ) ;
2022-02-09 12:52:07 -05:00
FMetasoundAssetBase * PresetMetaSoundAsset = IMetaSoundAssetManager : : GetChecked ( ) . TryLoadAssetFromKey ( RegistryKey ) ;
if ( ensure ( PresetMetaSoundAsset ) )
2021-11-24 15:45:43 -05:00
{
2022-02-09 12:52:07 -05:00
PresetMetaSoundAsset - > ConformObjectDataToInterfaces ( ) ;
2021-11-24 15:45:43 -05:00
}
2022-01-19 19:32:01 -05:00
InDocument - > RemoveUnreferencedDependencies ( ) ;
InDocument - > SynchronizeDependencyMetadata ( ) ;
2021-10-12 21:21:22 -04:00
}
}
2021-07-27 15:36:03 -04:00
}
else
{
2022-03-01 21:25:04 -05:00
using FVertexNameAndType = INodeController : : FVertexNameAndType ;
2021-11-18 14:37:34 -05:00
bDidEdit | = ! NodesToUpdate . IsEmpty ( ) ;
2021-07-27 15:36:03 -04:00
for ( const TPair < FNodeHandle , FMetasoundFrontendVersionNumber > & Pair : NodesToUpdate )
{
FNodeHandle ExistingNode = Pair . Key ;
2021-08-09 15:13:40 -04:00
FMetasoundFrontendVersionNumber InitialVersion = ExistingNode - > GetClassMetadata ( ) . GetVersion ( ) ;
2022-03-01 21:25:04 -05:00
TArray < FVertexNameAndType > DisconnectedInputs ;
TArray < FVertexNameAndType > DisconnectedOutputs ;
FNodeHandle NewNode = ExistingNode - > ReplaceWithVersion ( Pair . Value , & DisconnectedInputs , & DisconnectedOutputs ) ;
// Log warnings for any disconnections
if ( bLogWarningOnDroppedConnection )
{
if ( ( DisconnectedInputs . Num ( ) > 0 ) | | ( DisconnectedOutputs . Num ( ) > 0 ) )
{
const FString NodeClassName = NewNode - > GetClassMetadata ( ) . GetClassName ( ) . ToString ( ) ;
const FString NewClassVersion = Pair . Value . ToString ( ) ;
for ( const FVertexNameAndType & InputPin : DisconnectedInputs )
{
2023-10-11 18:37:59 -04:00
DocumentTransform : : LogAutoUpdateWarning ( FString : : Printf ( TEXT ( " Auto-Updating '%s' node class '%s (%s)': Previously connected input '%s' with data type '%s' no longer exists. " ) , * DebugAssetPath , * NodeClassName , * NewClassVersion , * InputPin . Get < 0 > ( ) . ToString ( ) , * InputPin . Get < 1 > ( ) . ToString ( ) ) ) ;
2022-03-01 21:25:04 -05:00
}
for ( const FVertexNameAndType & OutputPin : DisconnectedOutputs )
{
2023-10-11 18:37:59 -04:00
DocumentTransform : : LogAutoUpdateWarning ( FString : : Printf ( TEXT ( " Auto-Updating '%s' node class '%s (%s)': Previously connected output '%s' with data type '%s' no longer exists. " ) , * DebugAssetPath , * NodeClassName , * NewClassVersion , * OutputPin . Get < 0 > ( ) . ToString ( ) , * OutputPin . Get < 1 > ( ) . ToString ( ) ) ) ;
2022-03-01 21:25:04 -05:00
}
}
}
2021-07-27 15:36:03 -04:00
}
2021-11-24 15:45:43 -05:00
2022-01-19 19:32:01 -05:00
InDocument - > RemoveUnreferencedDependencies ( ) ;
InDocument - > SynchronizeDependencyMetadata ( ) ;
2021-07-27 15:36:03 -04:00
}
return bDidEdit ;
}
bool FRebuildPresetRootGraph : : Transform ( FDocumentHandle InDocument ) const
{
2023-08-10 20:04:23 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( Metasound : : Frontend : : FRebuildPresetRootGraph : : Transform ) ;
2022-02-09 12:52:07 -05:00
FGraphHandle PresetGraphHandle = InDocument - > GetRootGraph ( ) ;
if ( ! ensure ( PresetGraphHandle - > IsValid ( ) ) )
2021-07-27 15:36:03 -04:00
{
return false ;
}
2021-10-12 21:21:22 -04:00
// Callers of this transform should check that the graph is supposed to
// be managed externally before calling this transform. If a scenario
// arises where this transform is used outside of AutoUpdate, then this
// early exist should be removed as it's mostly here to protect against
// accidental manipulation of metasound graphs.
2022-02-09 12:52:07 -05:00
if ( ! ensure ( PresetGraphHandle - > GetGraphPresetOptions ( ) . bIsPreset ) )
2021-07-27 15:36:03 -04:00
{
return false ;
}
FConstGraphHandle ReferencedGraphHandle = ReferencedDocument - > GetRootGraph ( ) ;
if ( ! ensure ( ReferencedGraphHandle - > IsValid ( ) ) )
{
return false ;
}
2021-10-12 21:21:22 -04:00
// Determine the inputs and outputs needed in the wrapping graph. Also
// cache any exiting literals that have been set on the wrapping graph.
2022-02-09 12:52:07 -05:00
TSet < FName > InputsInheritingDefault ;
2023-08-16 13:21:17 -04:00
TArray < FMetasoundFrontendClassInput > ClassInputs = GenerateRequiredClassInputs ( InDocument , PresetGraphHandle , InputsInheritingDefault ) ;
TArray < FMetasoundFrontendClassOutput > ClassOutputs = GenerateRequiredClassOutputs ( InDocument , PresetGraphHandle ) ;
2021-07-27 15:36:03 -04:00
2021-10-12 21:21:22 -04:00
FGuid PresetNodeID ;
2022-02-09 12:52:07 -05:00
PresetGraphHandle - > IterateConstNodes ( [ InPresetNodeID = & PresetNodeID ] ( FConstNodeHandle PresetNodeHandle )
2021-07-27 15:36:03 -04:00
{
2021-10-12 21:21:22 -04:00
* InPresetNodeID = PresetNodeHandle - > GetID ( ) ;
2023-03-08 14:25:19 -05:00
} , EMetasoundFrontendClassType : : External ) ;
2023-06-20 13:12:39 -04:00
2023-03-08 14:25:19 -05:00
if ( ! PresetNodeID . IsValid ( ) )
{
2023-06-20 13:12:39 -04:00
// This ID was originally being set to FGuid::NewGuid.
// If you were reliant on that ID, please resave the asset so it is serialized with a valid ID
PresetNodeID = InDocument - > GetRootGraph ( ) - > GetClassID ( ) ;
2023-03-08 14:25:19 -05:00
}
2021-07-27 15:36:03 -04:00
2021-10-12 21:21:22 -04:00
// Clear the root graph so it can be rebuilt.
2022-02-09 12:52:07 -05:00
PresetGraphHandle - > ClearGraph ( ) ;
2021-07-27 15:36:03 -04:00
2021-11-22 15:55:50 -05:00
// Ensure preset interfaces match those found in referenced graph. Referenced graph is assumed to be
// well-formed (i.e. all inputs/outputs/environment variables declared by interfaces are present, and
// of proper name & data type).
2021-12-10 20:37:31 -05:00
const TSet < FMetasoundFrontendVersion > & RefInterfaceVersions = ReferencedDocument - > GetInterfaceVersions ( ) ;
2021-11-22 15:55:50 -05:00
for ( const FMetasoundFrontendVersion & Version : RefInterfaceVersions )
{
InDocument - > AddInterfaceVersion ( Version ) ;
}
2021-10-12 21:21:22 -04:00
// Add referenced node
FMetasoundFrontendClassMetadata ReferencedClassMetadata = ReferencedGraphHandle - > GetGraphMetadata ( ) ;
2021-07-27 15:36:03 -04:00
// Swap type on look-up as it will be referenced as an externally defined class relative to the new Preset asset
ReferencedClassMetadata . SetType ( EMetasoundFrontendClassType : : External ) ;
2022-02-09 12:52:07 -05:00
FNodeHandle ReferencedNodeHandle = PresetGraphHandle - > AddNode ( ReferencedClassMetadata , PresetNodeID ) ;
2022-02-10 15:07:39 -05:00
2022-03-16 19:27:27 -04:00
# if WITH_EDITOR
2022-02-10 15:07:39 -05:00
// Set node location.
2021-07-27 15:36:03 -04:00
FMetasoundFrontendNodeStyle RefNodeStyle ;
2021-10-12 21:21:22 -04:00
// Offset to be to the right of input nodes
2022-03-02 22:11:18 -05:00
RefNodeStyle . Display . Locations . Add ( FGuid ( ) , DisplayStyle : : NodeLayout : : DefaultOffsetX ) ;
2021-07-27 15:36:03 -04:00
ReferencedNodeHandle - > SetNodeStyle ( RefNodeStyle ) ;
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2021-07-27 15:36:03 -04:00
2021-10-12 21:21:22 -04:00
// Connect parent graph to referenced graph
2022-02-09 12:52:07 -05:00
PresetGraphHandle - > SetInputsInheritingDefault ( MoveTemp ( InputsInheritingDefault ) ) ;
AddAndConnectInputs ( ClassInputs , PresetGraphHandle , ReferencedNodeHandle ) ;
AddAndConnectOutputs ( ClassOutputs , PresetGraphHandle , ReferencedNodeHandle ) ;
2021-07-27 15:36:03 -04:00
return true ;
}
2023-05-30 14:39:48 -04:00
FRebuildPresetRootGraph : : FRebuildPresetRootGraph ( const FMetasoundFrontendDocument & InReferencedDocument )
{
// TODO: Swap implementation to not use access pointers/controllers
ReferencedDocument = IDocumentController : : CreateDocumentHandle ( InReferencedDocument ) ;
}
bool FRebuildPresetRootGraph : : Transform ( FMetasoundFrontendDocument & InDocument ) const
{
// TODO: Swap implementation to not use access pointers/controllers
return Transform ( IDocumentController : : CreateDocumentHandle ( InDocument ) ) ;
}
2021-07-27 15:36:03 -04:00
2022-02-09 12:52:07 -05:00
void FRebuildPresetRootGraph : : AddAndConnectInputs ( const TArray < FMetasoundFrontendClassInput > & InClassInputs , FGraphHandle & InPresetGraphHandle , FNodeHandle & InReferencedNode ) const
2021-10-12 21:21:22 -04:00
{
// Add inputs and space appropriately
FVector2D InputNodeLocation = FVector2D : : ZeroVector ;
2022-03-02 22:11:18 -05:00
FConstGraphHandle ReferencedGraphHandle = ReferencedDocument - > GetRootGraph ( ) ;
TArray < FNodeHandle > NodeHandles ;
2021-10-12 21:21:22 -04:00
for ( const FMetasoundFrontendClassInput & ClassInput : InClassInputs )
{
2022-02-09 12:52:07 -05:00
FNodeHandle InputNode = InPresetGraphHandle - > AddInputVertex ( ClassInput ) ;
2021-10-12 21:21:22 -04:00
if ( ensure ( InputNode - > IsValid ( ) ) )
{
// Connect input node to corresponding referencing node.
FOutputHandle OutputToConnect = InputNode - > GetOutputWithVertexName ( ClassInput . Name ) ;
FInputHandle InputToConnect = InReferencedNode - > GetInputWithVertexName ( ClassInput . Name ) ;
2022-03-02 22:11:18 -05:00
ensure ( OutputToConnect - > Connect ( * InputToConnect ) ) ;
NodeHandles . Add ( MoveTemp ( InputNode ) ) ;
2021-10-12 21:21:22 -04:00
}
}
2022-03-02 22:11:18 -05:00
# if WITH_EDITOR
// Sort before adding nodes to graph layout & copy to preset (must be done after all
// inputs/outputs are added but before setting locations to propagate effectively)
FMetasoundFrontendInterfaceStyle Style = ReferencedGraphHandle - > GetInputStyle ( ) ;
InPresetGraphHandle - > SetInputStyle ( Style ) ;
2022-03-16 19:27:27 -04:00
2022-03-17 16:56:13 -04:00
Style . SortDefaults ( NodeHandles , DocumentTransform : : GetNodeDisplayNameProjection ( ) ) ;
2022-03-02 22:11:18 -05:00
for ( const FNodeHandle & InputNode : NodeHandles )
{
// Set input node location
FMetasoundFrontendNodeStyle NodeStyle ;
NodeStyle . Display . Locations . Add ( FGuid ( ) , InputNodeLocation ) ;
InputNode - > SetNodeStyle ( NodeStyle ) ;
InputNodeLocation + = DisplayStyle : : NodeLayout : : DefaultOffsetY ;
}
# endif // WITH_EDITOR
2021-10-12 21:21:22 -04:00
}
2022-02-09 12:52:07 -05:00
void FRebuildPresetRootGraph : : AddAndConnectOutputs ( const TArray < FMetasoundFrontendClassOutput > & InClassOutputs , FGraphHandle & InPresetGraphHandle , FNodeHandle & InReferencedNode ) const
2021-10-12 21:21:22 -04:00
{
// Add outputs and space appropriately
FVector2D OutputNodeLocation = ( 2 * DisplayStyle : : NodeLayout : : DefaultOffsetX ) ;
2022-03-02 22:11:18 -05:00
FConstGraphHandle ReferencedGraphHandle = ReferencedDocument - > GetRootGraph ( ) ;
TArray < FNodeHandle > NodeHandles ;
2021-10-12 21:21:22 -04:00
for ( const FMetasoundFrontendClassOutput & ClassOutput : InClassOutputs )
{
2022-02-09 12:52:07 -05:00
FNodeHandle OutputNode = InPresetGraphHandle - > AddOutputVertex ( ClassOutput ) ;
2021-10-12 21:21:22 -04:00
if ( ensure ( OutputNode - > IsValid ( ) ) )
{
// Connect input node to corresponding referenced node.
FInputHandle InputToConnect = OutputNode - > GetInputWithVertexName ( ClassOutput . Name ) ;
FOutputHandle OutputToConnect = InReferencedNode - > GetOutputWithVertexName ( ClassOutput . Name ) ;
2022-03-02 22:11:18 -05:00
ensure ( InputToConnect - > Connect ( * OutputToConnect ) ) ;
NodeHandles . Add ( MoveTemp ( OutputNode ) ) ;
2021-10-12 21:21:22 -04:00
}
}
2022-03-02 22:11:18 -05:00
# if WITH_EDITOR
// Sort before adding nodes to graph layout & copy to preset (must be done after all
// inputs/outputs are added but before setting locations to propagate effectively)
FMetasoundFrontendInterfaceStyle Style = ReferencedGraphHandle - > GetOutputStyle ( ) ;
InPresetGraphHandle - > SetOutputStyle ( Style ) ;
2022-03-17 16:56:13 -04:00
Style . SortDefaults ( NodeHandles , DocumentTransform : : GetNodeDisplayNameProjection ( ) ) ;
2022-03-16 19:27:27 -04:00
2023-06-20 13:12:39 -04:00
// Set output node location
2022-03-02 22:11:18 -05:00
for ( const FNodeHandle & OutputNode : NodeHandles )
{
FMetasoundFrontendNodeStyle NodeStyle ;
NodeStyle . Display . Locations . Add ( FGuid ( ) , OutputNodeLocation ) ;
OutputNode - > SetNodeStyle ( NodeStyle ) ;
OutputNodeLocation + = DisplayStyle : : NodeLayout : : DefaultOffsetY ;
}
# endif // WITH_EDITOR
2021-10-12 21:21:22 -04:00
}
2023-08-16 13:21:17 -04:00
TArray < FMetasoundFrontendClassInput > FRebuildPresetRootGraph : : GenerateRequiredClassInputs ( FDocumentHandle & InDocumentHandle , const FConstGraphHandle & InPresetGraph , TSet < FName > & OutInputsInheritingDefault ) const
2021-10-12 21:21:22 -04:00
{
TArray < FMetasoundFrontendClassInput > ClassInputs ;
FConstGraphHandle ReferencedGraph = ReferencedDocument - > GetRootGraph ( ) ;
// Iterate through all input nodes of referenced graph
ReferencedGraph - > IterateConstNodes ( [ & ] ( FConstNodeHandle InputNode )
{
const FName NodeName = InputNode - > GetNodeName ( ) ;
FConstInputHandle Input = InputNode - > GetConstInputWithVertexName ( NodeName ) ;
if ( ensure ( Input - > IsValid ( ) ) )
{
FMetasoundFrontendClassInput ClassInput ;
ClassInput . Name = NodeName ;
ClassInput . TypeName = Input - > GetDataType ( ) ;
2022-07-18 17:14:25 -04:00
ClassInput . AccessType = Input - > GetVertexAccessType ( ) ;
2022-01-26 18:11:52 -05:00
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2022-01-26 18:11:52 -05:00
ClassInput . Metadata . SetDescription ( InputNode - > GetDescription ( ) ) ;
ClassInput . Metadata . SetDisplayName ( Input - > GetMetadata ( ) . GetDisplayName ( ) ) ;
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2023-08-16 13:21:17 -04:00
FDocumentAccessPtr DocumentPtr = InDocumentHandle - > GetDocumentPtr ( ) ;
2023-06-20 13:12:39 -04:00
const FMetasoundFrontendDocument * Document = DocumentPtr . Get ( ) ;
check ( Document ) ;
ClassInput . VertexID = FDocumentIDGenerator : : Get ( ) . CreateVertexID ( * Document ) ; ;
2021-10-12 21:21:22 -04:00
2022-02-09 12:52:07 -05:00
if ( const FMetasoundFrontendClassInput * ExistingClassInput = InPresetGraph - > FindClassInputWithName ( NodeName ) . Get ( ) )
2021-10-12 21:21:22 -04:00
{
ClassInput . NodeID = ExistingClassInput - > NodeID ;
}
2022-02-09 12:52:07 -05:00
if ( InPresetGraph - > ContainsInputVertex ( NodeName , ClassInput . TypeName ) )
2021-10-12 21:21:22 -04:00
{
// If the input vertex already exists in the parent graph,
2022-02-09 12:52:07 -05:00
// check if parent should be used or not from set of managed
// input names.
if ( InPresetGraph - > GetInputsInheritingDefault ( ) . Contains ( NodeName ) )
{
const FGuid ReferencedVertexID = ReferencedGraph - > GetVertexIDForInputVertex ( NodeName ) ;
ClassInput . DefaultLiteral = ReferencedGraph - > GetDefaultInput ( ReferencedVertexID ) ;
}
else
{
FGuid VertexID = InPresetGraph - > GetVertexIDForInputVertex ( NodeName ) ;
ClassInput . DefaultLiteral = InPresetGraph - > GetDefaultInput ( VertexID ) ;
}
2021-10-12 21:21:22 -04:00
}
else
{
// If the input vertex does not exist on the parent graph,
// then it is a new vertex and should use the default value
// of the referenced graph.
const FGuid ReferencedVertexID = ReferencedGraph - > GetVertexIDForInputVertex ( NodeName ) ;
ClassInput . DefaultLiteral = ReferencedGraph - > GetDefaultInput ( ReferencedVertexID ) ;
}
ClassInputs . Add ( MoveTemp ( ClassInput ) ) ;
}
} , EMetasoundFrontendClassType : : Input ) ;
2022-02-09 12:52:07 -05:00
// Fill new managed inputs set with names of all class inputs & if the old input was explicitly not
// marked as a managed input, then remove it from the new managed inputs if found.
OutInputsInheritingDefault . Reset ( ) ;
Algo : : Transform ( ClassInputs , OutInputsInheritingDefault , [ ] ( const FMetasoundFrontendClassInput & Input ) { return Input . Name ; } ) ;
const TSet < FName > & InputsInheritingDefault = InPresetGraph - > GetInputsInheritingDefault ( ) ;
InPresetGraph - > IterateConstNodes ( [ & InputsInheritingDefault , & OutInputsInheritingDefault ] ( FConstNodeHandle Input )
{
if ( ! InputsInheritingDefault . Contains ( Input - > GetNodeName ( ) ) )
{
OutInputsInheritingDefault . Remove ( Input - > GetNodeName ( ) ) ;
}
} , EMetasoundFrontendClassType : : Input ) ;
2021-10-12 21:21:22 -04:00
return ClassInputs ;
}
2023-08-16 13:21:17 -04:00
TArray < FMetasoundFrontendClassOutput > FRebuildPresetRootGraph : : GenerateRequiredClassOutputs ( FDocumentHandle & InDocumentHandle , const FConstGraphHandle & InPresetGraph ) const
2021-10-12 21:21:22 -04:00
{
TArray < FMetasoundFrontendClassOutput > ClassOutputs ;
FConstGraphHandle ReferencedGraph = ReferencedDocument - > GetRootGraph ( ) ;
// Iterate over the referenced graph's output nodes.
ReferencedGraph - > IterateConstNodes ( [ & ] ( FConstNodeHandle OutputNode )
{
const FName NodeName = OutputNode - > GetNodeName ( ) ;
FConstOutputHandle Output = OutputNode - > GetConstOutputWithVertexName ( NodeName ) ;
if ( ensure ( Output - > IsValid ( ) ) )
{
FMetasoundFrontendClassOutput ClassOutput ;
ClassOutput . Name = NodeName ;
ClassOutput . TypeName = Output - > GetDataType ( ) ;
2022-07-18 17:14:25 -04:00
ClassOutput . AccessType = Output - > GetVertexAccessType ( ) ;
2022-01-26 18:11:52 -05:00
2022-02-10 15:07:39 -05:00
# if WITH_EDITOR
2022-01-26 18:11:52 -05:00
ClassOutput . Metadata . SetDescription ( OutputNode - > GetDescription ( ) ) ;
ClassOutput . Metadata . SetDisplayName ( Output - > GetMetadata ( ) . GetDisplayName ( ) ) ;
2022-02-10 15:07:39 -05:00
# endif // WITH_EDITOR
2022-01-26 18:11:52 -05:00
2023-08-16 13:21:17 -04:00
FDocumentAccessPtr DocumentPtr = InDocumentHandle - > GetDocumentPtr ( ) ;
2023-06-20 13:12:39 -04:00
const FMetasoundFrontendDocument * Document = DocumentPtr . Get ( ) ;
check ( Document ) ;
ClassOutput . VertexID = FDocumentIDGenerator : : Get ( ) . CreateVertexID ( * Document ) ;
2021-10-12 21:21:22 -04:00
2022-02-09 12:52:07 -05:00
if ( const FMetasoundFrontendClassOutput * ExistingClassOutput = InPresetGraph - > FindClassOutputWithName ( NodeName ) . Get ( ) )
2021-10-12 21:21:22 -04:00
{
ClassOutput . NodeID = ExistingClassOutput - > NodeID ;
}
ClassOutputs . Add ( MoveTemp ( ClassOutput ) ) ;
}
} , EMetasoundFrontendClassType : : Output ) ;
return ClassOutputs ;
}
2022-03-10 21:19:13 -05:00
bool FRenameRootGraphClass : : Transform ( FDocumentHandle InDocument ) const
2021-07-27 20:30:14 -04:00
{
2022-03-10 21:19:13 -05:00
FGraphHandle RootGraph = InDocument - > GetRootGraph ( ) ;
FMetasoundFrontendClassMetadata Metadata = RootGraph - > GetGraphMetadata ( ) ;
Metadata . SetClassName ( NewClassName ) ;
RootGraph - > SetGraphMetadata ( Metadata ) ;
2021-07-27 15:36:03 -04:00
return true ;
}
2023-06-28 17:08:29 -04:00
bool FRenameRootGraphClass : : Transform ( FMetasoundFrontendDocument & InOutDocument ) const
{
FMetasoundFrontendClassMetadata & Metadata = InOutDocument . RootGraph . Metadata ;
Metadata . SetClassName ( NewClassName ) ;
return true ;
}
2021-03-31 00:23:34 -04:00
} // namespace Frontend
} // namespace Metasound