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:
1 parent
e4e159f0cd
commit
413c15de24
14 files changed
+1943
-187
No files matched your search
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user