feat(passages): passages are D-tunnels now -- legacy support writer fires ZERO

The owner was right and said so plainly: "the tunnels should be a flat D with then a
bit of noise added, as it's most basic shape -- this alone should make all tunnels not
over X steepness to be walkable." Codex had reached the same conclusion from the other
direction ("the native fix is to author the inter-strate passage floor profile during
passage construction"); it had measured the current position while he described the
destination. This is that refactor.

Walkable inter-strate passages now bake their floor profile and bounded relief during
GeneratePassages. Evaluation composes a D-shaped SmoothMax floor, with final MC
ownership after the generic writers -- the same ordering fix that made the tunnel floor
native in 2260c4d.

  native compositions      7,629
  legacy support fires         0
  floor-profile fallbacks      0

  player-fit cells    20,804 -> 20,830   (+26)
  walk-reachable      10,884 -> 10,909   (+25)
  climb-reachable              10,911
  connectivity        arrival -> departure, Connected
  largest component           10,909

⭐ Support-ON and support-OFF now produce IDENTICAL export geometry (hash 07C14005,
82,273 v / 153,346 t, mesh 2.981 s vs 2.960 s). The writer that previously cost
3,359 reachable cells when disabled is now inert -- the shape provides what it used to
patch. That is the ~17.43% of banded pixels from 4f8b4a9 addressed at the source.

The legacy writer stays only as a fail-closed fallback for a malformed/non-native
passage. It is no longer load-bearing.

PUSHBACK, and it is the right restraint: only passages carrying
`bWalkableTunnelContract` -- currently the authored `Straight` style -- receive the
guaranteed D-floor. Spiral, Cascading and Worm styles do NOT silently gain a
walkability promise without a separate gradient proof. Correct: a guarantee nobody
proved is worse than no guarantee.

ROUGHNESS -- the owner's report, measured properly this time and CLOSED
He confirmed twice that changing roughness type/frequency changed generation time from
startup. The earlier sweep was a single 128^3 export at LOD0 and could not have seen
it, so this one used the game path, fixed streaming centre, identical 850-tile workload:

  skip rate   LOD0 70.8%  LOD1 57.3%  LOD2 32.6% (Cellular 31.8%)  LOD3 2.5%  LOD4 0%
  gen p95 s   FBM .472/.369/.083/.087/.145   Ridged .473/.365/.084/.084/.143
              Mixed .474/.358/.084/.090/.149  Cellular .479/.360/.083/.089/.161
  FBM frequency 0.025-0.2: flat across every LOD

No classifier-bound collapse by type or frequency. The startup difference was
workload/streaming behaviour, not a type-specific bound defect. No unsafe bounds change
was made to chase it.

⚠️ Worth noting for later: the skip rate collapses with coarseness -- 70.8% at LOD0 to
0% at LOD4. That is expected (a LOD4 tile spans a huge volume and almost certainly
contains something) but it means the classifier does nothing for exactly the tiles that
cover the most ground.

VERIFIED BY ME: build 0 errors; export 1.418 / 1.445 / 1.458 s (4f8b4a9 was
1.353 / 1.430 / 1.432); 153,346 triangles. Peak game RSS ~2.05 GB.

Sources consulted: Epic's UE task-systems documentation for the worker/game-path
distinction, and Lorensen-Cline for the discrete isosurface context.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-11 16:29:49 +02:00
1 parent 4f8b4a9a27
commit adb34dffa9
6 files changed
+704 -28

No files matched your search

+33 -1
View File
@@ -60,6 +60,33 @@ namespace
bool GVoxelForgeStreamingBudgetReported = false;
bool GVoxelForgeGenerationCapHitReported = false;
int32 GVoxelForgeMaxPendingTilesObserved = 0;
bool GVoxelForgeTestStreamingCenterParsed = false;
bool GVoxelForgeUseTestStreamingCenter = false;
FIntVector GVoxelForgeTestStreamingCenter = FIntVector::ZeroValue;
void VF_ParseTestStreamingCenter()
{
if (GVoxelForgeTestStreamingCenterParsed) return;
GVoxelForgeTestStreamingCenterParsed = true;
int32 X = 0, Y = 0, Z = 0;
const TCHAR* CommandLine = FCommandLine::Get();
const bool bHaveX = FParse::Value(CommandLine, TEXT("voxel.StreamingTestCenterX="), X);
const bool bHaveY = FParse::Value(CommandLine, TEXT("voxel.StreamingTestCenterY="), Y);
const bool bHaveZ = FParse::Value(CommandLine, TEXT("voxel.StreamingTestCenterZ="), Z);
if (!bHaveX || !bHaveY || !bHaveZ)
{
return;
}
GVoxelForgeTestStreamingCenter = FIntVector(X, Y, Z);
GVoxelForgeUseTestStreamingCenter = true;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForge] diagnostic fixed streaming center: (%d,%d,%d)"),
GVoxelForgeTestStreamingCenter.X,
GVoxelForgeTestStreamingCenter.Y,
GVoxelForgeTestStreamingCenter.Z);
}
}
AVoxelWorld::AVoxelWorld()
@@ -1835,6 +1862,7 @@ int32 AVoxelWorld::GetMaxConcurrentTasks() const
void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition)
{
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_UpdateChunks);
VF_ParseTestStreamingCenter();
const int32 MaxTasks = GetMaxConcurrentTasks();
if (GVoxelForgeProfileTileGeneration != 0
&& !GVoxelForgeStreamingBudgetReported)
@@ -1859,7 +1887,11 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition)
GVoxelForgeStreamingBudgetReported = true;
}
const FIntVector CenterChunk = WorldToChunkCoord(WorldToLocalCm(CenterPosition)); // player's level-0 tile
FIntVector CenterChunk = WorldToChunkCoord(WorldToLocalCm(CenterPosition)); // player's level-0 tile
if (GVoxelForgeUseTestStreamingCenter)
{
CenterChunk = GVoxelForgeTestStreamingCenter;
}
CurrentCenterChunk = CenterChunk;
// Streaming anchors (AI / remote players, §9.3): prune dead ones + detect chunk crossings so the