feat(passages): §6.5 landings -- you arrive somewhere you can stand

The last unbuilt Tier 1 item. A passage was a bore through rock with no
floor inside the walkable angle, so its mouth was not a standing position
and neither was anything near it -- the departure mouth measured a FOUR-CELL
player-fit component. You arrived and there was nowhere to stand.

Both ends of all 7 passages now open into a landing, sized from the body:
  capsule diameter 2.72 vox      floor >= 12 vox (3 m) to stand and turn
  capsule height 7.04 vox        room >= 12 vox tall, stock mouth gives 18 (4.5 m)
  3-voxel floor slab, tube tangent to it -- no lip to climb out of
  connector 5-vox radius: 2.5 m clear width, 3 m clear height
  ramps over 44 deg get a deterministic level dog-leg

⭐ THE MOUTHS ARE NO LONGER SPECKS:
  archetype        components      largest      arrival-departure
  Maze             309 -> 340      176 -> 3210    43-81  -> 3210-3210
  TunnelNetwork    467 -> 227     1292 -> 3841     2-1   -> 3841-269
  Underwater        86 -> 117       35 -> 3838     3-11  -> 3838-267
  FlatPlain         45 ->  11      259 -> 2130     1-1   ->  268-273
  CrystalChamber   197 ->  90       24 ->  286     1-1   ->  286-274
  FloatingIslands    2 ->   1        3 ->   72     1-1   ->   72-72

Law 1/8 -> 2/7 connected (1 refused on MaxCells), and critically ZERO
connections now run through a four-cell pocket.

Every landing floor was verified against the FINAL density, after all posts:
14/14 pass the 44 deg slope bracket, worst room gradient .059, worst
connector .522. 14/14 deterministic root joins. 0 seal failures.
0 box-verdict violations across 82 proved + 174 mixed, 109,142 voxels.
Op-stack bit-identical, order-independence green over 10 slots / 31
passages / 3 pool orders. Cache 0.665 us rebuild, 0.646 us hot.

Post order: origin spine -> vertical seal -> passage tube + landing floor
-> XY edge seal. Landings clamp 4 voxels inside the vertical seal; the XY
seal stays last and wins at world edges.

⚠️ ARCHITECTURAL NOTE, not a detail: landings join a deterministic annular
ROOT HUB at (0,0), built identically for every endpoint -- no nearest-
component search, no passage ordering. That is what makes the join
guaranteed rather than likely, and it extends the (0,0) spine concept from
a landmark into a structural network. It also means routes tend to pass
through the centre, which is a gameplay consequence worth Jahni's eye.

⚠️ AND IT DOES NOT CLOSE THE LAW. Codex is explicit: the landings guarantee
membership in the new root network, NOT in the archetype's pre-existing fit
component. The whole-strate Maze check still shows a 71.9-voxel gap between
1,235 and 2,938-cell components. So the native corridor networks remain
disconnected from the root network in places. Real, separate, next.

52 build-and-measure cycles, ~9 hours, finding real geometry bugs on the
way (a landing floor overlapping the ramp cells and hiding a floor
discontinuity; the disturbance pass refilling connector headroom).

Build clean, 32/32 success, 0 failed. Showcase regenerated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8V9my1PL68pmQb2gutZZV
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-06 23:55:47 +02:00
1 parent e4e159f0cd
commit 413c15de24
14 files changed
+1943 -187

No files matched your search

