249 const TArray<UActorComponent*>& Components,
250 int32 SubsectionSizeQuads,
251 int32 NumSubsections,
252 int32 SampleFrequency,
253 const TArray<FHitDisplacementEntry>& HitDisplacementMap)
256 if (Components.Num() == 0)
259 int32 ComponentSizeQuads = SubsectionSizeQuads * NumSubsections;
261 if (ComponentSizeQuads == 0)
265 TEXT(
"Skipping landscape generation, SubsectionSizeQuads=%i NumSubsections=%i"),
271 UWorld* World = Components[0]->GetWorld();
273 FBox3d Bounds = FBox3d(ForceInit);
275 for (UActorComponent* Component : Components)
277 UPrimitiveComponent* PrimitiveComponent = Cast<UPrimitiveComponent>(Component);
278 if (PrimitiveComponent !=
nullptr)
280 FBox BoxBounds = PrimitiveComponent->Bounds.GetBox();
285 BoxBounds.GetCenter(),
286 BoxBounds.GetExtent(),
295 FVector3d Max = Bounds.Max;
296 FVector3d Min = Bounds.Min;
297 FVector3d Range = Max - Min;
298 FVector3d Center = Bounds.GetCenter();
300 FVector3d RangeMeters = Range * UE_CM_TO_M;
301 FIntPoint DesiredVertexCount = FIntPoint(
302 FMath::Max(1, (int32)FMath::CeilToInt(RangeMeters.X * SampleFrequency) + 1),
303 FMath::Max(1, (int32)FMath::CeilToInt(RangeMeters.Y * SampleFrequency) + 1));
305 FIntPoint DesiredQuadCount = (DesiredVertexCount - FIntPoint(1)).ComponentMax(FIntPoint(1));
306 FIntPoint ComponentSizeQuads2 = FIntPoint(ComponentSizeQuads);
307 FIntPoint RequiredQuadCount =
308 ((DesiredQuadCount + ComponentSizeQuads2 - 1) / ComponentSizeQuads2) *
310 FIntPoint HeightmapExtent =
314 TArray<uint16_t> HeightmapData;
315 HeightmapData.SetNumZeroed(HeightmapExtent.X * HeightmapExtent.Y);
317 FVector2d CellSize = FVector2d(Range) / (FVector2d(HeightmapExtent) - FVector2d(1.0));
320 const TSet<UActorComponent*> ComponentMap(Components);
321 TArray<TPair<FVector3d, FVector3d>> Failures;
322 FCriticalSection FailuresCS;
326 constexpr auto RingSampleCount = 8U;
327 const double I2R = 2.0 * PI / (double)RingSampleCount;
329 double MaxOffsetRadius = CellSize.GetMin() * 0.5;
331 FVector2d OffsetRing[RingSampleCount];
332 for (
size_t i = 0; i != RingSampleCount; ++i)
335 FMath::SinCos(&D.Y, &D.X, (
double)i * I2R);
339 FCollisionShape TestGeometry;
346 auto LockedPhysObject = FPhysicsObjectExternalInterface::LockRead(
347 World->GetPhysicsScene());
349 ParallelFor(HeightmapData.Num(), [&](int32 Index)
351 int32 Y = Index / HeightmapExtent.X;
352 int32 X = Index % HeightmapExtent.X;
354 FVector2d XY = FVector2d(Min) + CellSize * FVector2d(X, Y);
355 FVector3d Begin0 = FVector3d(XY.X, XY.Y, Max.Z);
356 FVector3d End0 = FVector3d(XY.X, XY.Y, Min.Z);
357 FVector3d Begin = Begin0;
358 FVector3d End = End0;
362 FCollisionQueryParams CQParams = FCollisionQueryParams::DefaultQueryParam;
363 CQParams.bTraceComplex = true;
364 CQParams.bFindInitialOverlaps = true;
365 CQParams.bReturnPhysicalMaterial = false;
366 CQParams.MobilityType = EQueryMobilityType::Any;
367 CQParams.bIgnoreTouches = true;
369 FRandomStream PRNG(Index);
371 if (!World->ParallelSweepSingleByChannel(
376 ECollisionChannel::ECC_WorldStatic,
380 HeightmapData[Index] = 0;
385 for (; Retry < MaxRetries; ++Retry)
387 if (World->ParallelLineTraceSingleByChannel(
390 ECollisionChannel::ECC_GameTraceChannel2,
393 if (ComponentMap.Contains(Hit.GetComponent()))
395 HitZ = Hit.Location.Z;
398 CQParams.AddIgnoredComponent(Hit.GetComponent());
402 int32 Index = PRNG.RandRange(0, (int32)RingSampleCount - 1);
403 FVector3d Offset = FVector3d(OffsetRing[Index], 0.0);
404 Offset *= PRNG.FRandRange(0.0, MaxOffsetRadius);
405 Begin = Begin0 + Offset;
410 if (Retry != MaxRetries)
412 UPrimitiveComponent* HitComponent = Hit.GetComponent();
414 double ZDisplacement = 0.0;
415 for (
auto& [Pattern, Displacement] : HitDisplacementMap)
417 if (std::abs(Displacement) <= std::abs(ZDisplacement))
419 TestName = UKismetSystemLibrary::GetDisplayName(HitComponent);
420 if (TestName.MatchesWildcard(Pattern))
422 ZDisplacement = Displacement;
425 TestName = UKismetSystemLibrary::GetDisplayName(HitComponent->GetOwner());
426 if (TestName.MatchesWildcard(Pattern))
428 ZDisplacement = Displacement;
432 HitZ += ZDisplacement;
437 HitZ = std::clamp(HitZ, 0.0, 1.0);
438 HeightmapData[Index] = (uint16)std::lround(
439 HitZ * TNumericLimits<uint16>::Max());
444 Failures.Add({ Begin, End });
446 HeightmapData[Index] = 0;
448 }, EParallelForFlags::Unbalanced);
449 LockedPhysObject.Release();
464 for (int32 Index = 0; Index != HeightmapData.Num(); ++Index)
466 int32 Y = Index / HeightmapExtent.X;
467 int32 X = Index % HeightmapExtent.X;
468 FVector2d XY = FVector2d(Min) + CellSize * FVector2d(X, Y);
469 FVector3d Begin = FVector3d(XY.X, XY.Y, Max.Z);
470 FVector3d End = FVector3d(XY.X, XY.Y, Min.Z);
471 DrawDebugLine(World, Begin, End, FColor::Green,
false, 10.0F);
477 for (
auto [Begin, End] : Failures)
478 DrawDebugLine(World, Begin, End, FColor::Red,
false, 20.0F);
481 if (Failures.Num() != 0 &&
false)
483 FString FailureString;
484 for (
auto [Begin, End] : Failures)
486 FailureString += FString::Printf(
487 TEXT(
"Failed to trace against world from (%f, %f, %f) to (%f, %f, %f), too many objects.\n"),
488 Begin.X, Begin.Y, Begin.Z,
489 End.X, End.Y, End.Z);
491 FFileHelper::SaveStringToFile(
493 *FString::Printf(TEXT(
"TraceFailures.txt")));
497 FActorSpawnParameters SpawnParams;
498 ALandscape* Landscape = World->SpawnActor<ALandscape>(
499 ALandscape::StaticClass(),
500 FVector3d::ZeroVector,
501 FRotator::ZeroRotator,
504 FGuid LandscapeGUID = FGuid::NewGuid();
506 Landscape->ComponentSizeQuads = ComponentSizeQuads;
507 Landscape->SubsectionSizeQuads = SubsectionSizeQuads;
508 Landscape->NumSubsections = NumSubsections;
510 TMap<FGuid, TArray<uint16>> LayerHeightMaps;
511 LayerHeightMaps.Add(FGuid(), MoveTemp(HeightmapData));
513 TMap<FGuid, TArray<FLandscapeImportLayerInfo>> LayerImportInfos;
514 LayerImportInfos.Add(FGuid(), TArray<FLandscapeImportLayerInfo>());
516 FVector3d LandscapeScale = FVector3d(CellSize, 100.0);
517 Landscape->SetActorTransform(FTransform(
525 HeightmapExtent.X - 1, HeightmapExtent.Y - 1,
526 NumSubsections, SubsectionSizeQuads,
530 ELandscapeImportAlphamapType::Additive,
531 TArrayView<const FLandscapeLayer>());
533 Landscape->SetLandscapeGuid(LandscapeGUID);
535 Landscape->CreateLandscapeInfo();
537 Landscape->ReregisterAllComponents();
538 Landscape->RecreateCollisionComponents();
539 Landscape->PostEditChange();
540 Landscape->MarkPackageDirty();