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

676 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "InputCoreTypes.h"
#include "HitProxies.h"
#include "AI/NavigationSystemBase.h"
#include "Editor/UnrealEdEngine.h"
#include "Engine/Texture2D.h"
#include "UnrealWidgetFwd.h"
#include "EditorModeManager.h"
#include "UnrealEdGlobals.h"
#include "EditorViewportClient.h"
#include "LandscapeToolInterface.h"
#include "LandscapeEdMode.h"
#include "LandscapeEditorObject.h"
#include "ScopedTransaction.h"
#include "LandscapeEdit.h"
#include "LandscapeRender.h"
#include "LandscapeDataAccess.h"
#include "LandscapeHeightfieldCollisionComponent.h"
#include "Raster.h"
#include "Landscape.h"
#include "Misc/MessageDialog.h"
#include "LandscapeEdModeTools.h"
#include "TextureResource.h"
#define LOCTEXT_NAMESPACE "Landscape"
class FLandscapeRampToolHeightRasterPolicy
{
public:
// X = Side Falloff Alpha, Y = Height
typedef FVector2D InterpolantType;
/** Initialization constructor. */
FLandscapeRampToolHeightRasterPolicy(TArray<uint16>& InData, int32 InMinX, int32 InMinY, int32 InMaxX, int32 InMaxY, bool InbRaiseTerrain, bool InbLowerTerrain) :
Data(InData),
MinX(InMinX),
MinY(InMinY),
MaxX(InMaxX),
MaxY(InMaxY),
bRaiseTerrain(InbRaiseTerrain),
bLowerTerrain(InbLowerTerrain)
{
}
protected:
// FTriangleRasterizer policy interface.
int32 GetMinX() const { return MinX; }
int32 GetMaxX() const { return MaxX; }
int32 GetMinY() const { return MinY; }
int32 GetMaxY() const { return MaxY; }
void ProcessPixel(int32 X, int32 Y, const InterpolantType& Interpolant, bool BackFacing)
{
const float CosInterpX = static_cast<float>(Interpolant.X >= 1 ? 1 : 0.5 - 0.5 * FMath::Cos(Interpolant.X * PI));
const float Alpha = CosInterpX;
uint16& Dest = Data[(Y - MinY)*(1 + MaxX - MinX) + X - MinX];
float Value = FMath::Lerp((float)Dest, (float)Interpolant.Y, Alpha);
uint16 DValue = static_cast<uint16>(FMath::Clamp<float>(Value, 0, LandscapeDataAccess::MaxValue));
if ((bRaiseTerrain && DValue > Dest) ||
(bLowerTerrain && DValue < Dest))
{
Dest = DValue;
}
}
private:
TArray<uint16>& Data;
int32 MinX, MinY, MaxX, MaxY;
uint32 bRaiseTerrain : 1, bLowerTerrain : 1;
};
class HLandscapeRampToolPointHitProxy : public HHitProxy
{
DECLARE_HIT_PROXY();
int8 Point;
HLandscapeRampToolPointHitProxy(int8 InPoint) :
HHitProxy(HPP_Foreground),
Point(InPoint)
{
}
virtual EMouseCursor::Type GetMouseCursor() override
{
return EMouseCursor::Crosshairs;
}
};
IMPLEMENT_HIT_PROXY(HLandscapeRampToolPointHitProxy, HHitProxy)
class FLandscapeToolRamp : public FLandscapeTool
{
protected:
FEdModeLandscape* EdMode;
TObjectPtr<UTexture2D> SpriteTexture;
FVector Points[2];
int8 NumPoints;
int8 SelectedPoint;
bool bMovingPoint;
public:
FLandscapeToolRamp(FEdModeLandscape* InEdMode)
: EdMode(InEdMode)
, SpriteTexture(LoadObject<UTexture2D>(NULL, TEXT("/Engine/EditorResources/S_Terrain.S_Terrain")))
, NumPoints(0)
, SelectedPoint(INDEX_NONE)
, bMovingPoint(false)
{
check(SpriteTexture);
}
virtual void AddReferencedObjects(FReferenceCollector& Collector) override
{
Collector.AddReferencedObject(SpriteTexture);
}
virtual const TCHAR* GetToolName() const override { return TEXT("Ramp"); }
virtual FText GetDisplayName() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Ramp", "Ramp"); };
virtual FText GetDisplayMessage() const override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Ramp_Message", "Create a ramp between two specified points, adding falloffs according to the settings. Note that this tool cannot use any brushes."); };
virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::None | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); }
virtual bool SupportsMask() override { return false; }
virtual ELandscapeToolTargetTypeMask::Type GetSupportedTargetTypes() override
{
return ELandscapeToolTargetTypeMask::Heightmap;
}
virtual void EnterTool() override
{
NumPoints = 0;
SelectedPoint = INDEX_NONE;
GLevelEditorModeTools().SetWidgetMode(UE::Widget::WM_Translate);
}
virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& Target, const FVector& InHitLocation) override
{
if (NumPoints < 2)
{
Points[NumPoints] = InHitLocation;
SelectedPoint = NumPoints;
NumPoints++;
bMovingPoint = true;
GLevelEditorModeTools().SetWidgetMode(UE::Widget::WM_Translate);
}
else
{
if (SelectedPoint != INDEX_NONE)
{
Points[SelectedPoint] = InHitLocation;
bMovingPoint = true;
GLevelEditorModeTools().SetWidgetMode(UE::Widget::WM_Translate);
}
}
GUnrealEd->RedrawLevelEditingViewports();
return true;
}
virtual void EndTool(FEditorViewportClient* ViewportClient) override
{
bMovingPoint = false;
}
virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y) override
{
if (bMovingPoint)
{
if (!Viewport->KeyState(EKeys::LeftMouseButton))
{
bMovingPoint = false;
return false;
}
FVector HitLocation;
if (EdMode->LandscapeMouseTrace(ViewportClient, x, y, HitLocation))
{
if (NumPoints == 1)
{
SelectedPoint = NumPoints;
NumPoints++;
}
Points[SelectedPoint] = HitLocation;
GUnrealEd->RedrawLevelEditingViewports();
}
return true;
}
return false;
}
virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent) override
{
if (InKey == EKeys::Enter && InEvent == IE_Pressed)
{
if (CanApplyRamp())
{
ApplyRamp();
}
}
if (InKey == EKeys::Escape && InEvent == IE_Pressed)
{
ResetRamp();
}
// Handle clicking on points to select them and drag them around
if (InKey == EKeys::LeftMouseButton)
{
if (InEvent == IE_Pressed)
{
if (!InViewport->KeyState(EKeys::MiddleMouseButton) && !InViewport->KeyState(EKeys::RightMouseButton) && !IsAltDown(InViewport) && InViewportClient->GetCurrentWidgetAxis() == EAxisList::None)
{
HHitProxy* HitProxy = InViewport->GetHitProxy(InViewport->GetMouseX(), InViewport->GetMouseY());
if (HitProxy && HitProxy->IsA(HLandscapeRampToolPointHitProxy::StaticGetType()))
{
HLandscapeRampToolPointHitProxy* PointHitProxy = (HLandscapeRampToolPointHitProxy*)HitProxy;
SelectedPoint = PointHitProxy->Point;
GLevelEditorModeTools().SetWidgetMode(UE::Widget::WM_Translate);
GUnrealEd->RedrawLevelEditingViewports();
bMovingPoint = true;
return true;
}
}
return false;
}
else if (InEvent == IE_Released)
{
bMovingPoint = false;
return false;
}
}
if (InKey == EKeys::End && InEvent == IE_Pressed)
{
if (SelectedPoint != INDEX_NONE)
{
const int32 MinX = FMath::FloorToInt32(Points[SelectedPoint].X);
const int32 MinY = FMath::FloorToInt32(Points[SelectedPoint].Y);
const int32 MaxX = MinX + 1;
const int32 MaxY = MinY + 1;
FLandscapeEditDataInterface LandscapeEdit(EdMode->CurrentToolTarget.LandscapeInfo.Get());
TArray<uint16> Data;
Data.AddZeroed(4);
int32 ValidMinX = MinX;
int32 ValidMinY = MinY;
int32 ValidMaxX = MaxX;
int32 ValidMaxY = MaxY;
LandscapeEdit.GetHeightData(ValidMinX, ValidMinY, ValidMaxX, ValidMaxY, Data.GetData(), 0);
if (ValidMaxX - ValidMinX != 1 && ValidMaxY - ValidMinY != 1)
{
// If we didn't read 4 values then we're partly off the edge of the landscape
return true;
}
checkSlow(ValidMinX == MinX);
checkSlow(ValidMinY == MinY);
checkSlow(ValidMaxX == MaxX);
checkSlow(ValidMaxY == MaxY);
Points[SelectedPoint].Z = (FMath::BiLerp<float>(Data[0], Data[1], Data[2], Data[3], FMath::Frac(Points[SelectedPoint].X), FMath::Frac(Points[SelectedPoint].Y)) - LandscapeDataAccess::MidValue) * LANDSCAPE_ZSCALE;
return true;
}
}
// Change Ramp Width
if ((InEvent == IE_Pressed || InEvent == IE_Repeat) && (InKey == EKeys::LeftBracket || InKey == EKeys::RightBracket))
{
const float OldValue = EdMode->UISettings->RampWidth;
const float SliderMin = 0.0f;
const float SliderMax = 8192.0f;
const float Diff = 0.05f;
float NewValue;
if (InKey == EKeys::LeftBracket)
{
NewValue = OldValue - OldValue * Diff;
NewValue = FMath::Min(NewValue, OldValue - 1.0f);
}
else
{
NewValue = OldValue + OldValue * Diff;
NewValue = FMath::Max(NewValue, OldValue + 1.0f);
}
NewValue = FMath::RoundToFloat(FMath::Clamp(NewValue, SliderMin, SliderMax));
EdMode->UISettings->RampWidth = NewValue;
return true;
}
return false;
}
virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override
{
if (SelectedPoint != INDEX_NONE && InViewportClient->GetCurrentWidgetAxis() != EAxisList::None)
{
if (const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo.IsValid() ? EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy() : nullptr)
{
const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld();
Points[SelectedPoint] += LandscapeToWorld.InverseTransformVector(InDrag);
}
return true;
}
return false;
}
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override
{
// The editor can try to render the tool before the UpdateLandscapeEditorData command runs and the landscape editor realizes that the landscape has been hidden/deleted
const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo.IsValid() ? EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy() : nullptr;
if ((LandscapeProxy != nullptr) && (NumPoints > 0))
{
const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld();
const FLinearColor SelectedSpriteColor = FLinearColor::White + (GEngine->GetSelectedMaterialColor() * GEngine->SelectionHighlightIntensity * 10);
FVector WorldPoints[2];
for (int32 i = 0; i < NumPoints; i++)
{
WorldPoints[i] = LandscapeToWorld.TransformPosition(Points[i]);
}
float SpriteScale = EdMode->UISettings->RampWidth / 4;
if (NumPoints > 1)
{
SpriteScale = FMath::Min(SpriteScale, static_cast<float>((WorldPoints[1] - WorldPoints[0]).Size() / 2));
}
SpriteScale = FMath::Clamp<float>(SpriteScale, 10, 500);
for (int8 i = 0; i < NumPoints; i++)
{
const FLinearColor SpriteColor = (i == SelectedPoint) ? SelectedSpriteColor : FLinearColor::White;
PDI->SetHitProxy(new HLandscapeRampToolPointHitProxy(i));
PDI->DrawSprite(WorldPoints[i],
SpriteScale,
SpriteScale,
SpriteTexture->GetResource(),
SpriteColor,
SDPG_Foreground,
/*U = */0.f, /*UL = */0.f,
/*V = */0.f, /*VL = */0.f,
SE_BLEND_Masked);
}
PDI->SetHitProxy(NULL);
if (NumPoints == 2)
{
const FVector Side = FVector::CrossProduct(Points[1] - Points[0], FVector(0, 0, 1)).GetSafeNormal2D();
FVector InnerSide = Side * (EdMode->UISettings->RampWidth * 0.5f * (1 - EdMode->UISettings->RampSideFalloff));
FVector OuterSide = Side * (EdMode->UISettings->RampWidth * 0.5f);
InnerSide = LandscapeToWorld.TransformVectorNoScale(InnerSide);
OuterSide = LandscapeToWorld.TransformVectorNoScale(OuterSide);
FVector InnerVerts[2][2];
InnerVerts[0][0] = WorldPoints[0] - InnerSide;
InnerVerts[0][1] = WorldPoints[0] + InnerSide;
InnerVerts[1][0] = WorldPoints[1] - InnerSide;
InnerVerts[1][1] = WorldPoints[1] + InnerSide;
FVector OuterVerts[2][2];
OuterVerts[0][0] = WorldPoints[0] - OuterSide;
OuterVerts[0][1] = WorldPoints[0] + OuterSide;
OuterVerts[1][0] = WorldPoints[1] - OuterSide;
OuterVerts[1][1] = WorldPoints[1] + OuterSide;
// Left
DrawDashedLine(PDI, OuterVerts[0][0], OuterVerts[1][0], FColor::White, 50, SDPG_Foreground);
// Center
DrawDashedLine(PDI, InnerVerts[0][0], InnerVerts[0][1], FColor::White, 50, SDPG_Foreground);
PDI->DrawLine(InnerVerts[0][0], InnerVerts[0][1], FLinearColor::White, SDPG_World);
DrawDashedLine(PDI, InnerVerts[0][0], InnerVerts[1][0], FColor::White, 50, SDPG_Foreground);
PDI->DrawLine(InnerVerts[0][0], InnerVerts[1][0], FLinearColor::White, SDPG_World);
DrawDashedLine(PDI, InnerVerts[0][1], InnerVerts[1][1], FColor::White, 50, SDPG_Foreground);
PDI->DrawLine(InnerVerts[0][1], InnerVerts[1][1], FLinearColor::White, SDPG_World);
DrawDashedLine(PDI, InnerVerts[1][0], InnerVerts[1][1], FColor::White, 50, SDPG_Foreground);
PDI->DrawLine(InnerVerts[1][0], InnerVerts[1][1], FLinearColor::White, SDPG_World);
// Right
DrawDashedLine(PDI, OuterVerts[0][1], OuterVerts[1][1], FColor::White, 50, SDPG_Foreground);
}
}
}
virtual bool OverrideSelection() const override
{
return true;
}
virtual bool IsSelectionAllowed(AActor* InActor, bool bInSelection) const override
{
// Only filter selection not deselection
if (bInSelection)
{
return false;
}
return true;
}
virtual bool UsesTransformWidget() const override
{
if (SelectedPoint != INDEX_NONE)
{
// The editor can try to render the transform widget before the landscape editor ticks and realizes that the landscape has been hidden/deleted
return EdMode->CurrentToolTarget.LandscapeInfo.IsValid() && (EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy() != nullptr);
}
return false;
}
virtual EAxisList::Type GetWidgetAxisToDraw(UE::Widget::EWidgetMode CheckMode) const override
{
if (SelectedPoint != INDEX_NONE)
{
if (CheckMode == UE::Widget::WM_Translate)
{
return EAxisList::XYZ;
}
else
{
return EAxisList::None;
}
}
return EAxisList::None;
}
virtual FVector GetWidgetLocation() const override
{
if (SelectedPoint != INDEX_NONE)
{
const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy();
if (LandscapeProxy)
{
const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld();
return LandscapeToWorld.TransformPosition(Points[SelectedPoint]);
}
}
return FVector::ZeroVector;
}
virtual FMatrix GetWidgetRotation() const override
{
if (SelectedPoint != INDEX_NONE)
{
const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy();
if (LandscapeProxy)
{
const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld();
return FQuatRotationTranslationMatrix(LandscapeToWorld.GetRotation(), FVector::ZeroVector);
}
}
return FMatrix::Identity;
}
virtual void ApplyRamp()
{
FText Reason;
if (!EdMode->CanEditLayer(&Reason))
{
FMessageDialog::Open(EAppMsgType::Ok, Reason);
return;
}
FScopedTransaction Transaction(LOCTEXT("Ramp_Apply", "Landscape Editing: Add ramp"));
ALandscape* Landscape = EdMode->GetLandscape();
FGuid EditLayerGUID = EdMode->GetCurrentLayerGuid();
const ULandscapeInfo* LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get();
const ALandscapeProxy* LandscapeProxy = LandscapeInfo->GetLandscapeProxy();
const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld();
const FVector2D Side = FVector2D(FVector::CrossProduct(Points[1] - Points[0], FVector(0,0,1))).GetSafeNormal();
const FVector2D InnerSide = Side * (EdMode->UISettings->RampWidth * 0.5f * (1 - EdMode->UISettings->RampSideFalloff)) / LandscapeToWorld.GetScale3D().X;
const FVector2D OuterSide = Side * (EdMode->UISettings->RampWidth * 0.5f) / LandscapeToWorld.GetScale3D().X;
FVector2D InnerVerts[2][2];
InnerVerts[0][0] = FVector2D(Points[0]) - InnerSide;
InnerVerts[0][1] = FVector2D(Points[0]) + InnerSide;
InnerVerts[1][0] = FVector2D(Points[1]) - InnerSide;
InnerVerts[1][1] = FVector2D(Points[1]) + InnerSide;
FVector2D OuterVerts[2][2];
OuterVerts[0][0] = FVector2D(Points[0]) - OuterSide;
OuterVerts[0][1] = FVector2D(Points[0]) + OuterSide;
OuterVerts[1][0] = FVector2D(Points[1]) - OuterSide;
OuterVerts[1][1] = FVector2D(Points[1]) + OuterSide;
const double Heights[2] = {
Points[0].Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue,
Points[1].Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue
};
int32 MinX = FMath::CeilToInt32(FMath::Min(FMath::Min(OuterVerts[0][0].X, OuterVerts[0][1].X), FMath::Min(OuterVerts[1][0].X, OuterVerts[1][1].X))) - 1; // +/- 1 to make sure we have enough data for calculating correct normals
int32 MinY = FMath::CeilToInt32(FMath::Min(FMath::Min(OuterVerts[0][0].Y, OuterVerts[0][1].Y), FMath::Min(OuterVerts[1][0].Y, OuterVerts[1][1].Y))) - 1;
int32 MaxX = FMath::FloorToInt32(FMath::Max(FMath::Max(OuterVerts[0][0].X, OuterVerts[0][1].X), FMath::Max(OuterVerts[1][0].X, OuterVerts[1][1].X))) + 1;
int32 MaxY = FMath::FloorToInt32(FMath::Max(FMath::Max(OuterVerts[0][0].Y, OuterVerts[0][1].Y), FMath::Max(OuterVerts[1][0].Y, OuterVerts[1][1].Y))) + 1;
// I'd dearly love to use FIntRect in this code, but Landscape works with "Inclusive Max" and FIntRect is "Exclusive Max"
int32 LandscapeMinX, LandscapeMinY, LandscapeMaxX, LandscapeMaxY;
if (!LandscapeInfo->GetLandscapeExtent(LandscapeMinX, LandscapeMinY, LandscapeMaxX, LandscapeMaxY))
{
return;
}
MinX = FMath::Max(MinX, LandscapeMinX);
MinY = FMath::Max(MinY, LandscapeMinY);
MaxX = FMath::Min(MaxX, LandscapeMaxX);
MaxY = FMath::Min(MaxY, LandscapeMaxY);
if (MinX > MaxX || MinY > MaxY)
{
// The bounds don't intersect any data, so we skip applying the ramp entirely
return;
}
// construct the caches, and set them to work in the EditLayer
FLandscapeEditDataInterface LandscapeEdit(EdMode->CurrentToolTarget.LandscapeInfo.Get(), EditLayerGUID);
FLandscapeHeightCache HeightCache(EdMode->CurrentToolTarget);
FLandscapeLayerDataCache<FHeightmapToolTarget> LayerHeightDataCache(EdMode->CurrentToolTarget, HeightCache);
LayerHeightDataCache.SetCacheEditingLayer(EditLayerGUID);
const bool bCombinedLayerOperation = EdMode->UISettings->bCombinedLayersOperation && Landscape && Landscape->HasLayersContent();
LayerHeightDataCache.Initialize(EdMode->CurrentToolTarget.LandscapeInfo.Get(), bCombinedLayerOperation);
// Heights raster
bool bRaiseTerrain = true; //EdMode->UISettings->Ramp_bRaiseTerrain;
bool bLowerTerrain = true; //EdMode->UISettings->Ramp_bLowerTerrain;
if (bRaiseTerrain || bLowerTerrain)
{
TArray<uint16> Data;
Data.AddZeroed((1 + MaxY - MinY) * (1 + MaxX - MinX));
int32 ValidMinX = MinX;
int32 ValidMinY = MinY;
int32 ValidMaxX = MaxX;
int32 ValidMaxY = MaxY;
LayerHeightDataCache.Read(ValidMinX, ValidMinY, ValidMaxX, ValidMaxY, Data);
if (ValidMinX > ValidMaxX || ValidMinY > ValidMaxY)
{
// The bounds don't intersect any data, so we skip applying the ramp entirely
return;
}
FLandscapeEditDataInterface::ShrinkData(Data, MinX, MinY, MaxX, MaxY, ValidMinX, ValidMinY, ValidMaxX, ValidMaxY);
MinX = ValidMinX;
MinY = ValidMinY;
MaxX = ValidMaxX;
MaxY = ValidMaxY;
FTriangleRasterizer<FLandscapeRampToolHeightRasterPolicy> Rasterizer(
FLandscapeRampToolHeightRasterPolicy(Data, MinX, MinY, MaxX, MaxY, bRaiseTerrain, bLowerTerrain));
// Left
Rasterizer.DrawTriangle(FVector2D(0, Heights[0]), FVector2D(1, Heights[0]), FVector2D(0, Heights[1]), OuterVerts[0][0], InnerVerts[0][0], OuterVerts[1][0], false);
Rasterizer.DrawTriangle(FVector2D(1, Heights[0]), FVector2D(0, Heights[1]), FVector2D(1, Heights[1]), InnerVerts[0][0], OuterVerts[1][0], InnerVerts[1][0], false);
// Center
Rasterizer.DrawTriangle(FVector2D(1, Heights[0]), FVector2D(1, Heights[0]), FVector2D(1, Heights[1]), InnerVerts[0][0], InnerVerts[0][1], InnerVerts[1][0], false);
Rasterizer.DrawTriangle(FVector2D(1, Heights[0]), FVector2D(1, Heights[1]), FVector2D(1, Heights[1]), InnerVerts[0][1], InnerVerts[1][0], InnerVerts[1][1], false);
// Right
Rasterizer.DrawTriangle(FVector2D(1, Heights[0]), FVector2D(0, Heights[0]), FVector2D(1, Heights[1]), InnerVerts[0][1], OuterVerts[0][1], InnerVerts[1][1], false);
Rasterizer.DrawTriangle(FVector2D(0, Heights[0]), FVector2D(1, Heights[1]), FVector2D(0, Heights[1]), OuterVerts[0][1], InnerVerts[1][1], OuterVerts[1][1], false);
LayerHeightDataCache.Write(MinX, MinY, MaxX, MaxY, Data);
// Dirty any runtime virtual textures that our landscape components write to.
LandscapeInfo->DirtyRuntimeVirtualTextureForLandscapeArea(MinX, MinY, MaxX, MaxY);
if (!EdMode->HasLandscapeLayersContent())
{
TSet<ULandscapeComponent*> Components;
if (LandscapeEdit.GetComponentsInRegion(MinX, MinY, MaxX, MaxY, &Components))
{
for (ULandscapeComponent* Component : Components)
{
// Recreate collision for modified components and update the navmesh
ULandscapeHeightfieldCollisionComponent* CollisionComponent = Component->GetCollisionComponent();
if (CollisionComponent)
{
CollisionComponent->RecreateCollision();
FNavigationSystem::UpdateComponentData(*CollisionComponent);
}
}
}
}
}
if (Landscape)
{
Landscape->RequestLayersContentUpdate(ELandscapeLayerUpdateMode::Update_Heightmap_All);
}
}
bool CanApplyRamp()
{
return EdMode->CanEditLayer() && (NumPoints == 2);
}
void ResetRamp()
{
NumPoints = 0;
SelectedPoint = INDEX_NONE;
}
};
void FEdModeLandscape::ApplyRampTool()
{
if (CurrentTool->GetToolName() == FName("Ramp"))
{
FLandscapeToolRamp* RampTool = (FLandscapeToolRamp*)CurrentTool;
RampTool->ApplyRamp();
GEditor->RedrawLevelEditingViewports();
}
}
bool FEdModeLandscape::CanApplyRampTool()
{
if (CurrentTool->GetToolName() == FName("Ramp"))
{
FLandscapeToolRamp* RampTool = (FLandscapeToolRamp*)CurrentTool;
return RampTool->CanApplyRamp();
}
return false;
}
void FEdModeLandscape::ResetRampTool()
{
if (CurrentTool->GetToolName() == FName("Ramp"))
{
FLandscapeToolRamp* RampTool = (FLandscapeToolRamp*)CurrentTool;
RampTool->ResetRamp();
GEditor->RedrawLevelEditingViewports();
}
}
//
// Toolset initialization
//
void FEdModeLandscape::InitializeTool_Ramp()
{
auto Tool_Ramp = MakeUnique<FLandscapeToolRamp>(this);
Tool_Ramp->ValidBrushes.Add("BrushSet_Dummy");
LandscapeTools.Add(MoveTemp(Tool_Ramp));
}
#undef LOCTEXT_NAMESPACE