Files
UnrealEngine/Engine/Plugins/Runtime/Metasound/Source/MetasoundFrontend/Private/MetasoundFrontendSubgraphNodeController.cpp
2025-05-18 13:04:45 +08:00

238 lines
9.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MetasoundFrontendSubgraphNodeController.h"
#include "MetasoundFrontendInputController.h"
#include "MetasoundFrontendOutputController.h"
#include "Algo/Transform.h"
namespace Metasound
{
namespace Frontend
{
namespace FrontendSubgraphNodeControllerPrivate
{
template<typename SetAType, typename SetBType>
void InplaceBidirectionalVertexSetDifference(TArray<const SetAType*>& SetA, TArray<const SetBType*>& SetB)
{
// TODO: comparison is done by `Name`. May update to a `ClassVertexID` in the future.
// Reverse iterate to allow removal of elements from SetA within loop.
for (int32 SetAIndex = (SetA.Num() - 1); SetAIndex >= 0; SetAIndex--)
{
const FVertexName& SetAName = SetA[SetAIndex]->Name;
int32 SetBIndex = SetB.IndexOfByPredicate([&](const SetBType* InVertex) { return InVertex->Name == SetAName; });
if (INDEX_NONE != SetBIndex)
{
SetA.RemoveAt(SetAIndex);
SetB.RemoveAt(SetBIndex);
}
}
}
// Manipulates the NodeVertexArray to match the ClassVertexArray.
template<typename NodeVertexType, typename ClassVertexType>
bool ConformNodeVertexArrayToClassVertexArray(TArray<NodeVertexType>& NodeVertexArray, const TArray<ClassVertexType>& ClassVertexArray)
{
// TODO: May need to change this to support dynamic pins.
bool bDidAlterNodeVertexArray = false;
TArray<const NodeVertexType*> NodeVerticesToRemove;
Algo::Transform(NodeVertexArray, NodeVerticesToRemove, [](const NodeVertexType& Vertex) { return &Vertex; });
TArray<const ClassVertexType*> ClassVerticesToAdd;
Algo::Transform(ClassVertexArray, ClassVerticesToAdd, [](const ClassVertexType& Vertex) { return &Vertex; });
FrontendSubgraphNodeControllerPrivate::InplaceBidirectionalVertexSetDifference(NodeVerticesToRemove, ClassVerticesToAdd);
bDidAlterNodeVertexArray = (NodeVerticesToRemove.Num() > 0) || (ClassVerticesToAdd.Num() > 0);
// Remove extra node vertices.
for (const NodeVertexType* VertexToRemove : NodeVerticesToRemove)
{
NodeVertexArray.RemoveAll([&](const NodeVertexType& InVertex) { return InVertex.VertexID == VertexToRemove->VertexID; });
}
// Add missing node vertices.
for (const ClassVertexType* VertexToAdd : ClassVerticesToAdd)
{
NodeVertexType NewNodeVertex(*VertexToAdd);
NewNodeVertex.VertexID = FGuid::NewGuid();
NodeVertexArray.Add(NewNodeVertex);
}
return bDidAlterNodeVertexArray;
}
}
//
// FSubgraphNodeController
//
FSubgraphNodeController::FSubgraphNodeController(EPrivateToken InToken, const FSubgraphNodeController::FInitParams& InParams)
: FBaseNodeController({InParams.NodePtr, InParams.ClassPtr, InParams.OwningGraph})
, GraphPtr(InParams.GraphPtr)
{
}
FNodeHandle FSubgraphNodeController::CreateNodeHandle(const FSubgraphNodeController::FInitParams& InParams)
{
if (FMetasoundFrontendNode* Node = InParams.NodePtr.Get())
{
if (const FMetasoundFrontendClass* Class = InParams.ClassPtr.Get())
{
// Cannot make a valid node handle if the node description and class description differ
if (Node->ClassID == Class->ID)
{
return MakeShared<FSubgraphNodeController>(EPrivateToken::Token, InParams);
}
else
{
UE_LOG(LogMetaSound, Warning, TEXT("Frontend Node [NodeID:%s, ClassID:%s] is not of expected class class [ClassID:%s]"), *Node->GetID().ToString(), *Node->ClassID.ToString(), *Class->ID.ToString());
}
}
}
return INodeController::GetInvalidHandle();
}
FConstNodeHandle FSubgraphNodeController::CreateConstNodeHandle(const FSubgraphNodeController::FInitParams& InParams)
{
if (FMetasoundFrontendNode* Node = InParams.NodePtr.Get())
{
if (const FMetasoundFrontendClass* Class = InParams.ClassPtr.Get())
{
// Cannot make a valid node handle if the node description and class description differ
if (Node->ClassID == Class->ID)
{
return MakeShared<const FSubgraphNodeController>(EPrivateToken::Token, InParams);
}
else
{
UE_LOG(LogMetaSound, Warning, TEXT("Frontend Node [NodeID:%s, ClassID:%s] is not of expected class class [ClassID:%s]"), *Node->GetID().ToString(), *Node->ClassID.ToString(), *Class->ID.ToString());
}
}
}
return INodeController::GetInvalidHandle();
}
bool FSubgraphNodeController::IsValid() const
{
return FBaseNodeController::IsValid() && (nullptr != GraphPtr.Get());
}
int32 FSubgraphNodeController::GetNumInputs() const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::GetNumInputs();
}
int32 FSubgraphNodeController::GetNumOutputs() const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::GetNumOutputs();
}
TArray<FBaseNodeController::FInputControllerParams> FSubgraphNodeController::GetInputControllerParams() const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::GetInputControllerParams();
}
TArray<FBaseNodeController::FOutputControllerParams> FSubgraphNodeController::GetOutputControllerParams() const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::GetOutputControllerParams();
}
bool FSubgraphNodeController::FindInputControllerParamsWithVertexName(const FVertexName& InName, FInputControllerParams& OutParams) const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::FindInputControllerParamsWithVertexName(InName, OutParams);
}
bool FSubgraphNodeController::FindOutputControllerParamsWithVertexName(const FVertexName& InName, FOutputControllerParams& OutParams) const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::FindOutputControllerParamsWithVertexName(InName, OutParams);
}
bool FSubgraphNodeController::FindInputControllerParamsWithID(FGuid InVertexID, FInputControllerParams& OutParams) const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::FindInputControllerParamsWithID(InVertexID, OutParams);
}
bool FSubgraphNodeController::FindOutputControllerParamsWithID(FGuid InVertexID, FOutputControllerParams& OutParams) const
{
// TODO: Trigger ConformNodeInterfaceToClassInterface() on an
// event callback rather than on every call to this function.
const_cast<FSubgraphNodeController*>(this)->ConformNodeInterfaceToClassInterface();
return Super::FindOutputControllerParamsWithID(InVertexID, OutParams);
}
void FSubgraphNodeController::ConformNodeInterfaceToClassInterface()
{
using namespace FrontendSubgraphNodeControllerPrivate;
if (FMetasoundFrontendNode* Node = NodePtr.Get())
{
if (const FMetasoundFrontendClassInterface* ClassInterface = GetClassInterfaceForNode())
{
ConformNodeVertexArrayToClassVertexArray(Node->Interface.Inputs, ClassInterface->Inputs);
ConformNodeVertexArrayToClassVertexArray(Node->Interface.Outputs, ClassInterface->Outputs);
// TODO: Conform environment variables by aggregating all dependent environment variables.
}
}
}
FDocumentAccess FSubgraphNodeController::ShareAccess()
{
FDocumentAccess Access = FBaseNodeController::ShareAccess();
Access.Graph = GraphPtr;
Access.ConstGraph = GraphPtr;
return Access;
}
FConstDocumentAccess FSubgraphNodeController::ShareAccess() const
{
FConstDocumentAccess Access = FBaseNodeController::ShareAccess();
Access.ConstGraph = GraphPtr;
return Access;
}
FInputHandle FSubgraphNodeController::CreateInputController(FGuid InVertexID, FConstVertexAccessPtr InNodeVertexPtr, FConstClassInputAccessPtr InClassInputPtr, FNodeHandle InOwningNode) const
{
return MakeShared<FBaseInputController>(FBaseInputController::FInitParams{InVertexID, InNodeVertexPtr, InClassInputPtr, GraphPtr, InOwningNode});
}
FOutputHandle FSubgraphNodeController::CreateOutputController(FGuid InVertexID, FConstVertexAccessPtr InNodeVertexPtr, FConstClassOutputAccessPtr InClassOutputPtr, FNodeHandle InOwningNode) const
{
return MakeShared<FBaseOutputController>(FBaseOutputController::FInitParams{InVertexID, InNodeVertexPtr, InClassOutputPtr, GraphPtr, InOwningNode});
}
}
}