735 lines
No EOL
22 KiB
C++
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;
|
|
} |