Files
UnrealEngine/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeComponentTools.cpp
2025-05-18 13:04:45 +08:00

2716 lines
99 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "Misc/MessageDialog.h"
#include "Misc/ScopedSlowTask.h"
#include "Modules/ModuleManager.h"
#include "Engine/EngineTypes.h"
#include "LandscapeToolInterface.h"
#include "LandscapeProxy.h"
#include "LandscapeGizmoActiveActor.h"
#include "LandscapeEdMode.h"
#include "LandscapeEditorObject.h"
#include "Landscape.h"
#include "LandscapeStreamingProxy.h"
#include "ObjectTools.h"
#include "LandscapeEdit.h"
#include "LandscapeComponent.h"
#include "LandscapeRender.h"
#include "PropertyEditorModule.h"
#include "InstancedFoliageActor.h"
#include "LandscapeEdModeTools.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "Materials/MaterialExpressionLandscapeVisibilityMask.h"
#include "Algo/Copy.h"
#include "LandscapeSubsystem.h"
#define LOCTEXT_NAMESPACE "Landscape"
//
// FLandscapeToolSelect
//
class FLandscapeToolStrokeSelect : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
bool bInitializedComponentInvert;
bool bInvert;
bool bNeedsSelectionUpdate;
public:
FLandscapeToolStrokeSelect(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
, bInitializedComponentInvert(false)
, bNeedsSelectionUpdate(false)
{
}
~FLandscapeToolStrokeSelect()
{
if (bNeedsSelectionUpdate)
{
TArray<UObject*> Objects;
if (LandscapeInfo)
{
TSet<ULandscapeComponent*> SelectedComponents = LandscapeInfo->GetSelectedComponents();
Objects.Reset(SelectedComponents.Num());
Algo::Copy(SelectedComponents, Objects);
}
FPropertyEditorModule& PropertyModule = FModuleManager::Get().LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));
PropertyModule.UpdatePropertyViews(Objects);
}
}
void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeSelect_Apply);
if (LandscapeInfo)
{
LandscapeInfo->Modify();
// TODO - only retrieve bounds as we don't need the data
const FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
// Shrink bounds by 1,1 to avoid GetComponentsInRegion picking up extra components on all sides due to the overlap between components
TSet<ULandscapeComponent*> NewComponents;
LandscapeInfo->GetComponentsInRegion(X1 + 1, Y1 + 1, X2 - 1, Y2 - 1, NewComponents);
if (!bInitializedComponentInvert)
{
// Get the component under the mouse location. Copied from FLandscapeBrushComponent::ApplyBrush()
const float MouseX = static_cast<float>(InteractorPositions[0].Position.X);
const float MouseY = static_cast<float>(InteractorPositions[0].Position.Y);
const int32 MouseComponentIndexX = (MouseX >= 0.0f) ? FMath::FloorToInt(MouseX / LandscapeInfo->ComponentSizeQuads) : FMath::CeilToInt(MouseX / LandscapeInfo->ComponentSizeQuads);
const int32 MouseComponentIndexY = (MouseY >= 0.0f) ? FMath::FloorToInt(MouseY / LandscapeInfo->ComponentSizeQuads) : FMath::CeilToInt(MouseY / LandscapeInfo->ComponentSizeQuads);
ULandscapeComponent* MouseComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(MouseComponentIndexX, MouseComponentIndexY));
if (MouseComponent != nullptr)
{
bInvert = LandscapeInfo->GetSelectedComponents().Contains(MouseComponent);
}
else
{
bInvert = false;
}
bInitializedComponentInvert = true;
}
TSet<ULandscapeComponent*> NewSelection;
if (bInvert)
{
NewSelection = LandscapeInfo->GetSelectedComponents().Difference(NewComponents);
}
else
{
NewSelection = LandscapeInfo->GetSelectedComponents().Union(NewComponents);
}
LandscapeInfo->Modify();
LandscapeInfo->UpdateSelectedComponents(NewSelection);
// Update Details tab with selection
bNeedsSelectionUpdate = true;
}
}
};
class FLandscapeToolSelect : public FLandscapeToolBase<FLandscapeToolStrokeSelect>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokeSelect>;
public:
FLandscapeToolSelect(FEdModeLandscape* InEdMode)
: Super(InEdMode)
{
}
virtual bool AffectsEditLayers() const { return false; }
virtual ELandscapeLayerUpdateMode GetBeginToolContentUpdateFlag() const override
{
return ELandscapeLayerUpdateMode::Update_None;
}
virtual ELandscapeLayerUpdateMode GetTickToolContentUpdateFlag() const override
{
return ELandscapeLayerUpdateMode::Update_None;
}
virtual ELandscapeLayerUpdateMode GetEndToolContentUpdateFlag() const override
{
return ELandscapeLayerUpdateMode::Update_None;
}
virtual const TCHAR* GetToolName() const override { return TEXT("Select"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Selection", "Component Selection"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Selection_Message", "Paint a mask on the Landscape to protect areas from editing."); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::SelectComponent | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
};
//
// FLandscapeToolMask
//
class FLandscapeToolStrokeMask : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
public:
FLandscapeToolStrokeMask(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
, Cache(InTarget)
{
}
void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeMask_Apply);
if (LandscapeInfo)
{
LandscapeInfo->Modify();
// Invert when holding Shift
bool bInvert = InteractorPositions[ InteractorPositions.Num() - 1].bModifierPressed;
const FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
// Tablet pressure
float Pressure = ViewportClient->Viewport->IsPenActive() ? ViewportClient->Viewport->GetTabletPressure() : 1.0f;
Cache.CacheData(X1, Y1, X2, Y2);
TArray<uint8> Data;
Cache.GetCachedData(X1, Y1, X2, Y2, Data);
TSet<ULandscapeComponent*> NewComponents;
LandscapeInfo->GetComponentsInRegion(X1, Y1, X2, Y2, NewComponents);
LandscapeInfo->UpdateSelectedComponents(NewComponents, false);
for (int32 Y = BrushInfo.GetBounds().Min.Y; Y < BrushInfo.GetBounds().Max.Y; Y++)
{
const float* BrushScanline = BrushInfo.GetDataPtr(FIntPoint(0, Y));
uint8* DataScanline = Data.GetData() + (Y - Y1) * (X2 - X1 + 1) + (0 - X1);
for (int32 X = BrushInfo.GetBounds().Min.X; X < BrushInfo.GetBounds().Max.X; X++)
{
const FIntPoint Key = FIntPoint(X, Y);
const float BrushValue = BrushScanline[X];
if (BrushValue > 0.0f && LandscapeInfo->IsValidPosition(X, Y))
{
float PaintValue = BrushValue * UISettings->GetCurrentToolStrength() * Pressure;
float Value = DataScanline[X] / 255.0f;
checkSlow(FMath::IsNearlyEqual(Value, LandscapeInfo->SelectedRegion.FindRef(Key), 1 / 255.0f));
if (bInvert)
{
Value = FMath::Max(Value - PaintValue, 0.0f);
}
else
{
Value = FMath::Min(Value + PaintValue, 1.0f);
}
if (Value > 0.0f)
{
LandscapeInfo->SelectedRegion.Add(Key, Value);
}
else
{
LandscapeInfo->SelectedRegion.Remove(Key);
}
DataScanline[X] = static_cast<uint8>(FMath::Clamp<int32>(FMath::RoundToInt(Value * 255), 0, 255));
}
}
}
Cache.SetCachedData(X1, Y1, X2, Y2, Data);
Cache.Flush();
}
}
protected:
FLandscapeDataCache Cache;
};
class FLandscapeToolMask : public FLandscapeToolBase<FLandscapeToolStrokeMask>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokeMask>;
public:
FLandscapeToolMask(FEdModeLandscape* InEdMode)
: Super(InEdMode)
{
}
virtual bool AffectsEditLayers() const { return false; }
virtual const TCHAR* GetToolName() const override { return TEXT("Mask"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Mask", "Region Selection"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Mask_Message", "Region Selection"); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::SelectRegion | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return true; }
virtual ELandscapeToolType GetToolType() override { return ELandscapeToolType::Mask; }
};
//
// FLandscapeToolVisibility
//
class FLandscapeToolStrokeVisibility : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
public:
FLandscapeToolStrokeVisibility(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
, Cache(InTarget)
{
}
void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeVisibility_Apply);
if (LandscapeInfo)
{
LandscapeInfo->Modify();
// Get list of verts to update
FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
// Invert when holding Shift
bool bInvert = InteractorPositions[InteractorPositions.Num() - 1].bModifierPressed;
// Tablet pressure
float Pressure = ViewportClient->Viewport->IsPenActive() ? ViewportClient->Viewport->GetTabletPressure() : 1.0f;
Cache.CacheData(X1, Y1, X2, Y2);
TArray<uint8> Data;
Cache.GetCachedData(X1, Y1, X2, Y2, Data);
for (int32 Y = BrushInfo.GetBounds().Min.Y; Y < BrushInfo.GetBounds().Max.Y; Y++)
{
const float* BrushScanline = BrushInfo.GetDataPtr(FIntPoint(0, Y));
uint8* DataScanline = Data.GetData() + (Y - Y1) * (X2 - X1 + 1) + (0 - X1);
for (int32 X = BrushInfo.GetBounds().Min.X; X < BrushInfo.GetBounds().Max.X; X++)
{
const float BrushValue = BrushScanline[X];
if (BrushValue > 0.0f)
{
uint8 Value = bInvert ? 0 : 255; // Just on and off for visibility, for masking...
DataScanline[X] = Value;
}
}
}
Cache.SetCachedData(X1, Y1, X2, Y2, Data);
Cache.Flush();
// Dirty any runtime virtual textures that our landscape components write to.
LandscapeInfo->DirtyRuntimeVirtualTextureForLandscapeArea(X1, Y1, X2, Y2);
}
}
protected:
FLandscapeVisCache Cache;
};
class FLandscapeToolVisibility : public FLandscapeToolBase<FLandscapeToolStrokeVisibility>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokeVisibility>;
public:
FLandscapeToolVisibility(FEdModeLandscape* InEdMode)
: Super(InEdMode)
{
}
virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& InTarget, const FVector& InHitLocation) override
{
return Super::BeginTool(ViewportClient, InTarget, InHitLocation);
}
virtual const TCHAR* GetToolName() const override { return TEXT("Visibility"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Visibility", "Visibility"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Visibility_Message", "This tool will allow you to mask out the visibility and collision of areas of your Landscape when used in conjunction with the Landscape Hole Material."); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::None | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
virtual ELandscapeToolTargetTypeMask::Type GetSupportedTargetTypes() override
{
return ELandscapeToolTargetTypeMask::Visibility;
}
};
//
// FLandscapeToolMoveToLevel
//
class FLandscapeToolStrokeMoveToLevel : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
public:
FLandscapeToolStrokeMoveToLevel(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
{
}
void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeMoveToLevel_Apply);
ALandscape* Landscape = LandscapeInfo ? LandscapeInfo->LandscapeActor.Get() : nullptr;
if (Landscape)
{
Landscape->Modify();
LandscapeInfo->Modify();
TArray<UObject*> RenameObjects;
FString MsgBoxList;
// Check the Physical Material is same package with Landscape
if (Landscape->DefaultPhysMaterial && Landscape->DefaultPhysMaterial->GetOutermost() == Landscape->GetOutermost())
{
//FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "LandscapePhyMaterial_Warning", "Landscape's DefaultPhysMaterial is in the same package as the Landscape Actor. To support streaming levels, you must move the PhysicalMaterial to another package.") );
RenameObjects.AddUnique(Landscape->DefaultPhysMaterial);
MsgBoxList += Landscape->DefaultPhysMaterial->GetPathName();
MsgBoxList += FString::Printf(TEXT("\n"));
}
// Check the LayerInfoObjects are same package with Landscape
for (int32 i = 0; i < LandscapeInfo->Layers.Num(); ++i)
{
ULandscapeLayerInfoObject* LayerInfo = LandscapeInfo->Layers[i].LayerInfoObj;
if (LayerInfo && LayerInfo->GetOutermost() == Landscape->GetOutermost())
{
RenameObjects.AddUnique(LayerInfo);
MsgBoxList += LayerInfo->GetPathName();
MsgBoxList += FString::Printf(TEXT("\n"));
}
}
auto SelectedComponents = LandscapeInfo->GetSelectedComponents();
bool bBrush = false;
if (!SelectedComponents.Num())
{
// Get list of verts to update
// TODO - only retrieve bounds as we don't need the data
FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
// Shrink bounds by 1,1 to avoid GetComponentsInRegion picking up extra components on all sides due to the overlap between components
LandscapeInfo->GetComponentsInRegion(X1 + 1, Y1 + 1, X2 - 1, Y2 - 1, SelectedComponents);
bBrush = true;
}
check(ViewportClient->GetScene());
UWorld* World = ViewportClient->GetScene()->GetWorld();
check(World);
if (SelectedComponents.Num())
{
bool bIsAllCurrentLevel = true;
for (ULandscapeComponent* Component : SelectedComponents)
{
if (Component->GetLandscapeProxy()->GetLevel() != World->GetCurrentLevel())
{
bIsAllCurrentLevel = false;
}
}
if (bIsAllCurrentLevel)
{
// Need to fix double WM
if (!bBrush)
{
// Remove Selection
LandscapeInfo->ClearSelectedRegion(true);
}
return;
}
for (ULandscapeComponent* Component : SelectedComponents)
{
UMaterialInterface* LandscapeMaterial = Component->GetLandscapeMaterial();
if (LandscapeMaterial && LandscapeMaterial->GetOutermost() == Component->GetOutermost())
{
RenameObjects.AddUnique(LandscapeMaterial);
MsgBoxList += Component->GetName() + TEXT("'s ") + LandscapeMaterial->GetPathName();
MsgBoxList += FString::Printf(TEXT("\n"));
//It.RemoveCurrent();
}
}
if (RenameObjects.Num())
{
if (FMessageDialog::Open(EAppMsgType::OkCancel,
FText::Format(
NSLOCTEXT("UnrealEd", "LandscapeMoveToStreamingLevel_SharedResources", "The following items must be moved out of the persistent level and into a package that can be shared between multiple levels:\n\n{0}"),
FText::FromString(MsgBoxList))) == EAppReturnType::Type::Ok)
{
FString Path = Landscape->GetOutermost()->GetName() + TEXT("_sharedassets/");
bool bSucceed = ObjectTools::RenameObjects(RenameObjects, false, TEXT(""), Path);
if (!bSucceed)
{
FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "LandscapeMoveToStreamingLevel_RenameFailed", "Move To Streaming Level did not succeed because shared resources could not be moved to a new package."));
return;
}
}
else
{
return;
}
}
FScopedSlowTask SlowTask(0, LOCTEXT("BeginMovingLandscapeComponentsToCurrentLevelTask", "Moving Landscape components to current level"));
SlowTask.MakeDialogDelayed(10); // show slow task dialog after 10 seconds
if (ALandscapeProxy* LandscapeProxy = LandscapeInfo->MoveComponentsToLevel(SelectedComponents.Array(), World->GetCurrentLevel()))
{
GEditor->SelectNone(false, true);
GEditor->SelectActor(LandscapeProxy, true, false, true);
GEditor->SelectNone(false, true);
// Remove Selection
LandscapeInfo->ClearSelectedRegion(true);
}
}
}
}
};
class FLandscapeToolMoveToLevel : public FLandscapeToolBase<FLandscapeToolStrokeMoveToLevel>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokeMoveToLevel>;
public:
FLandscapeToolMoveToLevel(FEdModeLandscape* InEdMode)
: Super(InEdMode)
{
}
virtual bool AffectsEditLayers() const override { return false; }
virtual const TCHAR* GetToolName() const override { return TEXT("MoveToLevel"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_MoveToLevel", "Move to Streaming Level"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_MoveToLevel_Message", "Move the selected components, via using the Selection tool, to the current streaming level. This makes it possible to move sections of a Landscape into a streaming level so that they will be streamed in and out with that level, optimizing the performance of the Landscape."); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::SelectComponent | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
};
//
// FLandscapeToolAddComponent
//
class FLandscapeToolStrokeAddComponent : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
public:
FLandscapeToolStrokeAddComponent(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
, HeightCache(InTarget)
, XYOffsetCache(InTarget)
{
}
virtual ~FLandscapeToolStrokeAddComponent()
{
// We flush here so here ~FXYOffsetmapAccessor can safely lock the heightmap data to update bounds
HeightCache.Flush();
XYOffsetCache.Flush();
}
virtual void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeAddComponent_Apply);
if (LandscapeInfo)
{
check(Brush->GetBrushType() == ELandscapeBrushType::Component);
// Get list of verts to update
// TODO - only retrieve bounds as we don't need the data
FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
// Find component range for this block of data, non shared vertices
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, LandscapeInfo->ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
// expand the area by one vertex in each direction to ensure normals are calculated correctly
X1 -= 1;
Y1 -= 1;
X2 += 1;
Y2 += 1;
TArray<uint16> Data;
TArray<FVector> XYOffsetData;
HeightCache.CacheData(X1, Y1, X2, Y2);
XYOffsetCache.CacheData(X1, Y1, X2, Y2);
HeightCache.GetCachedData(X1, Y1, X2, Y2, Data);
bool bHasXYOffset = XYOffsetCache.GetCachedData(X1, Y1, X2, Y2, XYOffsetData);
UWorld* World = ViewportClient->GetScene()->GetWorld();
TArray<ALandscapeProxy*> ModifiedProxies;
TArray<ULandscapeComponent*> NewComponents;
LandscapeInfo->Modify();
ULandscapeSubsystem* LandscapeSubsystem = World->GetSubsystem<ULandscapeSubsystem>();
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
{
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
{
ULandscapeComponent* LandscapeComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY));
if (!LandscapeComponent)
{
// Add New component...
FIntPoint ComponentBase = FIntPoint(ComponentIndexX, ComponentIndexY)* LandscapeInfo->ComponentSizeQuads;
if (ALandscapeProxy* LandscapeProxy = LandscapeSubsystem->FindOrAddLandscapeProxy(LandscapeInfo, ComponentBase))
{
LandscapeComponent = NewObject<ULandscapeComponent>(LandscapeProxy, NAME_None, RF_Transactional);
NewComponents.Add(LandscapeComponent);
LandscapeComponent->Init(
ComponentBase.X, ComponentBase.Y,
LandscapeProxy->ComponentSizeQuads,
LandscapeProxy->NumSubsections,
LandscapeProxy->SubsectionSizeQuads
);
TArray<FColor> HeightData;
const int32 ComponentVerts = (LandscapeComponent->SubsectionSizeQuads + 1) * LandscapeComponent->NumSubsections;
HeightData.Empty(FMath::Square(ComponentVerts));
HeightData.AddZeroed(FMath::Square(ComponentVerts));
LandscapeComponent->InitHeightmapData(HeightData, true);
LandscapeComponent->UpdateMaterialInstances();
LandscapeInfo->XYtoComponentMap.Add(FIntPoint(ComponentIndexX, ComponentIndexY), LandscapeComponent);
LandscapeInfo->XYtoAddCollisionMap.Remove(FIntPoint(ComponentIndexX, ComponentIndexY));
ModifiedProxies.AddUnique(LandscapeProxy);
}
}
}
}
// Need to register to use general height/xyoffset data update
for (int32 Idx = 0; Idx < NewComponents.Num(); Idx++)
{
NewComponents[Idx]->RegisterComponent();
}
if (bHasXYOffset)
{
XYOffsetCache.SetCachedData(X1, Y1, X2, Y2, XYOffsetData);
}
XYOffsetCache.Flush();
HeightCache.SetCachedData(X1, Y1, X2, Y2, Data);
HeightCache.Flush();
ALandscape* Landscape = LandscapeInfo->LandscapeActor.Get();
bool bHasLandscapeLayersContent = Landscape && Landscape->HasLayersContent();
if (bHasLandscapeLayersContent)
{
check(Landscape != nullptr); // Landscape actor is required if layer system is enabled
Landscape->RequestLayersInitialization();
}
for (ULandscapeComponent* NewComponent : NewComponents)
{
if (bHasLandscapeLayersContent)
{
TArray<ULandscapeComponent*> ComponentsUsingHeightmap;
ComponentsUsingHeightmap.Add(NewComponent);
for (const ULandscapeEditLayerBase* EditLayer : Landscape->GetEditLayersConst())
{
// Since we do not share heightmap when adding new component, we will provide the required array, but they will only be used for 1 component
TMap<UTexture2D*, UTexture2D*> CreatedHeightmapTextures;
NewComponent->AddDefaultLayerData(EditLayer->GetGuid(), ComponentsUsingHeightmap, CreatedHeightmapTextures);
}
}
// Update Collision
NewComponent->UpdateCachedBounds();
NewComponent->UpdateBounds();
NewComponent->MarkRenderStateDirty();
if (!bHasLandscapeLayersContent)
{
ULandscapeHeightfieldCollisionComponent* CollisionComp = NewComponent->GetCollisionComponent();
if (CollisionComp && !bHasXYOffset)
{
CollisionComp->MarkRenderStateDirty();
CollisionComp->RecreateCollision();
}
}
}
// Add/update "add collision" around the newly added components
if (!bHasLandscapeLayersContent)
{
// Top row
int32 ComponentIndexY = ComponentIndexY1 - 1;
for (int32 ComponentIndexX = ComponentIndexX1 - 1; ComponentIndexX <= ComponentIndexX2 + 1; ++ComponentIndexX)
{
if (!LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY)))
{
LandscapeInfo->UpdateAddCollision(FIntPoint(ComponentIndexX, ComponentIndexY));
}
}
// Sides
for (ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ++ComponentIndexY)
{
// Left
int32 ComponentIndexX = ComponentIndexX1 - 1;
if (!LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY)))
{
LandscapeInfo->UpdateAddCollision(FIntPoint(ComponentIndexX, ComponentIndexY));
}
// Right
ComponentIndexX = ComponentIndexX1 + 1;
if (!LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY)))
{
LandscapeInfo->UpdateAddCollision(FIntPoint(ComponentIndexX, ComponentIndexY));
}
}
// Bottom row
ComponentIndexY = ComponentIndexY2 + 1;
for (int32 ComponentIndexX = ComponentIndexX1 - 1; ComponentIndexX <= ComponentIndexX2 + 1; ++ComponentIndexX)
{
if (!LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY)))
{
LandscapeInfo->UpdateAddCollision(FIntPoint(ComponentIndexX, ComponentIndexY));
}
}
}
for (ALandscapeProxy* ModifiedProxy : ModifiedProxies)
{
// Make sure the proxy has an appropriate value for bHasLayersContent after this component creation :
ModifiedProxy->UpdateCachedHasLayersContent();
}
if (Landscape)
{
GEngine->BroadcastOnActorMoved(Landscape);
}
}
}
protected:
FLandscapeHeightCache HeightCache;
FLandscapeXYOffsetCache<true> XYOffsetCache;
};
class FLandscapeToolAddComponent : public FLandscapeToolBase<FLandscapeToolStrokeAddComponent>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokeAddComponent>;
public:
FLandscapeToolAddComponent(FEdModeLandscape* InEdMode)
: Super(InEdMode)
, bIsToolActionResolutionCompliant(true)
{
}
virtual bool AffectsEditLayers() const override { return false; }
virtual const TCHAR* GetToolName() const override { return TEXT("AddComponent"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_AddComponent", "Add New Landscape Component"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_AddComponent_Message", "Create new components for the current Landscape, one at a time. The cursor shows a green wireframe where new components can be added."); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::None | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
// Sphere traces can result in components being added at a good distance from any neighboring components, because it intersects against a virtual plane
virtual bool UseSphereTrace() override { return false; }
virtual bool CanToolBeActivated() const override
{
return Super::CanToolBeActivated() && bIsToolActionResolutionCompliant;
}
virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override
{
if (EdMode != nullptr)
{
bIsToolActionResolutionCompliant = EdMode->IsLandscapeResolutionCompliant();
}
Super::Tick(ViewportClient, DeltaTime);
}
virtual void EnterTool() override
{
Super::EnterTool();
AddCollision.Reset();
if(ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get())
{
LandscapeInfo->UpdateAllAddCollisions(); // Todo - as this is only used by this tool, move it into this tool?
}
}
virtual void ExitTool() override
{
Super::ExitTool();
AddCollision.Reset();
}
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override
{
if (AddCollision.IsSet())
{
const FColor LineColor = CanToolBeActivated() ? FColor(0, 255, 128) : FColor(255, 0, 64);
PDI->DrawLine(AddCollision->Corners[0], AddCollision->Corners[3], LineColor, SDPG_Foreground);
PDI->DrawLine(AddCollision->Corners[3], AddCollision->Corners[1], LineColor, SDPG_Foreground);
PDI->DrawLine(AddCollision->Corners[1], AddCollision->Corners[0], LineColor, SDPG_Foreground);
PDI->DrawLine(AddCollision->Corners[0], AddCollision->Corners[2], LineColor, SDPG_Foreground);
PDI->DrawLine(AddCollision->Corners[2], AddCollision->Corners[3], LineColor, SDPG_Foreground);
PDI->DrawLine(AddCollision->Corners[3], AddCollision->Corners[0], LineColor, SDPG_Foreground);
}
}
virtual bool HitTrace(const FVector& TraceStart, const FVector& TraceEnd, FVector& OutHitLocation) override
{
ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get();
if (!LandscapeInfo)
{
return false;
}
ALandscapeProxy* Proxy = LandscapeInfo->GetLandscapeProxy();
if (!Proxy)
{
return false;
}
AddCollision.Reset();
FVector IntersectPoint;
// Need to optimize collision for AddLandscapeComponent...?
for (auto& XYToAddCollisionPair : LandscapeInfo->XYtoAddCollisionMap)
{
FLandscapeAddCollision& CurrentAddCollision = XYToAddCollisionPair.Value;
// Triangle 1
if(RayIntersectTriangle(TraceStart, TraceEnd, CurrentAddCollision.Corners[0], CurrentAddCollision.Corners[3], CurrentAddCollision.Corners[1], IntersectPoint))
{
AddCollision = CurrentAddCollision;
OutHitLocation = Proxy->LandscapeActorToWorld().InverseTransformPosition(IntersectPoint);
return true;
}
// Triangle 2
if(RayIntersectTriangle(TraceStart, TraceEnd, CurrentAddCollision.Corners[0], CurrentAddCollision.Corners[2], CurrentAddCollision.Corners[3], IntersectPoint))
{
AddCollision = CurrentAddCollision;
OutHitLocation = Proxy->LandscapeActorToWorld().InverseTransformPosition(IntersectPoint);
return true;
}
}
return false;
}
virtual int32 GetToolActionResolutionDelta() const override
{
int32 ResolutionDelta = 0;
if (EdMode == nullptr)
{
return 0;
}
const FLandscapeToolTarget& ToolTarget = EdMode->CurrentToolTarget;
FLandscapeBrush* CurrentBrush = EdMode->CurrentBrush;
TOptional<FVector2D> LastMousePosition = CurrentBrush->GetLastMousePosition();
FIntRect LandscapeIndices;
ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get();
if ( LandscapeInfo != nullptr && ToolTarget.LandscapeInfo.IsValid() && LastMousePosition.IsSet() && ToolTarget.LandscapeInfo->GetLandscapeXYComponentBounds(LandscapeIndices))
{
const int32 BrushSize = FMath::Max(EdMode->UISettings->BrushComponentSize, 0);
const int32 ComponentSizeQuads = ToolTarget.LandscapeInfo->ComponentSizeQuads;
const float BrushOriginX = static_cast<float>(LastMousePosition.GetValue().X / ComponentSizeQuads - (BrushSize - 1) / 2.0);
const float BrushOriginY = static_cast<float>(LastMousePosition.GetValue().Y / ComponentSizeQuads - (BrushSize - 1) / 2.0);
const int32 ComponentIndexX = FMath::FloorToInt(BrushOriginX);
const int32 ComponentIndexY = FMath::FloorToInt(BrushOriginY);
int32 NumNewComponents = 0;
int32 HalfBrushSize = BrushSize / 2;
FIntRect BrushSupport{ ComponentIndexX - HalfBrushSize, ComponentIndexY - HalfBrushSize, ComponentIndexX + HalfBrushSize, ComponentIndexY + HalfBrushSize };
for (int32 Y = BrushSupport.Min.Y; Y <= BrushSupport.Max.Y; Y++)
{
for (int32 X = BrushSupport.Min.X; X <= BrushSupport.Max.X; X++)
{
NumNewComponents += LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X, Y)) == nullptr ? 1 : 0;
}
}
return NumNewComponents * ComponentSizeQuads * ComponentSizeQuads;
}
return 0;
}
private:
bool RayIntersectTriangle(const FVector& Start, const FVector& End, const FVector& A, const FVector& B, const FVector& C, FVector& IntersectPoint)
{
const FVector BA = A - B;
const FVector CB = B - C;
const FVector TriNormal = BA ^ CB;
bool bCollide = FMath::SegmentPlaneIntersection(Start, End, FPlane(A, TriNormal), IntersectPoint);
if (!bCollide)
{
return false;
}
FVector BaryCentric = FMath::ComputeBaryCentric2D(IntersectPoint, A, B, C);
if (BaryCentric.X > 0.0f && BaryCentric.Y > 0.0f && BaryCentric.Z > 0.0f)
{
return true;
}
return false;
}
TOptional<FLandscapeAddCollision> AddCollision;
bool bIsToolActionResolutionCompliant;
};
//
// FLandscapeToolDeleteComponent
//
class FLandscapeToolStrokeDeleteComponent : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
public:
FLandscapeToolStrokeDeleteComponent(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
{
}
void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeDeleteComponent_Apply);
if (LandscapeInfo)
{
auto SelectedComponents = LandscapeInfo->GetSelectedComponents();
if (SelectedComponents.Num() == 0)
{
// Get list of components to delete from brush
// TODO - only retrieve bounds as we don't need the vert data
FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
// Shrink bounds by 1,1 to avoid GetComponentsInRegion picking up extra components on all sides due to the overlap between components
LandscapeInfo->GetComponentsInRegion(X1 + 1, Y1 + 1, X2 - 1, Y2 - 1, SelectedComponents);
}
// Delete the components
EdMode->DeleteLandscapeComponents(LandscapeInfo, SelectedComponents);
}
}
};
class FLandscapeToolDeleteComponent : public FLandscapeToolBase<FLandscapeToolStrokeDeleteComponent>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokeDeleteComponent>;
public:
FLandscapeToolDeleteComponent(FEdModeLandscape* InEdMode)
: Super(InEdMode)
{
}
virtual bool AffectsEditLayers() const override { return false; }
virtual const TCHAR* GetToolName() const override { return TEXT("DeleteComponent"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_DeleteComponent", "Delete Landscape Components"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_DeleteComponent_Message", "Delete selected components . If no components are currently selected, deletes the component highlighted under the mouse cursor. "); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::SelectComponent | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
};
//
// FLandscapeToolCopy
//
template<class ToolTarget>
class FLandscapeToolStrokeCopy : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
public:
FLandscapeToolStrokeCopy(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
, Cache(InTarget)
, HeightCache(InTarget)
, WeightCache(InTarget)
{
}
struct FGizmoPreData
{
float Ratio;
float Data;
};
void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeCopy_Apply);
//ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo;
ALandscapeGizmoActiveActor* Gizmo = EdMode->CurrentGizmoActor.Get();
if (LandscapeInfo && Gizmo && Gizmo->GizmoTexture && Gizmo->GetRootComponent())
{
Gizmo->TargetLandscapeInfo = LandscapeInfo;
// Get list of verts to update
// TODO - only retrieve bounds as we don't need the data
FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
//Gizmo->Modify(); // No transaction for Copied data as other tools...
//Gizmo->SelectedData.Empty();
Gizmo->ClearGizmoData();
// Tablet pressure
//float Pressure = ViewportClient->Viewport->IsPenActive() ? ViewportClient->Viewport->GetTabletPressure() : 1.0f;
bool bApplyToAll = EdMode->UISettings->bApplyToAllTargets;
const int32 LayerNum = LandscapeInfo->Layers.Num();
TArray<uint16> HeightData;
TArray<uint8> WeightDatas; // Weight*Layers...
TArray<typename ToolTarget::CacheClass::DataType> Data;
TSet<ULandscapeLayerInfoObject*> LayerInfoSet;
if (bApplyToAll)
{
HeightCache.CacheData(X1, Y1, X2, Y2);
HeightCache.GetCachedData(X1, Y1, X2, Y2, HeightData);
WeightCache.CacheData(X1, Y1, X2, Y2);
WeightCache.GetCachedData(X1, Y1, X2, Y2, WeightDatas, LayerNum);
}
else
{
Cache.CacheData(X1, Y1, X2, Y2);
Cache.GetCachedData(X1, Y1, X2, Y2, Data);
}
const float ScaleXY = static_cast<float>(LandscapeInfo->DrawScale.X);
float Width = Gizmo->GetWidth();
float Height = Gizmo->GetHeight();
Gizmo->CachedWidth = Width;
Gizmo->CachedHeight = Height;
Gizmo->CachedScaleXY = ScaleXY;
// Rasterize Gizmo regions
int32 SizeX = FMath::CeilToInt(Width / ScaleXY);
int32 SizeY = FMath::CeilToInt(Height / ScaleXY);
const float W = (Width - ScaleXY) / (2 * ScaleXY);
const float H = (Height - ScaleXY) / (2 * ScaleXY);
FMatrix WToL = LandscapeInfo->GetLandscapeProxy()->LandscapeActorToWorld().ToMatrixWithScale().InverseFast();
//FMatrix LToW = Landscape->LocalToWorld();
FVector BaseLocation = WToL.TransformPosition(Gizmo->GetActorLocation());
FMatrix GizmoLocalToLandscape = FRotationTranslationMatrix(FRotator(0, Gizmo->GetActorRotation().Yaw, 0), FVector(BaseLocation.X, BaseLocation.Y, 0));
const int32 NeighborNum = 4;
bool bDidCopy = false;
bool bFullCopy = !EdMode->UISettings->bUseSelectedRegion || !LandscapeInfo->SelectedRegion.Num();
//bool bInverted = EdMode->UISettings->bUseSelectedRegion && EdMode->UISettings->bUseNegativeMask;
// TODO: This is a mess and badly needs refactoring
for (int32 Y = 0; Y < SizeY; ++Y)
{
for (int32 X = 0; X < SizeX; ++X)
{
FVector LandscapeLocal = GizmoLocalToLandscape.TransformPosition(FVector(-W + X, -H + Y, 0));
const int32 LX = FMath::FloorToInt32(LandscapeLocal.X);
const int32 LY = FMath::FloorToInt32(LandscapeLocal.Y);
{
for (int32 i = -1; (!bApplyToAll && i < 0) || i < LayerNum; ++i)
{
// Don't try to copy data for null layers
if ((bApplyToAll && i >= 0 && !LandscapeInfo->Layers[i].LayerInfoObj) ||
(!bApplyToAll && (EdMode->CurrentToolTarget.TargetType != ELandscapeToolTargetType::Heightmap) && !EdMode->CurrentToolTarget.LayerInfo.Get()))
{
continue;
}
FGizmoPreData GizmoPreData[NeighborNum];
for (int32 LocalY = 0; LocalY < 2; ++LocalY)
{
for (int32 LocalX = 0; LocalX < 2; ++LocalX)
{
int32 x = FMath::Clamp(LX + LocalX, X1, X2);
int32 y = FMath::Clamp(LY + LocalY, Y1, Y2);
GizmoPreData[LocalX + LocalY * 2].Ratio = LandscapeInfo->SelectedRegion.FindRef(FIntPoint(x, y));
int32 index = (x - X1) + (y - Y1)*(1 + X2 - X1);
if (bApplyToAll)
{
if (i < 0)
{
GizmoPreData[LocalX + LocalY * 2].Data = Gizmo->GetNormalizedHeight(HeightData[index]);
}
else
{
GizmoPreData[LocalX + LocalY * 2].Data = WeightDatas[index*LayerNum + i];
}
}
else
{
typename ToolTarget::CacheClass::DataType OriginalValue = Data[index];
if (EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Heightmap)
{
GizmoPreData[LocalX + LocalY * 2].Data = Gizmo->GetNormalizedHeight(OriginalValue);
}
else
{
GizmoPreData[LocalX + LocalY * 2].Data = OriginalValue;
}
}
}
}
FGizmoPreData LerpedData;
const float FracX = static_cast<float>(LandscapeLocal.X - LX);
const float FracY = static_cast<float>(LandscapeLocal.Y - LY);
LerpedData.Ratio = bFullCopy ? 1.0f :
FMath::Lerp(
FMath::Lerp(GizmoPreData[0].Ratio, GizmoPreData[1].Ratio, FracX),
FMath::Lerp(GizmoPreData[2].Ratio, GizmoPreData[3].Ratio, FracX),
FracY
);
LerpedData.Data = FMath::Lerp(
FMath::Lerp(GizmoPreData[0].Data, GizmoPreData[1].Data, FracX),
FMath::Lerp(GizmoPreData[2].Data, GizmoPreData[3].Data, FracX),
FracY
);
if (!bDidCopy && LerpedData.Ratio > 0.0f)
{
bDidCopy = true;
}
if (LerpedData.Ratio > 0.0f)
{
// Added for LayerNames
if (bApplyToAll)
{
if (i >= 0)
{
LayerInfoSet.Add(LandscapeInfo->Layers[i].LayerInfoObj);
}
}
else
{
if (EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Weightmap)
{
LayerInfoSet.Add(EdMode->CurrentToolTarget.LayerInfo.Get());
}
}
FGizmoSelectData* GizmoSelectData = Gizmo->SelectedData.Find(FIntPoint(X, Y));
if (GizmoSelectData)
{
if (bApplyToAll)
{
if (i < 0)
{
GizmoSelectData->HeightData = LerpedData.Data;
}
else
{
GizmoSelectData->WeightDataMap.Add(LandscapeInfo->Layers[i].LayerInfoObj, LerpedData.Data);
}
}
else
{
if (EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Heightmap)
{
GizmoSelectData->HeightData = LerpedData.Data;
}
else
{
GizmoSelectData->WeightDataMap.Add(EdMode->CurrentToolTarget.LayerInfo.Get(), LerpedData.Data);
}
}
}
else
{
FGizmoSelectData NewData;
NewData.Ratio = LerpedData.Ratio;
if (bApplyToAll)
{
if (i < 0)
{
NewData.HeightData = LerpedData.Data;
}
else
{
NewData.WeightDataMap.Add(LandscapeInfo->Layers[i].LayerInfoObj, LerpedData.Data);
}
}
else
{
if (EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Heightmap)
{
NewData.HeightData = LerpedData.Data;
}
else
{
NewData.WeightDataMap.Add(EdMode->CurrentToolTarget.LayerInfo.Get(), LerpedData.Data);
}
}
Gizmo->SelectedData.Add(FIntPoint(X, Y), NewData);
}
}
}
}
}
}
if (bDidCopy)
{
if (!bApplyToAll)
{
if (EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Heightmap)
{
Gizmo->DataType = ELandscapeGizmoType(Gizmo->DataType | LGT_Height);
}
else
{
Gizmo->DataType = ELandscapeGizmoType(Gizmo->DataType | LGT_Weight);
}
}
else
{
if (LayerNum > 0)
{
Gizmo->DataType = ELandscapeGizmoType(Gizmo->DataType | LGT_Height);
Gizmo->DataType = ELandscapeGizmoType(Gizmo->DataType | LGT_Weight);
}
else
{
Gizmo->DataType = ELandscapeGizmoType(Gizmo->DataType | LGT_Height);
}
}
Gizmo->SampleData(SizeX, SizeY);
// Update LayerInfos
for (ULandscapeLayerInfoObject* LayerInfo : LayerInfoSet)
{
Gizmo->LayerInfos.Add(LayerInfo);
}
}
//// Clean up Ratio 0 regions... (That was for sampling...)
//for ( TMap<uint64, FGizmoSelectData>::TIterator It(Gizmo->SelectedData); It; ++It )
//{
// FGizmoSelectData& Data = It.Value();
// if (Data.Ratio <= 0.0f)
// {
// Gizmo->SelectedData.Remove(It.Key());
// }
//}
Gizmo->ExportToClipboard();
GEngine->BroadcastLevelActorListChanged();
}
}
protected:
typename ToolTarget::CacheClass Cache;
FLandscapeHeightCache HeightCache;
FLandscapeFullWeightCache WeightCache;
};
template<class ToolTarget>
class FLandscapeToolCopy : public FLandscapeToolBase<FLandscapeToolStrokeCopy<ToolTarget>>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokeCopy<ToolTarget>>;
public:
FLandscapeToolCopy(FEdModeLandscape* InEdMode)
: Super(InEdMode)
, BackupCurrentBrush(nullptr)
{
}
virtual const TCHAR* GetToolName() const override { return TEXT("Copy"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Copy", "Copy"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Copy_Message", "Copy and Paste allows you to copy terrain data from one area of your Landscape to another. Use the select tool in conjunction with the Copy gizmo to further refine your selection."); };
virtual void SetEditRenderType() override
{
GLandscapeEditRenderMode = ELandscapeEditRenderMode::Gizmo | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask);
GLandscapeEditRenderMode |= (this->EdMode && this->EdMode->CurrentToolTarget.LandscapeInfo.IsValid() && this->EdMode->CurrentToolTarget.LandscapeInfo->SelectedRegion.Num()) ? ELandscapeEditRenderMode::SelectRegion : ELandscapeEditRenderMode::SelectComponent;
}
virtual ELandscapeToolTargetTypeMask::Type GetSupportedTargetTypes() override
{
return ELandscapeToolTargetTypeMask::FromType(ToolTarget::TargetType);
}
virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& InTarget, const FVector& InHitLocation) override
{
this->EdMode->GizmoBrush->Tick(ViewportClient, 0.1f);
// horrible hack
// (but avoids duplicating the code from FLandscapeToolBase)
BackupCurrentBrush = this->EdMode->CurrentBrush;
this->EdMode->CurrentBrush = this->EdMode->GizmoBrush;
return Super::BeginTool(ViewportClient, InTarget, InHitLocation);
}
virtual void EndTool(FEditorViewportClient* ViewportClient) override
{
Super::EndTool(ViewportClient);
this->EdMode->CurrentBrush = BackupCurrentBrush;
}
protected:
FLandscapeBrush* BackupCurrentBrush;
};
//
// FLandscapeToolPaste
//
template<class ToolTarget>
class FLandscapeToolStrokePaste : public FLandscapeToolStrokeBase
{
using Super = FLandscapeToolStrokeBase;
public:
FLandscapeToolStrokePaste(FEdModeLandscape* InEdMode, FEditorViewportClient* InViewportClient, const FLandscapeToolTarget& InTarget)
: Super(InEdMode, InViewportClient, InTarget)
, Cache(InTarget)
, HeightCache(InTarget)
, WeightCache(InTarget)
{
}
void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray<FLandscapeToolInteractorPosition>& InteractorPositions)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokePaste_Apply);
//ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo;
ALandscapeGizmoActiveActor* Gizmo = EdMode->CurrentGizmoActor.Get();
// Cache and copy in Gizmo's region...
if (LandscapeInfo && Gizmo && Gizmo->GetRootComponent())
{
if (Gizmo->SelectedData.Num() == 0)
{
return;
}
// Automatically fill in any placeholder layers
// This gives a much better user experience when copying data to a newly created landscape
for (ULandscapeLayerInfoObject* LayerInfo : Gizmo->LayerInfos)
{
int32 LayerInfoIndex = LandscapeInfo->GetLayerInfoIndex(LayerInfo);
if (LayerInfoIndex == INDEX_NONE)
{
LayerInfoIndex = LandscapeInfo->GetLayerInfoIndex(LayerInfo->LayerName);
if (LayerInfoIndex != INDEX_NONE)
{
FLandscapeInfoLayerSettings& LayerSettings = LandscapeInfo->Layers[LayerInfoIndex];
if (LayerSettings.LayerInfoObj == nullptr)
{
LayerSettings.Owner = LandscapeInfo->GetLandscapeProxy(); // this isn't strictly accurate, but close enough
LayerSettings.LayerInfoObj = LayerInfo;
}
}
}
}
Gizmo->TargetLandscapeInfo = LandscapeInfo;
float ScaleXY = static_cast<float>(LandscapeInfo->DrawScale.X);
//LandscapeInfo->Modify();
// Get list of verts to update
FLandscapeBrushData BrushInfo = Brush->ApplyBrush(InteractorPositions);
if (!BrushInfo)
{
return;
}
int32 X1, Y1, X2, Y2;
BrushInfo.GetInclusiveBounds(X1, Y1, X2, Y2);
// Tablet pressure
float Pressure = (ViewportClient && ViewportClient->Viewport->IsPenActive()) ? ViewportClient->Viewport->GetTabletPressure() : 1.0f;
// expand the area by one vertex in each direction to ensure normals are calculated correctly
X1 -= 1;
Y1 -= 1;
X2 += 1;
Y2 += 1;
const bool bApplyToAll = EdMode->UISettings->bApplyToAllTargets;
const int32 LayerNum = Gizmo->LayerInfos.Num() > 0 ? LandscapeInfo->Layers.Num() : 0;
TArray<uint16> HeightData;
TArray<uint8> WeightDatas; // Weight*Layers...
TArray<typename ToolTarget::CacheClass::DataType> Data;
if (bApplyToAll)
{
HeightCache.CacheData(X1, Y1, X2, Y2);
HeightCache.GetCachedData(X1, Y1, X2, Y2, HeightData);
if (LayerNum > 0)
{
WeightCache.CacheData(X1, Y1, X2, Y2);
WeightCache.GetCachedData(X1, Y1, X2, Y2, WeightDatas, LayerNum);
}
}
else
{
Cache.CacheData(X1, Y1, X2, Y2);
Cache.GetCachedData(X1, Y1, X2, Y2, Data);
}
const float Width = Gizmo->GetWidth();
const float Height = Gizmo->GetHeight();
const float W = Gizmo->GetWidth() / (2 * ScaleXY);
const float H = Gizmo->GetHeight() / (2 * ScaleXY);
const FVector GizmoScale3D = Gizmo->GetRootComponent()->GetRelativeScale3D();
const float SignX = GizmoScale3D.X > 0.0f ? 1.0f : -1.0f;
const float SignY = GizmoScale3D.Y > 0.0f ? 1.0f : -1.0f;
const float ScaleX = Gizmo->CachedWidth / Width * ScaleXY / Gizmo->CachedScaleXY;
const float ScaleY = Gizmo->CachedHeight / Height * ScaleXY / Gizmo->CachedScaleXY;
FMatrix WToL = LandscapeInfo->GetLandscapeProxy()->LandscapeActorToWorld().ToMatrixWithScale().InverseFast();
//FMatrix LToW = Landscape->LocalToWorld();
FVector BaseLocation = WToL.TransformPosition(Gizmo->GetActorLocation());
//FMatrix LandscapeLocalToGizmo = FRotationTranslationMatrix(FRotator(0, Gizmo->Rotation.Yaw, 0), FVector(BaseLocation.X - W + 0.5, BaseLocation.Y - H + 0.5, 0));
FMatrix LandscapeToGizmoLocal =
(FTranslationMatrix(FVector((-W + 0.5)*SignX, (-H + 0.5)*SignY, 0)) * FScaleRotationTranslationMatrix(FVector(SignX, SignY, 1.0f), FRotator(0, Gizmo->GetActorRotation().Yaw, 0), FVector(BaseLocation.X, BaseLocation.Y, 0))).InverseFast();
for (int32 Y = BrushInfo.GetBounds().Min.Y; Y < BrushInfo.GetBounds().Max.Y; Y++)
{
const float* BrushScanline = BrushInfo.GetDataPtr(FIntPoint(0, Y));
for (int32 X = BrushInfo.GetBounds().Min.X; X < BrushInfo.GetBounds().Max.X; X++)
{
const float BrushValue = BrushScanline[X];
if (BrushValue > 0.0f)
{
// TODO: This is a mess and badly needs refactoring
// Value before we apply our painting
int32 index = (X - X1) + (Y - Y1)*(1 + X2 - X1);
float PaintAmount = (Brush->GetBrushType() == ELandscapeBrushType::Gizmo) ? BrushValue : BrushValue * EdMode->UISettings->GetCurrentToolStrength() * Pressure;
FVector GizmoLocal = LandscapeToGizmoLocal.TransformPosition(FVector(X, Y, 0));
GizmoLocal.X *= ScaleX * SignX;
GizmoLocal.Y *= ScaleY * SignY;
const int32 LX = FMath::FloorToInt32(GizmoLocal.X);
const int32 LY = FMath::FloorToInt32(GizmoLocal.Y);
const float FracX = static_cast<float>(GizmoLocal.X - LX);
const float FracY = static_cast<float>(GizmoLocal.Y - LY);
FGizmoSelectData* Data00 = Gizmo->SelectedData.Find(FIntPoint(LX, LY));
FGizmoSelectData* Data10 = Gizmo->SelectedData.Find(FIntPoint(LX + 1, LY));
FGizmoSelectData* Data01 = Gizmo->SelectedData.Find(FIntPoint(LX, LY + 1));
FGizmoSelectData* Data11 = Gizmo->SelectedData.Find(FIntPoint(LX + 1, LY + 1));
for (int32 i = -1; (!bApplyToAll && i < 0) || i < LayerNum; ++i)
{
if ((bApplyToAll && (i < 0)) || (!bApplyToAll && EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Heightmap))
{
float OriginalValue;
if (bApplyToAll)
{
OriginalValue = HeightData[index];
}
else
{
OriginalValue = Data[index];
}
float Value = LandscapeDataAccess::GetLocalHeight(static_cast<uint16>(OriginalValue));
float DestValue = FLandscapeHeightCache::ClampValue(
LandscapeDataAccess::GetTexHeight(
FMath::Lerp(
FMath::Lerp(Data00 ? FMath::Lerp(Value, Gizmo->GetLandscapeHeight(Data00->HeightData), Data00->Ratio) : Value,
Data10 ? FMath::Lerp(Value, Gizmo->GetLandscapeHeight(Data10->HeightData), Data10->Ratio) : Value, FracX),
FMath::Lerp(Data01 ? FMath::Lerp(Value, Gizmo->GetLandscapeHeight(Data01->HeightData), Data01->Ratio) : Value,
Data11 ? FMath::Lerp(Value, Gizmo->GetLandscapeHeight(Data11->HeightData), Data11->Ratio) : Value, FracX),
FracY
))
);
switch (EdMode->UISettings->PasteMode)
{
case ELandscapeToolPasteMode::Raise:
PaintAmount = OriginalValue < DestValue ? PaintAmount : 0.0f;
break;
case ELandscapeToolPasteMode::Lower:
PaintAmount = OriginalValue > DestValue ? PaintAmount : 0.0f;
break;
default:
break;
}
if (bApplyToAll)
{
HeightData[index] = static_cast<uint16>(FMath::Lerp(OriginalValue, DestValue, PaintAmount));
}
else
{
Data[index] = static_cast<uint16>(FMath::Lerp(OriginalValue, DestValue, PaintAmount));
}
}
else
{
ULandscapeLayerInfoObject* LayerInfo;
float OriginalValue;
if (bApplyToAll)
{
LayerInfo = LandscapeInfo->Layers[i].LayerInfoObj;
OriginalValue = WeightDatas[index*LayerNum + i];
}
else
{
LayerInfo = EdMode->CurrentToolTarget.LayerInfo.Get();
OriginalValue = Data[index];
}
float DestValue = FLandscapeAlphaCache::ClampValue(static_cast<int32>(
FMath::Lerp(
FMath::Lerp(Data00 ? FMath::Lerp(OriginalValue, Data00->WeightDataMap.FindRef(LayerInfo), Data00->Ratio) : OriginalValue,
Data10 ? FMath::Lerp(OriginalValue, Data10->WeightDataMap.FindRef(LayerInfo), Data10->Ratio) : OriginalValue, FracX),
FMath::Lerp(Data01 ? FMath::Lerp(OriginalValue, Data01->WeightDataMap.FindRef(LayerInfo), Data01->Ratio) : OriginalValue,
Data11 ? FMath::Lerp(OriginalValue, Data11->WeightDataMap.FindRef(LayerInfo), Data11->Ratio) : OriginalValue, FracX),
FracY
)));
if (bApplyToAll)
{
WeightDatas[index*LayerNum + i] = static_cast<uint8>(FMath::Lerp(OriginalValue, DestValue, PaintAmount));
}
else
{
Data[index] = static_cast<typename ToolTarget::CacheClass::DataType>(FMath::Lerp(OriginalValue, DestValue, PaintAmount));
}
}
}
}
}
}
for (ULandscapeLayerInfoObject* LayerInfo : Gizmo->LayerInfos)
{
if (LandscapeInfo->GetLayerInfoIndex(LayerInfo) != INDEX_NONE)
{
WeightCache.AddDirtyLayer(LayerInfo);
}
}
if (bApplyToAll)
{
HeightCache.SetCachedData(X1, Y1, X2, Y2, HeightData);
HeightCache.Flush();
if (WeightDatas.Num())
{
// Set the layer data, bypassing painting restrictions because it doesn't work well when altering multiple layers
WeightCache.SetCachedData(X1, Y1, X2, Y2, WeightDatas, LayerNum, ELandscapeLayerPaintingRestriction::None);
}
WeightCache.Flush();
}
else
{
Cache.SetCachedData(X1, Y1, X2, Y2, Data);
Cache.Flush();
}
// Dirty any runtime virtual textures that our landscape components write to.
LandscapeInfo->DirtyRuntimeVirtualTextureForLandscapeArea(X1, Y1, X2, Y2);
GEngine->BroadcastLevelActorListChanged();
}
}
protected:
typename ToolTarget::CacheClass Cache;
FLandscapeHeightCache HeightCache;
FLandscapeFullWeightCache WeightCache;
};
template<class ToolTarget>
class FLandscapeToolPaste : public FLandscapeToolBase<FLandscapeToolStrokePaste<ToolTarget>>
{
using Super = FLandscapeToolBase<FLandscapeToolStrokePaste<ToolTarget>>;
public:
FLandscapeToolPaste(FEdModeLandscape* InEdMode)
: Super(InEdMode)
, bUseGizmoRegion(false)
, BackupCurrentBrush(nullptr)
{
}
virtual const TCHAR* GetToolName() const override { return TEXT("Paste"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Region", "Region Copy/Paste"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Region_Message", "Copy and Paste allows you to copy terrain data from one area of your Landscape to another. Use the select tool in conjunction with the Copy gizmo to further refine your selection."); };
virtual void SetEditRenderType() override
{
GLandscapeEditRenderMode = ELandscapeEditRenderMode::Gizmo | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask);
GLandscapeEditRenderMode |= (this->EdMode && this->EdMode->CurrentToolTarget.LandscapeInfo.IsValid() && this->EdMode->CurrentToolTarget.LandscapeInfo->SelectedRegion.Num()) ? ELandscapeEditRenderMode::SelectRegion : ELandscapeEditRenderMode::SelectComponent;
}
void SetGizmoMode(bool InbUseGizmoRegion)
{
bUseGizmoRegion = InbUseGizmoRegion;
}
virtual ELandscapeToolTargetTypeMask::Type GetSupportedTargetTypes() override
{
return ELandscapeToolTargetTypeMask::FromType(ToolTarget::TargetType);
}
virtual ELandscapeLayerUpdateMode GetBeginToolContentUpdateFlag() const override { return ELandscapeLayerUpdateMode::Update_All_Editing; }
virtual ELandscapeLayerUpdateMode GetTickToolContentUpdateFlag() const override { return GetBeginToolContentUpdateFlag(); }
virtual ELandscapeLayerUpdateMode GetEndToolContentUpdateFlag() const override { return ELandscapeLayerUpdateMode::Update_All; }
virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& InTarget, const FVector& InHitLocation) override
{
this->EdMode->GizmoBrush->Tick(ViewportClient, 0.1f);
// horrible hack
// (but avoids duplicating the code from FLandscapeToolBase)
BackupCurrentBrush = this->EdMode->CurrentBrush;
if (bUseGizmoRegion)
{
this->EdMode->CurrentBrush = this->EdMode->GizmoBrush;
}
return Super::BeginTool(ViewportClient, InTarget, InHitLocation);
}
virtual void EndTool(FEditorViewportClient* ViewportClient) override
{
Super::EndTool(ViewportClient);
if (bUseGizmoRegion)
{
this->EdMode->CurrentBrush = BackupCurrentBrush;
}
check(this->EdMode->CurrentBrush == BackupCurrentBrush);
}
virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y) override
{
if (bUseGizmoRegion)
{
return true;
}
return FLandscapeToolBase<FLandscapeToolStrokePaste<ToolTarget>>::MouseMove(ViewportClient, Viewport, x, y);
}
protected:
bool bUseGizmoRegion;
FLandscapeBrush* BackupCurrentBrush;
};
//
// FLandscapeToolCopyPaste
//
template<class ToolTarget>
class FLandscapeToolCopyPaste : public FLandscapeToolPaste<ToolTarget>
{
public:
FLandscapeToolCopyPaste(FEdModeLandscape* InEdMode)
: FLandscapeToolPaste<ToolTarget>(InEdMode)
, CopyTool(InEdMode)
{
}
// Just hybrid of Copy and Paste tool
virtual const TCHAR* GetToolName() const override { return TEXT("CopyPaste"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Region", "Region Copy/Paste"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Region_Message", "Copy and Paste allows you to copy terrain data from one area of your Landscape to another. Use the select tool in conjunction with the Copy gizmo to further refine your selection."); };
virtual void EnterTool() override
{
// Make sure gizmo actor is selected
ALandscapeGizmoActiveActor* Gizmo = this->EdMode->CurrentGizmoActor.Get();
if (Gizmo)
{
Gizmo->bFollowTerrainHeight = false;
GEditor->SelectNone(false, true);
GEditor->SelectActor(Gizmo, true, true, true);
}
}
virtual void ExitTool() override
{
if (ALandscapeGizmoActiveActor* Gizmo = this->EdMode->CurrentGizmoActor.Get())
{
Gizmo->bFollowTerrainHeight = true;
}
}
// Copy tool doesn't use any view information, so just do it as one function
void Copy()
{
CopyTool.BeginTool(nullptr, this->EdMode->CurrentToolTarget, FVector::ZeroVector);
CopyTool.EndTool(nullptr);
}
void Paste()
{
this->SetGizmoMode(true);
this->BeginTool(nullptr, this->EdMode->CurrentToolTarget, FVector::ZeroVector);
this->EndTool(nullptr);
this->SetGizmoMode(false);
}
protected:
FLandscapeToolCopy<ToolTarget> CopyTool;
};
void FEdModeLandscape::CopyDataToGizmo()
{
// For Copy operation...
if (CopyPasteTool /*&& CopyPasteTool == CurrentTool*/)
{
CopyPasteTool->Copy();
}
if (CurrentGizmoActor.IsValid())
{
GEditor->SelectNone(false, true);
GEditor->SelectActor(CurrentGizmoActor.Get(), true, true, true);
}
}
void FEdModeLandscape::PasteDataFromGizmo()
{
// For Paste for Gizmo Region operation...
if (CopyPasteTool /*&& CopyPasteTool == CurrentTool*/)
{
CopyPasteTool->Paste();
}
if (CurrentGizmoActor.IsValid())
{
GEditor->SelectNone(false, true);
GEditor->SelectActor(CurrentGizmoActor.Get(), true, true, true);
}
}
namespace ELandscapeEdge
{
enum Type
{
None,
// Edges
X_Negative,
X_Positive,
Y_Negative,
Y_Positive,
// Corners
X_Negative_Y_Negative,
X_Positive_Y_Negative,
X_Negative_Y_Positive,
X_Positive_Y_Positive,
};
}
struct HNewLandscapeGrabHandleProxy : public HHitProxy
{
DECLARE_HIT_PROXY();
ELandscapeEdge::Type Edge;
HNewLandscapeGrabHandleProxy(ELandscapeEdge::Type InEdge) :
HHitProxy(HPP_Wireframe),
Edge(InEdge)
{
}
virtual EMouseCursor::Type GetMouseCursor() override
{
switch (Edge)
{
case ELandscapeEdge::X_Negative:
case ELandscapeEdge::X_Positive:
return EMouseCursor::ResizeLeftRight;
case ELandscapeEdge::Y_Negative:
case ELandscapeEdge::Y_Positive:
return EMouseCursor::ResizeUpDown;
case ELandscapeEdge::X_Negative_Y_Negative:
case ELandscapeEdge::X_Positive_Y_Positive:
return EMouseCursor::ResizeSouthEast;
case ELandscapeEdge::X_Negative_Y_Positive:
case ELandscapeEdge::X_Positive_Y_Negative:
return EMouseCursor::ResizeSouthWest;
}
return EMouseCursor::SlashedCircle;
}
};
IMPLEMENT_HIT_PROXY(HNewLandscapeGrabHandleProxy, HHitProxy)
//
// FLandscapeToolNewLandscape
//
class FLandscapeToolNewLandscape : public FLandscapeTool
{
public:
FEdModeLandscape* EdMode;
ENewLandscapePreviewMode NewLandscapePreviewMode;
ELandscapeEdge::Type DraggingEdge;
float DraggingEdge_Remainder;
FLandscapeToolNewLandscape(FEdModeLandscape* InEdMode)
: FLandscapeTool()
, EdMode(InEdMode)
, NewLandscapePreviewMode(ENewLandscapePreviewMode::NewLandscape)
, DraggingEdge(ELandscapeEdge::None)
, DraggingEdge_Remainder(0.0f)
{
}
virtual bool AffectsEditLayers() const { return false; }
virtual const TCHAR* GetToolName() const override { return TEXT("NewLandscape"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_NewLandscape", "New Landscape"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_NewLandscape_Message", "Create or import a new heightmap. Assign a material and configure the components. When you are ready to create your new Landscape, press the Create button in the lower-right corner of this panel. "); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::None | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
virtual void EnterTool() override
{
DraggingEdge = ELandscapeEdge::None;
DraggingEdge_Remainder = 0.0f;
EdMode->NewLandscapePreviewMode = NewLandscapePreviewMode;
EdMode->UISettings->RefreshImports();
}
virtual void ExitTool() override
{
NewLandscapePreviewMode = EdMode->NewLandscapePreviewMode;
EdMode->NewLandscapePreviewMode = ENewLandscapePreviewMode::None;
EdMode->UISettings->ClearImportLandscapeData();
}
virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& Target, const FVector& InHitLocation) override
{
// does nothing
return false;
}
virtual void EndTool(FEditorViewportClient* ViewportClient) override
{
// does nothing
}
virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y) override
{
// does nothing
return false;
}
virtual bool InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) override
{
if (Key == EKeys::LeftMouseButton)
{
// Press mouse button
if (Event == IE_Pressed && !IsAltDown(Viewport))
{
// See if we clicked on a new landscape handle..
int32 HitX = Viewport->GetMouseX();
int32 HitY = Viewport->GetMouseY();
HHitProxy* HitProxy = Viewport->GetHitProxy(HitX, HitY);
if (HitProxy)
{
if (HitProxy->IsA(HNewLandscapeGrabHandleProxy::StaticGetType()))
{
HNewLandscapeGrabHandleProxy* EdgeProxy = (HNewLandscapeGrabHandleProxy*)HitProxy;
DraggingEdge = EdgeProxy->Edge;
DraggingEdge_Remainder = 0;
return true;
}
}
}
else if (Event == IE_Released)
{
if (DraggingEdge)
{
DraggingEdge = ELandscapeEdge::None;
DraggingEdge_Remainder = 0;
return true;
}
}
}
return false;
}
virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override
{
ULandscapeEditorObject* UISettings = EdMode->UISettings;
if (InViewportClient->GetCurrentWidgetAxis() != EAxisList::None)
{
FVector DeltaScale = InScale;
DeltaScale.X = DeltaScale.Y = (FMath::Abs(InScale.X) > FMath::Abs(InScale.Y)) ? InScale.X : InScale.Y;
UISettings->Modify();
UISettings->NewLandscape_Location += InDrag;
UISettings->NewLandscape_Rotation += InRot;
UISettings->NewLandscape_Scale += DeltaScale;
return true;
}
else if (DraggingEdge != ELandscapeEdge::None)
{
FVector HitLocation;
EdMode->LandscapePlaneTrace(InViewportClient, FPlane(UISettings->NewLandscape_Location, FVector(0, 0, 1)), HitLocation);
FTransform Transform(UISettings->NewLandscape_Rotation, UISettings->NewLandscape_Location, UISettings->NewLandscape_Scale * UISettings->NewLandscape_QuadsPerSection * UISettings->NewLandscape_SectionsPerComponent);
HitLocation = Transform.InverseTransformPosition(HitLocation);
UISettings->Modify();
auto DragEdge = [&UISettings, &HitLocation, &Transform](const ELandscapeEdge::Type Edge)
{
int32& ComponentCount = Edge == ELandscapeEdge::X_Negative || Edge == ELandscapeEdge::X_Positive ? UISettings->NewLandscape_ComponentCount.X : UISettings->NewLandscape_ComponentCount.Y;
const float Hit = static_cast<float>(Edge == ELandscapeEdge::X_Negative || Edge == ELandscapeEdge::X_Positive ? HitLocation.X : HitLocation.Y);
const float PosOrNeg = Edge == ELandscapeEdge::X_Negative || Edge == ELandscapeEdge::Y_Negative ? -1.0f : 1.0f;
const FVector XOrY = Edge == ELandscapeEdge::X_Negative || Edge == ELandscapeEdge::X_Positive ? FVector(1, 0, 0) : FVector(0, 1, 0);
const int32 InitialComponentCount = ComponentCount;
const int32 Delta = FMath::RoundToInt(Hit - PosOrNeg * static_cast<float>(InitialComponentCount) / 2.0f);
ComponentCount = static_cast<int32>(InitialComponentCount + PosOrNeg * Delta);
UISettings->NewLandscape_ClampSize();
const float ActualDelta = static_cast<float>(ComponentCount - InitialComponentCount) / 2.0f;
UISettings->NewLandscape_Location += PosOrNeg * XOrY * Transform.TransformVector(FVector(ActualDelta, ActualDelta, 0));
};
if (DraggingEdge == ELandscapeEdge::X_Negative ||
DraggingEdge == ELandscapeEdge::X_Negative_Y_Negative ||
DraggingEdge == ELandscapeEdge::X_Negative_Y_Positive)
{
DragEdge(ELandscapeEdge::X_Negative);
}
if (DraggingEdge == ELandscapeEdge::X_Positive ||
DraggingEdge == ELandscapeEdge::X_Positive_Y_Negative ||
DraggingEdge == ELandscapeEdge::X_Positive_Y_Positive)
{
DragEdge(ELandscapeEdge::X_Positive);
}
if (DraggingEdge == ELandscapeEdge::Y_Negative ||
DraggingEdge == ELandscapeEdge::X_Negative_Y_Negative ||
DraggingEdge == ELandscapeEdge::X_Positive_Y_Negative)
{
DragEdge(ELandscapeEdge::Y_Negative);
}
if (DraggingEdge == ELandscapeEdge::Y_Positive ||
DraggingEdge == ELandscapeEdge::X_Negative_Y_Positive ||
DraggingEdge == ELandscapeEdge::X_Positive_Y_Positive)
{
DragEdge(ELandscapeEdge::Y_Positive);
}
return true;
}
return false;
}
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override
{
if (EdMode->NewLandscapePreviewMode != ENewLandscapePreviewMode::None)
{
static constexpr float CornerSize = 0.33f;
static constexpr FLinearColor CornerColor(1.0f, 0.5f, 0.0f);
static constexpr FLinearColor EdgeColor(1.0f, 1.0f, 0.0f);
static constexpr FLinearColor ComponentBorderColor(0.0f, 0.85f, 0.0f);
static constexpr FLinearColor SectionBorderColor(0.0f, 0.4f, 0.0f);
static constexpr FLinearColor InnerColor(0.0f, 0.25f, 0.0f);
const ELevelViewportType ViewportType = ((FEditorViewportClient*)Viewport->GetClient())->ViewportType;
const int32 ComponentCountX = EdMode->UISettings->NewLandscape_ComponentCount.X;
const int32 ComponentCountY = EdMode->UISettings->NewLandscape_ComponentCount.Y;
const int32 QuadsPerComponent = EdMode->UISettings->NewLandscape_SectionsPerComponent * EdMode->UISettings->NewLandscape_QuadsPerSection;
const float ComponentSize = static_cast<float>(QuadsPerComponent);
const int32 GridSize = EdMode->UISettings->WorldPartitionGridSize;
const FVector Offset = EdMode->UISettings->NewLandscape_Location + FTransform(EdMode->UISettings->NewLandscape_Rotation, FVector::ZeroVector, EdMode->UISettings->NewLandscape_Scale).TransformVector(FVector(-ComponentCountX * ComponentSize / 2, -ComponentCountY * ComponentSize / 2, 0));
const FTransform Transform = FTransform(EdMode->UISettings->NewLandscape_Rotation, Offset, EdMode->UISettings->NewLandscape_Scale);
using LineCoords = TTuple<FVector, FVector>;
auto DrawLine = [&PDI, &Transform](const LineCoords& AB, const FLinearColor& Color, const uint8 DepthPriorityGroup)
{
PDI->DrawLine(Transform.TransformPosition(AB.Get<0>()), Transform.TransformPosition(AB.Get<1>()), Color, DepthPriorityGroup);
};
auto DrawLineBorder = [&PDI, &DrawLine](const ELandscapeEdge::Type Edge, const LineCoords& AB, const FLinearColor& Color)
{
PDI->SetHitProxy(new HNewLandscapeGrabHandleProxy(Edge));
DrawLine(AB, Color, SDPG_Foreground);
};
if (EdMode->NewLandscapePreviewMode == ENewLandscapePreviewMode::ImportLandscape)
{
auto GetColor = [&](int32 ComponentIndex)
{
if (EdMode->IsGridBased() && (ComponentIndex % GridSize == 0))
{
return EdgeColor;
}
return ComponentBorderColor;
};
const TArray<uint16>& ImportHeights = EdMode->UISettings->GetImportLandscapeData();
if (ImportHeights.Num() != 0)
{
const int32 NumLinesForeground = ComponentCountX * ComponentCountY * 2 + ComponentCountX + ComponentCountY + 8;
PDI->AddReserveLines(SDPG_Foreground, NumLinesForeground);
const int32 SizeX = ComponentCountX * QuadsPerComponent + 1;
const int32 SizeY = ComponentCountY * QuadsPerComponent + 1;
const int32 ImportSizeX = EdMode->UISettings->ImportLandscape_Width;
const int32 ImportSizeY = EdMode->UISettings->ImportLandscape_Height;
const int32 OffsetX = (SizeX - ImportSizeX) / 2;
const int32 OffsetY = (SizeY - ImportSizeY) / 2;
// Get coordinates for a line in X direction
auto LineX = [QuadsPerComponent, ImportSizeX, ImportSizeY, &ImportHeights, OffsetX, OffsetY](int32 X, int32 Y)
{
X *= QuadsPerComponent;
const int32 Y0 = Y * QuadsPerComponent;
const int32 Y1 = (Y + 1) * QuadsPerComponent;
const int32 ImportX = FMath::Clamp<int32>(X - OffsetX, 0, ImportSizeX - 1);
const int32 ImportY0 = FMath::Clamp<int32>(Y0 - OffsetY, 0, ImportSizeY - 1);
const int32 ImportY1 = FMath::Clamp<int32>(Y1 - OffsetY, 0, ImportSizeY - 1);
const float Z0 = LandscapeDataAccess::GetLocalHeight(ImportHeights[ImportX + ImportY0 * ImportSizeX]);
const float Z1 = LandscapeDataAccess::GetLocalHeight(ImportHeights[ImportX + ImportY1 * ImportSizeX]);
return LineCoords{FVector(X, Y0, Z0), FVector(X, Y1, Z1)};
};
// Get coordinates for a line in Y direction
auto LineY = [QuadsPerComponent, ImportSizeX, ImportSizeY, &ImportHeights, OffsetX, OffsetY](int32 X, int32 Y)
{
Y *= QuadsPerComponent;
const int32 X0 = X * QuadsPerComponent;
const int32 X1 = (X + 1) * QuadsPerComponent;
const int32 ImportY = FMath::Clamp<int32>(Y - OffsetY, 0, ImportSizeY - 1);
const int32 ImportX0 = FMath::Clamp<int32>(X0 - OffsetX, 0, ImportSizeX - 1);
const int32 ImportX1 = FMath::Clamp<int32>(X1 - OffsetX, 0, ImportSizeX - 1);
const float Z0 = LandscapeDataAccess::GetLocalHeight(ImportHeights[ImportX0 + ImportY * ImportSizeX]);
const float Z1 = LandscapeDataAccess::GetLocalHeight(ImportHeights[ImportX1 + ImportY * ImportSizeX]);
return LineCoords{FVector(X0, Y, Z0), FVector(X1, Y, Z1)};
};
// Draw a border in X direction
auto DrawBorderX = [&PDI, ComponentCountY, &DrawLine, &DrawLineBorder, &LineX](
const int32 X, ELandscapeEdge::Type FirstCornerEdge, ELandscapeEdge::Type BorderEdge, ELandscapeEdge::Type LastCornerEdge)
{
const LineCoords FirstComponent = LineX(X, 0);
const LineCoords LastComponent = LineX(X, ComponentCountY - 1);
const FVector FirstCornerEnd = FirstComponent.Get<0>() + CornerSize * (FirstComponent.Get<1>() - FirstComponent.Get<0>());
const FVector LastCornerBegin = LastComponent.Get<1>() - CornerSize * (LastComponent.Get<1>() - LastComponent.Get<0>());
DrawLineBorder(FirstCornerEdge, {FirstComponent.Get<0>(), FirstCornerEnd}, CornerColor);
PDI->SetHitProxy(new HNewLandscapeGrabHandleProxy(BorderEdge));
if (ComponentCountY == 1)
{
DrawLine({FirstCornerEnd, LastCornerBegin}, EdgeColor, SDPG_Foreground);
}
else
{
DrawLine({FirstCornerEnd, FirstComponent.Get<1>()}, EdgeColor, SDPG_Foreground);
for (int32 Y = 1; Y < ComponentCountY - 1; ++Y)
{
DrawLine(LineX(X, Y), EdgeColor, SDPG_Foreground);
}
DrawLine({LastComponent.Get<0>(), LastCornerBegin}, EdgeColor, SDPG_Foreground);
}
DrawLineBorder(LastCornerEdge, {LastCornerBegin, LastComponent.Get<1>()}, CornerColor);
};
// Draw a border in Y direction
auto DrawBorderY = [&PDI, ComponentCountX, &DrawLine, &DrawLineBorder, &LineY](
const int32 Y, ELandscapeEdge::Type FirstCornerEdge, ELandscapeEdge::Type BorderEdge, ELandscapeEdge::Type LastCornerEdge)
{
const LineCoords FirstComponent = LineY(0, Y);
const LineCoords LastComponent = LineY(ComponentCountX - 1, Y);
const FVector FirstCornerEnd = FirstComponent.Get<0>() + CornerSize * (FirstComponent.Get<1>() - FirstComponent.Get<0>());
const FVector LastCornerBegin = LastComponent.Get<1>() - CornerSize * (LastComponent.Get<1>() - LastComponent.Get<0>());
DrawLineBorder(FirstCornerEdge, {FirstComponent.Get<0>(), FirstCornerEnd}, CornerColor);
PDI->SetHitProxy(new HNewLandscapeGrabHandleProxy(BorderEdge));
if (ComponentCountX == 1)
{
DrawLine({FirstCornerEnd, LastCornerBegin}, EdgeColor, SDPG_Foreground);
}
else
{
DrawLine({FirstCornerEnd, FirstComponent.Get<1>()}, EdgeColor, SDPG_Foreground);
for (int32 X = 1; X < ComponentCountX - 1; ++X)
{
DrawLine(LineY(X, Y), EdgeColor, SDPG_Foreground);
}
DrawLine({LastComponent.Get<0>(), LastCornerBegin}, EdgeColor, SDPG_Foreground);
}
DrawLineBorder(LastCornerEdge, {LastCornerBegin, LastComponent.Get<1>()}, CornerColor);
};
// Left border
DrawBorderX(0, ELandscapeEdge::X_Negative_Y_Negative, ELandscapeEdge::X_Negative, ELandscapeEdge::X_Negative_Y_Positive);
// Right border
DrawBorderX(ComponentCountX, ELandscapeEdge::X_Positive_Y_Negative, ELandscapeEdge::X_Positive, ELandscapeEdge::X_Positive_Y_Positive);
// Bottom border
DrawBorderY(0, ELandscapeEdge::X_Negative_Y_Negative, ELandscapeEdge::Y_Negative, ELandscapeEdge::X_Positive_Y_Negative);
// Top border
DrawBorderY(ComponentCountY, ELandscapeEdge::X_Negative_Y_Positive, ELandscapeEdge::Y_Positive, ELandscapeEdge::X_Positive_Y_Positive);
// Reset mouse cursor after all border are drawn
PDI->SetHitProxy(nullptr);
// Left to right
for (int32 X = 1; X < ComponentCountX; ++X)
{
const FLinearColor Color = GetColor(X);
for (int32 Y = 0; Y < ComponentCountY; ++Y)
{
DrawLine(LineX(X, Y), Color, SDPG_Foreground);
}
}
// Bottom to top
for (int32 Y = 1; Y < ComponentCountY; ++Y)
{
const FLinearColor Color = GetColor(Y);
for (int32 X = 0; X < ComponentCountX; ++X)
{
DrawLine(LineY(X, Y), Color, SDPG_Foreground);
}
}
}
}
else // EdMode->NewLandscapePreviewMode == ENewLandscapePreviewMode::NewLandscape
{
auto GetColor = [&](int32 QuadIndex)
{
if (EdMode->IsGridBased() && (QuadIndex % (GridSize * QuadsPerComponent) == 0))
{
return EdgeColor;
}
if (QuadIndex % QuadsPerComponent == 0)
{
return ComponentBorderColor;
}
if (QuadIndex % EdMode->UISettings->NewLandscape_QuadsPerSection == 0)
{
return SectionBorderColor;
}
return InnerColor;
};
if (ViewportType == LVT_Perspective || ViewportType == LVT_OrthoXY || ViewportType == LVT_OrthoNegativeXY)
{
const int32 NumLines = ComponentCountX * QuadsPerComponent + 1 + ComponentCountY * QuadsPerComponent + 1;
const int32 NumLinesForeground = ComponentCountX + 1 + ComponentCountY + 1;
const int32 NumLinesWorld = NumLines - NumLinesForeground;
constexpr int32 NumLinesForegroundCorners = 8;
PDI->AddReserveLines(SDPG_Foreground, NumLinesForeground + NumLinesForegroundCorners);
PDI->AddReserveLines(SDPG_World, NumLinesWorld);
// Draw a border in X direction
auto DrawBorderX = [ComponentSize, ComponentCountY, &DrawLineBorder](
const int32 X, ELandscapeEdge::Type FirstCornerEdge, ELandscapeEdge::Type BorderEdge, ELandscapeEdge::Type LastCornerEdge)
{
DrawLineBorder(FirstCornerEdge, {FVector(X, 0, 0), FVector(X, CornerSize * ComponentSize, 0)}, CornerColor);
DrawLineBorder(BorderEdge, {FVector(X, CornerSize * ComponentSize, 0), FVector(X, (ComponentCountY - CornerSize) * ComponentSize, 0)}, EdgeColor);
DrawLineBorder(LastCornerEdge, {FVector(X, (ComponentCountY - CornerSize) * ComponentSize, 0), FVector(X, ComponentCountY * ComponentSize, 0)}, CornerColor);
};
// Draw a border in Y direction
auto DrawBorderY = [ComponentSize, ComponentCountX, &DrawLineBorder](
const int32 Y, ELandscapeEdge::Type FirstCornerEdge, ELandscapeEdge::Type BorderEdge, ELandscapeEdge::Type LastCornerEdge)
{
DrawLineBorder(FirstCornerEdge, {FVector(0, Y, 0), FVector(CornerSize * ComponentSize, Y, 0)}, CornerColor);
DrawLineBorder(BorderEdge, {FVector(CornerSize * ComponentSize, Y, 0), FVector((ComponentCountX - CornerSize) * ComponentSize, Y, 0)}, EdgeColor);
DrawLineBorder(LastCornerEdge, {FVector((ComponentCountX - CornerSize) * ComponentSize, Y, 0), FVector(ComponentCountX * ComponentSize, Y, 0)}, CornerColor);
};
// Left border
DrawBorderX(0, ELandscapeEdge::X_Negative_Y_Negative, ELandscapeEdge::X_Negative, ELandscapeEdge::X_Negative_Y_Positive);
// Right border
DrawBorderX(ComponentCountX * QuadsPerComponent, ELandscapeEdge::X_Positive_Y_Negative, ELandscapeEdge::X_Positive, ELandscapeEdge::X_Positive_Y_Positive);
// Bottom border
DrawBorderY(0, ELandscapeEdge::X_Negative_Y_Negative, ELandscapeEdge::Y_Negative, ELandscapeEdge::X_Positive_Y_Negative);
// Top border
DrawBorderY(ComponentCountY * QuadsPerComponent, ELandscapeEdge::X_Negative_Y_Positive, ELandscapeEdge::Y_Positive, ELandscapeEdge::X_Positive_Y_Positive);
// Reset mouse cursor after all border are drawn
PDI->SetHitProxy(nullptr);
// Left to right
for (int32 X = 1; X < ComponentCountX * QuadsPerComponent; ++X)
{
const FLinearColor CurrentColor = GetColor(X);
const uint8 DepthPriority = static_cast<uint8>(CurrentColor == InnerColor ? SDPG_World : SDPG_Foreground);
DrawLine({FVector(X, 0, 0), FVector(X, ComponentCountY * ComponentSize, 0)}, CurrentColor, DepthPriority);
}
// Bottom to top
for (int32 Y = 1; Y < ComponentCountY * QuadsPerComponent; ++Y)
{
const FLinearColor CurrentColor = GetColor(Y);
const uint8 DepthPriority = static_cast<uint8>(CurrentColor == InnerColor ? SDPG_World : SDPG_Foreground);
DrawLine({FVector(0, Y, 0), FVector(ComponentCountX * ComponentSize, Y, 0)}, CurrentColor, DepthPriority);
}
}
else
{
// Don't allow dragging to resize in side-view, and there is no point drawing the inner lines as only the outer are visible.
if (ViewportType == LVT_OrthoXZ || ViewportType == LVT_OrthoNegativeXZ)
{
DrawLine({FVector(0, 0, 0), FVector(ComponentCountX * ComponentSize, 0, 0)}, EdgeColor, SDPG_World);
DrawLine({FVector(0, ComponentCountY * QuadsPerComponent, 0), FVector(ComponentCountX * ComponentSize, ComponentCountY * QuadsPerComponent, 0)}, EdgeColor, SDPG_World);
}
if (ViewportType == LVT_OrthoYZ || ViewportType == LVT_OrthoNegativeYZ)
{
DrawLine({FVector(0, 0, 0), FVector(0, ComponentCountY * ComponentSize, 0)}, EdgeColor, SDPG_World);
DrawLine({FVector(ComponentCountX * QuadsPerComponent, 0, 0), FVector(ComponentCountX * QuadsPerComponent, ComponentCountY * ComponentSize, 0)}, EdgeColor, SDPG_World);
}
}
}
}
}
virtual int32 GetToolActionResolutionDelta() const override
{
if (EdMode != nullptr)
{
int32 NewLandscapeResolutionX = EdMode->GetNewLandscapeResolutionX();
int32 NewLandscapeResolutionY = EdMode->GetNewLandscapeResolutionY();
NewLandscapeResolutionX = (NewLandscapeResolutionX > 0) ? NewLandscapeResolutionX : 1;
NewLandscapeResolutionY = (NewLandscapeResolutionY > 0) ? NewLandscapeResolutionY : 1;
return NewLandscapeResolutionX * NewLandscapeResolutionY;
}
return 0;
}
};
//
// FLandscapeToolResizeLandscape
//
class FLandscapeToolResizeLandscape : public FLandscapeTool
{
public:
FEdModeLandscape* EdMode;
FLandscapeToolResizeLandscape(FEdModeLandscape* InEdMode)
: FLandscapeTool()
, EdMode(InEdMode)
{
}
virtual bool AffectsEditLayers() const { return false; }
virtual const TCHAR* GetToolName() const override { return TEXT("ResizeLandscape"); }
virtual FText GetDisplayName() const override { return LOCTEXT("LandscapeMode_ResizeLandscape", "Change Landscape Component Size"); };
virtual FText GetDisplayMessage() const override { return LOCTEXT("LandscapeMode_ResizeLandscape_Message", "Change Landscape Component Size"); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::None | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
virtual void EnterTool() override
{
if (ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get())
{
const int32 ComponentSizeQuads = LandscapeInfo->ComponentSizeQuads;
int32 MinX, MinY, MaxX, MaxY;
if (EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeExtent(MinX, MinY, MaxX, MaxY))
{
EdMode->UISettings->ResizeLandscape_Original_ComponentCount.X = (MaxX - MinX) / ComponentSizeQuads;
EdMode->UISettings->ResizeLandscape_Original_ComponentCount.Y = (MaxY - MinY) / ComponentSizeQuads;
EdMode->UISettings->ResizeLandscape_ComponentCount = EdMode->UISettings->ResizeLandscape_Original_ComponentCount;
}
else
{
EdMode->UISettings->ResizeLandscape_Original_ComponentCount = FIntPoint::ZeroValue;
EdMode->UISettings->ResizeLandscape_ComponentCount = FIntPoint::ZeroValue;
}
EdMode->UISettings->ResizeLandscape_Original_QuadsPerSection = EdMode->CurrentToolTarget.LandscapeInfo->SubsectionSizeQuads;
EdMode->UISettings->ResizeLandscape_Original_SectionsPerComponent = EdMode->CurrentToolTarget.LandscapeInfo->ComponentNumSubsections;
EdMode->UISettings->ResizeLandscape_QuadsPerSection = EdMode->UISettings->ResizeLandscape_Original_QuadsPerSection;
EdMode->UISettings->ResizeLandscape_SectionsPerComponent = EdMode->UISettings->ResizeLandscape_Original_SectionsPerComponent;
}
}
virtual void ExitTool() override
{
}
virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& Target, const FVector& InHitLocation) override
{
// does nothing
return false;
}
virtual void EndTool(FEditorViewportClient* ViewportClient) override
{
// does nothing
}
virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y) override
{
// does nothing
return false;
}
};
class FLandscapeToolImportExport : public FLandscapeTool
{
public:
FEdModeLandscape* EdMode;
FVector MinLocation;
FIntRect LandscapeExtent;
FLandscapeToolImportExport(FEdModeLandscape* InEdMode)
: FLandscapeTool()
, EdMode(InEdMode)
{
}
virtual const TCHAR* GetToolName() const override { return TEXT("ImportExport"); }
virtual FText GetDisplayName() const override { return LOCTEXT("LandscapeMode_ImportExport", "Import Export"); };
virtual FText GetDisplayMessage() const override { return LOCTEXT("LandscapeMode_ImportExport_Message", "Import/Export Landscape Data"); };
virtual bool UsesTransformWidget() const { return true; }
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::SelectComponent | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
virtual FVector GetWidgetLocation() const override
{
if (EdMode->UISettings->ImportType != ELandscapeImportTransformType::Resample)
{
if (ALandscapeGizmoActiveActor* GizmoActor = EdMode->CurrentGizmoActor.Get())
{
return EdMode->CurrentGizmoActor->GetActorLocation();
}
}
return MinLocation;
}
virtual FMatrix GetWidgetRotation() const override
{
return FMatrix::Identity;
}
virtual void EnterTool() override
{
if (EdMode)
{
EdMode->UISettings->RefreshImportLayersList(/* bRefreshFromTarget */true);
EdMode->UISettings->RefreshImports();
if (ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get())
{
MinLocation = FVector(0, 0, 0);
if (LandscapeInfo->GetLandscapeExtent(LandscapeExtent))
{
MinLocation = LandscapeInfo->GetLandscapeProxy()->LandscapeActorToWorld().TransformPosition(FVector(LandscapeExtent.Min.X, LandscapeExtent.Min.Y, 0.0f));
}
else
{
LandscapeExtent = FIntRect(0, 0, 0, 0);
}
FVector LocalPosition(EdMode->UISettings->ImportLandscape_GizmoLocalPosition.X, EdMode->UISettings->ImportLandscape_GizmoLocalPosition.Y, 0.0f);
FVector GizmoPosition = LandscapeInfo->GetLandscapeProxy()->LandscapeActorToWorld().TransformPosition(LocalPosition);
if (ALandscapeGizmoActiveActor* GizmoActor = EdMode->CurrentGizmoActor.Get())
{
GizmoActor->SetActorLocation(GizmoPosition);
}
}
}
}
virtual void ExitTool() override
{
}
virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& Target, const FVector& InHitLocation) override
{
// does nothing
return false;
}
virtual void EndTool(FEditorViewportClient* ViewportClient) override
{
// does nothing
}
virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y) override
{
// does nothing
return false;
}
virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale)
{
if (InViewportClient->GetCurrentWidgetAxis() != EAxisList::None)
{
// Resample Gizmo can't move
if (EdMode && EdMode->UISettings->ImportType != ELandscapeImportTransformType::Resample)
{
if (ALandscapeGizmoActiveActor* GizmoActor = EdMode->CurrentGizmoActor.Get())
{
GEditor->ApplyDeltaToActor(
GizmoActor,
true,
&InDrag,
nullptr,
nullptr,
InViewportClient->IsAltPressed(),
InViewportClient->IsShiftPressed(),
InViewportClient->IsCtrlPressed());
return true;
}
}
}
return false;
}
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override
{
ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get();
if (!LandscapeInfo)
{
return;
}
static const float CornerSize = 0.33f;
static const FLinearColor CornerColor(1.0f, 1.0f, 0.5f);
static const FLinearColor EdgeColor(1.0f, 1.0f, 0.0f);
static const FLinearColor ComponentBorderColor(0.0f, 0.85f, 0.0f);
static const FLinearColor SectionBorderColor(0.0f, 0.4f, 0.0f);
static const FLinearColor ComponentOutsideColor(0.0f, 0.0f, 0.85f);
static const FLinearColor InnerColor(0.0f, 0.25f, 0.0f);
const ELevelViewportType ViewportType = ((FEditorViewportClient*)Viewport->GetClient())->ViewportType;
const int32 ComponentSizeInt = EdMode->UISettings->NewLandscape_SectionsPerComponent * EdMode->UISettings->NewLandscape_QuadsPerSection;
const float ComponentSize = static_cast<float>(ComponentSizeInt);
const FTransform GizmoTransform = FTransform(FRotator(0,0,0), GetWidgetLocation(), LandscapeInfo->DrawScale);
const int32 Height = EdMode->UISettings->ImportType == ELandscapeImportTransformType::None ? EdMode->UISettings->ImportLandscape_Height : (LandscapeExtent.Height()+1);
const int32 Width = EdMode->UISettings->ImportType == ELandscapeImportTransformType::None ? EdMode->UISettings->ImportLandscape_Width: (LandscapeExtent.Width()+1);
const int32 ImportHeight = EdMode->UISettings->ImportType != ELandscapeImportTransformType::Resample ? EdMode->UISettings->ImportLandscape_Height : (LandscapeExtent.Height() + 1);
const int32 ImportWidth = EdMode->UISettings->ImportType != ELandscapeImportTransformType::Resample ? EdMode->UISettings->ImportLandscape_Height : (LandscapeExtent.Height() + 1);
const FTransform LandscapeTransfo = FTransform(FRotator(0, 0, 0), MinLocation, LandscapeInfo->DrawScale);
if (EdMode->ImportExportMode == EImportExportMode::Import)
{
if (ViewportType == LVT_Perspective || ViewportType == LVT_OrthoXY || ViewportType == LVT_OrthoNegativeXY)
{
for (int32 x = 0; x < ImportWidth; x++)
{
if (x == 0)
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, 0, 0)), GizmoTransform.TransformPosition(FVector(x, CornerSize * ComponentSize, 0)), CornerColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, CornerSize * ComponentSize, 0)), GizmoTransform.TransformPosition(FVector(x, ImportHeight - (CornerSize * ComponentSize), 0)), EdgeColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, ImportHeight - (CornerSize * ComponentSize), 0)), GizmoTransform.TransformPosition(FVector(x, ImportHeight, 0)), CornerColor, SDPG_Foreground);
}
else if (x == (ImportWidth - 1))
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, 0, 0)), GizmoTransform.TransformPosition(FVector(x, CornerSize * ComponentSize, 0)), CornerColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, CornerSize * ComponentSize, 0)), GizmoTransform.TransformPosition(FVector(x, ImportHeight - (CornerSize * ComponentSize), 0)), EdgeColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, ImportHeight - (CornerSize * ComponentSize), 0)), GizmoTransform.TransformPosition(FVector(x, ImportHeight, 0)), CornerColor, SDPG_Foreground);
}
else if ((x % ComponentSizeInt) == 0)
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, 0, 0)), GizmoTransform.TransformPosition(FVector(x, ImportHeight, 0)), ComponentBorderColor, SDPG_Foreground);
}
else if ((x % EdMode->UISettings->NewLandscape_QuadsPerSection) == 0)
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(x, 0, 0)), GizmoTransform.TransformPosition(FVector(x, ImportHeight, 0)), SectionBorderColor, SDPG_Foreground);
}
}
if (ImportWidth != Width)
{
for (int32 x = 0; x < Width; x++)
{
if ((x % ComponentSizeInt) == 0)
{
PDI->DrawLine(LandscapeTransfo.TransformPosition(FVector(x, 0, 0)), LandscapeTransfo.TransformPosition(FVector(x, Height, 0)), ComponentOutsideColor, SDPG_Foreground);
}
}
}
}
else
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(0, 0, 0)), GizmoTransform.TransformPosition(FVector(0, ImportHeight, 0)), EdgeColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(ImportWidth, 0, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth, ImportHeight, 0)), EdgeColor, SDPG_Foreground);
}
if (ViewportType == LVT_Perspective || ViewportType == LVT_OrthoXY || ViewportType == LVT_OrthoNegativeXY)
{
for (int32 y = 0; y < ImportHeight; y++)
{
if (y == 0)
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(0, y, 0)), GizmoTransform.TransformPosition(FVector(CornerSize * ComponentSize, y, 0)), CornerColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(CornerSize * ComponentSize, y, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth - (CornerSize * ComponentSize), y, 0)), EdgeColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(ImportWidth - (CornerSize * ComponentSize), y, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth, y, 0)), CornerColor, SDPG_Foreground);
}
else if (y == (ImportHeight - 1))
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(0, y, 0)), GizmoTransform.TransformPosition(FVector(CornerSize * ComponentSize, y, 0)), CornerColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(CornerSize * ComponentSize, y, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth - (CornerSize * ComponentSize), y, 0)), EdgeColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(ImportWidth - (CornerSize * ComponentSize), y, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth, y, 0)), CornerColor, SDPG_Foreground);
}
else if ((y % ComponentSizeInt) == 0)
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(0, y, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth, y, 0)), ComponentBorderColor, SDPG_Foreground);
}
else if ((y % EdMode->UISettings->NewLandscape_QuadsPerSection) == 0)
{
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(0, y, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth, y, 0)), SectionBorderColor, SDPG_Foreground);
}
}
if (ImportHeight != Height)
{
for (int32 y = 0; y < Height; y++)
{
if ((y % ComponentSizeInt) == 0)
{
PDI->DrawLine(LandscapeTransfo.TransformPosition(FVector(0, y, 0)), LandscapeTransfo.TransformPosition(FVector(Width, y, 0)), ComponentOutsideColor, SDPG_Foreground);
}
}
}
}
else
{
// and there's no point drawing the inner lines as only the outer is visible
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(0, 0, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth, 0, 0)), EdgeColor, SDPG_Foreground);
PDI->DrawLine(GizmoTransform.TransformPosition(FVector(0, ImportHeight, 0)), GizmoTransform.TransformPosition(FVector(ImportWidth, ImportHeight, 0)), EdgeColor, SDPG_Foreground);
}
}
}
virtual EAxisList::Type GetWidgetAxisToDraw(UE::Widget::EWidgetMode InWidgetMode) const override
{
return EAxisList::XY;
}
};
//////////////////////////////////////////////////////////////////////////
void FEdModeLandscape::InitializeTool_NewLandscape()
{
auto Tool_NewLandscape = MakeUnique<FLandscapeToolNewLandscape>(this);
Tool_NewLandscape->ValidBrushes.Add("BrushSet_Dummy");
LandscapeTools.Add(MoveTemp(Tool_NewLandscape));
}
void FEdModeLandscape::InitializeTool_ResizeLandscape()
{
auto Tool_ResizeLandscape = MakeUnique<FLandscapeToolResizeLandscape>(this);
Tool_ResizeLandscape->ValidBrushes.Add("BrushSet_Dummy");
LandscapeTools.Add(MoveTemp(Tool_ResizeLandscape));
}
void FEdModeLandscape::InitializeTool_ImportExport()
{
auto Tool_ImportExportLandscape = MakeUnique<FLandscapeToolImportExport>(this);
Tool_ImportExportLandscape->ValidBrushes.Add("BrushSet_Dummy");
LandscapeTools.Add(MoveTemp(Tool_ImportExportLandscape));
}
void FEdModeLandscape::InitializeTool_Select()
{
auto Tool_Select = MakeUnique<FLandscapeToolSelect>(this);
Tool_Select->ValidBrushes.Add("BrushSet_Component");
LandscapeTools.Add(MoveTemp(Tool_Select));
}
void FEdModeLandscape::InitializeTool_AddComponent()
{
auto Tool_AddComponent = MakeUnique<FLandscapeToolAddComponent>(this);
Tool_AddComponent->ValidBrushes.Add("BrushSet_Component");
LandscapeTools.Add(MoveTemp(Tool_AddComponent));
}
void FEdModeLandscape::InitializeTool_DeleteComponent()
{
auto Tool_DeleteComponent = MakeUnique<FLandscapeToolDeleteComponent>(this);
Tool_DeleteComponent->ValidBrushes.Add("BrushSet_Component");
LandscapeTools.Add(MoveTemp(Tool_DeleteComponent));
}
void FEdModeLandscape::InitializeTool_MoveToLevel()
{
auto Tool_MoveToLevel = MakeUnique<FLandscapeToolMoveToLevel>(this);
Tool_MoveToLevel->ValidBrushes.Add("BrushSet_Component");
LandscapeTools.Add(MoveTemp(Tool_MoveToLevel));
}
void FEdModeLandscape::InitializeTool_Mask()
{
auto Tool_Mask = MakeUnique<FLandscapeToolMask>(this);
Tool_Mask->ValidBrushes.Add("BrushSet_Circle");
Tool_Mask->ValidBrushes.Add("BrushSet_Alpha");
Tool_Mask->ValidBrushes.Add("BrushSet_Pattern");
LandscapeTools.Add(MoveTemp(Tool_Mask));
}
void FEdModeLandscape::InitializeTool_CopyPaste()
{
auto Tool_CopyPaste_Heightmap = MakeUnique<FLandscapeToolCopyPaste<FHeightmapToolTarget>>(this);
Tool_CopyPaste_Heightmap->ValidBrushes.Add("BrushSet_Circle");
Tool_CopyPaste_Heightmap->ValidBrushes.Add("BrushSet_Alpha");
Tool_CopyPaste_Heightmap->ValidBrushes.Add("BrushSet_Pattern");
Tool_CopyPaste_Heightmap->ValidBrushes.Add("BrushSet_Gizmo");
CopyPasteTool = Tool_CopyPaste_Heightmap.Get();
LandscapeTools.Add(MoveTemp(Tool_CopyPaste_Heightmap));
//auto Tool_CopyPaste_Weightmap = MakeUnique<FLandscapeToolCopyPaste<FWeightmapToolTarget>>(this);
//Tool_CopyPaste_Weightmap->ValidBrushes.Add("BrushSet_Circle");
//Tool_CopyPaste_Weightmap->ValidBrushes.Add("BrushSet_Alpha");
//Tool_CopyPaste_Weightmap->ValidBrushes.Add("BrushSet_Pattern");
//Tool_CopyPaste_Weightmap->ValidBrushes.Add("BrushSet_Gizmo");
//LandscapeTools.Add(MoveTemp(Tool_CopyPaste_Weightmap));
}
void FEdModeLandscape::InitializeTool_Visibility()
{
auto Tool_Visibility = MakeUnique<FLandscapeToolVisibility>(this);
Tool_Visibility->ValidBrushes.Add("BrushSet_Circle");
Tool_Visibility->ValidBrushes.Add("BrushSet_Alpha");
Tool_Visibility->ValidBrushes.Add("BrushSet_Pattern");
LandscapeTools.Add(MoveTemp(Tool_Visibility));
}
#undef LOCTEXT_NAMESPACE