CelticCraft/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorToolsPanel.cpp

735 lines
No EOL
22 KiB
C++

// Copyright 2020 Phyronnaz
#include "VoxelEditorToolsPanel.h"
#include "VoxelTools/VoxelToolManager.h"
#include "VoxelTools/Tools/VoxelTool.h"
#include "VoxelTools/Tools/VoxelFlattenTool.h"
#include "VoxelTools/Tools/VoxelLevelTool.h"
#include "VoxelTools/Tools/VoxelMeshTool.h"
#include "VoxelTools/Tools/VoxelRevertTool.h"
#include "VoxelTools/Tools/VoxelSmoothTool.h"
#include "VoxelTools/Tools/VoxelSphereTool.h"
#include "VoxelTools/Tools/VoxelSurfaceTool.h"
#include "VoxelTools/Tools/VoxelTrimTool.h"
#include "VoxelWorld.h"
#include "VoxelToolsCommands.h"
#include "VoxelDelegateHelpers.h"
#include "VoxelScopedTransaction.h"
#include "ActorFactoryVoxelWorld.h"
#include "VoxelUtilities/VoxelConfigUtilities.h"
#include "EngineUtils.h"
#include "Editor.h"
#include "EditorViewportClient.h"
#include "DetailLayoutBuilder.h"
#include "VariablePrecisionNumericInterface.h"
#include "Modules/ModuleManager.h"
#include "PropertyEditorModule.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/MessageDialog.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/SSpinBox.h"
#include "Slate/SceneViewport.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Framework/Application/SlateApplication.h"
#include "HAL/PlatformApplicationMisc.h"
static const FString ToolConfigSectionName = "VoxelTool";
FVoxelEditorToolsPanel::FVoxelEditorToolsPanel()
: Widget(SNew(SBox))
{
}
FVoxelEditorToolsPanel::~FVoxelEditorToolsPanel()
{
if (ToolManager)
{
// Always save before exiting the panel (eg shortcuts do not dirty saves)
FVoxelConfigUtilities::SaveConfig(&ToolManager->GetSharedConfig(), ToolConfigSectionName);
if (auto* Tool = ToolManager->GetActiveTool())
{
FVoxelConfigUtilities::SaveConfig(Tool, ToolConfigSectionName);
}
ToolManager->SetActiveTool(nullptr);
}
}
void FVoxelEditorToolsPanel::Init(const TSharedPtr<FUICommandList>& CommandListOverride)
{
CommandList = CommandListOverride;
if (!CommandList.IsValid())
{
CommandList = MakeShared<FUICommandList>();
}
FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
FDetailsViewArgs DetailsViewArgs(false, false, false, FDetailsViewArgs::HideNameArea);
DetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Automatic;
ToolManager = NewObject<UVoxelToolManager>(GetTransientPackage(), NAME_None, RF_Transient | RF_Transactional);
ToolManager->CreateDefaultTools(true);
ToolManager->GetSharedConfig().RefreshDetails.AddSP(this, &FVoxelEditorToolsPanel::RefreshDetails);
ToolManager->GetSharedConfig().RegisterTransaction.AddLambda([](FName Name, AVoxelWorld* VoxelWorld)
{
FVoxelScopedTransaction Transaction(VoxelWorld, Name, EVoxelChangeType::Edit);
});
ToolsOptions.Reset();
for (auto* Tool : ToolManager->GetTools())
{
FVoxelConfigUtilities::LoadConfig(Tool, ToolConfigSectionName);
ToolsOptions.Add(MakeSharedCopy(Tool->GetClass()));
}
FVoxelConfigUtilities::LoadConfig(&ToolManager->GetSharedConfig(), ToolConfigSectionName);
const auto IsPropertyVisibleDelegate = MakeWeakPtrDelegate(this, [=](const FPropertyAndParent& PropertyAndParent)
{
const auto& ParentProperties = PropertyAndParent.ParentProperties;
return IsPropertyVisible(PropertyAndParent.Property, ParentProperties);
});
SharedConfigDetailsPanel = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
SharedConfigDetailsPanel->SetObject(&ToolManager->GetSharedConfig());
SharedConfigDetailsPanel->SetIsPropertyVisibleDelegate(IsPropertyVisibleDelegate);
SharedConfigDetailsPanel->OnFinishedChangingProperties().AddWeakLambda(ToolManager, [=](auto&)
{
FVoxelConfigUtilities::SaveConfig(&ToolManager->GetSharedConfig(), ToolConfigSectionName);
});
ToolDetailsPanel = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
ToolDetailsPanel->SetObject(ToolManager->GetActiveTool());
ToolDetailsPanel->SetIsPropertyVisibleDelegate(IsPropertyVisibleDelegate);
ToolDetailsPanel->OnFinishedChangingProperties().AddWeakLambda(ToolManager, [=](auto&)
{
if (auto* Tool = ToolManager->GetActiveTool())
{
FVoxelConfigUtilities::SaveConfig(Tool, ToolConfigSectionName);
}
});
TSharedPtr<SVerticalBox> ToolBarsVerticalBox;
TSharedPtr<SVerticalBox> CustomToolBarsVerticalBox;
Widget->SetContent(
SNew(SScrollBox)
+ SScrollBox::Slot()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.Padding(FMargin(3, 5))
.AutoHeight()
[
SNew(SBorder)
.Visibility(TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FVoxelEditorToolsPanel::GetAddVoxelWorldVisibility)))
.BorderBackgroundColor(FLinearColor::Red)
.BorderImage(FCoreStyle::Get().GetBrush("ErrorReporting.Box"))
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Padding(FMargin(3, 0))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(STextBlock)
.ColorAndOpacity(FLinearColor::White)
.Font(FCoreStyle::GetDefaultFontStyle("Regular",12))
.Text(VOXEL_LOCTEXT("No Voxel World in the scene!"))
]
+ SVerticalBox::Slot()
.Padding(FMargin(0, 5))
[
SNew(SButton)
.OnClicked(FOnClicked::CreateSP(this, &FVoxelEditorToolsPanel::AddVoxelWorld))
[
SNew(STextBlock)
.ColorAndOpacity(FLinearColor::Black)
.Font(FCoreStyle::GetDefaultFontStyle("Regular",12))
.Text(VOXEL_LOCTEXT("Add Voxel World"))
.Justification(ETextJustify::Center)
]
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(2)
[
SAssignNew(ToolBarsVerticalBox, SVerticalBox)
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Center)
[
SNew(SButton)
.Text(VOXEL_LOCTEXT("Browse to tool BP"))
.Visibility_Lambda([=]()
{
if (ToolManager->GetActiveTool() && !ToolManager->GetActiveTool()->GetClass()->HasAnyClassFlags(CLASS_Native))
{
return EVisibility::Visible;
}
else
{
return EVisibility::Collapsed;
}
})
.OnClicked_Lambda([=]()
{
TArray<UObject*> Objects = { ToolManager->GetActiveTool()->GetClass() };
GEditor->SyncBrowserToObjects(Objects);
return FReply::Handled();
})
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SharedConfigDetailsPanel.ToSharedRef()
]
+ SVerticalBox::Slot()
.FillHeight(1)
[
ToolDetailsPanel.ToSharedRef()
]
]);
TArray<FToolBarBuilder> ToolBarBuilders;
TArray<FToolBarBuilder> CustomToolBarBuilders;
BuildToolBars(ToolBarBuilders, CustomToolBarBuilders);
BindCommands();
for (auto& ToolBarBuilder : ToolBarBuilders)
{
ToolBarsVerticalBox->AddSlot()
.AutoHeight()
.HAlign(HAlign_Center)
[
ToolBarBuilder.MakeWidget()
];
}
ToolBarsVerticalBox->AddSlot()
.AutoHeight()
[
SAssignNew(CustomToolBarsVerticalBox, SVerticalBox)
.Visibility_Lambda([=]()
{
return bShowCustomTools ? EVisibility::Visible : EVisibility::Collapsed;
})
];
GConfig->GetBool(TEXT("VoxelEditorToolsPanel"), TEXT("ShowCustomTools"), bShowCustomTools, GEditorPerProjectIni);
ToolBarsVerticalBox->AddSlot()
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("DetailsView.AdvancedDropdownBorder"))
.Padding(FMargin(0.0f, 3.0f, 16.f, 0.0f))
[
SAssignNew(ExpanderButton, SButton)
.ButtonStyle(FAppStyle::Get(), "NoBorder")
.HAlign(HAlign_Center)
.ContentPadding(2)
.OnClicked_Lambda([=]()
{
bShowCustomTools = !bShowCustomTools;
GConfig->SetBool(TEXT("VoxelEditorToolsPanel"), TEXT("ShowCustomTools"), bShowCustomTools, GEditorPerProjectIni);
return FReply::Handled();
})
.ToolTipText_Lambda([=]()
{
return bShowCustomTools ? VOXEL_LOCTEXT("Hide custom tools") : VOXEL_LOCTEXT("Show custom tools");
})
[
SNew(SImage)
.Image_Lambda([=]()
{
if (ExpanderButton->IsHovered())
{
return bShowCustomTools ? FAppStyle::GetBrush("DetailsView.PulldownArrow.Up.Hovered") : FAppStyle::GetBrush("DetailsView.PulldownArrow.Down.Hovered");
}
else
{
return bShowCustomTools ? FAppStyle::GetBrush("DetailsView.PulldownArrow.Up") : FAppStyle::GetBrush("DetailsView.PulldownArrow.Down");
}
})
]
]
];
CustomToolBarsVerticalBox->AddSlot()
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryMiddle"))
.Padding(FMargin(0.0f, 3.0f, 16.f, 0.0f))
[
SNew(SImage)
.Image(FAppStyle::GetBrush("DetailsView.AdvancedDropdownBorder.Open"))
]
];
for (auto& ToolBarBuilder : CustomToolBarBuilders)
{
CustomToolBarsVerticalBox->AddSlot()
.AutoHeight()
.HAlign(HAlign_Center)
[
ToolBarBuilder.MakeWidget()
];
}
FString ActiveTool;
GConfig->GetString(TEXT("VoxelEditorToolsPanel"), TEXT("ActiveTool"), ActiveTool, GEditorPerProjectIni);
SetActiveTool(*ToolsOptions[0]);
for (auto& It : ToolsOptions)
{
if ((**It).GetName() == ActiveTool)
{
SetActiveTool(*It);
}
}
// Show tooltip dialog
bool bShowToolsShortcutsDialog = true;
if (!GConfig->GetBool( TEXT("VoxelEditorToolsPanel"), TEXT("ShowToolsShortcutsDialog"), bShowToolsShortcutsDialog, GEditorPerProjectIni) || bShowToolsShortcutsDialog)
{
const auto Result = FMessageDialog::Open(EAppMsgType::YesNo, VOXEL_LOCTEXT(
"The voxel plugin tools shortcuts are the following:\n"
"1-8 (alphanum): select tools\n"
"[ ]: decrease/increase brush size\n"
"< >: decrease/increase brush strength\n\n"
"You can configure these shortcuts at any time in Edit/Editor Preferences/Keyboard Shortcuts/Voxel\n\n"
"Continue to show this popup?"));
if (Result == EAppReturnType::No)
{
GConfig->SetBool( TEXT("VoxelEditorToolsPanel"), TEXT("ShowToolsShortcutsDialog"), false, GEditorPerProjectIni);
}
}
}
void FVoxelEditorToolsPanel::CustomizeToolbar(FToolBarBuilder& ToolBarBuilder)
{
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelEditorToolsPanel::AddReferencedObjects(FReferenceCollector& Collector)
{
Collector.AddReferencedObject(ToolManager);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelEditorToolsPanel::MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY)
{
}
void FVoxelEditorToolsPanel::Tick(FEditorViewportClient* ViewportClient, float DeltaTime)
{
auto* World = ViewportClient->GetWorld();
if (!ensure(World)) return;
if (!LastWorld.IsValid())
{
// Toggle voxel worlds if none are created on first tick
const auto ToggleVoxelWorld = [&]()
{
for (auto* VoxelWorld : TActorRange<AVoxelWorld>(World))
{
if (VoxelWorld->IsCreated())
{
return;
}
}
for (auto* VoxelWorld : TActorRange<AVoxelWorld>(World))
{
if (!VoxelWorld->IsCreated())
{
VoxelWorld->Toggle();
}
}
};
ToggleVoxelWorld();
}
LastWorld = ViewportClient->GetWorld();
if (ToolManager)
{
auto* Tool = ToolManager->GetActiveTool();
if (Tool)
{
check(!ViewportClientForDeproject);
TGuardValue<FEditorViewportClient*> Guard(ViewportClientForDeproject, ViewportClient);
FViewport* Viewport = ViewportClient->Viewport;
TUniquePtr<FSceneViewFamilyContext> ViewFamily;
FSceneView* SceneView = GetSceneView(ViewFamily);
if (Viewport && SceneView)
{
TMap<FName, bool> Keys;
Keys.Add(FVoxelToolKeys::AlternativeMode,
ViewportClient->Viewport->KeyState(EKeys::LeftShift) ||
ViewportClient->Viewport->KeyState(EKeys::RightShift));
const bool bClick = ViewportClient->Viewport->KeyState(EKeys::LeftMouseButton);
FVoxelToolTickData TickData;
{
auto* SceneViewport = static_cast<FSceneViewport*>(Viewport);
const auto& Geometry = SceneViewport->GetCachedGeometry();
FVector2D MousePosition = FSlateApplication::Get().GetCursorPos();
if (Geometry.IsUnderLocation(MousePosition) || bClick) // Don't modify the position if editing
{
MousePosition = Geometry.AbsoluteToLocal(MousePosition);
}
else
{
MousePosition = Geometry.Size / 2;
}
// Make sure to use the window scale factor and not the scale under the cursor,
// as the window DPI scale is uniform
MousePosition *= ViewportClient->GetDPIScale();
TickData.MousePosition = FVector2D(MousePosition);
TickData.CameraViewDirection = SceneView->ViewMatrices.GetInvViewMatrix().TransformVector(FVector(0, 0, 1));
TickData.bEdit = bClick;
TickData.Keys = Keys;
TickData.Axes.Add(FVoxelToolAxes::BrushSize, BrushSizeDelta);
TickData.Axes.Add(FVoxelToolAxes::Falloff, FalloffDelta);
TickData.Axes.Add(FVoxelToolAxes::Strength, StrengthDelta);
BrushSizeDelta = 0;
FalloffDelta = 0;
StrengthDelta = 0;
const auto DeprojectLambda = [this, WeakPtr = MakeWeakPtr(this)](const FVector2D& InScreenPosition, FVector& OutWorldPosition, FVector& OutWorldDirection)
{
return WeakPtr.IsValid() && Deproject(InScreenPosition, OutWorldPosition, OutWorldDirection);
};
TickData.Init(DeprojectLambda);
}
Tool->AdvancedTick(World, TickData);
if (LastVoxelWorld != Tool->GetVoxelWorld())
{
LastVoxelWorld = Tool->GetVoxelWorld();
SharedConfigDetailsPanel->ForceRefresh();
ToolDetailsPanel->ForceRefresh();
}
ViewportClientForDeproject = nullptr;
}
}
}
TimeUntilNextGC -= DeltaTime;
if (TimeUntilNextGC <= 0)
{
TimeUntilNextGC = 30;
LOG_VOXEL(Log, TEXT("Forcing GC"));
GEngine->ForceGarbageCollection(true);
}
}
void FVoxelEditorToolsPanel::HandleClick(FEditorViewportClient* ViewportClient, HHitProxy* HitProxy, const FViewportClick& Click)
{
}
bool FVoxelEditorToolsPanel::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event)
{
if (Event != IE_Released && CommandList->ProcessCommandBindings(Key, FSlateApplication::Get().GetModifierKeys(), false/*Event == IE_Repeat*/))
{
return true;
}
if (Key == EKeys::LeftMouseButton)
{
return true;
}
else if (Key == EKeys::LeftShift || Key == EKeys::RightShift)
{
return true;
}
else
{
return false;
}
}
bool FVoxelEditorToolsPanel::InputAxis(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, float Delta, float DeltaTime)
{
return false;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
EVisibility FVoxelEditorToolsPanel::GetAddVoxelWorldVisibility() const
{
if (!LastWorld.IsValid())
{
return EVisibility::Collapsed;
}
for (auto* VoxelWorld : TActorRange<AVoxelWorld>(LastWorld.Get()))
{
return EVisibility::Collapsed;
}
return EVisibility::Visible;
}
FReply FVoxelEditorToolsPanel::AddVoxelWorld() const
{
if (!LastWorld.IsValid())
{
return FReply::Handled();
}
auto* Factory = NewObject<UActorFactoryVoxelWorld>();
if (ensure(Factory))
{
Factory->CreateActor(nullptr, LastWorld->GetLevel(0), FTransform::Identity);
}
return FReply::Handled();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelEditorToolsPanel::RefreshDetails() const
{
SharedConfigDetailsPanel->ForceRefresh();
ToolDetailsPanel->ForceRefresh();
}
bool FVoxelEditorToolsPanel::IsPropertyVisible(const FProperty& Property, const TArray<const FProperty*>& ParentProperties, int32 ParentPropertyIndex) const
{
if (Property.HasMetaData(STATIC_FNAME("HideInPanel")))
{
return false;
}
if (Property.HasMetaData(STATIC_FNAME("PaintMaterial")))
{
auto* ActiveTool = ToolManager->GetActiveTool();
if (ActiveTool && !ActiveTool->bShowPaintMaterial)
{
return false;
}
}
if (Property.HasMetaData(STATIC_FNAME("ShowForMaterialConfigs")))
{
const FString& ShowForMaterialConfigs = Property.GetMetaData(STATIC_FNAME("ShowForMaterialConfigs"));
if (LastVoxelWorld.IsValid())
{
const FString MaterialConfig = StaticEnum<EVoxelMaterialConfig>()->GetNameStringByValue(int64(LastVoxelWorld->MaterialConfig));
if (!ShowForMaterialConfigs.Contains(MaterialConfig))
{
return false;
}
}
}
// TODO
if (ParentProperties.IsValidIndex(ParentPropertyIndex))
{
return IsPropertyVisible(*ParentProperties[ParentPropertyIndex], ParentProperties, ParentPropertyIndex + 1);
}
else
{
return true;
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void FVoxelEditorToolsPanel::SetActiveTool(UClass* ToolClass)
{
ToolManager->SetActiveToolByClass(ToolClass);
ToolDetailsPanel->SetObject(ToolManager->GetActiveTool());
SharedConfigDetailsPanel->ForceRefresh();
if (ComboBox)
{
ComboBox->SetSelectedItem(*ToolsOptions.FindByPredicate([&](auto& ClassPtr) { return *ClassPtr == ToolClass; }));
}
GConfig->SetString(TEXT("VoxelEditorToolsPanel"), TEXT("ActiveTool"), ToolClass ? *ToolClass->GetName() : TEXT(""), GEditorPerProjectIni);
}
bool FVoxelEditorToolsPanel::IsToolActive(UClass* ToolClass) const
{
auto* Tool = ToolManager->GetActiveTool();
return Tool && Tool->GetClass() == ToolClass;
}
void FVoxelEditorToolsPanel::BuildToolBars(TArray<FToolBarBuilder>& OutToolBars, TArray<FToolBarBuilder>& OutCustomToolBars)
{
const auto& Commands = FVoxelToolsCommands::Get();
int32 NumTools = 0;
const auto GetToolBar = [&]()
{
if (NumTools % 4 == 0)
{
OutToolBars.Emplace(CommandList, FMultiBoxCustomization("VoxelEditorTools"));
}
NumTools++;
return OutToolBars.Last();
};
const auto GetUIAction = [&](auto* Class)
{
return FUIAction(
FExecuteAction::CreateSP(this, &FVoxelEditorToolsPanel::SetActiveTool, Class),
FCanExecuteAction::CreateLambda([]() { return true; }),
FIsActionChecked::CreateSP(this, &FVoxelEditorToolsPanel::IsToolActive, Class));
};
const auto AddTool = [&](auto& Command, auto* Class)
{
CommandList->MapAction(Command, GetUIAction(Class));
};
AddTool(Commands.FlattenTool, UVoxelFlattenTool::StaticClass());
AddTool(Commands.LevelTool, UVoxelLevelTool::StaticClass());
AddTool(Commands.MeshTool, UVoxelMeshTool::StaticClass());
AddTool(Commands.SmoothTool, UVoxelSmoothTool::StaticClass());
AddTool(Commands.SphereTool, UVoxelSphereTool::StaticClass());
AddTool(Commands.SurfaceTool, UVoxelSurfaceTool::StaticClass());
AddTool(Commands.RevertTool, UVoxelRevertTool::StaticClass());
AddTool(Commands.TrimTool, UVoxelTrimTool::StaticClass());
GetToolBar().AddToolBarButton(Commands.SurfaceTool);
GetToolBar().AddToolBarButton(Commands.SmoothTool);
GetToolBar().AddToolBarButton(Commands.MeshTool);
GetToolBar().AddToolBarButton(Commands.SphereTool);
GetToolBar().AddToolBarButton(Commands.FlattenTool);
GetToolBar().AddToolBarButton(Commands.LevelTool);
GetToolBar().AddToolBarButton(Commands.TrimTool);
GetToolBar().AddToolBarButton(Commands.RevertTool);
int32 NumCustomTools = 0;
const auto GetCustomToolBar = [&]()
{
if (NumCustomTools % 4 == 0)
{
OutCustomToolBars.Emplace(CommandList, FMultiBoxCustomization("VoxelEditorTools"));
}
NumCustomTools++;
return OutCustomToolBars.Last();
};
for (auto* Tool : ToolManager->GetTools())
{
auto* Class = Tool->GetClass();
if (Class->GetPathName().StartsWith(TEXT("/Script/Voxel.")))
{
// Builtin tools
continue;
}
GetCustomToolBar().AddToolBarButton(
GetUIAction(Class),
NAME_None,
FText::FromName(Tool->GetToolName()),
Tool->ToolTip,
FSlateIcon("VoxelStyle", "VoxelTools.Surface"),
EUserInterfaceActionType::ToggleButton);
}
}
void FVoxelEditorToolsPanel::BindCommands()
{
const auto& Commands = FVoxelToolsCommands::Get();
CommandList->MapAction(Commands.IncreaseBrushSize, MakeWeakPtrDelegate(this, [&](){ BrushSizeDelta++; }));
CommandList->MapAction(Commands.DecreaseBrushSize, MakeWeakPtrDelegate(this, [&](){ BrushSizeDelta--; }));
CommandList->MapAction(Commands.IncreaseBrushFalloff, MakeWeakPtrDelegate(this, [&](){ FalloffDelta++; }));
CommandList->MapAction(Commands.DecreaseBrushFalloff, MakeWeakPtrDelegate(this, [&](){ FalloffDelta--; }));
CommandList->MapAction(Commands.IncreaseBrushStrength, MakeWeakPtrDelegate(this, [&](){ StrengthDelta++; }));
CommandList->MapAction(Commands.DecreaseBrushStrength, MakeWeakPtrDelegate(this, [&](){ StrengthDelta--; }));
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
FSceneView* FVoxelEditorToolsPanel::GetSceneView(TUniquePtr<FSceneViewFamilyContext>& ViewFamily) const
{
if (!ensure(ViewportClientForDeproject))
{
return nullptr;
}
FViewport* Viewport = ViewportClientForDeproject->Viewport;
// Make sure we have a valid viewport, otherwise we won't be able to construct an FSceneView
if (!Viewport || Viewport->GetSizeXY().GetMin() <= 0)
{
return nullptr;
}
ViewFamily = MakeUnique<FSceneViewFamilyContext>(FSceneViewFamily::ConstructionValues(
Viewport,
ViewportClientForDeproject->GetScene(),
ViewportClientForDeproject->EngineShowFlags)
.SetRealtimeUpdate(ViewportClientForDeproject->IsRealtime()));
FSceneView* SceneView = ViewportClientForDeproject->CalcSceneView(ViewFamily.Get());
ensure(SceneView);
return SceneView;
}
bool FVoxelEditorToolsPanel::Deproject(const FVector2D& ScreenPosition, FVector& OutWorldPosition, FVector& OutWorldDirection) const
{
TUniquePtr<FSceneViewFamilyContext> ViewFamily;
auto* SceneView = GetSceneView(ViewFamily);
if (!SceneView)
{
return false;
}
const FViewportCursorLocation MouseViewportRay(SceneView, ViewportClientForDeproject, FMath::RoundToInt(ScreenPosition.X), FMath::RoundToInt(ScreenPosition.Y));
OutWorldPosition = MouseViewportRay.GetOrigin();
OutWorldDirection = MouseViewportRay.GetDirection();
// If we're dealing with an orthographic view, push the origin of the ray backward along the viewport forward axis
// to make sure that we can select objects that are behind the origin!
if (!ViewportClientForDeproject->IsPerspective())
{
OutWorldPosition -= OutWorldDirection * WORLD_MAX / 2;
}
return true;
}