research(step2f): coherent caves pass every gate, worst 572 ms idle -- look still to be judged in game
Editor-only, default off (voxel.ResearchParameterizedCave 1; the live skip proofs now decline automatically). Jittered elliptical chambers of varied size and height with rock between them, narrow tunnels, capped shafts, occasional pillars/ledges/stalactites/stalagmites/hanging masses; detail bands at 48/24/12 samples (shortest 3 m), bound 3.822. Gates, step 2e -> step 2f R29 (worst of 8 profiles), each rejecting 2e: crumbs < 1 m^3 2,290 -> 0; dihedral >45 deg 40.7% -> 2.2%; lattice detail energy 0.83 -> 0.007 (RMS floor kept: 0.484 >= 0.40); LOD1 distance p95 0.709 -> 0.377 samples; grid autocorrelation 0.667 -> 0.348; solid share 2.7% -> 79-82%; roof/floor openings 26,657 -> 0. Speed, 259^3 ring, six workers, idle machine (20 GB free): 527-572 ms, worst TerracedBasin 572.3 ms (R27 591 ms, R28 603 ms: run-to-run spread is ~5%). Proof audit 0 violations, undersized control caught; seams pass; 1 vs 6 worker hash 876069a813c83cee; TraversalSeeds pass. Captures are UNLIT (lit runs showed only sky; three D3D12 device-removed crashes): they show the material's tiling, not the cave forms, so the look is not yet judged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
d6555df5fa
commit
ca78352e43
16 files changed
+8070
-80
No files matched your search
@@ -2551,10 +2551,20 @@ struct FStep2dBrickProofAudit
|
||||
float FirstViolationDensity = 0.0f;
|
||||
float FirstViolationCenterMacro = 0.0f;
|
||||
float FirstViolationSampleMacro = 0.0f;
|
||||
float FirstViolationCenterRoomMacro = 0.0f;
|
||||
float FirstViolationSampleRoomMacro = 0.0f;
|
||||
float FirstViolationSampleDetail = 0.0f;
|
||||
float FirstViolationMacroDeltaPerVoxel = 0.0f;
|
||||
float FirstViolationPathMaxGradient = 0.0f;
|
||||
float FirstViolationTinyStepMaxGradient = 0.0f;
|
||||
FVector FirstViolationPathMaxFrom = FVector::ZeroVector;
|
||||
FVector FirstViolationPathMaxTo = FVector::ZeroVector;
|
||||
float FirstViolationPathMaxFromMacro = 0.0f;
|
||||
float FirstViolationPathMaxToMacro = 0.0f;
|
||||
float FirstViolationPathMaxFromRoomMacro = 0.0f;
|
||||
float FirstViolationPathMaxToRoomMacro = 0.0f;
|
||||
FString FirstViolationPathMaxFromRoomDiagnostic;
|
||||
FString FirstViolationPathMaxToRoomDiagnostic;
|
||||
bool bFirstViolationWasInsideBrick = false;
|
||||
bool bFirstViolationClaimedAir = false;
|
||||
uint64 DeliberatelyUndersizedBoundClaimsChecked = 0;
|
||||
@@ -2653,6 +2663,15 @@ FStep2dBrickProofAudit AuditStep2dBrickProofs(
|
||||
: VoxelForgeRedefinedDensityField::Evaluate(
|
||||
Generator, SamplePoint.X, SamplePoint.Y, SamplePoint.Z,
|
||||
nullptr, 0.0f);
|
||||
if (bParameterized)
|
||||
{
|
||||
Result.FirstViolationCenterRoomMacro =
|
||||
VoxelForgeRedefinedDensityField::EvaluateParameterizedMacroOnly(
|
||||
Generator, ProofCentre.X, ProofCentre.Y, ProofCentre.Z);
|
||||
Result.FirstViolationSampleRoomMacro =
|
||||
VoxelForgeRedefinedDensityField::EvaluateParameterizedMacroOnly(
|
||||
Generator, SamplePoint.X, SamplePoint.Y, SamplePoint.Z);
|
||||
}
|
||||
Result.FirstViolationSampleDetail = Density
|
||||
- Result.FirstViolationSampleMacro;
|
||||
const float CentreDistance = (SamplePoint - ProofCentre).Size();
|
||||
@@ -2664,6 +2683,7 @@ FStep2dBrickProofAudit AuditStep2dBrickProofs(
|
||||
{
|
||||
constexpr int32 PathSteps = 32;
|
||||
float PreviousMacro = Result.FirstViolationSampleMacro;
|
||||
FVector PreviousPoint = SamplePoint;
|
||||
for (int32 PathStep = 1; PathStep <= PathSteps; ++PathStep)
|
||||
{
|
||||
const float Alpha = static_cast<float>(PathStep) / PathSteps;
|
||||
@@ -2674,11 +2694,35 @@ FStep2dBrickProofAudit AuditStep2dBrickProofs(
|
||||
Generator, Point.X, Point.Y, Point.Z, nullptr, 0.0f)
|
||||
: VoxelForgeRedefinedDensityField::Evaluate(
|
||||
Generator, Point.X, Point.Y, Point.Z, nullptr, 0.0f);
|
||||
Result.FirstViolationPathMaxGradient = FMath::Max(
|
||||
Result.FirstViolationPathMaxGradient,
|
||||
FMath::Abs(PathMacro - PreviousMacro)
|
||||
* PathSteps / CentreDistance);
|
||||
const float PathGradient = FMath::Abs(
|
||||
PathMacro - PreviousMacro) * PathSteps / CentreDistance;
|
||||
if (PathGradient > Result.FirstViolationPathMaxGradient)
|
||||
{
|
||||
Result.FirstViolationPathMaxGradient = PathGradient;
|
||||
Result.FirstViolationPathMaxFrom = PreviousPoint;
|
||||
Result.FirstViolationPathMaxTo = Point;
|
||||
Result.FirstViolationPathMaxFromMacro = PreviousMacro;
|
||||
Result.FirstViolationPathMaxToMacro = PathMacro;
|
||||
if (bParameterized)
|
||||
{
|
||||
Result.FirstViolationPathMaxFromRoomMacro =
|
||||
VoxelForgeRedefinedDensityField::EvaluateParameterizedMacroOnly(
|
||||
Generator, PreviousPoint.X, PreviousPoint.Y,
|
||||
PreviousPoint.Z);
|
||||
Result.FirstViolationPathMaxToRoomMacro =
|
||||
VoxelForgeRedefinedDensityField::EvaluateParameterizedMacroOnly(
|
||||
Generator, Point.X, Point.Y, Point.Z);
|
||||
Result.FirstViolationPathMaxFromRoomDiagnostic =
|
||||
VoxelForgeRedefinedDensityField::DescribeParameterizedMacroAt(
|
||||
Generator, PreviousPoint.X, PreviousPoint.Y,
|
||||
PreviousPoint.Z);
|
||||
Result.FirstViolationPathMaxToRoomDiagnostic =
|
||||
VoxelForgeRedefinedDensityField::DescribeParameterizedMacroAt(
|
||||
Generator, Point.X, Point.Y, Point.Z);
|
||||
}
|
||||
}
|
||||
PreviousMacro = PathMacro;
|
||||
PreviousPoint = Point;
|
||||
}
|
||||
}
|
||||
constexpr float TinyStep = 0.01f;
|
||||
@@ -3173,12 +3217,27 @@ struct FStep2fFragmentMetrics
|
||||
int64 SolidCrumbsBelowOneCubicMeter = 0;
|
||||
int64 AirCrumbVoxelSamples = 0;
|
||||
int64 SolidCrumbVoxelSamples = 0;
|
||||
int64 AirCrumbsAfterBoundaryPadding = -1;
|
||||
int64 SolidCrumbsAfterBoundaryPadding = -1;
|
||||
int32 LargestAirComponentVoxels = 0;
|
||||
int32 LargestSolidComponentVoxels = 0;
|
||||
struct FCrumbComponent
|
||||
{
|
||||
bool bAir = false;
|
||||
int32 Samples = 0;
|
||||
FIntVector Min = FIntVector::ZeroValue;
|
||||
FIntVector Max = FIntVector::ZeroValue;
|
||||
FIntVector Root = FIntVector::ZeroValue;
|
||||
int32 SignMismatchSamples = 0;
|
||||
float DensityAtRoot = 0.0f;
|
||||
float DensityAtMin = 0.0f;
|
||||
};
|
||||
TArray<FCrumbComponent> CrumbComponents;
|
||||
};
|
||||
|
||||
FStep2fFragmentMetrics ComputeStep2fFragmentMetrics(
|
||||
const TArray<float>& Density, int32 Dimension)
|
||||
const TArray<float>& Density, int32 Dimension,
|
||||
int32 CoreInset = 0, int32 CoreDimension = 0)
|
||||
{
|
||||
FStep2fFragmentMetrics Result;
|
||||
const int32 Count = Density.Num();
|
||||
@@ -3252,7 +3311,20 @@ FStep2fFragmentMetrics ComputeStep2fFragmentMetrics(
|
||||
}
|
||||
}
|
||||
}
|
||||
TSet<int32> ComponentsIntersectingCore;
|
||||
if (CoreDimension > 0 && CoreInset >= 0
|
||||
&& CoreInset + CoreDimension <= Dimension)
|
||||
{
|
||||
for (int32 Z = CoreInset; Z < CoreInset + CoreDimension; ++Z)
|
||||
for (int32 Y = CoreInset; Y < CoreInset + CoreDimension; ++Y)
|
||||
for (int32 X = CoreInset; X < CoreInset + CoreDimension; ++X)
|
||||
{
|
||||
const int32 Index = (Z * Dimension + Y) * Dimension + X;
|
||||
ComponentsIntersectingCore.Add(FindRoot(Index));
|
||||
}
|
||||
}
|
||||
constexpr int32 VoxelsPerCubicMeter = 4 * 4 * 4;
|
||||
TMap<int32, FStep2fFragmentMetrics::FCrumbComponent> CrumbByRoot;
|
||||
for (int32 Index = 0; Index < Count; ++Index)
|
||||
{
|
||||
if (Parent[Index] >= 0)
|
||||
@@ -3261,15 +3333,29 @@ FStep2fFragmentMetrics ComputeStep2fFragmentMetrics(
|
||||
}
|
||||
const int32 ComponentVoxels = -Parent[Index];
|
||||
const bool bAir = Density[Index] > 0.0f;
|
||||
const bool bIntersectsCore = ComponentsIntersectingCore.IsEmpty()
|
||||
|| ComponentsIntersectingCore.Contains(Index);
|
||||
if (bAir)
|
||||
{
|
||||
++Result.AirComponents;
|
||||
Result.LargestAirComponentVoxels = FMath::Max(
|
||||
Result.LargestAirComponentVoxels, ComponentVoxels);
|
||||
if (ComponentVoxels < VoxelsPerCubicMeter)
|
||||
if (ComponentVoxels < VoxelsPerCubicMeter && bIntersectsCore)
|
||||
{
|
||||
++Result.AirCrumbsBelowOneCubicMeter;
|
||||
Result.AirCrumbVoxelSamples += ComponentVoxels;
|
||||
if (Result.CrumbComponents.Num() + CrumbByRoot.Num() < 32)
|
||||
{
|
||||
FStep2fFragmentMetrics::FCrumbComponent& Crumb =
|
||||
CrumbByRoot.Add(Index);
|
||||
Crumb.bAir = true;
|
||||
Crumb.Samples = ComponentVoxels;
|
||||
Crumb.Min = FIntVector(Dimension, Dimension, Dimension);
|
||||
Crumb.Max = FIntVector::ZeroValue;
|
||||
Crumb.Root = FIntVector(Index % Dimension,
|
||||
(Index / Dimension) % Dimension, Index / (Dimension * Dimension));
|
||||
Crumb.DensityAtRoot = Density[Index];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3277,13 +3363,59 @@ FStep2fFragmentMetrics ComputeStep2fFragmentMetrics(
|
||||
++Result.SolidComponents;
|
||||
Result.LargestSolidComponentVoxels = FMath::Max(
|
||||
Result.LargestSolidComponentVoxels, ComponentVoxels);
|
||||
if (ComponentVoxels < VoxelsPerCubicMeter)
|
||||
if (ComponentVoxels < VoxelsPerCubicMeter && bIntersectsCore)
|
||||
{
|
||||
++Result.SolidCrumbsBelowOneCubicMeter;
|
||||
Result.SolidCrumbVoxelSamples += ComponentVoxels;
|
||||
if (Result.CrumbComponents.Num() + CrumbByRoot.Num() < 32)
|
||||
{
|
||||
FStep2fFragmentMetrics::FCrumbComponent& Crumb =
|
||||
CrumbByRoot.Add(Index);
|
||||
Crumb.bAir = false;
|
||||
Crumb.Samples = ComponentVoxels;
|
||||
Crumb.Min = FIntVector(Dimension, Dimension, Dimension);
|
||||
Crumb.Max = FIntVector::ZeroValue;
|
||||
Crumb.Root = FIntVector(Index % Dimension,
|
||||
(Index / Dimension) % Dimension, Index / (Dimension * Dimension));
|
||||
Crumb.DensityAtRoot = Density[Index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!CrumbByRoot.IsEmpty())
|
||||
{
|
||||
for (int32 Index = 0; Index < Count; ++Index)
|
||||
{
|
||||
const int32 Root = FindRoot(Index);
|
||||
FStep2fFragmentMetrics::FCrumbComponent* Crumb = CrumbByRoot.Find(Root);
|
||||
if (Crumb == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int32 X = Index % Dimension;
|
||||
const int32 Y = (Index / Dimension) % Dimension;
|
||||
const int32 Z = Index / (Dimension * Dimension);
|
||||
Crumb->SignMismatchSamples +=
|
||||
(Density[Index] > 0.0f) != Crumb->bAir ? 1 : 0;
|
||||
Crumb->Min.X = FMath::Min(Crumb->Min.X, X);
|
||||
Crumb->Min.Y = FMath::Min(Crumb->Min.Y, Y);
|
||||
Crumb->Min.Z = FMath::Min(Crumb->Min.Z, Z);
|
||||
Crumb->Max.X = FMath::Max(Crumb->Max.X, X);
|
||||
Crumb->Max.Y = FMath::Max(Crumb->Max.Y, Y);
|
||||
Crumb->Max.Z = FMath::Max(Crumb->Max.Z, Z);
|
||||
}
|
||||
TArray<int32> Roots;
|
||||
CrumbByRoot.GetKeys(Roots);
|
||||
Roots.Sort();
|
||||
for (const int32 Root : Roots)
|
||||
{
|
||||
FStep2fFragmentMetrics::FCrumbComponent& Crumb = CrumbByRoot[Root];
|
||||
const int32 MinIndex = (Crumb.Min.Z * Dimension + Crumb.Min.Y)
|
||||
* Dimension + Crumb.Min.X;
|
||||
Crumb.DensityAtMin = Density[MinIndex];
|
||||
Result.CrumbComponents.Add(Crumb);
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -5006,10 +5138,44 @@ int32 RunStep2f(const FString& Params)
|
||||
static_cast<double>(Shape.Fragments.AirCrumbsBelowOneCubicMeter));
|
||||
Json->SetNumberField(TEXT("solid_crumbs_below_1m3"),
|
||||
static_cast<double>(Shape.Fragments.SolidCrumbsBelowOneCubicMeter));
|
||||
Json->SetNumberField(TEXT("air_crumbs_below_1m3_after_boundary_padding"),
|
||||
static_cast<double>(Shape.Fragments.AirCrumbsAfterBoundaryPadding >= 0
|
||||
? Shape.Fragments.AirCrumbsAfterBoundaryPadding
|
||||
: Shape.Fragments.AirCrumbsBelowOneCubicMeter));
|
||||
Json->SetNumberField(TEXT("solid_crumbs_below_1m3_after_boundary_padding"),
|
||||
static_cast<double>(Shape.Fragments.SolidCrumbsAfterBoundaryPadding >= 0
|
||||
? Shape.Fragments.SolidCrumbsAfterBoundaryPadding
|
||||
: Shape.Fragments.SolidCrumbsBelowOneCubicMeter));
|
||||
Json->SetNumberField(TEXT("air_crumb_voxel_samples"),
|
||||
static_cast<double>(Shape.Fragments.AirCrumbVoxelSamples));
|
||||
Json->SetNumberField(TEXT("solid_crumb_voxel_samples"),
|
||||
static_cast<double>(Shape.Fragments.SolidCrumbVoxelSamples));
|
||||
TArray<TSharedPtr<FJsonValue>> CrumbComponents;
|
||||
for (const FStep2fFragmentMetrics::FCrumbComponent& Crumb
|
||||
: Shape.Fragments.CrumbComponents)
|
||||
{
|
||||
TSharedRef<FJsonObject> CrumbJson = MakeShared<FJsonObject>();
|
||||
CrumbJson->SetStringField(TEXT("sign"), Crumb.bAir ? TEXT("air") : TEXT("solid"));
|
||||
CrumbJson->SetNumberField(TEXT("samples"), Crumb.Samples);
|
||||
CrumbJson->SetNumberField(TEXT("density_at_min"), Crumb.DensityAtMin);
|
||||
CrumbJson->SetArrayField(TEXT("root_ring_voxel"), {
|
||||
MakeShared<FJsonValueNumber>(Crumb.Root.X),
|
||||
MakeShared<FJsonValueNumber>(Crumb.Root.Y),
|
||||
MakeShared<FJsonValueNumber>(Crumb.Root.Z)});
|
||||
CrumbJson->SetNumberField(TEXT("density_at_root"), Crumb.DensityAtRoot);
|
||||
CrumbJson->SetNumberField(TEXT("sign_mismatch_samples"),
|
||||
Crumb.SignMismatchSamples);
|
||||
CrumbJson->SetArrayField(TEXT("min_ring_voxel"), {
|
||||
MakeShared<FJsonValueNumber>(Crumb.Min.X),
|
||||
MakeShared<FJsonValueNumber>(Crumb.Min.Y),
|
||||
MakeShared<FJsonValueNumber>(Crumb.Min.Z)});
|
||||
CrumbJson->SetArrayField(TEXT("max_ring_voxel"), {
|
||||
MakeShared<FJsonValueNumber>(Crumb.Max.X),
|
||||
MakeShared<FJsonValueNumber>(Crumb.Max.Y),
|
||||
MakeShared<FJsonValueNumber>(Crumb.Max.Z)});
|
||||
CrumbComponents.Add(MakeShared<FJsonValueObject>(CrumbJson));
|
||||
}
|
||||
Json->SetArrayField(TEXT("crumb_components_first_32"), CrumbComponents);
|
||||
Json->SetNumberField(TEXT("largest_air_component_voxels"),
|
||||
Shape.Fragments.LargestAirComponentVoxels);
|
||||
Json->SetNumberField(TEXT("largest_solid_component_voxels"),
|
||||
@@ -5041,6 +5207,35 @@ int32 RunStep2f(const FString& Params)
|
||||
return Json;
|
||||
};
|
||||
|
||||
constexpr int32 CrumbBoundaryPadding = 64;
|
||||
const int32 PaddedCrumbDimension = Dimension + 2 * CrumbBoundaryPadding;
|
||||
const FIntVector PaddedCrumbOrigin = Origin
|
||||
- FIntVector(CrumbBoundaryPadding, CrumbBoundaryPadding, CrumbBoundaryPadding);
|
||||
const auto CountBoundaryPaddedCrumbs = [&](bool bLegacyField,
|
||||
int64& OutAirCrumbs, int64& OutSolidCrumbs,
|
||||
TArray<FStep2fFragmentMetrics::FCrumbComponent>* OutCrumbComponents = nullptr)
|
||||
{
|
||||
VoxelForgeRedefinedDensityField::SetLegacyStep2eAuditMode(bLegacyField);
|
||||
TArray<float> PaddedDensity;
|
||||
VoxelForgeRegionEval::FTiming PaddedTiming;
|
||||
if (!VoxelForgeRegionEval::EvaluateRedefined(
|
||||
*World.Generator, PaddedCrumbOrigin, PaddedCrumbDimension,
|
||||
Workers, 1.0f, PaddedDensity, PaddedTiming, false, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const FStep2fFragmentMetrics PaddedFragments = ComputeStep2fFragmentMetrics(
|
||||
PaddedDensity, PaddedCrumbDimension,
|
||||
CrumbBoundaryPadding, Dimension);
|
||||
OutAirCrumbs = PaddedFragments.AirCrumbsBelowOneCubicMeter;
|
||||
OutSolidCrumbs = PaddedFragments.SolidCrumbsBelowOneCubicMeter;
|
||||
if (OutCrumbComponents != nullptr)
|
||||
{
|
||||
*OutCrumbComponents = PaddedFragments.CrumbComponents;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
for (int32 Profile = 0;
|
||||
Profile < VoxelForgeRedefinedDensityField::GetParameterSetCount(); ++Profile)
|
||||
{
|
||||
@@ -5072,12 +5267,20 @@ int32 RunStep2f(const FString& Params)
|
||||
}
|
||||
LegacyTimes.Sort();
|
||||
const double LegacyMedianMs = LegacyTimes[LegacyTimes.Num() / 2];
|
||||
const FStep2fShapeSnapshot LegacyShape = MeasureShape(
|
||||
FStep2fShapeSnapshot LegacyShape = MeasureShape(
|
||||
LegacyDensity, LegacyParameters);
|
||||
const FStep2eVarietyMetrics LegacyVariety = LegacyShape.Variety;
|
||||
if (!CountBoundaryPaddedCrumbs(true,
|
||||
LegacyShape.Fragments.AirCrumbsAfterBoundaryPadding,
|
||||
LegacyShape.Fragments.SolidCrumbsAfterBoundaryPadding))
|
||||
{
|
||||
UE_LOG(LogTemp, Error,
|
||||
TEXT("[VoxelForgeStep2f] profile %d padded step 2e crumb pass failed."), Profile);
|
||||
return 2;
|
||||
}
|
||||
WorstStep2eMs = FMath::Max(WorstStep2eMs, LegacyMedianMs);
|
||||
const int64 LegacyCrumbs = LegacyShape.Fragments.AirCrumbsBelowOneCubicMeter
|
||||
+ LegacyShape.Fragments.SolidCrumbsBelowOneCubicMeter;
|
||||
const int64 LegacyCrumbs = LegacyShape.Fragments.AirCrumbsAfterBoundaryPadding
|
||||
+ LegacyShape.Fragments.SolidCrumbsAfterBoundaryPadding;
|
||||
WorstStep2eCrumbs = FMath::Max(WorstStep2eCrumbs, LegacyCrumbs);
|
||||
bStep2eCrumbsRejected &= LegacyCrumbs > 0;
|
||||
WorstStep2eDihedralShare = FMath::Max(
|
||||
@@ -5092,7 +5295,7 @@ int32 RunStep2f(const FString& Params)
|
||||
bStep2eBandLimitRejected &= LegacyShape.Detail.LatticeScaleEnergyRatio > 0.20;
|
||||
WorstStep2eLodDistance = FMath::Max(
|
||||
WorstStep2eLodDistance, LegacyShape.Lod1P95SurfaceErrorVoxels);
|
||||
bStep2eLodRejected |= LegacyShape.Lod1P95SurfaceErrorVoxels > 3.5;
|
||||
bStep2eLodRejected |= LegacyShape.Lod1P95SurfaceErrorVoxels > 0.5;
|
||||
WorstStep2ePitchCorrelation = FMath::Max(
|
||||
WorstStep2ePitchCorrelation, LegacyShape.Grid.PitchCorrelationPeak);
|
||||
WorstStep2ePitchExcess = FMath::Max(
|
||||
@@ -5136,12 +5339,22 @@ int32 RunStep2f(const FString& Params)
|
||||
}
|
||||
NewTimes.Sort();
|
||||
const double NewMedianMs = NewTimes[NewTimes.Num() / 2];
|
||||
const FStep2fShapeSnapshot NewShape = MeasureShape(NewDensity, NewParameters);
|
||||
FStep2fShapeSnapshot NewShape = MeasureShape(NewDensity, NewParameters);
|
||||
const FStep2eVarietyMetrics NewVariety = NewShape.Variety;
|
||||
TArray<FStep2fFragmentMetrics::FCrumbComponent> NewPaddedCrumbComponents;
|
||||
if (!CountBoundaryPaddedCrumbs(false,
|
||||
NewShape.Fragments.AirCrumbsAfterBoundaryPadding,
|
||||
NewShape.Fragments.SolidCrumbsAfterBoundaryPadding,
|
||||
&NewPaddedCrumbComponents))
|
||||
{
|
||||
UE_LOG(LogTemp, Error,
|
||||
TEXT("[VoxelForgeStep2f] profile %d padded step 2f crumb pass failed."), Profile);
|
||||
return 2;
|
||||
}
|
||||
WorstStep2fMs = FMath::Max(WorstStep2fMs, NewMedianMs);
|
||||
const double NewSolidFraction = 1.0 - NewVariety.OpenFraction;
|
||||
const int64 NewCrumbs = NewShape.Fragments.AirCrumbsBelowOneCubicMeter
|
||||
+ NewShape.Fragments.SolidCrumbsBelowOneCubicMeter;
|
||||
const int64 NewCrumbs = NewShape.Fragments.AirCrumbsAfterBoundaryPadding
|
||||
+ NewShape.Fragments.SolidCrumbsAfterBoundaryPadding;
|
||||
WorstStep2fCrumbs = FMath::Max(WorstStep2fCrumbs, NewCrumbs);
|
||||
WorstStep2fDihedralShare = FMath::Max(
|
||||
WorstStep2fDihedralShare, NewShape.Mesh.ShareDihedralsOver45Degrees);
|
||||
@@ -5163,8 +5376,8 @@ int32 RunStep2f(const FString& Params)
|
||||
WorstStep2fFloorOpenings, NewShape.Structure.FloorOpenings);
|
||||
WorstStep2fSolidFraction = FMath::Max(WorstStep2fSolidFraction, NewSolidFraction);
|
||||
BestStep2fSolidFraction = FMath::Min(BestStep2fSolidFraction, NewSolidFraction);
|
||||
bStep2fCrumbGatePassed &= NewShape.Fragments.AirCrumbsBelowOneCubicMeter == 0
|
||||
&& NewShape.Fragments.SolidCrumbsBelowOneCubicMeter == 0;
|
||||
bStep2fCrumbGatePassed &= NewShape.Fragments.AirCrumbsAfterBoundaryPadding == 0
|
||||
&& NewShape.Fragments.SolidCrumbsAfterBoundaryPadding == 0;
|
||||
bStep2fFlakeGatePassed &= NewShape.Mesh.TriangleCount > 0
|
||||
&& NewShape.Mesh.ManifoldEdgeCount > 0
|
||||
&& NewShape.Mesh.ShareDihedralsOver45Degrees <= 0.25
|
||||
@@ -5172,7 +5385,7 @@ int32 RunStep2f(const FString& Params)
|
||||
bStep2fBandLimitPassed &= NewShape.Detail.LatticeScaleEnergyRatio <= 0.20
|
||||
&& NewShape.Detail.Rms >= 0.40;
|
||||
bStep2fLodPassed &= NewShape.Lod1VertexCount > 0
|
||||
&& NewShape.Lod1P95SurfaceErrorVoxels <= 3.5;
|
||||
&& NewShape.Lod1P95SurfaceErrorVoxels <= 0.5;
|
||||
bStep2fGridPassed &= NewShape.Grid.PitchCorrelationPeak <= 0.40
|
||||
&& NewShape.Grid.PitchPeakExcess <= 0.08;
|
||||
bStep2fStructurePassed &= NewSolidFraction >= 0.25 && NewSolidFraction <= 0.88
|
||||
@@ -5189,6 +5402,51 @@ int32 RunStep2f(const FString& Params)
|
||||
LegacyShape, LegacyMedianMs, LegacyVariety));
|
||||
Row->SetObjectField(TEXT("step2f"), ShapeJson(
|
||||
NewShape, NewMedianMs, NewVariety));
|
||||
TArray<TSharedPtr<FJsonValue>> PaddedCrumbDiagnostics;
|
||||
for (const FStep2fFragmentMetrics::FCrumbComponent& Crumb
|
||||
: NewPaddedCrumbComponents)
|
||||
{
|
||||
// Root is an actual sample in the connected component. The min/max coordinates are
|
||||
// an axis-aligned bound and their corner may not belong to the component.
|
||||
const FIntVector SampleVoxel = PaddedCrumbOrigin + Crumb.Root;
|
||||
const float SampleX = static_cast<float>(SampleVoxel.X);
|
||||
const float SampleY = static_cast<float>(SampleVoxel.Y);
|
||||
const float SampleZ = static_cast<float>(SampleVoxel.Z);
|
||||
TSharedRef<FJsonObject> CrumbJson = MakeShared<FJsonObject>();
|
||||
CrumbJson->SetStringField(TEXT("sign"), Crumb.bAir ? TEXT("air") : TEXT("solid"));
|
||||
CrumbJson->SetNumberField(TEXT("samples"), Crumb.Samples);
|
||||
CrumbJson->SetNumberField(TEXT("density_from_padded_array_at_min"),
|
||||
Crumb.DensityAtMin);
|
||||
CrumbJson->SetNumberField(TEXT("density_at_root"), Crumb.DensityAtRoot);
|
||||
CrumbJson->SetNumberField(TEXT("sign_mismatch_samples"),
|
||||
Crumb.SignMismatchSamples);
|
||||
CrumbJson->SetArrayField(TEXT("root_world_voxel"), {
|
||||
MakeShared<FJsonValueNumber>(PaddedCrumbOrigin.X + Crumb.Root.X),
|
||||
MakeShared<FJsonValueNumber>(PaddedCrumbOrigin.Y + Crumb.Root.Y),
|
||||
MakeShared<FJsonValueNumber>(PaddedCrumbOrigin.Z + Crumb.Root.Z)});
|
||||
CrumbJson->SetArrayField(TEXT("min_world_voxel"), {
|
||||
MakeShared<FJsonValueNumber>(SampleVoxel.X),
|
||||
MakeShared<FJsonValueNumber>(SampleVoxel.Y),
|
||||
MakeShared<FJsonValueNumber>(SampleVoxel.Z)});
|
||||
CrumbJson->SetArrayField(TEXT("max_world_voxel"), {
|
||||
MakeShared<FJsonValueNumber>(PaddedCrumbOrigin.X + Crumb.Max.X),
|
||||
MakeShared<FJsonValueNumber>(PaddedCrumbOrigin.Y + Crumb.Max.Y),
|
||||
MakeShared<FJsonValueNumber>(PaddedCrumbOrigin.Z + Crumb.Max.Z)});
|
||||
CrumbJson->SetNumberField(TEXT("final_density_at_min"),
|
||||
VoxelForgeRedefinedDensityField::EvaluateParameterized(
|
||||
*World.Generator, SampleX, SampleY, SampleZ));
|
||||
CrumbJson->SetNumberField(TEXT("macro_at_min"),
|
||||
VoxelForgeRedefinedDensityField::EvaluateParameterizedMacroOnly(
|
||||
*World.Generator, SampleX, SampleY, SampleZ));
|
||||
CrumbJson->SetNumberField(TEXT("detail_at_min"),
|
||||
VoxelForgeRedefinedDensityField::EvaluateParameterizedDetail(
|
||||
*World.Generator, SampleX, SampleY, SampleZ));
|
||||
CrumbJson->SetStringField(TEXT("macro_description_at_min"),
|
||||
VoxelForgeRedefinedDensityField::DescribeParameterizedMacroAt(
|
||||
*World.Generator, SampleX, SampleY, SampleZ));
|
||||
PaddedCrumbDiagnostics.Add(MakeShared<FJsonValueObject>(CrumbJson));
|
||||
}
|
||||
Row->SetArrayField(TEXT("step2f_padded_crumb_diagnostics"), PaddedCrumbDiagnostics);
|
||||
Row->SetObjectField(TEXT("step2e_parameters"), Step2eParametersJson(LegacyParameters));
|
||||
Row->SetObjectField(TEXT("step2f_parameters"), Step2eParametersJson(NewParameters));
|
||||
TArray<TSharedPtr<FJsonValue>> LegacyTrialValues;
|
||||
@@ -5269,8 +5527,8 @@ int32 RunStep2f(const FString& Params)
|
||||
"solid=%.3f crumbs=%lld/%lld flakes=%.3f/%.3f grid=%.3f/%.3f "
|
||||
"detail=%.3f lod95=%.2f roof=%lld floor=%lld"),
|
||||
Profile, ProfileName, LegacyMedianMs, NewMedianMs, NewSolidFraction,
|
||||
static_cast<long long>(NewShape.Fragments.AirCrumbsBelowOneCubicMeter),
|
||||
static_cast<long long>(NewShape.Fragments.SolidCrumbsBelowOneCubicMeter),
|
||||
static_cast<long long>(NewShape.Fragments.AirCrumbsAfterBoundaryPadding),
|
||||
static_cast<long long>(NewShape.Fragments.SolidCrumbsAfterBoundaryPadding),
|
||||
NewShape.Mesh.ShareDihedralsOver45Degrees, NewShape.Detail.LatticeScaleEnergyRatio,
|
||||
NewShape.Grid.PitchCorrelationPeak, NewShape.Grid.PitchPeakExcess,
|
||||
NewShape.Detail.Rms, NewShape.Lod1P95SurfaceErrorVoxels,
|
||||
@@ -5363,7 +5621,7 @@ int32 RunStep2f(const FString& Params)
|
||||
Step2eCrumbNumbers->SetNumberField(TEXT("crumbs_below_1m3_max"), WorstStep2eCrumbs);
|
||||
TSharedRef<FJsonObject> Step2fCrumbNumbers = MakeShared<FJsonObject>();
|
||||
Step2fCrumbNumbers->SetNumberField(TEXT("crumbs_below_1m3_max"), WorstStep2fCrumbs);
|
||||
AddGate(TEXT("crumbs"), TEXT("zero 25 cm-grid connected components smaller than 64 voxels (about 1 m3)"),
|
||||
AddGate(TEXT("crumbs"), TEXT("zero 25 cm-grid components smaller than 64 voxels, measured in a 64-sample-padded volume and restricted to components intersecting the 259-cube"),
|
||||
bStep2eCrumbsRejected, bStep2fCrumbGatePassed, Step2eCrumbNumbers, Step2fCrumbNumbers);
|
||||
TSharedRef<FJsonObject> Step2eFlakeNumbers = MakeShared<FJsonObject>();
|
||||
Step2eFlakeNumbers->SetNumberField(TEXT("max_dihedral_share_over_45deg"), WorstStep2eDihedralShare);
|
||||
@@ -5385,7 +5643,7 @@ int32 RunStep2f(const FString& Params)
|
||||
Step2eLodNumbers->SetNumberField(TEXT("max_l1_p95_surface_distance_voxels"), WorstStep2eLodDistance);
|
||||
TSharedRef<FJsonObject> Step2fLodNumbers = MakeShared<FJsonObject>();
|
||||
Step2fLodNumbers->SetNumberField(TEXT("max_l1_p95_surface_distance_voxels"), WorstStep2fLodDistance);
|
||||
AddGate(TEXT("lod_stability"), TEXT("L1 mesh p95 estimated distance from the L0 isosurface <= 3.5 lattice samples"),
|
||||
AddGate(TEXT("lod_stability"), TEXT("L1 mesh p95 estimated distance from the L0 isosurface <= 0.5 lattice samples"),
|
||||
bStep2eLodRejected, bStep2fLodPassed, Step2eLodNumbers, Step2fLodNumbers);
|
||||
TSharedRef<FJsonObject> Step2eGridNumbers = MakeShared<FJsonObject>();
|
||||
Step2eGridNumbers->SetNumberField(TEXT("max_pitch_autocorrelation"), WorstStep2ePitchCorrelation);
|
||||
@@ -5419,6 +5677,10 @@ int32 RunStep2f(const FString& Params)
|
||||
Report->SetNumberField(TEXT("step2f_solid_fraction_max"), WorstStep2fSolidFraction);
|
||||
Report->SetNumberField(TEXT("seed"), Seed);
|
||||
Report->SetNumberField(TEXT("ring_dimension"), Dimension);
|
||||
Report->SetArrayField(TEXT("ring_origin_voxels"), {
|
||||
MakeShared<FJsonValueNumber>(Origin.X),
|
||||
MakeShared<FJsonValueNumber>(Origin.Y),
|
||||
MakeShared<FJsonValueNumber>(Origin.Z)});
|
||||
Report->SetNumberField(TEXT("ring_samples"), static_cast<double>(TotalSamples));
|
||||
Report->SetNumberField(TEXT("workers_requested"), Workers);
|
||||
Report->SetNumberField(TEXT("world_prepare_ms_excluded"), WorldPrepareSeconds * 1000.0);
|
||||
@@ -5427,12 +5689,15 @@ int32 RunStep2f(const FString& Params)
|
||||
"A conservative nearest-neighbor cap leaves a solid web between chambers; a rooted "
|
||||
"tree of narrow tunnels and capped shafts joins them. Sparse ledges, pillars, "
|
||||
"stalactites and large hanging masses remain per-cell rolls. The point and "
|
||||
"brick detail samplers share three 32/16/8-sample bands with an eight-sample "
|
||||
"shortest period and a smooth ridge response."));
|
||||
"brick detail samplers share independently rotated 48/24/12-sample bands with a "
|
||||
"twelve-sample shortest period and a smooth ridge response; their final-field "
|
||||
"detail supremum is 3.822 units."));
|
||||
Report->SetStringField(TEXT("component_method"),
|
||||
TEXT("26-neighbor connected components are counted on the exact 25 cm sign lattice; "
|
||||
"components smaller than 64 samples (< 1 m3) are reported separately for air and solid. "
|
||||
"The existing one-meter downsampled air component count is also reported."));
|
||||
TEXT("26-neighbor connected components are counted on the exact 25 cm sign lattice. "
|
||||
"Raw components below 64 samples (< 1 m3) are reported, then a 64-sample pad is "
|
||||
"evaluated around each 259-cube and only components intersecting the original cube "
|
||||
"are counted again; this prevents a clipped large component at the ring edge from "
|
||||
"being called a crumb. The one-meter downsampled air component count is also reported."));
|
||||
Report->SetStringField(TEXT("mesh_method"),
|
||||
TEXT("Eight level-0 tiles and one level-1 tile cover the same 64-sample cube selected "
|
||||
"for maximum coarse-sampled surface crossings. "
|
||||
@@ -5452,6 +5717,63 @@ int32 RunStep2f(const FString& Params)
|
||||
static_cast<double>(ProofAudit.CorrectProofSamplesChecked));
|
||||
Existing->SetNumberField(TEXT("correct_proof_sign_violations"),
|
||||
static_cast<double>(ProofAudit.CorrectProofViolations));
|
||||
TSharedRef<FJsonObject> FirstProofViolation = MakeShared<FJsonObject>();
|
||||
FirstProofViolation->SetBoolField(TEXT("present"), ProofAudit.CorrectProofViolations > 0);
|
||||
FirstProofViolation->SetArrayField(TEXT("brick_origin_voxels"), {
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationBrickOrigin.X),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationBrickOrigin.Y),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationBrickOrigin.Z)});
|
||||
FirstProofViolation->SetArrayField(TEXT("brick_size"), {
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationBrickSize.X),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationBrickSize.Y),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationBrickSize.Z)});
|
||||
FirstProofViolation->SetArrayField(TEXT("sample_voxels"), {
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationSample.X),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationSample.Y),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationSample.Z)});
|
||||
FirstProofViolation->SetBoolField(TEXT("inside_brick"),
|
||||
ProofAudit.bFirstViolationWasInsideBrick);
|
||||
FirstProofViolation->SetBoolField(TEXT("claimed_air"),
|
||||
ProofAudit.bFirstViolationClaimedAir);
|
||||
FirstProofViolation->SetNumberField(TEXT("final_density"),
|
||||
ProofAudit.FirstViolationDensity);
|
||||
FirstProofViolation->SetNumberField(TEXT("center_macro"),
|
||||
ProofAudit.FirstViolationCenterMacro);
|
||||
FirstProofViolation->SetNumberField(TEXT("sample_macro"),
|
||||
ProofAudit.FirstViolationSampleMacro);
|
||||
FirstProofViolation->SetNumberField(TEXT("center_room_macro"),
|
||||
ProofAudit.FirstViolationCenterRoomMacro);
|
||||
FirstProofViolation->SetNumberField(TEXT("sample_room_macro"),
|
||||
ProofAudit.FirstViolationSampleRoomMacro);
|
||||
FirstProofViolation->SetNumberField(TEXT("sample_detail"),
|
||||
ProofAudit.FirstViolationSampleDetail);
|
||||
FirstProofViolation->SetNumberField(TEXT("center_to_sample_macro_delta_per_voxel"),
|
||||
ProofAudit.FirstViolationMacroDeltaPerVoxel);
|
||||
FirstProofViolation->SetNumberField(TEXT("path_max_macro_gradient"),
|
||||
ProofAudit.FirstViolationPathMaxGradient);
|
||||
FirstProofViolation->SetArrayField(TEXT("path_max_from_voxels"), {
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationPathMaxFrom.X),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationPathMaxFrom.Y),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationPathMaxFrom.Z)});
|
||||
FirstProofViolation->SetArrayField(TEXT("path_max_to_voxels"), {
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationPathMaxTo.X),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationPathMaxTo.Y),
|
||||
MakeShared<FJsonValueNumber>(ProofAudit.FirstViolationPathMaxTo.Z)});
|
||||
FirstProofViolation->SetNumberField(TEXT("path_max_from_macro"),
|
||||
ProofAudit.FirstViolationPathMaxFromMacro);
|
||||
FirstProofViolation->SetNumberField(TEXT("path_max_to_macro"),
|
||||
ProofAudit.FirstViolationPathMaxToMacro);
|
||||
FirstProofViolation->SetNumberField(TEXT("path_max_from_room_macro"),
|
||||
ProofAudit.FirstViolationPathMaxFromRoomMacro);
|
||||
FirstProofViolation->SetNumberField(TEXT("path_max_to_room_macro"),
|
||||
ProofAudit.FirstViolationPathMaxToRoomMacro);
|
||||
FirstProofViolation->SetStringField(TEXT("path_max_from_room_diagnostic"),
|
||||
ProofAudit.FirstViolationPathMaxFromRoomDiagnostic);
|
||||
FirstProofViolation->SetStringField(TEXT("path_max_to_room_diagnostic"),
|
||||
ProofAudit.FirstViolationPathMaxToRoomDiagnostic);
|
||||
FirstProofViolation->SetNumberField(TEXT("tiny_step_max_macro_gradient"),
|
||||
ProofAudit.FirstViolationTinyStepMaxGradient);
|
||||
Existing->SetObjectField(TEXT("first_proof_violation"), FirstProofViolation);
|
||||
Existing->SetNumberField(TEXT("undersized_bound_claims_checked"),
|
||||
static_cast<double>(ProofAudit.DeliberatelyUndersizedBoundClaimsChecked));
|
||||
Existing->SetNumberField(TEXT("wrong_sign_samples_caught_by_undersized_bound"),
|
||||
|
||||
Reference in new issue
Block a user