// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundFrontendInputController.h" #include "Internationalization/Text.h" #include "MetasoundFrontendController.h" #include "MetasoundFrontendDocumentAccessPtr.h" #include "MetasoundFrontendInvalidController.h" #include "Misc/Guid.h" #define LOCTEXT_NAMESPACE "MetasoundFrontendInputController" namespace Metasound { namespace Frontend { // // FBaseInputController // FBaseInputController::FBaseInputController(const FBaseInputController::FInitParams& InParams) : ID(InParams.ID) , NodeVertexPtr(InParams.NodeVertexPtr) , ClassInputPtr(InParams.ClassInputPtr) , GraphPtr(InParams.GraphPtr) , OwningNode(InParams.OwningNode) { } bool FBaseInputController::IsValid() const { return OwningNode->IsValid() && (nullptr != NodeVertexPtr.Get()) && (nullptr != GraphPtr.Get()); } FGuid FBaseInputController::GetID() const { return ID; } const FName& FBaseInputController::GetDataType() const { if (const FMetasoundFrontendVertex* Vertex = NodeVertexPtr.Get()) { return Vertex->TypeName; } return Invalid::GetInvalidName(); } const FVertexName& FBaseInputController::GetName() const { if (const FMetasoundFrontendVertex* Vertex = NodeVertexPtr.Get()) { return Vertex->Name; } return Invalid::GetInvalidName(); } FText FBaseInputController::GetDisplayName() const { if (const FMetasoundFrontendClassInput* ClassInput = ClassInputPtr.Get()) { if (!ClassInput->Metadata.DisplayName.IsEmptyOrWhitespace()) { return ClassInput->Metadata.DisplayName; } return FText::FromName(GetName()); } return Invalid::GetInvalidText(); } const FMetasoundFrontendLiteral* FBaseInputController::GetLiteral() const { if (const FMetasoundFrontendVertex* Vertex = NodeVertexPtr.Get()) { return OwningNode->GetInputLiteral(Vertex->VertexID); } return nullptr; } void FBaseInputController::SetLiteral(const FMetasoundFrontendLiteral& InLiteral) { if (const FMetasoundFrontendVertex* Vertex = NodeVertexPtr.Get()) { if (const FMetasoundFrontendLiteral* ClassLiteral = GetClassDefaultLiteral()) { // Clear if equivalent to class default as fallback is the class default if (ClassLiteral->IsEquivalent(InLiteral)) { OwningNode->ClearInputLiteral(Vertex->VertexID); return; } } OwningNode->SetInputLiteral(FMetasoundFrontendVertexLiteral{ Vertex->VertexID, InLiteral }); } } const FMetasoundFrontendLiteral* FBaseInputController::GetClassDefaultLiteral() const { if (const FMetasoundFrontendClassInput* ClassInput = ClassInputPtr.Get()) { return &(ClassInput->DefaultLiteral); } return nullptr; } const FText& FBaseInputController::GetTooltip() const { if (const FMetasoundFrontendClassInput* ClassInput = ClassInputPtr.Get()) { return ClassInput->Metadata.Description; } return Invalid::GetInvalidText(); } const FMetasoundFrontendVertexMetadata& FBaseInputController::GetMetadata() const { if (const FMetasoundFrontendClassInput* ClassInput = ClassInputPtr.Get()) { return ClassInput->Metadata; } return Invalid::GetInvalidVertexMetadata(); } bool FBaseInputController::IsConnected() const { return (nullptr != FindEdge()); } FGuid FBaseInputController::GetOwningNodeID() const { return OwningNode->GetID(); } FNodeHandle FBaseInputController::GetOwningNode() { return OwningNode; } FConstNodeHandle FBaseInputController::GetOwningNode() const { return OwningNode; } FOutputHandle FBaseInputController::GetConnectedOutput() { if (const FMetasoundFrontendEdge* Edge = FindEdge()) { // Create output handle from output node. FGraphHandle Graph = OwningNode->GetOwningGraph(); FNodeHandle OutputNode = Graph->GetNodeWithID(Edge->FromNodeID); return OutputNode->GetOutputWithID(Edge->FromVertexID); } return IOutputController::GetInvalidHandle(); } FConstOutputHandle FBaseInputController::GetConnectedOutput() const { if (const FMetasoundFrontendEdge* Edge = FindEdge()) { // Create output handle from output node. FConstGraphHandle Graph = OwningNode->GetOwningGraph(); FConstNodeHandle OutputNode = Graph->GetNodeWithID(Edge->FromNodeID); return OutputNode->GetOutputWithID(Edge->FromVertexID); } return IOutputController::GetInvalidHandle(); } FConnectability FBaseInputController::CanConnectTo(const IOutputController& InController) const { FConnectability OutConnectability; OutConnectability.Connectable = FConnectability::EConnectable::No; const FName& DataType = GetDataType(); const FName& OtherDataType = InController.GetDataType(); if (DataType == Invalid::GetInvalidName()) { return OutConnectability; } if (OtherDataType == DataType) { // If data types are equal, connection can happen. OutConnectability.Connectable = FConnectability::EConnectable::Yes; return OutConnectability; } // If data types are not equal, check for converter nodes which could // convert data type. OutConnectability.PossibleConverterNodeClasses = FRegistry::Get()->GetPossibleConverterNodes(OtherDataType, DataType); if (OutConnectability.PossibleConverterNodeClasses.Num() > 0) { OutConnectability.Connectable = FConnectability::EConnectable::YesWithConverterNode; return OutConnectability; } return OutConnectability; } bool FBaseInputController::Connect(IOutputController& InController) { const FName& DataType = GetDataType(); const FName& OtherDataType = InController.GetDataType(); if (DataType == Invalid::GetInvalidName()) { return false; } if (FMetasoundFrontendGraph* Graph = GraphPtr.Get()) { if (ensureAlwaysMsgf(OtherDataType == DataType, TEXT("Cannot connect incompatible types."))) { // Overwrite an existing connection if it exists. FMetasoundFrontendEdge* Edge = FindEdge(); if (!Edge) { Edge = &Graph->Edges.AddDefaulted_GetRef(); Edge->ToNodeID = GetOwningNodeID(); Edge->ToVertexID = GetID(); } Edge->FromNodeID = InController.GetOwningNodeID(); Edge->FromVertexID = InController.GetID(); return true; } } return false; } bool FBaseInputController::ConnectWithConverterNode(IOutputController& InController, const FConverterNodeInfo& InConverterInfo) { FGraphHandle OwningGraph = OwningNode->GetOwningGraph(); // Generate the converter node. FNodeHandle ConverterNode = OwningGraph->AddNode(InConverterInfo.NodeKey); FInputHandle ConverterInput = ConverterNode->GetInputWithVertexName(InConverterInfo.PreferredConverterInputPin); FOutputHandle ConverterOutput = ConverterNode->GetOutputWithVertexName(InConverterInfo.PreferredConverterOutputPin); if (!ConverterInput->IsValid()) { UE_LOG(LogMetaSound, Warning, TEXT("Converter node [Name: %s] does not support preferred input vertex [Vertex: %s]"), *ConverterNode->GetNodeName().ToString(), *InConverterInfo.PreferredConverterInputPin.ToString()); return false; } if (!ConverterOutput->IsValid()) { UE_LOG(LogMetaSound, Warning, TEXT("Converter node [Name: %s] does not support preferred output vertex [Vertex: %s]"), *ConverterNode->GetNodeName().ToString(), *InConverterInfo.PreferredConverterOutputPin.ToString()); return false; } // Connect the output InController to the converter, than connect the converter to this input. if (ConverterInput->Connect(InController) && Connect(*ConverterOutput)) { return true; } return false; } bool FBaseInputController::Disconnect(IOutputController& InController) { if (FMetasoundFrontendGraph* Graph = GraphPtr.Get()) { FGuid FromNodeID = InController.GetOwningNodeID(); FGuid FromVertexID = InController.GetID(); FGuid ToNodeID = GetOwningNodeID(); FGuid ToVertexID = GetID(); auto IsMatchingEdge = [&](const FMetasoundFrontendEdge& Edge) { return (Edge.FromNodeID == FromNodeID) && (Edge.FromVertexID == FromVertexID) && (Edge.ToNodeID == ToNodeID) && (Edge.ToVertexID == ToVertexID); }; int32 NumRemoved = Graph->Edges.RemoveAllSwap(IsMatchingEdge); return NumRemoved > 0; } return false; } bool FBaseInputController::Disconnect() { if (FMetasoundFrontendGraph* Graph = GraphPtr.Get()) { const FGuid NodeID = GetOwningNodeID(); FGuid VertexID = GetID(); auto EdgeHasMatchingDestination = [&](const FMetasoundFrontendEdge& Edge) { return (Edge.ToNodeID == NodeID) && (Edge.ToVertexID == VertexID); }; int32 NumRemoved = Graph->Edges.RemoveAllSwap(EdgeHasMatchingDestination); return NumRemoved > 0; } return false; } const FMetasoundFrontendEdge* FBaseInputController::FindEdge() const { if (const FMetasoundFrontendGraph* Graph = GraphPtr.Get()) { const FGuid NodeID = GetOwningNodeID(); FGuid VertexID = GetID(); auto EdgeHasMatchingDestination = [&](const FMetasoundFrontendEdge& Edge) { return (Edge.ToNodeID == NodeID) && (Edge.ToVertexID == VertexID); }; return Graph->Edges.FindByPredicate(EdgeHasMatchingDestination); } return nullptr; } FMetasoundFrontendEdge* FBaseInputController::FindEdge() { if (FMetasoundFrontendGraph* Graph = GraphPtr.Get()) { const FGuid NodeID = GetOwningNodeID(); FGuid VertexID = GetID(); auto EdgeHasMatchingDestination = [&](const FMetasoundFrontendEdge& Edge) { return (Edge.ToNodeID == NodeID) && (Edge.ToVertexID == VertexID); }; return Graph->Edges.FindByPredicate(EdgeHasMatchingDestination); } return nullptr; } FDocumentAccess FBaseInputController::ShareAccess() { FDocumentAccess Access; Access.ConstVertex = NodeVertexPtr; Access.ConstClassInput = ClassInputPtr; Access.Graph = GraphPtr; Access.ConstGraph = GraphPtr; return Access; } FConstDocumentAccess FBaseInputController::ShareAccess() const { FConstDocumentAccess Access; Access.ConstVertex = NodeVertexPtr; Access.ConstClassInput = ClassInputPtr; Access.ConstGraph = GraphPtr; return Access; } // // FOutputNodeInputController // FOutputNodeInputController::FOutputNodeInputController(const FOutputNodeInputController::FInitParams& InParams) : FBaseInputController({InParams.ID, InParams.NodeVertexPtr, InParams.ClassInputPtr, InParams.GraphPtr, InParams.OwningNode}) , OwningGraphClassOutputPtr(InParams.OwningGraphClassOutputPtr) { } bool FOutputNodeInputController::IsValid() const { return FBaseInputController::IsValid() && (nullptr != OwningGraphClassOutputPtr.Get()); } FText FOutputNodeInputController::GetDisplayName() const { if (const FMetasoundFrontendClassOutput* OwningOutput = OwningGraphClassOutputPtr.Get()) { if (const FMetasoundFrontendClassInput* ClassInput = ClassInputPtr.Get()) { // If there the ClassInput exists, combine the variable name and class input name. // of the variable should be added to the names of the vertices. CachedDisplayName = FText::Format(LOCTEXT("OutputNodeInputControllerFormat", "{1} {0}"), OwningOutput->Metadata.DisplayName, ClassInput->Metadata.DisplayName); } else { // If there is not ClassInput, then use the variable name. CachedDisplayName = OwningOutput->Metadata.DisplayName; } } return CachedDisplayName; } const FText& FOutputNodeInputController::GetTooltip() const { if (const FMetasoundFrontendClassOutput* OwningOutput = OwningGraphClassOutputPtr.Get()) { return OwningOutput->Metadata.Description; } return Invalid::GetInvalidText(); } const FMetasoundFrontendVertexMetadata& FOutputNodeInputController::GetMetadata() const { if (const FMetasoundFrontendClassOutput* OwningOutput = OwningGraphClassOutputPtr.Get()) { return OwningOutput->Metadata; } return Invalid::GetInvalidVertexMetadata(); } void FOutputNodeInputController::SetName(const FVertexName& InName) { if (FMetasoundFrontendVertex* Vertex = ConstCastAccessPtr(NodeVertexPtr).Get()) { Vertex->Name = InName; } } FDocumentAccess FOutputNodeInputController::ShareAccess() { FDocumentAccess Access = FBaseInputController::ShareAccess(); Access.ConstClassOutput = OwningGraphClassOutputPtr; return Access; } FConstDocumentAccess FOutputNodeInputController::ShareAccess() const { FConstDocumentAccess Access = FBaseInputController::ShareAccess(); Access.ConstClassOutput = OwningGraphClassOutputPtr; return Access; } // // FInputNodeInputController // FInputNodeInputController::FInputNodeInputController(const FInputNodeInputController::FInitParams& InParams) : FBaseInputController({InParams.ID, InParams.NodeVertexPtr, InParams.ClassInputPtr, InParams.GraphPtr, InParams.OwningNode}) , OwningGraphClassInputPtr(InParams.OwningGraphClassInputPtr) { } bool FInputNodeInputController::IsValid() const { return FBaseInputController::IsValid() && (nullptr != OwningGraphClassInputPtr.Get()); } FText FInputNodeInputController::GetDisplayName() const { if (const FMetasoundFrontendClassInput* OwningInput = OwningGraphClassInputPtr.Get()) { if (!OwningInput->Metadata.DisplayName.IsEmptyOrWhitespace()) { return OwningInput->Metadata.DisplayName; } return FText::FromName(GetName()); } return Invalid::GetInvalidText(); } const FText& FInputNodeInputController::GetTooltip() const { if (const FMetasoundFrontendClassInput* OwningInput = OwningGraphClassInputPtr.Get()) { return OwningInput->Metadata.Description; } return Invalid::GetInvalidText(); } const FMetasoundFrontendVertexMetadata& FInputNodeInputController::GetMetadata() const { if (const FMetasoundFrontendClassInput* OwningInput = OwningGraphClassInputPtr.Get()) { return OwningInput->Metadata; } return Invalid::GetInvalidVertexMetadata(); } void FInputNodeInputController::SetName(const FVertexName& InName) { if (FMetasoundFrontendVertex* Vertex = ConstCastAccessPtr(NodeVertexPtr).Get()) { Vertex->Name = InName; } } FConnectability FInputNodeInputController::CanConnectTo(const IOutputController& InController) const { static const FConnectability Connectability = {FConnectability::EConnectable::No}; return Connectability; } bool FInputNodeInputController::Connect(IOutputController& InController) { return false; } bool FInputNodeInputController::ConnectWithConverterNode(IOutputController& InController, const FConverterNodeInfo& InNodeClassName) { return false; } } } #undef LOCTEXT_NAMESPACE