Ajout du projet Depths sur Git
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright Benoit Pelletier 2025 All Rights Reserved.
|
||||
//
|
||||
// This software is available under different licenses depending on the source from which it was obtained:
|
||||
// - The Fab EULA (https://fab.com/eula) applies when obtained from the Fab marketplace.
|
||||
// - The CeCILL-C license (https://cecill.info/licences/Licence_CeCILL-C_V1-en.html) applies when obtained from any other source.
|
||||
// Please refer to the accompanying LICENSE file for further details.
|
||||
|
||||
#include "ProceduralDungeonEditorTool.h"
|
||||
#include "EditorMode/ProceduralDungeonEdMode.h"
|
||||
#include "RoomLevel.h"
|
||||
#include "RoomData.h"
|
||||
|
||||
URoomData* FProceduralDungeonEditorTool::GetRoomData() const
|
||||
{
|
||||
auto Level = EdMode->GetLevel();
|
||||
if (!Level.IsValid())
|
||||
return nullptr;
|
||||
|
||||
return Level.Get()->Data;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright Benoit Pelletier 2023 - 2025 All Rights Reserved.
|
||||
//
|
||||
// This software is available under different licenses depending on the source from which it was obtained:
|
||||
// - The Fab EULA (https://fab.com/eula) applies when obtained from the Fab marketplace.
|
||||
// - The CeCILL-C license (https://cecill.info/licences/Licence_CeCILL-C_V1-en.html) applies when obtained from any other source.
|
||||
// Please refer to the accompanying LICENSE file for further details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "UObject/GCObject.h"
|
||||
#include "EdMode.h"
|
||||
#include "EditorUndoClient.h"
|
||||
#include "ProceduralDungeonEdTypes.h"
|
||||
|
||||
class FProceduralDungeonEdMode;
|
||||
|
||||
class FProceduralDungeonEditorTool : public FGCObject, public FSelfRegisteringEditorUndoClient
|
||||
{
|
||||
public:
|
||||
FProceduralDungeonEditorTool(FProceduralDungeonEdMode* InEdMode)
|
||||
: EdMode(InEdMode)
|
||||
{
|
||||
}
|
||||
|
||||
virtual const TCHAR* GetToolName() = 0;
|
||||
virtual FText GetDisplayName() = 0;
|
||||
virtual FText GetDisplayMessage() = 0;
|
||||
|
||||
virtual void EnterTool() = 0;
|
||||
virtual void ExitTool() = 0;
|
||||
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) {}
|
||||
virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) {}
|
||||
virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY) { return false; }
|
||||
virtual bool HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) { return false; }
|
||||
virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent) { return false; }
|
||||
virtual bool InputAxis(FEditorViewportClient* InViewportClient, FViewport* Viewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime) { return false; }
|
||||
virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) { return false; }
|
||||
virtual bool UsesTransformWidget() const { return false; }
|
||||
virtual bool UsesTransformWidget(WidgetMode CheckMode) const { return false; }
|
||||
virtual FVector GetWidgetLocation() const { return FVector::ZeroVector; }
|
||||
virtual bool GetCursor(EMouseCursor::Type& OutCursor) const { return false; }
|
||||
|
||||
//~ Begin FGCObject Interface
|
||||
virtual void AddReferencedObjects(FReferenceCollector& Collector) override {}
|
||||
virtual FString GetReferencerName() const override
|
||||
{
|
||||
return TEXT("FProceduralDungeonEditorTool");
|
||||
}
|
||||
//~ End FGCObject Interface
|
||||
|
||||
virtual void OnLevelChanged(const class ARoomLevel* NewLevel) {}
|
||||
virtual void OnDataChanged(const class URoomData* NewData) {}
|
||||
virtual void OnDataPropertiesChanged(const class URoomData* Data) {}
|
||||
|
||||
class URoomData* GetRoomData() const;
|
||||
|
||||
protected:
|
||||
FProceduralDungeonEdMode* EdMode = nullptr;
|
||||
};
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
// Copyright Benoit Pelletier 2023 - 2025 All Rights Reserved.
|
||||
//
|
||||
// This software is available under different licenses depending on the source from which it was obtained:
|
||||
// - The Fab EULA (https://fab.com/eula) applies when obtained from the Fab marketplace.
|
||||
// - The CeCILL-C license (https://cecill.info/licences/Licence_CeCILL-C_V1-en.html) applies when obtained from any other source.
|
||||
// Please refer to the accompanying LICENSE file for further details.
|
||||
|
||||
#include "ProceduralDungeonEditorTool_Door.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "EditorMode/ProceduralDungeonEdMode.h"
|
||||
#include "EditorMode/ProceduralDungeonEditorObject.h"
|
||||
#include "ProceduralDungeonEdLog.h"
|
||||
#include "ProceduralDungeonUtils.h"
|
||||
#include "Room.h"
|
||||
#include "RoomLevel.h"
|
||||
#include "RoomData.h"
|
||||
#include "Door.h"
|
||||
|
||||
bool AreDoorsOverlapping(const FDoorDef& DoorA, const FDoorDef& DoorB, const FVector RoomUnit)
|
||||
{
|
||||
// If not in same direction, they will never overlap.
|
||||
if (DoorA.Direction != DoorB.Direction)
|
||||
return false;
|
||||
|
||||
// If same direction and position, then will always overlap.
|
||||
if (DoorA.Position == DoorB.Position)
|
||||
return true;
|
||||
|
||||
const FBoxCenterAndExtent DoorBoundsA = DoorA.GetBounds(RoomUnit);
|
||||
const FBoxCenterAndExtent DoorBoundsB = DoorB.GetBounds(RoomUnit);
|
||||
return Intersect(DoorBoundsA, DoorBoundsB);
|
||||
}
|
||||
|
||||
bool IsPositionInside(const FDoorDef& Door, const FIntVector& Position, const FVector RoomUnit)
|
||||
{
|
||||
// If same position, then always inside.
|
||||
if (Door.Position == Position)
|
||||
return true;
|
||||
|
||||
const FBoxCenterAndExtent DoorBounds = Door.GetBounds(RoomUnit);
|
||||
const FVector LocalDoorExtent = DoorBounds.Extent / RoomUnit;
|
||||
const FVector RelativePosition = (FVector(Position) - (DoorBounds.Center / RoomUnit)).GetAbs();
|
||||
return RelativePosition.X <= LocalDoorExtent.X && RelativePosition.Y <= LocalDoorExtent.Y && RelativePosition.Z <= LocalDoorExtent.Z;
|
||||
}
|
||||
|
||||
bool IsDoorValid(const URoomData* Data, const FDoorDef& Door)
|
||||
{
|
||||
check(IsValid(Data));
|
||||
for (const FDoorDef& DoorDef : Data->Doors)
|
||||
{
|
||||
if (AreDoorsOverlapping(Door, DoorDef, Data->GetRoomUnit()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::EnterTool()
|
||||
{
|
||||
DungeonEd_LogInfo("Enter Door Tool.");
|
||||
UpdateCollision();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::ExitTool()
|
||||
{
|
||||
DungeonEd_LogInfo("Exit Door Tool.");
|
||||
DestroyCollision();
|
||||
CachedLevel.Reset();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void RenderBoundingBox(FPrimitiveDrawInterface* PDI, const FBoxCenterAndExtent& Box, const FColor& Color)
|
||||
{
|
||||
const FVector Center = Box.Center;
|
||||
const FVector Extent = Box.Extent;
|
||||
const FVector A = Center + FVector(Extent.X, Extent.Y, Extent.Z);
|
||||
const FVector B = Center + FVector(Extent.X, Extent.Y, -Extent.Z);
|
||||
const FVector C = Center + FVector(Extent.X, -Extent.Y, Extent.Z);
|
||||
const FVector D = Center + FVector(Extent.X, -Extent.Y, -Extent.Z);
|
||||
const FVector E = Center + FVector(-Extent.X, Extent.Y, Extent.Z);
|
||||
const FVector F = Center + FVector(-Extent.X, Extent.Y, -Extent.Z);
|
||||
const FVector G = Center + FVector(-Extent.X, -Extent.Y, Extent.Z);
|
||||
const FVector H = Center + FVector(-Extent.X, -Extent.Y, -Extent.Z);
|
||||
PDI->DrawLine(A, B, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(A, C, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(A, E, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(B, D, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(B, F, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(C, D, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(C, G, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(D, H, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(E, F, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(E, G, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(F, H, Color, SDPG_Foreground);
|
||||
PDI->DrawLine(G, H, Color, SDPG_Foreground);
|
||||
}
|
||||
|
||||
void RenderInterlinesBoundingBox(FPrimitiveDrawInterface* PDI, const FBoxMinAndMax& Box, const FColor& Color, const FVector& RoomUnit)
|
||||
{
|
||||
FIntVector Min, Max;
|
||||
IntVector::MinMax(Box.GetMin(), Box.GetMax(), Min, Max);
|
||||
|
||||
// Vertical Lines on X
|
||||
for (int32 i = Min.X + 1; i < Max.X; ++i)
|
||||
{
|
||||
FIntVector BottomA(i, Min.Y, Min.Z);
|
||||
FIntVector BottomB(i, Max.Y, Min.Z);
|
||||
FIntVector TopA(i, Min.Y, Max.Z);
|
||||
FIntVector TopB(i, Max.Y, Max.Z);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(BottomA, RoomUnit), Dungeon::ToWorldLocation(TopA, RoomUnit), Color, SDPG_World);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(BottomB, RoomUnit), Dungeon::ToWorldLocation(TopB, RoomUnit), Color, SDPG_World);
|
||||
}
|
||||
|
||||
// Vertical Lines on Y
|
||||
for (int32 i = Min.Y + 1; i < Max.Y; ++i)
|
||||
{
|
||||
FIntVector BottomA(Min.X, i, Min.Z);
|
||||
FIntVector BottomB(Max.X, i, Min.Z);
|
||||
FIntVector TopA(Min.X, i, Max.Z);
|
||||
FIntVector TopB(Max.X, i, Max.Z);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(BottomA, RoomUnit), Dungeon::ToWorldLocation(TopA, RoomUnit), Color, SDPG_World);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(BottomB, RoomUnit), Dungeon::ToWorldLocation(TopB, RoomUnit), Color, SDPG_World);
|
||||
}
|
||||
|
||||
// Horizontal Lines on X and Y
|
||||
for (int32 i = Min.Z + 1; i < Max.Z; ++i)
|
||||
{
|
||||
FIntVector A(Min.X, Min.Y, i);
|
||||
FIntVector B(Min.X, Max.Y, i);
|
||||
FIntVector C(Max.X, Max.Y, i);
|
||||
FIntVector D(Max.X, Min.Y, i);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(A, RoomUnit), Dungeon::ToWorldLocation(B, RoomUnit), Color, SDPG_World);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(B, RoomUnit), Dungeon::ToWorldLocation(C, RoomUnit), Color, SDPG_World);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(C, RoomUnit), Dungeon::ToWorldLocation(D, RoomUnit), Color, SDPG_World);
|
||||
PDI->DrawLine(Dungeon::ToWorldLocation(D, RoomUnit), Dungeon::ToWorldLocation(A, RoomUnit), Color, SDPG_World);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI)
|
||||
{
|
||||
FProceduralDungeonEditorTool::Render(View, Viewport, PDI);
|
||||
|
||||
const URoomData* Data = GetRoomData();
|
||||
if (!IsValid(Data))
|
||||
return;
|
||||
|
||||
const FColor LineColor(100, 20, 0);
|
||||
const FVector RoomUnit = Data->GetRoomUnit();
|
||||
|
||||
for (const auto& BoundingBox : Data->BoundingBoxes)
|
||||
{
|
||||
RenderInterlinesBoundingBox(PDI, BoundingBox, LineColor, RoomUnit);
|
||||
}
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::Tick(FEditorViewportClient* ViewportClient, float DeltaTime)
|
||||
{
|
||||
FProceduralDungeonEditorTool::Tick(ViewportClient, DeltaTime);
|
||||
|
||||
if (ShowDoorPreview)
|
||||
{
|
||||
const URoomData* Data = GetRoomData();
|
||||
check(IsValid(Data));
|
||||
|
||||
UWorld* World = ViewportClient->GetWorld();
|
||||
FDoorDef::DrawDebug(World, DoorPreview, Data->GetRoomUnit(), FTransform::Identity, /*includeOffset = */ true, /*isConnected = */ IsDoorValid(Data, DoorPreview));
|
||||
}
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Door::HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click)
|
||||
{
|
||||
if (Click.IsAltDown() || Click.IsControlDown() || Click.IsShiftDown())
|
||||
return false;
|
||||
|
||||
if (!ShowDoorPreview)
|
||||
return false;
|
||||
|
||||
URoomData* Data = GetRoomData();
|
||||
check(IsValid(Data));
|
||||
|
||||
if (Click.GetKey() == EKeys::LeftMouseButton)
|
||||
{
|
||||
if (IsDoorValid(Data, DoorPreview))
|
||||
{
|
||||
GEditor->BeginTransaction(FText::FromString(TEXT("Add Door")));
|
||||
Data->Modify();
|
||||
Data->Doors.Add(DoorPreview);
|
||||
GEditor->EndTransaction();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Click.GetKey() == EKeys::RightMouseButton)
|
||||
{
|
||||
if (Data->Doors.Contains(DoorPreview))
|
||||
{
|
||||
GEditor->BeginTransaction(FText::FromString(TEXT("Remove Door")));
|
||||
Data->Modify();
|
||||
Data->Doors.Remove(DoorPreview);
|
||||
GEditor->EndTransaction();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Door::MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY)
|
||||
{
|
||||
ShowDoorPreview = false;
|
||||
|
||||
const URoomData* Data = GetRoomData();
|
||||
if (!IsValid(Data))
|
||||
return false;
|
||||
|
||||
FHitResult Hit;
|
||||
if (!RoomTraceFromMouse(Hit, ViewportClient))
|
||||
return false;
|
||||
|
||||
FIntVector RoomCell;
|
||||
EDoorDirection DoorDirection;
|
||||
if (!GetRoomCellFromHit(Hit, Data->GetRoomUnit(), RoomCell, DoorDirection))
|
||||
return false;
|
||||
|
||||
ShowDoorPreview = true;
|
||||
DoorPreview.Position = RoomCell;
|
||||
DoorPreview.Direction = DoorDirection;
|
||||
DoorPreview.Type = EdMode->Settings->DoorType;
|
||||
|
||||
// Snap preview to existing door if RoomCell is inside
|
||||
for (const FDoorDef& RoomDoor : Data->Doors)
|
||||
{
|
||||
if (RoomDoor.Direction != DoorPreview.Direction)
|
||||
continue;
|
||||
|
||||
if (IsPositionInside(RoomDoor, DoorPreview.Position, Data->GetRoomUnit()))
|
||||
{
|
||||
DoorPreview = RoomDoor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Door::GetCursor(EMouseCursor::Type& OutCursor) const
|
||||
{
|
||||
if (!ShowDoorPreview)
|
||||
return false;
|
||||
|
||||
const URoomData* Data = GetRoomData();
|
||||
if (!IsValid(Data))
|
||||
return false;
|
||||
|
||||
OutCursor = IsDoorValid(Data, DoorPreview) ? EMouseCursor::Hand : EMouseCursor::SlashedCircle;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::OnLevelChanged(const ARoomLevel* NewLevel)
|
||||
{
|
||||
UpdateCollision();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::OnDataChanged(const URoomData* NewData)
|
||||
{
|
||||
UpdateCollision();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::OnDataPropertiesChanged(const URoomData* Data)
|
||||
{
|
||||
UpdateCollision();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::CreateCollision(ARoomLevel* Level)
|
||||
{
|
||||
if (!IsValid(Level))
|
||||
return;
|
||||
|
||||
if (!IsValid(Level->Data))
|
||||
return;
|
||||
|
||||
check(IsValid(Level->GetWorld()));
|
||||
|
||||
const int32 NumBoxes = Level->Data->BoundingBoxes.Num();
|
||||
const int32 Delta = NumBoxes - RoomBoxes.Num();
|
||||
|
||||
// Create new boxes when delta is positive
|
||||
for (int i = 0; i < Delta; ++i)
|
||||
{
|
||||
FName BoxName = FName(*FString::Printf(TEXT("Editor Room Collision %d"), RoomBoxes.Num()));
|
||||
UBoxComponent* RoomBox = NewObject<UBoxComponent>(Level, BoxName, RF_Transient);
|
||||
check(IsValid(RoomBox));
|
||||
RoomBox->SetupAttachment(Level->GetRootComponent());
|
||||
RoomBox->RegisterComponent();
|
||||
RoomBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
||||
RoomBox->SetCollisionObjectType(ECollisionChannel::ECC_MAX);
|
||||
RoomBoxes.Add(RoomBox);
|
||||
DungeonEd_LogInfo("Created RoomBox: %s", *GetNameSafe(RoomBox));
|
||||
}
|
||||
|
||||
// Destroy boxes when delta is negative
|
||||
for (int i = 0; i > Delta; --i)
|
||||
{
|
||||
if (RoomBoxes.Num() == 0)
|
||||
break;
|
||||
TWeakObjectPtr<UBoxComponent> RoomBox = RoomBoxes.Pop();
|
||||
if (!RoomBox.IsValid())
|
||||
continue;
|
||||
RoomBox->DestroyComponent();
|
||||
DungeonEd_LogInfo("Destroyed RoomBox: %s", *GetNameSafe(RoomBox.Get()));
|
||||
}
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::UpdateCollision()
|
||||
{
|
||||
auto Level = EdMode->GetLevelInstance();
|
||||
if (Level != CachedLevel)
|
||||
{
|
||||
DestroyCollision();
|
||||
CachedLevel = Level;
|
||||
}
|
||||
|
||||
|
||||
if (!CachedLevel.IsValid())
|
||||
return;
|
||||
|
||||
URoomData* Data = CachedLevel->Data;
|
||||
if (!IsValid(Data))
|
||||
return;
|
||||
|
||||
CreateCollision(CachedLevel.Get());
|
||||
check(Data->BoundingBoxes.Num() == RoomBoxes.Num());
|
||||
|
||||
for (int i = 0; i < Data->BoundingBoxes.Num(); ++i)
|
||||
{
|
||||
const FBoxMinAndMax& DataBox = Data->BoundingBoxes[i];
|
||||
UBoxComponent* RoomBox = RoomBoxes[i].Get();
|
||||
|
||||
FBoxCenterAndExtent Box = Dungeon::ToWorld(DataBox, Data->GetRoomUnit());
|
||||
RoomBox->SetRelativeLocation(Box.Center);
|
||||
RoomBox->SetBoxExtent(Box.Extent);
|
||||
|
||||
DungeonEd_LogInfo("Updated RoomBox: %s", *GetNameSafe(RoomBox));
|
||||
}
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Door::DestroyCollision()
|
||||
{
|
||||
for (const auto& RoomBox : RoomBoxes)
|
||||
{
|
||||
if (!RoomBox.IsValid())
|
||||
continue;
|
||||
|
||||
RoomBox->DestroyComponent();
|
||||
}
|
||||
|
||||
RoomBoxes.Empty();
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Door::RoomTraceFromMouse(FHitResult& OutHit, FEditorViewportClient* ViewportClient) const
|
||||
{
|
||||
int32 MouseX = ViewportClient->Viewport->GetMouseX();
|
||||
int32 MouseY = ViewportClient->Viewport->GetMouseY();
|
||||
|
||||
// Compute a world space ray from the screen space mouse coordinates
|
||||
FSceneViewFamilyContext ViewFamily(
|
||||
FSceneViewFamilyContext::ConstructionValues(ViewportClient->Viewport, ViewportClient->GetScene(), ViewportClient->EngineShowFlags)
|
||||
.SetRealtimeUpdate(ViewportClient->IsRealtime()));
|
||||
|
||||
FSceneView* View = ViewportClient->CalcSceneView(&ViewFamily);
|
||||
FViewportCursorLocation MouseViewportRay(View, ViewportClient, MouseX, MouseY);
|
||||
FVector MouseViewportRayDirection = MouseViewportRay.GetDirection();
|
||||
|
||||
FVector Start = MouseViewportRay.GetOrigin();
|
||||
FVector End = Start + WORLD_MAX * MouseViewportRayDirection;
|
||||
if (ViewportClient->IsOrtho())
|
||||
{
|
||||
Start -= WORLD_MAX * MouseViewportRayDirection;
|
||||
}
|
||||
|
||||
return RoomTrace(OutHit, Start, End);
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Door::RoomTrace(FHitResult& OutHit, const FVector& RayOrigin, const FVector& RayEnd) const
|
||||
{
|
||||
OutHit = FHitResult();
|
||||
FHitResult Hit;
|
||||
bool bHasHit = false;
|
||||
for (const auto& RoomBox : RoomBoxes)
|
||||
{
|
||||
if (!RoomBox.IsValid())
|
||||
continue;
|
||||
|
||||
const bool bSuccess = RoomBox->LineTraceComponent(Hit, RayOrigin, RayEnd, FCollisionQueryParams(SCENE_QUERY_STAT(RoomTrace)));
|
||||
if (!bSuccess)
|
||||
continue;
|
||||
|
||||
if (!bHasHit || Hit.Distance < OutHit.Distance)
|
||||
{
|
||||
OutHit = Hit;
|
||||
bHasHit = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bHasHit;
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Door::GetRoomCellFromHit(const FHitResult& Hit, const FVector RoomUnit, FIntVector& OutCell, EDoorDirection& OutDirection) const
|
||||
{
|
||||
// Direction is up or down: invalid
|
||||
if (FMath::Abs(FVector::DotProduct(Hit.ImpactNormal, FVector::UpVector)) >= 0.5f)
|
||||
return false;
|
||||
|
||||
// Determine direction from the hit normal
|
||||
float DirX = FVector::DotProduct(Hit.ImpactNormal, FVector::ForwardVector);
|
||||
float DirY = FVector::DotProduct(Hit.ImpactNormal, FVector::RightVector);
|
||||
if (FMath::Abs(DirX) > FMath::Abs(DirY))
|
||||
OutDirection = (DirX > 0) ? EDoorDirection::North : EDoorDirection::South;
|
||||
else
|
||||
OutDirection = (DirY > 0) ? EDoorDirection::East : EDoorDirection::West;
|
||||
|
||||
// Determine the room cell
|
||||
FVector RoomSpacePoint = Hit.ImpactPoint / RoomUnit;
|
||||
RoomSpacePoint -= 0.5f * (ToVector(OutDirection) + FVector::UpVector);
|
||||
OutCell = FIntVector(FMath::RoundToInt(RoomSpacePoint.X), FMath::RoundToInt(RoomSpacePoint.Y), FMath::RoundToInt(RoomSpacePoint.Z));
|
||||
return true;
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// Copyright Benoit Pelletier 2023 - 2025 All Rights Reserved.
|
||||
//
|
||||
// This software is available under different licenses depending on the source from which it was obtained:
|
||||
// - The Fab EULA (https://fab.com/eula) applies when obtained from the Fab marketplace.
|
||||
// - The CeCILL-C license (https://cecill.info/licences/Licence_CeCILL-C_V1-en.html) applies when obtained from any other source.
|
||||
// Please refer to the accompanying LICENSE file for further details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProceduralDungeonEditorTool.h"
|
||||
|
||||
class ARoomLevel;
|
||||
|
||||
class FProceduralDungeonEditorTool_Door : public FProceduralDungeonEditorTool
|
||||
{
|
||||
public:
|
||||
FProceduralDungeonEditorTool_Door(FProceduralDungeonEdMode* InEdMode)
|
||||
: FProceduralDungeonEditorTool(InEdMode)
|
||||
{
|
||||
}
|
||||
|
||||
//~ Begin FProceduralDungeonEditorTool Interface
|
||||
virtual const TCHAR* GetToolName() override { return TEXT("Tool_Door"); }
|
||||
virtual FText GetDisplayName() override { return NSLOCTEXT("ProceduralDungeonEditor", "Tool_Door", "Door"); }
|
||||
virtual FText GetDisplayMessage() override { return NSLOCTEXT("ProceduralDungeonEditor", "Tool_Door_Tooltip", ""); }
|
||||
|
||||
virtual void EnterTool() override;
|
||||
virtual void ExitTool() override;
|
||||
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override;
|
||||
virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override;
|
||||
virtual bool HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) override;
|
||||
virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY) override;
|
||||
virtual bool GetCursor(EMouseCursor::Type& OutCursor) const override;
|
||||
|
||||
virtual void OnLevelChanged(const ARoomLevel* NewLevel) override;
|
||||
virtual void OnDataChanged(const URoomData* NewData) override;
|
||||
virtual void OnDataPropertiesChanged(const URoomData* Data) override;
|
||||
//~ End FProceduralDungeonEditorTool Interface
|
||||
|
||||
protected:
|
||||
void CreateCollision(ARoomLevel* level);
|
||||
void UpdateCollision();
|
||||
void DestroyCollision();
|
||||
bool RoomTraceFromMouse(FHitResult& OutHit, FEditorViewportClient* ViewportClient) const;
|
||||
bool RoomTrace(FHitResult& OutHit, const FVector& RayOrigin, const FVector& RayEnd) const;
|
||||
bool GetRoomCellFromHit(const FHitResult& Hit, const FVector RoomUnit, FIntVector& OutCell, EDoorDirection& OutDirection) const;
|
||||
|
||||
private:
|
||||
TWeakObjectPtr<ARoomLevel> CachedLevel = nullptr;
|
||||
TArray<TWeakObjectPtr<class UBoxComponent>> RoomBoxes;
|
||||
FDoorDef DoorPreview;
|
||||
bool ShowDoorPreview {false};
|
||||
};
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
// Copyright Benoit Pelletier 2023 - 2025 All Rights Reserved.
|
||||
//
|
||||
// This software is available under different licenses depending on the source from which it was obtained:
|
||||
// - The Fab EULA (https://fab.com/eula) applies when obtained from the Fab marketplace.
|
||||
// - The CeCILL-C license (https://cecill.info/licences/Licence_CeCILL-C_V1-en.html) applies when obtained from any other source.
|
||||
// Please refer to the accompanying LICENSE file for further details.
|
||||
|
||||
#include "ProceduralDungeonEditorTool_Size.h"
|
||||
#include "EditorMode/ProceduralDungeonEdMode.h"
|
||||
#include "EditorModeManager.h"
|
||||
#include "Engine/Selection.h"
|
||||
#include "ProceduralDungeonEdLog.h"
|
||||
#include "ProceduralDungeonUtils.h"
|
||||
#include "Room.h"
|
||||
#include "RoomLevel.h"
|
||||
#include "RoomData.h"
|
||||
|
||||
IMPLEMENT_HIT_PROXY(HRoomPointProxy, HHitProxy);
|
||||
|
||||
void FRoomPoint::AddLinkedPoint(FRoomPoint& Point, EAxisList::Type Axis)
|
||||
{
|
||||
LinkedPoints.Add({&Point, Axis});
|
||||
Point.LinkedPoints.Add({this, Axis});
|
||||
}
|
||||
|
||||
void FRoomPoint::SetLocation(FVector InLocation)
|
||||
{
|
||||
SetDirty();
|
||||
Location = InLocation;
|
||||
UpdateWithPropagation();
|
||||
}
|
||||
|
||||
void FRoomPoint::SetDirty()
|
||||
{
|
||||
if (bDirty)
|
||||
return;
|
||||
|
||||
bDirty = EAxisList::XYZ;
|
||||
for (FLink& Link : LinkedPoints)
|
||||
{
|
||||
check(Link.Point);
|
||||
Link.Point->SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void FRoomPoint::UpdateWithPropagation()
|
||||
{
|
||||
TQueue<FRoomPoint*> PendingNodes;
|
||||
PendingNodes.Enqueue(this);
|
||||
FRoomPoint* Node = nullptr;
|
||||
while (PendingNodes.Dequeue(Node))
|
||||
{
|
||||
Node->UpdateLinkedPoints(PendingNodes);
|
||||
}
|
||||
}
|
||||
|
||||
void FRoomPoint::UpdateLinkedPoints(TQueue<FRoomPoint*>& PendingNodes)
|
||||
{
|
||||
for (FLink& Link : LinkedPoints)
|
||||
{
|
||||
check(Link.Point);
|
||||
if (Link.Point->bDirty)
|
||||
{
|
||||
Link.Point->UpdateFrom(*this, Link.Axis);
|
||||
PendingNodes.Enqueue(Link.Point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FRoomPoint::UpdateFrom(FRoomPoint& From, EAxisList::Type Axis)
|
||||
{
|
||||
if (bDirty & Axis & EAxisList::X)
|
||||
{
|
||||
bDirty &= ~EAxisList::X;
|
||||
From.bDirty &= ~EAxisList::X;
|
||||
Location.X = From.Location.X;
|
||||
}
|
||||
if (bDirty & Axis & EAxisList::Y)
|
||||
{
|
||||
bDirty &= ~EAxisList::Y;
|
||||
From.bDirty &= ~EAxisList::Y;
|
||||
Location.Y = From.Location.Y;
|
||||
}
|
||||
if (bDirty & Axis & EAxisList::Z)
|
||||
{
|
||||
bDirty &= ~EAxisList::Z;
|
||||
From.bDirty &= ~EAxisList::Z;
|
||||
Location.Z = From.Location.Z;
|
||||
}
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::EnterTool()
|
||||
{
|
||||
OnDataChanged();
|
||||
ResetSelectedPoint();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::ExitTool()
|
||||
{
|
||||
DungeonEd_LogInfo("Exit Size Tool.");
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI)
|
||||
{
|
||||
const URoomData* Data = GetRoomData();
|
||||
if (!IsValid(Data))
|
||||
return;
|
||||
|
||||
for (const FBoxPoints& Box : Boxes)
|
||||
{
|
||||
Box.Draw(PDI, SelectedPoint);
|
||||
}
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Size::HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click)
|
||||
{
|
||||
ResetSelectedPoint();
|
||||
|
||||
if (!HitProxy)
|
||||
return false;
|
||||
|
||||
if (!HitProxy->IsA(HRoomPointProxy::StaticGetType()))
|
||||
return false;
|
||||
|
||||
const HRoomPointProxy* RoomProxy = (HRoomPointProxy*)HitProxy;
|
||||
checkf(RoomProxy->Index >= 0 && RoomProxy->Index < GetMaxIndex(), TEXT("A room point has been clicked but is out of bounds!"));
|
||||
|
||||
SetSelectedPoint(RoomProxy->Index);
|
||||
GEditor->GetSelectedActors()->DeselectAll();
|
||||
|
||||
// Force translate widget when a point is selected.
|
||||
FEditorModeTools* ModeTools = EdMode->GetModeManager();
|
||||
if (ModeTools)
|
||||
ModeTools->SetWidgetMode(WidgetMode::WM_Translate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Size::InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent)
|
||||
{
|
||||
if (InKey == EKeys::LeftMouseButton && InEvent == IE_Released)
|
||||
ResetDragPoint();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Size::InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale)
|
||||
{
|
||||
if (InViewportClient->GetCurrentWidgetAxis() == EAxisList::None)
|
||||
return false;
|
||||
|
||||
if (!HasValidSelection())
|
||||
return false;
|
||||
|
||||
const URoomData* Data = GetRoomData();
|
||||
if (!IsValid(Data))
|
||||
return false;
|
||||
|
||||
if (!InDrag.IsNearlyZero())
|
||||
{
|
||||
DragPoint += InDrag;
|
||||
FRoomPoint& Point = GetSelectedPoint();
|
||||
FVector OldPoint = Point.GetLocation();
|
||||
Point.SetLocation(Dungeon::SnapPoint(DragPoint, Data->GetRoomUnit()));
|
||||
if (OldPoint != Point.GetLocation())
|
||||
UpdateDataAsset(GetBoxIndexFromPointIndex(SelectedPoint));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Size::UsesTransformWidget() const
|
||||
{
|
||||
return HasValidSelection() || (GEditor->GetSelectedActorCount() > 0);
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Size::UsesTransformWidget(WidgetMode CheckMode) const
|
||||
{
|
||||
return HasValidSelection() ? (CheckMode == WidgetMode::WM_Translate) : EdMode->FEdMode::UsesTransformWidget(CheckMode);
|
||||
}
|
||||
|
||||
FVector FProceduralDungeonEditorTool_Size::GetWidgetLocation() const
|
||||
{
|
||||
return HasValidSelection() ? DragPoint : EdMode->FEdMode::GetWidgetLocation();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::PostUndo(bool bSuccess)
|
||||
{
|
||||
OnDataChanged();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::PostRedo(bool bSuccess)
|
||||
{
|
||||
OnDataChanged();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::OnDataChanged(const URoomData* NewData)
|
||||
{
|
||||
// When the room data has changed, reset all
|
||||
ResetSelectedPoint();
|
||||
ResetDragPoint();
|
||||
|
||||
UpdateBoxes(NewData);
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::OnDataPropertiesChanged(const URoomData* Data)
|
||||
{
|
||||
UpdateBoxes(Data);
|
||||
}
|
||||
|
||||
bool FProceduralDungeonEditorTool_Size::HasValidSelection() const
|
||||
{
|
||||
return SelectedPoint >= 0 && SelectedPoint < GetMaxIndex();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::ResetDragPoint()
|
||||
{
|
||||
if (HasValidSelection())
|
||||
DragPoint = GetSelectedPoint().GetLocation();
|
||||
else
|
||||
DragPoint = FVector::ZeroVector;
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::UpdateDataAsset(int32 BoxIndex) const
|
||||
{
|
||||
URoomData* Data = GetRoomData();
|
||||
if (!IsValid(Data))
|
||||
return;
|
||||
|
||||
if (BoxIndex >= 0 && BoxIndex < Data->BoundingBoxes.Num())
|
||||
{
|
||||
Data->Modify();
|
||||
const FVector RoomUnit = Data->GetRoomUnit();
|
||||
const FBoxPoints& Box = Boxes[BoxIndex];
|
||||
FBoxMinAndMax& DataBox = Data->BoundingBoxes[BoxIndex];
|
||||
const FIntVector A = Dungeon::ToRoomLocation(Box.GetMin(), RoomUnit);
|
||||
const FIntVector B = Dungeon::ToRoomLocation(Box.GetMax(), RoomUnit);
|
||||
DataBox.SetMinAndMax(A, B);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::SetSelectedPoint(int32 Index)
|
||||
{
|
||||
if (Index < 0 || Index >= GetMaxIndex())
|
||||
Index = -1;
|
||||
|
||||
DungeonEd_LogInfo("Selected Point: %d", Index);
|
||||
SelectedPoint = Index;
|
||||
ResetDragPoint();
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::ResetSelectedPoint()
|
||||
{
|
||||
SetSelectedPoint(-1);
|
||||
}
|
||||
|
||||
FRoomPoint& FProceduralDungeonEditorTool_Size::GetSelectedPoint()
|
||||
{
|
||||
checkf(HasValidSelection(), TEXT("Trying to get selected point but selection is invalid!"));
|
||||
const int32 BoxIndex = GetBoxIndexFromPointIndex(SelectedPoint);
|
||||
const int32 PointIndex = GetBoxPointIndex(SelectedPoint);
|
||||
checkf(BoxIndex >= 0 && BoxIndex < Boxes.Num(), TEXT("Selected point's box index is out of bounds!"));
|
||||
checkf(PointIndex >= 0 && PointIndex < FBoxPoints::NbPoints, TEXT("Selected point's point index is out of bounds!"));
|
||||
return Boxes[BoxIndex].GetPoint(PointIndex);
|
||||
}
|
||||
|
||||
void FProceduralDungeonEditorTool_Size::UpdateBoxes(const URoomData* Data)
|
||||
{
|
||||
if (!IsValid(Data))
|
||||
return;
|
||||
|
||||
const int32 NbBoxes = Data->BoundingBoxes.Num();
|
||||
if (Boxes.Num() != NbBoxes)
|
||||
{
|
||||
Boxes.SetNum(NbBoxes);
|
||||
}
|
||||
|
||||
const FVector RoomUnit = Data->GetRoomUnit();
|
||||
for (int32 i = 0; i < NbBoxes; ++i)
|
||||
{
|
||||
const FBoxMinAndMax& Box = Data->BoundingBoxes[i];
|
||||
Boxes[i].SetID(i);
|
||||
Boxes[i].SetMin(Dungeon::ToWorldLocation(Box.GetMin(), RoomUnit));
|
||||
Boxes[i].SetMax(Dungeon::ToWorldLocation(Box.GetMax(), RoomUnit));
|
||||
}
|
||||
}
|
||||
|
||||
FBoxPoints::FBoxPoints()
|
||||
{
|
||||
// 6 1
|
||||
// 2 7
|
||||
// 4 5
|
||||
// 0 3
|
||||
|
||||
Points[0].AddLinkedPoint(Points[2], EAxisList::XY);
|
||||
Points[0].AddLinkedPoint(Points[3], EAxisList::XZ);
|
||||
Points[0].AddLinkedPoint(Points[4], EAxisList::YZ);
|
||||
|
||||
Points[1].AddLinkedPoint(Points[5], EAxisList::XY);
|
||||
Points[1].AddLinkedPoint(Points[6], EAxisList::XZ);
|
||||
Points[1].AddLinkedPoint(Points[7], EAxisList::YZ);
|
||||
|
||||
Points[2].AddLinkedPoint(Points[6], EAxisList::YZ);
|
||||
Points[2].AddLinkedPoint(Points[7], EAxisList::XZ);
|
||||
|
||||
Points[3].AddLinkedPoint(Points[5], EAxisList::YZ);
|
||||
Points[3].AddLinkedPoint(Points[7], EAxisList::XY);
|
||||
|
||||
Points[4].AddLinkedPoint(Points[5], EAxisList::XZ);
|
||||
Points[4].AddLinkedPoint(Points[6], EAxisList::XY);
|
||||
}
|
||||
|
||||
void FBoxPoints::SetMin(FVector Value)
|
||||
{
|
||||
Points[0].SetLocation(Value);
|
||||
}
|
||||
|
||||
void FBoxPoints::SetMax(FVector Value)
|
||||
{
|
||||
Points[1].SetLocation(Value);
|
||||
}
|
||||
|
||||
void FBoxPoints::Draw(FPrimitiveDrawInterface* PDI, int32 SelectedPoint) const
|
||||
{
|
||||
static const FColor NormalColor(200, 200, 200);
|
||||
static const FColor SelectedColor(255, 128, 0);
|
||||
|
||||
const int32 LocalSelectedIndex = SelectedPoint - (ID * NbPoints);
|
||||
for (int i = 0; i < NbPoints; ++i)
|
||||
{
|
||||
const bool bSelected = (LocalSelectedIndex == i);
|
||||
const FColor& Color = bSelected ? SelectedColor : NormalColor;
|
||||
|
||||
PDI->SetHitProxy(new HRoomPointProxy(ID * NbPoints + i));
|
||||
PDI->DrawPoint(Points[i].GetLocation(), Color, 10.0f, SDPG_Foreground);
|
||||
PDI->SetHitProxy(nullptr);
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// Copyright Benoit Pelletier 2023 - 2025 All Rights Reserved.
|
||||
//
|
||||
// This software is available under different licenses depending on the source from which it was obtained:
|
||||
// - The Fab EULA (https://fab.com/eula) applies when obtained from the Fab marketplace.
|
||||
// - The CeCILL-C license (https://cecill.info/licences/Licence_CeCILL-C_V1-en.html) applies when obtained from any other source.
|
||||
// Please refer to the accompanying LICENSE file for further details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ProceduralDungeonEditorTool.h"
|
||||
|
||||
struct HRoomPointProxy : public HHitProxy
|
||||
{
|
||||
DECLARE_HIT_PROXY();
|
||||
|
||||
HRoomPointProxy(int32 InIndex)
|
||||
: HHitProxy(HPP_UI)
|
||||
, Index(InIndex)
|
||||
{
|
||||
}
|
||||
|
||||
int32 Index {-1};
|
||||
};
|
||||
|
||||
struct FRoomPoint
|
||||
{
|
||||
struct FLink
|
||||
{
|
||||
FRoomPoint* Point;
|
||||
EAxisList::Type Axis;
|
||||
};
|
||||
|
||||
void AddLinkedPoint(FRoomPoint& Point, EAxisList::Type Axis);
|
||||
FVector GetLocation() const { return Location; }
|
||||
void SetLocation(FVector InLocation);
|
||||
FVector* operator->() { return &Location; }
|
||||
|
||||
private:
|
||||
void SetDirty();
|
||||
void UpdateWithPropagation();
|
||||
void UpdateLinkedPoints(TQueue<FRoomPoint*>& PendingNodes);
|
||||
void UpdateFrom(FRoomPoint& From, EAxisList::Type Axis);
|
||||
|
||||
private:
|
||||
EAxisList::Type bDirty {EAxisList::None};
|
||||
FVector Location {0};
|
||||
TArray<FLink> LinkedPoints {};
|
||||
};
|
||||
|
||||
struct FBoxPoints
|
||||
{
|
||||
public:
|
||||
FBoxPoints();
|
||||
|
||||
void SetID(uint32 InID) { ID = InID; }
|
||||
void SetMin(FVector Value);
|
||||
void SetMax(FVector Value);
|
||||
uint32 GetID() const { return ID; }
|
||||
FVector GetMin() const { return Points[0].GetLocation(); }
|
||||
FVector GetMax() const { return Points[1].GetLocation(); }
|
||||
|
||||
FRoomPoint& GetPoint(int32 Index)
|
||||
{
|
||||
check(Index >= 0 && Index < NbPoints);
|
||||
return Points[Index];
|
||||
}
|
||||
|
||||
void Draw(FPrimitiveDrawInterface* PDI, int32 SelectedPoint) const;
|
||||
|
||||
static const uint32 NbPoints {8};
|
||||
|
||||
private:
|
||||
uint32 ID;
|
||||
FRoomPoint Points[NbPoints];
|
||||
};
|
||||
|
||||
class FProceduralDungeonEditorTool_Size : public FProceduralDungeonEditorTool
|
||||
{
|
||||
public:
|
||||
FProceduralDungeonEditorTool_Size(FProceduralDungeonEdMode* InEdMode)
|
||||
: FProceduralDungeonEditorTool(InEdMode)
|
||||
{
|
||||
}
|
||||
|
||||
virtual const TCHAR* GetToolName() override { return TEXT("Tool_Size"); }
|
||||
virtual FText GetDisplayName() override { return NSLOCTEXT("ProceduralDungeonEditor", "Tool_Size", "Size"); }
|
||||
virtual FText GetDisplayMessage() override { return NSLOCTEXT("ProceduralDungeonEditor", "Tool_Size_Tooltip", ""); }
|
||||
|
||||
virtual void EnterTool() override;
|
||||
virtual void ExitTool() override;
|
||||
|
||||
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override;
|
||||
virtual bool HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) override;
|
||||
virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent) override;
|
||||
virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override;
|
||||
virtual bool UsesTransformWidget() const override;
|
||||
virtual bool UsesTransformWidget(WidgetMode CheckMode) const override;
|
||||
virtual FVector GetWidgetLocation() const override;
|
||||
|
||||
/** FEditorUndoClient interface */
|
||||
virtual void PostUndo(bool bSuccess) override;
|
||||
virtual void PostRedo(bool bSuccess) override;
|
||||
|
||||
virtual void OnDataChanged(const URoomData* NewData = nullptr) override;
|
||||
virtual void OnDataPropertiesChanged(const URoomData* Data = nullptr) override;
|
||||
|
||||
bool HasValidSelection() const;
|
||||
void ResetDragPoint();
|
||||
void UpdateDataAsset(int32 BoxIndex) const;
|
||||
|
||||
void SetSelectedPoint(int32 Index);
|
||||
void ResetSelectedPoint();
|
||||
|
||||
private:
|
||||
int32 GetBoxIndexFromPointIndex(int32 PointIndex) const { return PointIndex / FBoxPoints::NbPoints; }
|
||||
int32 GetBoxPointIndex(int32 PointIndex) const { return PointIndex % FBoxPoints::NbPoints; }
|
||||
int32 GetMaxIndex() const { return Boxes.Num() * FBoxPoints::NbPoints; }
|
||||
FRoomPoint& GetSelectedPoint();
|
||||
void UpdateBoxes(const URoomData* Data);
|
||||
|
||||
private:
|
||||
int32 SelectedPoint {-1};
|
||||
TArray<FBoxPoints> Boxes;
|
||||
FVector DragPoint;
|
||||
};
|
||||
Reference in New Issue
Block a user