CelticCraft/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.cpp

716 lines
17 KiB
C++
Raw Normal View History

2023-07-03 16:17:13 +00:00
// Copyright 2020 Phyronnaz
#include "VoxelGraphNode.h"
#include "VoxelGraphGenerator.h"
#include "VoxelNode.h"
#include "VoxelNodes/VoxelLocalVariables.h"
#include "VoxelNodes/VoxelGraphMacro.h"
#include "VoxelGraphNodes/VoxelGraphNode_Knot.h"
#include "VoxelGraphEditorUtilities.h"
#include "IVoxelGraphEditorToolkit.h"
#include "VoxelGraphEditorCommands.h"
#include "VoxelEdGraph.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraph/EdGraphNode.h"
#include "GraphEditorActions.h"
#include "Engine/Font.h"
#include "ScopedTransaction.h"
#include "Editor/EditorEngine.h"
#include "Framework/Commands/GenericCommands.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Launch/Resources/Version.h"
void UVoxelGraphNode::SetVoxelNode(UVoxelNode* InNode)
{
check(InNode);
VoxelNode = InNode;
bCanRenameNode = VoxelNode->CanRenameNode();
}
void UVoxelGraphNode::PostCopyNode()
{
// Make sure the VoxelNode goes back to being owned by the generator after copying.
ResetVoxelNodeOwner();
}
void UVoxelGraphNode::CreateInputPin()
{
const int32 PinIndex = GetInputCount();
UEdGraphPin* NewPin = CreatePin(EGPD_Input, FVoxelPinCategory::GetName(VoxelNode->GetInputPinCategory(PinIndex)), FName(), nullptr, VoxelNode->GetInputPinName(PinIndex));
if (NewPin->PinName.IsNone())
{
// Makes sure pin has a name for lookup purposes but user will never see it
NewPin->PinName = CreateUniquePinName(TEXT("Input"));
NewPin->PinFriendlyName = FText::FromString(TEXT(" "));
}
NewPin->DefaultValue = VoxelNode->GetInputPinDefaultValue(PinIndex);
if (NewPin->DefaultValue.IsEmpty())
{
NewPin->DefaultValue = FVoxelPinCategory::GetDefaultValue(VoxelNode->GetInputPinCategory(PinIndex));
}
}
void UVoxelGraphNode::CreateOutputPin()
{
const int32 PinIndex = GetOutputCount();
UEdGraphPin* NewPin = CreatePin(EGPD_Output, FVoxelPinCategory::GetName(VoxelNode->GetOutputPinCategory(PinIndex)), FName(), nullptr, VoxelNode->GetOutputPinName(PinIndex));
if (NewPin->PinName.IsNone())
{
// Makes sure pin has a name for lookup purposes but user will never see it
NewPin->PinName = CreateUniquePinName(TEXT("Output"));
NewPin->PinFriendlyName = FText::FromString(TEXT(" "));
}
}
void UVoxelGraphNode::AddInputPin()
{
const FScopedTransaction Transaction(VOXEL_LOCTEXT("Add Input Pin"));
Modify();
const int32 Increment = VoxelNode->GetInputPinsIncrement();
VoxelNode->InputPinCount += Increment;
ensure(VoxelNode->InputPinCount <= VoxelNode->GetMaxInputPins());
for (int32 Index = 0; Index < Increment; ++Index)
{
CreateInputPin();
}
VoxelNode->OnInputPinCountModified();
UVoxelGraphGenerator* Generator = CastChecked<UVoxelEdGraph>(GetGraph())->GetGenerator();
Generator->CompileVoxelNodesFromGraphNodes();
// Refresh the current graph, so the pins can be updated
GetGraph()->NotifyGraphChanged();
}
void UVoxelGraphNode::RemoveInputPin(UEdGraphPin* InGraphPin)
{
const FScopedTransaction Transaction(VOXEL_LOCTEXT("Delete Input Pin"));
Modify();
for (auto* InputPin : GetInputPins())
{
if (InGraphPin == InputPin)
{
InGraphPin->MarkAsGarbage();
Pins.Remove(InGraphPin);
const int32 Increment = VoxelNode->GetInputPinsIncrement();
if (Increment > 1)
{
const int32 PinIndex = VoxelNode->GetInputPinIndex(InGraphPin->PinId);
if (ensure(PinIndex != -1))
{
// Below = higher index!
const int32 PinsBelow = (VoxelNode->InputPinCount - 1 - PinIndex) % Increment;
const int32 PinsAbove = Increment - 1 - PinsBelow;
for (int32 Index = PinIndex - PinsAbove; Index <= PinIndex + PinsBelow; Index++)
{
if (ensure(VoxelNode->InputPins.IsValidIndex(Index)))
{
const auto PinId = VoxelNode->InputPins[Index].PinId;
Pins.RemoveAll([&](auto& ArrayPin) { return ArrayPin->PinId == PinId; });
}
}
}
}
// also remove the VoxelNode child node so ordering matches
VoxelNode->Modify();
VoxelNode->InputPinCount -= Increment;
ensure(VoxelNode->InputPinCount >= VoxelNode->GetMinInputPins());
break;
}
}
VoxelNode->OnInputPinCountModified();
UVoxelGraphGenerator* Generator = CastChecked<UVoxelEdGraph>(GetGraph())->GetGenerator();
Generator->CompileVoxelNodesFromGraphNodes();
// Refresh the current graph, so the pins can be updated
GetGraph()->NotifyGraphChanged();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool UVoxelGraphNode::CanSplitPin_Voxel(const UEdGraphPin& Pin) const
{
return const_cast<UVoxelGraphNode*>(this)->TrySplitPin(const_cast<UEdGraphPin&>(Pin), true);
}
bool UVoxelGraphNode::CanCombinePin(const UEdGraphPin& Pin) const
{
return const_cast<UVoxelGraphNode*>(this)->TryCombinePin(const_cast<UEdGraphPin&>(Pin), true);
}
bool UVoxelGraphNode::TrySplitPin(UEdGraphPin& Pin, bool bOnlyCheck)
{
ensure(!Pin.bHidden);
if (Pin.SubPins.Num() == 0 || Pin.LinkedTo.Num() > 0)
{
return false;
}
if (bOnlyCheck)
{
return true;
}
TArray<FString> SubDefaultValues;
Pin.DefaultValue.ParseIntoArray(SubDefaultValues, TEXT(","));
for (int32 Index = 0; Index < Pin.SubPins.Num(); Index++)
{
auto* SubPin = Pin.SubPins[Index];
ensure(SubPin->bHidden);
ensure(SubPin->ParentPin == &Pin);
SubPin->bHidden = false;
SubPin->ParentPin = nullptr;
SubPin->DefaultValue = SubDefaultValues.IsValidIndex(Index) ? SubDefaultValues[Index] : "";
}
Pin.SubPins.Empty();
ensure(RemovePin(&Pin));
GetGraph()->NotifyGraphChanged();
return true;
}
bool UVoxelGraphNode::TryCombinePin(UEdGraphPin& Pin, bool bOnlyCheck)
{
ensure(!Pin.bHidden);
const auto NeighborPins = Pin.Direction == EGPD_Input ? GetInputPins() : GetOutputPins();
const int32 PinIndex = NeighborPins.Find(&Pin);
if (!ensure(PinIndex != -1))
{
return false;
}
const auto CheckStart = [&](int32 Index)
{
if (!NeighborPins.IsValidIndex(Index) ||
!NeighborPins.IsValidIndex(Index + 2))
{
return false;
}
FString Name = NeighborPins[Index]->GetName();
if (!Name.RemoveFromStart("X"))
{
return false;
}
return
NeighborPins[Index + 1]->GetName() == "Y" + Name &&
NeighborPins[Index + 2]->GetName() == "Z" + Name;
};
const auto CheckEnd = [&](int32 Index)
{
if (!NeighborPins.IsValidIndex(Index) ||
!NeighborPins.IsValidIndex(Index + 2))
{
return false;
}
FString Name = NeighborPins[Index]->GetName();
if (!Name.RemoveFromEnd("X"))
{
return false;
}
return
NeighborPins[Index + 1]->GetName() == Name + "Y" &&
NeighborPins[Index + 2]->GetName() == Name + "Z";
};
int32 IndexX = -1;
bool bIsStart = false;
for (int32 Index = PinIndex - 2; Index <= PinIndex; Index++)
{
if (CheckStart(Index))
{
bIsStart = true;
IndexX = Index;
break;
}
if (CheckEnd(Index))
{
bIsStart = false;
IndexX = Index;
break;
}
}
if (IndexX == -1)
{
return false;
}
for (int32 Index = 0; Index < 3; Index++)
{
if (NeighborPins[IndexX + Index]->LinkedTo.Num() > 0)
{
return false;
}
}
if (bOnlyCheck)
{
return true;
}
FString ParentPinName = NeighborPins[IndexX]->GetName();
if (bIsStart)
{
ensure(ParentPinName.RemoveFromStart("X"));
ParentPinName.RemoveFromStart(".");
}
else
{
ensure(ParentPinName.RemoveFromEnd("X"));
ParentPinName.RemoveFromEnd(".");
}
auto* ParentPin = CreatePin(Pin.Direction, FVoxelPinCategory::GetName(EVoxelPinCategory::Vector), FName(), nullptr, *ParentPinName);
Pins.Pop(false);
FVector DefaultValue;
for (int32 Index = 0; Index < 3; Index++)
{
auto* SubPin = NeighborPins[IndexX + Index];
SubPin->bHidden = true;
SubPin->ParentPin = ParentPin;
DefaultValue[Index] = FCString::Atof(*SubPin->DefaultValue);
ParentPin->SubPins.Add(SubPin);
}
ParentPin->DefaultValue = FString::Printf(TEXT("%f,%f,%f"), DefaultValue.X, DefaultValue.Y, DefaultValue.Z);
// Add the parent before the sub pins
const int32 InsertIndex = Pins.Find(NeighborPins[IndexX]);
check(InsertIndex != -1);
Pins.Insert(ParentPin, InsertIndex);
GetGraph()->NotifyGraphChanged();
return true;
}
void UVoxelGraphNode::CombineAll()
{
const auto Copy = Pins;
for (auto& Pin : Copy)
{
if (!Pin->bHidden)
{
TryCombinePin(*Pin, false);
}
}
}
bool UVoxelGraphNode::HasVectorPin(UVoxelNode& Node, EEdGraphPinDirection Direction)
{
TArray<FString> Names;
if (Direction == EGPD_Input)
{
const int32 InputCount = Node.GetMinInputPins();
for (int32 Index = 0; Index < InputCount; Index++)
{
Names.Add(Node.GetInputPinName(Index).ToString());
}
}
else
{
const int32 OutputCount = Node.GetOutputPinsCount();
for (int32 Index = 0; Index < OutputCount; Index++)
{
Names.Add(Node.GetOutputPinName(Index).ToString());
}
}
const auto CheckStart = [&](int32 Index)
{
FString Name = Names[Index];
if (!Name.RemoveFromStart("X"))
{
return false;
}
return
Names[Index + 1] == "Y" + Name &&
Names[Index + 2] == "Z" + Name;
};
const auto CheckEnd = [&](int32 Index)
{
FString Name = Names[Index];
if (!Name.RemoveFromEnd("X"))
{
return false;
}
return
Names[Index + 1] == Name + "Y" &&
Names[Index + 2] == Name + "Z";
};
for (int32 Index = 0; Index < Names.Num() - 2; Index++)
{
if (CheckStart(Index) || CheckEnd(Index))
{
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool UVoxelGraphNode::CanAddInputPin() const
{
if (VoxelNode)
{
const int32 MinPins = VoxelNode->GetMinInputPins();
const int32 MaxPins = VoxelNode->GetMaxInputPins();
if (MinPins == MaxPins)
{
return false;
}
else
{
return GetInputCount() < MaxPins;
}
}
else
{
return false;
}
}
bool UVoxelGraphNode::IsCompact() const
{
return VoxelNode && VoxelNode->IsCompact();
}
FLinearColor UVoxelGraphNode::GetNodeBodyColor() const
{
if (!IsNodeEnabled())
{
return FLinearColor(1.0f, 1.0f, 1.0f, 0.5f);
}
if (VoxelNode)
{
for (auto& Pin : Pins)
{
if (Pin->bIsDiffing)
{
return FLinearColor(0.f, 0.f, 1.0f, 1.f);
}
}
return VoxelNode->GetNodeBodyColor();
}
return FLinearColor::White;
}
bool UVoxelGraphNode::IsOutdated() const
{
int32 InputIndex = 0;
int32 OutputIndex = 0;
for (auto* Pin : Pins)
{
if (Pin->SubPins.Num() > 0)
{
continue;
}
if (Pin->Direction == EGPD_Input)
{
if (FVoxelPinCategory::GetName(VoxelNode->GetInputPinCategory(InputIndex)) != Pin->PinType.PinCategory)
{
return true;
}
const FName PinName = VoxelNode->GetInputPinName(InputIndex);
if (!PinName.IsNone() && PinName != Pin->PinName)
{
return true;
}
InputIndex++;
}
else
{
check(Pin->Direction == EGPD_Output);
if (FVoxelPinCategory::GetName(VoxelNode->GetOutputPinCategory(OutputIndex)) != Pin->PinType.PinCategory)
{
return true;
}
const FName PinName = VoxelNode->GetOutputPinName(OutputIndex);
if (!PinName.IsNone() && PinName != Pin->PinName)
{
return true;
}
OutputIndex++;
}
}
return false;
}
void UVoxelGraphNode::CreateInputPins()
{
if (!ensure(VoxelNode)) return;
VoxelNode->InputPinCount = FMath::Clamp(VoxelNode->InputPinCount, VoxelNode->GetMinInputPins(), VoxelNode->GetMaxInputPins());
while (GetInputCount() < VoxelNode->InputPinCount)
{
CreateInputPin();
}
}
void UVoxelGraphNode::CreateOutputPins()
{
if (!ensure(VoxelNode)) return;
while (GetOutputCount() < VoxelNode->GetOutputPinsCount())
{
CreateOutputPin();
}
}
void UVoxelGraphNode::RestoreVectorPins(const TArray<UEdGraphPin*>& OldInputPins, const TArray<UEdGraphPin*>& OldOutputPins)
{
const auto NewInputPins = GetInputPins();
const auto NewOutputPins = GetOutputPins();
const auto Restore = [&](const TArray<UEdGraphPin*>& OldPins, const TArray<UEdGraphPin*>& NewPins)
{
int32 NewIndex = 0;
for (int32 Index = 0; Index < OldPins.Num() && NewIndex < NewPins.Num(); Index++)
{
auto* OldPin = OldPins[Index];
if (OldPin->SubPins.Num() == 0)
{
// Not a parent pin
auto* NewPin = NewPins[NewIndex++];
if (OldPin->ParentPin && !NewPin->ParentPin)
{
TryCombinePin(*NewPin, false);
}
}
}
};
Restore(OldInputPins, NewInputPins);
Restore(OldOutputPins, NewOutputPins);
}
FText UVoxelGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (VoxelNode)
{
if (TitleType == ENodeTitleType::EditableTitle)
{
return FText::FromString(VoxelNode->GetEditableName());
}
else
{
return VoxelNode->GetTitle();
}
}
else
{
return Super::GetNodeTitle(TitleType);
}
}
FLinearColor UVoxelGraphNode::GetNodeTitleColor() const
{
if (VoxelNode)
{
return VoxelNode->GetColor();
}
else
{
return FLinearColor::Gray;
}
}
void UVoxelGraphNode::PrepareForCopying()
{
if (VoxelNode)
{
// Temporarily take ownership of the VoxelNode, so that it is not deleted when cutting
VoxelNode->Rename(NULL, this, REN_DontCreateRedirectors);
}
}
FText UVoxelGraphNode::GetTooltipText() const
{
if (VoxelNode)
{
return VoxelNode->GetTooltip();
}
else
{
return GetNodeTitle(ENodeTitleType::ListView);
}
}
FString UVoxelGraphNode::GetDocumentationExcerptName() const
{
// Default the node to searching for an excerpt named for the C++ node class name, including the U prefix.
// This is done so that the excerpt name in the doc file can be found by find-in-files when searching for the full class name.
UClass* MyClass = (VoxelNode != NULL) ? VoxelNode->GetClass() : this->GetClass();
return FString::Printf(TEXT("%s%s"), MyClass->GetPrefixCPP(), *MyClass->GetName());
}
bool UVoxelGraphNode::CanUserDeleteNode() const
{
return !VoxelNode || VoxelNode->CanUserDeleteNode();
}
bool UVoxelGraphNode::CanDuplicateNode() const
{
return !VoxelNode || VoxelNode->CanDuplicateNode();
}
bool UVoxelGraphNode::CanJumpToDefinition() const
{
return VoxelNode && ((VoxelNode->IsA(UVoxelGraphMacroNode::StaticClass()) && CastChecked<UVoxelGraphMacroNode>(VoxelNode)->Macro) || VoxelNode->IsA<UVoxelLocalVariableUsage>());
}
void UVoxelGraphNode::JumpToDefinition() const
{
if (auto* Macro = Cast<UVoxelGraphMacroNode>(VoxelNode))
{
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(Macro->Macro);
}
else if (auto* Usage = Cast<UVoxelLocalVariableUsage>(VoxelNode))
{
if (Usage->Declaration)
{
FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(VoxelNode->Graph->VoxelGraph)->SelectNodesAndZoomToFit({ Usage->Declaration->GraphNode });
}
}
}
void UVoxelGraphNode::OnRenameNode(const FString& NewName)
{
if (VoxelNode)
{
VoxelNode->Modify();
VoxelNode->SetEditableName(NewName);
VoxelNode->MarkPackageDirty();
}
}
void UVoxelGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const
{
if (!VoxelNode)
{
return;
}
TArray<FGuid> PinIds;
PinIds.Add(Pin.PinId);
for (auto& SubPin : Pin.SubPins)
{
PinIds.Add(SubPin->PinId);
}
for (auto& PinId : PinIds)
{
int32 Index = VoxelNode->GetInputPinIndex(PinId);
if (Index != -1)
{
if (!HoverTextOut.IsEmpty()) HoverTextOut += "\n";
HoverTextOut += VoxelNode->GetInputPinToolTip(Index);
}
else
{
Index = VoxelNode->GetOutputPinIndex(PinId);
if (Index != -1)
{
if (!HoverTextOut.IsEmpty()) HoverTextOut += "\n";
HoverTextOut += VoxelNode->GetOutputPinToolTip(Index);
}
}
}
}
void UVoxelGraphNode::PostLoad()
{
Super::PostLoad();
// Fixup any VoxelNode back pointers that may be out of date
if (VoxelNode)
{
VoxelNode->GraphNode = this;
}
for (int32 Index = 0; Index < Pins.Num(); ++Index)
{
UEdGraphPin* Pin = Pins[Index];
Pin->PinType.bIsConst = false;
Pin->PinType.ContainerType = EPinContainerType::None; // Remove preview
if (Pin->PinName.IsNone())
{
// Makes sure pin has a name for lookup purposes but user will never see it
if (Pin->Direction == EGPD_Input)
{
Pin->PinName = CreateUniquePinName(TEXT("Input"));
}
else
{
Pin->PinName = CreateUniquePinName(TEXT("Output"));
}
Pin->PinFriendlyName = FText::FromString(TEXT(" "));
}
}
}
void UVoxelGraphNode::PostEditImport()
{
// Make sure this VoxelNode is owned by the generator it's being pasted into.
ResetVoxelNodeOwner();
}
void UVoxelGraphNode::PostDuplicate(bool bDuplicateForPIE)
{
Super::PostDuplicate(bDuplicateForPIE);
if (!bDuplicateForPIE)
{
CreateNewGuid();
}
}
void UVoxelGraphNode::ResetVoxelNodeOwner()
{
if (VoxelNode)
{
UVoxelGraphGenerator* Generator = CastChecked<UVoxelEdGraph>(GetGraph())->GetGenerator();
if (VoxelNode->GetOuter() != Generator)
{
// Ensures VoxelNode is owned by the generator
VoxelNode->Rename(NULL, Generator, REN_DontCreateRedirectors);
}
// Set up the back pointer for newly created voxel nodes
VoxelNode->GraphNode = this;
}
}