2023-03-07 17:01:52 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundFrontendDocumentBuilder.h"
# include "Algo/AnyOf.h"
# include "Algo/Find.h"
# include "Algo/NoneOf.h"
# include "Algo/Transform.h"
# include "MetasoundAssetBase.h"
# include "MetasoundAssetManager.h"
# include "MetasoundDocumentInterface.h"
# include "MetasoundFrontendArchetypeRegistry.h"
# include "MetasoundFrontendDocumentCache.h"
# include "MetasoundFrontendDocument.h"
# include "MetasoundFrontendRegistries.h"
# include "MetasoundFrontendSearchEngine.h"
# include "MetasoundFrontendTransform.h"
# include "MetasoundTrace.h"
# include UE_INLINE_GENERATED_CPP_BY_NAME(MetasoundFrontendDocumentBuilder)
namespace Metasound : : Frontend
{
namespace DocumentBuilderPrivate
{
void FinalizeGraphVertexNode ( FMetasoundFrontendNode & InOutNode , const FMetasoundFrontendClassVertex & InVertex )
{
InOutNode . Name = InVertex . Name ;
// Set name on related vertices of input node
auto IsVertexWithTypeName = [ & InVertex ] ( const FMetasoundFrontendVertex & Vertex ) { return Vertex . TypeName = = InVertex . TypeName ; } ;
if ( FMetasoundFrontendVertex * InputVertex = InOutNode . Interface . Inputs . FindByPredicate ( IsVertexWithTypeName ) )
{
InputVertex - > Name = InVertex . Name ;
}
else
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Node associated with graph vertex of type '%s' does not contain input vertex with type '%s' " ) , * InVertex . TypeName . ToString ( ) ) ;
}
if ( FMetasoundFrontendVertex * OutputVertex = InOutNode . Interface . Outputs . FindByPredicate ( IsVertexWithTypeName ) )
{
OutputVertex - > Name = InVertex . Name ;
}
else
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Node associated with graph vertex of type '%s' does not contain output vertex of matching type. " ) , * InVertex . TypeName . ToString ( ) ) ;
}
}
} // namespace DocumentBuilderPrivate
} // namespace Metasound::Frontend
FMetaSoundFrontendDocumentBuilder : : FMetaSoundFrontendDocumentBuilder ( )
{
}
FMetaSoundFrontendDocumentBuilder : : FMetaSoundFrontendDocumentBuilder ( TScriptInterface < IMetaSoundDocumentInterface > InDocumentInterface )
: DocumentInterface ( InDocumentInterface )
{
ReloadCache ( ) ;
}
FMetaSoundFrontendDocumentBuilder : : FMetaSoundFrontendDocumentBuilder ( const FMetaSoundFrontendDocumentBuilder & InBuilder )
{
2023-03-09 01:21:59 -05:00
DocumentInterface = InBuilder . DocumentInterface ;
if ( DocumentInterface )
{
ReloadCache ( ) ;
}
2023-03-07 17:01:52 -05:00
}
FMetaSoundFrontendDocumentBuilder : : FMetaSoundFrontendDocumentBuilder ( FMetaSoundFrontendDocumentBuilder & & InBuilder )
: DocumentInterface ( MoveTemp ( InBuilder . DocumentInterface ) )
, DocumentCache ( MoveTemp ( InBuilder . DocumentCache ) )
{
}
FMetaSoundFrontendDocumentBuilder & FMetaSoundFrontendDocumentBuilder : : operator = ( FMetaSoundFrontendDocumentBuilder & & InRHS )
{
using namespace Metasound : : Frontend ;
DocumentInterface = MoveTemp ( InRHS . DocumentInterface ) ;
DocumentCache = MoveTemp ( InRHS . DocumentCache ) ;
return * this ;
}
FMetaSoundFrontendDocumentBuilder & FMetaSoundFrontendDocumentBuilder : : operator = ( const FMetaSoundFrontendDocumentBuilder & InRHS )
{
using namespace Metasound : : Frontend ;
2023-03-09 01:21:59 -05:00
DocumentInterface = InRHS . DocumentInterface ;
if ( DocumentInterface )
{
ReloadCache ( ) ;
}
2023-03-07 17:01:52 -05:00
return * this ;
}
const FMetasoundFrontendClass * FMetaSoundFrontendDocumentBuilder : : AddGraphDependency ( const FMetasoundFrontendGraphClass & InClass )
{
using namespace Metasound : : Frontend ;
checkf ( InClass . Metadata . GetType ( ) = = EMetasoundFrontendClassType : : Graph , TEXT ( " Call should only be used for 'Graph' type dependencies. Use 'AddNativeDependency' for other class types. " ) ) ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const FMetasoundFrontendClass * Dependency = nullptr ;
FMetasoundFrontendClass NewDependency = InClass ;
// All dependencies are listed as 'External' from the perspective of the owning document.
// This makes them implementation agnostic to accommodate nativization of assets.
NewDependency . Metadata . SetType ( EMetasoundFrontendClassType : : External ) ;
NewDependency . ID = FGuid : : NewGuid ( ) ;
Dependency = & Document . Dependencies . Emplace_GetRef ( MoveTemp ( NewDependency ) ) ;
const int32 NewIndex = Document . Dependencies . Num ( ) - 1 ;
DocumentCache - > OnDependencyAdded ( NewIndex ) ;
return Dependency ;
}
const FMetasoundFrontendClass * FMetaSoundFrontendDocumentBuilder : : AddNativeDependency ( const FMetasoundFrontendClassMetadata & InClassMetadata )
{
using namespace Metasound : : Frontend ;
checkf ( InClassMetadata . GetType ( ) ! = EMetasoundFrontendClassType : : Graph , TEXT ( " Graph dependencies must be added via 'AddGraphDependency' to avoid overhead of registration where not necessary " ) ) ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const FMetasoundFrontendClass * Dependency = nullptr ;
const FNodeRegistryKey RegistryKey = NodeRegistryKey : : CreateKey ( InClassMetadata ) ;
FMetasoundFrontendClass NewDependency = GenerateClass ( RegistryKey ) ;
NewDependency . ID = FGuid : : NewGuid ( ) ;
Dependency = & Document . Dependencies . Emplace_GetRef ( MoveTemp ( NewDependency ) ) ;
const int32 NewIndex = Document . Dependencies . Num ( ) - 1 ;
DocumentCache - > OnDependencyAdded ( NewIndex ) ;
return Dependency ;
}
const FMetasoundFrontendEdge * FMetaSoundFrontendDocumentBuilder : : AddEdge ( FMetasoundFrontendEdge & & InNewEdge )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
if ( CanAddEdge ( InNewEdge ) )
{
IDocumentGraphEdgeCache & EdgeCache = DocumentCache - > GetEdgeCache ( ) ;
const FMetasoundFrontendEdge & NewEdge = Graph . Edges . Add_GetRef ( MoveTemp ( InNewEdge ) ) ;
const int32 NewIndex = Graph . Edges . Num ( ) - 1 ;
EdgeCache . OnEdgeAdded ( NewIndex ) ;
return & NewEdge ;
}
return nullptr ;
}
bool FMetaSoundFrontendDocumentBuilder : : AddEdgesByNamedConnections ( const TSet < Metasound : : Frontend : : FNamedEdge > & ConnectionsToMake , TArray < const FMetasoundFrontendEdge * > * OutNewEdges )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
if ( OutNewEdges )
{
OutNewEdges - > Reset ( ) ;
}
bool bSuccess = true ;
TArray < FMetasoundFrontendEdge > EdgesToAdd ;
for ( const FNamedEdge & Connection : ConnectionsToMake )
{
const FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
const FMetasoundFrontendVertex * OutputVertex = NodeCache . FindOutputVertex ( Connection . OutputNodeID , Connection . OutputName ) ;
const FMetasoundFrontendVertex * InputVertex = NodeCache . FindInputVertex ( Connection . InputNodeID , Connection . InputName ) ;
if ( OutputVertex & & InputVertex )
{
FMetasoundFrontendEdge NewEdge = { Connection . OutputNodeID , OutputVertex - > VertexID , Connection . InputNodeID , InputVertex - > VertexID } ;
if ( CanAddEdge ( NewEdge ) )
{
EdgesToAdd . Add ( MoveTemp ( NewEdge ) ) ;
}
else
{
bSuccess = false ;
UE_LOG ( LogMetaSound , Warning , TEXT ( " Failed to add connections between MetaSound output '%s' and input '%s': Vertex types either incompatable (eg. construction pin to non-construction pin) or input already connected. " ) , * Connection . OutputName . ToString ( ) , * Connection . InputName . ToString ( ) ) ;
}
}
}
for ( FMetasoundFrontendEdge & EdgeToAdd : EdgesToAdd )
{
const FMetasoundFrontendEdge * NewEdge = AddEdge ( MoveTemp ( EdgeToAdd ) ) ;
if ( ensureAlwaysMsgf ( NewEdge , TEXT ( " Failed to add MetaSound graph edge via DocumentBuilder when prior step validated edge add was valid " ) ) )
{
if ( OutNewEdges )
{
OutNewEdges - > Add ( NewEdge ) ;
}
}
else
{
bSuccess = false ;
}
}
return bSuccess ;
}
bool FMetaSoundFrontendDocumentBuilder : : AddEdgesByNodeClassInterfaceBindings ( const FGuid & InFromNodeID , const FGuid & InToNodeID )
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
auto FindNodeClassDocAndInterfaces = [ this ] ( const FGuid & InNodeID ) - > const FMetasoundFrontendDocument *
{
const TScriptInterface < IMetaSoundDocumentInterface > NodeDocumentUInterface = FindOrLoadNodeClassDocument ( InNodeID ) ;
if ( NodeDocumentUInterface )
{
return & NodeDocumentUInterface - > GetDocument ( ) ;
}
return nullptr ;
} ;
const FMetasoundFrontendDocument * OutputNodeDocument = FindNodeClassDocAndInterfaces ( InFromNodeID ) ;
const FMetasoundFrontendDocument * InputNodeDocument = FindNodeClassDocAndInterfaces ( InToNodeID ) ;
if ( ! InputNodeDocument | | ! OutputNodeDocument )
{
return false ;
}
const TSet < FMetasoundFrontendVersion > & OutputInterfaceVersions = OutputNodeDocument - > Interfaces ;
TArray < const IInterfaceRegistryEntry * > InputNodeInterfaces ;
FindDeclaredInterfaces ( * InputNodeDocument , InputNodeInterfaces ) ;
TSet < FNamedEdge > ConnectionsToMake ;
TSet < FName > InputsToConnect ;
for ( const IInterfaceRegistryEntry * InputInterfaceEntry : InputNodeInterfaces )
{
const FMetasoundFrontendInterface & InputInterface = InputInterfaceEntry - > GetInterface ( ) ;
const TArray < FMetasoundFrontendInterfaceBinding > & OutputBindings = InputInterfaceEntry - > GetOutputBindings ( ) ;
// Bindings are sorted in registry with earlier entries being higher priority to apply connections,
// so earlier listed connections are selected over potential collisions with later entries.
for ( const FMetasoundFrontendInterfaceBinding & OutputBinding : OutputBindings )
{
if ( OutputInterfaceVersions . Contains ( OutputBinding . Version ) )
{
for ( const FMetasoundFrontendInterfaceBindingConnection & Connection : OutputBinding . Connections )
{
if ( ! InputsToConnect . Contains ( Connection . InputName ) )
{
InputsToConnect . Add ( Connection . InputName ) ;
ConnectionsToMake . Add ( FNamedEdge { InFromNodeID , Connection . OutputName , InToNodeID , Connection . InputName } ) ;
}
}
}
}
}
return AddEdgesByNamedConnections ( ConnectionsToMake ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : AddEdgesFromMatchingInterfaceNodeOutputsToGraphOutputs ( const FGuid & InNodeID , TArray < const FMetasoundFrontendEdge * > & OutEdgesCreated )
{
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( FMetaSoundFrontendDocumentBuilder : : AddEdgesFromMatchingInterfaceNodeOutputsToGraphOutputs ) ;
using namespace Metasound : : Frontend ;
OutEdgesCreated . Reset ( ) ;
const TScriptInterface < IMetaSoundDocumentInterface > NodeDocumentInterface = FindOrLoadNodeClassDocument ( InNodeID ) ;
if ( ! NodeDocumentInterface )
{
return false ;
}
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
const TSet < FMetasoundFrontendVersion > & NodeInterfaces = NodeDocumentInterface - > GetDocument ( ) . Interfaces ;
const TSet < FMetasoundFrontendVersion > CommonInterfaces = NodeInterfaces . Intersect ( GetDocument ( ) . Interfaces ) ;
TSet < FNamedEdge > ConnectionsToMake ;
for ( const FMetasoundFrontendVersion & Version : CommonInterfaces )
{
const FInterfaceRegistryKey InterfaceKey = GetInterfaceRegistryKey ( Version ) ;
if ( const IInterfaceRegistryEntry * RegistryEntry = IInterfaceRegistry : : Get ( ) . FindInterfaceRegistryEntry ( InterfaceKey ) )
{
Algo : : Transform ( RegistryEntry - > GetInterface ( ) . Outputs , ConnectionsToMake , [ this , & NodeCache , InNodeID ] ( const FMetasoundFrontendClassOutput & Output )
{
const FMetasoundFrontendGraph & Graph = GetDocument ( ) . RootGraph . Graph ;
const FMetasoundFrontendVertex * NodeVertex = NodeCache . FindOutputVertex ( InNodeID , Output . Name ) ;
check ( NodeVertex ) ;
const FMetasoundFrontendNode * OutputNode = NodeCache . FindOutputNode ( Output . Name ) ;
check ( OutputNode ) ;
const TArray < FMetasoundFrontendVertex > & Inputs = OutputNode - > Interface . Inputs ;
check ( ! Inputs . IsEmpty ( ) ) ;
return FNamedEdge { InNodeID , NodeVertex - > Name , OutputNode - > GetID ( ) , Inputs . Last ( ) . Name } ;
} ) ;
}
}
return AddEdgesByNamedConnections ( ConnectionsToMake , & OutEdgesCreated ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : AddEdgesFromMatchingInterfaceNodeInputsToGraphInputs ( const FGuid & InNodeID , TArray < const FMetasoundFrontendEdge * > & OutEdgesCreated )
{
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( FMetaSoundFrontendDocumentBuilder : : AddEdgesFromMatchingInterfaceNodeInputsToGraphInputs ) ;
using namespace Metasound : : Frontend ;
OutEdgesCreated . Reset ( ) ;
const TScriptInterface < IMetaSoundDocumentInterface > NodeDocumentInterface = FindOrLoadNodeClassDocument ( InNodeID ) ;
if ( ! NodeDocumentInterface )
{
return false ;
}
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
const TSet < FMetasoundFrontendVersion > & NodeInterfaces = NodeDocumentInterface - > GetDocument ( ) . Interfaces ;
const TSet < FMetasoundFrontendVersion > CommonInterfaces = NodeInterfaces . Intersect ( GetDocument ( ) . Interfaces ) ;
TSet < FNamedEdge > ConnectionsToMake ;
for ( const FMetasoundFrontendVersion & Version : CommonInterfaces )
{
const FInterfaceRegistryKey InterfaceKey = GetInterfaceRegistryKey ( Version ) ;
if ( const IInterfaceRegistryEntry * RegistryEntry = IInterfaceRegistry : : Get ( ) . FindInterfaceRegistryEntry ( InterfaceKey ) )
{
Algo : : Transform ( RegistryEntry - > GetInterface ( ) . Inputs , ConnectionsToMake , [ this , & NodeCache , InNodeID ] ( const FMetasoundFrontendClassInput & Input )
{
const FMetasoundFrontendGraph & Graph = GetDocument ( ) . RootGraph . Graph ;
const FMetasoundFrontendVertex * NodeVertex = NodeCache . FindOutputVertex ( InNodeID , Input . Name ) ;
check ( NodeVertex ) ;
const FMetasoundFrontendNode * InputNode = NodeCache . FindInputNode ( Input . Name ) ;
check ( InputNode ) ;
const TArray < FMetasoundFrontendVertex > & Outputs = InputNode - > Interface . Outputs ;
check ( ! Outputs . IsEmpty ( ) ) ;
return FNamedEdge { InNodeID , NodeVertex - > Name , InputNode - > GetID ( ) , Outputs . Last ( ) . Name } ;
} ) ;
}
}
return AddEdgesByNamedConnections ( ConnectionsToMake , & OutEdgesCreated ) ;
}
const FMetasoundFrontendNode * FMetaSoundFrontendDocumentBuilder : : AddGraphInput ( const FMetasoundFrontendClassInput & InClassInput )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendGraphClass & RootGraph = GetDocument ( ) . RootGraph ;
checkf ( InClassInput . NodeID . IsValid ( ) , TEXT ( " Unassigned NodeID when adding graph input " ) ) ;
checkf ( InClassInput . VertexID . IsValid ( ) , TEXT ( " Unassigned VertexID when adding graph input " ) ) ;
if ( InClassInput . TypeName . IsNone ( ) )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " TypeName unset when attempting to add class input '%s' " ) , * InClassInput . Name . ToString ( ) ) ;
return nullptr ;
}
else if ( const FMetasoundFrontendNode * Node = DocumentCache - > GetNodeCache ( ) . FindInputNode ( InClassInput . Name ) )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Attempting to add MetaSound graph input '%s' when input with name already exists " ) , * InClassInput . Name . ToString ( ) ) ;
return Node ;
}
auto FindRegistryClass = [ & InClassInput ] ( FMetasoundFrontendClass & OutClass ) - > bool
{
switch ( InClassInput . AccessType )
{
case EMetasoundFrontendVertexAccessType : : Value :
{
return IDataTypeRegistry : : Get ( ) . GetFrontendConstructorInputClass ( InClassInput . TypeName , OutClass ) ;
}
break ;
case EMetasoundFrontendVertexAccessType : : Reference :
{
return IDataTypeRegistry : : Get ( ) . GetFrontendInputClass ( InClassInput . TypeName , OutClass ) ;
}
break ;
case EMetasoundFrontendVertexAccessType : : Unset :
default :
{
checkNoEntry ( ) ;
}
break ;
}
return false ;
} ;
FMetasoundFrontendClass Class ;
if ( FindRegistryClass ( Class ) )
{
if ( ! FindDependency ( Class . Metadata ) )
{
AddNativeDependency ( Class . Metadata ) ;
}
auto FinalizeNode = [ & InClassInput ] ( FMetasoundFrontendNode & InOutNode , const Metasound : : Frontend : : FNodeRegistryKey & )
{
DocumentBuilderPrivate : : FinalizeGraphVertexNode ( InOutNode , InClassInput ) ;
} ;
if ( FMetasoundFrontendNode * NewNode = AddNodeInternal ( Class . Metadata , FinalizeNode , InClassInput . NodeID ) )
{
FMetasoundFrontendClassInput & NewInput = RootGraph . Interface . Inputs . Add_GetRef ( InClassInput ) ;
if ( ! NewInput . VertexID . IsValid ( ) )
{
NewInput . VertexID = FGuid : : NewGuid ( ) ;
}
return NewNode ;
}
}
return nullptr ;
}
const FMetasoundFrontendNode * FMetaSoundFrontendDocumentBuilder : : AddGraphOutput ( const FMetasoundFrontendClassOutput & InClassOutput )
{
using namespace Metasound : : Frontend ;
checkf ( InClassOutput . NodeID . IsValid ( ) , TEXT ( " Unassigned NodeID when adding graph output " ) ) ;
checkf ( InClassOutput . VertexID . IsValid ( ) , TEXT ( " Unassigned VertexID when adding graph output " ) ) ;
FMetasoundFrontendGraphClass & RootGraph = GetDocument ( ) . RootGraph ;
if ( InClassOutput . TypeName . IsNone ( ) )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " TypeName unset when attempting to add class output '%s' " ) , * InClassOutput . Name . ToString ( ) ) ;
return nullptr ;
}
else if ( const FMetasoundFrontendNode * Node = DocumentCache - > GetNodeCache ( ) . FindInputNode ( InClassOutput . Name ) )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Attempting to add MetaSound graph output '%s' when output with name already exists " ) , * InClassOutput . Name . ToString ( ) ) ;
return Node ;
}
auto FindRegistryClass = [ & InClassOutput ] ( FMetasoundFrontendClass & OutClass ) - > bool
{
switch ( InClassOutput . AccessType )
{
case EMetasoundFrontendVertexAccessType : : Value :
{
return IDataTypeRegistry : : Get ( ) . GetFrontendConstructorOutputClass ( InClassOutput . TypeName , OutClass ) ;
}
break ;
case EMetasoundFrontendVertexAccessType : : Reference :
{
return IDataTypeRegistry : : Get ( ) . GetFrontendOutputClass ( InClassOutput . TypeName , OutClass ) ;
}
break ;
case EMetasoundFrontendVertexAccessType : : Unset :
default :
{
checkNoEntry ( ) ;
}
break ;
}
return false ;
} ;
FMetasoundFrontendClass Class ;
if ( FindRegistryClass ( Class ) )
{
if ( ! FindDependency ( Class . Metadata ) )
{
AddNativeDependency ( Class . Metadata ) ;
}
auto FinalizeNode = [ & InClassOutput ] ( FMetasoundFrontendNode & InOutNode , const Metasound : : Frontend : : FNodeRegistryKey & )
{
DocumentBuilderPrivate : : FinalizeGraphVertexNode ( InOutNode , InClassOutput ) ;
} ;
if ( FMetasoundFrontendNode * NewNode = AddNodeInternal ( Class . Metadata , FinalizeNode , InClassOutput . NodeID ) )
{
FMetasoundFrontendClassOutput & NewOutput = RootGraph . Interface . Outputs . Add_GetRef ( InClassOutput ) ;
if ( ! NewOutput . VertexID . IsValid ( ) )
{
NewOutput . VertexID = FGuid : : NewGuid ( ) ;
}
return NewNode ;
}
}
return nullptr ;
}
bool FMetaSoundFrontendDocumentBuilder : : AddInterface ( FName InterfaceName )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendInterface Interface ;
if ( ISearchEngine : : Get ( ) . FindInterfaceWithHighestVersion ( InterfaceName , Interface ) )
{
if ( GetDocument ( ) . Interfaces . Contains ( Interface . Version ) )
{
return true ;
}
const FModifyRootGraphInterfaces ModifyInterfaces ( { } , { Interface . Version } ) ;
if ( ModifyInterfaces . Transform ( GetDocument ( ) ) )
{
// TODO: Add ability to update local builder caches dynamically
// in ModifyRootGraphInterfaces transform once it moves away
// from using controllers internally. Then cache updates can
// be more isolated and not use this full rebuild call.
ReloadCache ( ) ;
return true ;
}
}
return false ;
}
const FMetasoundFrontendNode * FMetaSoundFrontendDocumentBuilder : : AddGraphNode ( const FMetasoundFrontendGraphClass & InGraphClass , FGuid InNodeID )
{
auto FinalizeNode = [ ] ( const FMetasoundFrontendNode & Node , const Metasound : : Frontend : : FNodeRegistryKey & ClassKey )
{
# if WITH_EDITOR
using namespace Metasound : : Frontend ;
// Cache the asset name on the node if it node is reference to asset-defined graph.
if ( IMetaSoundAssetManager * AssetManager = IMetaSoundAssetManager : : Get ( ) )
{
if ( const FSoftObjectPath * Path = AssetManager - > FindObjectPathFromKey ( ClassKey ) )
{
return FName ( * Path - > GetAssetName ( ) ) ;
}
}
# endif // WITH_EDITOR
return Node . Name ;
} ;
// Dependency is considered "External" when looked up or added on another graph
FMetasoundFrontendClassMetadata NewClassMetadata = InGraphClass . Metadata ;
NewClassMetadata . SetType ( EMetasoundFrontendClassType : : External ) ;
if ( ! FindDependency ( NewClassMetadata ) )
{
AddGraphDependency ( InGraphClass ) ;
}
return AddNodeInternal ( NewClassMetadata , FinalizeNode , InNodeID ) ;
}
FMetasoundFrontendNode * FMetaSoundFrontendDocumentBuilder : : AddNodeInternal ( const FMetasoundFrontendClassMetadata & InClassMetadata , FFinalizeNodeFunctionRef FinalizeNode , FGuid InNodeID )
{
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( FMetaSoundFrontendDocumentBuilder : : AddNodeInternal ) ;
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const FNodeRegistryKey ClassKey = NodeRegistryKey : : CreateKey ( InClassMetadata ) ;
if ( const FMetasoundFrontendClass * Dependency = DocumentCache - > FindDependency ( ClassKey ) )
{
FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
TArray < FMetasoundFrontendNode > & Nodes = Graph . Nodes ;
FMetasoundFrontendNode & Node = Nodes . Emplace_GetRef ( * Dependency ) ;
Node . UpdateID ( InNodeID ) ;
FinalizeNode ( Node , ClassKey ) ;
const int32 NewIndex = Nodes . Num ( ) - 1 ;
IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
NodeCache . OnNodeAdded ( NewIndex ) ;
return & Node ;
}
return nullptr ;
}
bool FMetaSoundFrontendDocumentBuilder : : CanAddEdge ( const FMetasoundFrontendEdge & InEdge ) const
{
using namespace Metasound : : Frontend ;
const FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const IDocumentGraphEdgeCache & EdgeCache = DocumentCache - > GetEdgeCache ( ) ;
if ( ! EdgeCache . IsNodeInputConnected ( InEdge . ToNodeID , InEdge . ToVertexID ) )
{
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
const FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
const FMetasoundFrontendVertex * OutputVertex = NodeCache . FindOutputVertex ( InEdge . FromNodeID , InEdge . FromVertexID ) ;
const FMetasoundFrontendVertex * InputVertex = NodeCache . FindInputVertex ( InEdge . ToNodeID , InEdge . ToVertexID ) ;
if ( OutputVertex & & InputVertex )
{
if ( OutputVertex - > TypeName = = InputVertex - > TypeName )
{
const FMetasoundFrontendClassOutput * ClassOutput = FindNodeOutputClassOutput ( InEdge . FromNodeID , InEdge . FromVertexID ) ;
const FMetasoundFrontendClassInput * ClassInput = FindNodeInputClassInput ( InEdge . ToNodeID , InEdge . ToVertexID ) ;
if ( ClassInput & & ClassOutput )
{
return FMetasoundFrontendClassVertex : : CanConnectVertexAccessTypes ( ClassOutput - > AccessType , ClassInput - > AccessType ) ;
}
}
}
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : ContainsEdge ( const FMetasoundFrontendEdge & InEdge ) const
{
using namespace Metasound : : Frontend ;
IDocumentGraphEdgeCache & EdgeCache = DocumentCache - > GetEdgeCache ( ) ;
return EdgeCache . ContainsEdge ( InEdge ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : ContainsNode ( const FGuid & InNodeID ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . ContainsNode ( InNodeID ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : ConvertFromPreset ( )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
FMetasoundFrontendGraphClass & RootGraphClass = Document . RootGraph ;
FMetasoundFrontendGraphClassPresetOptions & PresetOptions = RootGraphClass . PresetOptions ;
PresetOptions . bIsPreset = false ;
# if WITH_EDITOR
FMetasoundFrontendGraphStyle Style = Document . RootGraph . Graph . Style ;
Style . bIsGraphEditable = true ;
# endif // WITH_EDITOR
return true ;
}
bool FMetaSoundFrontendDocumentBuilder : : FindDeclaredInterfaces ( TArray < const Metasound : : Frontend : : IInterfaceRegistryEntry * > & OutInterfaces ) const
{
return FindDeclaredInterfaces ( GetDocument ( ) , OutInterfaces ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : FindDeclaredInterfaces ( const FMetasoundFrontendDocument & InDocument , TArray < const Metasound : : Frontend : : IInterfaceRegistryEntry * > & OutInterfaces )
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
bool bInterfacesFound = true ;
Algo : : Transform ( InDocument . Interfaces , OutInterfaces , [ & bInterfacesFound ] ( const FMetasoundFrontendVersion & Version )
{
const FInterfaceRegistryKey InterfaceKey = GetInterfaceRegistryKey ( Version ) ;
const IInterfaceRegistryEntry * RegistryEntry = IInterfaceRegistry : : Get ( ) . FindInterfaceRegistryEntry ( InterfaceKey ) ;
if ( ! RegistryEntry )
{
bInterfacesFound = false ;
UE_LOG ( LogMetaSound , Warning , TEXT ( " No registered interface matching interface version on document [InterfaceVersion:%s] " ) , * Version . ToString ( ) ) ;
}
return RegistryEntry ;
} ) ;
return bInterfacesFound ;
}
const FMetasoundFrontendClass * FMetaSoundFrontendDocumentBuilder : : FindDependency ( const FGuid & InClassID ) const
{
return DocumentCache - > FindDependency ( InClassID ) ;
}
const FMetasoundFrontendClass * FMetaSoundFrontendDocumentBuilder : : FindDependency ( const FMetasoundFrontendClassMetadata & InMetadata ) const
{
using namespace Metasound : : Frontend ;
checkf ( InMetadata . GetType ( ) ! = EMetasoundFrontendClassType : : Graph ,
TEXT ( " Dependencies are never listed as 'Graph' types. Graphs are considered 'External' from the perspective of the parent document to allow for nativization. " ) ) ;
const FNodeRegistryKey RegistryKey = NodeRegistryKey : : CreateKey ( InMetadata ) ;
return DocumentCache - > FindDependency ( RegistryKey ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : FindInterfaceInputNodes ( FName InterfaceName , TArray < const FMetasoundFrontendNode * > & OutInputs ) const
{
using namespace Metasound : : Frontend ;
OutInputs . Reset ( ) ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
FMetasoundFrontendInterface Interface ;
if ( ISearchEngine : : Get ( ) . FindInterfaceWithHighestVersion ( InterfaceName , Interface ) )
{
if ( GetDocument ( ) . Interfaces . Contains ( Interface . Version ) )
{
TArray < const FMetasoundFrontendNode * > InterfaceInputs ;
for ( const FMetasoundFrontendClassInput & Input : Interface . Inputs )
{
if ( const int32 * NodeIndex = NodeCache . FindInputNodeIndex ( Input . Name ) )
{
const FMetasoundFrontendNode & Node = GetDocument ( ) . RootGraph . Graph . Nodes [ * NodeIndex ] ;
InterfaceInputs . Add ( & Node ) ;
}
else
{
return false ;
}
}
OutInputs = MoveTemp ( InterfaceInputs ) ;
return true ;
}
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : FindInterfaceOutputNodes ( FName InterfaceName , TArray < const FMetasoundFrontendNode * > & OutOutputs ) const
{
using namespace Metasound : : Frontend ;
OutOutputs . Reset ( ) ;
FMetasoundFrontendInterface Interface ;
if ( ISearchEngine : : Get ( ) . FindInterfaceWithHighestVersion ( InterfaceName , Interface ) )
{
if ( GetDocument ( ) . Interfaces . Contains ( Interface . Version ) )
{
TArray < const FMetasoundFrontendNode * > InterfaceOutputs ;
for ( const FMetasoundFrontendClassOutput & Output : Interface . Outputs )
{
if ( const int32 * NodeIndex = DocumentCache - > GetNodeCache ( ) . FindOutputNodeIndex ( Output . Name ) )
{
const FMetasoundFrontendNode & Node = GetDocument ( ) . RootGraph . Graph . Nodes [ * NodeIndex ] ;
InterfaceOutputs . Add ( & Node ) ;
}
else
{
return false ;
}
}
OutOutputs = MoveTemp ( InterfaceOutputs ) ;
return true ;
}
}
return false ;
}
const FMetasoundFrontendNode * FMetaSoundFrontendDocumentBuilder : : FindGraphInputNode ( FName InputName ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . FindInputNode ( InputName ) ;
}
const FMetasoundFrontendNode * FMetaSoundFrontendDocumentBuilder : : FindGraphOutputNode ( FName OutputName ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . FindOutputNode ( OutputName ) ;
}
const FMetasoundFrontendVertex * FMetaSoundFrontendDocumentBuilder : : FindNodeInput ( const FGuid & InNodeID , const FGuid & InVertexID ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . FindInputVertex ( InNodeID , InVertexID ) ;
}
const FMetasoundFrontendVertex * FMetaSoundFrontendDocumentBuilder : : FindNodeInput ( const FGuid & InNodeID , FName InVertexName ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . FindInputVertex ( InNodeID , InVertexName ) ;
}
const FMetasoundFrontendVertex * FMetaSoundFrontendDocumentBuilder : : FindNodeOutput ( const FGuid & InNodeID , const FGuid & InVertexID ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . FindOutputVertex ( InNodeID , InVertexID ) ;
}
const FMetasoundFrontendVertex * FMetaSoundFrontendDocumentBuilder : : FindNodeOutput ( const FGuid & InNodeID , FName InVertexName ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . FindOutputVertex ( InNodeID , InVertexName ) ;
}
const FMetasoundFrontendClassInput * FMetaSoundFrontendDocumentBuilder : : FindNodeInputClassInput ( const FGuid & InNodeID , const FGuid & InVertexID ) const
{
using namespace Metasound : : Frontend ;
const FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
if ( const FMetasoundFrontendNode * Node = NodeCache . FindNode ( InNodeID ) )
{
if ( const FMetasoundFrontendClass * Class = DocumentCache - > FindDependency ( Node - > ClassID ) )
{
auto InputMatchesVertex = [ & InVertexID ] ( const FMetasoundFrontendClassVertex & ClassVertex ) { return ClassVertex . VertexID = = InVertexID ; } ;
return Class - > Interface . Inputs . FindByPredicate ( InputMatchesVertex ) ;
}
}
return nullptr ;
}
const FMetasoundFrontendClassOutput * FMetaSoundFrontendDocumentBuilder : : FindNodeOutputClassOutput ( const FGuid & InNodeID , const FGuid & InVertexID ) const
{
using namespace Metasound : : Frontend ;
const FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
if ( const FMetasoundFrontendNode * Node = NodeCache . FindNode ( InNodeID ) )
{
if ( const FMetasoundFrontendClass * Class = DocumentCache - > FindDependency ( Node - > ClassID ) )
{
auto OutputMatchesVertex = [ & InVertexID ] ( const FMetasoundFrontendClassVertex & ClassVertex ) { return ClassVertex . VertexID = = InVertexID ; } ;
return Class - > Interface . Outputs . FindByPredicate ( OutputMatchesVertex ) ;
}
}
return nullptr ;
}
const FMetasoundFrontendNode * FMetaSoundFrontendDocumentBuilder : : FindNode ( const FGuid & InNodeID ) const
{
using namespace Metasound : : Frontend ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
return NodeCache . FindNode ( InNodeID ) ;
}
TArray < const FMetasoundFrontendVertex * > FMetaSoundFrontendDocumentBuilder : : FindNodeInputs ( const FGuid & InNodeID , FName TypeName ) const
{
return DocumentCache - > GetNodeCache ( ) . FindNodeInputs ( InNodeID , TypeName ) ;
}
TArray < const FMetasoundFrontendVertex * > FMetaSoundFrontendDocumentBuilder : : FindNodeOutputs ( const FGuid & InNodeID , FName TypeName ) const
{
return DocumentCache - > GetNodeCache ( ) . FindNodeOutputs ( InNodeID , TypeName ) ;
}
const TScriptInterface < IMetaSoundDocumentInterface > FMetaSoundFrontendDocumentBuilder : : FindOrLoadNodeClassDocument ( const FGuid & InNodeID ) const
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
const FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
if ( const FMetasoundFrontendNode * Node = NodeCache . FindNode ( InNodeID ) )
{
if ( const FMetasoundFrontendClass * NodeClass = DocumentCache - > FindDependency ( Node - > ClassID ) )
{
const FNodeRegistryKey NodeClassRegistryKey = NodeRegistryKey : : CreateKey ( NodeClass - > Metadata ) ;
if ( FMetasoundAssetBase * Asset = IMetaSoundAssetManager : : GetChecked ( ) . TryLoadAssetFromKey ( NodeClassRegistryKey ) )
{
UObject * Object = Asset - > GetOwningAsset ( ) ;
check ( Object ) ;
return Object ;
}
}
}
return nullptr ;
}
FMetasoundFrontendDocument & FMetaSoundFrontendDocumentBuilder : : GetDocument ( )
{
IMetaSoundDocumentInterface * Interface = DocumentInterface . GetInterface ( ) ;
return Interface - > GetDocument ( ) ;
}
const FMetasoundFrontendDocument & FMetaSoundFrontendDocumentBuilder : : GetDocument ( ) const
{
return DocumentInterface - > GetDocument ( ) ;
}
void FMetaSoundFrontendDocumentBuilder : : InitGraphClassMetadata ( FMetasoundFrontendClassMetadata & InOutMetadata , bool bResetVersion )
{
InOutMetadata . SetClassName ( FMetasoundFrontendClassName ( FName ( ) , * FGuid : : NewGuid ( ) . ToString ( ) , FName ( ) ) ) ;
if ( bResetVersion )
{
InOutMetadata . SetVersion ( { 1 , 0 } ) ;
}
InOutMetadata . SetType ( EMetasoundFrontendClassType : : Graph ) ;
}
void FMetaSoundFrontendDocumentBuilder : : InitDocument ( FName UClassName )
{
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( FMetaSoundFrontendDocumentBuilder : : InitDocument ) ;
using namespace Metasound : : Frontend ;
// 1. Set default class Metadata
{
constexpr bool bResetVersion = true ;
FMetasoundFrontendClassMetadata & ClassMetadata = GetDocument ( ) . RootGraph . Metadata ;
InitGraphClassMetadata ( ClassMetadata , bResetVersion ) ;
}
// 2. Set default doc version Metadata
{
FMetasoundFrontendDocumentMetadata & DocMetadata = GetDocument ( ) . Metadata ;
DocMetadata . Version . Number = FMetasoundFrontendDocument : : GetMaxVersion ( ) ;
}
// 3. Add default interfaces for given UClass
{
TArray < FMetasoundFrontendInterface > InitInterfaces = ISearchEngine : : Get ( ) . FindUClassDefaultInterfaces ( UClassName ) ;
FModifyRootGraphInterfaces ModifyRootGraphInterfaces ( { } , InitInterfaces ) ;
ModifyRootGraphInterfaces . Transform ( GetDocument ( ) ) ;
}
// 5. Reload the whole cache as transforms above mutate cached collections. Can be safely removed once transforms above
// are re-implemented in builder and support cache to enable marginally better performance on document initialization.
ReloadCache ( ) ;
}
void FMetaSoundFrontendDocumentBuilder : : InitNodeLocations ( )
{
# if WITH_EDITORONLY_DATA
using namespace Metasound ;
using namespace Metasound : : Frontend ;
FMetasoundFrontendGraph & Graph = GetDocument ( ) . RootGraph . Graph ;
FVector2D InputNodeLocation = FVector2D : : ZeroVector ;
FVector2D ExternalNodeLocation = InputNodeLocation + DisplayStyle : : NodeLayout : : DefaultOffsetX ;
FVector2D OutputNodeLocation = ExternalNodeLocation + DisplayStyle : : NodeLayout : : DefaultOffsetX ;
TArray < FMetasoundFrontendNode > & Nodes = Graph . Nodes ;
for ( FMetasoundFrontendNode & Node : Nodes )
{
if ( const int32 * ClassIndex = DocumentCache - > FindDependencyIndex ( Node . ClassID ) )
{
FMetasoundFrontendClass & Class = GetDocument ( ) . Dependencies [ * ClassIndex ] ;
const EMetasoundFrontendClassType NodeType = Class . Metadata . GetType ( ) ;
FVector2D NewLocation ;
if ( NodeType = = EMetasoundFrontendClassType : : Input )
{
NewLocation = InputNodeLocation ;
InputNodeLocation + = DisplayStyle : : NodeLayout : : DefaultOffsetY ;
}
else if ( NodeType = = EMetasoundFrontendClassType : : Output )
{
NewLocation = OutputNodeLocation ;
OutputNodeLocation + = DisplayStyle : : NodeLayout : : DefaultOffsetY ;
}
else
{
NewLocation = ExternalNodeLocation ;
ExternalNodeLocation + = DisplayStyle : : NodeLayout : : DefaultOffsetY ;
}
// TODO: Find consistent location for controlling node locations.
// Currently it is split between MetasoundEditor and MetasoundFrontend modules.
FMetasoundFrontendNodeStyle & Style = Node . Style ;
Style . Display . Locations = { { FGuid : : NewGuid ( ) , NewLocation } } ;
}
}
# endif // WITH_EDITORONLY_DATA
}
bool FMetaSoundFrontendDocumentBuilder : : IsNodeInputConnected ( const FGuid & InNodeID , const FGuid & InVertexID ) const
{
return DocumentCache - > GetEdgeCache ( ) . IsNodeInputConnected ( InNodeID , InVertexID ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : IsNodeOutputConnected ( const FGuid & InNodeID , const FGuid & InVertexID ) const
{
return DocumentCache - > GetEdgeCache ( ) . IsNodeOutputConnected ( InNodeID , InVertexID ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : IsInterfaceDeclared ( FName InInterfaceName ) const
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendInterface Interface ;
if ( ISearchEngine : : Get ( ) . FindInterfaceWithHighestVersion ( InInterfaceName , Interface ) )
{
return IsInterfaceDeclared ( Interface . Version ) ;
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : IsInterfaceDeclared ( const FMetasoundFrontendVersion & InInterfaceVersion ) const
{
return GetDocument ( ) . Interfaces . Contains ( InInterfaceVersion ) ;
}
void FMetaSoundFrontendDocumentBuilder : : ReloadCache ( )
{
using namespace Metasound : : Frontend ;
DocumentCache = MakeUnique < FDocumentCache > ( GetDocument ( ) ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveDependency ( EMetasoundFrontendClassType ClassType , const FMetasoundFrontendClassName & InClassName , const FMetasoundFrontendVersionNumber & InClassVersionNumber )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
TArray < FMetasoundFrontendClass > & Dependencies = Document . Dependencies ;
const FNodeRegistryKey ClassKey = NodeRegistryKey : : CreateKey ( ClassType , InClassName . GetFullName ( ) . ToString ( ) , InClassVersionNumber . Major , InClassVersionNumber . Minor ) ;
if ( const int32 * IndexPtr = DocumentCache - > FindDependencyIndex ( ClassKey ) )
{
const int32 Index = * IndexPtr ;
TArray < const FMetasoundFrontendNode * > Nodes = NodeCache . FindNodesOfClassID ( Dependencies [ Index ] . ID ) ;
for ( const FMetasoundFrontendNode * Node : Nodes )
{
if ( ! RemoveNode ( Node - > GetID ( ) ) )
{
return false ;
}
}
const int32 LastIndex = Dependencies . Num ( ) - 1 ;
DocumentCache - > OnRemovingDependency ( Index ) ;
if ( Index ! = LastIndex )
{
DocumentCache - > OnRemovingDependency ( LastIndex ) ;
}
Dependencies . RemoveAtSwap ( Index , 1 , false ) ;
if ( Index ! = LastIndex )
{
DocumentCache - > OnDependencyAdded ( Index ) ;
}
}
return true ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveEdge ( const FMetasoundFrontendEdge & EdgeToRemove )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
TArray < FMetasoundFrontendEdge > & Edges = Graph . Edges ;
IDocumentGraphEdgeCache & EdgeCache = DocumentCache - > GetEdgeCache ( ) ;
if ( const int32 * IndexPtr = EdgeCache . FindEdgeIndexToNodeInput ( EdgeToRemove . ToNodeID , EdgeToRemove . ToVertexID ) )
{
const int32 Index = * IndexPtr ;
const int32 LastIndex = Edges . Num ( ) - 1 ;
EdgeCache . OnRemovingEdge ( Index ) ;
if ( Index ! = LastIndex )
{
EdgeCache . OnRemovingEdge ( LastIndex ) ;
}
Edges . RemoveAtSwap ( Index , 1 , false ) ;
if ( Index ! = LastIndex )
{
EdgeCache . OnEdgeAdded ( Index ) ;
}
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveEdgesByNamedConnections ( const TSet < Metasound : : Frontend : : FNamedEdge > & ConnectionsToRemove , TArray < FMetasoundFrontendEdge > * OutRemovedEdges )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
const IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
if ( OutRemovedEdges )
{
OutRemovedEdges - > Reset ( ) ;
}
bool bSuccess = true ;
TArray < FMetasoundFrontendEdge > EdgesToRemove ;
for ( const FNamedEdge & Connection : ConnectionsToRemove )
{
const FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
const FMetasoundFrontendVertex * OutputVertex = NodeCache . FindOutputVertex ( Connection . OutputNodeID , Connection . OutputName ) ;
const FMetasoundFrontendVertex * InputVertex = NodeCache . FindInputVertex ( Connection . InputNodeID , Connection . InputName ) ;
if ( OutputVertex & & InputVertex )
{
FMetasoundFrontendEdge NewEdge = { Connection . OutputNodeID , OutputVertex - > VertexID , Connection . InputNodeID , InputVertex - > VertexID } ;
if ( ContainsEdge ( NewEdge ) )
{
EdgesToRemove . Add ( MoveTemp ( NewEdge ) ) ;
}
else
{
bSuccess = false ;
UE_LOG ( LogMetaSound , Warning , TEXT ( " Failed to remove connections between MetaSound node output '%s' and input '%s': No connection found. " ) , * Connection . OutputName . ToString ( ) , * Connection . InputName . ToString ( ) ) ;
}
}
}
for ( FMetasoundFrontendEdge & EdgeToRemove : EdgesToRemove )
{
const bool bRemovedEdge = RemoveEdgeToNodeInput ( EdgeToRemove . ToNodeID , EdgeToRemove . ToVertexID ) ;
if ( ensureAlwaysMsgf ( bRemovedEdge , TEXT ( " Failed to remove MetaSound graph edge via DocumentBuilder when prior step validated edge remove was valid " ) ) )
{
if ( OutRemovedEdges )
{
OutRemovedEdges - > Add ( EdgeToRemove ) ;
}
}
else
{
bSuccess = false ;
}
}
return bSuccess ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveEdgesByNodeClassInterfaceBindings ( const FGuid & InFromNodeID , const FGuid & InToNodeID )
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
auto FindNodeClassDocAndInterfaces = [ this ] ( const FGuid & InNodeID ) - > const FMetasoundFrontendDocument *
{
const TScriptInterface < IMetaSoundDocumentInterface > NodeDocumentUInterface = FindOrLoadNodeClassDocument ( InNodeID ) ;
if ( NodeDocumentUInterface )
{
return & NodeDocumentUInterface - > GetDocument ( ) ;
}
return nullptr ;
} ;
const FMetasoundFrontendDocument * OutputNodeDocument = FindNodeClassDocAndInterfaces ( InFromNodeID ) ;
const FMetasoundFrontendDocument * InputNodeDocument = FindNodeClassDocAndInterfaces ( InToNodeID ) ;
if ( ! InputNodeDocument | | ! OutputNodeDocument )
{
return false ;
}
const TSet < FMetasoundFrontendVersion > & OutputInterfaceVersions = OutputNodeDocument - > Interfaces ;
TArray < const IInterfaceRegistryEntry * > InputNodeInterfaces ;
FindDeclaredInterfaces ( * InputNodeDocument , InputNodeInterfaces ) ;
bool bSuccess = true ;
TSet < FNamedEdge > ConnectionsToRemove ;
TSet < FName > InputsToDisconnect ;
for ( const IInterfaceRegistryEntry * InputInterfaceEntry : InputNodeInterfaces )
{
const FMetasoundFrontendInterface & InputInterface = InputInterfaceEntry - > GetInterface ( ) ;
const TArray < FMetasoundFrontendInterfaceBinding > & OutputBindings = InputInterfaceEntry - > GetOutputBindings ( ) ;
// Bindings are sorted in registry with earlier entries being higher priority to apply connections,
// so earlier listed connections are selected over potential collisions with later entries.
for ( const FMetasoundFrontendInterfaceBinding & OutputBinding : OutputBindings )
{
if ( OutputInterfaceVersions . Contains ( OutputBinding . Version ) )
{
for ( const FMetasoundFrontendInterfaceBindingConnection & Connection : OutputBinding . Connections )
{
if ( ! InputsToDisconnect . Contains ( Connection . InputName ) )
{
InputsToDisconnect . Add ( Connection . InputName ) ;
ConnectionsToRemove . Add ( FNamedEdge { InFromNodeID , Connection . OutputName , InToNodeID , Connection . InputName } ) ;
}
}
}
}
}
return RemoveEdgesByNamedConnections ( ConnectionsToRemove ) ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveEdgesFromNodeOutput ( const FGuid & InNodeID , const FGuid & InVertexID )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
IDocumentGraphEdgeCache & EdgeCache = DocumentCache - > GetEdgeCache ( ) ;
if ( const TArray < int32 > * Indices = EdgeCache . FindEdgeIndicesFromNodeOutput ( InNodeID , InVertexID ) )
{
TArray < int32 > IndicesCopy = * Indices ; // Copy off indices as the array may be modified when notifying the cache in the loop below
for ( int32 Index : IndicesCopy )
{
const int32 LastIndex = Graph . Edges . Num ( ) - 1 ;
EdgeCache . OnRemovingEdge ( Index ) ;
if ( Index ! = LastIndex )
{
EdgeCache . OnRemovingEdge ( LastIndex ) ;
}
Graph . Edges . RemoveAtSwap ( Index , 1 , false ) ;
if ( Index ! = LastIndex )
{
EdgeCache . OnEdgeAdded ( Index ) ;
}
}
return true ;
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveEdgeToNodeInput ( const FGuid & InNodeID , const FGuid & InVertexID )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
IDocumentGraphEdgeCache & EdgeCache = DocumentCache - > GetEdgeCache ( ) ;
FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
if ( const int32 * IndexPtr = EdgeCache . FindEdgeIndexToNodeInput ( InNodeID , InVertexID ) )
{
const int32 Index = * IndexPtr ; // Copy off indices as the pointer may be modified when notifying the cache below
const int32 LastIndex = Graph . Edges . Num ( ) - 1 ;
EdgeCache . OnRemovingEdge ( Index ) ;
if ( Index ! = LastIndex )
{
EdgeCache . OnRemovingEdge ( LastIndex ) ;
}
Graph . Edges . RemoveAtSwap ( Index , 1 , false ) ;
if ( Index ! = LastIndex )
{
EdgeCache . OnEdgeAdded ( Index ) ;
}
return true ;
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveInterface ( FName InterfaceName )
{
using namespace Metasound : : Frontend ;
FMetasoundFrontendInterface Interface ;
if ( ISearchEngine : : Get ( ) . FindInterfaceWithHighestVersion ( InterfaceName , Interface ) )
{
if ( ! GetDocument ( ) . Interfaces . Contains ( Interface . Version ) )
{
return true ;
}
const FModifyRootGraphInterfaces ModifyInterfaces ( { { Interface . Version } , { } } ) ;
if ( ModifyInterfaces . Transform ( GetDocument ( ) ) )
{
// TODO: Add ability to update local builder caches dynamically
// in ModifyRootGraphInterfaces transform once it moves away
// from using controllers internally. Then cache updates can
// be more isolated and not use this full rebuild call.
ReloadCache ( ) ;
return true ;
}
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : RemoveNode ( const FGuid & InNodeID )
{
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( FMetaSoundFrontendDocumentBuilder : : RemoveNode ) ;
using namespace Metasound : : Frontend ;
FMetasoundFrontendDocument & Document = GetDocument ( ) ;
IDocumentGraphNodeCache & NodeCache = DocumentCache - > GetNodeCache ( ) ;
FMetasoundFrontendGraph & Graph = Document . RootGraph . Graph ;
TArray < FMetasoundFrontendNode > & Nodes = Graph . Nodes ;
if ( const int32 * IndexPtr = NodeCache . FindNodeIndex ( InNodeID ) )
{
const int32 Index = * IndexPtr ; // Copy off indices as the pointer may be modified when notifying the cache below
const FMetasoundFrontendNode & Node = Nodes [ Index ] ;
for ( const FMetasoundFrontendVertex & Vertex : Node . Interface . Inputs )
{
if ( ! RemoveEdgeToNodeInput ( Node . GetID ( ) , Vertex . VertexID ) )
{
return false ;
}
}
for ( const FMetasoundFrontendVertex & Vertex : Node . Interface . Outputs )
{
RemoveEdgesFromNodeOutput ( Node . GetID ( ) , Vertex . VertexID ) ;
}
NodeCache . OnRemovingNode ( Index ) ;
const int32 LastIndex = Nodes . Num ( ) - 1 ;
if ( Index ! = LastIndex )
{
NodeCache . OnRemovingNode ( LastIndex ) ;
}
Nodes . RemoveAtSwap ( Index , 1 , false ) ;
if ( Index ! = LastIndex )
{
NodeCache . OnNodeAdded ( Index ) ;
}
return true ;
}
return false ;
}
# if WITH_EDITOR
void FMetaSoundFrontendDocumentBuilder : : SetAuthor ( const FString & InAuthor )
{
FMetasoundFrontendClassMetadata & ClassMetadata = GetDocument ( ) . RootGraph . Metadata ;
ClassMetadata . SetAuthor ( InAuthor ) ;
}
# endif // WITH_EDITOR
bool FMetaSoundFrontendDocumentBuilder : : SetNodeInputDefault ( const FGuid & InNodeID , const FGuid & InVertexID , const FMetasoundFrontendLiteral & InLiteral )
{
using namespace Metasound : : Frontend ;
if ( const int32 * Index = DocumentCache - > GetNodeCache ( ) . FindNodeIndex ( InNodeID ) )
{
FMetasoundFrontendGraph & Graph = GetDocument ( ) . RootGraph . Graph ;
FMetasoundFrontendNode & Node = Graph . Nodes [ * Index ] ;
TArray < FMetasoundFrontendVertex > & Inputs = Node . Interface . Inputs ;
auto IsVertex = [ & InVertexID ] ( const FMetasoundFrontendVertex & Vertex ) { return Vertex . VertexID = = InVertexID ; } ;
if ( Inputs . ContainsByPredicate ( IsVertex ) )
{
FMetasoundFrontendVertexLiteral NewVertexLiteral ;
NewVertexLiteral . VertexID = InVertexID ;
NewVertexLiteral . Value = InLiteral ;
auto IsLiteral = [ & InVertexID ] ( const FMetasoundFrontendVertexLiteral & Literal ) { return Literal . VertexID = = InVertexID ; } ;
if ( FMetasoundFrontendVertexLiteral * InputLiteral = Node . InputLiterals . FindByPredicate ( IsLiteral ) )
{
* InputLiteral = MoveTemp ( NewVertexLiteral ) ;
}
else
{
Node . InputLiterals . Add ( MoveTemp ( NewVertexLiteral ) ) ;
}
return true ;
}
}
return false ;
}
bool FMetaSoundFrontendDocumentBuilder : : ModifyInterfaces ( TArray < FMetasoundFrontendVersion > OutputFormatsToRemove , TArray < FMetasoundFrontendVersion > OutputFormatsToAdd )
{
using namespace Metasound : : Frontend ;
// Integrating this transform into the builder will allow for more selective updates and not having to rebuild the cache from scratch after performing the transform.
FModifyRootGraphInterfaces ModifyRootGraph ( OutputFormatsToRemove , OutputFormatsToAdd ) ;
if ( ModifyRootGraph . Transform ( GetDocument ( ) ) )
{
ReloadCache ( ) ;
return true ;
}
return false ;
}