Files
UnrealEngine/Engine/Plugins/Mutable/Source/CustomizableObjectEditor/Private/MuCOE/GraphTraversal.cpp
2025-05-18 13:04:45 +08:00

946 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MuCOE/GraphTraversal.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "MuCO/LoadUtils.h"
#include "MuCOE/CustomizableObjectPin.h"
#include "MuCOE/EdGraphSchema_CustomizableObject.h"
#include "MuCOE/Nodes/CustomizableObjectNodeEnumParameter.h"
#include "MuCOE/Nodes/CustomizableObjectNodeExposePin.h"
#include "MuCOE/Nodes/CustomizableObjectNodeModifierExtendMeshSection.h"
#include "MuCOE/Nodes/CustomizableObjectNodeExternalPin.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMaterial.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMaterialVariation.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMeshMorph.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMeshMorphStackApplication.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMeshMorphStackDefinition.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMeshReshape.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMeshSwitch.h"
#include "MuCOE/Nodes/CustomizableObjectNodeMeshVariation.h"
#include "MuCOE/Nodes/CustomizableObjectNodeSkeletalMeshParameter.h"
#include "MuCOE/Nodes/CustomizableObjectNodeObject.h"
#include "MuCOE/Nodes/CustomizableObjectNodeSkeletalMesh.h"
#include "MuCOE/Nodes/CustomizableObjectNodeStaticMesh.h"
#include "MuCOE/Nodes/CustomizableObjectNodeStaticString.h"
#include "MuCOE/Nodes/CustomizableObjectNodeTable.h"
#include "MuCOE/Nodes/CustomizableObjectNodeAnimationPose.h"
#include "MuCOE/Nodes/CustomizableObjectNodeObjectGroup.h"
#include "MuCOE/Nodes/CustomizableObjectNodeReroute.h"
#include "UObject/UObjectIterator.h"
TArray<UEdGraphPin*> FollowPinArray(const UEdGraphPin& Pin, bool bIgnoreOrphan, bool* bOutCycleDetected)
{
MUTABLE_CPUPROFILER_SCOPE(FollowPinArray)
bool bCycleDetected = false;
TArray<UEdGraphPin*> Result;
// Early out optimization
if (Pin.LinkedTo.IsEmpty())
{
if (bOutCycleDetected)
{
*bOutCycleDetected = bCycleDetected;
}
return Result;
}
TSet<const UEdGraphPin*> Visited;
Visited.Reserve(32);
TArray<const UEdGraphPin*> PinsToVisit;
PinsToVisit.Reserve(32);
PinsToVisit.Add(&Pin);
while (PinsToVisit.Num())
{
const UEdGraphPin& CurrentPin = *PinsToVisit.Pop();
if (!bIgnoreOrphan && IsPinOrphan(CurrentPin))
{
continue;
}
Visited.FindOrAdd(&CurrentPin, &bCycleDetected);
if (bCycleDetected)
{
continue;
}
for (UEdGraphPin* LinkedPin : CurrentPin.LinkedTo)
{
if (!bIgnoreOrphan && IsPinOrphan(*LinkedPin))
{
continue;
}
if (const UCustomizableObjectNodeReroute* NodeReroute = Cast<UCustomizableObjectNodeReroute>(LinkedPin->GetOwningNodeUnchecked()))
{
PinsToVisit.Add(Pin.Direction == EGPD_Input ? NodeReroute->GetInputPin() : NodeReroute->GetOutputPin());
}
else if (const UCustomizableObjectNodeExternalPin* ExternalPinNode = Cast<UCustomizableObjectNodeExternalPin>(LinkedPin->GetOwningNodeUnchecked()))
{
check(Pin.Direction == EGPD_Input);
if (const UCustomizableObjectNodeExposePin* LinkedNode = ExternalPinNode->GetNodeExposePin())
{
const UEdGraphPin* ExposePin = LinkedNode->InputPin();
check(ExposePin);
PinsToVisit.Add(ExposePin);
}
}
else if (const UCustomizableObjectNodeExposePin* ExposePinNode = Cast<UCustomizableObjectNodeExposePin>(LinkedPin->GetOwningNodeUnchecked()))
{
check(Pin.Direction == EGPD_Output);
for (TObjectIterator<UCustomizableObjectNodeExternalPin> It; It; ++It)
{
const UCustomizableObjectNodeExternalPin* LinkedNode = *It;
if (LinkedNode &&
LinkedNode->GetNodeExposePin() == ExposePinNode)
{
const UEdGraphPin* ExternalPin = LinkedNode->GetExternalPin();
check(ExternalPin);
PinsToVisit.Add(ExternalPin);
}
}
}
else
{
Result.Add(LinkedPin);
}
}
}
if (bOutCycleDetected)
{
*bOutCycleDetected = bCycleDetected;
}
return Result;
}
TArray<UEdGraphPin*> FollowInputPinArray(const UEdGraphPin& Pin, bool* bOutCycleDetected)
{
check(Pin.Direction == EGPD_Input);
return FollowPinArray(Pin, true, bOutCycleDetected);
}
UEdGraphPin* FollowInputPin(const UEdGraphPin& Pin, bool* CycleDetected)
{
TArray<UEdGraphPin*> Result = FollowInputPinArray(Pin, CycleDetected);
check(Result.Num() <= 1); // Use FollowInputPinArray if the pin can have more than one input.
if (!Result.IsEmpty())
{
return Result[0];
}
else
{
return nullptr;
}
}
TArray<UEdGraphPin*> FollowOutputPinArray(const UEdGraphPin& Pin, bool* bOutCycleDetected)
{
check(Pin.Direction == EGPD_Output);
return FollowPinArray(Pin, true, bOutCycleDetected);
}
UEdGraphPin* FollowOutputPin(const UEdGraphPin& Pin, bool* CycleDetected)
{
TArray<UEdGraphPin*> Result = FollowOutputPinArray(Pin, CycleDetected);
check(Result.Num() <= 1); // Use FollowInputPinArray if the pin can have more than one input.
if (!Result.IsEmpty())
{
return Result[0];
}
else
{
return nullptr;
}
}
TArray<UEdGraphPin*> ReverseFollowPinArray(const UEdGraphPin& Pin, bool bIgnoreOrphan, bool* bOutCycleDetected)
{
bool bCycleDetected = false;
TArray<UEdGraphPin*> Result;
TSet<const UEdGraphPin*> Visited;
TArray<UEdGraphPin*> PinsToVisit;
PinsToVisit.Add(const_cast<UEdGraphPin*>(&Pin));
while (PinsToVisit.Num())
{
UEdGraphPin& CurrentPin = *PinsToVisit.Pop();
if (!bIgnoreOrphan && IsPinOrphan(CurrentPin))
{
continue;
}
Visited.FindOrAdd(&CurrentPin, &bCycleDetected);
if (bCycleDetected)
{
continue;
}
if (const UCustomizableObjectNodeExposePin* ExposePinNode = Cast<UCustomizableObjectNodeExposePin>(CurrentPin.GetOwningNodeUnchecked()))
{
check(Pin.Direction == EGPD_Input);
for (TObjectIterator<UCustomizableObjectNodeExternalPin> It; It; ++It)
{
const UCustomizableObjectNodeExternalPin* LinkedNode = *It;
if (IsValid(LinkedNode) &&
!LinkedNode->IsTemplate() &&
LinkedNode->GetNodeExposePin() == ExposePinNode)
{
const UEdGraphPin* ExternalPin = LinkedNode->GetExternalPin();
check(ExternalPin);
for (UEdGraphPin* LinkedPin : ExternalPin->LinkedTo)
{
PinsToVisit.Add(LinkedPin);
}
}
}
}
else if (const UCustomizableObjectNodeExternalPin* ExternalPinNode = Cast<UCustomizableObjectNodeExternalPin>(CurrentPin.GetOwningNodeUnchecked()))
{
check(Pin.Direction == EGPD_Output);
if (const UCustomizableObjectNodeExposePin* LinkedNode = ExternalPinNode->GetNodeExposePin())
{
const UEdGraphPin* ExposePin = LinkedNode->InputPin();
check(ExposePin);
for (UEdGraphPin* LinkedPin : ExposePin->LinkedTo)
{
PinsToVisit.Add(LinkedPin);
}
}
}
else if (const UCustomizableObjectNodeReroute* NodeReroute = Cast<UCustomizableObjectNodeReroute>(CurrentPin.GetOwningNodeUnchecked()))
{
UEdGraphPin* ReroutePin = Pin.Direction == EGPD_Output ? NodeReroute->GetInputPin() : NodeReroute->GetOutputPin();
for (UEdGraphPin* LinkedPin : ReroutePin->LinkedTo)
{
PinsToVisit.Add(LinkedPin);
}
}
else
{
if (bIgnoreOrphan || !IsPinOrphan(CurrentPin))
{
Result.Add(&CurrentPin);
}
}
}
if (bOutCycleDetected)
{
*bOutCycleDetected = bCycleDetected;
}
return Result;
}
UCustomizableObjectNodeObject* GetRootNode(const UCustomizableObject* Object)
{
if (!Object)
{
return nullptr;
}
for (UEdGraphNode* Node : Object->GetPrivate()->GetSource()->Nodes)
{
if (UCustomizableObjectNodeObject* NodeObject = Cast<UCustomizableObjectNodeObject>(Node))
{
if (NodeObject->bIsBase)
{
return NodeObject;
}
}
}
return nullptr;
}
bool GetParentsUntilRoot(const UCustomizableObject* Object, TArray<UCustomizableObjectNodeObject*>& ArrayNodeObject, TArray<const UCustomizableObject*>& ArrayCustomizableObject)
{
UCustomizableObjectNodeObject* Root = GetRootNode(Object);
bool bSuccess = true;
if (Root)
{
if (!ArrayCustomizableObject.Contains(Object))
{
ArrayNodeObject.Add(Root);
ArrayCustomizableObject.Add(Object);
}
else
{
// This object has already been visted which means that there is a Cycle between Customizable Objects
return false;
}
if (Root->ParentObject != nullptr)
{
bSuccess = GetParentsUntilRoot(Root->ParentObject, ArrayNodeObject, ArrayCustomizableObject);
}
}
return bSuccess;
}
bool HasCandidateAsParent(UCustomizableObjectNodeObject* Node, UCustomizableObject* ParentCandidate)
{
if (Node->ParentObject == ParentCandidate)
{
return true;
}
if (Node->ParentObject != nullptr)
{
UCustomizableObjectNodeObject* ParentNodeObject = GetRootNode(Node->ParentObject);
if (ParentNodeObject->ParentObject)
{
return HasCandidateAsParent(ParentNodeObject, ParentCandidate);
}
else
{
return false;
}
}
return false;
}
UCustomizableObject* GetFullGraphRootObject(const UCustomizableObjectNodeObject* Node, TArray<UCustomizableObject*>& VisitedObjects)
{
if (Node->ParentObject != nullptr)
{
VisitedObjects.Add(Node->ParentObject);
UCustomizableObjectNodeObject* Root = GetRootNode(Node->ParentObject);
if (Root->ParentObject == nullptr)
{
return Node->ParentObject;
}
else
{
if (VisitedObjects.Contains(Root->ParentObject))
{
//There is a cycle
return nullptr;
}
else
{
return GetFullGraphRootObject(Root, VisitedObjects);
}
}
}
return nullptr;
}
UCustomizableObject* GraphTraversal::GetObject(const UCustomizableObjectNode& Node)
{
if (Node.IsInMacro())
{
return nullptr;
}
return CastChecked<UCustomizableObject>(Node.GetGraph()->GetOuter());
}
UCustomizableObject* GraphTraversal::GetRootObject(UCustomizableObject* ChildObject)
{
const UCustomizableObject* ConstChildObject = ChildObject;
return const_cast<UCustomizableObject*>(GetRootObject(ConstChildObject));
}
const UCustomizableObject* GraphTraversal::GetRootObject(const UCustomizableObject* ChildObject)
{
// Grab a node to start the search -> Get the root since it should be always present
UCustomizableObjectNodeObject* ObjectRootNode = GetRootNode(ChildObject);
if (ObjectRootNode && ObjectRootNode->ParentObject)
{
TArray<UCustomizableObject*> VisitedNodes;
return GetFullGraphRootObject(ObjectRootNode, VisitedNodes);
}
// No parent object found, return input as the parent of the graph
// This can also mean the ObjectRootNode does not exist because it has not been opened yet (so no nodes have been generated)
return ChildObject;
}
void RecursiveVisitNodes(UCustomizableObjectNode& CurrentNode, const TFunction<void(UCustomizableObjectNode&)>& VisitFunction,
TArray<const UCustomizableObjectNodeMacroInstance*>& MacroContext, TSet<UCustomizableObjectNode*>& VisitedNodes, const TMultiMap<FGuid, UCustomizableObjectNodeObject*>& ObjectGroupMap)
{
// Check if we already visited this node
if (VisitedNodes.Contains(&CurrentNode))
{
return;
}
// Add it to the cache of already visited nodes
VisitedNodes.Add(&CurrentNode);
// Visit the current Node
VisitFunction(CurrentNode);
// Iterate through all nodes linked to the current node
for (UEdGraphPin* Pin : CurrentNode.GetAllNonOrphanPins())
{
if (Pin->Direction != EGPD_Input)
{
continue;
}
for (UEdGraphPin* ConnectedPin : FollowInputPinArray(*Pin))
{
UEdGraphNode* ConnectedNode = ConnectedPin->GetOwningNode();
if (UCustomizableObjectNodeObjectGroup* ObjectGroupNode = Cast<UCustomizableObjectNodeObjectGroup>(ConnectedNode))
{
// Visit the pins directly connected to the ObjectGroup node such as the child objects and projectors
RecursiveVisitNodes(*ObjectGroupNode, VisitFunction, MacroContext, VisitedNodes, ObjectGroupMap);
// Visit the child objects in other COs
TArray<UCustomizableObjectNodeObject*> ChildObjectNodes;
ObjectGroupMap.MultiFind(ObjectGroupNode->NodeGuid, ChildObjectNodes);
for (UCustomizableObjectNode* ChildObjectNode : ChildObjectNodes)
{
RecursiveVisitNodes(*ChildObjectNode, VisitFunction, MacroContext, VisitedNodes, ObjectGroupMap);
}
}
else if (UCustomizableObjectNodeMacroInstance* MacroInstanceNode = Cast<UCustomizableObjectNodeMacroInstance>(ConnectedNode))
{
// We visit this node here because we need to pass through it multiple times
if (!VisitedNodes.Contains(MacroInstanceNode))
{
VisitFunction(*MacroInstanceNode);
VisitedNodes.Add(MacroInstanceNode);
}
if (const UEdGraphPin* OutputPin = MacroInstanceNode->GetMacroIOPin(ECOMacroIOType::COMVT_Output, ConnectedPin->PinName))
{
if (UCustomizableObjectNode* OutputNode = Cast<UCustomizableObjectNode>(OutputPin->GetOwningNode()))
{
// We visit this node here because we need to pass through it multiple times
if (!VisitedNodes.Contains(OutputNode))
{
VisitFunction(*OutputNode);
VisitedNodes.Add(OutputNode);
}
}
// Going to the node linked to the OutputPin
if (const UEdGraphPin* OutputLinkedPin = FollowInputPin(*OutputPin))
{
// We go directly to the node linked to the output node to ensure that is linked and we need to visit it.
if (UCustomizableObjectNode* OutputLinkedNode = Cast<UCustomizableObjectNode>(OutputLinkedPin->GetOwningNode()))
{
MacroContext.Push(MacroInstanceNode);
RecursiveVisitNodes(*OutputLinkedNode, VisitFunction, MacroContext, VisitedNodes, ObjectGroupMap);
MacroContext.Pop();
}
}
}
}
else if (UCustomizableObjectNodeTunnel* IOMacroNode = Cast<UCustomizableObjectNodeTunnel>(ConnectedNode))
{
// Output Nodes are already visited
if (IOMacroNode->bIsInputNode)
{
check(MacroContext.Num());
// We visit this node here because we need to pass through it multiple times
if (!VisitedNodes.Contains(IOMacroNode))
{
VisitFunction(*IOMacroNode);
VisitedNodes.Add(IOMacroNode);
}
// Going to the parent's graph where the current macro was instantiated
const UCustomizableObjectNodeMacroInstance* ParentMacroInstanceNode = MacroContext.Pop();
if (ParentMacroInstanceNode)
{
if (const UEdGraphPin* InputPin = ParentMacroInstanceNode->FindPin(ConnectedPin->PinName, EEdGraphPinDirection::EGPD_Input))
{
// Going to the node linked to the Macro Instance
if (const UEdGraphPin* InputLinkedPin = FollowInputPin(*InputPin))
{
if (UCustomizableObjectNode* InputLinkedNode = Cast<UCustomizableObjectNode>(InputLinkedPin->GetOwningNode()))
{
RecursiveVisitNodes(*InputLinkedNode, VisitFunction, MacroContext, VisitedNodes, ObjectGroupMap);
}
}
}
}
MacroContext.Push(ParentMacroInstanceNode);
}
}
else if (UCustomizableObjectNode* Node = Cast<UCustomizableObjectNode>(ConnectedNode))
{
RecursiveVisitNodes(*Node, VisitFunction, MacroContext, VisitedNodes, ObjectGroupMap);
}
}
}
}
void GraphTraversal::VisitNodes(UCustomizableObjectNode& StartNode, const TFunction<void(UCustomizableObjectNode&)>& VisitFunction, const TMultiMap<FGuid, UCustomizableObjectNodeObject*>* ObjectGroupMap, TArray<const UCustomizableObjectNodeMacroInstance*>* MacroContext)
{
TSet<UCustomizableObjectNode*> VisitedNodes;
TMultiMap<FGuid, UCustomizableObjectNodeObject*> LocalObjectGroupMap;
TArray<const UCustomizableObjectNodeMacroInstance*> LocalMacroContext;
if (!ObjectGroupMap)
{
LocalObjectGroupMap = GetNodeGroupObjectNodeMapping(GetObject(StartNode));
ObjectGroupMap = &LocalObjectGroupMap;
}
RecursiveVisitNodes(StartNode, VisitFunction, MacroContext ? *MacroContext : LocalMacroContext, VisitedNodes, *ObjectGroupMap);
check(MacroContext ? MacroContext->IsEmpty() : LocalMacroContext.IsEmpty());
}
const UEdGraphPin* GraphTraversal::FindIOPinSourceThroughMacroContext(const UEdGraphPin& Pin, TArray<const UCustomizableObjectNodeMacroInstance*>* MacroContext)
{
const UEdGraphPin* ReturnPin = nullptr;
const UEdGraphNode* Node = Pin.GetOwningNode();
check(Node);
if (const UCustomizableObjectNodeMacroInstance* NodeMacro = Cast<UCustomizableObjectNodeMacroInstance>(Node))
{
if (const UEdGraphPin* OutputPin = NodeMacro->GetMacroIOPin(Pin.Direction == EGPD_Output ? ECOMacroIOType::COMVT_Output : ECOMacroIOType::COMVT_Input, Pin.PinName))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*OutputPin))
{
TArray<const UCustomizableObjectNodeMacroInstance*> NewMacroContext;
if (!MacroContext)
{
MacroContext = &NewMacroContext;
}
else if (MacroContext->Contains(NodeMacro))
{
return nullptr;
}
MacroContext->Push(NodeMacro);
ReturnPin = GraphTraversal::FindIOPinSourceThroughMacroContext(*ConnectedPin, MacroContext);
MacroContext->Pop();
}
}
}
else if (const UCustomizableObjectNodeTunnel* NodeTunnel = Cast<UCustomizableObjectNodeTunnel>(Node))
{
if (MacroContext && MacroContext->Num())
{
const UCustomizableObjectNodeMacroInstance* MacroInstanceNode = MacroContext->Pop();
if (MacroInstanceNode)
{
if (const UEdGraphPin* FollowPin = MacroInstanceNode->FindPin(Pin.PinName, NodeTunnel->bIsInputNode ? EEdGraphPinDirection::EGPD_Input : EEdGraphPinDirection::EGPD_Output))
{
if (const UEdGraphPin* ConnectedPin = NodeTunnel->bIsInputNode ? FollowInputPin(*FollowPin) : FollowOutputPin(*FollowPin))
{
ReturnPin = GraphTraversal::FindIOPinSourceThroughMacroContext(*ConnectedPin, MacroContext);
}
}
}
MacroContext->Push(MacroInstanceNode);
}
}
else
{
ReturnPin = &Pin;
}
return ReturnPin;
}
UCustomizableObjectNodeObject* GraphTraversal::GetFullGraphRootNode(const UCustomizableObject* Object, TArray<const UCustomizableObject*>& VisitedObjects)
{
if (Object != nullptr)
{
VisitedObjects.Add(Object);
UCustomizableObjectNodeObject* Root = GetRootNode(Object);
if (Root->ParentObject == nullptr)
{
return Root;
}
else
{
if (VisitedObjects.Contains(Root->ParentObject))
{
//There is a cycle
return nullptr;
}
else
{
return GetFullGraphRootNode(Root->ParentObject, VisitedObjects);
}
}
}
return nullptr;
}
const UEdGraphPin* FindMeshBaseSource(const UEdGraphPin& Pin, const bool bOnlyLookForStaticMesh, TArray<const UCustomizableObjectNodeMacroInstance*>* MacroContext)
{
check(Pin.Direction == EGPD_Output);
check(Pin.PinType.PinCategory == UEdGraphSchema_CustomizableObject::PC_Mesh
||
Pin.PinType.PinCategory == UEdGraphSchema_CustomizableObject::PC_PassThroughMesh
||
Pin.PinType.PinCategory == UEdGraphSchema_CustomizableObject::PC_Material
||
Pin.PinType.PinCategory == UEdGraphSchema_CustomizableObject::PC_Modifier
);
const UEdGraphNode* Node = Pin.GetOwningNode();
check(Node);
if (Cast<UCustomizableObjectNodeSkeletalMesh>(Node))
{
if (!bOnlyLookForStaticMesh)
{
return &Pin;
}
}
else if (Cast<UCustomizableObjectNodeStaticMesh>(Node))
{
return &Pin;
}
else if (const UCustomizableObjectNodeMeshReshape* TypedNodeReshape = Cast<UCustomizableObjectNodeMeshReshape>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeReshape->BaseMeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeMeshMorph* TypedNodeMorph = Cast<UCustomizableObjectNodeMeshMorph>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeMorph->MeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeMeshSwitch* TypedNodeSwitch = Cast<UCustomizableObjectNodeMeshSwitch>(Node))
{
if (const UEdGraphPin* EnumParameterPin = FollowInputPin(*TypedNodeSwitch->SwitchParameter()))
{
if (const UCustomizableObjectNodeEnumParameter* EnumNode = Cast<UCustomizableObjectNodeEnumParameter>(EnumParameterPin->GetOwningNode()))
{
if (const UEdGraphPin* DefaultPin = TypedNodeSwitch->GetElementPin(EnumNode->DefaultIndex))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*DefaultPin))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
}
}
}
else if (const UCustomizableObjectNodeMeshVariation* TypedNodeMeshVar = Cast<UCustomizableObjectNodeMeshVariation>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeMeshVar->DefaultPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
for (int32 i = 0; i < TypedNodeMeshVar->GetNumVariations(); ++i)
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeMeshVar->VariationPin(i)))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
}
else if (const UCustomizableObjectNodeMaterialBase* TypedNodeMat = Cast<UCustomizableObjectNodeMaterialBase>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeMat->GetMeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeMaterialVariation* TypedNodeMatVar = Cast<UCustomizableObjectNodeMaterialVariation>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeMatVar->DefaultPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeModifierExtendMeshSection* TypedNodeExtend = Cast<UCustomizableObjectNodeModifierExtendMeshSection>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeExtend->AddMeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeMeshMorphStackDefinition* TypedNodeMorphStackDef = Cast<UCustomizableObjectNodeMeshMorphStackDefinition>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeMorphStackDef->GetMeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeMeshMorphStackApplication* TypedNodeMorphStackApp = Cast<UCustomizableObjectNodeMeshMorphStackApplication>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*TypedNodeMorphStackApp->GetMeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeMeshReshape* NodeMeshReshape = Cast<UCustomizableObjectNodeMeshReshape>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*NodeMeshReshape->BaseMeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (Cast<UCustomizableObjectNodeTable>(Node))
{
if (!bOnlyLookForStaticMesh)
{
return &Pin;
}
}
else if (const UCustomizableObjectNodeAnimationPose* NodeMeshPose = Cast<UCustomizableObjectNodeAnimationPose>(Node))
{
if (const UEdGraphPin* ConnectedPin = FollowInputPin(*NodeMeshPose->GetInputMeshPin()))
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (Cast<UCustomizableObjectNodeSkeletalMeshParameter>(Node))
{
if (!bOnlyLookForStaticMesh)
{
return &Pin;
}
}
else if (const UCustomizableObjectNodeMacroInstance* NodeMacro = Cast<UCustomizableObjectNodeMacroInstance>(Node))
{
const UEdGraphPin* ConnectedPin = GraphTraversal::FindIOPinSourceThroughMacroContext(Pin, MacroContext);
if (ConnectedPin)
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else if (const UCustomizableObjectNodeTunnel* NodeTunnel = Cast<UCustomizableObjectNodeTunnel>(Node))
{
const UEdGraphPin* ConnectedPin = GraphTraversal::FindIOPinSourceThroughMacroContext(Pin, MacroContext);
if (ConnectedPin)
{
return FindMeshBaseSource(*ConnectedPin, bOnlyLookForStaticMesh, MacroContext);
}
}
else
{
unimplemented(); // Case missing.
}
return nullptr;
}
void GetNodeGroupObjectNodeMappingImmersive(UCustomizableObject* Object, IAssetRegistry& AssetRegistry, TSet<UCustomizableObject*>& Visited, TMultiMap<FGuid, UCustomizableObjectNodeObject*>& Mapping)
{
Visited.Add(Object);
TArray<FName> ArrayReferenceNames;
AssetRegistry.GetReferencers(*Object->GetOuter()->GetPathName(), ArrayReferenceNames, UE::AssetRegistry::EDependencyCategory::Package, UE::AssetRegistry::EDependencyQuery::Hard);
FARFilter Filter;
Filter.bIncludeOnlyOnDiskAssets = false;
for (const FName& ReferenceName : ArrayReferenceNames)
{
if (!ReferenceName.ToString().StartsWith(TEXT("/TempAutosave")))
{
Filter.PackageNames.Add(ReferenceName);
}
}
TArray<FAssetData> ArrayAssetData;
AssetRegistry.GetAssets(Filter, ArrayAssetData);
for (FAssetData& AssetData : ArrayAssetData)
{
UCustomizableObject* ChildObject = Cast<UCustomizableObject>(MutablePrivate::LoadObject(AssetData));
if (!ChildObject)
{
continue;
}
if (ChildObject != Object && !ChildObject->HasAnyFlags(RF_Transient))
{
if (UCustomizableObjectNodeObject* ChildRoot = GetRootNode(ChildObject))
{
if (ChildRoot->ParentObject == Object)
{
Mapping.Add(ChildRoot->ParentObjectGroupId, ChildRoot);
}
}
}
if (!Visited.Contains(ChildObject))
{
GetNodeGroupObjectNodeMappingImmersive(ChildObject, AssetRegistry, Visited, Mapping);
}
}
}
TMultiMap<FGuid, UCustomizableObjectNodeObject*> GetNodeGroupObjectNodeMapping(UCustomizableObject* Object)
{
MUTABLE_CPUPROFILER_SCOPE(GetNodeGroupObjectNodeMapping);
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
TSet<UCustomizableObject*> Visited;
TMultiMap<FGuid, UCustomizableObjectNodeObject*> Mapping;
GetNodeGroupObjectNodeMappingImmersive(Object, AssetRegistry, Visited, Mapping);
return Mapping;
}
void GetAllObjectsInGraph(UCustomizableObject* Object, TSet<UCustomizableObject*>& OutObjects)
{
if (!Object)
{
return;
}
// Search the root of the CO's graph
UCustomizableObject* RootObject = GraphTraversal::GetRootObject(Object);
TMultiMap<FGuid, UCustomizableObjectNodeObject*> DummyMap;
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
GetNodeGroupObjectNodeMappingImmersive(RootObject, AssetRegistry, OutObjects, DummyMap);
}
namespace GraphTraversal
{
bool IsRootObject(const UCustomizableObject& Object)
{
const TObjectPtr<UEdGraph> Source = Object.GetPrivate()->GetSource();
if (!Source || !Source->Nodes.Num())
{
// Conservative approach.
return true;
}
TArray<UCustomizableObjectNodeObject*> ObjectNodes;
Source->GetNodesOfClass<UCustomizableObjectNodeObject>(ObjectNodes);
// Look for the base object node
const UCustomizableObjectNodeObject* Root = nullptr;
for (TArray<UCustomizableObjectNodeObject*>::TIterator It(ObjectNodes); It; ++It)
{
if ((*It)->bIsBase)
{
Root = *It;
}
}
return Root && !Root->ParentObject;
}
}
void NodePinConnectionListChanged(const TArray<UEdGraphPin*>& Pins)
{
TMap<UEdGraphNode*, TSet<UEdGraphPin*>> SortedPins;
for (UEdGraphPin* Pin : Pins)
{
if (UEdGraphNode* Node = Pin->GetOwningNodeUnchecked())
{
SortedPins.FindOrAdd(Node).Add(Pin);
}
}
for (TTuple<UEdGraphNode*, TSet<UEdGraphPin*>> Pair : SortedPins)
{
for (UEdGraphPin* ConnectedPin : Pair.Value)
{
Pair.Key->PinConnectionListChanged(ConnectedPin);
}
Pair.Key->NodeConnectionListChanged();
}
}