Files
VoxelForge/Source/VoxelForge/Private/Tests/VoxelForgeComposerStructureRollTest.cpp
T
Claude 8117d118eb refactor(opstack): the operator stack is the only density path
Every strate now generates through its operator stack: authored
definitions, cooked-season entries (recipe or native) and editor
composer candidates alike. The per-strate opt-in and the archetype
switch it guarded are gone.

- Remove UVoxelStrateDefinition::bUseOperatorStack and the season
  wiring that set it; UsesOperatorStackForChunk now only says whether
  the slot has a known archetype.
- GetDensityAt always builds a stack (composer, then recipe, then the
  native archetype stack); a degenerate zero-height strate stays air.
- Remove the startup opt-in warning, the operator_stack startup-trace
  field and the four "Cave Bail Not Op Stack" stats (the remaining
  bail is counted by Cave Bail No Stack).
- Explore commandlet: drop -opstack and the operator_stack fields;
  VoxelForgeTest.ps1 no longer passes -opstack=1.
- Tests: FTestWorld::Build loses its bool parameter; legacy-vs-stack
  comparisons (MazeSeam, VerticalShaftSeam, TerrainDetailLiveness,
  WorldEdgeSeal, TilePostReach) collapse to the single stack path.

This changes terrain for any strate asset that did not already have
the opt-in ticked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 15:56:19 +00:00

922 lines
41 KiB
C++

