wip(tunnels): continuous world-core sweep, slope in degrees, bounded winding -- NOT MERGED: +18-30% worker CPU, visuals unconfirmed
Tunnel round from the owner's playtest. Luna's ablations traced the ribs to the world-space tunnel-core stage: disabling it removes them, while terracing, detail ops, roughness, the support floor, lateral warp, radius jitter, winding and LOD do not. The per-span capsule union is replaced by a deterministic Catmull-Rom sweep. Winding and graph expansion are now gated on bTunnelFloorEnabled (they ran even with the floor off). TunnelFloorGentleSlopeThreshold is authored in degrees (0-90, min 1 deg, DetSinCos conversion, legacy tangent values migrated: 0.3 -> 16.7 deg). The route length is capped at 6 waves x 640 = 3840 voxels, so 0.1 deg loads instead of freezing (2048 collapsed canonical reach; 24576 used ~2 GB). Room-wall trimming is behind voxel.TunnelMouthTrim (default OFF): with it on, a 36 m gap disconnects the walk. Canonical walk: 20,907 / 10,981, connected (was 20,830 / 10,909); exports identical with -voxel.CrtFma3=0; TilePostReachProof, DetSinCos and strate crossing pass. NOT merged because: one S/M pair vs the tip DLL shows worker CPU +17.8% static / +30.3% moving and worse p95; and the orchestrator's look at the before/after renders is inconclusive (camera not matched; regular grooves still visible in the after interior). Next: owner checks the look in game; if smooth, make the sweep cheap before merging; if not, drop the sweep and keep the degrees/bound/wind-gate parts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
bf9384a1d5
commit
0231072cc4
8 files changed
+1138
-248
No files matched your search
@@ -34,6 +34,7 @@
|
||||
#include "VoxelGenerator.h"
|
||||
#include "VoxelMarchingCubesMesher.h"
|
||||
#include "VoxelNoise.h"
|
||||
#include "VoxelPassageGeometry.h"
|
||||
#include "VoxelSettings.h"
|
||||
#include "VoxelStackSampler.h"
|
||||
#include "VoxelStrateDefinition.h"
|
||||
@@ -213,6 +214,49 @@ bool ParseRenderCamera(const FString& Text, FVector& OutCamera)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseDensitySlice(
|
||||
const FString& Text,
|
||||
FVector& OutOrigin,
|
||||
FVector& OutDirection,
|
||||
int32& OutSamples,
|
||||
float& OutStep)
|
||||
{
|
||||
TArray<FString> Components;
|
||||
Text.ParseIntoArray(Components, TEXT(","), true);
|
||||
if (Components.Num() != 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const float OriginX = FCString::Atof(*Components[0]);
|
||||
const float OriginY = FCString::Atof(*Components[1]);
|
||||
const float OriginZ = FCString::Atof(*Components[2]);
|
||||
const float DirectionX = FCString::Atof(*Components[3]);
|
||||
const float DirectionY = FCString::Atof(*Components[4]);
|
||||
const float DirectionZ = FCString::Atof(*Components[5]);
|
||||
const int32 Samples = FCString::Atoi(*Components[6]);
|
||||
const float Step = FCString::Atof(*Components[7]);
|
||||
if (!FMath::IsFinite(OriginX) || !FMath::IsFinite(OriginY)
|
||||
|| !FMath::IsFinite(OriginZ) || !FMath::IsFinite(DirectionX)
|
||||
|| !FMath::IsFinite(DirectionY) || !FMath::IsFinite(DirectionZ)
|
||||
|| !FMath::IsFinite(Step) || Samples < 2 || Samples > 4096
|
||||
|| Step <= 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FVector Direction(DirectionX, DirectionY, DirectionZ);
|
||||
if (!Direction.Normalize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
OutOrigin = FVector(OriginX, OriginY, OriginZ);
|
||||
OutDirection = Direction;
|
||||
OutSamples = Samples;
|
||||
OutStep = Step;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NeedsOriginInWindow(ECaveGeneratorType Archetype)
|
||||
{
|
||||
// This is the same topology policy as VoxelForgePlayerFitWindow.h. That header is test-only,
|
||||
@@ -261,8 +305,11 @@ float BoundarySealForArchetype(
|
||||
struct FExploreArguments
|
||||
{
|
||||
int32 Seed = 0;
|
||||
bool bSeedOverride = false;
|
||||
ECaveGeneratorType Archetype = ECaveGeneratorType::Maze;
|
||||
int32 Slot = 4;
|
||||
FString StrateReference;
|
||||
FString SettingsReference;
|
||||
bool bRender = true;
|
||||
bool bWalk = true;
|
||||
bool bExport = true;
|
||||
@@ -296,6 +343,8 @@ struct FExploreArguments
|
||||
float BudgetMinutes = DefaultBudgetMinutes;
|
||||
bool bSurfaceRoughnessOverride = false;
|
||||
float SurfaceRoughness = 0.0f;
|
||||
bool bTunnelWarpStrengthOverride = false;
|
||||
float TunnelWarpStrength = 0.0f;
|
||||
bool bRoughnessNoiseTypeOverride = false;
|
||||
EVoxelNoiseType RoughnessNoiseType = EVoxelNoiseType::FBM;
|
||||
bool bRoughnessFrequencyOverride = false;
|
||||
@@ -322,6 +371,13 @@ struct FExploreArguments
|
||||
int32 PassageFloorWrites = 1;
|
||||
bool bRenderCameraOverride = false;
|
||||
FVector RenderCamera = FVector::ZeroVector;
|
||||
bool bRenderTargetOverride = false;
|
||||
FVector RenderTarget = FVector::ZeroVector;
|
||||
bool bDensitySlice = false;
|
||||
FVector DensitySliceOrigin = FVector::ZeroVector;
|
||||
FVector DensitySliceDirection = FVector::ForwardVector;
|
||||
int32 DensitySliceSamples = 257;
|
||||
float DensitySliceStep = 0.5f;
|
||||
|
||||
bool IsDensityProfilingEnabled() const
|
||||
{
|
||||
@@ -349,9 +405,11 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
FString ModesText = TEXT("render,walk,export");
|
||||
FString OutText;
|
||||
|
||||
FParse::Value(*Params, TEXT("seed="), OutArguments.Seed);
|
||||
OutArguments.bSeedOverride = FParse::Value(*Params, TEXT("seed="), OutArguments.Seed);
|
||||
FParse::Value(*Params, TEXT("archetype="), ArchetypeText);
|
||||
FParse::Value(*Params, TEXT("slot="), OutArguments.Slot);
|
||||
FParse::Value(*Params, TEXT("strateref="), OutArguments.StrateReference);
|
||||
FParse::Value(*Params, TEXT("settingsref="), OutArguments.SettingsReference);
|
||||
FParse::Value(*Params, TEXT("modes="), ModesText);
|
||||
// FParse::Value treats a comma as a command-line separator in some UE commandlet
|
||||
// launch paths. Recover the complete token so both -modes=walk,export and the
|
||||
@@ -427,6 +485,11 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
const bool bSurfaceRoughnessParsed = FParse::Value(
|
||||
*Params, TEXT("surfaceroughness="), OutArguments.SurfaceRoughness);
|
||||
OutArguments.bSurfaceRoughnessOverride = bSurfaceRoughnessSpecified;
|
||||
const bool bTunnelWarpStrengthSpecified = Params.Contains(
|
||||
TEXT("tunnelwarp="), ESearchCase::IgnoreCase);
|
||||
const bool bTunnelWarpStrengthParsed = FParse::Value(
|
||||
*Params, TEXT("tunnelwarp="), OutArguments.TunnelWarpStrength);
|
||||
OutArguments.bTunnelWarpStrengthOverride = bTunnelWarpStrengthSpecified;
|
||||
FString RoughnessNoiseTypeText;
|
||||
const bool bRoughnessNoiseTypeSpecified = Params.Contains(
|
||||
TEXT("roughnesstype="), ESearchCase::IgnoreCase);
|
||||
@@ -510,6 +573,53 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
}
|
||||
}
|
||||
OutArguments.bRenderCameraOverride = bRenderCameraSpecified;
|
||||
|
||||
FString RenderTargetText;
|
||||
const bool bRenderTargetSpecified = Params.Contains(
|
||||
TEXT("rendertarget="), ESearchCase::IgnoreCase);
|
||||
bool bRenderTargetParsed = false;
|
||||
if (bRenderTargetSpecified)
|
||||
{
|
||||
const int32 TargetOffset = Params.Find(TEXT("rendertarget="), ESearchCase::IgnoreCase);
|
||||
if (TargetOffset != INDEX_NONE)
|
||||
{
|
||||
RenderTargetText = Params.Mid(TargetOffset + 13);
|
||||
const int32 TargetEnd = RenderTargetText.Find(TEXT(" "));
|
||||
if (TargetEnd != INDEX_NONE)
|
||||
{
|
||||
RenderTargetText.LeftInline(TargetEnd);
|
||||
}
|
||||
RenderTargetText.TrimQuotesInline();
|
||||
bRenderTargetParsed = ParseRenderCamera(RenderTargetText, OutArguments.RenderTarget);
|
||||
}
|
||||
}
|
||||
OutArguments.bRenderTargetOverride = bRenderTargetSpecified;
|
||||
|
||||
FString DensitySliceText;
|
||||
const bool bDensitySliceSpecified = Params.Contains(
|
||||
TEXT("densityslice="), ESearchCase::IgnoreCase);
|
||||
bool bDensitySliceParsed = false;
|
||||
if (bDensitySliceSpecified)
|
||||
{
|
||||
const int32 SliceOffset = Params.Find(TEXT("densityslice="), ESearchCase::IgnoreCase);
|
||||
if (SliceOffset != INDEX_NONE)
|
||||
{
|
||||
DensitySliceText = Params.Mid(SliceOffset + 13);
|
||||
const int32 SliceEnd = DensitySliceText.Find(TEXT(" "));
|
||||
if (SliceEnd != INDEX_NONE)
|
||||
{
|
||||
DensitySliceText.LeftInline(SliceEnd);
|
||||
}
|
||||
DensitySliceText.TrimQuotesInline();
|
||||
bDensitySliceParsed = ParseDensitySlice(
|
||||
DensitySliceText,
|
||||
OutArguments.DensitySliceOrigin,
|
||||
OutArguments.DensitySliceDirection,
|
||||
OutArguments.DensitySliceSamples,
|
||||
OutArguments.DensitySliceStep);
|
||||
}
|
||||
}
|
||||
OutArguments.bDensitySlice = bDensitySliceSpecified;
|
||||
int32 FailureFocusRender = 0;
|
||||
FParse::Value(*Params, TEXT("failurefocus="), FailureFocusRender);
|
||||
OutArguments.bFailureFocusRender = FailureFocusRender != 0;
|
||||
@@ -656,6 +766,14 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
OutError = TEXT("surfaceroughness must be finite and greater than or equal to zero.");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bTunnelWarpStrengthOverride
|
||||
&& (!bTunnelWarpStrengthParsed
|
||||
|| !FMath::IsFinite(OutArguments.TunnelWarpStrength)
|
||||
|| OutArguments.TunnelWarpStrength < 0.0f))
|
||||
{
|
||||
OutError = TEXT("tunnelwarp must be finite and greater than or equal to zero.");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bRoughnessNoiseTypeOverride && !bRoughnessNoiseTypeParsed)
|
||||
{
|
||||
OutError = TEXT("roughnesstype must be FBM, Ridged, Mixed, or Cellular.");
|
||||
@@ -704,9 +822,11 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
if (OutArguments.bTunnelFloorGentleOverride
|
||||
&& (!bTunnelFloorGentleParsed
|
||||
|| !FMath::IsFinite(OutArguments.TunnelFloorGentle)
|
||||
|| OutArguments.TunnelFloorGentle < 0.0f))
|
||||
|| OutArguments.TunnelFloorGentle < 0.0f
|
||||
|| OutArguments.TunnelFloorGentle
|
||||
> VoxelPassageGeometry::TunnelFloorGentleSlopeMaximumDegrees))
|
||||
{
|
||||
OutError = TEXT("tunnelfloorgentle must be finite and non-negative.");
|
||||
OutError = TEXT("tunnelfloorgentle must be finite degrees in [0,90].");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bTunnelFloorLedgesOverride
|
||||
@@ -756,6 +876,17 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
OutError = TEXT("rendercamera must be three finite comma-separated voxel coordinates.");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bRenderTargetOverride && !bRenderTargetParsed)
|
||||
{
|
||||
OutError = TEXT("rendertarget must be three finite comma-separated voxel coordinates.");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bDensitySlice && !bDensitySliceParsed)
|
||||
{
|
||||
OutError = TEXT(
|
||||
"densityslice must be origin x,y,z, direction x,y,z, sample count, and positive step.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OutText.IsEmpty())
|
||||
{
|
||||
@@ -946,64 +1077,242 @@ struct FExploreWorld
|
||||
|
||||
bool Build(const FExploreArguments& Arguments, FString& OutError)
|
||||
{
|
||||
const int32 NumStrates = Arguments.Slot + 2;
|
||||
const bool bUseAuthoredSettings = !Arguments.SettingsReference.IsEmpty();
|
||||
const bool bUseAuthoredStrate = !Arguments.StrateReference.IsEmpty();
|
||||
UVoxelStrateDefinition* AuthoredDefinition = nullptr;
|
||||
if (bUseAuthoredStrate)
|
||||
{
|
||||
AuthoredDefinition = LoadObject<UVoxelStrateDefinition>(
|
||||
nullptr, *Arguments.StrateReference);
|
||||
if (AuthoredDefinition == nullptr)
|
||||
{
|
||||
OutError = FString::Printf(
|
||||
TEXT("Could not load staged strate asset '%s'."),
|
||||
*Arguments.StrateReference);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bUseAuthoredSettings)
|
||||
{
|
||||
UVoxelSettings* AuthoredSettings = LoadObject<UVoxelSettings>(
|
||||
nullptr, *Arguments.SettingsReference);
|
||||
if (AuthoredSettings == nullptr)
|
||||
{
|
||||
OutError = FString::Printf(
|
||||
TEXT("Could not load staged settings asset '%s'."),
|
||||
*Arguments.SettingsReference);
|
||||
return false;
|
||||
}
|
||||
Settings = TStrongObjectPtr<UVoxelSettings>(
|
||||
DuplicateObject<UVoxelSettings>(AuthoredSettings, GetTransientPackage()));
|
||||
if (!Settings.IsValid())
|
||||
{
|
||||
OutError = TEXT("Could not duplicate the staged settings asset into the transient package.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings = TStrongObjectPtr<UVoxelSettings>(
|
||||
NewObject<UVoxelSettings>(GetTransientPackage(), NAME_None, RF_Transient));
|
||||
}
|
||||
|
||||
const int32 NumStrates = bUseAuthoredSettings
|
||||
? FMath::Max(Settings->TotalStrates, Arguments.Slot + 2)
|
||||
: Arguments.Slot + 2;
|
||||
if (NumStrates > MaxSyntheticStrates)
|
||||
{
|
||||
OutError = TEXT("The synthetic layout would exceed the commandlet strate cap.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Settings = TStrongObjectPtr<UVoxelSettings>(
|
||||
NewObject<UVoxelSettings>(GetTransientPackage(), NAME_None, RF_Transient));
|
||||
Settings->Seed = Arguments.Seed;
|
||||
if (!bUseAuthoredSettings)
|
||||
{
|
||||
Settings->Seed = Arguments.Seed;
|
||||
Settings->TotalStrates = NumStrates;
|
||||
Settings->InterStrateGapChunks = 0;
|
||||
Settings->WorldRadiusVoxels = 0.0f;
|
||||
Settings->EdgeSealThickness = 64.0f;
|
||||
Settings->OriginSpineRadius = 14.0f;
|
||||
Settings->bOpenSurfaceEntry = true;
|
||||
}
|
||||
else if (Arguments.bSeedOverride)
|
||||
{
|
||||
Settings->Seed = Arguments.Seed;
|
||||
}
|
||||
// The transient fixed-definition layout may need to extend an authored settings asset
|
||||
// for a requested interior slot. Keep that duplicate's advertised count consistent; the
|
||||
// loaded package remains untouched.
|
||||
Settings->TotalStrates = NumStrates;
|
||||
Settings->InterStrateGapChunks = 0;
|
||||
Settings->WorldRadiusVoxels = 0.0f;
|
||||
Settings->EdgeSealThickness = 64.0f;
|
||||
Settings->OriginSpineRadius = 14.0f;
|
||||
Settings->bOpenSurfaceEntry = true;
|
||||
|
||||
if (bUseAuthoredStrate)
|
||||
{
|
||||
// The owner’s snapshot states that every slot uses DA_Strate3. Clone it per slot so
|
||||
// commandlet overrides never mutate a loaded package or leak between slots.
|
||||
Settings->StratePool.Reset();
|
||||
Settings->FixedStrates.Reset();
|
||||
}
|
||||
Definitions.Reserve(NumStrates);
|
||||
for (int32 Index = 0; Index < NumStrates; ++Index)
|
||||
{
|
||||
UVoxelStrateDefinition* Definition = NewObject<UVoxelStrateDefinition>(
|
||||
GetTransientPackage(), NAME_None, RF_Transient);
|
||||
Definition->StrateName = FText::FromString(FString::Printf(
|
||||
TEXT("VoxelForgeExplore_%s_slot%d"),
|
||||
ArchetypeName(Index == Arguments.Slot
|
||||
? Arguments.Archetype
|
||||
: ECaveGeneratorType::TunnelNetwork),
|
||||
Index));
|
||||
Definition->StrateDescription = FText::FromString(
|
||||
TEXT("Transient, deterministic commandlet definition."));
|
||||
// The commandlet's acceptance sweep uses the owner-facing base spacing:
|
||||
// 4 chunks * 32 voxels * 0.25 m = 32 m per strate. Production assets may be taller;
|
||||
// this keeps the measured tunnel arithmetic tied to the stated ~32 m descent.
|
||||
Definition->StrateHeightInChunks = 4;
|
||||
Definition->TransitionType = EVoxelStrateTransition::Hard;
|
||||
Definition->GeneratorType = Index == Arguments.Slot
|
||||
? Arguments.Archetype
|
||||
: ECaveGeneratorType::TunnelNetwork;
|
||||
// Explicitly select the production switch so the same synthetic layout, seed and
|
||||
// mesher invocation can be measured through both density branches.
|
||||
Definition->bUseOperatorStack = Arguments.bUseOperatorStack;
|
||||
if (Arguments.bWormsEnabledOverride)
|
||||
UVoxelStrateDefinition* Definition = bUseAuthoredStrate
|
||||
? DuplicateObject<UVoxelStrateDefinition>(
|
||||
AuthoredDefinition, GetTransientPackage())
|
||||
: NewObject<UVoxelStrateDefinition>(
|
||||
GetTransientPackage(), NAME_None, RF_Transient);
|
||||
if (Definition == nullptr)
|
||||
{
|
||||
Definition->bEnableWorms = Arguments.bWormsEnabled;
|
||||
OutError = FString::Printf(
|
||||
TEXT("Could not create transient strate definition for slot %d."), Index);
|
||||
return false;
|
||||
}
|
||||
if (Arguments.bWormStrengthOverride
|
||||
&& (Definition->GeneratorType == ECaveGeneratorType::TunnelNetwork
|
||||
|| Definition->GeneratorType == ECaveGeneratorType::Underwater))
|
||||
if (!bUseAuthoredStrate)
|
||||
{
|
||||
Definition->GenerationParams.WormStrength = Arguments.WormStrength;
|
||||
Definition->StrateName = FText::FromString(FString::Printf(
|
||||
TEXT("VoxelForgeExplore_%s_slot%d"),
|
||||
ArchetypeName(Index == Arguments.Slot
|
||||
? Arguments.Archetype
|
||||
: ECaveGeneratorType::TunnelNetwork),
|
||||
Index));
|
||||
Definition->StrateDescription = FText::FromString(
|
||||
TEXT("Transient, deterministic commandlet definition."));
|
||||
// The commandlet's acceptance sweep uses the owner-facing base spacing:
|
||||
// 4 chunks * 32 voxels * 0.25 m = 32 m per strate. Production assets may be taller;
|
||||
// this keeps the measured tunnel arithmetic tied to the stated ~32 m descent.
|
||||
Definition->StrateHeightInChunks = 4;
|
||||
Definition->TransitionType = EVoxelStrateTransition::Hard;
|
||||
Definition->GeneratorType = Index == Arguments.Slot
|
||||
? Arguments.Archetype
|
||||
: ECaveGeneratorType::TunnelNetwork;
|
||||
// Explicitly select the production switch so the same synthetic layout, seed and
|
||||
// mesher invocation can be measured through both density branches.
|
||||
Definition->bUseOperatorStack = Arguments.bUseOperatorStack;
|
||||
if (Arguments.bWormsEnabledOverride)
|
||||
{
|
||||
Definition->bEnableWorms = Arguments.bWormsEnabled;
|
||||
}
|
||||
if (Arguments.bWormStrengthOverride
|
||||
&& (Definition->GeneratorType == ECaveGeneratorType::TunnelNetwork
|
||||
|| Definition->GeneratorType == ECaveGeneratorType::Underwater))
|
||||
{
|
||||
Definition->GenerationParams.WormStrength = Arguments.WormStrength;
|
||||
}
|
||||
}
|
||||
|
||||
const TSoftObjectPtr<UVoxelStrateDefinition> SoftDefinition(Definition);
|
||||
Settings->FixedStrates.Add(Index, SoftDefinition);
|
||||
Settings->StratePool.Add(SoftDefinition);
|
||||
if (!bUseAuthoredStrate)
|
||||
{
|
||||
Settings->StratePool.Add(SoftDefinition);
|
||||
}
|
||||
Definitions.Add(TStrongObjectPtr<UVoxelStrateDefinition>(Definition));
|
||||
}
|
||||
|
||||
auto ApplyTargetOverrides = [&Arguments](UVoxelStrateDefinition* Definition) -> bool
|
||||
{
|
||||
if (Definition == nullptr) { return false; }
|
||||
const ECaveGeneratorType TargetArchetype = Definition->GeneratorType;
|
||||
if (Arguments.bSurfaceRoughnessOverride)
|
||||
{
|
||||
switch (TargetArchetype)
|
||||
{
|
||||
case ECaveGeneratorType::FlatPlain:
|
||||
case ECaveGeneratorType::CrystalChamber:
|
||||
Definition->SlabParams.FloorRoughness = Arguments.SurfaceRoughness;
|
||||
Definition->SlabParams.CeilingRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::Maze:
|
||||
Definition->MazeParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::SurfaceWorld:
|
||||
Definition->SurfaceParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::VerticalShafts:
|
||||
Definition->VerticalShaftParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::FloatingIslands:
|
||||
Definition->FloatingIslandParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::TunnelNetwork:
|
||||
case ECaveGeneratorType::Underwater:
|
||||
default:
|
||||
Definition->GenerationParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Arguments.bRoughnessNoiseTypeOverride
|
||||
|| Arguments.bRoughnessFrequencyOverride)
|
||||
{
|
||||
if (TargetArchetype != ECaveGeneratorType::TunnelNetwork
|
||||
&& TargetArchetype != ECaveGeneratorType::Underwater)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (Arguments.bRoughnessNoiseTypeOverride)
|
||||
{
|
||||
Definition->GenerationParams.RoughnessNoiseType = Arguments.RoughnessNoiseType;
|
||||
}
|
||||
if (Arguments.bRoughnessFrequencyOverride)
|
||||
{
|
||||
Definition->GenerationParams.RoughnessFrequency = Arguments.RoughnessFrequency;
|
||||
}
|
||||
}
|
||||
FStrateGenerationParams& TunnelParams = Definition->GenerationParams;
|
||||
if (Arguments.bTunnelWarpStrengthOverride)
|
||||
{
|
||||
if (TargetArchetype != ECaveGeneratorType::TunnelNetwork
|
||||
&& TargetArchetype != ECaveGeneratorType::Underwater)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TunnelParams.TunnelWarpStrength = Arguments.TunnelWarpStrength;
|
||||
}
|
||||
if (Arguments.bTunnelFloorEnabledOverride)
|
||||
{
|
||||
TunnelParams.bTunnelFloorEnabled = Arguments.TunnelFloorEnabled != 0;
|
||||
}
|
||||
if (Arguments.bTunnelFloorTerracingOverride)
|
||||
{
|
||||
TunnelParams.bTunnelFloorTerracingEnabled = Arguments.TunnelFloorTerracing != 0;
|
||||
}
|
||||
if (Arguments.bTunnelFloorStepOverride)
|
||||
{
|
||||
TunnelParams.TunnelFloorTerraceStepHeight = Arguments.TunnelFloorStep;
|
||||
}
|
||||
if (Arguments.bTunnelFloorMaxLedgeOverride)
|
||||
{
|
||||
TunnelParams.TunnelFloorMaxLedgeHeight = Arguments.TunnelFloorMaxLedge;
|
||||
}
|
||||
if (Arguments.bTunnelFloorGentleOverride)
|
||||
{
|
||||
TunnelParams.TunnelFloorGentleSlopeThreshold = Arguments.TunnelFloorGentle;
|
||||
}
|
||||
if (Arguments.bTunnelFloorLedgesOverride)
|
||||
{
|
||||
TunnelParams.TunnelFloorLedgeCountPreference = Arguments.TunnelFloorLedges;
|
||||
}
|
||||
if (Arguments.bTunnelFloorMaxLedgesOverride)
|
||||
{
|
||||
TunnelParams.TunnelFloorMaxLedges = Arguments.TunnelFloorMaxLedges;
|
||||
}
|
||||
if (Arguments.bRoomMouthRiseOverride)
|
||||
{
|
||||
TunnelParams.RoomMouthRiseStrength = Arguments.RoomMouthRise;
|
||||
}
|
||||
if (Arguments.bRoomMouthRiseBlendOverride)
|
||||
{
|
||||
TunnelParams.RoomMouthRiseBlendVoxels = Arguments.RoomMouthRiseBlend;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!Definitions.IsValidIndex(Arguments.Slot)
|
||||
|| !ApplyTargetOverrides(Definitions[Arguments.Slot].Get()))
|
||||
{
|
||||
OutError = TEXT("The target definition rejected a commandlet override.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Manager = TStrongObjectPtr<UVoxelStrateManager>(
|
||||
NewObject<UVoxelStrateManager>(GetTransientPackage(), NAME_None, RF_Transient));
|
||||
if (!Manager->Initialize(Settings.Get(), Settings->Seed))
|
||||
@@ -1028,104 +1337,40 @@ struct FExploreWorld
|
||||
OutError = TEXT("The requested target slot has no resolved definition.");
|
||||
return false;
|
||||
}
|
||||
if (Arguments.bSurfaceRoughnessOverride)
|
||||
{
|
||||
// This is deliberately applied after layout construction and only to the target slot's
|
||||
// resolved definition. Passage placement and every neighbouring strate retain their
|
||||
// authored/default values; only the sampled target field gets the experiment knob.
|
||||
switch (Arguments.Archetype)
|
||||
{
|
||||
case ECaveGeneratorType::FlatPlain:
|
||||
case ECaveGeneratorType::CrystalChamber:
|
||||
Target.Definition->SlabParams.FloorRoughness = Arguments.SurfaceRoughness;
|
||||
Target.Definition->SlabParams.CeilingRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::Maze:
|
||||
Target.Definition->MazeParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::SurfaceWorld:
|
||||
Target.Definition->SurfaceParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::VerticalShafts:
|
||||
Target.Definition->VerticalShaftParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::FloatingIslands:
|
||||
Target.Definition->FloatingIslandParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
case ECaveGeneratorType::TunnelNetwork:
|
||||
case ECaveGeneratorType::Underwater:
|
||||
default:
|
||||
Target.Definition->GenerationParams.SurfaceRoughness = Arguments.SurfaceRoughness;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Arguments.bRoughnessNoiseTypeOverride
|
||||
|| Arguments.bRoughnessFrequencyOverride)
|
||||
{
|
||||
if (Arguments.Archetype != ECaveGeneratorType::TunnelNetwork
|
||||
&& Arguments.Archetype != ECaveGeneratorType::Underwater)
|
||||
{
|
||||
OutError = TEXT(
|
||||
"roughnesstype/roughnessfrequency are only supported by TunnelNetwork or Underwater.");
|
||||
return false;
|
||||
}
|
||||
if (Arguments.bRoughnessNoiseTypeOverride)
|
||||
{
|
||||
Target.Definition->GenerationParams.RoughnessNoiseType =
|
||||
Arguments.RoughnessNoiseType;
|
||||
}
|
||||
if (Arguments.bRoughnessFrequencyOverride)
|
||||
{
|
||||
Target.Definition->GenerationParams.RoughnessFrequency =
|
||||
Arguments.RoughnessFrequency;
|
||||
}
|
||||
}
|
||||
// Floor controls are applied only to the requested target definition. They are
|
||||
// commandlet overrides for measured archetype comparisons; authored assets still carry
|
||||
// the defaults/rolled values in FStrateGenerationParams.
|
||||
FStrateGenerationParams& TargetTunnelParams = Target.Definition->GenerationParams;
|
||||
if (Arguments.bTunnelFloorEnabledOverride)
|
||||
{
|
||||
TargetTunnelParams.bTunnelFloorEnabled =
|
||||
Arguments.TunnelFloorEnabled != 0;
|
||||
}
|
||||
if (Arguments.bTunnelFloorTerracingOverride)
|
||||
{
|
||||
TargetTunnelParams.bTunnelFloorTerracingEnabled =
|
||||
Arguments.TunnelFloorTerracing != 0;
|
||||
}
|
||||
if (Arguments.bTunnelFloorStepOverride)
|
||||
{
|
||||
TargetTunnelParams.TunnelFloorTerraceStepHeight = Arguments.TunnelFloorStep;
|
||||
}
|
||||
if (Arguments.bTunnelFloorMaxLedgeOverride)
|
||||
{
|
||||
TargetTunnelParams.TunnelFloorMaxLedgeHeight = Arguments.TunnelFloorMaxLedge;
|
||||
}
|
||||
if (Arguments.bTunnelFloorGentleOverride)
|
||||
{
|
||||
TargetTunnelParams.TunnelFloorGentleSlopeThreshold = Arguments.TunnelFloorGentle;
|
||||
}
|
||||
if (Arguments.bTunnelFloorLedgesOverride)
|
||||
{
|
||||
TargetTunnelParams.TunnelFloorLedgeCountPreference = Arguments.TunnelFloorLedges;
|
||||
}
|
||||
if (Arguments.bTunnelFloorMaxLedgesOverride)
|
||||
{
|
||||
TargetTunnelParams.TunnelFloorMaxLedges = Arguments.TunnelFloorMaxLedges;
|
||||
}
|
||||
if (Arguments.bRoomMouthRiseOverride)
|
||||
{
|
||||
TargetTunnelParams.RoomMouthRiseStrength = Arguments.RoomMouthRise;
|
||||
}
|
||||
if (Arguments.bRoomMouthRiseBlendOverride)
|
||||
{
|
||||
TargetTunnelParams.RoomMouthRiseBlendVoxels = Arguments.RoomMouthRiseBlend;
|
||||
}
|
||||
const ECaveGeneratorType TargetArchetype = Target.Definition->GeneratorType;
|
||||
const FStrateGenerationParams& ReportParams = Target.Definition->GenerationParams;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeExplore][OwnerAssets] strate=%s settings=%s slot=%d "
|
||||
"seed=%d height_chunks=%d archetype=%s opstack=%d "
|
||||
"floor_enabled=%d floor_terracing=%d floor_step=%.9g max_ledge=%.9g "
|
||||
"gentle_slope_degrees=%.9g gentle_gradient=%.9g route_cap=%.9g "
|
||||
"wind_wave_cap=%d "
|
||||
"ledge_preference=%d max_ledges=%d tunnel_warp=%.9g terrain_ops=%d terrace_step=%.9g "
|
||||
"layer_spacing=%.9g rib_spacing=%.9g"),
|
||||
bUseAuthoredStrate ? *Arguments.StrateReference : TEXT("synthetic"),
|
||||
bUseAuthoredSettings ? *Arguments.SettingsReference : TEXT("synthetic"),
|
||||
Arguments.Slot, Settings->Seed, Target.Definition->StrateHeightInChunks,
|
||||
ArchetypeName(TargetArchetype), Target.Definition->bUseOperatorStack ? 1 : 0,
|
||||
ReportParams.bTunnelFloorEnabled ? 1 : 0,
|
||||
ReportParams.bTunnelFloorTerracingEnabled ? 1 : 0,
|
||||
ReportParams.TunnelFloorTerraceStepHeight,
|
||||
ReportParams.TunnelFloorMaxLedgeHeight,
|
||||
ReportParams.TunnelFloorGentleSlopeThreshold,
|
||||
VoxelPassageGeometry::TunnelFloorGradientFromDegrees(
|
||||
ReportParams.TunnelFloorGentleSlopeThreshold),
|
||||
VoxelPassageGeometry::TunnelFloorMaximumRouteLengthVoxels,
|
||||
VoxelPassageGeometry::TunnelFloorMaximumWindWaves,
|
||||
ReportParams.TunnelFloorLedgeCountPreference,
|
||||
ReportParams.TunnelFloorMaxLedges,
|
||||
ReportParams.TunnelWarpStrength,
|
||||
Target.Definition->TerrainOperations.Num(),
|
||||
ReportParams.TerraceStepHeight,
|
||||
ReportParams.LayerLineSpacing,
|
||||
ReportParams.RibbingSpacing);
|
||||
TargetBottomWorldZ = Target.BottomChunkZ * CHUNK_SIZE;
|
||||
TargetTopWorldZ = (Target.TopChunkZ + 1) * CHUNK_SIZE;
|
||||
TargetBoundarySealThickness = BoundarySealForArchetype(
|
||||
*Target.Definition, Arguments.Archetype);
|
||||
*Target.Definition, TargetArchetype);
|
||||
if (TargetTopWorldZ <= TargetBottomWorldZ)
|
||||
{
|
||||
OutError = TEXT("The target strate has no positive voxel-space height.");
|
||||
@@ -1315,6 +1560,22 @@ struct FExploreExportOutput
|
||||
bool bTruncated = false;
|
||||
};
|
||||
|
||||
struct FExploreDensitySliceOutput
|
||||
{
|
||||
FString Status;
|
||||
FString RefusalReason;
|
||||
FString FileName;
|
||||
int32 SampleCount = 0;
|
||||
int32 FiniteSampleCount = 0;
|
||||
int32 NonFiniteSampleCount = 0;
|
||||
int32 ZeroCrossingCount = 0;
|
||||
int32 SaturatedSampleCount = 0;
|
||||
float MinDensity = 0.0f;
|
||||
float MaxDensity = 0.0f;
|
||||
float MaxAbsDensity = 0.0f;
|
||||
double Seconds = 0.0;
|
||||
};
|
||||
|
||||
struct FExploreProfilerComparison
|
||||
{
|
||||
bool bAvailable = false;
|
||||
@@ -1335,6 +1596,7 @@ struct FExploreRunOutput
|
||||
FExploreRenderOutput Render;
|
||||
FExploreWalkOutput Walk;
|
||||
FExploreExportOutput Export;
|
||||
FExploreDensitySliceOutput DensitySlice;
|
||||
double BudgetSeconds = 0.0;
|
||||
double ElapsedSeconds = 0.0;
|
||||
double WalkSeconds = 0.0;
|
||||
@@ -2776,7 +3038,12 @@ bool RunRender(
|
||||
OutOutput.bAllCamerasPlayerFit = true;
|
||||
FVector RouteFocus = FVector::ZeroVector;
|
||||
int32 RouteFocusCount = 0;
|
||||
if (CameraSeedWalk.bHasArrival)
|
||||
if (Arguments.bRenderTargetOverride)
|
||||
{
|
||||
RouteFocus = Arguments.RenderTarget;
|
||||
RouteFocusCount = 1;
|
||||
}
|
||||
else if (CameraSeedWalk.bHasArrival)
|
||||
{
|
||||
RouteFocus += CameraSeedWalk.ArrivalVoxels;
|
||||
++RouteFocusCount;
|
||||
@@ -4136,6 +4403,7 @@ void WriteTunnelFloorOverrides(
|
||||
{
|
||||
Writer.WriteValue(TEXT("gentle_slope"),
|
||||
static_cast<double>(Arguments.TunnelFloorGentle));
|
||||
Writer.WriteValue(TEXT("gentle_slope_units"), TEXT("degrees"));
|
||||
}
|
||||
Writer.WriteValue(TEXT("ledge_count_override"), Arguments.bTunnelFloorLedgesOverride);
|
||||
if (Arguments.bTunnelFloorLedgesOverride)
|
||||
@@ -4434,6 +4702,100 @@ bool RunExport(
|
||||
return !OutOutput.bTruncated;
|
||||
}
|
||||
|
||||
bool RunDensitySlice(
|
||||
const FExploreArguments& Arguments,
|
||||
FExploreWorld& World,
|
||||
FExploreDensitySliceOutput& OutOutput,
|
||||
FExploreBudget& Budget)
|
||||
{
|
||||
OutOutput = FExploreDensitySliceOutput();
|
||||
OutOutput.FileName = TEXT("density_slice.csv");
|
||||
OutOutput.SampleCount = Arguments.DensitySliceSamples;
|
||||
const double StartSeconds = FPlatformTime::Seconds();
|
||||
FString Csv;
|
||||
Csv.Reserve(Arguments.DensitySliceSamples * 96);
|
||||
Csv += TEXT("index,x_voxels,y_voxels,z_voxels,density\r\n");
|
||||
|
||||
float PreviousDensity = 0.0f;
|
||||
bool bHavePreviousDensity = false;
|
||||
float MinDensity = TNumericLimits<float>::Max();
|
||||
float MaxDensity = -TNumericLimits<float>::Max();
|
||||
float MaxAbsDensity = 0.0f;
|
||||
for (int32 Index = 0; Index < Arguments.DensitySliceSamples; ++Index)
|
||||
{
|
||||
if ((Index & 127) == 0 && Budget.ShouldStop(TEXT("density-slice")))
|
||||
{
|
||||
OutOutput.Status = TEXT("truncated");
|
||||
OutOutput.RefusalReason = TEXT("The wall-clock budget stopped the density slice.");
|
||||
OutOutput.Seconds = FPlatformTime::Seconds() - StartSeconds;
|
||||
return false;
|
||||
}
|
||||
|
||||
const FVector Position = Arguments.DensitySliceOrigin
|
||||
+ Arguments.DensitySliceDirection
|
||||
* (Arguments.DensitySliceStep * static_cast<float>(Index));
|
||||
const float Density = World.Generator->GetDensityAt(
|
||||
Position.X, Position.Y, Position.Z);
|
||||
Csv += FString::Printf(
|
||||
TEXT("%d,%.9g,%.9g,%.9g,%.9g\r\n"),
|
||||
Index, Position.X, Position.Y, Position.Z, Density);
|
||||
|
||||
if (!FMath::IsFinite(Density))
|
||||
{
|
||||
++OutOutput.NonFiniteSampleCount;
|
||||
bHavePreviousDensity = false;
|
||||
continue;
|
||||
}
|
||||
++OutOutput.FiniteSampleCount;
|
||||
MinDensity = FMath::Min(MinDensity, Density);
|
||||
MaxDensity = FMath::Max(MaxDensity, Density);
|
||||
MaxAbsDensity = FMath::Max(MaxAbsDensity, FMath::Abs(Density));
|
||||
if (FMath::Abs(Density) >= 0.999f)
|
||||
{
|
||||
++OutOutput.SaturatedSampleCount;
|
||||
}
|
||||
if (bHavePreviousDensity
|
||||
&& ((PreviousDensity < 0.0f && Density > 0.0f)
|
||||
|| (PreviousDensity > 0.0f && Density < 0.0f)))
|
||||
{
|
||||
++OutOutput.ZeroCrossingCount;
|
||||
}
|
||||
PreviousDensity = Density;
|
||||
bHavePreviousDensity = true;
|
||||
}
|
||||
|
||||
if (OutOutput.FiniteSampleCount > 0)
|
||||
{
|
||||
OutOutput.MinDensity = MinDensity;
|
||||
OutOutput.MaxDensity = MaxDensity;
|
||||
OutOutput.MaxAbsDensity = MaxAbsDensity;
|
||||
}
|
||||
const FString Path = FPaths::Combine(Arguments.OutDirectory, OutOutput.FileName);
|
||||
if (!FFileHelper::SaveStringToFile(
|
||||
Csv, *Path, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))
|
||||
{
|
||||
OutOutput.Status = TEXT("error");
|
||||
OutOutput.RefusalReason = TEXT("Could not write density_slice.csv.");
|
||||
OutOutput.Seconds = FPlatformTime::Seconds() - StartSeconds;
|
||||
return false;
|
||||
}
|
||||
OutOutput.Status = TEXT("ok");
|
||||
OutOutput.Seconds = FPlatformTime::Seconds() - StartSeconds;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeDensitySlice] file=%s samples=%d finite=%d non_finite=%d "
|
||||
"zero_crossings=%d saturated=%d min=%.9g max=%.9g max_abs=%.9g "
|
||||
"origin=(%.3f,%.3f,%.3f) direction=(%.6f,%.6f,%.6f) step=%.6g"),
|
||||
*Path, OutOutput.SampleCount, OutOutput.FiniteSampleCount,
|
||||
OutOutput.NonFiniteSampleCount, OutOutput.ZeroCrossingCount,
|
||||
OutOutput.SaturatedSampleCount, OutOutput.MinDensity,
|
||||
OutOutput.MaxDensity, OutOutput.MaxAbsDensity,
|
||||
Arguments.DensitySliceOrigin.X, Arguments.DensitySliceOrigin.Y,
|
||||
Arguments.DensitySliceOrigin.Z, Arguments.DensitySliceDirection.X,
|
||||
Arguments.DensitySliceDirection.Y, Arguments.DensitySliceDirection.Z,
|
||||
Arguments.DensitySliceStep);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WritePlayerDimensions(FExploreJsonWriter& Writer, const TCHAR* Key)
|
||||
{
|
||||
Writer.WriteObjectStart(Key);
|
||||
@@ -4459,7 +4821,7 @@ FString BuildExploreJson(
|
||||
TSharedRef<FExploreJsonWriter> Writer =
|
||||
TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&Json);
|
||||
Writer->WriteObjectStart();
|
||||
Writer->WriteValue(TEXT("schema_version"), 6);
|
||||
Writer->WriteValue(TEXT("schema_version"), 7);
|
||||
Writer->WriteValue(TEXT("tool"), TEXT("VoxelForgeExplore"));
|
||||
Writer->WriteValue(TEXT("read_only_generation"), true);
|
||||
|
||||
@@ -4534,6 +4896,12 @@ FString BuildExploreJson(
|
||||
Writer->WriteValue(TEXT("sample_stacks_interval_us"),
|
||||
static_cast<double>(Arguments.SampleStacksIntervalUs));
|
||||
Writer->WriteValue(TEXT("profile_lod"), Arguments.bProfileLod);
|
||||
Writer->WriteValue(TEXT("density_slice_requested"), Arguments.bDensitySlice);
|
||||
Writer->WriteValue(TEXT("render_target_override"), Arguments.bRenderTargetOverride);
|
||||
if (Arguments.bRenderTargetOverride)
|
||||
{
|
||||
WriteJsonVector(*Writer, TEXT("render_target_voxels"), Arguments.RenderTarget, 1.0f);
|
||||
}
|
||||
Writer->WriteValue(TEXT("op_bounds"), Arguments.bOpBounds);
|
||||
Writer->WriteValue(TEXT("budget_minutes"), static_cast<double>(Arguments.BudgetMinutes));
|
||||
Writer->WriteValue(TEXT("out_directory"), Arguments.OutDirectory);
|
||||
@@ -4549,6 +4917,27 @@ FString BuildExploreJson(
|
||||
Arguments.bBlockEarlyOut ? 1 : 0));
|
||||
Writer->WriteObjectEnd();
|
||||
|
||||
Writer->WriteObjectStart(TEXT("density_slice"));
|
||||
Writer->WriteValue(TEXT("status"), Output.DensitySlice.Status);
|
||||
Writer->WriteValue(TEXT("file"), Output.DensitySlice.FileName);
|
||||
Writer->WriteValue(TEXT("refusal_or_error"), Output.DensitySlice.RefusalReason);
|
||||
Writer->WriteValue(TEXT("sample_count"), Output.DensitySlice.SampleCount);
|
||||
Writer->WriteValue(TEXT("finite_sample_count"), Output.DensitySlice.FiniteSampleCount);
|
||||
Writer->WriteValue(TEXT("non_finite_sample_count"), Output.DensitySlice.NonFiniteSampleCount);
|
||||
Writer->WriteValue(TEXT("zero_crossing_count"), Output.DensitySlice.ZeroCrossingCount);
|
||||
Writer->WriteValue(TEXT("saturated_sample_count"), Output.DensitySlice.SaturatedSampleCount);
|
||||
Writer->WriteValue(TEXT("min_density"), static_cast<double>(Output.DensitySlice.MinDensity));
|
||||
Writer->WriteValue(TEXT("max_density"), static_cast<double>(Output.DensitySlice.MaxDensity));
|
||||
Writer->WriteValue(TEXT("max_abs_density"), static_cast<double>(Output.DensitySlice.MaxAbsDensity));
|
||||
Writer->WriteValue(TEXT("seconds"), Output.DensitySlice.Seconds);
|
||||
if (Arguments.bDensitySlice)
|
||||
{
|
||||
WriteJsonVector(*Writer, TEXT("origin_voxels"), Arguments.DensitySliceOrigin, 1.0f);
|
||||
WriteJsonVector(*Writer, TEXT("direction"), Arguments.DensitySliceDirection, 1.0f);
|
||||
Writer->WriteValue(TEXT("step_voxels"), static_cast<double>(Arguments.DensitySliceStep));
|
||||
}
|
||||
Writer->WriteObjectEnd();
|
||||
|
||||
Writer->WriteObjectStart(TEXT("summary"));
|
||||
const double SummaryMeshSeconds = Arguments.bExport
|
||||
? Output.Export.MeshSeconds
|
||||
@@ -4615,6 +5004,13 @@ FString BuildExploreJson(
|
||||
Writer->WriteObjectStart(TEXT("tile_ledger"));
|
||||
Writer->WriteValue(TEXT("order"), TEXT("z_then_y_then_x"));
|
||||
Writer->WriteValue(TEXT("tile_cells"), CHUNK_SIZE);
|
||||
int32 TileLod = 0;
|
||||
for (int32 SampleStep = Arguments.ExportStep; SampleStep > 1; SampleStep >>= 1)
|
||||
{
|
||||
++TileLod;
|
||||
}
|
||||
Writer->WriteValue(TEXT("sample_step"), Arguments.ExportStep);
|
||||
Writer->WriteValue(TEXT("lod"), TileLod);
|
||||
Writer->WriteValue(TEXT("tile_count"), World.ExploreMeshTileCount);
|
||||
Writer->WriteValue(TEXT("completed_tiles"), World.ExploreMeshTilesCompleted);
|
||||
Writer->WriteArrayStart(TEXT("tiles"));
|
||||
@@ -4629,6 +5025,8 @@ FString BuildExploreJson(
|
||||
Writer->WriteValue(TEXT("x"), TileX);
|
||||
Writer->WriteValue(TEXT("y"), TileY);
|
||||
Writer->WriteValue(TEXT("z"), TileZ);
|
||||
Writer->WriteValue(TEXT("sample_step"), Arguments.ExportStep);
|
||||
Writer->WriteValue(TEXT("lod"), TileLod);
|
||||
Writer->WriteValue(TEXT("triangle_count"), World.ExploreTileTriangleCounts[TileIndex]);
|
||||
Writer->WriteValue(TEXT("geometry_hash"), World.ExploreTileGeometryHashes[TileIndex]);
|
||||
Writer->WriteObjectEnd();
|
||||
@@ -5750,7 +6148,26 @@ int32 RunExploreCase(const FString& Params, FString* OutJson)
|
||||
return 1;
|
||||
}
|
||||
|
||||
FExploreRunOutput Output;
|
||||
bool bRequestedModeFailed = false;
|
||||
if (Arguments.bDensitySlice)
|
||||
{
|
||||
const double Start = FPlatformTime::Seconds();
|
||||
const bool bDensitySliceOk = !Budget.bTruncated
|
||||
&& RunDensitySlice(Arguments, World, Output.DensitySlice, Budget);
|
||||
if (!bDensitySliceOk && !Budget.bTruncated)
|
||||
{
|
||||
bRequestedModeFailed = true;
|
||||
UE_LOG(LogTemp, Error,
|
||||
TEXT("[VoxelForgeExplore] density slice failed: %s"),
|
||||
*Output.DensitySlice.RefusalReason);
|
||||
}
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeExplore] density slice %.3fs (%s)"),
|
||||
FPlatformTime::Seconds() - Start,
|
||||
bDensitySliceOk ? TEXT("ok")
|
||||
: (Budget.bTruncated ? TEXT("truncated") : TEXT("error")));
|
||||
}
|
||||
if (Arguments.bOpBounds)
|
||||
{
|
||||
const double Start = FPlatformTime::Seconds();
|
||||
@@ -5837,7 +6254,6 @@ int32 RunExploreCase(const FString& Params, FString* OutJson)
|
||||
}
|
||||
}
|
||||
|
||||
FExploreRunOutput Output;
|
||||
if (Budget.ShouldStop(TEXT("setup")))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning,
|
||||
|
||||
Reference in new issue
Block a user