void RasterizeSegmentPoints(ULandscapeInfo* LandscapeInfo, TArray<FLandscapeSplineInterpPoint> Points, const FTransform& SplineToWorld, bool bRaiseTerrain, bool bLowerTerrain, ULandscapeLayerInfoObject* LayerInfo) { ALandscapeProxy* LandscapeProxy = LandscapeInfo->GetLandscapeProxy(); const FTransform SplineToLandscape = SplineToWorld.GetRelativeTransform(LandscapeProxy->LandscapeActorToWorld()); FLandscapeEditDataInterface LandscapeEdit(LandscapeInfo); TSet<ULandscapeComponent*> ModifiedComponents; // I'd dearly love to use FIntRect in this code, but Landscape works with "Inclusive Max" and FIntRect is "Exclusive Max" int32 LandscapeMinX, LandscapeMinY, LandscapeMaxX, LandscapeMaxY; if (!LandscapeInfo->GetLandscapeExtent(LandscapeMinX, LandscapeMinY, LandscapeMaxX, LandscapeMaxY)) { return; } FBox SegmentBounds = FBox(0); for (const FLandscapeSplineInterpPoint& Point : Points) { SegmentBounds += Point.FalloffLeft; SegmentBounds += Point.FalloffRight; } SegmentBounds = SegmentBounds.TransformBy(SplineToLandscape.ToMatrixWithScale()); int32 MinX = FMath::CeilToInt(SegmentBounds.Min.X); int32 MinY = FMath::CeilToInt(SegmentBounds.Min.Y); int32 MaxX = FMath::FloorToInt(SegmentBounds.Max.X); int32 MaxY = FMath::FloorToInt(SegmentBounds.Max.Y); MinX = FMath::Max(MinX, LandscapeMinX); MinY = FMath::Max(MinY, LandscapeMinY); MaxX = FMath::Min(MaxX, LandscapeMaxX); MaxY = FMath::Min(MaxY, LandscapeMaxY); if (MinX > MaxX || MinY > MaxY) { // The segment's bounds don't intersect the landscape, so skip it entirely return; } for (int32 j = 0; j < Points.Num(); j++) { Points[j].Center = SplineToLandscape.TransformPosition(Points[j].Center); Points[j].Left = SplineToLandscape.TransformPosition(Points[j].Left); Points[j].Right = SplineToLandscape.TransformPosition(Points[j].Right); Points[j].FalloffLeft = SplineToLandscape.TransformPosition(Points[j].FalloffLeft); Points[j].FalloffRight = SplineToLandscape.TransformPosition(Points[j].FalloffRight); // local-heights to texture value heights Points[j].Left.Z = Points[j].Left.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].Right.Z = Points[j].Right.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].FalloffLeft.Z = Points[j].FalloffLeft.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].FalloffRight.Z = Points[j].FalloffRight.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; } // Heights raster if (bRaiseTerrain || bLowerTerrain) { RasterizeSegmentHeight(MinX, MinY, MaxX, MaxY, LandscapeEdit, Points, bRaiseTerrain, bLowerTerrain, ModifiedComponents); if (MinX > MaxX || MinY > MaxY) { // The segment's bounds don't intersect any data, so we skip it entirely // it wouldn't intersect any weightmap data either so we don't even bother trying } } // Blend layer raster if (LayerInfo != NULL) { RasterizeSegmentAlpha(MinX, MinY, MaxX, MaxY, LandscapeEdit, Points, LayerInfo, ModifiedComponents); } LandscapeEdit.Flush(); for (ULandscapeComponent* Component : ModifiedComponents) { // 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); } } } }
bool ULandscapeInfo::ApplySplinesInternal(bool bOnlySelected, ALandscapeProxy* Landscape) { if (!Landscape || !Landscape->SplineComponent || Landscape->SplineComponent->ControlPoints.Num() == 0 || Landscape->SplineComponent->Segments.Num() == 0) { return false; } FScopedTransaction Transaction(LOCTEXT("LandscapeSpline_ApplySplines", "Apply Splines to Landscape")); const FTransform SplineToLandscape = Landscape->SplineComponent->ComponentToWorld.GetRelativeTransform(Landscape->LandscapeActorToWorld()); FLandscapeEditDataInterface LandscapeEdit(this); TSet<ULandscapeComponent*> ModifiedComponents; // I'd dearly love to use FIntRect in this code, but Landscape works with "Inclusive Max" and FIntRect is "Exclusive Max" int32 LandscapeMinX, LandscapeMinY, LandscapeMaxX, LandscapeMaxY; if (!GetLandscapeExtent(LandscapeMinX, LandscapeMinY, LandscapeMaxX, LandscapeMaxY)) { return false; } for (const ULandscapeSplineControlPoint* ControlPoint : Landscape->SplineComponent->ControlPoints) { if (bOnlySelected && !ControlPoint->IsSplineSelected()) { continue; } if (ControlPoint->GetPoints().Num() < 2) { continue; } FBox ControlPointBounds = ControlPoint->GetBounds(); ControlPointBounds = ControlPointBounds.TransformBy(SplineToLandscape.ToMatrixWithScale()); int32 MinX = FMath::CeilToInt(ControlPointBounds.Min.X); int32 MinY = FMath::CeilToInt(ControlPointBounds.Min.Y); int32 MaxX = FMath::FloorToInt(ControlPointBounds.Max.X); int32 MaxY = FMath::FloorToInt(ControlPointBounds.Max.Y); MinX = FMath::Max(MinX, LandscapeMinX); MinY = FMath::Max(MinY, LandscapeMinY); MaxX = FMath::Min(MaxX, LandscapeMaxX); MaxY = FMath::Min(MaxY, LandscapeMaxY); if (MinX > MaxX || MinY > MaxY) { // The control point's bounds don't intersect the landscape, so skip it entirely continue; } TArray<FLandscapeSplineInterpPoint> Points = ControlPoint->GetPoints(); for (int32 j = 0; j < Points.Num(); j++) { Points[j].Center = SplineToLandscape.TransformPosition(Points[j].Center); Points[j].Left = SplineToLandscape.TransformPosition(Points[j].Left); Points[j].Right = SplineToLandscape.TransformPosition(Points[j].Right); Points[j].FalloffLeft = SplineToLandscape.TransformPosition(Points[j].FalloffLeft); Points[j].FalloffRight = SplineToLandscape.TransformPosition(Points[j].FalloffRight); // local-heights to texture value heights Points[j].Left.Z = Points[j].Left.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].Right.Z = Points[j].Right.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].FalloffLeft.Z = Points[j].FalloffLeft.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].FalloffRight.Z = Points[j].FalloffRight.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; } // Heights raster if (ControlPoint->bRaiseTerrain || ControlPoint->bLowerTerrain) { const FVector Center3D = SplineToLandscape.TransformPosition(ControlPoint->Location); RasterizeControlPointHeights(MinX, MinY, MaxX, MaxY, LandscapeEdit, Center3D, Points, ControlPoint->bRaiseTerrain, ControlPoint->bLowerTerrain, ModifiedComponents); } // Blend layer raster ULandscapeLayerInfoObject* LayerInfo = GetLayerInfoByName(ControlPoint->LayerName); if (ControlPoint->LayerName != NAME_None && LayerInfo != NULL) { const FVector Center3D = SplineToLandscape.TransformPosition(ControlPoint->Location); RasterizeControlPointAlpha(MinX, MinY, MaxX, MaxY, LandscapeEdit, Center3D, Points, LayerInfo, ModifiedComponents); } } for (const ULandscapeSplineSegment* Segment : Landscape->SplineComponent->Segments) { if (bOnlySelected && !Segment->IsSplineSelected()) { continue; } FBox SegmentBounds = Segment->GetBounds(); SegmentBounds = SegmentBounds.TransformBy(SplineToLandscape.ToMatrixWithScale()); int32 MinX = FMath::CeilToInt(SegmentBounds.Min.X); int32 MinY = FMath::CeilToInt(SegmentBounds.Min.Y); int32 MaxX = FMath::FloorToInt(SegmentBounds.Max.X); int32 MaxY = FMath::FloorToInt(SegmentBounds.Max.Y); MinX = FMath::Max(MinX, LandscapeMinX); MinY = FMath::Max(MinY, LandscapeMinY); MaxX = FMath::Min(MaxX, LandscapeMaxX); MaxY = FMath::Min(MaxY, LandscapeMaxY); if (MinX > MaxX || MinY > MaxY) { // The segment's bounds don't intersect the landscape, so skip it entirely continue; } TArray<FLandscapeSplineInterpPoint> Points = Segment->GetPoints(); for (int32 j = 0; j < Points.Num(); j++) { Points[j].Center = SplineToLandscape.TransformPosition(Points[j].Center); Points[j].Left = SplineToLandscape.TransformPosition(Points[j].Left); Points[j].Right = SplineToLandscape.TransformPosition(Points[j].Right); Points[j].FalloffLeft = SplineToLandscape.TransformPosition(Points[j].FalloffLeft); Points[j].FalloffRight = SplineToLandscape.TransformPosition(Points[j].FalloffRight); // local-heights to texture value heights Points[j].Left.Z = Points[j].Left.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].Right.Z = Points[j].Right.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].FalloffLeft.Z = Points[j].FalloffLeft.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; Points[j].FalloffRight.Z = Points[j].FalloffRight.Z * LANDSCAPE_INV_ZSCALE + LandscapeDataAccess::MidValue; } // Heights raster if (Segment->bRaiseTerrain || Segment->bLowerTerrain) { RasterizeSegmentHeight(MinX, MinY, MaxX, MaxY, LandscapeEdit, Points, Segment->bRaiseTerrain, Segment->bLowerTerrain, ModifiedComponents); if (MinX > MaxX || MinY > MaxY) { // The segment's bounds don't intersect any data, so we skip it entirely // it wouldn't intersect any weightmap data either so we don't even bother trying } } // Blend layer raster ULandscapeLayerInfoObject* LayerInfo = GetLayerInfoByName(Segment->LayerName); if (Segment->LayerName != NAME_None && LayerInfo != NULL) { RasterizeSegmentAlpha(MinX, MinY, MaxX, MaxY, LandscapeEdit, Points, LayerInfo, ModifiedComponents); } } LandscapeEdit.Flush(); for (ULandscapeComponent* Component : ModifiedComponents) { // 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); } } } return true; }