// Tier 4b — deterministic structure recipes, dependency-safe materialisation, and measurement.
#if WITH_DEV_AUTOMATION_TESTS
#include "Misc/AutomationTest.h"
#include "HAL/PlatformTime.h"
#include "Math/RandomStream.h"
#include "VoxelForgeTestFixture.h"
#include "VoxelDensityOpStack.h"
#include "VoxelSettings.h"
#include "VoxelStrateComposer.h"
#include "VoxelStrateMeasure.h"
#include "VoxelStratePreview.h"
#include "Misc/Paths.h"
namespace
{
constexpr int32 NumCandidates = 64;
constexpr float LargestComponentSurvivalThreshold = 0.50f;
constexpr int32 BoxChecksPerCandidate = 40;
constexpr int32 BoxStep = 1;
constexpr int32 BoxCells = 8;
constexpr int32 FinePreviewSampleStep = 1;
constexpr int32 FinePreviewRadiusVoxels = 64;
constexpr int32 FinePreviewMaxCells = 2000000;
const TCHAR* VF_ConnectivityName(EVoxelConnectivityResult Result)
{
switch (Result)
{
case EVoxelConnectivityResult::Connected: return TEXT("Connected");
case EVoxelConnectivityResult::NotConnectedAtThisResolution: return TEXT("NotConnected");
case EVoxelConnectivityResult::StartCellSolid: return TEXT("StartCellSolid");
case EVoxelConnectivityResult::GoalCellSolid: return TEXT("GoalCellSolid");
case EVoxelConnectivityResult::OutOfWindow: return TEXT("OutOfWindow");
case EVoxelConnectivityResult::CoarseLiedBudgetExhausted: return TEXT("CoarseLiedBudget");
case EVoxelConnectivityResult::StartCellNotPlayerFit: return TEXT("StartCellNotPlayerFit");
case EVoxelConnectivityResult::GoalCellNotPlayerFit: return TEXT("GoalCellNotPlayerFit");
}
return TEXT("Unknown");
}
const TCHAR* VF_ShapeName(EVoxelStrateOpClass OpClass)
{
switch (OpClass)
{
case EVoxelStrateOpClass::RoomGraphSource: return TEXT("RoomGraph");
case EVoxelStrateOpClass::LatticeCorridorSource:return TEXT("Lattice");
case EVoxelStrateOpClass::ShaftFieldSource: return TEXT("Shaft");
case EVoxelStrateOpClass::IslandBlobSource: return TEXT("Island");
case EVoxelStrateOpClass::NoiseRibbonSource: return TEXT("Noise");
default: return TEXT("invalid");
}
}
int32 VF_FloorDiv(int32 A, int32 B)
{
const int32 Q = A / B;
const int32 R = A % B;
return (R != 0 && (R < 0) != (B < 0)) ? Q - 1 : Q;
}
bool VF_BitEqual(float A, float B)
{
uint32 ABits = 0;
uint32 BBits = 0;
FMemory::Memcpy(&ABits, &A, sizeof(ABits));
FMemory::Memcpy(&BBits, &B, sizeof(BBits));
return ABits == BBits;
}
struct FBlockSpec
{
EVoxelStrateParamBlock Block;
ECaveGeneratorType Archetype;
uint32 SeedSalt;
};
static const FBlockSpec GBlockSpecs[] =
{
{ EVoxelStrateParamBlock::TunnelNetwork, ECaveGeneratorType::TunnelNetwork, 0x1001u },
{ EVoxelStrateParamBlock::Slab, ECaveGeneratorType::FlatPlain, 0x1003u },
{ EVoxelStrateParamBlock::Maze, ECaveGeneratorType::Maze, 0x1005u },
{ EVoxelStrateParamBlock::Surface, ECaveGeneratorType::SurfaceWorld, 0x1007u },
{ EVoxelStrateParamBlock::VerticalShaft, ECaveGeneratorType::VerticalShafts, 0x1009u },
{ EVoxelStrateParamBlock::FloatingIsland, ECaveGeneratorType::FloatingIslands, 0x100Bu },
};
void VF_CopyRollBlock(FVoxelStrateArchetypeParams& OutParams,
const FVoxelStrateRollInfo& Roll,
ECaveGeneratorType Archetype)
{
switch (Archetype)
{
case ECaveGeneratorType::TunnelNetwork:
OutParams.TunnelNetworkParams = Roll.ArchetypeParams.TunnelNetworkParams;
break;
case ECaveGeneratorType::FlatPlain:
case ECaveGeneratorType::CrystalChamber:
OutParams.SlabParams = Roll.ArchetypeParams.SlabParams;
break;
case ECaveGeneratorType::Maze:
OutParams.MazeParams = Roll.ArchetypeParams.MazeParams;
break;
case ECaveGeneratorType::SurfaceWorld:
OutParams.SurfaceParams = Roll.ArchetypeParams.SurfaceParams;
break;
case ECaveGeneratorType::VerticalShafts:
OutParams.VerticalShaftParams = Roll.ArchetypeParams.VerticalShaftParams;
break;
case ECaveGeneratorType::FloatingIslands:
OutParams.FloatingIslandParams = Roll.ArchetypeParams.FloatingIslandParams;
break;
default:
break;
}
}
void VF_SetAllRuntimeBounds(FVoxelStrateArchetypeParams& Params,
float TopWorldZ, float BottomWorldZ)
{
Params.TunnelNetworkParams.StrateTopWorldZ = TopWorldZ;
Params.TunnelNetworkParams.StrateBottomWorldZ = BottomWorldZ;
Params.SlabParams.StrateTopWorldZ = TopWorldZ;
Params.SlabParams.StrateBottomWorldZ = BottomWorldZ;
Params.MazeParams.StrateTopWorldZ = TopWorldZ;
Params.MazeParams.StrateBottomWorldZ = BottomWorldZ;
Params.SurfaceParams.StrateTopWorldZ = TopWorldZ;
Params.SurfaceParams.StrateBottomWorldZ = BottomWorldZ;
Params.VerticalShaftParams.StrateTopWorldZ = TopWorldZ;
Params.VerticalShaftParams.StrateBottomWorldZ = BottomWorldZ;
Params.FloatingIslandParams.StrateTopWorldZ = TopWorldZ;
Params.FloatingIslandParams.StrateBottomWorldZ = BottomWorldZ;
}
class FStackDensitySampler final : public IVoxelStrateDensitySampler
{
public:
explicit FStackDensitySampler(const FVoxelOpStack& InStack) : Stack(InStack) {}
float SampleDensity(float WorldX, float WorldY, float WorldZ) const override
{
return Stack.EvalMC(WorldX, WorldY, WorldZ);
}
private:
const FVoxelOpStack& Stack;
};
struct FBoxVerdictReport
{
int32 Mixed = 0;
int32 AllSolid = 0;
int32 AllAir = 0;
int32 Proved = 0;
int64 CheckedVoxels = 0;
int32 Violations = 0;
FString FirstViolation;
};
FBoxVerdictReport VF_CheckStackBoxVerdicts(const FVoxelOpStack& Stack,
const FVoxelOpContext& Context,
int32 CandidateIndex,
int32 BottomVoxelZ,
int32 TopVoxelZ)
{
FBoxVerdictReport Report;
constexpr int32 Extent = BoxStep * BoxCells;
constexpr int32 SpanCells = 40;
const int32 LoTile = VF_FloorDiv(BottomVoxelZ, Extent);
const int32 HiTile = FMath::Max(LoTile, VF_FloorDiv(TopVoxelZ, Extent));
FRandomStream Rng(0x6C617731 ^ CandidateIndex);
for (int32 BoxIndex = 0; BoxIndex < BoxChecksPerCandidate; ++BoxIndex)
{
const FIntVector Origin(
Rng.RandRange(-SpanCells, SpanCells) * Extent,
Rng.RandRange(-SpanCells, SpanCells) * Extent,
Rng.RandRange(LoTile, HiTile) * Extent);
const FBox Box(
FVector((float)(Origin.X - BoxStep), (float)(Origin.Y - BoxStep),
(float)(Origin.Z - BoxStep)),
FVector((float)(Origin.X + (BoxCells + 1) * BoxStep),
(float)(Origin.Y + (BoxCells + 1) * BoxStep),
(float)(Origin.Z + (BoxCells + 1) * BoxStep)));
const EVoxelTileClass Verdict = Stack.ClassifyBox(Box, Context);
if (Verdict == EVoxelTileClass::Mixed)
{
++Report.Mixed;
continue;
}
if (Verdict == EVoxelTileClass::AllSolid)
{
++Report.AllSolid;
}
else
{
++Report.AllAir;
}
++Report.Proved;
const int32 GridDim = BoxCells + 1;
const bool bClaimsSolid = Verdict == EVoxelTileClass::AllSolid;
for (int32 GZ = -1; GZ <= GridDim; ++GZ)
for (int32 GY = -1; GY <= GridDim; ++GY)
for (int32 GX = -1; GX <= GridDim; ++GX)
{
const float X = (float)(Origin.X + GX * BoxStep);
const float Y = (float)(Origin.Y + GY * BoxStep);
const float Z = (float)(Origin.Z + GZ * BoxStep);
const float Density = Stack.EvalMC(X, Y, Z);
++Report.CheckedVoxels;
const bool bAgrees = bClaimsSolid ? (Density < 0.0f) : (Density >= 0.0f);
if (!bAgrees)
{
++Report.Violations;
if (Report.FirstViolation.IsEmpty())
{
Report.FirstViolation = FString::Printf(
TEXT("candidate %d box %d origin (%d,%d,%d) said %s but MC density "
"at (%.0f,%.0f,%.0f) was %.9g"),
CandidateIndex, BoxIndex, Origin.X, Origin.Y, Origin.Z,
bClaimsSolid ? TEXT("AllSolid") : TEXT("AllAir"),
X, Y, Z, Density);
}
}
}
}
return Report;
}
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FVoxelForgeComposerStructureRollTest,
"VoxelForge.Composer.StructureRoll",
EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::EngineFilter)
bool FVoxelForgeComposerStructureRollTest::RunTest(const FString& Parameters)
{
using namespace VoxelForgeTest;
(void)Parameters;
const double TestStartSeconds = FPlatformTime::Seconds();
const FString SettingsPath = TEXT("/Game/VoxelForge/DA_Settings.DA_Settings");
UVoxelSettings* AuthoredSettings = LoadObject<UVoxelSettings>(nullptr, *SettingsPath);
if (AuthoredSettings == nullptr)
{
AddError(FString::Printf(TEXT("Could not load %s; structure roll did not run."), *SettingsPath));
return false;
}
FVoxelStrateCorpus Corpus;
FString CorpusReport;
const bool bCorpusLoaded = Corpus.LoadFromAssetRegistry(CorpusReport);
AddInfo(CorpusReport);
TestTrue(TEXT("structure roll has a usable corpus for parameter blocks"),
bCorpusLoaded && Corpus.IsValid());
if (!bCorpusLoaded || !Corpus.IsValid())
{
return false;
}
FTestWorld World;
World.Build(1337, 2);
if (!World.IsValid())
{
AddError(World.WhyInvalid());
return false;
}
TestTrue(TEXT("structure-roll fixture keeps WorldRadiusVoxels at 0"),
World.Settings->WorldRadiusVoxels == 0.0f);
constexpr int32 CandidateStrateIndex = FTestWorld::SlotFlatPlain;
int32 TopVoxelZ = 0;
int32 BottomVoxelZ = 0;
if (!World.GetSlotVoxelZRange(CandidateStrateIndex, TopVoxelZ, BottomVoxelZ))
{
AddError(TEXT("structure-roll fixture has no candidate Z range"));
return false;
}
const float TopWorldZ = (float)TopVoxelZ + 1.0f;
const float BottomWorldZ = (float)BottomVoxelZ;
FVector ArrivalPoint = FVector::ZeroVector;
FVector DeparturePoint = FVector::ZeroVector;
int32 ArrivalCount = 0;
int32 DepartureCount = 0;
for (const FVoxelPassage& Passage : World.StrateManager->GetPassages())
{
if (Passage.LowerStrateIndex == CandidateStrateIndex
&& Passage.UpperStrateIndex + 1 == CandidateStrateIndex)
{
ArrivalPoint = Passage.LowerPoint;
++ArrivalCount;
}
if (Passage.UpperStrateIndex == CandidateStrateIndex
&& Passage.LowerStrateIndex == CandidateStrateIndex + 1)
{
DeparturePoint = Passage.UpperPoint;
++DepartureCount;
}
}
TestEqual(TEXT("candidate has exactly one arrival mouth"), ArrivalCount, 1);
TestEqual(TEXT("candidate has exactly one departure mouth"), DepartureCount, 1);
FVoxelStrateMeasureSettings MeasureSettings;
MeasureSettings.SampleStep = 4;
MeasureSettings.RadiusInVoxels = 256;
MeasureSettings.CenterXY = FVector2D::ZeroVector;
MeasureSettings.MaxCells = 8000000;
MeasureSettings.MaxRouteRetries = 16;
MeasureSettings.HeadroomCells = 2;
MeasureSettings.InteriorMarginVoxels = -1;
FVoxelStrateFinePreviewSettings FinePreviewSettings;
FinePreviewSettings.SampleStep = FinePreviewSampleStep;
FinePreviewSettings.RadiusInVoxels = FinePreviewRadiusVoxels;
FinePreviewSettings.MaxCells = FinePreviewMaxCells;
TestTrue(TEXT("fine preview defaults are a bounded valid ROI"), FinePreviewSettings.IsValid());
const FVoxelStrateMeasureSettings FineMeasureSettings =
FinePreviewSettings.MakeMeasureSettings(MeasureSettings);
FString CandidateTable = TEXT(
"candidate | recipe (compact) | air | largest share | walkable | feature scale | arrival->departure\n");
int32 NumSurvivors = 0;
int32 NumNonVacuous = 0;
int32 NumLargestEnough = 0;
int32 NumLawPasses = 0;
int32 NumInvalidRecipes = 0;
int32 NumParameterRollFailures = 0;
int32 TotalBoxMixed = 0;
int32 TotalBoxAllSolid = 0;
int32 TotalBoxAllAir = 0;
int32 TotalBoxProved = 0;
int64 TotalBoxCheckedVoxels = 0;
int32 TotalBoxViolations = 0;
FString FirstBoxViolation;
int32 TotalBootstrapJitterFields = 0;
TSet<FString> BootstrapJitterFieldNames;
TArray<FVoxelStratePreviewCandidate> PreviewCandidates;
PreviewCandidates.Reserve(NumCandidates);
FVoxelStratePreviewWindow PreviewWindow;
bool bPreviewWindowSet = false;
int32 NumFineRequested = 0;
int32 NumFineRendered = 0;
int32 NumFineContourRendered = 0;
int32 NumFineRefused = 0;
int32 NumFinePlanBlankBeforeCentering = 0;
int32 NumFinePlanBlankAfterCentering = 0;
int32 NumFineCardBlankBeforeCentering = 0;
int32 NumFineCardBlankAfterCentering = 0;
int64 TotalFineCells = 0;
int64 PeakFineCaptureBytes = 0;
int64 PeakFineRasterBytes = 0;
double FineMeasureSeconds = 0.0;
double FineRenderSeconds = 0.0;
double CoarsePreviewSeconds = 0.0;
bool bFineCapRefusalChecked = false;
const FString PreviewRunId = FString::Printf(
TEXT("structure_seed_%d_corpus_%08x_step_%d_radius_%d_fine_step_%d_fine_radius_%d"),
AuthoredSettings->Seed, Corpus.GetContentsHash(),
MeasureSettings.SampleStep, MeasureSettings.RadiusInVoxels,
FinePreviewSettings.SampleStep, FinePreviewSettings.RadiusInVoxels);
const FString PreviewDirectory = FPaths::ProjectSavedDir()
/ TEXT("ComposerPreview") / PreviewRunId;
TArray<int32> ShapeCounts;
ShapeCounts.Init(0, 5);
int32 KCounts[9] = { 0 };
TArray<FVoxelOpStackRecipe> DistinctRecipes;
DistinctRecipes.Reserve(NumCandidates);
const double RollAndMeasureStartSeconds = FPlatformTime::Seconds();
for (int32 CandidateIndex = 0; CandidateIndex < NumCandidates; ++CandidateIndex)
{
#if WITH_EDITOR
// Keep the editor hand-off and the structure test on the same recipe/block roll seam.
const FVoxelStrateComposerCandidate Candidate = VF_RollStrateCandidate(
Corpus, AuthoredSettings->Seed, CandidateIndex, true);
TestTrue(FString::Printf(TEXT("candidate %d hand-off roll is valid"), CandidateIndex),
Candidate.bValid);
const FVoxelOpStackRecipe Recipe = Candidate.Recipe;
#else
const FVoxelOpStackRecipe Recipe = VF_RollStrateStructure(
AuthoredSettings->Seed, CandidateIndex);
#endif
const FVoxelOpStackRecipe RepeatRecipe = VF_RollStrateStructure(
AuthoredSettings->Seed, CandidateIndex);
TestTrue(FString::Printf(TEXT("candidate %d recipe reroll is identical"), CandidateIndex),
VF_AreStrateStructureRecipesIdentical(Recipe, RepeatRecipe));
const uint32 RecipeHash = VF_HashStrateStructureRecipe(Recipe);
bool bSeenRecipe = false;
for (const FVoxelOpStackRecipe& Existing : DistinctRecipes)
{
if (VF_HashStrateStructureRecipe(Existing) == RecipeHash
&& VF_AreStrateStructureRecipesIdentical(Existing, Recipe))
{
bSeenRecipe = true;
break;
}
}
if (!bSeenRecipe) { DistinctRecipes.Add(Recipe); }
switch (Recipe.ShapeSource.OpClass)
{
case EVoxelStrateOpClass::RoomGraphSource: ++ShapeCounts[0]; break;
case EVoxelStrateOpClass::LatticeCorridorSource:++ShapeCounts[1]; break;
case EVoxelStrateOpClass::ShaftFieldSource: ++ShapeCounts[2]; break;
case EVoxelStrateOpClass::IslandBlobSource: ++ShapeCounts[3]; break;
case EVoxelStrateOpClass::NoiseRibbonSource: ++ShapeCounts[4]; break;
default: break;
}
if (Recipe.Modifiers.IsValidIndex(0))
{
++KCounts[Recipe.Modifiers.Num()];
}
FVoxelStrateArchetypeParams CandidateParams;
bool bParameterRollsValid = true;
double CandidateDistanceSquared = 0.0;
for (int32 BlockIndex = 0; BlockIndex < UE_ARRAY_COUNT(GBlockSpecs); ++BlockIndex)
{
#if WITH_EDITOR
const FBlockSpec& Spec = GBlockSpecs[BlockIndex];
if (!Candidate.StructureBlockRolls.IsValidIndex(BlockIndex))
{
bParameterRollsValid = false;
AddError(FString::Printf(TEXT("candidate %d missing parameter block %d"),
CandidateIndex, static_cast<int32>(Spec.Block)));
continue;
}
const FVoxelStrateRollInfo& BlockRoll = Candidate.StructureBlockRolls[BlockIndex];
#else
const FBlockSpec& Spec = GBlockSpecs[BlockIndex];
const FVoxelStrateRollInfo BlockRoll = VF_RollStrateParamsDetailedForArchetype(
Corpus, Spec.Archetype,
AuthoredSettings->Seed ^ static_cast<int32>(Spec.SeedSalt), CandidateIndex);
#endif
if (!BlockRoll.bValid)
{
bParameterRollsValid = false;
++NumParameterRollFailures;
AddError(FString::Printf(TEXT("candidate %d parameter block %d failed: %s"),
CandidateIndex, static_cast<int32>(Spec.Block),
*BlockRoll.FailureReason));
continue;
}
TotalBootstrapJitterFields += BlockRoll.BootstrapJitterFieldCount;
for (const FString& FieldName : BlockRoll.BootstrapJitterFieldNames)
{
BootstrapJitterFieldNames.Add(FieldName);
}
const double BlockDistance = VF_DistanceFromStrateCorpusCentroid(
Corpus, BlockRoll.ArchetypeParams, Spec.Archetype);
CandidateDistanceSquared += BlockDistance * BlockDistance;
VF_CopyRollBlock(CandidateParams, BlockRoll, Spec.Archetype);
}
VF_SetAllRuntimeBounds(CandidateParams, TopWorldZ, BottomWorldZ);
FString AirText = TEXT("invalid");
FString LargestText = TEXT("invalid");
FString WalkableText = TEXT("invalid");
FString FeatureText = TEXT("invalid");
FString LawText = TEXT("not measured");
FVoxelOpStack Stack;
FVoxelOpContext Context;
FString BuildError;
const bool bBuilt = bParameterRollsValid && VF_BuildStackFromRecipe(
Recipe, CandidateParams, AuthoredSettings->Seed, 14.0f,
World.StrateManager.Get(), Stack, Context, &BuildError);
if (!bBuilt)
{
++NumInvalidRecipes;
CandidateTable += FString::Printf(
TEXT("%d | %s | invalid | invalid | invalid | invalid | invalid | build=%s\n"),
CandidateIndex, *VF_FormatStrateStructureRecipe(Recipe),
bParameterRollsValid ? *BuildError : TEXT("parameter block roll failed"));
TestTrue(FString::Printf(TEXT("candidate %d recipe materialises"), CandidateIndex), false);
continue;
}
TestEqual(FString::Printf(TEXT("candidate %d has four mandatory structural posts"), CandidateIndex),
Stack.Num(), Recipe.Modifiers.Num() + 7);
FString ValidationError;
TestTrue(FString::Printf(TEXT("candidate %d passes ValidateChannelOrder"), CandidateIndex),
Stack.ValidateChannelOrder(&ValidationError));
TestTrue(FString::Printf(TEXT("candidate %d context keeps WorldRadiusVoxels at 0"), CandidateIndex),
Context.WorldRadiusVoxels == 0.0f);
Stack.PrepareChunk(Context);
FStackDensitySampler Sampler(Stack);
if (!bFineCapRefusalChecked)
{
FVoxelStrateMeasureSettings RefusalSettings = FineMeasureSettings;
RefusalSettings.MaxCells = 1;
FVoxelStrateSampleGrid RefusedFineGrid;
const FVoxelStrateMetrics RefusedFineMetrics = VF_MeasureStrateWithSampler(
Sampler, BottomVoxelZ, TopVoxelZ + 1,
Context.EdgeSealThickness, RefusalSettings, &RefusedFineGrid);
TestTrue(TEXT("fine preview refuses an over-cap ROI before allocation"),
!RefusedFineMetrics.bValid
&& RefusedFineGrid.Air.Num() == 0
&& RefusedFineGrid.Density.Num() == 0
&& RefusedFineMetrics.RefusalReason.Contains(TEXT("MaxCells")));
bFineCapRefusalChecked = true;
}
FVoxelStrateSampleGrid SampleGrid;
const FVoxelStrateMetrics Metrics = VF_MeasureStrateWithSampler(
Sampler, BottomVoxelZ, TopVoxelZ + 1,
Context.EdgeSealThickness, MeasureSettings, &SampleGrid);
FVoxelConnectivityDiagnostics Law;
if (ArrivalCount == 1 && DepartureCount == 1)
{
Law = VF_DiagnoseConnectivityWithSampler(
Sampler, BottomVoxelZ, TopVoxelZ + 1,
Context.EdgeSealThickness, ArrivalPoint, DeparturePoint, MeasureSettings);
LawText = VF_ConnectivityName(Law.Result);
if (Law.bStartSnapped || Law.bGoalSnapped) { LawText += TEXT(" (snapped)"); }
}
else
{
LawText = FString::Printf(TEXT("mouths=%d/%d"), ArrivalCount, DepartureCount);
}
const bool bNonVacuous = Metrics.bValid
&& Metrics.NumSampled > 0 && Metrics.NumAir > 0 && Metrics.NumSolid > 0;
const bool bLargestEnough = bNonVacuous
&& Metrics.LargestComponentShare >= LargestComponentSurvivalThreshold;
const bool bLawPass = ArrivalCount == 1 && DepartureCount == 1
&& Law.bValid && Law.Result == EVoxelConnectivityResult::Connected
&& !Law.bStartSnapped && !Law.bGoalSnapped;
if (bNonVacuous) { ++NumNonVacuous; }
if (bLargestEnough) { ++NumLargestEnough; }
if (bLawPass) { ++NumLawPasses; }
if (bNonVacuous && bLargestEnough && bLawPass) { ++NumSurvivors; }
if (Metrics.bValid)
{
AirText = FString::Printf(TEXT("%.6f"), Metrics.AirFraction);
LargestText = FString::Printf(TEXT("%.6f"), Metrics.LargestComponentShare);
WalkableText = FString::Printf(TEXT("%.6f"), Metrics.WalkableFraction);
FeatureText = FString::Printf(TEXT("%.6f"), Metrics.MedianFeatureScale);
}
FString RejectionReason;
if (!bNonVacuous)
{
RejectionReason = TEXT("vacuous");
}
else
{
if (!bLargestEnough)
{
RejectionReason = TEXT("fragmented");
}
if (!bLawPass)
{
if (!RejectionReason.IsEmpty())
{
RejectionReason += TEXT("; ");
}
RejectionReason += TEXT("law failed");
}
}
const FBoxVerdictReport BoxReport = VF_CheckStackBoxVerdicts(
Stack, Context, CandidateIndex, BottomVoxelZ, TopVoxelZ);
TotalBoxMixed += BoxReport.Mixed;
TotalBoxAllSolid += BoxReport.AllSolid;
TotalBoxAllAir += BoxReport.AllAir;
TotalBoxProved += BoxReport.Proved;
TotalBoxCheckedVoxels += BoxReport.CheckedVoxels;
TotalBoxViolations += BoxReport.Violations;
if (FirstBoxViolation.IsEmpty() && !BoxReport.FirstViolation.IsEmpty())
{
FirstBoxViolation = BoxReport.FirstViolation;
}
const double CandidateDistance = FMath::Sqrt(
FMath::Max(0.0, CandidateDistanceSquared));
const FVoxelStratePreviewWindow CandidateWindow =
VF_GetStratePreviewWindow(SampleGrid);
if (CandidateWindow.IsValid())
{
if (!bPreviewWindowSet)
{
PreviewWindow = CandidateWindow;
bPreviewWindowSet = true;
}
}
TestTrue(FString::Printf(TEXT("candidate %d preview captures its measurement grid"),
CandidateIndex),
!Metrics.bValid || SampleGrid.HasScalarDensity());
FVoxelStratePreviewCandidate PreviewCandidate;
FString PreviewError;
const double CoarsePreviewStartSeconds = FPlatformTime::Seconds();
const bool bPreviewWritten = VF_WriteStratePreviewCandidate(
PreviewDirectory,
CandidateIndex,
VF_FormatStrateStructureRecipe(Recipe),
SampleGrid,
Metrics,
MeasureSettings.HeadroomCells,
CandidateDistance,
LawText,
!(bNonVacuous && bLargestEnough && bLawPass),
RejectionReason,
PreviewCandidate,
PreviewError);
CoarsePreviewSeconds += FPlatformTime::Seconds() - CoarsePreviewStartSeconds;
TestTrue(FString::Printf(TEXT("candidate %d preview rasterises without a second sample"),
CandidateIndex),
bPreviewWritten);
TestTrue(FString::Printf(TEXT("candidate %d has images when its measurement is valid"),
CandidateIndex),
!Metrics.bValid
|| (PreviewCandidate.bRendered && PreviewCandidate.bContourRendered));
if (!bPreviewWritten)
{
AddError(FString::Printf(TEXT("candidate %d preview failed: %s"),
CandidateIndex, *PreviewError));
}
const bool bCandidateSurvivor = bNonVacuous && bLargestEnough && bLawPass;
if (bCandidateSurvivor)
{
++NumFineRequested;
// Render the old origin-centred pass into the same filenames first. The temporary
// record gives an exact before count from the renderer's content-variation check; the
// corrected capture below overwrites those files. The two grids are sequential, so
// peak retained memory remains one fine capture.
FVoxelStrateSampleGrid OriginFineGrid;
const double OriginFineMeasureStartSeconds = FPlatformTime::Seconds();
const FVoxelStrateMetrics OriginFineMetrics = VF_MeasureStrateWithSampler(
Sampler, BottomVoxelZ, TopVoxelZ + 1,
Context.EdgeSealThickness, FineMeasureSettings, &OriginFineGrid);
FineMeasureSeconds += FPlatformTime::Seconds() - OriginFineMeasureStartSeconds;
FVoxelStratePreviewCandidate OriginFinePreview;
FString OriginFinePreviewError;
const double OriginFineRenderStartSeconds = FPlatformTime::Seconds();
const bool bOriginFineWritten = VF_WriteStratePreviewFineCandidate(
PreviewDirectory, CandidateIndex, OriginFineGrid,
FineMeasureSettings.HeadroomCells,
OriginFineMetrics.bValid ? FString() : OriginFineMetrics.RefusalReason,
OriginFinePreview, OriginFinePreviewError);
FineRenderSeconds += FPlatformTime::Seconds() - OriginFineRenderStartSeconds;
TestTrue(FString::Printf(TEXT("candidate %d origin baseline fine render records"),
CandidateIndex),
bOriginFineWritten);
if (OriginFinePreview.bFineRendered && !OriginFinePreview.bFinePlanContentVaries)
{
++NumFinePlanBlankBeforeCentering;
}
if (OriginFinePreview.bFineRendered && OriginFinePreview.bFineBlank)
{
++NumFineCardBlankBeforeCentering;
}
FVoxelStrateMeasureSettings CenteredFineMeasureSettings = FineMeasureSettings;
if (Metrics.bValid && FMath::IsFinite(Metrics.LargestComponentPoint.X)
&& FMath::IsFinite(Metrics.LargestComponentPoint.Y))
{
CenteredFineMeasureSettings.CenterXY = FVector2D(
Metrics.LargestComponentPoint.X, Metrics.LargestComponentPoint.Y);
}
FVoxelStrateSampleGrid FineGrid;
const double FineMeasureStartSeconds = FPlatformTime::Seconds();
const FVoxelStrateMetrics FineMetrics = VF_MeasureStrateWithSampler(
Sampler, BottomVoxelZ, TopVoxelZ + 1,
Context.EdgeSealThickness, CenteredFineMeasureSettings, &FineGrid);
FineMeasureSeconds += FPlatformTime::Seconds() - FineMeasureStartSeconds;
const FString FineFailureReason = FineMetrics.bValid
? FString() : FineMetrics.RefusalReason;
const double FineRenderStartSeconds = FPlatformTime::Seconds();
FString FinePreviewError;
const bool bFineWritten = VF_WriteStratePreviewFineCandidate(
PreviewDirectory, CandidateIndex, FineGrid,
FineMeasureSettings.HeadroomCells, FineFailureReason,
PreviewCandidate, FinePreviewError);
FineRenderSeconds += FPlatformTime::Seconds() - FineRenderStartSeconds;
TestTrue(FString::Printf(TEXT("candidate %d fine preview records without a second renderer sample"),
CandidateIndex),
bFineWritten);
if (PreviewCandidate.bFineRendered && !PreviewCandidate.bFinePlanContentVaries)
{
++NumFinePlanBlankAfterCentering;
}
if (PreviewCandidate.bFineRendered && PreviewCandidate.bFineBlank)
{
++NumFineCardBlankAfterCentering;
}
TestTrue(FString::Printf(TEXT("candidate %d fine capture keeps scalar density"),
CandidateIndex),
!FineMetrics.bValid || FineGrid.HasScalarDensity());
if (FineMetrics.bValid)
{
TotalFineCells += FineGrid.CellCount;
PeakFineCaptureBytes = FMath::Max(
PeakFineCaptureBytes,
static_cast<int64>(FineGrid.Air.GetAllocatedSize())
+ static_cast<int64>(FineGrid.Density.GetAllocatedSize()));
}
if (!FineMetrics.bValid)
{
++NumFineRefused;
}
if (PreviewCandidate.bFineRendered)
{
++NumFineRendered;
PeakFineRasterBytes = FMath::Max(
PeakFineRasterBytes,
static_cast<int64>(PreviewCandidate.FineVerticalImageWidth)
* static_cast<int64>(PreviewCandidate.FineVerticalImageHeight)
* static_cast<int64>(sizeof(FColor)));
PeakFineRasterBytes = FMath::Max(
PeakFineRasterBytes,
static_cast<int64>(PreviewCandidate.FinePlanImageWidth)
* static_cast<int64>(PreviewCandidate.FinePlanImageHeight)
* static_cast<int64>(sizeof(FColor)));
}
if (PreviewCandidate.bFineContourRendered)
{
++NumFineContourRendered;
}
if (!bFineWritten)
{
AddError(FString::Printf(TEXT("candidate %d fine preview failed: %s"),
CandidateIndex, *FinePreviewError));
}
}
PreviewCandidates.Add(MoveTemp(PreviewCandidate));
// Same recipe + same block vectors + same seed must yield the same world values. This is
// checked on a fixed point array for every candidate, including cache-backed sources.
FVoxelOpStack RepeatStack;
FVoxelOpContext RepeatContext;
FString RepeatBuildError;
const bool bRepeatBuilt = VF_BuildStackFromRecipe(
Recipe, CandidateParams, AuthoredSettings->Seed, 14.0f,
World.StrateManager.Get(), RepeatStack, RepeatContext, &RepeatBuildError);
TestTrue(FString::Printf(TEXT("candidate %d same recipe rebuild succeeds"), CandidateIndex),
bRepeatBuilt);
if (bRepeatBuilt)
{
RepeatStack.PrepareChunk(RepeatContext);
const FVector ProbePoints[] = {
FVector(-91.0f, -37.0f, BottomWorldZ + 17.0f),
FVector(0.0f, 0.0f, (BottomWorldZ + TopWorldZ) * 0.5f),
FVector(73.0f, 119.0f, TopWorldZ - 19.0f),
FVector(-256.0f, 256.0f, BottomWorldZ + 61.0f),
};
bool bSameWorld = true;
for (const FVector& Point : ProbePoints)
{
if (!VF_BitEqual(Stack.EvalMC(Point.X, Point.Y, Point.Z),
RepeatStack.EvalMC(Point.X, Point.Y, Point.Z)))
{
bSameWorld = false;
break;
}
}
TestTrue(FString::Printf(TEXT("candidate %d same seed yields same world"), CandidateIndex),
bSameWorld);
}
CandidateTable += FString::Printf(
TEXT("%d | %s | %s | %s | %s | %s | %s\n"),
CandidateIndex,
*VF_FormatStrateStructureRecipe(Recipe),
*AirText, *LargestText, *WalkableText, *FeatureText, *LawText);
}
const double RollAndMeasureSeconds = FPlatformTime::Seconds() - RollAndMeasureStartSeconds;
AddInfo(CandidateTable);
FString ShapeDistribution = TEXT("Shape source distribution:");
for (int32 Index = 0; Index < 5; ++Index)
{
const EVoxelStrateOpClass ShapeClass[] = {
EVoxelStrateOpClass::RoomGraphSource,
EVoxelStrateOpClass::LatticeCorridorSource,
EVoxelStrateOpClass::ShaftFieldSource,
EVoxelStrateOpClass::IslandBlobSource,
EVoxelStrateOpClass::NoiseRibbonSource,
};
ShapeDistribution += FString::Printf(TEXT(" %s=%d"),
VF_ShapeName(ShapeClass[Index]), ShapeCounts[Index]);
}
FString KDistribution = TEXT("k distribution:");
for (int32 K = 4; K <= 8; ++K)
{
KDistribution += FString::Printf(TEXT(" k%d=%d"), K, KCounts[K]);
}
AddInfo(ShapeDistribution);
AddInfo(KDistribution);
AddInfo(FString::Printf(
TEXT("Structure diversity: %d distinct recipes of %d; invalid recipes emitted=%d; "
"parameter-block roll failures=%d; recipe/build/measure time=%.3fs."),
DistinctRecipes.Num(), NumCandidates, NumInvalidRecipes,
NumParameterRollFailures, RollAndMeasureSeconds));
AddInfo(FString::Printf(
TEXT("Survival: %d/%d (%.1f%%). Criteria: non-vacuous (valid, sampled, air>0, solid>0), "
"largest component share >= %.2f, and exact unsnapped arrival->departure law "
"Result=Connected. Intermediate counts: non-vacuous=%d, largest threshold=%d, "
"law pass=%d. Lower survival is expected for aggressive structure rolls."),
NumSurvivors, NumCandidates, 100.0f * (float)NumSurvivors / (float)NumCandidates,
LargestComponentSurvivalThreshold, NumNonVacuous, NumLargestEnough, NumLawPasses));
AddInfo(FString::Printf(
TEXT("Rolled custom-stack box verdicts: mixed=%d, AllSolid=%d, AllAir=%d, proved=%d, "
"brute-force voxels checked=%lld, violations=%d. %s"),
TotalBoxMixed, TotalBoxAllSolid, TotalBoxAllAir, TotalBoxProved,
TotalBoxCheckedVoxels, TotalBoxViolations,
FirstBoxViolation.IsEmpty() ? TEXT("No violations.") : *FirstBoxViolation));
AddInfo(FString::Printf(
TEXT("Preview cost: coarse step=%d 64-card pass render %.3fs; fine ROI requested=%d "
"(survivors=%d), rendered=%d, contour pairs=%d, refused=%d; "
"blank plan origin=%d, blank plan largest-open-space=%d; blank card origin=%d, "
"blank card largest-open-space=%d; fine sample %.3fs + "
"render %.3fs; fine cells sampled=%lld; peak fine Air+Density capture=%lld bytes; "
"peak single RGBA raster=%lld bytes; ROI step=%d radius=%d MaxCells=%d."),
MeasureSettings.SampleStep, CoarsePreviewSeconds,
NumFineRequested, NumSurvivors, NumFineRendered, NumFineContourRendered, NumFineRefused,
NumFinePlanBlankBeforeCentering, NumFinePlanBlankAfterCentering,
NumFineCardBlankBeforeCentering, NumFineCardBlankAfterCentering,
FineMeasureSeconds, FineRenderSeconds, TotalFineCells,
PeakFineCaptureBytes, PeakFineRasterBytes,
FinePreviewSettings.SampleStep, FinePreviewSettings.RadiusInVoxels,
FinePreviewSettings.MaxCells));
FString PreviewIndexPath;
FString PreviewError;
const bool bPreviewIndexWritten = bPreviewWindowSet
&& PreviewCandidates.Num() == NumCandidates
&& VF_WriteStratePreviewIndex(
PreviewDirectory,
FString::Printf(TEXT("VoxelForge composer structure preview — seed %d"),
AuthoredSettings->Seed),
PreviewWindow,
PreviewCandidates,
PreviewIndexPath,
PreviewError);
TestTrue(TEXT("64-candidate composer preview index is written"), bPreviewIndexWritten);
if (!bPreviewIndexWritten)
{
AddError(FString::Printf(TEXT("composer preview index failed: %s"), *PreviewError));
}
if (bPreviewWindowSet && PreviewCandidates.Num() > 0)
{
const FVoxelStratePreviewCandidate& FirstPreview = PreviewCandidates[0];
int32 NumRenderedPairs = 0;
int32 NumRenderedContourPairs = 0;
for (const FVoxelStratePreviewCandidate& Candidate : PreviewCandidates)
{
if (Candidate.bRendered)
{
++NumRenderedPairs;
}
if (Candidate.bContourRendered)
{
++NumRenderedContourPairs;
}
}
AddInfo(FString::Printf(
TEXT("Composer preview: %d candidates, %d filled pairs + %d contour pairs, "
"vertical=%dx%d, plan=%dx%d, "
"plan Z chosen from each candidate's measured layer with the highest solid/air "
"boundary count (first candidate Z=%d); index=%s; first-candidate window=%s; "
"fine previews are linked on survivor cards and centred on each largest open "
"space point (never the strate midpoint); image cap=512 px including 20 px "
"scale footer."),
PreviewCandidates.Num(),
NumRenderedPairs,
NumRenderedContourPairs,
FirstPreview.VerticalImageWidth, FirstPreview.VerticalImageHeight,
FirstPreview.PlanImageWidth, FirstPreview.PlanImageHeight,
FirstPreview.PlanSliceWorldZ,
*PreviewIndexPath,
*PreviewWindow.Describe()));
}
AddInfo(FString::Printf(
TEXT("Bootstrap jitter: %d field applications across the six structure blocks; %d "
"distinct archetype fields took the bootstrap path."),
TotalBootstrapJitterFields, BootstrapJitterFieldNames.Num()));
TestEqual(TEXT("exactly 64 structure candidates were requested"), NumCandidates, 64);
TestEqual(TEXT("invalid recipes emitted by the roller/build path"), NumInvalidRecipes, 0);
TestEqual(TEXT("custom-stack box verdict violations"), TotalBoxViolations, 0);
TestTrue(TEXT("fine cap refusal was checked before any fine allocation"),
bFineCapRefusalChecked);
TestEqual(TEXT("fine previews are requested only for coarse survivors"),
NumFineRequested, NumSurvivors);
TestEqual(TEXT("fine preview outcomes account for every requested survivor"),
NumFineRendered + NumFineRefused, NumFineRequested);
AddInfo(FString::Printf(TEXT(
"Fine blank baseline: plan projection %d/%d origin-centred versus %d/%d centred on "
"LargestComponentPoint; complete two-projection cards %d/%d versus %d/%d. A blank "
"projection/card means its filled-cell pixels were uniform (scale footer excluded), "
"not that the measurement was refused."),
NumFinePlanBlankBeforeCentering, NumFineRequested,
NumFinePlanBlankAfterCentering, NumFineRequested,
NumFineCardBlankBeforeCentering, NumFineRequested,
NumFineCardBlankAfterCentering, NumFineRequested));
TestEqual(TEXT("every rendered fine preview has a scalar contour pair"),
NumFineContourRendered, NumFineRendered);
TestTrue(TEXT("structure roller emits 64 distinct structural recipes"),
DistinctRecipes.Num() == NumCandidates);
const double TestSeconds = FPlatformTime::Seconds() - TestStartSeconds;
AddInfo(FString::Printf(TEXT("StructureRoll total runtime %.3fs; WorldRadiusVoxels=0."),
TestSeconds));
return true;
}
#endif // WITH_DEV_AUTOMATION_TESTS