// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "LandscapeEditorPrivatePCH.h" #include "LandscapeEdMode.h" #include "ScopedTransaction.h" #include "LandscapeEdit.h" #include "LandscapeRender.h" #include "LandscapeHeightfieldCollisionComponent.h" #include "Algo/Reverse.h" //#include "LandscapeDataAccess.h" //#include "AI/Navigation/NavigationSystem.h" #define LOCTEXT_NAMESPACE "Landscape" class FLandscapeToolMirror : public FLandscapeTool { protected: FEdModeLandscape* EdMode; UMaterialInstanceDynamic* MirrorPlaneMaterial; ECoordSystem SavedCoordSystem; public: FLandscapeToolMirror(FEdModeLandscape* InEdMode) : EdMode(InEdMode) { UMaterialInterface* BaseMirrorPlaneMaterial = LoadObject(nullptr, TEXT("/Engine/EditorLandscapeResources/MirrorPlaneMaterial.MirrorPlaneMaterial")); MirrorPlaneMaterial = UMaterialInstanceDynamic::Create(BaseMirrorPlaneMaterial, GetTransientPackage()); MirrorPlaneMaterial->SetScalarParameterValue(FName("LineThickness"), 2.0f); } virtual void AddReferencedObjects(FReferenceCollector& Collector) override { Collector.AddReferencedObject(MirrorPlaneMaterial); } virtual const TCHAR* GetToolName() override { return TEXT("Mirror"); } virtual FText GetDisplayName() override { return FText(); /*NSLOCTEXT("UnrealEd", "LandscapeTool_Mirror", "Mirror Landscape");*/ }; 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 { if (EdMode->UISettings->MirrorPoint == FVector2D::ZeroVector) { CenterMirrorPoint(); } GLevelEditorModeTools().SetWidgetMode(FWidget::WM_Translate); SavedCoordSystem = GLevelEditorModeTools().GetCoordSystem(); GLevelEditorModeTools().SetCoordSystem(COORD_Local); } virtual void ExitTool() override { GLevelEditorModeTools().SetCoordSystem(SavedCoordSystem); } virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& Target, const FVector& InHitLocation) override { return true; } virtual void EndTool(FEditorViewportClient* ViewportClient) override { } virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y) override { return false; } virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent) override { if (InKey == EKeys::Enter && InEvent == IE_Pressed) { ApplyMirror(); } return false; } virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override { if (InViewportClient->GetCurrentWidgetAxis() != EAxisList::None) { const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy(); const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld(); EdMode->UISettings->MirrorPoint += FVector2D(LandscapeToWorld.InverseTransformVector(InDrag)); return true; } return false; } virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override { const ULandscapeInfo* const LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get(); const ALandscapeProxy* const LandscapeProxy = LandscapeInfo->GetLandscapeProxy(); const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld(); int32 MinX, MinY, MaxX, MaxY; if (LandscapeInfo->GetLandscapeExtent(MinX, MinY, MaxX, MaxY)) { FVector MirrorPoint3D = FVector((MaxX + MinX) / 2.0f, (MaxY + MinY) / 2.0f, 0); FVector MirrorPlaneScale = FVector(0, 1, 100); if (EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::MinusXToPlusX || EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::PlusXToMinusX) { MirrorPoint3D.X = EdMode->UISettings->MirrorPoint.X; MirrorPlaneScale.Y = (MaxY - MinY) / 2.0f; } else { MirrorPoint3D.Y = EdMode->UISettings->MirrorPoint.Y; MirrorPlaneScale.Y = (MaxX - MinX) / 2.0f; } MirrorPoint3D = LandscapeToWorld.TransformPosition(MirrorPoint3D); FMatrix Matrix; if (EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::MinusYToPlusY || EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::PlusYToMinusY) { Matrix = FScaleRotationTranslationMatrix(MirrorPlaneScale, FRotator(0, 90, 0), FVector::ZeroVector); } else { Matrix = FScaleMatrix(MirrorPlaneScale); } Matrix *= LandscapeToWorld.ToMatrixWithScale(); Matrix.SetOrigin(MirrorPoint3D); // Convert plane from horizontal to vertical Matrix = FMatrix(FVector(0, 1, 0), FVector(0, 0, 1), FVector(1, 0, 0), FVector(0, 0, 0)) * Matrix; const FBox Box = FBox(FVector(-1, -1, 0), FVector(+1, +1, 0)); DrawWireBox(PDI, Matrix, Box, FLinearColor::Green, SDPG_World); const float LandscapeScaleRatio = LandscapeToWorld.GetScale3D().Z / LandscapeToWorld.GetScale3D().X; FVector2D UVScale = FVector2D(FMath::RoundToFloat(MirrorPlaneScale.Y / 10), FMath::RoundToFloat(MirrorPlaneScale.Z * LandscapeScaleRatio / 10 / 2) * 2); MirrorPlaneMaterial->SetVectorParameterValue(FName("GridSize"), FVector(UVScale, 0)); DrawPlane10x10(PDI, Matrix, 1, FVector2D(0, 0), FVector2D(1, 1), MirrorPlaneMaterial->GetRenderProxy(false), SDPG_World); } } 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 { return true; } virtual EAxisList::Type GetWidgetAxisToDraw(FWidget::EWidgetMode CheckMode) const override { if (CheckMode == FWidget::WM_Translate) { switch (EdMode->UISettings->MirrorOp) { case ELandscapeMirrorOperation::MinusXToPlusX: case ELandscapeMirrorOperation::PlusXToMinusX: return EAxisList::X; case ELandscapeMirrorOperation::MinusYToPlusY: case ELandscapeMirrorOperation::PlusYToMinusY: return EAxisList::Y; default: check(0); return EAxisList::None; } } else { return EAxisList::None; } return EAxisList::None; } virtual FVector GetWidgetLocation() const override { const ULandscapeInfo* const LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get(); const ALandscapeProxy* const LandscapeProxy = LandscapeInfo->GetLandscapeProxy(); const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld(); int32 MinX, MinY, MaxX, MaxY; if (!LandscapeInfo->GetLandscapeExtent(MinX, MinY, MaxX, MaxY)) { MinX = MinY = 0; MaxX = MaxY = 0; } FVector MirrorPoint3D = FVector((MaxX + MinX) / 2.0f, (MaxY + MinY) / 2.0f, 0); if (EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::MinusXToPlusX || EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::PlusXToMinusX) { MirrorPoint3D.X = EdMode->UISettings->MirrorPoint.X; } else { MirrorPoint3D.Y = EdMode->UISettings->MirrorPoint.Y; } MirrorPoint3D = LandscapeToWorld.TransformPosition(MirrorPoint3D); return MirrorPoint3D; } virtual FMatrix GetWidgetRotation() const override { const ALandscapeProxy* const LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy(); const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld(); FMatrix Result = FQuatRotationTranslationMatrix(LandscapeToWorld.GetRotation(), FVector::ZeroVector); if (EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::PlusXToMinusX || EdMode->UISettings->MirrorOp == ELandscapeMirrorOperation::PlusYToMinusY) { Result = FRotationMatrix(FRotator(0, 180, 0)) * Result; } return Result; } protected: template void ApplyMirrorInternal(T* MirrorData, int32 SizeX, int32 SizeY, int32 MirrorSize) { switch (EdMode->UISettings->MirrorOp) { case ELandscapeMirrorOperation::MinusXToPlusX: { for (int32 Y = 0; Y < SizeY; ++Y) // copy extra column to calc normals for mirror column { Algo::Reverse(&MirrorData[Y * (2 + MirrorSize) + 1], MirrorSize + 1); MirrorData[Y * (2 + MirrorSize) + 0] = MirrorData[Y * (2 + MirrorSize) + 2]; } } break; case ELandscapeMirrorOperation::PlusXToMinusX: { for (int32 Y = 0; Y < SizeY; ++Y) // copy extra column to calc normals for mirror column { Algo::Reverse(&MirrorData[Y * (2 + MirrorSize) + 0], MirrorSize + 1); MirrorData[Y * (2 + MirrorSize) + MirrorSize + 1] = MirrorData[Y * (2 + MirrorSize) + MirrorSize - 1]; } } break; case ELandscapeMirrorOperation::MinusYToPlusY: { FMemory::Memcpy(&MirrorData[(MirrorSize + 1) * SizeX], &MirrorData[(MirrorSize - 1) * SizeX], SizeX * sizeof(T)); // copy extra row to calc normals for mirror row } break; case ELandscapeMirrorOperation::PlusYToMinusY: { FMemory::Memcpy(&MirrorData[0 * SizeX], &MirrorData[2 * SizeX], SizeX * sizeof(T)); // copy extra row to calc normals for mirror row } break; default: check(0); return; } } public: virtual void ApplyMirror() { FScopedTransaction Transaction(LOCTEXT("Mirror_Apply", "Landscape Editing: Mirror Landscape")); const ULandscapeInfo* const LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get(); const ALandscapeProxy* const LandscapeProxy = LandscapeInfo->GetLandscapeProxy(); const FTransform LandscapeToWorld = LandscapeProxy->LandscapeActorToWorld(); const FVector2D MirrorPoint = EdMode->UISettings->MirrorPoint; FLandscapeEditDataInterface LandscapeEdit(EdMode->CurrentToolTarget.LandscapeInfo.Get()); int32 MinX, MinY, MaxX, MaxY; if (!LandscapeInfo->GetLandscapeExtent(MinX, MinY, MaxX, MaxY)) { return; } const int32 SizeX = (1 + MaxX - MinX); const int32 SizeY = (1 + MaxY - MinY); int32 SourceMinX, SourceMinY; int32 SourceMaxX, SourceMaxY; int32 DestMinX, DestMinY; int32 DestMaxX, DestMaxY; int32 MirrorSize; int32 DataSize; int32 ReadOffset; int32 ReadStride; int32 WriteOffset; int32 WriteStride; switch (EdMode->UISettings->MirrorOp) { case ELandscapeMirrorOperation::MinusXToPlusX: { const int32 MirrorX = FMath::RoundToInt(MirrorPoint.X); if (MirrorX <= MinX || MirrorX >= MaxX) { return; } MirrorSize = FMath::Max(MaxX - MirrorX, MirrorX - MinX); // not including the mirror column itself DataSize = SizeY * (2 + MirrorSize); // +2 for mirror column and extra to calc normals for mirror column SourceMinX = MirrorX - MirrorSize; SourceMaxX = MirrorX; SourceMinY = MinY; SourceMaxY = MaxY; DestMinX = MirrorX - 1; // extra column to calc normals for mirror column DestMaxX = MirrorX + MirrorSize; DestMinY = MinY; DestMaxY = MaxY; ReadOffset = 1; ReadStride = (2 + MirrorSize); WriteOffset = 0; WriteStride = (2 + MirrorSize); } break; case ELandscapeMirrorOperation::PlusXToMinusX: { const int32 MirrorX = FMath::RoundToInt(MirrorPoint.X); if (MirrorX <= MinX || MirrorX >= MaxX) { return; } MirrorSize = FMath::Max(MaxX - MirrorX, MirrorX - MinX); // not including the mirror column itself DataSize = SizeY * (2 + MirrorSize); // +2 for mirror column and extra to calc normals for mirror column SourceMinX = MirrorX; SourceMaxX = MirrorX + MirrorSize; SourceMinY = MinY; SourceMaxY = MaxY; DestMinX = MirrorX - MirrorSize; DestMaxX = MirrorX + 1; // extra column to calc normals for mirror column DestMinY = MinY; DestMaxY = MaxY; ReadOffset = 0; ReadStride = (2 + MirrorSize); WriteOffset = 0; WriteStride = (2 + MirrorSize); } break; case ELandscapeMirrorOperation::MinusYToPlusY: { const int32 MirrorY = FMath::RoundToInt(MirrorPoint.Y); if (MirrorY <= MinY || MirrorY >= MaxY) { return; } MirrorSize = FMath::Max(MaxY - MirrorY, MirrorY - MinY); // not including the mirror row itself DataSize = (2 + MirrorSize) * SizeX; // +2 for mirror row and extra to calc normals for mirror row SourceMinX = MinX; SourceMaxX = MaxX; SourceMinY = MirrorY - MirrorSize; SourceMaxY = MirrorY; DestMinX = MinX; DestMaxX = MaxX; DestMinY = MirrorY - 1; // extra column to calc normals for mirror column DestMaxY = MirrorY + MirrorSize; ReadOffset = 0 * SizeX; ReadStride = SizeX; WriteOffset = DataSize - SizeX; // last line first and negative stride to flip :) WriteStride = -SizeX; } break; case ELandscapeMirrorOperation::PlusYToMinusY: { const int32 MirrorY = FMath::RoundToInt(MirrorPoint.Y); if (MirrorY <= MinY || MirrorY >= MaxY) { return; } MirrorSize = FMath::Max(MaxY - MirrorY, MirrorY - MinY); // not including the mirror row itself DataSize = (2 + MirrorSize) * SizeX; // +2 for mirror row and extra to calc normals for mirror row SourceMinX = MinX; SourceMaxX = MaxX; SourceMinY = MirrorY; SourceMaxY = MirrorY + MirrorSize; DestMinX = MinX; DestMaxX = MaxX; DestMinY = MirrorY - MirrorSize; DestMaxY = MirrorY + 1; // extra column to calc normals for mirror column ReadOffset = 1 * SizeX; ReadStride = SizeX; WriteOffset = DataSize - SizeX; // last line first and negative stride to flip :) WriteStride = -SizeX; } break; default: check(0); return; } TArray MirrorHeightData; MirrorHeightData.AddZeroed(DataSize); int32 TempMinX = SourceMinX; // GetHeightData overwrites its input min/max x/y int32 TempMaxX = SourceMaxX; int32 TempMinY = SourceMinY; int32 TempMaxY = SourceMaxY; LandscapeEdit.GetHeightData(TempMinX, TempMinY, TempMaxX, TempMaxY, &MirrorHeightData[ReadOffset], ReadStride); ApplyMirrorInternal(MirrorHeightData.GetData(), SizeX, SizeY, MirrorSize); LandscapeEdit.SetHeightData(DestMinX, DestMinY, DestMaxX, DestMaxY, &MirrorHeightData[WriteOffset], WriteStride, true); TArray MirrorWeightData; MirrorWeightData.AddZeroed(DataSize); for (const auto& LayerSettings : LandscapeInfo->Layers) { ULandscapeLayerInfoObject* LayerInfo = LayerSettings.LayerInfoObj; if (LayerInfo) { TempMinX = SourceMinX; // GetWeightData overwrites its input min/max x/y TempMaxX = SourceMaxX; TempMinY = SourceMinY; TempMaxY = SourceMaxY; LandscapeEdit.GetWeightData(LayerInfo, TempMinX, TempMinY, TempMaxX, TempMaxY, &MirrorWeightData[ReadOffset], ReadStride); ApplyMirrorInternal(MirrorWeightData.GetData(), SizeX, SizeY, MirrorSize); LandscapeEdit.SetAlphaData(LayerInfo, DestMinX, DestMinY, DestMaxX, DestMaxY, &MirrorWeightData[WriteOffset], WriteStride, ELandscapeLayerPaintingRestriction::None, false, false); } } LandscapeEdit.Flush(); TSet Components; if (LandscapeEdit.GetComponentsInRegion(DestMinX, DestMinY, DestMaxX, DestMaxY, &Components)) { for (ULandscapeComponent* Component : Components) { // Recreate collision for modified components and update the navmesh ULandscapeHeightfieldCollisionComponent* CollisionComponent = Component->CollisionComponent.Get(); if (CollisionComponent) { CollisionComponent->RecreateCollision(); UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(Component); if (NavSys) { NavSys->UpdateNavOctree(CollisionComponent); } } } } } void CenterMirrorPoint() { ULandscapeInfo* const LandscapeInfo = EdMode->CurrentToolTarget.LandscapeInfo.Get(); ALandscapeProxy* const LandscapeProxy = LandscapeInfo->GetLandscapeProxy(); int32 MinX, MinY, MaxX, MaxY; if (LandscapeInfo->GetLandscapeExtent(MinX, MinY, MaxX, MaxY)) { EdMode->UISettings->MirrorPoint = FVector2D((float)(MinX + MaxX) / 2.0f, (float)(MinY + MaxY) / 2.0f); } else { EdMode->UISettings->MirrorPoint = FVector2D(0, 0); } } }; void FEdModeLandscape::ApplyMirrorTool() { if (CurrentTool->GetToolName() == FName("Mirror")) { FLandscapeToolMirror* MirrorTool = (FLandscapeToolMirror*)CurrentTool; MirrorTool->ApplyMirror(); GEditor->RedrawLevelEditingViewports(); } } void FEdModeLandscape::CenterMirrorTool() { if (CurrentTool->GetToolName() == FName("Mirror")) { FLandscapeToolMirror* MirrorTool = (FLandscapeToolMirror*)CurrentTool; MirrorTool->CenterMirrorPoint(); GEditor->RedrawLevelEditingViewports(); } } // // Toolset initialization // void FEdModeLandscape::InitializeTool_Mirror() { auto Tool_Mirror = MakeUnique(this); Tool_Mirror->ValidBrushes.Add("BrushSet_Dummy"); LandscapeTools.Add(MoveTemp(Tool_Mirror)); } #undef LOCTEXT_NAMESPACE