// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. #include "LandscapeEditorPrivatePCH.h" #include "ObjectTools.h" #include "LandscapeEdMode.h" #include "ScopedTransaction.h" #include "Landscape/LandscapeEdit.h" #include "Landscape/LandscapeRender.h" #include "Landscape/LandscapeDataAccess.h" #include "Landscape/LandscapeSplineProxies.h" #include "LandscapeEditorModule.h" #include "Editor/PropertyEditor/Public/PropertyEditorModule.h" #include "LandscapeEdModeTools.h" #include "Components/SplineMeshComponent.h" #include "LandscapeSplineImportExport.h" #include "Landscape/LandscapeSplinesComponent.h" #include "Landscape/LandscapeSplineControlPoint.h" #include "Landscape/LandscapeSplineSegment.h" #include "Landscape/ControlPointMeshComponent.h" #define LOCTEXT_NAMESPACE "Landscape" // // FLandscapeToolSplines // class FLandscapeToolSplines : public FLandscapeTool, public FEditorUndoClient { public: FLandscapeToolSplines(FEdModeLandscape* InEdMode) : EdMode(InEdMode) , LandscapeInfo(NULL) , SelectedSplineControlPoints() , SelectedSplineSegments() , DraggingTangent_Segment(NULL) , DraggingTangent_End(false) , bMovingControlPoint(false) , bAutoRotateOnJoin(true) , bAutoChangeConnectionsOnMove(true) , bDeleteLooseEnds(false) , bCopyMeshToNewControlPoint(false) { // Register to update when an undo/redo operation has been called to update our list of actors GEditor->RegisterForUndo(this); } ~FLandscapeToolSplines() { // GEditor is invalid at shutdown as the object system is unloaded before the landscape module. if (UObjectInitialized()) { // Remove undo delegate GEditor->UnregisterForUndo(this); } } virtual const TCHAR* GetToolName() override { return TEXT("Splines"); } virtual FText GetDisplayName() override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Splines", "Splines"); }; virtual void SetEditRenderType() override { GLandscapeEditRenderMode = ELandscapeEditRenderMode::None | (GLandscapeEditRenderMode & ELandscapeEditRenderMode::BitMaskForMask); } virtual bool SupportsMask() override { return false; } void CreateSplineComponent(ALandscapeProxy* Landscape, FVector Scale3D) { Landscape->SplineComponent = ConstructObject(ULandscapeSplinesComponent::StaticClass(), Landscape, NAME_None, RF_Transactional); Landscape->SplineComponent->RelativeScale3D = Scale3D; Landscape->SplineComponent->AttachTo(Landscape->GetRootComponent()); Landscape->SplineComponent->ShowSplineEditorMesh(true); } void UpdatePropertiesWindows() { if (GLevelEditorModeTools().IsModeActive(EdMode->GetID())) { TArray Objects; Objects.Reset(SelectedSplineControlPoints.Num() + SelectedSplineSegments.Num()); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { Objects.Add(ControlPoint); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { Objects.Add(Segment); } FPropertyEditorModule& PropertyModule = FModuleManager::Get().LoadModuleChecked(TEXT("PropertyEditor")); PropertyModule.UpdatePropertyViews(Objects); } } void ClearSelectedControlPoints() { for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { checkSlow(ControlPoint->IsSplineSelected()); ControlPoint->Modify(); ControlPoint->SetSplineSelected(false); } SelectedSplineControlPoints.Empty(); } void ClearSelectedSegments() { for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { checkSlow(Segment->IsSplineSelected()); Segment->Modify(); Segment->SetSplineSelected(false); } SelectedSplineSegments.Empty(); } void ClearSelection() { ClearSelectedControlPoints(); ClearSelectedSegments(); } void DeselectControlPoint(ULandscapeSplineControlPoint* ControlPoint) { checkSlow(ControlPoint->IsSplineSelected()); SelectedSplineControlPoints.Remove(ControlPoint); ControlPoint->Modify(); ControlPoint->SetSplineSelected(false); } void DeSelectSegment(ULandscapeSplineSegment* Segment) { checkSlow(Segment->IsSplineSelected()); SelectedSplineSegments.Remove(Segment); Segment->Modify(); Segment->SetSplineSelected(false); } void SelectControlPoint(ULandscapeSplineControlPoint* ControlPoint) { checkSlow(!ControlPoint->IsSplineSelected()); SelectedSplineControlPoints.Add(ControlPoint); ControlPoint->Modify(); ControlPoint->SetSplineSelected(true); } void SelectSegment(ULandscapeSplineSegment* Segment) { checkSlow(!Segment->IsSplineSelected()); SelectedSplineSegments.Add(Segment); Segment->Modify(); Segment->SetSplineSelected(true); GLevelEditorModeTools().SetWidgetMode(FWidget::WM_Scale); } void SelectConnected() { TArray ControlPointsToProcess = SelectedSplineControlPoints.Array(); while (ControlPointsToProcess.Num() > 0) { const ULandscapeSplineControlPoint* ControlPoint = ControlPointsToProcess.Pop(); for (const FLandscapeSplineConnection& Connection : ControlPoint->ConnectedSegments) { ULandscapeSplineControlPoint* OtherEnd = Connection.GetFarConnection().ControlPoint; if (!OtherEnd->IsSplineSelected()) { SelectControlPoint(OtherEnd); ControlPointsToProcess.Add(OtherEnd); } } } TArray SegmentsToProcess = SelectedSplineSegments.Array(); while (SegmentsToProcess.Num() > 0) { const ULandscapeSplineSegment* Segment = SegmentsToProcess.Pop(); for (const FLandscapeSplineSegmentConnection& SegmentConnection : Segment->Connections) { for (const FLandscapeSplineConnection& Connection : SegmentConnection.ControlPoint->ConnectedSegments) { if (Connection.Segment != Segment && !Connection.Segment->IsSplineSelected()) { SelectSegment(Connection.Segment); SegmentsToProcess.Add(Connection.Segment); } } } } } void SelectAdjacentControlPoints() { for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { if (!Segment->Connections[0].ControlPoint->IsSplineSelected()) { SelectControlPoint(Segment->Connections[0].ControlPoint); } if (!Segment->Connections[1].ControlPoint->IsSplineSelected()) { SelectControlPoint(Segment->Connections[1].ControlPoint); } } } void SelectAdjacentSegments() { for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { for (const FLandscapeSplineConnection& Connection : ControlPoint->ConnectedSegments) { if (!Connection.Segment->IsSplineSelected()) { SelectSegment(Connection.Segment); } } } } void AddSegment(ULandscapeSplineControlPoint* Start, ULandscapeSplineControlPoint* End, bool bAutoRotateStart, bool bAutoRotateEnd) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_AddSegment", "Add Landscape Spline Segment")); if (Start == End) { //UE_LOG( TEXT("Can't join spline control point to itself.") ); return; } if (Start->GetOuterULandscapeSplinesComponent() != End->GetOuterULandscapeSplinesComponent()) { //UE_LOG( TEXT("Can't join spline control points across different terrains.") ); return; } for (const FLandscapeSplineConnection& Connection : Start->ConnectedSegments) { // if the *other* end on the connected segment connects to the "end" control point... if (Connection.GetFarConnection().ControlPoint == End) { //UE_LOG( TEXT("Spline control points already joined connected!") ); return; } } ULandscapeSplinesComponent* SplinesComponent = Start->GetOuterULandscapeSplinesComponent(); SplinesComponent->Modify(); Start->Modify(); End->Modify(); ULandscapeSplineSegment* NewSegment = ConstructObject(ULandscapeSplineSegment::StaticClass(), SplinesComponent, NAME_None, RF_Transactional); SplinesComponent->Segments.Add(NewSegment); NewSegment->Connections[0].ControlPoint = Start; NewSegment->Connections[1].ControlPoint = End; NewSegment->Connections[0].SocketName = Start->GetBestConnectionTo(End->Location); NewSegment->Connections[1].SocketName = End->GetBestConnectionTo(Start->Location); FVector StartLocation; FRotator StartRotation; Start->GetConnectionLocationAndRotation(NewSegment->Connections[0].SocketName, StartLocation, StartRotation); FVector EndLocation; FRotator EndRotation; End->GetConnectionLocationAndRotation(NewSegment->Connections[1].SocketName, EndLocation, EndRotation); // Set up tangent lengths NewSegment->Connections[0].TangentLen = (EndLocation - StartLocation).Size(); NewSegment->Connections[1].TangentLen = NewSegment->Connections[0].TangentLen; NewSegment->AutoFlipTangents(); // set up other segment options if (Start->ConnectedSegments.Num() > 0) { NewSegment->LayerName = Start->ConnectedSegments[0].Segment->LayerName; NewSegment->SplineMeshes = Start->ConnectedSegments[0].Segment->SplineMeshes; NewSegment->LDMaxDrawDistance = Start->ConnectedSegments[0].Segment->LDMaxDrawDistance; NewSegment->bRaiseTerrain = Start->ConnectedSegments[0].Segment->bRaiseTerrain; NewSegment->bLowerTerrain = Start->ConnectedSegments[0].Segment->bLowerTerrain; NewSegment->bEnableCollision = Start->ConnectedSegments[0].Segment->bEnableCollision; NewSegment->bCastShadow = Start->ConnectedSegments[0].Segment->bCastShadow; } else if (End->ConnectedSegments.Num() > 0) { NewSegment->LayerName = End->ConnectedSegments[0].Segment->LayerName; NewSegment->SplineMeshes = End->ConnectedSegments[0].Segment->SplineMeshes; NewSegment->LDMaxDrawDistance = End->ConnectedSegments[0].Segment->LDMaxDrawDistance; NewSegment->bRaiseTerrain = End->ConnectedSegments[0].Segment->bRaiseTerrain; NewSegment->bLowerTerrain = End->ConnectedSegments[0].Segment->bLowerTerrain; NewSegment->bEnableCollision = End->ConnectedSegments[0].Segment->bEnableCollision; NewSegment->bCastShadow = End->ConnectedSegments[0].Segment->bCastShadow; } else { // Use defaults } Start->ConnectedSegments.Add(FLandscapeSplineConnection(NewSegment, 0)); End->ConnectedSegments.Add(FLandscapeSplineConnection(NewSegment, 1)); if (bAutoRotateStart) { Start->AutoCalcRotation(); Start->UpdateSplinePoints(); } if (bAutoRotateEnd) { End->AutoCalcRotation(); End->UpdateSplinePoints(); } // Control points' points are currently based on connected segments, so need to be updated. if (Start->Mesh != NULL) { Start->UpdateSplinePoints(); } if (End->Mesh != NULL) { Start->UpdateSplinePoints(); } NewSegment->UpdateSplinePoints(); } void AddControlPoint(ALandscapeProxy* Landscape, const FVector& LocalLocation) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_AddControlPoint", "Add Landscape Spline Control Point")); Landscape->SplineComponent->Modify(); ULandscapeSplineControlPoint* NewControlPoint = ConstructObject(ULandscapeSplineControlPoint::StaticClass(), Landscape->SplineComponent, NAME_None, RF_Transactional); Landscape->SplineComponent->ControlPoints.Add(NewControlPoint); NewControlPoint->Location = LocalLocation; if (SelectedSplineControlPoints.Num() > 0) { ULandscapeSplineControlPoint* FirstPoint = *SelectedSplineControlPoints.CreateConstIterator(); NewControlPoint->Rotation = (NewControlPoint->Location - FirstPoint->Location).Rotation(); NewControlPoint->Width = FirstPoint->Width; NewControlPoint->SideFalloff = FirstPoint->SideFalloff; NewControlPoint->EndFalloff = FirstPoint->EndFalloff; if (bCopyMeshToNewControlPoint) { NewControlPoint->Mesh = FirstPoint->Mesh; NewControlPoint->MeshScale = FirstPoint->MeshScale; NewControlPoint->bEnableCollision = FirstPoint->bEnableCollision; NewControlPoint->bCastShadow = FirstPoint->bCastShadow; } for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { AddSegment(ControlPoint, NewControlPoint, bAutoRotateOnJoin, true); } } ClearSelection(); SelectControlPoint(NewControlPoint); UpdatePropertiesWindows(); if (!Landscape->SplineComponent->IsRegistered()) { Landscape->SplineComponent->RegisterComponent(); } else { Landscape->SplineComponent->MarkRenderStateDirty(); } } void DeleteSegment(ULandscapeSplineSegment* ToDelete, bool bInDeleteLooseEnds) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_DeleteSegment", "Delete Landscape Spline Segment")); ULandscapeSplinesComponent* SplinesComponent = ToDelete->GetOuterULandscapeSplinesComponent(); SplinesComponent->Modify(); ToDelete->Modify(); ToDelete->DeleteSplinePoints(); ToDelete->Connections[0].ControlPoint->Modify(); ToDelete->Connections[1].ControlPoint->Modify(); ToDelete->Connections[0].ControlPoint->ConnectedSegments.Remove(FLandscapeSplineConnection(ToDelete, 0)); ToDelete->Connections[1].ControlPoint->ConnectedSegments.Remove(FLandscapeSplineConnection(ToDelete, 1)); if (bInDeleteLooseEnds) { if (ToDelete->Connections[0].ControlPoint->ConnectedSegments.Num() == 0) { SplinesComponent->ControlPoints.Remove(ToDelete->Connections[0].ControlPoint); } if (ToDelete->Connections[1].ControlPoint != ToDelete->Connections[0].ControlPoint && ToDelete->Connections[1].ControlPoint->ConnectedSegments.Num() == 0) { SplinesComponent->ControlPoints.Remove(ToDelete->Connections[1].ControlPoint); } } SplinesComponent->Segments.Remove(ToDelete); // Control points' points are currently based on connected segments, so need to be updated. if (ToDelete->Connections[0].ControlPoint->Mesh != NULL) { ToDelete->Connections[0].ControlPoint->UpdateSplinePoints(); } if (ToDelete->Connections[1].ControlPoint->Mesh != NULL) { ToDelete->Connections[1].ControlPoint->UpdateSplinePoints(); } SplinesComponent->MarkRenderStateDirty(); } void DeleteControlPoint(ULandscapeSplineControlPoint* ToDelete, bool bInDeleteLooseEnds) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_DeleteControlPoint", "Delete Landscape Spline Control Point")); ULandscapeSplinesComponent* SplinesComponent = ToDelete->GetOuterULandscapeSplinesComponent(); SplinesComponent->Modify(); ToDelete->Modify(); ToDelete->DeleteSplinePoints(); if (ToDelete->ConnectedSegments.Num() == 2 && ToDelete->ConnectedSegments[0].Segment != ToDelete->ConnectedSegments[1].Segment) { int32 Result = FMessageDialog::Open(EAppMsgType::YesNoCancel, LOCTEXT("WantToJoinControlPoint", "Control point has two segments attached, do you want to join them?")); switch (Result) { case EAppReturnType::Yes: { // Copy the other end of connection 1 into the near end of connection 0, then delete connection 1 TArray& Connections = ToDelete->ConnectedSegments; Connections[0].Segment->Modify(); Connections[1].Segment->Modify(); Connections[0].GetNearConnection() = Connections[1].GetFarConnection(); Connections[0].Segment->UpdateSplinePoints(); Connections[1].Segment->DeleteSplinePoints(); // Get the control point at the *other* end of the segment and remove it from it ULandscapeSplineControlPoint* OtherEnd = Connections[1].GetFarConnection().ControlPoint; OtherEnd->Modify(); FLandscapeSplineConnection* OtherConnection = OtherEnd->ConnectedSegments.FindByKey(FLandscapeSplineConnection(Connections[1].Segment, 1 - Connections[1].End)); *OtherConnection = FLandscapeSplineConnection(Connections[0].Segment, Connections[0].End); SplinesComponent->Segments.Remove(Connections[1].Segment); ToDelete->ConnectedSegments.Empty(); SplinesComponent->ControlPoints.Remove(ToDelete); SplinesComponent->MarkRenderStateDirty(); return; } break; case EAppReturnType::No: // Use the "delete all segments" code below break; case EAppReturnType::Cancel: // Do nothing return; } } for (FLandscapeSplineConnection& Connection : ToDelete->ConnectedSegments) { Connection.Segment->Modify(); Connection.Segment->DeleteSplinePoints(); // Get the control point at the *other* end of the segment and remove it from it ULandscapeSplineControlPoint* OtherEnd = Connection.GetFarConnection().ControlPoint; OtherEnd->Modify(); OtherEnd->ConnectedSegments.Remove(FLandscapeSplineConnection(Connection.Segment, 1 - Connection.End)); SplinesComponent->Segments.Remove(Connection.Segment); if (bInDeleteLooseEnds) { if (OtherEnd != ToDelete && OtherEnd->ConnectedSegments.Num() == 0) { SplinesComponent->ControlPoints.Remove(OtherEnd); } } } ToDelete->ConnectedSegments.Empty(); SplinesComponent->ControlPoints.Remove(ToDelete); SplinesComponent->MarkRenderStateDirty(); } void SplitSegment(ULandscapeSplineSegment* Segment, const FVector& LocalLocation) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_SplitSegment", "Split Landscape Spline Segment")); ULandscapeSplinesComponent* SplinesComponent = Segment->GetOuterULandscapeSplinesComponent(); SplinesComponent->Modify(); Segment->Modify(); Segment->Connections[1].ControlPoint->Modify(); float t; FVector Location; FVector Tangent; Segment->FindNearest(LocalLocation, t, Location, Tangent); ULandscapeSplineControlPoint* NewControlPoint = ConstructObject(ULandscapeSplineControlPoint::StaticClass(), SplinesComponent, NAME_None, RF_Transactional); SplinesComponent->ControlPoints.Add(NewControlPoint); NewControlPoint->Location = Location; NewControlPoint->Rotation = Tangent.Rotation(); NewControlPoint->Rotation.Roll = FMath::Lerp(Segment->Connections[0].ControlPoint->Rotation.Roll, Segment->Connections[1].ControlPoint->Rotation.Roll, t); NewControlPoint->Width = FMath::Lerp(Segment->Connections[0].ControlPoint->Width, Segment->Connections[1].ControlPoint->Width, t); NewControlPoint->SideFalloff = FMath::Lerp(Segment->Connections[0].ControlPoint->SideFalloff, Segment->Connections[1].ControlPoint->SideFalloff, t); NewControlPoint->EndFalloff = FMath::Lerp(Segment->Connections[0].ControlPoint->EndFalloff, Segment->Connections[1].ControlPoint->EndFalloff, t); ULandscapeSplineSegment* NewSegment = ConstructObject(ULandscapeSplineSegment::StaticClass(), SplinesComponent, NAME_None, RF_Transactional); SplinesComponent->Segments.Add(NewSegment); NewSegment->Connections[0].ControlPoint = NewControlPoint; NewSegment->Connections[0].TangentLen = Tangent.Size() * (1 - t); NewSegment->Connections[0].ControlPoint->ConnectedSegments.Add(FLandscapeSplineConnection(NewSegment, 0)); NewSegment->Connections[1].ControlPoint = Segment->Connections[1].ControlPoint; NewSegment->Connections[1].TangentLen = Segment->Connections[1].TangentLen * (1 - t); NewSegment->Connections[1].ControlPoint->ConnectedSegments.Add(FLandscapeSplineConnection(NewSegment, 1)); NewSegment->LayerName = Segment->LayerName; NewSegment->SplineMeshes = Segment->SplineMeshes; NewSegment->LDMaxDrawDistance = Segment->LDMaxDrawDistance; NewSegment->bRaiseTerrain = Segment->bRaiseTerrain; NewSegment->bLowerTerrain = Segment->bLowerTerrain; NewSegment->bEnableCollision = Segment->bEnableCollision; NewSegment->bCastShadow = Segment->bCastShadow; Segment->Connections[0].TangentLen *= t; Segment->Connections[1].ControlPoint->ConnectedSegments.Remove(FLandscapeSplineConnection(Segment, 1)); Segment->Connections[1].ControlPoint = NewControlPoint; Segment->Connections[1].TangentLen = -Tangent.Size() * t; Segment->Connections[1].ControlPoint->ConnectedSegments.Add(FLandscapeSplineConnection(Segment, 1)); Segment->UpdateSplinePoints(); NewSegment->UpdateSplinePoints(); ClearSelection(); UpdatePropertiesWindows(); SplinesComponent->MarkRenderStateDirty(); } void FlipSegment(ULandscapeSplineSegment* Segment) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_FlipSegment", "Flip Landscape Spline Segment")); ULandscapeSplinesComponent* SplinesComponent = Segment->GetOuterULandscapeSplinesComponent(); SplinesComponent->Modify(); Segment->Modify(); Segment->Connections[0].ControlPoint->Modify(); Segment->Connections[1].ControlPoint->Modify(); Segment->Connections[0].ControlPoint->ConnectedSegments.FindByKey(FLandscapeSplineConnection(Segment, 0))->End = 1; Segment->Connections[1].ControlPoint->ConnectedSegments.FindByKey(FLandscapeSplineConnection(Segment, 1))->End = 0; Swap(Segment->Connections[0], Segment->Connections[1]); Segment->UpdateSplinePoints(); } void SnapControlPointToGround(ULandscapeSplineControlPoint* ControlPoint) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_SnapToGround", "Snap Landscape Spline to Ground")); ULandscapeSplinesComponent* SplinesComponent = ControlPoint->GetOuterULandscapeSplinesComponent(); SplinesComponent->Modify(); ControlPoint->Modify(); const FTransform LocalToWorld = SplinesComponent->GetComponentToWorld(); const FVector Start = LocalToWorld.TransformPosition(ControlPoint->Location); const FVector End = Start + FVector(0, 0, -HALF_WORLD_MAX); static FName TraceTag = FName(TEXT("SnapLandscapeSplineControlPointToGround")); FHitResult Hit; UWorld* World = SplinesComponent->GetWorld(); check(World); if (World->LineTraceSingle(Hit, Start, End, FCollisionQueryParams(true), FCollisionObjectQueryParams(ECC_WorldStatic))) { ControlPoint->Location = LocalToWorld.InverseTransformPosition(Hit.Location); ControlPoint->UpdateSplinePoints(); SplinesComponent->MarkRenderStateDirty(); } } void ShowSplineProperties() { TArray Objects; Objects.Reset(SelectedSplineControlPoints.Num() + SelectedSplineSegments.Num()); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { Objects.Add(ControlPoint); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { Objects.Add(Segment); } FPropertyEditorModule& PropertyModule = FModuleManager::Get().LoadModuleChecked(TEXT("PropertyEditor")); if (!PropertyModule.HasUnlockedDetailViews()) { PropertyModule.CreateFloatingDetailsView(Objects, true); } else { PropertyModule.UpdatePropertyViews(Objects); } } virtual bool BeginTool(FEditorViewportClient* ViewportClient, const FLandscapeToolTarget& InTarget, const FVector& InHitLocation) override { if (ViewportClient->IsCtrlPressed()) { LandscapeInfo = InTarget.LandscapeInfo.Get(); ALandscapeProxy* Landscape = LandscapeInfo->GetCurrentLevelLandscapeProxy(true); if (!Landscape) { return false; } if (!Landscape->SplineComponent) { CreateSplineComponent(Landscape, FVector(1.f) / Landscape->GetRootComponent()->RelativeScale3D); } const FTransform LandscapeToSpline = Landscape->LandscapeActorToWorld().GetRelativeTransform(Landscape->SplineComponent->ComponentToWorld); AddControlPoint(Landscape, LandscapeToSpline.TransformPosition(InHitLocation)); GUnrealEd->RedrawLevelEditingViewports(); return true; } return false; } virtual void EndTool(FEditorViewportClient* ViewportClient) override { LandscapeInfo = NULL; } virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y) override { FVector HitLocation; if (EdMode->LandscapeMouseTrace(ViewportClient, x, y, HitLocation)) { //if( bToolActive ) //{ // // Apply tool // ApplyTool(ViewportClient); //} } return true; } virtual void ApplyTool(FEditorViewportClient* ViewportClient) { } virtual bool HandleClick(HHitProxy* HitProxy, const FViewportClick& Click) override { if ((!HitProxy || !HitProxy->IsA(HWidgetAxis::StaticGetType())) && !Click.IsShiftDown()) { ClearSelection(); UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); } if (HitProxy) { ULandscapeSplineControlPoint* ClickedControlPoint = NULL; ULandscapeSplineSegment* ClickedSplineSegment = NULL; if (HitProxy->IsA(HLandscapeSplineProxy_ControlPoint::StaticGetType())) { HLandscapeSplineProxy_ControlPoint* SplineProxy = (HLandscapeSplineProxy_ControlPoint*)HitProxy; ClickedControlPoint = SplineProxy->ControlPoint; } else if (HitProxy->IsA(HLandscapeSplineProxy_Segment::StaticGetType())) { HLandscapeSplineProxy_Segment* SplineProxy = (HLandscapeSplineProxy_Segment*)HitProxy; ClickedSplineSegment = SplineProxy->SplineSegment; } else if (HitProxy->IsA(HActor::StaticGetType())) { HActor* ActorProxy = (HActor*)HitProxy; AActor* Actor = ActorProxy->Actor; ULandscapeSplinesComponent* SplineComponent = Actor->FindComponentByClass(); if (SplineComponent != NULL) { if (const UControlPointMeshComponent* ControlPointMeshComponent = Cast(ActorProxy->PrimComponent)) { for (ULandscapeSplineControlPoint* ControlPoint : SplineComponent->ControlPoints) { if (ControlPoint->OwnsComponent(ControlPointMeshComponent)) { ClickedControlPoint = ControlPoint; break; } } } else if (const USplineMeshComponent* SplineMeshComponent = Cast(ActorProxy->PrimComponent)) { for (ULandscapeSplineSegment* SplineSegment : SplineComponent->Segments) { if (SplineSegment->OwnsComponent(SplineMeshComponent)) { ClickedSplineSegment = SplineSegment; break; } } } } } if (ClickedControlPoint != NULL) { if (Click.IsShiftDown() && ClickedControlPoint->IsSplineSelected()) { DeselectControlPoint(ClickedControlPoint); } else { SelectControlPoint(ClickedControlPoint); } GEditor->SelectNone(true, true); UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); return true; } else if (ClickedSplineSegment != NULL) { // save info about what we grabbed if (Click.IsShiftDown() && ClickedSplineSegment->IsSplineSelected()) { DeSelectSegment(ClickedSplineSegment); } else { SelectSegment(ClickedSplineSegment); } GEditor->SelectNone(true, true); UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); return true; } } return false; } virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent) override { if (InKey == EKeys::F4 && InEvent == IE_Pressed) { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { ShowSplineProperties(); return true; } } if (InKey == EKeys::R && InEvent == IE_Pressed) { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_AutoRotate", "Auto-rotate Landscape Spline Control Points")); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { ControlPoint->AutoCalcRotation(); ControlPoint->UpdateSplinePoints(); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { Segment->Connections[0].ControlPoint->AutoCalcRotation(); Segment->Connections[0].ControlPoint->UpdateSplinePoints(); Segment->Connections[1].ControlPoint->AutoCalcRotation(); Segment->Connections[1].ControlPoint->UpdateSplinePoints(); } return true; } } if (InKey == EKeys::F && InEvent == IE_Pressed) { if (SelectedSplineSegments.Num() > 0) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_FlipSegments", "Flip Landscape Spline Segments")); for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { FlipSegment(Segment); } return true; } } if (InKey == EKeys::T && InEvent == IE_Pressed) { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_AutoFlipTangents", "Auto-flip Landscape Spline Tangents")); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { ControlPoint->AutoFlipTangents(); ControlPoint->UpdateSplinePoints(); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { Segment->Connections[0].ControlPoint->AutoFlipTangents(); Segment->Connections[0].ControlPoint->UpdateSplinePoints(); Segment->Connections[1].ControlPoint->AutoFlipTangents(); Segment->Connections[1].ControlPoint->UpdateSplinePoints(); } return true; } } if (InKey == EKeys::End && InEvent == IE_Pressed) { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_SnapToGround", "Snap Landscape Spline to Ground")); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { SnapControlPointToGround(ControlPoint); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { SnapControlPointToGround(Segment->Connections[0].ControlPoint); SnapControlPointToGround(Segment->Connections[1].ControlPoint); } UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); return true; } } if (InKey == EKeys::A && InEvent == IE_Pressed && IsCtrlDown(InViewport)) { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { SelectConnected(); UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); return true; } } if (SelectedSplineControlPoints.Num() > 0) { if (InKey == EKeys::LeftMouseButton && InEvent == IE_Pressed && IsCtrlDown(InViewport)) { int32 HitX = InViewport->GetMouseX(); int32 HitY = InViewport->GetMouseY(); HHitProxy* HitProxy = InViewport->GetHitProxy(HitX, HitY); if (HitProxy != NULL) { ULandscapeSplineControlPoint* ClickedControlPoint = NULL; if (HitProxy->IsA(HLandscapeSplineProxy_ControlPoint::StaticGetType())) { HLandscapeSplineProxy_ControlPoint* SplineProxy = (HLandscapeSplineProxy_ControlPoint*)HitProxy; ClickedControlPoint = SplineProxy->ControlPoint; } else if (HitProxy->IsA(HActor::StaticGetType())) { HActor* ActorProxy = (HActor*)HitProxy; ULandscapeSplinesComponent* SplineComponent = ActorProxy->Actor->FindComponentByClass(); if (SplineComponent != NULL) { const UControlPointMeshComponent* ControlPointMeshComponent = Cast(ActorProxy->PrimComponent); if (ControlPointMeshComponent != NULL) { for (ULandscapeSplineControlPoint* ControlPoint : SplineComponent->ControlPoints) { if (ControlPoint->OwnsComponent(ControlPointMeshComponent)) { ClickedControlPoint = ControlPoint; break; } } } } } if (ClickedControlPoint != NULL) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_AddSegment", "Add Landscape Spline Segment")); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { AddSegment(ControlPoint, ClickedControlPoint, bAutoRotateOnJoin, bAutoRotateOnJoin); } GUnrealEd->RedrawLevelEditingViewports(); return true; } } } } if (SelectedSplineControlPoints.Num() == 0) { if (InKey == EKeys::LeftMouseButton && InEvent == IE_Pressed && IsCtrlDown(InViewport)) { int32 HitX = InViewport->GetMouseX(); int32 HitY = InViewport->GetMouseY(); HHitProxy* HitProxy = InViewport->GetHitProxy(HitX, HitY); if (HitProxy) { ULandscapeSplineSegment* ClickedSplineSegment = NULL; FTransform LandscapeToSpline; if (HitProxy->IsA(HLandscapeSplineProxy_Segment::StaticGetType())) { HLandscapeSplineProxy_Segment* SplineProxy = (HLandscapeSplineProxy_Segment*)HitProxy; ClickedSplineSegment = SplineProxy->SplineSegment; LandscapeToSpline = ClickedSplineSegment->GetTypedOuter()->ActorToWorld().GetRelativeTransform(ClickedSplineSegment->GetTypedOuter()->ComponentToWorld); } else if (HitProxy->IsA(HActor::StaticGetType())) { HActor* ActorProxy = (HActor*)HitProxy; const USplineMeshComponent* SplineMeshComponent = Cast(ActorProxy->PrimComponent); if (SplineMeshComponent != NULL) { ULandscapeSplinesComponent* SplineComponent = ActorProxy->Actor->FindComponentByClass(); if (SplineComponent != NULL) { for (ULandscapeSplineSegment* SplineSegment : SplineComponent->Segments) { if (SplineSegment->OwnsComponent(SplineMeshComponent)) { ClickedSplineSegment = SplineSegment; LandscapeToSpline = ActorProxy->Actor->ActorToWorld().GetRelativeTransform(SplineComponent->ComponentToWorld); break; } } } } } if (ClickedSplineSegment != NULL) { FVector HitLocation; if (EdMode->LandscapeMouseTrace(InViewportClient, HitLocation)) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_SplitSegment", "Split Landscape Spline Segment")); SplitSegment(ClickedSplineSegment, LandscapeToSpline.TransformPosition(HitLocation)); GUnrealEd->RedrawLevelEditingViewports(); } return true; } } } } if (InKey == EKeys::LeftMouseButton) { // Press mouse button if (InEvent == IE_Pressed) { // See if we clicked on a spline handle.. int32 HitX = InViewport->GetMouseX(); int32 HitY = InViewport->GetMouseY(); HHitProxy* HitProxy = InViewport->GetHitProxy(HitX, HitY); if (HitProxy) { if (HitProxy->IsA(HWidgetAxis::StaticGetType())) { checkSlow(SelectedSplineControlPoints.Num() > 0); bMovingControlPoint = true; GEditor->BeginTransaction(LOCTEXT("LandscapeSpline_ModifyControlPoint", "Modify Landscape Spline Control Point")); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { ControlPoint->Modify(); ControlPoint->GetOuterULandscapeSplinesComponent()->Modify(); } return false; // We're not actually handling this case ourselves, just wrapping it in a transaction } else if (HitProxy->IsA(HLandscapeSplineProxy_Tangent::StaticGetType())) { HLandscapeSplineProxy_Tangent* SplineProxy = (HLandscapeSplineProxy_Tangent*)HitProxy; DraggingTangent_Segment = SplineProxy->SplineSegment; DraggingTangent_End = SplineProxy->End; GEditor->BeginTransaction(LOCTEXT("LandscapeSpline_ModifyTangent", "Modify Landscape Spline Tangent")); ULandscapeSplinesComponent* SplinesComponent = DraggingTangent_Segment->GetOuterULandscapeSplinesComponent(); SplinesComponent->Modify(); DraggingTangent_Segment->Modify(); return false; // false to let FEditorViewportClient.InputKey start mouse tracking and enable InputDelta() so we can use it } } } else if (InEvent == IE_Released) { if (bMovingControlPoint) { bMovingControlPoint = false; for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { ControlPoint->UpdateSplinePoints(true); } GEditor->EndTransaction(); return false; // We're not actually handling this case ourselves, just wrapping it in a transaction } else if (DraggingTangent_Segment) { DraggingTangent_Segment->UpdateSplinePoints(true); DraggingTangent_Segment = NULL; GEditor->EndTransaction(); return false; // false to let FEditorViewportClient.InputKey end mouse tracking } } } return false; } virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override { FVector Drag = InDrag; if (DraggingTangent_Segment) { const ULandscapeSplinesComponent* SplinesComponent = DraggingTangent_Segment->GetOuterULandscapeSplinesComponent(); FLandscapeSplineSegmentConnection& Connection = DraggingTangent_Segment->Connections[DraggingTangent_End]; FVector StartLocation; FRotator StartRotation; Connection.ControlPoint->GetConnectionLocationAndRotation(Connection.SocketName, StartLocation, StartRotation); float OldTangentLen = Connection.TangentLen; Connection.TangentLen += SplinesComponent->ComponentToWorld.InverseTransformVector(-Drag) | StartRotation.Vector(); // Disallow a tangent of exactly 0 if (Connection.TangentLen == 0) { if (OldTangentLen > 0) { Connection.TangentLen = SMALL_NUMBER; } else { Connection.TangentLen = -SMALL_NUMBER; } } // Flipping the tangent is only allowed if not using a socket if (Connection.SocketName != NAME_None) { Connection.TangentLen = FMath::Max(SMALL_NUMBER, Connection.TangentLen); } DraggingTangent_Segment->UpdateSplinePoints(false); return true; } if (SelectedSplineControlPoints.Num() > 0 && InViewportClient->GetCurrentWidgetAxis() != EAxisList::None) { for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { const ULandscapeSplinesComponent* SplinesComponent = ControlPoint->GetOuterULandscapeSplinesComponent(); ControlPoint->Location += SplinesComponent->ComponentToWorld.InverseTransformVector(Drag); FVector RotAxis; float RotAngle; InRot.Quaternion().ToAxisAndAngle(RotAxis, RotAngle); RotAxis = (SplinesComponent->ComponentToWorld.GetRotation().Inverse() * ControlPoint->Rotation.Quaternion().Inverse()).RotateVector(RotAxis); // Hack: for some reason FQuat.Rotator() Clamps to 0-360 range, so use .GetNormalized() to recover the original negative rotation. ControlPoint->Rotation += FQuat(RotAxis, RotAngle).Rotator().GetNormalized(); ControlPoint->Rotation.Yaw = FRotator::NormalizeAxis(ControlPoint->Rotation.Yaw); ControlPoint->Rotation.Pitch = FMath::Clamp(ControlPoint->Rotation.Pitch, -85.0f, 85.0f); ControlPoint->Rotation.Roll = FMath::Clamp(ControlPoint->Rotation.Roll, -85.0f, 85.0f); if (bAutoChangeConnectionsOnMove) { ControlPoint->AutoSetConnections(true); } ControlPoint->UpdateSplinePoints(false); } return true; } return false; } void FixSelection() { SelectedSplineControlPoints.Empty(); SelectedSplineSegments.Empty(); if (EdMode->CurrentTool != NULL && EdMode->CurrentTool == this) { for (const FLandscapeListInfo& Info : EdMode->GetLandscapeList()) { ALandscape* Landscape = Info.Info->LandscapeActor.Get(); if (Landscape != NULL && Landscape->SplineComponent != NULL) { for (ULandscapeSplineControlPoint* ControlPoint : Landscape->SplineComponent->ControlPoints) { if (ControlPoint->IsSplineSelected()) { SelectedSplineControlPoints.Add(ControlPoint); } } for (ULandscapeSplineSegment* Segment : Landscape->SplineComponent->Segments) { if (Segment->IsSplineSelected()) { SelectedSplineSegments.Add(Segment); } } } for (ALandscapeProxy* LandscapeProxy : Info.Info->Proxies) { if (LandscapeProxy != NULL && LandscapeProxy->SplineComponent != NULL) { for (ULandscapeSplineControlPoint* ControlPoint : LandscapeProxy->SplineComponent->ControlPoints) { if (ControlPoint->IsSplineSelected()) { SelectedSplineControlPoints.Add(ControlPoint); } } for (ULandscapeSplineSegment* Segment : LandscapeProxy->SplineComponent->Segments) { if (Segment->IsSplineSelected()) { SelectedSplineSegments.Add(Segment); } } } } } } else { for (const FLandscapeListInfo& Info : EdMode->GetLandscapeList()) { ALandscape* Landscape = Info.Info->LandscapeActor.Get(); if (Landscape != NULL && Landscape->SplineComponent != NULL) { for (ULandscapeSplineControlPoint* ControlPoint : Landscape->SplineComponent->ControlPoints) { ControlPoint->SetSplineSelected(false); } for (ULandscapeSplineSegment* Segment : Landscape->SplineComponent->Segments) { Segment->SetSplineSelected(false); } } for (ALandscapeProxy* LandscapeProxy : Info.Info->Proxies) { if (LandscapeProxy != NULL && LandscapeProxy->SplineComponent != NULL) { for (ULandscapeSplineControlPoint* ControlPoint : LandscapeProxy->SplineComponent->ControlPoints) { ControlPoint->SetSplineSelected(false); } for (ULandscapeSplineSegment* Segment : LandscapeProxy->SplineComponent->Segments) { Segment->SetSplineSelected(false); } } } } } } void OnUndo() { FixSelection(); UpdatePropertiesWindows(); } virtual void EnterTool() override { GEditor->SelectNone(true, true, false); for (const FLandscapeListInfo& Info : EdMode->GetLandscapeList()) { ALandscapeProxy* Landscape = Info.Info->GetCurrentLevelLandscapeProxy(true); if (Landscape != NULL && Landscape->SplineComponent != NULL) { Landscape->SplineComponent->ShowSplineEditorMesh(true); } } } virtual void ExitTool() override { ClearSelection(); UpdatePropertiesWindows(); for (const FLandscapeListInfo& Info : EdMode->GetLandscapeList()) { ALandscapeProxy* Landscape = Info.Info->GetCurrentLevelLandscapeProxy(true); if (Landscape != NULL && Landscape->SplineComponent != NULL) { Landscape->SplineComponent->ShowSplineEditorMesh(false); } } } virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override { for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { const ULandscapeSplinesComponent* SplinesComponent = ControlPoint->GetOuterULandscapeSplinesComponent(); FVector HandlePos0 = SplinesComponent->ComponentToWorld.TransformPosition(ControlPoint->Location + ControlPoint->Rotation.Vector() * -20); FVector HandlePos1 = SplinesComponent->ComponentToWorld.TransformPosition(ControlPoint->Location + ControlPoint->Rotation.Vector() * 20); DrawDashedLine(PDI, HandlePos0, HandlePos1, FColor::White, 20, SDPG_Foreground); if (GLevelEditorModeTools().GetWidgetMode() == FWidget::WM_Scale) { for (const FLandscapeSplineConnection& Connection : ControlPoint->ConnectedSegments) { FVector StartLocation; FRotator StartRotation; Connection.GetNearConnection().ControlPoint->GetConnectionLocationAndRotation(Connection.GetNearConnection().SocketName, StartLocation, StartRotation); FVector StartPos = SplinesComponent->ComponentToWorld.TransformPosition(StartLocation); FVector HandlePos = SplinesComponent->ComponentToWorld.TransformPosition(StartLocation + StartRotation.Vector() * Connection.GetNearConnection().TangentLen / 2); PDI->DrawLine(StartPos, HandlePos, FColor::White, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(new HLandscapeSplineProxy_Tangent(Connection.Segment, Connection.End)); PDI->DrawPoint(HandlePos, FColor(255, 255, 255), 10.f, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(NULL); } } } if (GLevelEditorModeTools().GetWidgetMode() == FWidget::WM_Scale) { for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { const ULandscapeSplinesComponent* SplinesComponent = Segment->GetOuterULandscapeSplinesComponent(); for (int32 End = 0; End <= 1; End++) { const FLandscapeSplineSegmentConnection& Connection = Segment->Connections[End]; FVector StartLocation; FRotator StartRotation; Connection.ControlPoint->GetConnectionLocationAndRotation(Connection.SocketName, StartLocation, StartRotation); FVector EndPos = SplinesComponent->ComponentToWorld.TransformPosition(StartLocation); FVector EndHandlePos = SplinesComponent->ComponentToWorld.TransformPosition(StartLocation + StartRotation.Vector() * Connection.TangentLen / 2); PDI->DrawLine(EndPos, EndHandlePos, FColor::White, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(new HLandscapeSplineProxy_Tangent(Segment, !!End)); PDI->DrawPoint(EndHandlePos, FColor(255, 255, 255), 10.f, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(NULL); } } } } virtual bool OverrideSelection() const override { return true; } virtual bool IsSelectionAllowed(AActor* InActor, bool bInSelection) const override { // Only filter selection not deselection if (bInSelection) { return false; } return true; } virtual bool UsesTransformWidget() const override { if (SelectedSplineControlPoints.Num() > 0) { return true; } return false; } virtual EAxisList::Type GetWidgetAxisToDraw(FWidget::EWidgetMode CheckMode) const override { if (SelectedSplineControlPoints.Num() > 0) { //if (CheckMode == FWidget::WM_Rotate // && SelectedSplineControlPoints.Num() >= 2) //{ // return AXIS_X; //} //else if (CheckMode != FWidget::WM_Scale) { return EAxisList::XYZ; } else { return EAxisList::None; } } return EAxisList::None; } virtual FVector GetWidgetLocation() const override { if (SelectedSplineControlPoints.Num() > 0) { ULandscapeSplineControlPoint* FirstPoint = *SelectedSplineControlPoints.CreateConstIterator(); ULandscapeSplinesComponent* SplinesComponent = FirstPoint->GetOuterULandscapeSplinesComponent(); return SplinesComponent->ComponentToWorld.TransformPosition(FirstPoint->Location); } return FVector::ZeroVector; } virtual FMatrix GetWidgetRotation() const override { if (SelectedSplineControlPoints.Num() > 0) { ULandscapeSplineControlPoint* FirstPoint = *SelectedSplineControlPoints.CreateConstIterator(); ULandscapeSplinesComponent* SplinesComponent = FirstPoint->GetOuterULandscapeSplinesComponent(); return FQuatRotationTranslationMatrix(FirstPoint->Rotation.Quaternion() * SplinesComponent->ComponentToWorld.GetRotation(), FVector::ZeroVector); } return FMatrix::Identity; } virtual EEditAction::Type GetActionEditDuplicate() override { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { return EEditAction::Process; } return EEditAction::Skip; } virtual EEditAction::Type GetActionEditDelete() override { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { return EEditAction::Process; } return EEditAction::Skip; } virtual EEditAction::Type GetActionEditCut() override { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { return EEditAction::Process; } return EEditAction::Skip; } virtual EEditAction::Type GetActionEditCopy() override { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { return EEditAction::Process; } return EEditAction::Skip; } virtual EEditAction::Type GetActionEditPaste() override { FString PasteString; FPlatformMisc::ClipboardPaste(PasteString); if (PasteString.StartsWith("BEGIN SPLINES")) { return EEditAction::Process; } return EEditAction::Skip; } virtual bool ProcessEditDuplicate() override { InternalProcessEditDuplicate(); return true; } virtual bool ProcessEditDelete() override { InternalProcessEditDelete(); return true; } virtual bool ProcessEditCut() override { InternalProcessEditCut(); return true; } virtual bool ProcessEditCopy() override { InternalProcessEditCopy(); return true; } virtual bool ProcessEditPaste() override { InternalProcessEditPaste(); return true; } void InternalProcessEditDuplicate() { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_Duplicate", "Duplicate Landscape Splines")); FString Data; InternalProcessEditCopy(&Data); InternalProcessEditPaste(&Data, true); } } void InternalProcessEditDelete() { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_Delete", "Delete Landscape Splines")); for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { DeleteControlPoint(ControlPoint, bDeleteLooseEnds); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { DeleteSegment(Segment, bDeleteLooseEnds); } ClearSelection(); UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); } } void InternalProcessEditCut() { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_Cut", "Cut Landscape Splines")); InternalProcessEditCopy(); InternalProcessEditDelete(); } } void InternalProcessEditCopy(FString* OutData = NULL) { if (SelectedSplineControlPoints.Num() > 0 || SelectedSplineSegments.Num() > 0) { TArray Objects; Objects.Reserve(SelectedSplineControlPoints.Num() + SelectedSplineSegments.Num() * 3); // worst case // Control Points then segments for (ULandscapeSplineControlPoint* ControlPoint : SelectedSplineControlPoints) { Objects.Add(ControlPoint); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { Objects.AddUnique(Segment->Connections[0].ControlPoint); Objects.AddUnique(Segment->Connections[1].ControlPoint); } for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { Objects.Add(Segment); } // Perform export to text format FStringOutputDevice Ar; Ar.Logf(TEXT("Begin Splines\r\n")); for (UObject* Object : Objects) { UExporter::ExportToOutputDevice(NULL, Object, NULL, Ar, TEXT("copy"), 3, PPF_None, false); } Ar.Logf(TEXT("End Splines\r\n")); if (OutData != NULL) { *OutData = MoveTemp(Ar); } else { FPlatformMisc::ClipboardCopy(*Ar); } } } void InternalProcessEditPaste(FString* InData = NULL, bool bOffset = false) { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_Paste", "Paste Landscape Splines")); ALandscapeProxy* Landscape = EdMode->CurrentToolTarget.LandscapeInfo->GetCurrentLevelLandscapeProxy(true); if (!Landscape) { return; } if (!Landscape->SplineComponent) { CreateSplineComponent(Landscape, FVector(1.f) / Landscape->GetRootComponent()->RelativeScale3D); } const TCHAR* Data = NULL; FString PasteString; if (InData != NULL) { Data = **InData; } else { FPlatformMisc::ClipboardPaste(PasteString); Data = *PasteString; } FLandscapeSplineTextObjectFactory Factory; TArray OutObjects = Factory.ImportSplines(Landscape->SplineComponent, Data); if (bOffset) { for (UObject* Object : OutObjects) { ULandscapeSplineControlPoint* ControlPoint = Cast(Object); if (ControlPoint != NULL) { Landscape->SplineComponent->ControlPoints.Add(ControlPoint); ControlPoint->Location += FVector(500, 500, 0); ControlPoint->UpdateSplinePoints(); } } } } protected: // Begin FEditorUndoClient virtual void PostUndo(bool bSuccess) override { OnUndo(); } virtual void PostRedo(bool bSuccess) override { PostUndo(bSuccess); } // End of FEditorUndoClient protected: FEdModeLandscape* EdMode; ULandscapeInfo* LandscapeInfo; TSet SelectedSplineControlPoints; TSet SelectedSplineSegments; ULandscapeSplineSegment* DraggingTangent_Segment; uint32 DraggingTangent_End : 1; uint32 bMovingControlPoint : 1; uint32 bAutoRotateOnJoin : 1; uint32 bAutoChangeConnectionsOnMove : 1; uint32 bDeleteLooseEnds : 1; uint32 bCopyMeshToNewControlPoint : 1; friend FEdModeLandscape; }; void FEdModeLandscape::ShowSplineProperties() { if (SplinesTool /*&& SplinesTool == CurrentTool*/) { SplinesTool->ShowSplineProperties(); } } void FEdModeLandscape::SelectAllConnectedSplineControlPoints() { if (SplinesTool /*&& SplinesTool == CurrentTool*/) { SplinesTool->SelectAdjacentControlPoints(); SplinesTool->ClearSelectedSegments(); SplinesTool->SelectConnected(); SplinesTool->UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); } } void FEdModeLandscape::SelectAllConnectedSplineSegments() { if (SplinesTool /*&& SplinesTool == CurrentTool*/) { SplinesTool->SelectAdjacentSegments(); SplinesTool->ClearSelectedControlPoints(); SplinesTool->SelectConnected(); SplinesTool->UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); } } void FEdModeLandscape::SplineMoveToCurrentLevel() { FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_AddControlPoint", "Add Landscape Spline Control Point")); if (SplinesTool /*&& SplinesTool == CurrentTool*/) { // Select all connected control points SplinesTool->SelectAdjacentSegments(); SplinesTool->SelectAdjacentControlPoints(); SplinesTool->SelectConnected(); TSet FromProxies; ALandscapeProxy* Landscape = NULL; for (ULandscapeSplineControlPoint* ControlPoint : SplinesTool->SelectedSplineControlPoints) { ULandscapeSplinesComponent* LandscapeSplinesComp = ControlPoint->GetOuterULandscapeSplinesComponent(); ALandscapeProxy* FromProxy = LandscapeSplinesComp ? Cast(LandscapeSplinesComp->GetOuter()) : NULL; if (FromProxy) { if (!Landscape) { ULandscapeInfo* LandscapeInfo = FromProxy->GetLandscapeInfo(false); check(LandscapeInfo); Landscape = LandscapeInfo->GetCurrentLevelLandscapeProxy(true); if (!Landscape) { // No Landscape Proxy, don't support for creating only for Spline now return; } } if (Landscape != FromProxy) { Landscape->Modify(); if (Landscape->SplineComponent == NULL) { SplinesTool->CreateSplineComponent(Landscape, FromProxy->SplineComponent->RelativeScale3D); } const FTransform OldToNewTransform = Landscape->SplineComponent->ComponentToWorld.GetRelativeTransform(FromProxy->SplineComponent->ComponentToWorld); Landscape->SplineComponent->Modify(); if (FromProxies.Find(FromProxy) == NULL) { FromProxies.Add(FromProxy); FromProxy->Modify(); FromProxy->SplineComponent->Modify(); FromProxy->SplineComponent->MarkRenderStateDirty(); } // Add/Remove Control Point/Segment/MeshComponents FromProxy->SplineComponent->ControlPoints.Remove(ControlPoint); ControlPoint->Rename(NULL, Landscape->SplineComponent); Landscape->SplineComponent->ControlPoints.Add(ControlPoint); ControlPoint->Location = OldToNewTransform.TransformPosition(ControlPoint->Location); if (ControlPoint->MeshComponent) { UControlPointMeshComponent* ControlPointMeshComp = ControlPoint->MeshComponent; ControlPointMeshComp->Modify(); ControlPointMeshComp->UnregisterComponent(); ControlPointMeshComp->DetachFromParent(true); ControlPointMeshComp->InvalidateLightingCache(); ControlPointMeshComp->Rename(NULL, Landscape); ControlPointMeshComp->AttachTo(Landscape->SplineComponent, NAME_None, EAttachLocation::KeepWorldPosition); } ControlPoint->UpdateSplinePoints(true, false); } } } for (ULandscapeSplineSegment* Segment : SplinesTool->SelectedSplineSegments) { ULandscapeSplinesComponent* LandscapeSplinesComp = Segment->GetOuterULandscapeSplinesComponent(); ALandscapeProxy* FromProxy = LandscapeSplinesComp ? Cast(LandscapeSplinesComp->GetOuter()) : NULL; if (FromProxy) { if (!Landscape) { ULandscapeInfo* LandscapeInfo = FromProxy->GetLandscapeInfo(false); check(LandscapeInfo); Landscape = LandscapeInfo->GetCurrentLevelLandscapeProxy(true); if (!Landscape) { // No Landscape Proxy, don't support for creating only for Spline now return; } } if (Landscape != FromProxy) { Landscape->Modify(); if (Landscape->SplineComponent == NULL) { SplinesTool->CreateSplineComponent(Landscape, FromProxy->SplineComponent->RelativeScale3D); } Landscape->SplineComponent->Modify(); if (FromProxies.Find(FromProxy) == NULL) { FromProxies.Add(FromProxy); FromProxy->Modify(); FromProxy->SplineComponent->Modify(); FromProxy->SplineComponent->MarkRenderStateDirty(); } // Add/Remove Control Point/Segment/MeshComponents FromProxy->SplineComponent->Segments.Remove(Segment); Segment->Rename(NULL, Landscape->SplineComponent); Landscape->SplineComponent->Segments.Add(Segment); for (USplineMeshComponent* SplineMeshComp : Segment->MeshComponents) { SplineMeshComp->Modify(); SplineMeshComp->UnregisterComponent(); SplineMeshComp->DetachFromParent(true); SplineMeshComp->InvalidateLightingCache(); SplineMeshComp->Rename(NULL, Landscape); SplineMeshComp->AttachTo(Landscape->SplineComponent, NAME_None, EAttachLocation::KeepWorldPosition); } Segment->UpdateSplinePoints(); } } } if (Landscape && Landscape->SplineComponent) { if (!Landscape->SplineComponent->IsRegistered()) { Landscape->SplineComponent->RegisterComponent(); } else { Landscape->SplineComponent->MarkRenderStateDirty(); } } SplinesTool->ClearSelection(); SplinesTool->UpdatePropertiesWindows(); GUnrealEd->RedrawLevelEditingViewports(); } } void FEdModeLandscape::SetbUseAutoRotateOnJoin(bool InbAutoRotateOnJoin) { if (SplinesTool /*&& SplinesTool == CurrentTool*/) { SplinesTool->bAutoRotateOnJoin = InbAutoRotateOnJoin; } } bool FEdModeLandscape::GetbUseAutoRotateOnJoin() { if (SplinesTool /*&& SplinesTool == CurrentTool*/) { return SplinesTool->bAutoRotateOnJoin; } return true; // default value } void FEdModeLandscape::InitializeTool_Splines() { auto Tool_Splines = MakeUnique(this); Tool_Splines->ValidBrushes.Add("BrushSet_Splines"); SplinesTool = Tool_Splines.Get(); LandscapeTools.Add(MoveTemp(Tool_Splines)); } #undef LOCTEXT_NAMESPACE