+107 -21
View File
@@ -289,12 +289,6 @@ static FORCEINLINE void ApplyBoundarySeal(float& Density, float WorldZ,
VF_ApplyBoundarySeal(Density, WorldZ, StrateTopZ, StrateBottomZ, Thickness, BaseDensity);
}
static FORCEINLINE void ApplyPassageCarving(float& Density, float ModSDF,
float BaseDensity, float SealThickness)
{
VF_ApplyPassageCarving(Density, ModSDF, BaseDensity, SealThickness);
}
static FORCEINLINE void ApplyOriginSpine(float& Density, float WorldX, float WorldY, float WorldZ,
float StrateTopZ, float StrateBottomZ, float SealThickness, float BaseDensity, float Radius)
{
@@ -681,11 +675,12 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
{
// SOLID BEDROCK gap between two strates. The auto-carved passages still tunnel
// through it, but the (0,0) descent stays solid here so the player digs the gap
// to reach the next layer. No caves, no spine, no vertical seal — just rock + passages;
// to reach the next layer. No caves, no spine, no vertical seal — just rock + passage
// tube/landing;
// the global XY edge seal is applied below for bounded worlds.
float Density = 8.0f; // bedrock solidity (positive = solid)
const float ModSDF = StrateManager->EvaluateModifierSDF(WorldX, WorldY, WorldZ);
ApplyPassageCarving(Density, ModSDF, 8.0f, 0.0f);
StrateManager->ApplyPassageModifier(
Density, WorldX, WorldY, WorldZ, 8.0f, 0.0f);
VF_ApplyXYEdgeSeal(Density, WorldX, WorldY, WorldRadiusVoxels, EdgeSealThickness, 8.0f);
Result = -Density;
}
@@ -996,6 +991,81 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
// Disturbance layer (the "wow" post-process) — cached params, MC convention.
ApplyDisturbances(Result, WorldX, WorldY, WorldZ, CP_Dist, (uint32)Seed);
// A disturbance is allowed to add visual detail, but it must not refill the landing's
// measured air volume or turn its support slab back into a hole. Reassert the landing
// air first, then the structural floor, in MC space before the final XY seal. The two
// calls use the same landing geometry already evaluated by the legacy and op-stack post.
float LandingBaseDensity = CP_Dist.BaseDensity;
#if WITH_EDITOR
// A composer parent can deliberately use a different structural base than the authored
// definition. The landing floor was already applied by PassageCarveOp with that parent
// base; use the same value for this post-disturbance MC backstop or live-vs-direct stack
// evaluation would differ exactly on floor voxels.
if (CP_UseComposerRegions && CP_ComposerRegions.bHasGlobalStructuralParams)
{
LandingBaseDensity = CP_ComposerRegions.BaseDensity;
}
else
#endif
if (CP_UseCustomRecipe)
{
switch (CP_CustomRecipe.StructuralParamBlock)
{
case EVoxelStrateParamBlock::TunnelNetwork:
LandingBaseDensity = CP_CustomParams.TunnelNetworkParams.BaseDensity;
break;
case EVoxelStrateParamBlock::Slab:
LandingBaseDensity = CP_CustomParams.SlabParams.BaseDensity;
break;
case EVoxelStrateParamBlock::Maze:
LandingBaseDensity = CP_CustomParams.MazeParams.BaseDensity;
break;
case EVoxelStrateParamBlock::Surface:
LandingBaseDensity = CP_CustomParams.SurfaceParams.BaseDensity;
break;
case EVoxelStrateParamBlock::VerticalShaft:
LandingBaseDensity = CP_CustomParams.VerticalShaftParams.BaseDensity;
break;
case EVoxelStrateParamBlock::FloatingIsland:
LandingBaseDensity = CP_CustomParams.FloatingIslandParams.BaseDensity;
break;
default:
break;
}
}
else
{
switch (CP_GenType)
{
case ECaveGeneratorType::FlatPlain:
case ECaveGeneratorType::CrystalChamber:
LandingBaseDensity = CP_Slab.BaseDensity;
break;
case ECaveGeneratorType::Maze:
LandingBaseDensity = CP_Maze.BaseDensity;
break;
case ECaveGeneratorType::SurfaceWorld:
LandingBaseDensity = CP_Surface.BaseDensity;
break;
case ECaveGeneratorType::VerticalShafts:
LandingBaseDensity = CP_Vert.BaseDensity;
break;
case ECaveGeneratorType::FloatingIslands:
LandingBaseDensity = CP_Float.BaseDensity;
break;
case ECaveGeneratorType::TunnelNetwork:
case ECaveGeneratorType::Underwater:
default:
LandingBaseDensity = CP_Tunnel.BaseDensity;
break;
}
}
StrateManager->ApplyPassageLandingAirMC(
Result, WorldX, WorldY, WorldZ, LandingBaseDensity,
CP_Dist.BoundarySealThickness);
StrateManager->ApplyPassageLandingFloorMC(
Result, WorldX, WorldY, WorldZ, LandingBaseDensity);
}
else
{
@@ -2151,8 +2221,9 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
//=========================================================================
if (StrateManager)
{
const float ModSDF = StrateManager->EvaluateModifierSDF(WorldX, WorldY, WorldZ);
ApplyPassageCarving(Density, ModSDF, Params.BaseDensity, Params.BoundarySealThickness);
StrateManager->ApplyPassageModifier(
Density, WorldX, WorldY, WorldZ,
Params.BaseDensity, Params.BoundarySealThickness);
}
// Fourth structural post: the XY edge wins over a passage near the rim.
@@ -2373,8 +2444,9 @@ float UVoxelGenerator::GetSlabDensity(float WorldX, float WorldY, float WorldZ,
if (StrateManager)
{
const float ModSDF = StrateManager->EvaluateModifierSDF(WorldX, WorldY, WorldZ);
ApplyPassageCarving(Density, ModSDF, Params.BaseDensity, Params.BoundarySealThickness);
StrateManager->ApplyPassageModifier(
Density, WorldX, WorldY, WorldZ,
Params.BaseDensity, Params.BoundarySealThickness);
}
VF_ApplyXYEdgeSeal(Density, WorldX, WorldY,
@@ -2493,8 +2565,9 @@ float UVoxelGenerator::GetMazeDensity(float WorldX, float WorldY, float WorldZ,
if (StrateManager)
{
const float ModSDF = StrateManager->EvaluateModifierSDF(WorldX, WorldY, WorldZ);
ApplyPassageCarving(Density, ModSDF, Params.BaseDensity, Params.BoundarySealThickness);
StrateManager->ApplyPassageModifier(
Density, WorldX, WorldY, WorldZ,
Params.BaseDensity, Params.BoundarySealThickness);
}
VF_ApplyXYEdgeSeal(Density, WorldX, WorldY,
@@ -2756,8 +2829,9 @@ float UVoxelGenerator::SurfaceDensityFromColumn(float WorldX, float WorldY, floa
if (StrateManager)
{
const float ModSDF = StrateManager->EvaluateModifierSDF(WorldX, WorldY, WorldZ);
ApplyPassageCarving(Density, ModSDF, S.BaseDensity, S.BoundarySealThickness);
StrateManager->ApplyPassageModifier(
Density, WorldX, WorldY, WorldZ,
S.BaseDensity, S.BoundarySealThickness);
}
VF_ApplyXYEdgeSeal(Density, WorldX, WorldY,
@@ -3139,6 +3213,16 @@ EVoxelTileClass UVoxelGenerator::ClassifyTile(const FIntVector& OriginVoxels, in
{
bCanSolid = false;
}
// A landing is bidirectional geometry: its chamber kills the AllSolid hypothesis, while its
// guaranteed support slab kills AllAir. Without this second guard a tile covering only the
// floor could be classified uniformly air and the mesher would skip the solid support that
// keeps the player from falling through it. The operator-stack path returns Both for the
// same box, so the hand-written legacy fold must carry the identical hypothesis kill here.
if (StrateManager->AnyPassageLandingFloorNearBox(
FVector(MinX, MinY, MinZ), FVector(MaxX, MaxY, MaxZ)))
{
bCanAir = false;
}
if (OriginSpineRadius > 0.0f)
{
// Spine (0,0) : colonne XY rayon R + blend 3 (cf. ApplyOriginSpine). Test cercle/boîte XY.
@@ -4468,8 +4552,9 @@ float UVoxelGenerator::GetVerticalShaftDensity(float WorldX, float WorldY, float
if (StrateManager)
{
const float ModSDF = StrateManager->EvaluateModifierSDF(WorldX, WorldY, WorldZ);
ApplyPassageCarving(Density, ModSDF, Params.BaseDensity, Params.BoundarySealThickness);
StrateManager->ApplyPassageModifier(
Density, WorldX, WorldY, WorldZ,
Params.BaseDensity, Params.BoundarySealThickness);
}
VF_ApplyXYEdgeSeal(Density, WorldX, WorldY,
@@ -4628,8 +4713,9 @@ float UVoxelGenerator::GetFloatingIslandDensity(float WorldX, float WorldY, floa
if (StrateManager)
{
const float ModSDF = StrateManager->EvaluateModifierSDF(WorldX, WorldY, WorldZ);
ApplyPassageCarving(Density, ModSDF, Params.BaseDensity, Params.BoundarySealThickness);
StrateManager->ApplyPassageModifier(
Density, WorldX, WorldY, WorldZ,
Params.BaseDensity, Params.BoundarySealThickness);
}
VF_ApplyXYEdgeSeal(Density, WorldX, WorldY,