Files
UnrealEngineUWP/Engine/Plugins/Runtime/Metasound/Source/MetasoundFrontend/Private/MetasoundFrontendBaseClasses.cpp
phil popp 4cdd926b68 Metasound Frontend Revamp
- New node name {namespace.name.variant}
- PointIDs for exact pin-to-pin connections
- TAccessPtr updates
- Controller/Handle interface
- Document Handle
- Frontend Transforms
- Input/Output node external registration
- Revamped document model

#rb Max.Hayes, Rob.Gay
#jira UEAU-626

[CL 15066822 by phil popp in ue5-main branch]
2021-01-13 10:48:59 -04:00

978 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MetasoundFrontendBaseClasses.h"
#include "MetasoundDataReference.h"
#include "MetasoundFrontendRegistries.h"
namespace Metasound
{
namespace Frontend
{
TMap<FNodeRegistryKey, FNodeRegistryElement>& GetExternalNodeRegistry()
{
return FMetasoundFrontendRegistryContainer::Get()->GetExternalNodeRegistry();
}
TUniquePtr<INode> ConstructInputNode(const FName& InInputType, FInputNodeConstructorParams&& InParams)
{
return FMetasoundFrontendRegistryContainer::Get()->ConstructInputNode(InInputType, MoveTemp(InParams));
}
TUniquePtr<INode> ConstructOutputNode(const FName& InOutputType, const FOutputNodeConstructorParams& InParams)
{
return FMetasoundFrontendRegistryContainer::Get()->ConstructOutputNode(InOutputType, InParams);
}
TUniquePtr<INode> ConstructExternalNode(const FName& InNodeType, uint32 InNodeHash, const FNodeInitData& InInitData)
{
return FMetasoundFrontendRegistryContainer::Get()->ConstructExternalNode(InNodeType, InNodeHash, InInitData);
}
#if 0
void SetLiteralDescription(FMetasoundFrontendLiteral& OutDescription, bool InValue)
{
OutDescription.LiteralType = EMetasoundFrontendLiteralType::Bool;
OutDescription.AsBool = InValue;
OutDescription.AsInteger = 0;
OutDescription.AsFloat = 0.0f;
OutDescription.AsString.Empty();
OutDescription.AsUObject = nullptr;
OutDescription.AsUObjectArray.Empty();
}
void SetLiteralDescription(FMetasoundFrontendLiteral& OutDescription, int32 InValue)
{
OutDescription.LiteralType = EMetasoundFrontendLiteralType::Integer;
OutDescription.AsBool = false;
OutDescription.AsInteger = InValue;
OutDescription.AsFloat = 0.0f;
OutDescription.AsString.Empty();
OutDescription.AsUObject = nullptr;
OutDescription.AsUObjectArray.Empty();
}
void SetLiteralDescription(FMetasoundFrontendLiteral& OutDescription, float InValue)
{
OutDescription.LiteralType = EMetasoundFrontendLiteralType::Float;
OutDescription.AsBool = false;
OutDescription.AsInteger = 0;
OutDescription.AsFloat = InValue;
OutDescription.AsString.Empty();
OutDescription.AsUObject = nullptr;
OutDescription.AsUObjectArray.Empty();
}
void SetLiteralDescription(FMetasoundFrontendLiteral& OutDescription, const FString& InValue)
{
OutDescription.LiteralType = EMetasoundFrontendLiteralType::String;
OutDescription.AsBool = false;
OutDescription.AsInteger = 0;
OutDescription.AsFloat = 0.0f;
OutDescription.AsString = InValue;
OutDescription.AsUObject = nullptr;
OutDescription.AsUObjectArray.Empty();
}
void SetLiteralDescription(FMetasoundFrontendLiteral& OutDescription, UObject* InValue)
{
OutDescription.LiteralType = EMetasoundFrontendLiteralType::UObject;
OutDescription.AsBool = false;
OutDescription.AsInteger = 0;
OutDescription.AsFloat = 0.0f;
OutDescription.AsString.Empty();
OutDescription.AsUObject = InValue;
OutDescription.AsUObjectArray.Empty();
}
void SetLiteralDescription(FMetasoundFrontendLiteral& OutDescription, const TArray<UObject*>& InValue)
{
OutDescription.LiteralType = EMetasoundFrontendLiteralType::UObjectArray;
OutDescription.AsBool = false;
OutDescription.AsInteger = 0;
OutDescription.AsFloat = 0.0f;
OutDescription.AsString.Empty();
OutDescription.AsUObject = nullptr;
OutDescription.AsUObjectArray = InValue;
}
void ClearLiteralDescription(FMetasoundFrontendLiteral& OutDescription)
{
OutDescription.LiteralType = EMetasoundFrontendLiteralType::None;
OutDescription.AsBool = false;
OutDescription.AsInteger = 0;
OutDescription.AsFloat = 0.0f;
OutDescription.AsString.Empty();
OutDescription.AsUObject = nullptr;
OutDescription.AsUObjectArray.Empty();
}
#endif
FLiteral GetLiteralParamForDataType(FName InDataType, const FMetasoundFrontendLiteral& InDescription)
{
EMetasoundFrontendLiteralType LiteralType = InDescription.Type;
FMetasoundFrontendRegistryContainer* Registry = FMetasoundFrontendRegistryContainer::Get();
switch (LiteralType)
{
case EMetasoundFrontendLiteralType::None:
{
return GetDefaultParamForDataType(InDataType);
}
case EMetasoundFrontendLiteralType::Bool:
{
if (!Registry->DoesDataTypeSupportLiteralType(InDataType, ELiteralType::Boolean))
{
return FLiteral::CreateInvalid();
}
else
{
return FLiteral(InDescription.AsBool);
}
}
case EMetasoundFrontendLiteralType::Float:
{
if (!Registry->DoesDataTypeSupportLiteralType(InDataType, ELiteralType::Float))
{
return FLiteral::CreateInvalid();
}
else
{
return FLiteral(InDescription.AsFloat);
}
}
case EMetasoundFrontendLiteralType::Integer:
{
if (!Registry->DoesDataTypeSupportLiteralType(InDataType, ELiteralType::Integer))
{
return FLiteral::CreateInvalid();
}
else
{
return FLiteral(InDescription.AsInteger);
}
}
case EMetasoundFrontendLiteralType::String:
{
if (!Registry->DoesDataTypeSupportLiteralType(InDataType, ELiteralType::String))
{
return FLiteral::CreateInvalid();
}
else
{
return FLiteral(InDescription.AsString);
}
}
case EMetasoundFrontendLiteralType::UObject:
{
if (!Registry->DoesDataTypeSupportLiteralType(InDataType, ELiteralType::UObjectProxy))
{
return FLiteral::CreateInvalid();
}
else
{
return Registry->GenerateLiteralForUObject(InDataType, InDescription.AsUObject);
}
}
case EMetasoundFrontendLiteralType::UObjectArray:
{
if (!Registry->DoesDataTypeSupportLiteralType(InDataType, ELiteralType::UObjectProxyArray))
{
return FLiteral::CreateInvalid();
}
else
{
return Registry->GenerateLiteralForUObjectArray(InDataType, InDescription.AsUObjectArray);
}
}
case EMetasoundFrontendLiteralType::Invalid:
default:
{
return FLiteral::CreateInvalid();
}
}
}
bool DoesDataTypeSupportLiteralType(FName InDataType, EMetasoundFrontendLiteralType InLiteralType)
{
switch (InLiteralType)
{
case EMetasoundFrontendLiteralType::None:
return DoesDataTypeSupportLiteralType(InDataType, ELiteralType::None);
case EMetasoundFrontendLiteralType::Bool:
return DoesDataTypeSupportLiteralType(InDataType, ELiteralType::Boolean);
case EMetasoundFrontendLiteralType::Float:
return DoesDataTypeSupportLiteralType(InDataType, ELiteralType::Float);
case EMetasoundFrontendLiteralType::Integer:
return DoesDataTypeSupportLiteralType(InDataType, ELiteralType::Integer);
case EMetasoundFrontendLiteralType::String:
return DoesDataTypeSupportLiteralType(InDataType, ELiteralType::String);
case EMetasoundFrontendLiteralType::UObject:
return DoesDataTypeSupportLiteralType(InDataType, ELiteralType::UObjectProxy);
case EMetasoundFrontendLiteralType::UObjectArray:
return DoesDataTypeSupportLiteralType(InDataType, ELiteralType::UObjectProxyArray);
case EMetasoundFrontendLiteralType::Invalid:
default:
return false;
}
}
bool DoesDataTypeSupportLiteralType(FName InDataType, ELiteralType InLiteralType)
{
FMetasoundFrontendRegistryContainer* Registry = FMetasoundFrontendRegistryContainer::Get();
return Registry->DoesDataTypeSupportLiteralType(InDataType, InLiteralType);
}
Metasound::FLiteral GetLiteralParam(const FMetasoundFrontendLiteral& InDescription)
{
EMetasoundFrontendLiteralType LiteralType = InDescription.Type;
switch (LiteralType)
{
case EMetasoundFrontendLiteralType::Bool:
{
return FLiteral(InDescription.AsBool);
}
case EMetasoundFrontendLiteralType::Float:
{
return FLiteral(InDescription.AsFloat);
}
case EMetasoundFrontendLiteralType::Integer:
{
return FLiteral(InDescription.AsInteger);
}
case EMetasoundFrontendLiteralType::String:
{
return FLiteral(InDescription.AsString);
}
case EMetasoundFrontendLiteralType::UObject:
case EMetasoundFrontendLiteralType::UObjectArray:
case EMetasoundFrontendLiteralType::None:
case EMetasoundFrontendLiteralType::Invalid:
default:
{
return FLiteral::CreateInvalid();
}
}
}
Metasound::FLiteral GetDefaultParamForDataType(FName InDataType)
{
FMetasoundFrontendRegistryContainer* Registry = FMetasoundFrontendRegistryContainer::Get();
ELiteralType DesiredArgType = Registry->GetDesiredLiteralTypeForDataType(InDataType);
switch (DesiredArgType)
{
case Metasound::ELiteralType::Boolean:
{
return FLiteral(false);
}
case Metasound::ELiteralType::Integer:
{
return FLiteral(0);
}
case Metasound::ELiteralType::Float:
{
return FLiteral(0.0f);
}
case Metasound::ELiteralType::String:
{
return FLiteral(FString());
}
case Metasound::ELiteralType::UObjectProxy:
case Metasound::ELiteralType::UObjectProxyArray:
case Metasound::ELiteralType::None:
{
return FLiteral();
}
case Metasound::ELiteralType::Invalid:
default:
{
return FLiteral::CreateInvalid();
}
}
}
ITransactable::ITransactable(uint32 InUndoLimit, TWeakObjectPtr<UObject> InOwningAsset /* = nullptr */)
: UndoLimit(InUndoLimit)
, OwningAsset(InOwningAsset)
{
}
bool ITransactable::Undo()
{
if (UndoTransactableStack.Num() == 0)
{
return false;
}
if (TSharedPtr<ITransactable> Owner = OwningTransactable.Pin())
{
Owner->DiscardUndoFromOwnedTransactable(AsShared());
}
TSharedPtr<ITransactable> Transactable = UndoTransactableStack.Pop().Pin();
TSharedPtr<ITransactable> PreviousTransactable = AsShared();
RedoTransactableStack.Push(Transactable);
while(Transactable)
{
if (Transactable == PreviousTransactable)
{
bool bUndoSucceeded = Transactable->PerformLocalUndo();
if (bUndoSucceeded && OwningAsset.IsValid())
{
OwningAsset->MarkPackageDirty();
}
return bUndoSucceeded;
}
PreviousTransactable = Transactable;
Transactable = Transactable->UndoTransactableStack.Pop().Pin();
PreviousTransactable->RedoTransactableStack.Push(Transactable);
}
return false;
}
bool ITransactable::Redo()
{
if (RedoTransactableStack.Num() == 0)
{
return false;
}
if (TSharedPtr<ITransactable> Owner = OwningTransactable.Pin())
{
Owner->DiscardRedoFromOwnedTransactable(AsShared());
}
TSharedPtr<ITransactable> Transactable = RedoTransactableStack.Pop().Pin();
TSharedPtr<ITransactable> PreviousTransactable = AsShared();
UndoTransactableStack.Push(Transactable);
while (Transactable)
{
if (Transactable == PreviousTransactable)
{
const bool bRedoSucceeded = Transactable->PerformLocalRedo();
if (bRedoSucceeded && OwningAsset.IsValid())
{
OwningAsset->MarkPackageDirty();
}
return bRedoSucceeded;
}
PreviousTransactable = Transactable;
Transactable = Transactable->RedoTransactableStack.Pop().Pin();
PreviousTransactable->UndoTransactableStack.Push(Transactable);
}
return false;
}
void ITransactable::CommitTransaction(FReversibleTransaction&& InTransactionDescription)
{
if (((uint32)LocalUndoTransactionStack.Num()) >= UndoLimit)
{
// Discard the oldest undo action.
LocalUndoTransactionStack.RemoveAt(0);
}
LocalUndoTransactionStack.Push(MoveTemp(InTransactionDescription));
RedoTransactableStack.Reset();
TWeakPtr<ITransactable> WeakThisPtr = AsShared();
UndoTransactableStack.Push(WeakThisPtr);
if (TSharedPtr<ITransactable> Owner = OwningTransactable.Pin())
{
Owner->PushUndoFromOwnedTransactable(WeakThisPtr);
}
if (OwningAsset.IsValid())
{
OwningAsset->MarkPackageDirty();
}
}
bool ITransactable::RegisterOwningTransactable(ITransactable& InOwningTransactable)
{
// Sanity check that this will not create a cycle.
while (TSharedPtr<ITransactable> Owner = InOwningTransactable.OwningTransactable.Pin())
{
if (Owner.Get() == this)
{
return false;
}
}
OwningTransactable = InOwningTransactable.AsShared();
return true;
}
bool ITransactable::PerformLocalUndo()
{
if (LocalUndoTransactionStack.Num() == 0)
{
return false;
}
FReversibleTransaction Transaction = LocalUndoTransactionStack.Pop();
bool bResult = Transaction.UndoTransaction();
if (bResult)
{
LocalRedoTransactionStack.Push(MoveTemp(Transaction));
}
return bResult;
}
bool ITransactable::PerformLocalRedo()
{
if (LocalRedoTransactionStack.Num() == 0)
{
return false;
}
FReversibleTransaction Transaction = LocalRedoTransactionStack.Pop();
bool bResult = Transaction.RedoTransaction();
if (bResult)
{
LocalUndoTransactionStack.Push(MoveTemp(Transaction));
}
return bResult;
}
bool ITransactable::DiscardUndoFromOwnedTransactable(TWeakPtr<ITransactable> InOwnedTransactable)
{
// NOTE: This relies on TArray.Push adding an element to the end of the array.
// Start at the end of the array and work backwards until we find the most recent undo operation for this transactable.
for (int32 Index = UndoTransactableStack.Num() - 1; Index >= 0; Index--)
{
if (UndoTransactableStack[Index] == InOwnedTransactable)
{
// Discard, but preserve the stack order.
UndoTransactableStack.RemoveAt(Index);
return true;
}
}
return false;
}
bool ITransactable::DiscardRedoFromOwnedTransactable(TWeakPtr<ITransactable> InOwnedTransactable)
{
// NOTE: This relies on TArray.Push adding an element to the end of the array.
// Start at the end of the array and work backwards until we find the most recent undo operation for this transactable.
for (int32 Index = RedoTransactableStack.Num() - 1; Index >= 0; Index--)
{
if (RedoTransactableStack[Index] == InOwnedTransactable)
{
// Discard, but preserve the stack order.
RedoTransactableStack.RemoveAt(Index);
return true;
}
}
return false;
}
bool ITransactable::PushUndoFromOwnedTransactable(TWeakPtr<ITransactable> InOwnedTransactable)
{
UndoTransactableStack.Push(MoveTemp(InOwnedTransactable));
RedoTransactableStack.Reset();
return true;
}
bool ITransactable::PushRedoFromOwnedTransactable(TWeakPtr<ITransactable> InOwnedTransactable)
{
RedoTransactableStack.Push(MoveTemp(InOwnedTransactable));
DiscardUndoFromOwnedTransactable(InOwnedTransactable);
return true;
}
#if 0
FDescriptionAccessPoint::FDescriptionAccessPoint(TAccessPtr<FMetasoundFrontendDocument> InRootDocumentPtr)
: RootDocumentPtr(InRootDocumentPtr)
{
}
FMetasoundFrontendDocument& FDescriptionAccessPoint::GetRootChecked() const
{
check(RootDocumentPtr.IsValid());
return *RootDocumentPtr;
}
bool FDescriptionAccessPoint::GetDescriptionPtrFromPath(const FDescPath& InPathFromRoot, FMetasoundDescriptionPtr& OutPtr) const
{
if (!RootDocumentPtr.IsValid())
{
return false;
}
FDescPath CurrentPath = InPathFromRoot;
OutPtr.Set<FMetasoundFrontendDocument*>(RootDocumentPtr.Get());
FDescriptionUnwindStep CurrentStep = { OutPtr, Path::EDescType::Document };
while (CurrentPath.Num() != 0 && CurrentStep.Type != Path::EDescType::Invalid)
{
CurrentStep = GoToNext(CurrentPath, CurrentStep);
}
OutPtr = CurrentStep.DescriptionStructPtr;
return CurrentStep.Type != Path::EDescType::Invalid;
}
FDescriptionAccessPoint::FDescriptionUnwindStep FDescriptionAccessPoint::GoToNextFromDocument(FMetasoundFrontendDocument& InDocument, FDescPath& InPath, const Path::FElement& InNext) const
{
check(RootDocumentPtr.IsValid());
switch (InNext.CurrentDescType)
{
case Path::EDescType::Document:
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendDocument*>(&InDocument);
UnwindStep.Type = Path::EDescType::Document;
return UnwindStep;
}
case Path::EDescType::Class:
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendClass*>(&(InDocument.RootGraph));
UnwindStep.Type = Path::EDescType::Class;
return UnwindStep;
}
case Path::EDescType::DocDependencies:
{
// The next element in a path after Dependencies will always be the name of the dependency.
Path::FElement DependencyElement = InPath.Path[0];
InPath.Path.RemoveAt(0);
if (!ensureAlwaysMsgf(DependencyElement.CurrentDescType == Path::EDescType::Class, TEXT("Invalid path set up.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
FString& DependencyName = DependencyElement.LookupName;
int32 DependencyID = DependencyElement.LookupID;
if (!ensureAlwaysMsgf(DependencyID != INDEX_NONE || DependencyName.Len() > 0, TEXT("Path to a dependency did not include a valid ID or dependency name.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
if (DependencyID == FMetasoundFrontendClass::RootClassID)
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendClass*>(&(InDocument.RootGraph));
UnwindStep.Type = Path::EDescType::Class;
return UnwindStep;
}
TArray<FMetasoundFrontendClass>& DependenciesList = InDocument.Dependencies;
// Dependencies can be looked up by ID or by name.
if (DependencyID != INDEX_NONE)
{
// Scan the dependencies list for the matching lookup ID.
for (FMetasoundFrontendClass& Dependency : DependenciesList)
{
if (Dependency.UniqueID == DependencyID)
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendClass*>(&Dependency);
UnwindStep.Type = Path::EDescType::Class;
return UnwindStep;
}
}
}
else
{
// TODO: remove this chunk of code in the "else{}" block. Not sure if it has to be here.
checkNoEntry();
// fall back to scanning the dependencies list for the matching lookup name.
for (FMetasoundFrontendClass& Dependency : DependenciesList)
{
if (Dependency.Metadata.NodeName == DependencyName)
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendClass*>(&Dependency);
UnwindStep.Type = Path::EDescType::Class;
return UnwindStep;
}
}
}
// If we reached the end of the Dependencies list and didn't find a match, ensure.
ensureAlwaysMsgf(false, TEXT("Couldn't find dependency %s in path."), *DependencyName);
return FDescriptionUnwindStep::CreateInvalid();
}
}
checkNoEntry();
return FDescriptionUnwindStep::CreateInvalid();
}
FDescriptionAccessPoint::FDescriptionUnwindStep FDescriptionAccessPoint::GoToNextFromClass(FMetasoundFrontendClass& InClassDescription, FDescPath& InPath, const Path::FElement& InNext) const
{
switch (InNext.CurrentDescType)
{
case Path::EDescType::Graph:
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendGraph*>(&(InClassDescription.Graph));
UnwindStep.Type = Path::EDescType::Graph;
return UnwindStep;
}
case Path::EDescType::ClassDependencies:
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FClassDependencyIDs*>(&(InClassDescription.DependencyIDs));
UnwindStep.Type = Path::EDescType::ClassDependencies;
return UnwindStep;
}
case Path::EDescType::Inputs:
{
// The next element after an Inputs element should always be the name of an input.
Path::FElement InputElement = InPath.Path[0];
InPath.Path.RemoveAt(0);
if (!ensureAlwaysMsgf(InputElement.CurrentDescType == Path::EDescType::Input, TEXT("Invalid path set up.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
FString& InputName = InputElement.LookupName;
// Scan the inputs list for the lookup name.
TArray<FMetasoundFrontendClassInput>& InputsList = InClassDescription.Inputs;
for (FMetasoundFrontendClassInput& Input : InputsList)
{
if (Input.Name == InputName)
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendClassInput*>(&Input);
UnwindStep.Type = Path::EDescType::Input;
return UnwindStep;
}
}
// If we reached the end of the Inputs list and didn't find a match, ensure.
ensureAlwaysMsgf(false, TEXT("Couldn't find input %s in path."), *InputName);
return FDescriptionUnwindStep::CreateInvalid();
}
case Path::EDescType::Outputs:
{
// The next element after an Outputs element should always be the name of an output.
Path::FElement OutputElement = InPath.Path[0];
InPath.Path.RemoveAt(0);
if (!ensureAlwaysMsgf(OutputElement.CurrentDescType == Path::EDescType::Output, TEXT("Invalid path set up.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
FString& OutputName = OutputElement.LookupName;
// Scan the outputs list for the lookup name.
TArray<FMetasoundFrontendClassOutput>& OutputsList = InClassDescription.Outputs;
for (FMetasoundFrontendClassOutput& Output : OutputsList)
{
if (Output.Name == OutputElement.LookupName)
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendClassOutput*>(&Output);
UnwindStep.Type = Path::EDescType::Output;
return UnwindStep;
}
}
// If we reached the end of the Inputs list and didn't find a match, ensure.
ensureAlwaysMsgf(false, TEXT("Couldn't find output %s in path."), *InNext.LookupName);
return FDescriptionUnwindStep::CreateInvalid();
}
case Path::EDescType::Metadata:
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendClassMetadata*>(&(InClassDescription.Metadata));
UnwindStep.Type = Path::EDescType::Metadata;
return UnwindStep;
}
default:
{
ensureAlwaysMsgf(false, TEXT("Invalid path- Tried to path directly from a Class Description to a type that wasn't a direct memember of the Class"));
return FDescriptionUnwindStep::CreateInvalid();
}
}
}
FDescriptionAccessPoint::FDescriptionUnwindStep FDescriptionAccessPoint::GoToNext(FDescPath& InPath, FDescriptionUnwindStep InElement) const
{
if (!ensureMsgf(InPath.Path.Num() != 0, TEXT("Attempted to unwind an empty path.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
Path::FElement NextStep = InPath.Path[0];
InPath.Path.RemoveAt(0);
switch (InElement.Type)
{
case Path::EDescType::Document:
{
FMetasoundFrontendDocument* Document = InElement.DescriptionStructPtr.Get<FMetasoundFrontendDocument*>();
return GoToNextFromDocument(*Document, InPath, NextStep);
break;
}
case Path::EDescType::Class:
{
FMetasoundFrontendClass* ClassDescription = InElement.DescriptionStructPtr.Get<FMetasoundFrontendClass*>();
return GoToNextFromClass(*ClassDescription, InPath, NextStep);
break;
}
case Path::EDescType::Graph:
{
if (!ensureAlwaysMsgf(NextStep.CurrentDescType == Path::EDescType::Nodes, TEXT("Invalid path. the Graph description only contains the Nodes list.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
FMetasoundFrontendGraph* GraphDescription = InElement.DescriptionStructPtr.Get<FMetasoundFrontendGraph*>();
if (!ensureAlwaysMsgf(InPath.Path.Num() != 0, TEXT("Incomplete path! path stopped at Nodes list without specifying a node ID.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
Path::FElement NodeElement = InPath.Path[0];
InPath.Path.RemoveAt(0);
if (!ensureAlwaysMsgf(NodeElement.CurrentDescType == Path::EDescType::Node, TEXT("Invalid path! a Nodes element must always be followed by a Node ID.")))
{
return FDescriptionUnwindStep::CreateInvalid();
}
int32 NodeID = NodeElement.LookupID;
TArray<FMetasoundFrontendNode>& NodeList = GraphDescription->Nodes;
for (FMetasoundFrontendNode& Node : NodeList)
{
if (Node.UniqueID == NodeID)
{
FDescriptionUnwindStep UnwindStep;
UnwindStep.DescriptionStructPtr.Set<FMetasoundFrontendNode*>(&Node);
UnwindStep.Type = Path::EDescType::Node;
return UnwindStep;
}
}
break;
}
case Path::EDescType::Inputs:
{
ensureAlwaysMsgf(false, TEXT("Invalid path. Inputs will always be pathed directly after a Class."));
return FDescriptionUnwindStep::CreateInvalid();
}
case Path::EDescType::DocDependencies:
{
ensureAlwaysMsgf(false, TEXT("Invalid path. Document dependencies should always follow after a Document and should always list a specific dependency by ID or name."));
return FDescriptionUnwindStep::CreateInvalid();
}
case Path::EDescType::Input:
case Path::EDescType::Output:
case Path::EDescType::Node:
case Path::EDescType::Metadata:
default:
{
ensureAlwaysMsgf(false, TEXT("Invalid path. Input, Output, Node, and Metadata don't have any child elements."));
return FDescriptionUnwindStep::CreateInvalid();
}
}
// if we ever hit this, we likely missed a return on one of these branches.
checkNoEntry();
return FDescriptionUnwindStep::CreateInvalid();
}
FDescPath Path::GetPathToClassForNode(FDescPath InPathForNode, FString& InNodeName)
{
return FDescPath()[EFromDocument::ToDependencies][*InNodeName];
}
FDescPath Path::GetOwningClassDescription(FDescPath InPathForGraph)
{
// Backtrack from the end of the path until we find a Class element.
for (int32 Level = InPathForGraph.Path.Num() - 1; Level >= 0; Level--)
{
if (InPathForGraph.Path[Level].CurrentDescType == Path::EDescType::Class)
{
return InPathForGraph;
}
else
{
InPathForGraph.Path.RemoveAt(InPathForGraph.Path.Num() - 1);
}
}
return InPathForGraph;
}
FDescPath Path::GetDependencyPath(int32 InDependencyID)
{
return FDescPath()[Path::EFromDocument::ToDependencies][InDependencyID];
}
FDescPath Path::GetInputDescriptionPath(FDescPath InPathForInputNode, const FString& InputName)
{
return (InPathForInputNode << 3)[Path::EFromClass::ToInputs][*InputName];
}
FDescPath Path::GetOutputDescriptionPath(FDescPath InPathForOutputNode, const FString& OutputName)
{
return (InPathForOutputNode << 3)[Path::EFromClass::ToOutputs][*OutputName];
}
FDescPath Path::GetOuterGraphPath(FDescPath InPath)
{
// Unwind element by element until we hit a graph.
while (InPath.Path.Num() > 1 && InPath.Path.Last().CurrentDescType != Path::EDescType::Graph)
{
InPath.Path.Pop();
}
return InPath;
}
FString Path::GetPrintableString(FDescPath InPath)
{
FString OutString = FString(TEXT("//"));
for (FElement& PathElement : InPath.Path)
{
switch (PathElement.CurrentDescType)
{
case EDescType::Document:
{
OutString += TEXT("Document/");
break;
}
case EDescType::Class:
{
OutString += TEXT("Class/");
break;
}
case EDescType::DocDependencies:
{
OutString += TEXT("Dependencies(");
if (PathElement.LookupID != INDEX_NONE)
{
OutString.AppendInt(PathElement.LookupID);
}
else
{
OutString += PathElement.LookupName;
}
OutString += TEXT(")/");
break;
}
case EDescType::ClassDependencies:
{
OutString += TEXT("Dependencies(");
OutString.AppendInt(PathElement.LookupID);
OutString += TEXT(")/");
break;
}
case EDescType::Graph:
{
OutString += TEXT("Graph/");
break;
}
case EDescType::Inputs:
{
OutString += TEXT("Inputs/");
break;
}
case EDescType::Input:
{
OutString += TEXT("Input(");
OutString += PathElement.LookupName;
OutString += TEXT(")/");
break;
}
case EDescType::Metadata:
{
OutString += TEXT("Metadata/");
break;
}
case EDescType::Nodes:
{
OutString += TEXT("Nodes/");
break;
}
case EDescType::Node:
{
OutString += TEXT("Node(");
OutString.AppendInt(PathElement.LookupID);
OutString += TEXT(")/");
break;
}
case EDescType::Outputs:
{
OutString += TEXT("Outputs/");
break;
}
case EDescType::Output:
{
OutString += TEXT("Output(");
OutString += PathElement.LookupName;
OutString += TEXT(")/");
break;
}
default:
{
OutString += TEXT("Unknown/");
break;
}
}
}
return OutString;
}
#endif
void InitializeFrontend()
{
FMetasoundFrontendRegistryContainer::Get()->InitializeFrontend();
}
}
}