2020-12-04 11:19:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundFrontendGraph.h"
# include "Algo/AllOf.h"
# include "Algo/AnyOf.h"
# include "Algo/Transform.h"
# include "CoreMinimal.h"
# include "MetasoundFrontend.h"
# include "MetasoundGraph.h"
2021-01-13 10:48:59 -04:00
# include "MetasoundLog.h"
2020-12-04 11:19:17 -04:00
# include "MetasoundNodeInterface.h"
namespace Metasound
{
2021-01-28 19:02:51 -04:00
FFrontendGraph : : FFrontendGraph ( const FString & InInstanceName , const FGuid & InInstanceID )
: FGraph ( InInstanceName , InInstanceID )
2020-12-04 11:19:17 -04:00
{
}
2021-01-20 17:26:40 -04:00
void FFrontendGraph : : AddInputNode ( FGuid InDependencyId , int32 InIndex , const FVertexKey & InVertexKey , TUniquePtr < INode > InNode )
2020-12-04 11:19:17 -04:00
{
if ( InNode . IsValid ( ) )
{
// There shouldn't be duplicate IDs.
check ( ! InputNodes . Contains ( InIndex ) ) ;
// Input nodes need an extra Index value to keep track of their position in the graph's inputs.
InputNodes . Add ( InIndex , InNode . Get ( ) ) ;
AddInputDataDestination ( * InNode , InVertexKey ) ;
AddNode ( InDependencyId , MoveTemp ( InNode ) ) ;
}
}
2021-01-20 17:26:40 -04:00
void FFrontendGraph : : AddOutputNode ( FGuid InNodeID , int32 InIndex , const FVertexKey & InVertexKey , TUniquePtr < INode > InNode )
2020-12-04 11:19:17 -04:00
{
if ( InNode . IsValid ( ) )
{
// There shouldn't be duplicate IDs.
check ( ! OutputNodes . Contains ( InIndex ) ) ;
// Output nodes need an extra Index value to keep track of their position in the graph's inputs.
OutputNodes . Add ( InIndex , InNode . Get ( ) ) ;
AddOutputDataSource ( * InNode , InVertexKey ) ;
AddNode ( InNodeID , MoveTemp ( InNode ) ) ;
}
}
/** Store a node on this graph. */
2021-01-20 17:26:40 -04:00
void FFrontendGraph : : AddNode ( FGuid InNodeID , TUniquePtr < INode > InNode )
2020-12-04 11:19:17 -04:00
{
if ( InNode . IsValid ( ) )
{
// There shouldn't be duplicate IDs.
check ( ! NodeMap . Contains ( InNodeID ) ) ;
NodeMap . Add ( InNodeID , InNode . Get ( ) ) ;
StoreNode ( MoveTemp ( InNode ) ) ;
}
}
2021-01-20 17:26:40 -04:00
const INode * FFrontendGraph : : FindNode ( FGuid InNodeID ) const
2020-12-04 11:19:17 -04:00
{
INode * const * NodePtr = NodeMap . Find ( InNodeID ) ;
if ( nullptr ! = NodePtr )
{
return * NodePtr ;
}
return nullptr ;
}
const INode * FFrontendGraph : : FindInputNode ( int32 InIndex ) const
{
INode * const * NodePtr = InputNodes . Find ( InIndex ) ;
if ( nullptr ! = NodePtr )
{
return * NodePtr ;
}
return nullptr ;
}
const INode * FFrontendGraph : : FindOutputNode ( int32 InIndex ) const
{
INode * const * NodePtr = OutputNodes . Find ( InIndex ) ;
if ( nullptr ! = NodePtr )
{
return * NodePtr ;
}
return nullptr ;
}
/** Returns true if all edges, destinations and sources refer to
* nodes stored in this graph . */
bool FFrontendGraph : : OwnsAllReferencedNodes ( ) const
{
const TArray < FDataEdge > & AllEdges = GetDataEdges ( ) ;
for ( const FDataEdge & Edge : AllEdges )
{
if ( ! StoredNodes . Contains ( Edge . From . Node ) )
{
return false ;
}
if ( ! StoredNodes . Contains ( Edge . To . Node ) )
{
return false ;
}
}
const FInputDataDestinationCollection & AllInputDestinations = GetInputDataDestinations ( ) ;
for ( auto & DestTuple : AllInputDestinations )
{
if ( ! StoredNodes . Contains ( DestTuple . Value . Node ) )
{
return false ;
}
}
const FOutputDataSourceCollection & AllOutputSources = GetOutputDataSources ( ) ;
for ( auto & SourceTuple : AllOutputSources )
{
if ( ! StoredNodes . Contains ( SourceTuple . Value . Node ) )
{
return false ;
}
}
return true ;
}
void FFrontendGraph : : StoreNode ( TUniquePtr < INode > InNode )
{
check ( InNode . IsValid ( ) ) ;
StoredNodes . Add ( InNode . Get ( ) ) ;
Storage . Add ( MoveTemp ( InNode ) ) ;
}
2021-01-13 10:48:59 -04:00
TUniquePtr < INode > FFrontendGraphBuilder : : CreateInputNode ( const FMetasoundFrontendNode & InNode , const FMetasoundFrontendClass & InClass , const FMetasoundFrontendClassInput & InOwningGraphClassInput ) const
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
const FMetasoundFrontendVertexLiteral * VertexLiteral = FindInputLiteralForInputNode ( InNode , InClass , InOwningGraphClassInput ) ;
if ( nullptr ! = VertexLiteral )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
if ( ensure ( InNode . Interface . Inputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendVertex & InputVertex = InNode . Interface . Inputs [ 0 ] ;
const bool IsLiteralParsableByDataType = Frontend : : DoesDataTypeSupportLiteralType ( InputVertex . TypeName , VertexLiteral - > Value . Type ) ;
if ( IsLiteralParsableByDataType )
{
FLiteral Literal = Frontend : : GetLiteralParamForDataType ( InputVertex . TypeName , VertexLiteral - > Value ) ;
FInputNodeConstructorParams InitParams =
{
InNode . Name ,
2021-01-28 19:02:51 -04:00
InNode . ID ,
2021-01-13 10:48:59 -04:00
InputVertex . Name ,
MoveTemp ( Literal )
} ;
// TODO: create input node using external class definition
return FMetasoundFrontendRegistryContainer : : Get ( ) - > ConstructInputNode ( InputVertex . TypeName , MoveTemp ( InitParams ) ) ;
}
else
{
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Error , TEXT ( " Cannot create input node [NodeID:%s]. [Vertex:%s] cannot be constructed with the provided literal type. " ) , * InNode . ID . ToString ( ) , * InputVertex . Name ) ;
2021-01-13 10:48:59 -04:00
}
}
}
else
{
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Error , TEXT ( " Cannot create input node [NodeID:%s]. No default literal set for input node. " ) , * InNode . ID . ToString ( ) ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
return TUniquePtr < INode > ( nullptr ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
TUniquePtr < INode > FFrontendGraphBuilder : : CreateOutputNode ( const FMetasoundFrontendNode & InNode , const FMetasoundFrontendClass & InClass ) const
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
check ( InClass . Metadata . Type = = EMetasoundFrontendClassType : : Output ) ;
check ( InNode . ClassID = = InClass . ID ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( ensure ( InNode . Interface . Outputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendVertex & OutputVertex = InNode . Interface . Outputs [ 0 ] ;
FOutputNodeConstructorParams InitParams =
{
InNode . Name ,
2021-01-28 19:02:51 -04:00
InNode . ID ,
2021-01-13 10:48:59 -04:00
OutputVertex . Name ,
} ;
// TODO: create output node using external class definition
return FMetasoundFrontendRegistryContainer : : Get ( ) - > ConstructOutputNode ( OutputVertex . TypeName , InitParams ) ;
}
return TUniquePtr < INode > ( nullptr ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
TUniquePtr < INode > FFrontendGraphBuilder : : CreateExternalNode ( const FMetasoundFrontendNode & InNode , const FMetasoundFrontendClass & InClass ) const
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
check ( InClass . Metadata . Type = = EMetasoundFrontendClassType : : External ) ;
check ( InNode . ClassID = = InClass . ID ) ;
2020-12-04 11:19:17 -04:00
FNodeInitData InitData ;
InitData . InstanceName . Append ( InNode . Name ) ;
InitData . InstanceName . AppendChar ( ' _ ' ) ;
2021-01-20 17:26:40 -04:00
InitData . InstanceName . Append ( InNode . ID . ToString ( ) ) ;
2021-01-13 10:48:59 -04:00
2021-01-28 19:02:51 -04:00
InitData . InstanceID = InNode . ID ;
2020-12-04 11:19:17 -04:00
// Copy over our initialization params.
2021-01-13 10:48:59 -04:00
/*
2020-12-04 11:19:17 -04:00
for ( auto & StaticParamTuple : InNode . StaticParameters )
{
2021-01-13 10:48:59 -04:00
FLiteral LiteralParam = Frontend : : GetLiteralParam ( StaticParamTuple . Value ) ;
2020-12-04 11:19:17 -04:00
if ( LiteralParam . IsValid ( ) )
{
InitData . ParamMap . Add ( StaticParamTuple . Key , MoveTemp ( LiteralParam ) ) ;
}
}
2021-01-13 10:48:59 -04:00
*/
// TODO: handle check to see if node interface conforms to class interface here.
// TODO: check to see if external object supports class interface.
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
Metasound : : Frontend : : FNodeRegistryKey Key = FMetasoundFrontendRegistryContainer : : GetRegistryKey ( InClass . Metadata ) ;
2021-01-28 19:02:51 -04:00
return FMetasoundFrontendRegistryContainer : : Get ( ) - > ConstructExternalNode ( Key . NodeClassFullName , Key . NodeHash , InitData ) ;
2021-01-13 10:48:59 -04:00
}
const FMetasoundFrontendClassInput * FFrontendGraphBuilder : : FindClassInputForInputNode ( const FMetasoundFrontendGraphClass & InOwningGraph , const FMetasoundFrontendNode & InInputNode , int32 & OutClassInputIndex ) const
{
OutClassInputIndex = INDEX_NONE ;
// TODO: assumes input node has exactly one input
if ( ensure ( InInputNode . Interface . Inputs . Num ( ) = = 1 ) )
{
const FName & TypeName = InInputNode . Interface . Inputs [ 0 ] . TypeName ;
auto IsMatchingInput = [ & ] ( const FMetasoundFrontendClassInput & GraphInput )
{
return ( InInputNode . ID = = GraphInput . NodeID ) ;
} ;
OutClassInputIndex = InOwningGraph . Interface . Inputs . IndexOfByPredicate ( IsMatchingInput ) ;
if ( INDEX_NONE ! = OutClassInputIndex )
{
return & InOwningGraph . Interface . Inputs [ OutClassInputIndex ] ;
}
}
return nullptr ;
}
const FMetasoundFrontendClassOutput * FFrontendGraphBuilder : : FindClassOutputForOutputNode ( const FMetasoundFrontendGraphClass & InOwningGraph , const FMetasoundFrontendNode & InOutputNode , int32 & OutClassOutputIndex ) const
{
OutClassOutputIndex = INDEX_NONE ;
// TODO: assumes input node has exactly one input
if ( ensure ( InOutputNode . Interface . Outputs . Num ( ) = = 1 ) )
{
const FName & TypeName = InOutputNode . Interface . Outputs [ 0 ] . TypeName ;
auto IsMatchingOutput = [ & ] ( const FMetasoundFrontendClassOutput & GraphOutput )
{
return ( InOutputNode . ID = = GraphOutput . NodeID ) ;
} ;
OutClassOutputIndex = InOwningGraph . Interface . Outputs . IndexOfByPredicate ( IsMatchingOutput ) ;
if ( INDEX_NONE ! = OutClassOutputIndex )
{
return & InOwningGraph . Interface . Outputs [ OutClassOutputIndex ] ;
}
}
return nullptr ;
}
2021-01-20 17:26:40 -04:00
/*
const FMetasoundFrontendVertexLiteral * FFrontendGraphBuilder : : FindInputLiteralForInput ( FGuid InPointID , const FMetasoundFrontendNode & InNode , const FMetasoundFrontendClass & InNodeClass ) const
{
// TODO: Need utility for matching class input to node input. Will need to be cognisant of input type [Point, Array, ....]
// Default value priority is:
// 1. A value set directly on the node
// 3. A default value on the node class.
const FMetasoundFrontendVertexLiteral * VertexLiteral = nullptr ;
// Check for default value directly on node.
if ( ensure ( InNode . Interface . Inputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendVertex & InputVertex = InNode . Interface . Inputs [ 0 ] ;
// Currently only support one point on input vertex.
if ( ensure ( InputVertex . PointIDs . Num ( ) = = 1 ) )
{
VertexLiteral = InNode . InputLiterals . FindByPredicate (
[ & ] ( const FMetasoundFrontendVertexLiteral & InVertexLiteral )
{
return InVertexLiteral . PointID = = InputVertex . PointIDs [ 0 ] ;
}
) ;
}
}
// Check for default value on input node class
if ( nullptr = = VertexLiteral & & ensure ( InNodeClass . Interface . Inputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendClassInput & InputNodeClassInput = InNodeClass . Interface . Inputs [ 0 ] ;
if ( ensure ( InputNodeClassInput . PointIDs . Num ( ) = = 1 ) )
{
int32 PointID = InputNodeClassInput . PointIDs [ 0 ] ;
VertexLiteral = InputNodeClassInput . Defaults . FindByPredicate (
[ & ] ( const FMetasoundFrontendVertexLiteral & InVertexLiteral )
{
return InVertexLiteral . PointID = = PointID ;
}
) ;
}
}
return VertexLiteral ;
return nullptr ;
}
*/
2021-01-13 10:48:59 -04:00
const FMetasoundFrontendVertexLiteral * FFrontendGraphBuilder : : FindInputLiteralForInputNode ( const FMetasoundFrontendNode & InInputNode , const FMetasoundFrontendClass & InInputNodeClass , const FMetasoundFrontendClassInput & InOwningGraphClassInput ) const
{
// Default value priority is:
// 1. A value set directly on the node
// 2. A default value of the owning graph
// 3. A default value on the input node class.
const FMetasoundFrontendVertexLiteral * VertexLiteral = nullptr ;
// Check for default value directly on node.
if ( ensure ( InInputNode . Interface . Inputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendVertex & InputVertex = InInputNode . Interface . Inputs [ 0 ] ;
// Currently only support one point on input vertex.
if ( ensure ( InputVertex . PointIDs . Num ( ) = = 1 ) )
{
VertexLiteral = InInputNode . InputLiterals . FindByPredicate (
[ & ] ( const FMetasoundFrontendVertexLiteral & InVertexLiteral )
{
return InVertexLiteral . PointID = = InputVertex . PointIDs [ 0 ] ;
}
) ;
}
}
// Check for default value on owning graph.
if ( nullptr = = VertexLiteral )
{
if ( ensure ( InOwningGraphClassInput . PointIDs . Num ( ) = = 1 ) )
{
2021-01-20 17:26:40 -04:00
const FGuid & PointID = InOwningGraphClassInput . PointIDs [ 0 ] ;
2021-01-13 10:48:59 -04:00
VertexLiteral = InOwningGraphClassInput . Defaults . FindByPredicate (
[ & ] ( const FMetasoundFrontendVertexLiteral & InVertexLiteral )
{
return InVertexLiteral . PointID = = PointID ;
}
) ;
}
}
// Check for default value on input node class
if ( nullptr = = VertexLiteral & & ensure ( InInputNodeClass . Interface . Inputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendClassInput & InputNodeClassInput = InInputNodeClass . Interface . Inputs [ 0 ] ;
if ( ensure ( InputNodeClassInput . PointIDs . Num ( ) = = 1 ) )
{
2021-01-20 17:26:40 -04:00
const FGuid & PointID = InputNodeClassInput . PointIDs [ 0 ] ;
2021-01-13 10:48:59 -04:00
VertexLiteral = InputNodeClassInput . Defaults . FindByPredicate (
[ & ] ( const FMetasoundFrontendVertexLiteral & InVertexLiteral )
{
return InVertexLiteral . PointID = = PointID ;
}
) ;
}
}
return VertexLiteral ;
2020-12-04 11:19:17 -04:00
}
// TODO: add errors here. Most will be a "PromptIfMissing"...
2021-01-20 17:26:40 -04:00
void FFrontendGraphBuilder : : AddNodesToGraph ( const FMetasoundFrontendGraphClass & InGraphClass , const TMap < FGuid , const FMetasoundFrontendClass * > & InClasses , FFrontendGraph & OutGraph ) const
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
for ( const FMetasoundFrontendNode & Node : InGraphClass . Graph . Nodes )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
const FMetasoundFrontendClass * NodeClass = InClasses . FindRef ( Node . ClassID ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( ensure ( nullptr ! = NodeClass ) )
{
switch ( NodeClass - > Metadata . Type )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
case EMetasoundFrontendClassType : : Input :
{
int32 InputIndex = INDEX_NONE ;
const FMetasoundFrontendClassInput * ClassInput = FindClassInputForInputNode ( InGraphClass , Node , InputIndex ) ;
if ( ( nullptr ! = ClassInput ) & & ( INDEX_NONE ! = InputIndex ) )
{
OutGraph . AddInputNode ( Node . ID , InputIndex , ClassInput - > Name , CreateInputNode ( Node , * NodeClass , * ClassInput ) ) ;
}
else
{
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Error , TEXT ( " Failed to match input node [NodeID:%s, NodeName:%s] to owning graph [ClassID:%s] output. " ) , * Node . ID . ToString ( ) , * Node . Name , * InGraphClass . ID . ToString ( ) ) ;
2021-01-13 10:48:59 -04:00
}
}
break ;
case EMetasoundFrontendClassType : : Output :
{
int32 OutputIndex = INDEX_NONE ;
const FMetasoundFrontendClassOutput * ClassOutput = FindClassOutputForOutputNode ( InGraphClass , Node , OutputIndex ) ;
if ( ( nullptr ! = ClassOutput ) & & ( INDEX_NONE ! = OutputIndex ) )
{
OutGraph . AddOutputNode ( Node . ID , OutputIndex , ClassOutput - > Name , CreateOutputNode ( Node , * NodeClass ) ) ;
}
else
{
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Error , TEXT ( " Failed to match output node [NodeID:%s, NodeName:%s] to owning graph [ClassID:%s] output. " ) , * Node . ID . ToString ( ) , * Node . Name , * InGraphClass . ID . ToString ( ) ) ;
2021-01-13 10:48:59 -04:00
}
}
break ;
default :
OutGraph . AddNode ( Node . ID , CreateExternalNode ( Node , * NodeClass ) ) ;
2020-12-04 11:19:17 -04:00
}
}
}
}
2021-01-13 10:48:59 -04:00
void FFrontendGraphBuilder : : AddEdgesToGraph ( const FMetasoundFrontendGraph & InFrontendGraph , FFrontendGraph & OutGraph ) const
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
// Pair of frontend node and core node. The frontend node can be one of
// several types.
struct FCoreNodeAndFrontendVertex
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
const INode * Node = nullptr ;
const FMetasoundFrontendVertex * Vertex = nullptr ;
} ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
// TODO: add support for array vertices.
2021-01-20 17:26:40 -04:00
typedef TTuple < FGuid , FGuid > FNodeIDPointID ;
2021-01-13 10:48:59 -04:00
TMap < FNodeIDPointID , FCoreNodeAndFrontendVertex > NodeSourcesByID ;
TMap < FNodeIDPointID , FCoreNodeAndFrontendVertex > NodeDestinationsByID ;
// Add nodes to NodeID/PointID map
for ( const FMetasoundFrontendNode & Node : InFrontendGraph . Nodes )
{
const INode * CoreNode = OutGraph . FindNode ( Node . ID ) ;
if ( nullptr = = CoreNode )
2020-12-04 11:19:17 -04:00
{
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Display , TEXT ( " Could not find referenced node [NodeID:%s] " ) , * Node . ID . ToString ( ) ) ;
2020-12-04 11:19:17 -04:00
continue ;
}
2021-01-13 10:48:59 -04:00
for ( const FMetasoundFrontendVertex & Vertex : Node . Interface . Inputs )
2020-12-04 11:19:17 -04:00
{
2021-01-20 17:26:40 -04:00
for ( const FGuid & PointID : Vertex . PointIDs )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
NodeDestinationsByID . Add ( FNodeIDPointID ( Node . ID , PointID ) , FCoreNodeAndFrontendVertex ( { CoreNode , & Vertex } ) ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
}
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
for ( const FMetasoundFrontendVertex & Vertex : Node . Interface . Outputs )
{
2021-01-20 17:26:40 -04:00
for ( const FGuid & PointID : Vertex . PointIDs )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
NodeSourcesByID . Add ( FNodeIDPointID ( Node . ID , PointID ) , FCoreNodeAndFrontendVertex ( { CoreNode , & Vertex } ) ) ;
2020-12-04 11:19:17 -04:00
}
}
2021-01-13 10:48:59 -04:00
} ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
for ( const FMetasoundFrontendEdge & Edge : InFrontendGraph . Edges )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
const FNodeIDPointID DestinationKey ( Edge . ToNodeID , Edge . ToPointID ) ;
const FCoreNodeAndFrontendVertex * DestinationNodeAndVertex = NodeDestinationsByID . Find ( DestinationKey ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( nullptr = = DestinationNodeAndVertex )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
// TODO: bubble up error
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Error , TEXT ( " Failed to add edge. Could not find destination [NodeID:%s, PointID:%s] " ) , * Edge . ToNodeID . ToString ( ) , * Edge . ToPointID . ToString ( ) ) ;
2020-12-04 11:19:17 -04:00
continue ;
}
2021-01-13 10:48:59 -04:00
if ( nullptr = = DestinationNodeAndVertex - > Node )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
// TODO: bubble up error
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Warning , TEXT ( " Skipping edge. Null destination node [NodeID:%s] " ) , * Edge . ToNodeID . ToString ( ) ) ;
2020-12-04 11:19:17 -04:00
continue ;
}
2021-01-13 10:48:59 -04:00
const FNodeIDPointID SourceKey ( Edge . FromNodeID , Edge . FromPointID ) ;
const FCoreNodeAndFrontendVertex * SourceNodeAndVertex = NodeSourcesByID . Find ( SourceKey ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( nullptr = = SourceNodeAndVertex )
2020-12-04 11:19:17 -04:00
{
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Error , TEXT ( " Failed to add edge. Could not find source [NodeID:%s, PointID:%s] " ) , * Edge . FromNodeID . ToString ( ) , * Edge . FromPointID . ToString ( ) ) ;
2021-01-13 10:48:59 -04:00
continue ;
}
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( nullptr = = SourceNodeAndVertex - > Node )
{
// TODO: bubble up error
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Warning , TEXT ( " Skipping edge. Null source node [NodeID:%s] " ) , * Edge . FromNodeID . ToString ( ) ) ;
2021-01-13 10:48:59 -04:00
continue ;
}
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
const INode * FromNode = SourceNodeAndVertex - > Node ;
const FVertexKey FromVertexKey = SourceNodeAndVertex - > Vertex - > Name ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
const INode * ToNode = DestinationNodeAndVertex - > Node ;
const FVertexKey ToVertexKey = DestinationNodeAndVertex - > Vertex - > Name ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
bool bSuccess = OutGraph . AddDataEdge ( * FromNode , FromVertexKey , * ToNode , ToVertexKey ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( ! bSuccess )
{
2021-01-20 17:26:40 -04:00
UE_LOG ( LogMetasound , Error , TEXT ( " Failed to connect edge from [NodeID:%s, PointID:%s] to [NodeID:%s, PointID:%s] " ) , * Edge . FromNodeID . ToString ( ) , * Edge . FromPointID . ToString ( ) , * Edge . ToNodeID . ToString ( ) , * Edge . ToPointID . ToString ( ) ) ;
2020-12-04 11:19:17 -04:00
}
}
}
/** Check that all dependencies are C++ class dependencies. */
2021-01-13 10:48:59 -04:00
bool FFrontendGraphBuilder : : IsFlat ( const FMetasoundFrontendDocument & InDocument ) const
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
if ( InDocument . Subgraphs . Num ( ) > 0 )
{
return false ;
}
return IsFlat ( InDocument . RootGraph , InDocument . Dependencies ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
bool FFrontendGraphBuilder : : IsFlat ( const FMetasoundFrontendGraphClass & InRoot , const TArray < FMetasoundFrontendClass > & InDependencies ) const
2020-12-04 11:19:17 -04:00
{
// All dependencies are external dependencies in a flat graph
2021-01-13 10:48:59 -04:00
auto IsClassExternal = [ & ] ( const FMetasoundFrontendClass & InDesc )
{
bool bIsExternal = ( InDesc . Metadata . Type = = EMetasoundFrontendClassType : : External ) | |
( InDesc . Metadata . Type = = EMetasoundFrontendClassType : : Input ) | |
( InDesc . Metadata . Type = = EMetasoundFrontendClassType : : Output ) ;
return bIsExternal ;
} ;
2020-12-04 11:19:17 -04:00
const bool bIsEveryDependencyExternal = Algo : : AllOf ( InDependencies , IsClassExternal ) ;
if ( ! bIsEveryDependencyExternal )
{
return false ;
}
2021-01-13 10:48:59 -04:00
// All the dependencies are met
2021-01-20 17:26:40 -04:00
TSet < FGuid > AvailableDependencies ;
2021-01-13 10:48:59 -04:00
Algo : : Transform ( InDependencies , AvailableDependencies , [ ] ( const FMetasoundFrontendClass & InDesc ) { return InDesc . ID ; } ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
auto IsDependencyMet = [ & ] ( const FMetasoundFrontendNode & InNode )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
return AvailableDependencies . Contains ( InNode . ClassID ) ;
2020-12-04 11:19:17 -04:00
} ;
const bool bIsEveryDependencyMet = Algo : : AllOf ( InRoot . Graph . Nodes , IsDependencyMet ) ;
2021-01-13 10:48:59 -04:00
2020-12-04 11:19:17 -04:00
return bIsEveryDependencyMet ;
}
2021-01-13 10:48:59 -04:00
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendGraphClass & InGraph , const TArray < FMetasoundFrontendGraphClass > & InSubgraphs , const TArray < FMetasoundFrontendClass > & InDependencies ) const
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
if ( ! IsFlat ( InGraph , InDependencies ) )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
// Likely this will change in the future and the builder will be able
// to build graphs with subgraphs..
UE_LOG ( LogMetasound , Error , TEXT ( " Provided graph not flat. FFrontendGraphBuilder can only build flat graphs " ) ) ;
2020-12-04 11:19:17 -04:00
return TUniquePtr < FFrontendGraph > ( nullptr ) ;
}
2021-01-20 17:26:40 -04:00
TMap < FGuid , const FMetasoundFrontendClass * > ClassMap ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
for ( const FMetasoundFrontendClass & ExtClass : InDependencies )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
ClassMap . Add ( ExtClass . ID , & ExtClass ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-28 19:02:51 -04:00
TUniquePtr < FFrontendGraph > MetasoundGraph = MakeUnique < FFrontendGraph > ( InGraph . Metadata . ClassName . GetFullName ( ) . ToString ( ) , FGuid : : NewGuid ( ) ) ;
2020-12-04 11:19:17 -04:00
// TODO: will likely want to bubble up errors here for case where
// a datatype or node is not registered.
2021-01-13 10:48:59 -04:00
AddNodesToGraph ( InGraph , ClassMap , * MetasoundGraph ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
AddEdgesToGraph ( InGraph . Graph , * MetasoundGraph ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
check ( MetasoundGraph - > OwnsAllReferencedNodes ( ) ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
return MoveTemp ( MetasoundGraph ) ;
}
/* Metasound document should be inflated by now. */
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendDocument & InDocument ) const
{
return CreateGraph ( InDocument . RootGraph , InDocument . Subgraphs , InDocument . Dependencies ) ;
2020-12-04 11:19:17 -04:00
}
}