// 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 Objects; if (LandscapeInfo) { TSet SelectedComponents = LandscapeInfo->GetSelectedComponents(); Objects.Reset(SelectedComponents.Num()); Algo::Copy(SelectedComponents, Objects); } FPropertyEditorModule& PropertyModule = FModuleManager::Get().LoadModuleChecked(TEXT("PropertyEditor")); PropertyModule.UpdatePropertyViews(Objects); } } void Apply(FEditorViewportClient* ViewportClient, FLandscapeBrush* Brush, const ULandscapeEditorObject* UISettings, const TArray& 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 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(InteractorPositions[0].Position.X); const float MouseY = static_cast(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 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 { using Super = FLandscapeToolBase; 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& 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 Data; Cache.GetCachedData(X1, Y1, X2, Y2, Data); TSet 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(FMath::Clamp(FMath::RoundToInt(Value * 255), 0, 255)); } } } Cache.SetCachedData(X1, Y1, X2, Y2, Data); Cache.Flush(); } } protected: FLandscapeDataCache Cache; }; class FLandscapeToolMask : public FLandscapeToolBase { using Super = FLandscapeToolBase; 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& 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 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 { using Super = FLandscapeToolBase; 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& InteractorPositions) { TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeToolStrokeMoveToLevel_Apply); ALandscape* Landscape = LandscapeInfo ? LandscapeInfo->LandscapeActor.Get() : nullptr; if (Landscape) { Landscape->Modify(); LandscapeInfo->Modify(); TArray 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 { using Super = FLandscapeToolBase; 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& 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 Data; TArray 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 ModifiedProxies; TArray NewComponents; LandscapeInfo->Modify(); ULandscapeSubsystem* LandscapeSubsystem = World->GetSubsystem(); 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(LandscapeProxy, NAME_None, RF_Transactional); NewComponents.Add(LandscapeComponent); LandscapeComponent->Init( ComponentBase.X, ComponentBase.Y, LandscapeProxy->ComponentSizeQuads, LandscapeProxy->NumSubsections, LandscapeProxy->SubsectionSizeQuads ); TArray 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 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 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 XYOffsetCache; }; class FLandscapeToolAddComponent : public FLandscapeToolBase { using Super = FLandscapeToolBase; 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 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(LastMousePosition.GetValue().X / ComponentSizeQuads - (BrushSize - 1) / 2.0); const float BrushOriginY = static_cast(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 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& 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 { using Super = FLandscapeToolBase; 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 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& 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 HeightData; TArray WeightDatas; // Weight*Layers... TArray Data; TSet 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(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(LandscapeLocal.X - LX); const float FracY = static_cast(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::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 FLandscapeToolCopy : public FLandscapeToolBase> { using Super = FLandscapeToolBase>; 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 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& 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(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 HeightData; TArray WeightDatas; // Weight*Layers... TArray 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(GizmoLocal.X - LX); const float FracY = static_cast(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(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(FMath::Lerp(OriginalValue, DestValue, PaintAmount)); } else { Data[index] = static_cast(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( 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(FMath::Lerp(OriginalValue, DestValue, PaintAmount)); } else { Data[index] = static_cast(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 FLandscapeToolPaste : public FLandscapeToolBase> { using Super = FLandscapeToolBase>; 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>::MouseMove(ViewportClient, Viewport, x, y); } protected: bool bUseGizmoRegion; FLandscapeBrush* BackupCurrentBrush; }; // // FLandscapeToolCopyPaste // template class FLandscapeToolCopyPaste : public FLandscapeToolPaste { public: FLandscapeToolCopyPaste(FEdModeLandscape* InEdMode) : FLandscapeToolPaste(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 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(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(InitialComponentCount) / 2.0f); ComponentCount = static_cast(InitialComponentCount + PosOrNeg * Delta); UISettings->NewLandscape_ClampSize(); const float ActualDelta = static_cast(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(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; 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& 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(X - OffsetX, 0, ImportSizeX - 1); const int32 ImportY0 = FMath::Clamp(Y0 - OffsetY, 0, ImportSizeY - 1); const int32 ImportY1 = FMath::Clamp(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(Y - OffsetY, 0, ImportSizeY - 1); const int32 ImportX0 = FMath::Clamp(X0 - OffsetX, 0, ImportSizeX - 1); const int32 ImportX1 = FMath::Clamp(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(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(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(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(this); Tool_NewLandscape->ValidBrushes.Add("BrushSet_Dummy"); LandscapeTools.Add(MoveTemp(Tool_NewLandscape)); } void FEdModeLandscape::InitializeTool_ResizeLandscape() { auto Tool_ResizeLandscape = MakeUnique(this); Tool_ResizeLandscape->ValidBrushes.Add("BrushSet_Dummy"); LandscapeTools.Add(MoveTemp(Tool_ResizeLandscape)); } void FEdModeLandscape::InitializeTool_ImportExport() { auto Tool_ImportExportLandscape = MakeUnique(this); Tool_ImportExportLandscape->ValidBrushes.Add("BrushSet_Dummy"); LandscapeTools.Add(MoveTemp(Tool_ImportExportLandscape)); } void FEdModeLandscape::InitializeTool_Select() { auto Tool_Select = MakeUnique(this); Tool_Select->ValidBrushes.Add("BrushSet_Component"); LandscapeTools.Add(MoveTemp(Tool_Select)); } void FEdModeLandscape::InitializeTool_AddComponent() { auto Tool_AddComponent = MakeUnique(this); Tool_AddComponent->ValidBrushes.Add("BrushSet_Component"); LandscapeTools.Add(MoveTemp(Tool_AddComponent)); } void FEdModeLandscape::InitializeTool_DeleteComponent() { auto Tool_DeleteComponent = MakeUnique(this); Tool_DeleteComponent->ValidBrushes.Add("BrushSet_Component"); LandscapeTools.Add(MoveTemp(Tool_DeleteComponent)); } void FEdModeLandscape::InitializeTool_MoveToLevel() { auto Tool_MoveToLevel = MakeUnique(this); Tool_MoveToLevel->ValidBrushes.Add("BrushSet_Component"); LandscapeTools.Add(MoveTemp(Tool_MoveToLevel)); } void FEdModeLandscape::InitializeTool_Mask() { auto Tool_Mask = MakeUnique(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>(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>(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(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