CelticCraft/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.cpp

1731 lines
No EOL
53 KiB
C++

// Copyright 2020 Phyronnaz
#include "VoxelGraphEditorToolkit.h"
#include "VoxelGraphEditorUtilities.h"
#include "VoxelGraphGenerator.h"
#include "VoxelGraphShortcuts.h"
#include "VoxelGraphPreviewSettings.h"
#include "VoxelGraphSchema.h"
#include "VoxelNodes/VoxelGraphMacro.h"
#include "VoxelNodes/VoxelLocalVariables.h"
#include "VoxelGraphEditorCommands.h"
#include "VoxelGraphNodes/VoxelGraphNode_Root.h"
#include "VoxelGraphNodes/VoxelGraphNode.h"
#include "VoxelGraphNodes/SVoxelGraphNode.h"
#include "VoxelGraphNodes/VoxelGraphNode_Knot.h"
#include "VoxelMessages.h"
#include "VoxelDebugGraphUtils.h"
#include "VoxelEditorModule.h"
#include "SVoxelPalette.h"
#include "Preview/SVoxelGraphPreview.h"
#include "Preview/SVoxelGraphPreviewViewport.h"
#include "Preview/VoxelGraphPreview.h"
#include "IDetailsView.h"
#include "PropertyEditorModule.h"
#include "Modules/ModuleManager.h"
#include "ScopedTransaction.h"
#include "Misc/MessageDialog.h"
#include "HAL/PlatformApplicationMisc.h"
#include "Editor.h"
#include "EditorStyleSet.h"
#include "GraphEditorActions.h"
#include "GraphEditor.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Framework/Commands/GenericCommands.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SScaleBox.h"
#include "EdGraph/EdGraph.h"
#include "EdGraphUtilities.h"
#include "AdvancedPreviewScene.h"
#include "MessageLogModule.h"
#include "IMessageLogListing.h"
#include "Logging/TokenizedMessage.h"
const FName FVoxelGraphEditorToolkit::GraphCanvasTabId(TEXT("VoxelGraphEditor_GraphCanvas"));
const FName FVoxelGraphEditorToolkit::DebugGraphCanvasTabId(TEXT("VoxelGraphEditor_DebugGraphCanvas"));
const FName FVoxelGraphEditorToolkit::PropertiesTabId(TEXT("VoxelGraphEditor_Properties"));
const FName FVoxelGraphEditorToolkit::ShortcutsTabId(TEXT("VoxelGraphEditor_Shortcuts"));
const FName FVoxelGraphEditorToolkit::PreviewSettingsTabId(TEXT("VoxelGraphEditor_PreviewSettings"));
const FName FVoxelGraphEditorToolkit::PaletteTabId(TEXT("VoxelGraphEditor_Palette"));
const FName FVoxelGraphEditorToolkit::PreviewTabId(TEXT("VoxelGraphEditor_Preview"));
const FName FVoxelGraphEditorToolkit::PreviewViewportTabId(TEXT("VoxelGraphEditor_PreviewViewport"));
const FName FVoxelGraphEditorToolkit::MessagesTabId(TEXT("VoxelGraphEditor_Messages"));
FVoxelGraphEditorToolkit::FVoxelGraphEditorToolkit()
{
}
FVoxelGraphEditorToolkit::~FVoxelGraphEditorToolkit()
{
GEditor->UnregisterForUndo(this);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::RegisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager)
{
WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(VOXEL_LOCTEXT("Voxel Editor"));
auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef();
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
InTabManager->RegisterTabSpawner(GraphCanvasTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_GraphCanvas))
.SetDisplayName(VOXEL_LOCTEXT("Main Graph"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "GraphEditor.EventGraph_16x"));
InTabManager->RegisterTabSpawner(DebugGraphCanvasTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_DebugGraphCanvas))
.SetDisplayName(VOXEL_LOCTEXT("Debug Graph"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "GraphEditor.EventGraph_16x"));
InTabManager->RegisterTabSpawner(PropertiesTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Properties))
.SetDisplayName(VOXEL_LOCTEXT("Details"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details"));
InTabManager->RegisterTabSpawner(ShortcutsTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Shortcuts))
.SetDisplayName(VOXEL_LOCTEXT("Shortcuts"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details"));
InTabManager->RegisterTabSpawner(PreviewSettingsTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_PreviewSettings))
.SetDisplayName(VOXEL_LOCTEXT("Preview Settings"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details"));
InTabManager->RegisterTabSpawner(PaletteTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Palette))
.SetDisplayName(VOXEL_LOCTEXT("Palette"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "Kismet.Tabs.Palette"));
InTabManager->RegisterTabSpawner(PreviewTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Preview))
.SetDisplayName(VOXEL_LOCTEXT("Preview"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports"));
InTabManager->RegisterTabSpawner(PreviewViewportTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_PreviewViewport))
.SetDisplayName(VOXEL_LOCTEXT("3D Preview"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports"));
InTabManager->RegisterTabSpawner(MessagesTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Messages))
.SetDisplayName(VOXEL_LOCTEXT("Messages"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "MessageLog.TabIcon"));
}
void FVoxelGraphEditorToolkit::UnregisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager)
{
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
InTabManager->UnregisterTabSpawner(GraphCanvasTabId);
InTabManager->UnregisterTabSpawner(DebugGraphCanvasTabId);
InTabManager->UnregisterTabSpawner(PropertiesTabId);
InTabManager->UnregisterTabSpawner(ShortcutsTabId);
InTabManager->UnregisterTabSpawner(PreviewSettingsTabId);
InTabManager->UnregisterTabSpawner(PaletteTabId);
InTabManager->UnregisterTabSpawner(PreviewTabId);
InTabManager->UnregisterTabSpawner(PreviewViewportTabId);
InTabManager->UnregisterTabSpawner(MessagesTabId);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::InitVoxelEditor(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit)
{
FVoxelMessages::Info("You can view and edit Voxel Graphs, but running them requires Voxel Plugin Pro");
Generator = CastChecked<UVoxelGraphGenerator>(ObjectToEdit);
if (!ensureAlways(Generator->VoxelGraph && Generator->VoxelDebugGraph))
{
FAssetEditorToolkit::InitAssetEditor(
Mode,
InitToolkitHost,
TEXT("VoxelGraphEditorApp"),
FTabManager::NewLayout("Standalone_VoxelGraphEditor_Crash")->AddArea(FTabManager::NewPrimaryArea()),
false,
false,
ObjectToEdit,
false);
return;
}
// Support undo/redo
Generator->SetFlags(RF_Transactional);
GEditor->RegisterForUndo(this);
FGraphEditorCommands::Register();
FVoxelGraphEditorCommands::Register();
BindGraphCommands();
CreateInternalWidgets();
const TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_VoxelGraphEditor_Layout_v8")
->AddArea
(
FTabManager::NewPrimaryArea() ->SetOrientation(Orient_Vertical)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.1f)
->SetHideTabWell( true )
->AddTab(GetToolbarTabId(), ETabState::OpenedTab)
)
->Split
(
FTabManager::NewSplitter() ->SetOrientation(Orient_Horizontal) ->SetSizeCoefficient(0.9f)
->Split
(
FTabManager::NewSplitter() ->SetOrientation(Orient_Vertical) ->SetSizeCoefficient(0.2f)
->Split
(
FTabManager::NewStack()
->AddTab( PaletteTabId, ETabState::ClosedTab )
->AddTab( PreviewSettingsTabId, ETabState::OpenedTab)
)
->Split
(
FTabManager::NewStack()
->AddTab( ShortcutsTabId, ETabState::OpenedTab )
->AddTab( PropertiesTabId, ETabState::OpenedTab )
)
)
->Split
(
FTabManager::NewSplitter() ->SetOrientation( Orient_Vertical )
->SetSizeCoefficient(0.7f)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.8f)
->SetHideTabWell( true )
->AddTab( GraphCanvasTabId, ETabState::OpenedTab )
->AddTab( DebugGraphCanvasTabId, ETabState::ClosedTab )
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.2f)
->AddTab( MessagesTabId, ETabState::OpenedTab )
)
)
->Split
(
FTabManager::NewSplitter() ->SetOrientation(Orient_Vertical)
->SetSizeCoefficient(0.3f)
->Split
(
FTabManager::NewStack()
->SetHideTabWell( true )
->AddTab( PreviewTabId, ETabState::OpenedTab )
)
->Split
(
FTabManager::NewStack()
->SetHideTabWell( true )
->AddTab( PreviewViewportTabId, ETabState::OpenedTab )
)
)
)
);
const bool bCreateDefaultStandaloneMenu = true;
const bool bCreateDefaultToolbar = true;
FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, TEXT("VoxelGraphEditorApp"), StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit, false);
ExtendToolbar();
ExtendMenu();
RegenerateMenusAndToolbars();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::CreateInternalWidgets()
{
VoxelGraphEditor = CreateGraphEditorWidget(false);
VoxelDebugGraphEditor = CreateGraphEditorWidget(true);
FDetailsViewArgs Args;
Args.bHideSelectionTip = true;
Args.NotifyHook = this;
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
VoxelProperties = PropertyModule.CreateDetailView(Args);
VoxelProperties->SetObject(Generator);
ShortcutsProperties = PropertyModule.CreateDetailView(Args);
ShortcutsProperties->SetObject(GetMutableDefault<UVoxelGraphShortcuts>());
if (!Generator->PreviewSettings)
{
Generator->PreviewSettings = NewObject<UVoxelGraphPreviewSettings>(Generator);
Generator->PreviewSettings->Graph = Generator;
}
// Needed for undo/redo
Generator->PreviewSettings->SetFlags(RF_Transactional);
PreviewSettings = PropertyModule.CreateDetailView(Args);
PreviewSettings->SetObject(Generator->PreviewSettings);
// Must be created before PreviewViewport
PreviewScene = MakeShareable(new FAdvancedPreviewScene(FPreviewScene::ConstructionValues()));
Palette = SNew(SVoxelPalette);
Preview = SNew(SVoxelGraphPreview).PreviewSettings(Generator->PreviewSettings);
PreviewViewport = SNew(SVoxelGraphPreviewViewport).VoxelGraphEditorToolkit(SharedThis(this));
PreviewHandler = MakeShared<FVoxelGraphPreview>(Generator, Preview, PreviewViewport, PreviewScene);
Preview->SetTexture(Generator->GetPreviewTexture());
// Messages panel
FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");
FMessageLogInitializationOptions LogOptions;
LogOptions.bShowPages = false;
LogOptions.bShowFilters = true;
LogOptions.bAllowClear = false;
LogOptions.MaxPageCount = 1;
MessagesListing = MessageLogModule.CreateLogListing("VoxelGraphEditorErrors", LogOptions);
MessagesWidget = MessageLogModule.CreateLogListingWidget(MessagesListing.ToSharedRef());
}
void FVoxelGraphEditorToolkit::FillToolbar(FToolBarBuilder& ToolbarBuilder)
{
ToolbarBuilder.BeginSection("Toolbar");
auto& Commands = FVoxelGraphEditorCommands::Get();
ToolbarBuilder.AddToolBarButton(Commands.CompileToCpp);
ToolbarBuilder.AddSeparator();
ToolbarBuilder.AddToolBarButton(Commands.ToggleAutomaticPreview);
ToolbarBuilder.AddToolBarButton(Commands.UpdatePreview);
ToolbarBuilder.AddSeparator();
ToolbarBuilder.AddToolBarButton(Commands.ClearNodesMessages);
ToolbarBuilder.AddSeparator();
ToolbarBuilder.AddToolBarButton(Commands.ShowAxisDependencies);
ToolbarBuilder.AddSeparator();
ToolbarBuilder.AddToolBarButton(Commands.ShowStats);
ToolbarBuilder.AddToolBarButton(Commands.ShowValues);
ToolbarBuilder.EndSection();
}
void FVoxelGraphEditorToolkit::ExtendToolbar()
{
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
ToolbarExtender->AddToolBarExtension(
"Asset",
EExtensionHook::After,
GetToolkitCommands(),
FToolBarExtensionDelegate::CreateRaw(this, &FVoxelGraphEditorToolkit::FillToolbar)
);
AddToolbarExtender(ToolbarExtender);
}
void FVoxelGraphEditorToolkit::FillVoxelMenu(FMenuBuilder& MenuBuilder)
{
auto& Commands = FVoxelGraphEditorCommands::Get();
MenuBuilder.AddMenuEntry(Commands.RecreateNodes);
}
void FVoxelGraphEditorToolkit::AddEditorMenus(FMenuBarBuilder& MenuBarBuilder)
{
MenuBarBuilder.AddPullDownMenu(
VOXEL_LOCTEXT("Voxel"),
VOXEL_LOCTEXT("Open the Voxel menu"),
FNewMenuDelegate::CreateRaw(this, &FVoxelGraphEditorToolkit::FillVoxelMenu),
"Voxel");
}
void FVoxelGraphEditorToolkit::ExtendMenu()
{
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
MenuExtender->AddMenuBarExtension(
"Edit",
EExtensionHook::After,
GetToolkitCommands(),
FMenuBarExtensionDelegate::CreateRaw(this, &FVoxelGraphEditorToolkit::AddEditorMenus));
AddMenuExtender(MenuExtender);
}
void FVoxelGraphEditorToolkit::BindGraphCommands()
{
auto& Commands = FVoxelGraphEditorCommands::Get();
ToolkitCommands->MapAction(
Commands.CompileToCpp,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CompileToCpp));
ToolkitCommands->MapAction(
Commands.RecreateNodes,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::RecreateNodes));
ToolkitCommands->MapAction(
Commands.ToggleAutomaticPreview,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ToggleAutomaticPreview),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FVoxelGraphEditorToolkit::IsToggleAutomaticPreviewChecked));
ToolkitCommands->MapAction(
Commands.UpdatePreview,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::UpdatePreview, EVoxelGraphPreviewFlags::UpdateAll | EVoxelGraphPreviewFlags::ManualPreview));
ToolkitCommands->MapAction(
Commands.UpdateVoxelWorlds,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::UpdateVoxelWorlds));
ToolkitCommands->MapAction(
Commands.ClearNodesMessages,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ClearNodesMessages));
ToolkitCommands->MapAction(
Commands.ShowAxisDependencies,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ShowAxisDependencies));
ToolkitCommands->MapAction(
Commands.ShowStats,
FExecuteAction::CreateWeakLambda(Generator, [=]()
{
Generator->PreviewSettings->bShowStats = !Generator->PreviewSettings->bShowStats;
UpdatePreview(EVoxelGraphPreviewFlags::UpdateTextures);
}),
FCanExecuteAction(),
FIsActionChecked::CreateWeakLambda(Generator, [=]() { return Generator->PreviewSettings->bShowStats; }));
ToolkitCommands->MapAction(
Commands.ShowValues,
FExecuteAction::CreateWeakLambda(Generator, [=]()
{
Generator->PreviewSettings->bShowValues = !Generator->PreviewSettings->bShowValues;
UpdatePreview(EVoxelGraphPreviewFlags::UpdateTextures);
}),
FCanExecuteAction(),
FIsActionChecked::CreateWeakLambda(Generator, [=]() { return Generator->PreviewSettings->bShowValues; }));
ToolkitCommands->MapAction(
FGenericCommands::Get().Undo,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::UndoGraphAction));
ToolkitCommands->MapAction(
FGenericCommands::Get().Redo,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::RedoGraphAction));
}
TSharedRef<SGraphEditor> FVoxelGraphEditorToolkit::CreateGraphEditorWidget(bool bDebug)
{
if (!GraphEditorCommands.IsValid())
{
GraphEditorCommands = MakeShareable(new FUICommandList);
GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().AddInput,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::AddInput),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanAddInput));
GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().DeleteInput,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::DeleteInput),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanDeleteInput));
GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().TogglePinPreview,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnTogglePinPreview));
GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().SplitPin,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnSplitPin));
GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().CombinePin,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnCombinePin));
// Graph Editor Commands
GraphEditorCommands->MapAction(FGraphEditorCommands::Get().CreateComment,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnCreateComment)
);
// Editing commands
GraphEditorCommands->MapAction(FGenericCommands::Get().SelectAll,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::SelectAllNodes),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanSelectAllNodes)
);
GraphEditorCommands->MapAction(FGenericCommands::Get().Delete,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::DeleteSelectedNodes),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanDeleteNodes)
);
GraphEditorCommands->MapAction(FGenericCommands::Get().Copy,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CopySelectedNodes),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanCopyNodes)
);
GraphEditorCommands->MapAction(FGenericCommands::Get().Cut,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CutSelectedNodes),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanCutNodes)
);
GraphEditorCommands->MapAction(FGenericCommands::Get().Paste,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::PasteNodes),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanPasteNodes)
);
GraphEditorCommands->MapAction(FGenericCommands::Get().Duplicate,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::DuplicateNodes),
FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanDuplicateNodes)
);
GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().SelectLocalVariableDeclaration,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnSelectLocalVariableDeclaration)
);
GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().SelectLocalVariableUsages,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnSelectLocalVariableUsages)
);
GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().ConvertRerouteToVariables,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnConvertRerouteToVariables)
);
GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().ConvertVariablesToReroute,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnConvertVariablesToReroute)
);
GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().ReconstructNode,
FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ReconstructNode)
);
// Alignment Commands
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesTop,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignTop )
);
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesMiddle,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignMiddle )
);
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesBottom,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignBottom )
);
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesLeft,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignLeft )
);
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesCenter,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignCenter )
);
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesRight,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignRight )
);
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().StraightenConnections,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnStraightenConnections )
);
// Distribution Commands
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().DistributeNodesHorizontally,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnDistributeNodesH )
);
GraphEditorCommands->MapAction( FGraphEditorCommands::Get().DistributeNodesVertically,
FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnDistributeNodesV )
);
}
if (bDebug)
{
FGraphAppearanceInfo AppearanceInfo;
AppearanceInfo.CornerText = VOXEL_LOCTEXT("VOXEL DEBUG");
return SNew(SGraphEditor)
.IsEditable(true)
.Appearance(AppearanceInfo)
.GraphToEdit(Generator->VoxelDebugGraph)
.AutoExpandActionMenu(false)
.ShowGraphStateOverlay(false);
}
else
{
FGraphAppearanceInfo AppearanceInfo;
AppearanceInfo.CornerText = VOXEL_LOCTEXT("VOXEL");
SGraphEditor::FGraphEditorEvents InEvents;
InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FVoxelGraphEditorToolkit::OnSelectedNodesChanged);
InEvents.OnTextCommitted = FOnNodeTextCommitted::CreateSP(this, &FVoxelGraphEditorToolkit::OnNodeTitleCommitted);
InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FVoxelGraphEditorToolkit::OnNodeDoubleClicked);
InEvents.OnSpawnNodeByShortcut = SGraphEditor::FOnSpawnNodeByShortcut::CreateSP(this, &FVoxelGraphEditorToolkit::OnSpawnGraphNodeByShortcut);
return SNew(SGraphEditor)
.AdditionalCommands(GraphEditorCommands)
.IsEditable(true)
.Appearance(AppearanceInfo)
.GraphToEdit(Generator->VoxelGraph)
.GraphEvents(InEvents)
.AutoExpandActionMenu(false)
.ShowGraphStateOverlay(false);
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool FVoxelGraphEditorToolkit::GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding)
{
return VoxelGraphEditor->GetBoundsForSelectedNodes(Rect, Padding);
}
int32 FVoxelGraphEditorToolkit::GetNumberOfSelectedNodes() const
{
return VoxelGraphEditor->GetSelectedNodes().Num();
}
FGraphPanelSelectionSet FVoxelGraphEditorToolkit::GetSelectedNodes() const
{
return VoxelGraphEditor->GetSelectedNodes();
}
void FVoxelGraphEditorToolkit::SelectNodesAndZoomToFit(const TArray<UEdGraphNode*>& Nodes)
{
if (Nodes.Num() > 0)
{
VoxelGraphEditor->ClearSelectionSet();
for (auto& Node : Nodes)
{
VoxelGraphEditor->SetNodeSelection(Node, true);
}
VoxelGraphEditor->ZoomToFit(true);
}
}
void FVoxelGraphEditorToolkit::RefreshNodesMessages()
{
for (auto Node : Generator->VoxelGraph->Nodes)
{
if (Node->IsA<UVoxelGraphNode>() && !Node->IsA<UVoxelGraphNode_Knot>())
{
TSharedPtr<SGraphNode> Widget = Node->DEPRECATED_NodeWidget.Pin();
if (Widget.IsValid())
{
static_cast<SVoxelGraphNode*>(Widget.Get())->RefreshErrorInfo();
}
}
}
}
void FVoxelGraphEditorToolkit::TriggerUpdatePreview(EVoxelGraphPreviewFlags Flags)
{
if (Generator->bAutomaticPreview || EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::ManualPreview))
{
bUpdatePreviewOnNextTick = true;
NextPreviewFlags |= Flags;
}
}
FAdvancedPreviewScene* FVoxelGraphEditorToolkit::GetPreviewScene() const
{
return PreviewScene.Get();
}
void FVoxelGraphEditorToolkit::DebugNodes(const TSet<FVoxelCompilationNode*>& Nodes)
{
}
inline EMessageSeverity::Type VoxelMessageTypeToMessageSeverity(EVoxelGraphNodeMessageType Type)
{
switch (Type)
{
default: ensure(false);
case EVoxelGraphNodeMessageType::Info:
return EMessageSeverity::Info;
case EVoxelGraphNodeMessageType::Warning:
return EMessageSeverity::Warning;
case EVoxelGraphNodeMessageType::Error:
return EMessageSeverity::Error;
}
}
void FVoxelGraphEditorToolkit::AddMessages(const TArray<FVoxelGraphMessage>& Messages)
{
CurrentMessages.Append(Messages);
TArray<TSharedRef<FTokenizedMessage>> ListingMessages;
for (auto& Message : Messages)
{
TSharedRef<FTokenizedMessage> ListingMessage = FTokenizedMessage::Create(VoxelMessageTypeToMessageSeverity(Message.Type));
if (Message.Node.IsValid())
{
ListingMessage->AddToken(FActionToken::Create(
Message.Node->GetTitle(),
Message.Node->GetTitle(),
FOnActionTokenExecuted::CreateSP(
this,
&FVoxelGraphEditorToolkit::SelectNodeAndZoomToFit,
Message.Node)
));
}
ListingMessage->AddToken(FTextToken::Create(FText::FromString(Message.Message)));
ListingMessages.Add(ListingMessage);
}
MessagesListing->AddMessages(ListingMessages, false);
}
void FVoxelGraphEditorToolkit::ClearMessages(bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear)
{
MessagesListing->ClearMessages();
if (bClearAll)
{
CurrentMessages.Reset();
}
else
{
TArray<FVoxelGraphMessage> Copy = CurrentMessages;
Copy.RemoveAll([&](auto& Message) { return Message.Type == MessagesToClear; });
CurrentMessages.Reset();
AddMessages(Copy);
}
}
void FVoxelGraphEditorToolkit::SaveAsset_Execute()
{
if (Generator->bCompileToCppOnSave)
{
}
// Make sure to save AFTER compile to cpp to avoid dirtying it again
FAssetEditorToolkit::SaveAsset_Execute();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::AddReferencedObjects(FReferenceCollector& Collector)
{
Collector.AddReferencedObject(Generator);
if (PreviewHandler.IsValid())
{
PreviewHandler->AddReferencedObjects(Collector);
}
}
void FVoxelGraphEditorToolkit::PostUndo(bool bSuccess)
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->ClearSelectionSet();
VoxelGraphEditor->NotifyGraphChanged();
}
TriggerUpdatePreview(EVoxelGraphPreviewFlags::UpdateAll);
}
void FVoxelGraphEditorToolkit::NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged)
{
if (VoxelGraphEditor.IsValid() && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive)
{
VoxelGraphEditor->NotifyGraphChanged();
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::Tick(float DeltaTime)
{
if (bUpdatePreviewOnNextTick)
{
UpdatePreview(NextPreviewFlags);
bUpdatePreviewOnNextTick = false;
NextPreviewFlags = EVoxelGraphPreviewFlags::None;
}
}
TStatId FVoxelGraphEditorToolkit::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FVoxelGraphEditorToolkit, STATGROUP_Tickables);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_GraphCanvas(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == GraphCanvasTabId);
auto Tab = SNew(SDockTab)
.Label(VOXEL_LOCTEXT("Main Graph"));
GraphTab = Tab;
GraphTab->SetContent(VoxelGraphEditor.ToSharedRef());
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_DebugGraphCanvas(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == DebugGraphCanvasTabId);
auto Tab = SNew(SDockTab)
.Label(VOXEL_LOCTEXT("Debug Graph"));
DebugGraphTab = Tab;
DebugGraphTab->SetContent(VoxelDebugGraphEditor.ToSharedRef());
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_Properties(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == PropertiesTabId);
auto Tab =
SNew(SDockTab)
.Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details"))
.Label(VOXEL_LOCTEXT("Details"))
[
VoxelProperties.ToSharedRef()
];
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_Shortcuts(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == ShortcutsTabId);
auto Tab =
SNew(SDockTab)
.Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details"))
.Label(VOXEL_LOCTEXT("Shortcuts"))
[
ShortcutsProperties.ToSharedRef()
];
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_PreviewSettings(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == PreviewSettingsTabId);
auto Tab =
SNew(SDockTab)
.Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details"))
.Label(VOXEL_LOCTEXT("Preview Settings"))
[
PreviewSettings.ToSharedRef()
];
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_Palette(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == PaletteTabId);
auto Tab =
SNew(SDockTab)
.Icon(FEditorStyle::GetBrush("Kismet.Tabs.Palette"))
.Label(VOXEL_LOCTEXT("Palette"))
[
Palette.ToSharedRef()
];
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_Preview(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == PreviewTabId);
auto Tab =
SNew(SDockTab)
.Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Viewports"))
.Label(VOXEL_LOCTEXT("Preview"))
[
// Do the scaling here to make math easier
SNew(SScaleBox)
.Stretch(EStretch::ScaleToFit)
[
Preview.ToSharedRef()
]
];
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_PreviewViewport(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == PreviewViewportTabId);
auto Tab =
SNew(SDockTab)
.Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Viewports"))
.Label(VOXEL_LOCTEXT("3D Preview"))
[
PreviewViewport.ToSharedRef()
];
return Tab;
}
TSharedRef<SDockTab> FVoxelGraphEditorToolkit::SpawnTab_Messages(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == MessagesTabId);
auto Tab =
SNew(SDockTab)
.Icon(FEditorStyle::GetBrush("MessageLog.TabIcon"))
.Label(VOXEL_LOCTEXT("Messages"))
[
MessagesWidget.ToSharedRef()
];
return Tab;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::OnSelectedNodesChanged(const TSet<class UObject*>& NewSelection)
{
TArray<UObject*> Selection;
if (NewSelection.Num())
{
for (auto* Object : NewSelection)
{
if (Cast<UVoxelGraphNode_Root>(Object) || Cast<UVoxelGraphMacroInputOutputNode>(Object))
{
Selection.Add(Generator);
}
else if (UVoxelGraphNode* GraphNode = Cast<UVoxelGraphNode>(Object))
{
Selection.Add(GraphNode->VoxelNode);
}
else
{
Selection.Add(Object);
}
}
}
else
{
Selection.Add(Generator);
}
VoxelProperties->SetObjects(Selection);
}
void FVoxelGraphEditorToolkit::OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged)
{
if (NodeBeingChanged)
{
const FScopedTransaction Transaction(VOXEL_LOCTEXT("Rename Node"));
NodeBeingChanged->Modify();
NodeBeingChanged->OnRenameNode(NewText.ToString());
}
}
void FVoxelGraphEditorToolkit::OnNodeDoubleClicked(UEdGraphNode* Node)
{
if (Node->CanJumpToDefinition())
{
Node->JumpToDefinition();
}
}
FReply FVoxelGraphEditorToolkit::OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition)
{
auto* Ptr = GetDefault<UVoxelGraphShortcuts>()->Shortcuts.FindByPredicate([&](auto& Key) { return Key.IsSameAs(InChord); });
UClass* ClassToSpawn = Ptr ? Ptr->Class : nullptr;
if (ClassToSpawn)
{
FVoxelGraphSchemaAction_NewNode Action(FText(), FText(), FText(), 0);
Action.VoxelNodeClass = ClassToSpawn;
Action.PerformAction(Generator->VoxelGraph, nullptr, InPosition);
}
return FReply::Handled();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::AddInput()
{
const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();
// Iterator used but should only contain one node
for (auto* SelectedNode : SelectedNodes)
{
if (auto* Node = Cast<UVoxelGraphNode>(SelectedNode))
{
Node->AddInputPin();
break;
}
}
}
bool FVoxelGraphEditorToolkit::CanAddInput() const
{
return GetSelectedNodes().Num() == 1;
}
void FVoxelGraphEditorToolkit::DeleteInput()
{
UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu();
UVoxelGraphNode* SelectedNode = Cast<UVoxelGraphNode>(SelectedPin->GetOwningNode());
if (SelectedNode && SelectedNode == SelectedPin->GetOwningNode())
{
SelectedNode->RemoveInputPin(SelectedPin);
}
}
bool FVoxelGraphEditorToolkit::CanDeleteInput() const
{
return true;
}
void FVoxelGraphEditorToolkit::OnCreateComment()
{
FVoxelGraphSchemaAction_NewComment CommentAction;
CommentAction.PerformAction(Generator->VoxelGraph, NULL, VoxelGraphEditor->GetPasteLocation());
}
void FVoxelGraphEditorToolkit::OnTogglePinPreview()
{
UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu();
UVoxelGraphNode* SelectedNode = Cast<UVoxelGraphNode>(SelectedPin->GetOwningNode());
UVoxelGraphNode* GraphNodeToPreview = Cast<UVoxelGraphNode>(SelectedNode);
if (GraphNodeToPreview && GraphNodeToPreview->VoxelNode)
{
const bool bIsPreviewing = SelectedPin->bIsDiffing;
if (Generator->PreviewedPin.Get())
{
ensure(!bIsPreviewing || SelectedPin == Generator->PreviewedPin.Get());
ensure(Generator->PreviewedPin.Get()->bIsDiffing);
Generator->PreviewedPin.Get()->bIsDiffing = false;
Generator->PreviewedPin.SetPin(nullptr);
}
ensure(!SelectedPin->bIsDiffing);
if (!bIsPreviewing)
{
SelectedPin->bIsDiffing = true;
Generator->PreviewedPin.SetPin(SelectedPin);
}
VoxelGraphEditor->NotifyGraphChanged();
}
UpdatePreview(EVoxelGraphPreviewFlags::UpdateAll | EVoxelGraphPreviewFlags::ManualPreview);
}
void FVoxelGraphEditorToolkit::OnSplitPin()
{
UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu();
UVoxelGraphNode* SelectedNode = Cast<UVoxelGraphNode>(SelectedPin->GetOwningNode());
SelectedNode->TrySplitPin(*SelectedPin, false);
}
void FVoxelGraphEditorToolkit::OnCombinePin()
{
UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu();
UVoxelGraphNode* SelectedNode = Cast<UVoxelGraphNode>(SelectedPin->GetOwningNode());
SelectedNode->TryCombinePin(*SelectedPin, false);
}
void FVoxelGraphEditorToolkit::SelectAllNodes()
{
VoxelGraphEditor->SelectAllNodes();
}
void FVoxelGraphEditorToolkit::DeleteSelectedNodes()
{
const FScopedTransaction Transaction(VOXEL_LOCTEXT("Delete Selected Voxel Node"));
VoxelGraphEditor->GetCurrentGraph()->Modify();
const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();
VoxelGraphEditor->ClearSelectionSet();
for (auto* Object : SelectedNodes)
{
UEdGraphNode* Node = CastChecked<UEdGraphNode>(Object);
if (Node->CanUserDeleteNode())
{
if (UVoxelGraphNode* VoxelGraphNode = Cast<UVoxelGraphNode>(Node))
{
UVoxelNode* VoxelNode = VoxelGraphNode->VoxelNode;
if (VoxelNode)
{
VoxelNode->Modify();
VoxelNode->MarkPendingKill();
}
auto* PreviewedPin = Generator->PreviewedPin.Get();
if (PreviewedPin && PreviewedPin->GetOwningNode() == VoxelGraphNode)
{
// Clear previewed pin if we delete the owning node
Generator->PreviewedPin = {};
// Clear since we're not previewing it anymore
PreviewedPin->bIsDiffing = false;
}
FBlueprintEditorUtils::RemoveNode(NULL, VoxelGraphNode, true);
// Make sure Voxel is updated to match graph
Generator->CompileVoxelNodesFromGraphNodes();
// Remove this node from the list of all VoxelNodes
Generator->AllNodes.Remove(VoxelNode);
Generator->MarkPackageDirty();
}
else
{
FBlueprintEditorUtils::RemoveNode(NULL, Node, true);
}
}
}
}
bool FVoxelGraphEditorToolkit::CanDeleteNodes() const
{
const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();
if (SelectedNodes.Num() == 1)
{
for (auto* Node : SelectedNodes)
{
UVoxelGraphNode* GraphNode = Cast<UVoxelGraphNode>(Node);
if (GraphNode && !GraphNode->CanUserDeleteNode())
{
return false;
}
}
}
return SelectedNodes.Num() > 0;
}
void FVoxelGraphEditorToolkit::DeleteSelectedDuplicatableNodes()
{
// Cache off the old selection
const FGraphPanelSelectionSet OldSelectedNodes = GetSelectedNodes();
// Clear the selection and only select the nodes that can be duplicated
FGraphPanelSelectionSet RemainingNodes;
VoxelGraphEditor->ClearSelectionSet();
for (auto* SelectedNode : OldSelectedNodes)
{
UEdGraphNode* Node = Cast<UEdGraphNode>(SelectedNode);
if (Node && Node->CanDuplicateNode())
{
VoxelGraphEditor->SetNodeSelection(Node, true);
}
else
{
RemainingNodes.Add(Node);
}
}
// Delete the duplicable nodes
DeleteSelectedNodes();
// Reselect whatever's left from the original selection after the deletion
VoxelGraphEditor->ClearSelectionSet();
for (auto* RemainingNode : RemainingNodes)
{
if (UEdGraphNode* Node = Cast<UEdGraphNode>(RemainingNode))
{
VoxelGraphEditor->SetNodeSelection(Node, true);
}
}
}
void FVoxelGraphEditorToolkit::CutSelectedNodes()
{
CopySelectedNodes();
// Cut should only delete nodes that can be duplicated
DeleteSelectedDuplicatableNodes();
}
bool FVoxelGraphEditorToolkit::CanCutNodes() const
{
return CanCopyNodes() && CanDeleteNodes();
}
void FVoxelGraphEditorToolkit::CopySelectedNodes()
{
// Export the selected nodes and place the text on the clipboard
FGraphPanelSelectionSet SelectedNodes;
{
FGraphPanelSelectionSet AllSelectedNodes = GetSelectedNodes();
for (auto* SelectedNode : AllSelectedNodes)
{
auto* Node = Cast<UEdGraphNode>(SelectedNode);
if (Node && Node->CanDuplicateNode())
{
SelectedNodes.Add(Node);
}
}
}
FString ExportedText;
for (auto It = SelectedNodes.CreateIterator(); It; ++It)
{
CastChecked<UEdGraphNode>(*It)->PrepareForCopying();
}
FEdGraphUtilities::ExportNodesToText(SelectedNodes, /*out*/ ExportedText);
FPlatformApplicationMisc::ClipboardCopy(*ExportedText);
// Make sure the voxel graph remains the owner of the copied nodes
for (auto It = SelectedNodes.CreateIterator(); It; ++It)
{
if (auto* Node = Cast<UVoxelGraphNode>(*It))
{
Node->PostCopyNode();
}
}
}
bool FVoxelGraphEditorToolkit::CanCopyNodes() const
{
// If any of the nodes can be duplicated then we should allow copying
const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();
for (auto* SelectedNode : SelectedNodes)
{
UEdGraphNode* Node = Cast<UEdGraphNode>(SelectedNode);
if (Node && Node->CanDuplicateNode())
{
return true;
}
}
return false;
}
void FVoxelGraphEditorToolkit::PasteNodes()
{
PasteNodesHere(VoxelGraphEditor->GetPasteLocation());
}
void FVoxelGraphEditorToolkit::PasteNodesHere(const FVector2D& Location)
{
// Undo/Redo support
const FScopedTransaction Transaction(VOXEL_LOCTEXT("Paste Voxel Node"));
Generator->VoxelGraph->Modify();
Generator->Modify();
// Clear the selection set (newly pasted stuff will be selected)
VoxelGraphEditor->ClearSelectionSet();
// Grab the text to paste from the clipboard.
FString TextToImport;
FPlatformApplicationMisc::ClipboardPaste(TextToImport);
// Import the nodes
TSet<UEdGraphNode*> PastedNodes;
FEdGraphUtilities::ImportNodesFromText(Generator->VoxelGraph, TextToImport, /*out*/ PastedNodes);
//Average position of nodes so we can move them while still maintaining relative distances to each other
FVector2D AvgNodePosition(0.0f, 0.0f);
for (auto* Node : PastedNodes)
{
AvgNodePosition.X += Node->NodePosX;
AvgNodePosition.Y += Node->NodePosY;
}
if (PastedNodes.Num() > 0)
{
float InvNumNodes = 1.0f / float(PastedNodes.Num());
AvgNodePosition.X *= InvNumNodes;
AvgNodePosition.Y *= InvNumNodes;
}
TArray<UVoxelNode*> PastedVoxelNodes;
for (auto* Node : PastedNodes)
{
if (UVoxelGraphNode* VoxelGraphNode = Cast<UVoxelGraphNode>(Node))
{
if (UVoxelNode* VoxelNode = VoxelGraphNode->VoxelNode)
{
PastedVoxelNodes.Add(VoxelNode);
Generator->AllNodes.Add(VoxelNode);
VoxelNode->Graph = Generator;
}
}
// Select the newly pasted stuff
VoxelGraphEditor->SetNodeSelection(Node, true);
Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X;
Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y;
Node->SnapToGrid(SNodePanel::GetSnapGridSize());
// Give new node a different Guid from the old one
Node->CreateNewGuid();
}
// Force new pasted VoxelNodes to have same connections as graph nodes
Generator->CompileVoxelNodesFromGraphNodes();
// Post copy for local variables
for (auto* Node : PastedVoxelNodes)
{
Node->PostCopyNode(PastedVoxelNodes);
}
// Update UI
VoxelGraphEditor->NotifyGraphChanged();
Generator->PostEditChange();
Generator->MarkPackageDirty();
}
bool FVoxelGraphEditorToolkit::CanPasteNodes() const
{
FString ClipboardContent;
FPlatformApplicationMisc::ClipboardPaste(ClipboardContent);
return FEdGraphUtilities::CanImportNodesFromText(Generator->VoxelGraph, ClipboardContent);
}
void FVoxelGraphEditorToolkit::DuplicateNodes()
{
// Copy and paste current selection
CopySelectedNodes();
PasteNodes();
}
bool FVoxelGraphEditorToolkit::CanDuplicateNodes() const
{
return CanCopyNodes();
}
void FVoxelGraphEditorToolkit::OnAlignTop()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnAlignTop();
}
}
void FVoxelGraphEditorToolkit::OnAlignMiddle()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnAlignMiddle();
}
}
void FVoxelGraphEditorToolkit::OnAlignBottom()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnAlignBottom();
}
}
void FVoxelGraphEditorToolkit::OnAlignLeft()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnAlignLeft();
}
}
void FVoxelGraphEditorToolkit::OnAlignCenter()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnAlignCenter();
}
}
void FVoxelGraphEditorToolkit::OnAlignRight()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnAlignRight();
}
}
void FVoxelGraphEditorToolkit::OnStraightenConnections()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnStraightenConnections();
}
}
void FVoxelGraphEditorToolkit::OnDistributeNodesH()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnDistributeNodesH();
}
}
void FVoxelGraphEditorToolkit::OnDistributeNodesV()
{
if (VoxelGraphEditor.IsValid())
{
VoxelGraphEditor->OnDistributeNodesV();
}
}
void FVoxelGraphEditorToolkit::OnSelectLocalVariableDeclaration()
{
const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes();
if (SelectedNodes.Num() == 1)
{
VoxelGraphEditor->ClearSelectionSet();
for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
{
UVoxelGraphNode* GraphNode = Cast<UVoxelGraphNode>(*NodeIt);
if (GraphNode)
{
UVoxelNode* CurrentSelectedNoe = GraphNode->VoxelNode;
UVoxelLocalVariableUsage* Usage = Cast<UVoxelLocalVariableUsage>(CurrentSelectedNoe);
if (Usage && Usage->Declaration)
{
UEdGraphNode* DeclarationGraphNode = Usage->Declaration->GraphNode;
if (DeclarationGraphNode)
{
VoxelGraphEditor->SetNodeSelection(DeclarationGraphNode, true);
}
}
}
}
VoxelGraphEditor->ZoomToFit(true);
}
}
void FVoxelGraphEditorToolkit::OnSelectLocalVariableUsages()
{
const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes();
if (SelectedNodes.Num() == 1)
{
bool bZoom = false;
VoxelGraphEditor->ClearSelectionSet();
for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
{
UVoxelGraphNode* GraphNode = Cast<UVoxelGraphNode>(*NodeIt);
if (GraphNode)
{
UVoxelNode* CurrentSelectedNode = GraphNode->VoxelNode;
UVoxelLocalVariableDeclaration* Declaration = Cast<UVoxelLocalVariableDeclaration>(CurrentSelectedNode);
for (UVoxelNode* Node : Generator->AllNodes)
{
auto* Usage = Cast<UVoxelLocalVariableUsage>(Node);
if (Usage && Usage->Declaration == Declaration)
{
UEdGraphNode* UsageGraphNode = Usage->GraphNode;
if (UsageGraphNode)
{
bZoom = true;
VoxelGraphEditor->SetNodeSelection(UsageGraphNode, true);
}
}
}
}
}
if (bZoom)
{
VoxelGraphEditor->ZoomToFit(true);
}
}
}
void FVoxelGraphEditorToolkit::OnConvertRerouteToVariables()
{
const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes();
if (SelectedNodes.Num() == 1)
{
VoxelGraphEditor->ClearSelectionSet();
for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
{
UVoxelGraphNode_Knot* GraphNode = Cast<UVoxelGraphNode_Knot>(*NodeIt);
if (GraphNode)
{
UEdGraph* Graph = GraphNode->GetGraph();
const FScopedTransaction Transaction(VOXEL_LOCTEXT("Convert reroute to local variables"));
Graph->Modify();
const TArray<UEdGraphPin*>& InputPins = GraphNode->GetInputPin()->LinkedTo;
TArray<UEdGraphPin*> OutputPins = GraphNode->GetOutputPin()->LinkedTo;
OutputPins.Sort([](UEdGraphPin& A, UEdGraphPin& B) { return A.GetOwningNode()->NodePosY < B.GetOwningNode()->NodePosY; });
TArray<UVoxelLocalVariableUsage*> Usages;
int UsageIndex = -OutputPins.Num() / 2;
for (auto* OutputPin : OutputPins)
{
auto* Usage = Generator->ConstructNewNode<UVoxelLocalVariableUsage>(FVector2D(GraphNode->NodePosX + 50, GraphNode->NodePosY + 50 * UsageIndex));
Usages.Add(Usage);
UsageIndex++;
}
// Spawn declaration AFTER usages so that it gets renamed
auto* Declaration = Generator->ConstructNewNode<UVoxelLocalVariableDeclaration>(FVector2D(GraphNode->NodePosX - 50, GraphNode->NodePosY));
Declaration->SetCategory(FVoxelPinCategory::FromString(GraphNode->GetInputPin()->PinType.PinCategory));
Declaration->GraphNode->ReconstructNode();
check(Declaration->GraphNode->Pins.Num() == 1);
UEdGraphPin* DeclarationInputPin = Declaration->GraphNode->Pins[0];
check(DeclarationInputPin->Direction == EEdGraphPinDirection::EGPD_Input)
for (auto* InputPin : InputPins)
{
InputPin->MakeLinkTo(DeclarationInputPin);
}
for (int32 Index = 0; Index < OutputPins.Num() ; Index++)
{
auto* Usage = Usages[Index];
Usage->Declaration = Declaration;
Usage->DeclarationGuid = Declaration->VariableGuid;
Usage->GraphNode->ReconstructNode();
Usage->GraphNode->GetAllPins()[0]->MakeLinkTo(OutputPins[Index]); // usage node has a single pin
}
GraphNode->DestroyNode();
}
}
}
}
void FVoxelGraphEditorToolkit::OnConvertVariablesToReroute()
{
const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes();
if (SelectedNodes.Num() == 1)
{
for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
{
UVoxelGraphNode* GraphNode = Cast<UVoxelGraphNode>(*NodeIt);
if (GraphNode)
{
UEdGraph* Graph = GraphNode->GetGraph();
const FScopedTransaction Transaction(VOXEL_LOCTEXT("Convert local variables to reroute"));
Graph->Modify();
UVoxelNode* CurrentSelectedNode = GraphNode->VoxelNode;
UVoxelLocalVariableDeclaration* Declaration = Cast<UVoxelLocalVariableDeclaration>(CurrentSelectedNode);
if (!Declaration)
{
UVoxelLocalVariableUsage* Usage = Cast<UVoxelLocalVariableUsage>(CurrentSelectedNode);
if (Usage)
{
Declaration = Usage->Declaration;
}
}
if (!Declaration)
{
return;
}
UEdGraphNode* DeclarationGraphNode = Declaration->GraphNode;
FGraphNodeCreator<UVoxelGraphNode_Knot> KnotNodeCreator(*Graph);
UVoxelGraphNode_Knot* KnotNode = KnotNodeCreator.CreateNode();
KnotNodeCreator.Finalize();
KnotNode->NodePosX = DeclarationGraphNode->NodePosX + 50;
KnotNode->NodePosY = DeclarationGraphNode->NodePosY;
for (UEdGraphPin* Pin : DeclarationGraphNode->GetAllPins())
{
if (Pin->Direction == EEdGraphPinDirection::EGPD_Input)
{
for (UEdGraphPin* InputPin : Pin->LinkedTo)
{
KnotNode->GetInputPin()->MakeLinkTo(InputPin);
}
}
if (Pin->Direction == EEdGraphPinDirection::EGPD_Output)
{
for (UEdGraphPin* OutputPin : Pin->LinkedTo)
{
KnotNode->GetOutputPin()->MakeLinkTo(OutputPin);
}
}
}
DeclarationGraphNode->DestroyNode();
for(UVoxelNode* Node : Generator->AllNodes)
{
auto* Usage = Cast<UVoxelLocalVariableUsage>(Node);
if (Usage && Usage->Declaration == Declaration)
{
UEdGraphNode* UsageGraphNode = Usage->GraphNode;
if (UsageGraphNode)
{
UEdGraphPin* Pin = Usage->GraphNode->GetAllPins()[0]; // usage node has a single pin
for (UEdGraphPin* OutputPin : Pin->LinkedTo)
{
KnotNode->GetOutputPin()->MakeLinkTo(OutputPin);
}
UsageGraphNode->DestroyNode();
}
}
}
KnotNode->PropagatePinType();
}
}
}
}
void FVoxelGraphEditorToolkit::ReconstructNode()
{
const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes();
for(auto& Object : SelectedNodes)
{
if (auto* Node = Cast<UVoxelGraphNode>(Object))
{
Node->ReconstructNode();
}
}
Generator->CompileVoxelNodesFromGraphNodes();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::RecreateNodes()
{
for (int32 I = 0; I < 4; I++) // Hack to make sure they are really recreated
{
TArray<UVoxelGraphNode*> AllNodes;
VoxelGraphEditor->GetCurrentGraph()->GetNodesOfClass<UVoxelGraphNode>(AllNodes);
for (auto* Node : AllNodes)
{
Node->ReconstructNode();
}
Generator->CompileVoxelNodesFromGraphNodes();
GraphTab->ClearContent();
VoxelGraphEditor = CreateGraphEditorWidget(false);
GraphTab->SetContent(VoxelGraphEditor.ToSharedRef());
}
ClearNodesMessages();
}
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::CompileToCpp()
{
FVoxelMessages::Info("Compiling graphs to C++ requires Voxel Plugin Pro");
}
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::ToggleAutomaticPreview()
{
Generator->Modify();
Generator->bAutomaticPreview = !Generator->bAutomaticPreview;
}
bool FVoxelGraphEditorToolkit::IsToggleAutomaticPreviewChecked() const
{
return Generator->bAutomaticPreview;
}
void FVoxelGraphEditorToolkit::UpdatePreview(EVoxelGraphPreviewFlags Flags)
{
PreviewHandler->Update(Flags);
if (EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::ManualPreview))
{
FVoxelMessages::Info("You can view and edit Voxel Graphs, but running and previewing them requires Voxel Plugin Pro");
}
}
void FVoxelGraphEditorToolkit::UpdateVoxelWorlds()
{
IVoxelEditorModule* VoxelEditorModule = &FModuleManager::LoadModuleChecked<IVoxelEditorModule>("VoxelEditor");
VoxelEditorModule->RefreshVoxelWorlds(Generator);
}
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::ClearNodesMessages()
{
FVoxelGraphErrorReporter::ClearNodesMessages(Generator);
ClearMessages(true, EVoxelGraphNodeMessageType::Info);
}
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::ShowAxisDependencies()
{
FVoxelGraphErrorReporter::ClearNodesMessages(Generator);
}
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::UndoGraphAction()
{
GEditor->UndoTransaction();
}
void FVoxelGraphEditorToolkit::RedoGraphAction()
{
// Clear selection, to avoid holding refs to nodes that go away
VoxelGraphEditor->ClearSelectionSet();
GEditor->RedoTransaction();
}
///////////////////////////////////////////////////////////////////////////////
void FVoxelGraphEditorToolkit::SelectNodeAndZoomToFit(TWeakObjectPtr<const UVoxelNode> Node)
{
if (Node.IsValid() && Node->Graph)
{
if (Node->Graph == Generator)
{
SelectNodesAndZoomToFit({ Node->GraphNode });
}
else
{
if (ensure(GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(Node->Graph)))
{
auto NewEditor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Node->Graph->VoxelGraph);
if (ensure(NewEditor.IsValid()))
{
NewEditor->SelectNodesAndZoomToFit({ Node->GraphNode });
}
}
}
}
}