core: Vertical Shafts on the shared Core cave planner (L139, v237; 15/16 seeds)

Shaft mode reuses PlanResolvedCavernNetworkV1 with a capped full-height cylinder primitive (shapeKind=1), ledges,
access bores and cross links; 11 VerticalShaftParams fields mapped. Basal law: arrival 2-3 to a walkable terminal
landing; drops/climbs stay reporting tiers. Seed 2 fails shell/seams at the 2-3 arrival mouth (3 variants tried,
stopped per AGENTS.md). Whole view faster than assembler/world in 8/8 cells; near-ready slower in 7/8.
Source/: VoxelForgeCoreAdapter.cpp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-11-01 05:34:00 +01:00
1 parent fa4417aad0
commit 318c5cf4f2
31 files changed
+1719 -106

No files matched your search

@@ -510,7 +510,8 @@ namespace VoxelForgeCore
// preserves open cave lanes through passage shell overlaps; 236 binds passage mouths to
// the planned graph route, preserves its walk corridor through shell overlaps, and keeps
// boundary-blended host rock from widening the authored passage wall.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 236;
// v237 adds the shared Core cave-graph shaft primitive and basal terminal route.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 237;
enum class FieldChannel : std::uint32_t
{
@@ -906,6 +907,7 @@ namespace VoxelForgeCore
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV3 = 3;
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV1 = 1;
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV2 = 2;
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV3 = 3;
inline constexpr std::uint32_t ResolvedLayoutOreVeinVocabularyVersionV1 = 1;
enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict };
@@ -967,6 +969,10 @@ namespace VoxelForgeCore
{
std::string stableId;
std::int32_t strateIndex = -1;
// 0 = floor-backed room; 1 = a true vertical cylindrical shaft. The
// shaft form is part of the shared cave graph so other recipes can reuse
// the same graph, indexing, interval and density machinery.
std::uint8_t shapeKind = 0;
std::array<std::int64_t, 3> centerMicrometres{};
std::array<std::int64_t, 3> halfExtentsMicrometres{};
std::int64_t floorMicrometres = 0;
@@ -982,6 +988,11 @@ namespace VoxelForgeCore
std::int64_t irregularityAmplitudeMicrometres = 0;
std::int64_t irregularityWavelengthMicrometres = 0;
std::uint64_t irregularitySeed = 0;
// Used only by shapeKind=1. floorMicrometres is the shaft top; the
// bottom is center.z - halfExtents.z. Ledges face one cardinal side.
std::int64_t ledgeSpacingMicrometres = 0;
std::int64_t ledgeDepthMicrometres = 0;
std::uint8_t ledgeSide = 0;
std::array<std::int64_t, 3> boundsMinimumMicrometres{};
std::array<std::int64_t, 3> boundsMaximumMicrometres{};
};
@@ -1044,6 +1055,12 @@ namespace VoxelForgeCore
std::int32_t tunnelHorizontalBiasQ16 = 32768;
std::int32_t originRoomMaximumConnections = 4;
bool tunnelsFlowTowardOrigin = true;
// Vertical Shafts uses this same planner and graph field. It binds its
// incoming passage and resolves a bedrock terminal when no departure
// mouth exists, then adds shaft records beside the walk-only backbone.
bool verticalShaftMode = false;
std::int64_t ledgeSpacingMicrometres = 8000000;
std::int64_t ledgeDepthMicrometres = 1000000;
};
// Immutable material-only deposits. The capsule and its conservative bounds are in
@@ -1150,7 +1167,8 @@ namespace VoxelForgeCore
inline bool IsNativeCaveFieldStratum(const ResolvedLayoutStratumV2& stratum) noexcept
{
return (stratum.generatorKind=="Caves"||stratum.generatorKind=="TunnelNetwork")
return (stratum.generatorKind=="Caves"||stratum.generatorKind=="TunnelNetwork"
||stratum.generatorKind=="VerticalShafts")
&&stratum.recipeId=="Core.Caves.PrimitiveField"
&&stratum.recipeVersion==1;
}
+263 -31
View File
@@ -626,8 +626,12 @@ namespace
<<",\"floor_profile_distance_um\":"<<room.floorProfileDistanceMicrometres
<<",\"floor_gradient_q16\":["<<room.floorGradientQ16[0]<<','
<<room.floorGradientQ16[1]<<']'
<<",\"smooth_width_um\":"<<room.smoothWidthMicrometres
;
<<",\"smooth_width_um\":"<<room.smoothWidthMicrometres;
if(room.shapeKind!=0)
out<<",\"shape_kind\":"<<static_cast<unsigned>(room.shapeKind)
<<",\"ledge_spacing_um\":"<<room.ledgeSpacingMicrometres
<<",\"ledge_depth_um\":"<<room.ledgeDepthMicrometres
<<",\"ledge_side\":"<<static_cast<unsigned>(room.ledgeSide);
if(room.unionWidthMicrometres>0)
out<<",\"union_width_um\":"<<room.unionWidthMicrometres;
out<<",\"irregularity_amplitude_um\":"<<room.irregularityAmplitudeMicrometres
@@ -1294,7 +1298,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
"cavern graph vocabulary version is set without graph records");
}
else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1
&&cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV2)
&&cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV2
&&cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV3)
add("cavern_graph_vocabulary_version",
"cavern graph records require a supported vocabulary version");
if(cavernGraphVocabularyVersion!=0)
@@ -1327,6 +1332,33 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
||(room.irregularityAmplitudeMicrometres>0
&&room.irregularityWavelengthMicrometres<=0))
add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters");
const auto* roomStratum=findStratum(room.strateIndex);
if(room.shapeKind>1)
add("cavern_room_shape",room.stableId+" has an unknown shape kind");
if(room.shapeKind==1)
{
const bool verticalShaftStratum=roomStratum
&&roomStratum->generatorKind=="VerticalShafts"
&&IsNativeCaveFieldStratum(*roomStratum);
if(cavernGraphVocabularyVersion<ResolvedLayoutCavernGraphVocabularyVersionV3
||!verticalShaftStratum
||room.halfExtentsMicrometres[0]!=room.halfExtentsMicrometres[1]
||room.ledgeSpacingMicrometres<0
||room.ledgeSpacingMicrometres>100000000
||room.ledgeDepthMicrometres<0
||room.ledgeDepthMicrometres>=room.halfExtentsMicrometres[0]
||room.ledgeSide>3
||room.floorMicrometres
!=room.centerMicrometres[2]+room.halfExtentsMicrometres[2]
||!room.floorProfileStableId.empty()
||room.floorGradientQ16[0]!=0||room.floorGradientQ16[1]!=0)
add("cavern_shaft_parameters",room.stableId+
" has invalid native shaft bounds, ledges, or recipe binding");
}
else if(room.ledgeSpacingMicrometres!=0||room.ledgeDepthMicrometres!=0
||room.ledgeSide!=0)
add("cavern_room_shape",room.stableId+
" carries shaft parameters on an ordinary room");
const std::int64_t gradientSquared=static_cast<std::int64_t>(room.floorGradientQ16[0])
*room.floorGradientQ16[0]+static_cast<std::int64_t>(room.floorGradientQ16[1])
*room.floorGradientQ16[1];
@@ -1415,11 +1447,37 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
if(room!=cavernRooms.end())
{
const auto& endpoint=edge.controlPointsMicrometres.back();
bool validRoomEndpoint=false;
if(room->shapeKind==1)
{
const std::int64_t shaftBottom=room->centerMicrometres[2]
-room->halfExtentsMicrometres[2];
const std::int64_t shaftTop=room->centerMicrometres[2]
+room->halfExtentsMicrometres[2];
const bool insideShaft=endpoint[2]>=shaftBottom
&&endpoint[2]<=shaftTop;
bool onLanding=room->ledgeSpacingMicrometres==0;
if(room->ledgeSpacingMicrometres>0)
{
const std::int64_t ledgeIndex=(shaftTop-endpoint[2]
+room->ledgeSpacingMicrometres/2)
/room->ledgeSpacingMicrometres;
const std::int64_t ledgeZ=shaftTop
-ledgeIndex*room->ledgeSpacingMicrometres;
const std::int64_t maximumLedge=(shaftTop-shaftBottom-1000000)
/room->ledgeSpacingMicrometres;
onLanding=ledgeIndex>=1&&ledgeIndex<=maximumLedge
&&std::abs(endpoint[2]-ledgeZ)<=500000;
}
validRoomEndpoint=insideShaft&&onLanding;
}
else validRoomEndpoint=
std::abs(endpoint[2]-room->floorMicrometres)<=500000;
if(endpoint[0]<room->centerMicrometres[0]-room->halfExtentsMicrometres[0]
||endpoint[0]>room->centerMicrometres[0]+room->halfExtentsMicrometres[0]
||endpoint[1]<room->centerMicrometres[1]-room->halfExtentsMicrometres[1]
||endpoint[1]>room->centerMicrometres[1]+room->halfExtentsMicrometres[1]
||std::abs(endpoint[2]-room->floorMicrometres)>500000)
||!validRoomEndpoint)
{
add("cavern_edge_portal",edge.stableId+" endpoint_um=("
+std::to_string(endpoint[0])+","+std::to_string(endpoint[1])+","+
@@ -1733,6 +1791,10 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
room.irregularityWavelengthMicrometres=LayoutInt(v,"irregularity_wavelength_um");
room.irregularitySeed=static_cast<std::uint64_t>(LayoutInt(v,"irregularity_seed"));
room.shapeKind=static_cast<std::uint8_t>(LayoutInt(v,"shape_kind"));
room.ledgeSpacingMicrometres=LayoutInt(v,"ledge_spacing_um");
room.ledgeDepthMicrometres=LayoutInt(v,"ledge_depth_um");
room.ledgeSide=static_cast<std::uint8_t>(LayoutInt(v,"ledge_side"));
s.cavernRooms.push_back(std::move(room));
}
for(const auto& v:root.Get("cavern_edges").AsArray())
@@ -2088,7 +2150,11 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
if(!candidate.finalize(error))return false;
layout=std::move(candidate);return true;
}
candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV2;
const bool hasVerticalShaft=std::any_of(rooms.begin(),rooms.end(),
[](const auto& room){return room.shapeKind==1;});
candidate.cavernGraphVocabularyVersion=hasVerticalShaft
?ResolvedLayoutCavernGraphVocabularyVersionV3
:ResolvedLayoutCavernGraphVocabularyVersionV2;
candidate.cavernRooms.assign(rooms.begin(),rooms.end());
candidate.cavernEdges.assign(edges.begin(),edges.end());
const auto isNativeCavesStrate=[&candidate](std::int32_t strateIndex)
@@ -2268,7 +2334,11 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
||settings.irregularityAmplitudeMicrometres<0
||settings.irregularityAmplitudeMicrometres>10000000
||(settings.irregularityAmplitudeMicrometres>0
&&settings.irregularityWavelengthMicrometres<=0))
&&settings.irregularityWavelengthMicrometres<=0)
||(settings.ledgeSpacingMicrometres<0
||settings.ledgeSpacingMicrometres>100000000)
||settings.ledgeDepthMicrometres<0
||settings.ledgeDepthMicrometres>5000000)
{error="cavern planner settings are outside the bounded room, tunnel or relief ranges";return false;}
const ResolvedLayoutPassageV1* arrivalPassage=nullptr;
@@ -2282,12 +2352,17 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
&&(!departurePassage||passage.stableId<departurePassage->stableId))
departurePassage=&passage;
}
if(!arrivalPassage||!departurePassage)
{error="cavern planner needs an incoming lower mouth and an outgoing upper mouth in the target strate";return false;}
const bool terminalShaftRoute=settings.verticalShaftMode
&&stratum->generatorKind=="VerticalShafts"&&!departurePassage;
if(!arrivalPassage||(!departurePassage&&!terminalShaftRoute)
||(settings.verticalShaftMode&&!terminalShaftRoute))
{error="cavern planner needs an incoming mouth and either an outgoing mouth or a Vertical Shafts basal terminal";return false;}
if(layout.coreVoxelSizeMicrometres==0)
{error="cavern planner requires a nonzero Core voxel size";return false;}
using FPoint=std::array<std::int64_t,3>;
const std::string routePrefix=(terminalShaftRoute?"vertical-shafts/strate-"
:"tunnel-network/strate-")+std::to_string(strateIndex)+"/";
constexpr std::int64_t MicrometresPerMetre=1000000;
constexpr std::int64_t MinimumBoreRadius=1750000;
constexpr std::int64_t MinimumBoreHeight=2200000;
@@ -2490,19 +2565,47 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
std::array<std::int64_t,2> arrivalTangentQ16{},departureTangentQ16{};
std::int64_t arrivalFloorSlopeQ16=0,departureFloorSlopeQ16=0;
if(!mouthInnerPoint(*arrivalPassage,true,routeStart,arrivalTangentQ16,
arrivalFloorSlopeQ16)
||!mouthInnerPoint(*departurePassage,false,routeEnd,departureTangentQ16,
arrivalFloorSlopeQ16))
{error="cavern passage mouth is missing a usable horizontal route tangent";return false;}
if(!terminalShaftRoute
&&!mouthInnerPoint(*departurePassage,false,routeEnd,departureTangentQ16,
departureFloorSlopeQ16))
{error="cavern passage mouth is missing a usable horizontal route tangent";return false;}
if(terminalShaftRoute)
{
departureTangentQ16=arrivalTangentQ16;
routeEnd[2]=stratum->content.bottomMicrometres+3000000;
departureFloorSlopeQ16=0;
}
const std::int64_t dx=routeEnd[0]-routeStart[0];
const std::int64_t dy=routeEnd[1]-routeStart[1];
std::int64_t dx=routeEnd[0]-routeStart[0];
std::int64_t dy=routeEnd[1]-routeStart[1];
const std::int64_t dz=routeEnd[2]-routeStart[2];
const std::int64_t horizontalBias=std::clamp<std::int64_t>(
settings.tunnelHorizontalBiasQ16,0,65536);
const std::int64_t planGradeDenominator=MaximumWalkGradeDenominator
+horizontalBias/32768;
const std::int64_t requiredLength=(std::abs(dz)*planGradeDenominator*130)/100+1000;
if(terminalShaftRoute)
{
// The basal stratum has no next mouth by design. The walk-only spine starts
// at its incoming passage and ends on a supported landing above bedrock.
// Optional open shafts are side features with separate climb/fall reporting.
// Normalize by the truncated Q16 tangent magnitude: dividing by 65536
// shortens non-cardinal vectors enough to miss the strict grade-run bound.
const std::uint64_t tangentSquared=static_cast<std::uint64_t>(
departureTangentQ16[0]*departureTangentQ16[0]
+departureTangentQ16[1]*departureTangentQ16[1]);
const std::int64_t tangentMagnitudeQ16=static_cast<std::int64_t>(
integerSqrt(tangentSquared));
if(tangentMagnitudeQ16<=0)
{error="cavern planner terminal tangent has zero horizontal length";return false;}
const std::int64_t terminalProjectionLength=requiredLength+1000;
for(std::size_t axis=0;axis<2;++axis)
routeEnd[axis]=routeStart[axis]
+departureTangentQ16[axis]*terminalProjectionLength/tangentMagnitudeQ16;
dx=routeEnd[0]-routeStart[0];dy=routeEnd[1]-routeStart[1];
}
std::int64_t pathAxisX=dx,pathAxisY=dy;
FPoint routeLeadStart=routeStart,routeLeadEnd=routeEnd;
for(std::size_t axis=0;axis<2;++axis)
@@ -2648,7 +2751,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
}
};
appendPassageClearanceSpans(*arrivalPassage,true);
appendPassageClearanceSpans(*departurePassage,false);
if(departurePassage)appendPassageClearanceSpans(*departurePassage,false);
const auto segmentDistanceMicrometres=[](const std::array<double,3>& p0,
const std::array<double,3>& p1,const std::array<double,3>& q0,
const std::array<double,3>& q1)
@@ -2804,8 +2907,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
cumulative[i]=cumulative[i-1]+distance2d(routePoints[i-1],routePoints[i]);
if(cumulative.back()!=totalRouteLength)
{error="cavern planner route length accounting failed";return false;}
const std::string backboneFloorProfileId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/backbone-floor";
const std::string backboneFloorProfileId=routePrefix+"backbone-floor";
constexpr std::int64_t ProfileOne=std::int64_t{1}<<30;
const auto roundedDivide=[](std::int64_t numerator,std::int64_t denominator)
{
@@ -3008,7 +3110,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
std::int64_t radiusMicrometres=0;
bool destination=false;
};
std::vector<FRoomNode> nodes;nodes.reserve(nodeDistances.size());
std::vector<FRoomNode> nodes;nodes.reserve(nodeDistances.size()+1);
const std::int64_t floorCutMinimum=clampQ16(settings.roomFloorCutMinimumQ16);
const std::int64_t floorCutMaximum=std::max(floorCutMinimum,
clampQ16(settings.roomFloorCutMaximumQ16));
@@ -3025,15 +3127,23 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
node.floorProfileStableId=backboneFloorProfileId;
node.floorProfileDistanceMicrometres=distance;
node.floorGradientQ16=routeFloorGradientAt(distance);
node.destination=distance==mainRouteMidpoint;
node.id="tunnel-network/strate-"+std::to_string(strateIndex)+"/room-"
+std::to_string(nodeIndex);
node.destination=!terminalShaftRoute&&distance==mainRouteMidpoint;
node.id=routePrefix+"room-"+std::to_string(nodeIndex);
const std::uint64_t hash=LayoutStampSeed(seed,strateIndex,node.id);
node.radiusMicrometres=node.destination?settings.destinationRadiusMicrometres
:settings.minimumRoomRadiusMicrometres
+static_cast<std::int64_t>(hash%static_cast<std::uint64_t>(baseRoomSpan+1));
nodes.push_back(std::move(node));
}
if(terminalShaftRoute)
{
FRoomNode terminal;terminal.id=routePrefix+"basal-landing";
terminal.point=routeEnd;terminal.floorProfileStableId=backboneFloorProfileId;
terminal.floorProfileDistanceMicrometres=totalRouteLength;
terminal.floorGradientQ16=routeFloorGradientAt(totalRouteLength);
terminal.radiusMicrometres=settings.destinationRadiusMicrometres;
terminal.destination=true;nodes.push_back(std::move(terminal));
}
const auto makeCompound=[&](const FRoomNode& node,bool destination,
std::vector<ResolvedCavernRoomV1>& outRooms)
{
@@ -3154,6 +3264,130 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
}
return edge;
};
struct FPlannedShaftLanding
{
FRoomNode node;
FPoint point{};
};
std::vector<FPlannedShaftLanding> shaftLandings;
std::size_t plannedShaftCount=0;
if(terminalShaftRoute&&settings.roomDensityQ16>0)
{
const std::int64_t ledgeSpacing=std::max<std::int64_t>(0,
settings.ledgeSpacingMicrometres);
const std::int64_t ledgeDepth=std::clamp<std::int64_t>(
settings.ledgeDepthMicrometres,0,5000000);
bool emittedShaft=false;
const std::int64_t routeLengthXY=std::max<std::int64_t>(1,
static_cast<std::int64_t>(integerSqrt(static_cast<std::uint64_t>(
pathAxisX*pathAxisX+pathAxisY*pathAxisY))));
const std::int64_t sideX=-pathAxisY*65536/routeLengthXY;
const std::int64_t sideY=pathAxisX*65536/routeLengthXY;
const auto selectedRadius=[&](const std::string& stableId)
{
const std::int64_t span=settings.maximumRoomRadiusMicrometres
-settings.minimumRoomRadiusMicrometres;
return settings.minimumRoomRadiusMicrometres+(span==0?0:
static_cast<std::int64_t>(LayoutStampSeed(seed,strateIndex,stableId)
%static_cast<std::uint64_t>(span+1)));
};
for(std::size_t nodeIndex=1;nodeIndex+1<nodes.size()&&rooms.size()<2048;
++nodeIndex)
{
const auto& parent=nodes[nodeIndex];
const std::uint64_t hash=LayoutStampSeed(seed,strateIndex,
parent.id+"/shaft");
if(emittedShaft&&static_cast<std::int64_t>(hash&0xffffu)
>=settings.roomDensityQ16)continue;
const std::int64_t radius=selectedRadius(parent.id+"/shaft-radius");
const std::int64_t top=parent.point[2]
+(ledgeSpacing>0?ledgeSpacing:1000000);
const std::int64_t minimumTop=stratum->content.bottomMicrometres+4000000;
if(top>stratum->content.topMicrometres-1000000
||top<minimumTop)continue;
// Shaft length has no independent owner parameter: use as much content
// as available, with the graph's 200 m half-extent bound as a hard cap.
const std::int64_t bottom=std::max(
stratum->content.bottomMicrometres+1000000,top-200000000);
if(ledgeSpacing>0&&top-bottom<ledgeSpacing+2000000)continue;
const bool positiveSide=(hash&0x10000u)!=0;
const std::int64_t sideXSign=(sideX>=0?1:-1)*(positiveSide?1:-1);
const std::int64_t sideYSign=(sideY>=0?1:-1)*(positiveSide?1:-1);
const bool useX=std::abs(sideX)>=std::abs(sideY);
const std::uint8_t outwardSide=useX
?static_cast<std::uint8_t>(sideXSign>0?0:2)
:static_cast<std::uint8_t>(sideYSign>0?1:3);
const std::array<std::array<std::int64_t,2>,4> cardinal={{{1,0},{0,1},
{-1,0},{0,-1}}};
const auto outward=cardinal[outwardSide];
const std::int64_t offset=std::max<std::int64_t>(
parent.radiusMicrometres+radius+3000000,
settings.roomSpacingMicrometres/2);
FPlannedShaftLanding landing;landing.node.id=routePrefix+"shaft-"
+std::to_string(shaftLandings.size());
landing.node.point={parent.point[0]+outward[0]*offset,
parent.point[1]+outward[1]*offset,parent.point[2]};
landing.node.radiusMicrometres=radius;
const std::int64_t landingZ=top-(ledgeSpacing>0?ledgeSpacing:1000000);
const std::array<std::int64_t,2> toParent={-outward[0],-outward[1]};
const std::uint8_t ledgeSide=static_cast<std::uint8_t>(
(outwardSide+2)%4);
ResolvedCavernRoomV1 shaft;shaft.stableId=landing.node.id+"/primitive-0";
shaft.strateIndex=strateIndex;shaft.shapeKind=1;
shaft.centerMicrometres={landing.node.point[0],landing.node.point[1],
(top+bottom)/2};
shaft.halfExtentsMicrometres={radius,radius,(top-bottom)/2};
shaft.floorMicrometres=top;shaft.smoothWidthMicrometres=smooth;
shaft.ledgeSpacingMicrometres=ledgeSpacing;
shaft.ledgeDepthMicrometres=ledgeDepth;shaft.ledgeSide=ledgeSide;
shaft.irregularityAmplitudeMicrometres=irregularity;
shaft.irregularityWavelengthMicrometres=
settings.irregularityWavelengthMicrometres;
shaft.irregularitySeed=LayoutStampSeed(seed,strateIndex,shaft.stableId);
rooms.push_back(std::move(shaft));
landing.point={landing.node.point[0]
+toParent[0]*(radius-ledgeDepth/2),
landing.node.point[1]+toParent[1]*(radius-ledgeDepth/2),landingZ};
landing.node.point=landing.point;
const std::string accessId=routePrefix+"shaft-access-"
+std::to_string(shaftLandings.size());
auto access=makeEdge(accessId,componentRoomId(parent),parent.point,
landing.node,routeBoreRadius,-1,-1);
// The connector's own floor SDF retains its tread. It must not enter
// the descending backbone's finite floor-cap arbitration, where its
// nearby but independent floor could suppress backbone support.
access.floorSupportDepthMicrometres=0;
const std::int64_t run=distance2d(parent.point,landing.point);
const std::int64_t rise=std::abs(parent.point[2]-landing.point[2]);
if(run<=0||rise*MaximumWalkGradeDenominator>run)
{
rooms.pop_back();
continue;
}
edges.push_back(std::move(access));
shaftLandings.push_back(std::move(landing));
emittedShaft=true;
}
plannedShaftCount=shaftLandings.size();
for(std::size_t index=1;index<shaftLandings.size();++index)
{
const std::uint64_t hash=LayoutStampSeed(seed,strateIndex,
shaftLandings[index-1].node.id+"/cross-"+std::to_string(index));
if(static_cast<std::int64_t>(hash&0xffffu)>=settings.tunnelDensityQ16)
continue;
const auto& from=shaftLandings[index-1];
const auto& to=shaftLandings[index];
const std::int64_t run=distance2d(from.point,to.point);
const std::int64_t rise=std::abs(from.point[2]-to.point[2]);
if(run<=0||run>settings.maximumTunnelLengthMicrometres
||rise*MaximumWalkGradeDenominator>run)continue;
auto link=makeEdge(routePrefix+"shaft-cross-"+std::to_string(index-1),
componentRoomId(from.node),from.point,to.node,routeBoreRadius,-1,-1);
link.floorSupportDepthMicrometres=0;
edges.push_back(std::move(link));
}
}
const auto arrivalPoint=routeStart;
FPoint previousPoint=arrivalPoint;
std::int64_t previousProfileDistance=0;
@@ -3161,8 +3395,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
for(std::size_t nodeIndex=0;nodeIndex<nodes.size();++nodeIndex)
{
const auto& node=nodes[nodeIndex];
const std::string edgeId="tunnel-network/strate-"+std::to_string(strateIndex)
+"/route-edge-"+std::to_string(nodeIndex);
const std::string edgeId=routePrefix+"route-edge-"+std::to_string(nodeIndex);
auto edge=makeEdge(edgeId,previousAnchor,previousPoint,node,
selectedBoreRadius(edgeId),
previousProfileDistance,node.floorProfileDistanceMicrometres);
@@ -3171,10 +3404,9 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
previousProfileDistance=node.floorProfileDistanceMicrometres;
previousAnchor=componentRoomId(node);
}
if(!nodes.empty())
if(!nodes.empty()&&departurePassage)
{
const std::string departureEdgeId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/departure-edge";
const std::string departureEdgeId=routePrefix+"departure-edge";
auto edge=makeEdge(departureEdgeId,departurePassage->stableId+"/upper-mouth",
routeEnd,nodes.back(),selectedBoreRadius(departureEdgeId),
totalRouteLength,nodes.back().floorProfileDistanceMicrometres);
@@ -3187,7 +3419,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
const std::size_t branchLimit=settings.originRoomMaximumConnections<=0
?32u:static_cast<std::size_t>(std::clamp(settings.originRoomMaximumConnections,0,32));
std::size_t branchCount=0;
for(std::size_t nodeIndex=1;nodeIndex+1<nodes.size()
for(std::size_t nodeIndex=1;!terminalShaftRoute&&nodeIndex+1<nodes.size()
&&branchCount<branchLimit&&rooms.size()<2048;++nodeIndex)
{
const auto& parent=nodes[nodeIndex];
@@ -3210,8 +3442,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
// A side branch leaves the backbone laterally, so it has its own local flat
// floor and must not reference the parent's one-dimensional h(s) profile.
makeCompound(branch,false,rooms);
const std::string branchEdgeId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/side-edge-"+std::to_string(branchCount);
const std::string branchEdgeId=routePrefix+"side-edge-"+std::to_string(branchCount);
ResolvedCavernEdgeV1 branchEdge;
if(settings.tunnelsFlowTowardOrigin)
branchEdge=makeEdge(branchEdgeId,componentRoomId(branch),
@@ -3226,7 +3457,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
++branchCount;
}
if(nodes.empty()||rooms.empty()||edges.empty())
{error="cavern planner emitted an empty arrival-to-departure graph";return false;}
{error="cavern planner emitted an empty mouth-to-terminal graph";return false;}
double maximumGradeDegrees=0.0;
for(const auto& edge:edges)
@@ -3255,9 +3486,10 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
}
const double plannerMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-plannerStarted).count();
std::printf("[CavernPlannerV1] strate=%d seed=%d mouths=%s->%s route_nodes=%zu room_primitives=%zu edges=%zu branches=%zu route_horizontal_m=%.3f floor_delta_m=%.3f max_grade_deg=%.3f mouth_tangent_min_dot=%.3f grade_limit_deg=11.310 clearance_radius_m=%.3f clearance_height_m=%.3f destination_radius_m=%.3f plan_ms=%.3f status=PASS\n",
strateIndex,seed,arrivalPassage->stableId.c_str(),departurePassage->stableId.c_str(),
nodes.size(),rooms.size(),edges.size(),branchCount,totalRouteLength/1.0e6,
std::printf("[CavernPlannerV1] strate=%d seed=%d route=%s->%s route_nodes=%zu room_primitives=%zu shafts=%zu edges=%zu branches=%zu route_horizontal_m=%.3f floor_delta_m=%.3f max_grade_deg=%.3f mouth_tangent_min_dot=%.3f grade_limit_deg=11.310 clearance_radius_m=%.3f clearance_height_m=%.3f destination_radius_m=%.3f plan_ms=%.3f status=PASS\n",
strateIndex,seed,arrivalPassage->stableId.c_str(),
departurePassage?departurePassage->stableId.c_str():"basal-terminal",
nodes.size(),rooms.size(),plannedShaftCount,edges.size(),branchCount,totalRouteLength/1.0e6,
std::abs(dz)/1.0e6,maximumGradeDegrees,plannedMouthTangentAlignment,
routeBoreRadius/1.0e6,PlannedBoreHeight/1.0e6,
settings.destinationRadiusMicrometres/1.0e6,plannerMilliseconds);
+89 -6
View File
@@ -1284,7 +1284,8 @@ public:
const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
const auto* queryStratum=FindBoundStratumForManagerZ(Z);
const bool plannedTunnelStratum=queryStratum&&queryStratum->Resolved
&&queryStratum->Resolved->generatorKind=="TunnelNetwork"
&&(queryStratum->Resolved->generatorKind=="TunnelNetwork"
||queryStratum->Resolved->generatorKind=="VerticalShafts")
&&queryStratum->Resolved->recipeId=="Core.Caves.PrimitiveField"
&&queryStratum->Resolved->recipeVersion==1;
const auto* routePassageIndices=passageTileScopeMatches
@@ -1617,9 +1618,12 @@ public:
const auto plannedPortalWalkCorridorContains=[&]()
{
if(!queryStratum||!queryStratum->Resolved
||queryStratum->Resolved->generatorKind!="TunnelNetwork"
||(queryStratum->Resolved->generatorKind!="TunnelNetwork"
&&queryStratum->Resolved->generatorKind!="VerticalShafts")
||queryStratum->Resolved->recipeId!="Core.Caves.PrimitiveField"
||queryStratum->Resolved->recipeVersion!=1)return false;
const bool terminalShaftRoute=
queryStratum->Resolved->generatorKind=="VerticalShafts";
const VoxelForgeCore::ResolvedLayoutAnchorV2* first=nullptr;
const VoxelForgeCore::ResolvedLayoutAnchorV2* second=nullptr;
const VoxelForgeCore::ResolvedLayoutAnchorV2* third=nullptr;
@@ -1638,8 +1642,12 @@ public:
}
if(routeCount<6||!first||!second||!third||!lastThree||!lastTwo||!last
||!first->stableId.ends_with("000-arrival")
||!second->stableId.ends_with("000-edge-start")
||!lastTwo->stableId.ends_with("998-departure-inner")
||!second->stableId.ends_with("000-edge-start"))return false;
if(terminalShaftRoute)
{
if(!last->stableId.ends_with("999-basal-terminal"))return false;
}
else if(!lastTwo->stableId.ends_with("998-departure-inner")
||!last->stableId.ends_with("999-departure"))return false;
const double metresPerSubvoxel=1.0/qPerMetre;
const auto position=[&](const VoxelForgeCore::ResolvedLayoutAnchorV2& anchor)
@@ -1677,6 +1685,7 @@ public:
if(nearRouteSegment(*first,*second,PortalWalkLeadMetres,false,0.0)
||nearRouteSegment(*second,*third,PortalWalkLeadMetres,false,
arrivalInnerDistance))return true;
if(terminalShaftRoute)return false;
const auto departureInner=position(*lastTwo),departure=position(*last);
const double departureTailDistance=std::hypot(
departure[0]-departureInner[0],departure[1]-departureInner[1]);
@@ -3835,8 +3844,11 @@ private:
const VoxelForgeCore::ResolvedCavernRoomV1* room=nullptr;
const VoxelForgeCore::ResolvedCavernEdgeV1* edge=nullptr;
bool nativeGraph=false;
std::uint8_t roomShapeKind=0;
std::uint8_t ledgeSide=0;
std::array<std::int64_t,3> boundsMinimumMicrometres{},boundsMaximumMicrometres{};
std::array<double,3> centerMetres{},halfExtentsMetres{};
double ledgeSpacingMetres=0.0,ledgeDepthMetres=0.0;
struct FSegment
{
std::array<double,3> start{},end{},minimum{},maximum{};
@@ -4285,7 +4297,9 @@ private:
||(ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1
&&ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV2))return;
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV2
&&ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV3))return;
const auto isNativeGraphStrate=[this](std::int32_t strateIndex)
{
const auto found=std::find_if(ResolvedLayout->strates.begin(),
@@ -4302,6 +4316,10 @@ private:
{
FCavernRuntimeFeature runtime;runtime.room=&room;
runtime.nativeGraph=isNativeGraphStrate(room.strateIndex);
runtime.roomShapeKind=room.shapeKind;
runtime.ledgeSide=room.ledgeSide;
runtime.ledgeSpacingMetres=room.ledgeSpacingMicrometres/1000000.0;
runtime.ledgeDepthMetres=room.ledgeDepthMicrometres/1000000.0;
runtime.boundsMinimumMicrometres=room.boundsMinimumMicrometres;
runtime.boundsMaximumMicrometres=room.boundsMaximumMicrometres;
for(std::size_t axis=0;axis<3;++axis)
@@ -4956,7 +4974,8 @@ private:
ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.lowerStrateIndex;});
if(lowerStratum!=ResolvedLayout->strates.end()
&&lowerStratum->generatorKind=="TunnelNetwork"
&&(lowerStratum->generatorKind=="TunnelNetwork"
||lowerStratum->generatorKind=="VerticalShafts")
&&routeEnd<controls.size())
{
double routeHeight=0.0,distanceSquared=0.0;
@@ -5667,6 +5686,11 @@ public:
FMetricInterval featureVoid;
if(feature.room)
{
// Shaft wall and ledge SDFs are evaluated exactly per sample.
// Until a conservative interval bound for their carved shelves is
// available, keep intersecting boxes Mixed instead of proving a
// potentially false uniform sign.
if(feature.roomShapeKind==1)return {0.0,0.0,false};
double normalizedMinimumSquared=0.0,normalizedMaximumSquared=0.0;
double meanRadius=0.0;
for(std::size_t axis=0;axis<3;++axis)
@@ -7884,6 +7908,64 @@ private:
double edgeFloor=0.0;
if(feature.room)
{
if(feature.roomShapeKind==1)
{
const double radius=feature.halfExtentsMetres[0];
const double bottom=feature.centerMetres[2]-feature.halfExtentsMetres[2];
const double top=feature.centerMetres[2]+feature.halfExtentsMetres[2];
const double radial=std::hypot(point[0]-feature.centerMetres[0],
point[1]-feature.centerMetres[1]);
const double radialQ=radial-radius;
const double verticalQ=std::max(bottom-point[2],point[2]-top);
double shaftDistance=std::min(std::max(radialQ,verticalQ),0.0)
+std::hypot(std::max(radialQ,0.0),std::max(verticalQ,0.0));
if(feature.irregularityAmplitudeVoxels>0.0f)
shaftDistance+=StampValueNoise(
X-feature.centerVoxels[0],Y-feature.centerVoxels[1],
Z-feature.centerVoxels[2],feature.irregularityWavelengthVoxels,
feature.irregularitySeed)
*feature.irregularityAmplitudeVoxels*metresPerVoxel;
// A cardinal ledge faces the walkable backbone. Shelves are
// cut out of the otherwise open cylinder, leaving most of the
// shaft cross-section clear at every interval.
if(feature.ledgeSpacingMetres>0.0&&feature.ledgeDepthMetres>0.0)
{
const long long nearest=static_cast<long long>(std::llround(
(top-point[2])/feature.ledgeSpacingMetres));
const long long maximum=static_cast<long long>(std::floor(
(top-bottom-1000000.0)/feature.ledgeSpacingMetres));
if(nearest>=1&&nearest<=maximum)
{
const double ledgeZ=top-nearest*feature.ledgeSpacingMetres;
constexpr double LedgeThicknessMetres=0.6;
constexpr double LedgeFilletMetres=0.1;
const double dx=point[0]-feature.centerMetres[0];
const double dy=point[1]-feature.centerMetres[1];
const std::array<std::array<double,2>,4> sides={{{1.0,0.0},
{0.0,1.0},{-1.0,0.0},{0.0,-1.0}}};
const auto side=sides[feature.ledgeSide%4];
const double radialLocal=dx*side[0]+dy*side[1];
const double tangentLocal=-dx*side[1]+dy*side[0];
const double halfDepth=feature.ledgeDepthMetres*0.5;
const double halfWidth=radius*0.45;
const double halfThickness=LedgeThicknessMetres*0.5;
const double qx=std::abs(radialLocal-(radius-halfDepth))-halfDepth;
const double qy=std::abs(tangentLocal)-halfWidth;
const double qz=std::abs(point[2]-(ledgeZ-halfThickness))
-halfThickness;
const double outside=std::hypot(std::max(qx,0.0),
std::max(qy,0.0),std::max(qz,0.0));
const double platformDistance=outside
+std::min(std::max(qx,std::max(qy,qz)),0.0)
-LedgeFilletMetres;
shaftDistance=std::max(shaftDistance,-platformDistance);
}
}
featureVoid=shaftDistance;
}
else
{
double normalizedSquared=0.0,meanRadius=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
@@ -7907,6 +7989,7 @@ private:
+feature.floorGradient[1]*(point[1]-feature.centerMetres[1]);
const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
}
}
else if(feature.edge&&!feature.segments.empty())
{
@@ -0,0 +1,92 @@
# Linux139 — Vertical Shafts on the Core planner
## Result
S3 now reports `Core.Caves.PrimitiveField` / `Caves-native`, using the shared Tunnel Network
planner. The composed acceptance gate is not green: **15/16 seeds pass; seed 2 fails the passage
shell and tile seam checks at the arrival from passage 2–3.** This worktree is not promoted as an
accepted Vertical Shafts generator.
The basal primordial route law means arrival passage 2–3 to a walkable terminal landing above the
bedrock seal. There is no departure mouth at the bottom of the owner world. The shared planner
builds that walk-only spine, its access tunnels, and landings within the existing grade contract.
The full-height open shafts and their ledges are an exploration tier: a drop or climb is not
walk-only traversal, and climb or mine access stays in the reporting tiers. `Core.Lattice` was not
used because L58's VerticalShafts lattice route looked like a corkscrew, not an open shaft.
## Plan and parameter mapping
`PlanResolvedCavernNetworkV1` is reused without a planner fork. `Core.Caves.PrimitiveField` is the
shared evaluator, extended with `shapeKind=1`: the existing room/tunnel shapes do not represent a
capped, full-height vertical bore, and the L58 lattice route was a corkscrew. Shaft records use
that cylinder primitive with ledges, short access bores, and cross-shaft links. Generic cave
branches are disabled in shaft mode.
| `FVerticalShaftParams` field(s) | Core setting |
|---|---|
| `ShaftSpacing` | Shared planner room spacing; owner voxels convert to integer micrometres. |
| `ShaftDensity` | `roomDensityQ16`, controlling shaft placement; a nonzero setting guarantees the route's initial shaft. |
| `ShaftMinRadius`, `ShaftMaxRadius` | Minimum and maximum shaft radii. |
| `CrossConnectChance` | `tunnelDensityQ16` for links between shafts. |
| `ConnectorRadius` | Equal minimum/maximum radius for access and connector bores. |
| `LedgeSpacing`, `LedgeDepth` | Vertical shelf interval and intrusion; zero spacing disables shelves and zero depth makes them nonintrusive. |
| `SurfaceRoughness` | Roughness amplitude; frequency derives as `max(0.5 m, 4 owner voxels)` because no frequency field exists. |
| `BoundarySealThickness`, `BaseDensity` | Existing resolved top/basal boundary bands via `sealAndDensity`, outside planner settings. |
## Measurements
The final source stamp was `b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1`.
The composed sweep covered seeds 0–15 with one run per seed (reported process time 320.485 s in
total; individual runs 10.296–34.015 s). Seeds 0, 1, and 3–15 passed passages, route, seams, shell,
and blocked-route controls. Seed 2's route grades/support, naturalness, mouth profile, ripple,
dispatch, and negative controls passed; its composed passage, seam, and shell summaries failed.
The shell audit reported eight `wall_missing` samples on `passage/2-3/0`, at route stations
489.627–490.321 m. The final log records positive native density at those samples and negative
wall-shell margins. See `Saved/Linux/l139/seeds/seed2-final.log` and
`Saved/Linux/l139/seeds/summary.json`.
Three geometry variants were tried against this same shell gate: starting passage clearance at
12 m, adding a guarded cubic lateral bow, and starting clearance at 8 m. The first and third still
failed wall checks; the bow failed floor checks. Per the three-failure stop rule, these were
discarded. The final binary retains the successful Q16 endpoint-normalization correction needed
for seed 1 and no seed-2 geometry patch.
L80 ran after the seed sweep. The host gate was **load 1.31, CPU idle 99.4%**. It used one warm-up
per build and stratum/worker cell, then five interleaved measured runs for each of three builds at
W8 and W12 across all four strata: 120 measured runs total. Comparisons use `fa4417a`,
`assembler/world`, and the L139 binary; the established L137 A/A noise floor is ±1% ([A/A report](../Linux137/REPORT.md)).
Times are medians in seconds; deltas are L139 relative to each comparison build. Paired values in
each cell are W8 / W12.
| Stratum | LOD0–2 L139 | Δ vs `fa4417a` | Δ vs `assembler/world` | Whole-view L139 | Δ vs `fa4417a` | Δ vs `assembler/world` |
|---|---:|---:|---:|---:|---:|---:|
| S0 Surface World | 0.838 / 0.886 | +1.71% / +3.97% | +2.92% / +5.06% | 1.862 / 1.804 | +2.42% / +1.63% | −0.11% / −0.33% |
| S1 Floating Islands | 1.036 / 1.038 | +4.72% / +4.61% | +2.09% / +5.35% | 1.842 / 1.830 | +0.11% / +1.16% | −6.73% / −1.24% |
| S2 Tunnel Network | 1.105 / 1.078 | +2.44% / +3.07% | +3.57% / +7.57% | 1.876 / 1.808 | −1.11% / +0.00% | −4.33% / −1.15% |
| S3 Vertical Shafts | 0.899 / 0.884 | −3.81% / −0.94% | −0.95% / +2.00% | 1.706 / 1.657 | −6.57% / −2.53% | −6.21% / −4.77% |
Whole-view medians are numerically lower than `assembler/world` in all eight cells; the S0 W8 and
W12 differences are within the ±1% noise floor. Seven of eight LOD0–2 cells are slower than
`assembler/world`; the remaining cell is within the noise floor. This is characterization of an
unaccepted build, not a promotion claim. Raw runs and medians are in
`Saved/Linux/l139/timing/summary.json`.
## Checks and captures
- Fast tier: **PASS**, 284.0 s; the S3 dispatch line is `VerticalShafts` → `Core.Caves.PrimitiveField` v1 / `Caves-native` / `FCavesFieldAdapter`.
- Clang and GCC determinism: **PASS**, W1/W8/W12 (six cases), 13 hash lines and resolved-layout identity matched. Both compilers built the final source stamp above.
- Canonical v237 baselines added for Surface World and Floating Islands; both match their v236 values.
- Five eye-height views were captured before and after: arrival mouth, shaft access landing, looking down, looking up from a ledge, and basal terminal landing. All five L139 views pass the existing quality threshold. Two L138 baseline views fail it: the old layer has no shaft at the selected site, so the down-shaft view is flat, and the upward view is low contrast. The threshold was not relaxed. Images and camera details are in `captures/`.
- The outside views are `captures/after-whole-layer-density-cutaway.jpg` (coarse LOD density-mesh slice at y=−5 m) and `captures/after-whole-layer-side-elevation.svg` (resolved-plan projection that labels the walk route and all 53 shaft primitives). The cutaway passes the unchanged image-quality threshold; its plan companion makes the full shaft layout clearer.
- Linux `VFHostEditor` Development build: **PASS**, 27 actions, 72.58 s, run after timing with `-NoUBA -MaxParallelActions=4` and no `prlimit`. It emitted three pre-existing deprecated RealtimeMesh API warnings from `VoxelWorld.cpp`; that file was not changed.
- `git diff --check` and Python bytecode compilation of the L139 runners passed. Windows build and Windows determinism were not run.
Logs and source-stamped binaries are under `Saved/Linux/l139/`. The only changed Unreal `Source/`
file is `Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp`.
## Remaining risk
The passage shell and seam break at the incoming mouth on seed 2 remain unresolved. The local
clearance/heading premise failed three geometry attempts; a new route-to-mouth design premise is
needed before another geometry change. Walk-only connectivity stays guaranteed by construction;
vertical drops, climbs, and mine access remain reporting tiers.
@@ -0,0 +1,278 @@
#!/usr/bin/env python3
"""Capture comparable player-height views of the L138 and L139 owner shaft layer."""
from __future__ import annotations
import json
import math
import os
from pathlib import Path
import re
import subprocess
import sys
import time
ROOT = Path(__file__).resolve().parents[4]
DATA = ROOT / "Standalone/data/owner_world.json"
LAYOUT = ROOT / "Saved/Linux/l139/seeds/seed0.json"
OUT = ROOT / "Saved/Linux/l139/captures"
ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux139/captures"
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
VERSIONS = {
"before-l138": {
"headless": ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-clang",
"renderer": ROOT / "Saved/Linux/l138/bin/VoxelForgeOffscreenCapture-l138-clang",
"stamp": "9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f",
},
"after-l139": {
"headless": ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
"renderer": ROOT / "Saved/Linux/l139/bin/VoxelForgeOffscreenCapture-l139-clang",
"stamp": "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1",
},
}
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
def fmt(values: tuple[float, ...]) -> str:
return ",".join(f"{value:.6f}" for value in values)
def angles(camera: tuple[float, float, float],
target: tuple[float, float, float]) -> tuple[float, float]:
dx, dy, dz = (target[i] - camera[i] for i in range(3))
return math.degrees(math.atan2(dx, dy)), math.degrees(
math.atan2(dz, math.hypot(dx, dy)))
def stamped_run(name: str, command: list[str], log: Path, expected: str,
env: dict[str, str]) -> str:
binary = Path(command[0])
probe = subprocess.run([*LIMIT, "timeout", "15s", str(binary), "--print-source-stamp"],
cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, check=False, timeout=20)
match = STAMP_RE.search(probe.stdout or "")
if probe.returncode != 0 or not match or match.group(1) != expected:
raise RuntimeError(f"{name}: executable stamp mismatch for {binary}")
log.parent.mkdir(parents=True, exist_ok=True)
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
timeout=1745, check=False)
output = log.read_text(encoding="utf-8", errors="replace")
emitted = STAMP_RE.search(output)
if (result.returncode != 0 or not emitted or emitted.group(1) != expected):
raise RuntimeError(f"{name}: process failed or source changed; see {log}")
print(f"[L139CaptureRun] step={name} elapsed_s={time.monotonic()-started:.1f} "
f"log={log.relative_to(ROOT)}", flush=True)
return output
def point_um(values: list[int]) -> tuple[float, float, float]:
return tuple(value / 1_000_000.0 for value in values)
def make_views(layout: dict) -> list[dict]:
edges = {row["stable_id"]: row for row in layout["cavern_edges"]}
rooms = {row["stable_id"]: row for row in layout["cavern_rooms"]}
shaft = rooms["vertical-shafts/strate-3/shaft-0/primitive-0"]
access = edges["vertical-shafts/strate-3/shaft-access-0"]["polyline_um"]
arrival = edges["vertical-shafts/strate-3/route-edge-0"]["polyline_um"]
basal = rooms["vertical-shafts/strate-3/basal-landing/primitive-0"]
center = point_um(shaft["center_um"])
half = point_um(shaft["half_extents_um"])
shaft_top = center[2] + half[2]
shaft_bottom = center[2] - half[2]
route_a, route_b = point_um(arrival[0]), point_um(arrival[1])
tx, ty = route_b[0] - route_a[0], route_b[1] - route_a[1]
tlen = max(1.0e-9, math.hypot(tx, ty))
tangent = (tx / tlen, ty / tlen)
mouth_camera = (route_a[0] - tangent[0] * 2.0,
route_a[1] - tangent[1] * 2.0, route_a[2] + 1.45)
mouth_target = (route_a[0] + tangent[0] * 8.0,
route_a[1] + tangent[1] * 8.0, route_a[2] + 1.45)
access_a, access_b = point_um(access[0]), point_um(access[-1])
access_camera = (access_a[0], access_a[1], access_a[2] + 1.45)
access_target = (access_b[0], access_b[1], access_b[2] + 1.45)
shaft_lip_camera = (access_b[0], access_b[1], access_b[2] + 1.45)
basal_center = point_um(basal["center_um"])
basal_camera = (basal_center[0], basal_center[1], basal["floor_um"] / 1.0e6 + 1.45)
basal_target = (basal_center[0] + tangent[0] * 8.0,
basal_center[1] + tangent[1] * 8.0, basal_camera[2])
ledge_z = shaft_top - 12 * (shaft["ledge_spacing_um"] / 1.0e6)
return [
{"name": "arrival-mouth", "camera": mouth_camera, "target": mouth_target,
"focus": (route_a[0], route_a[1], route_a[2] + 1.4, 12.0),
"note": "player eye at the passage arrival, looking into the connected layer"},
{"name": "landing-at-shaft-access", "camera": access_camera,
"target": access_target,
"focus": (center[0], center[1], access_a[2], 14.0),
"note": "walkable route landing and short shaft access tunnel"},
{"name": "looking-down-shaft", "camera": shaft_lip_camera,
"target": (center[0], center[1], shaft_top - 30.0),
"focus": (center[0], center[1], shaft_top - 25.0, 14.0),
"fov": 35.0,
"note": "player eye at the upper access landing, looking down the open shaft"},
{"name": "looking-up-from-ledge", "camera": (center[0], center[1], ledge_z + 1.45),
"target": (center[0], center[1], shaft_top - 2.0),
"focus": (center[0], center[1], shaft_top - 22.0, 20.0),
"note": "player eye at an internal ledge, looking up; reaching this ledge is a climb tier"},
{"name": "basal-terminal-landing", "camera": basal_camera,
"target": basal_target,
"focus": (basal_center[0], basal_center[1], basal_center[2], 16.0),
"note": "walkable terminal landing above the basal seal"},
]
def write_overview(layout: dict, destination: Path) -> None:
shafts = [room for room in layout["cavern_rooms"] if room.get("shape_kind") == 1]
route_edges = sorted((edge for edge in layout["cavern_edges"]
if edge["stable_id"].startswith("vertical-shafts/strate-3/route-edge-")),
key=lambda edge: int(edge["stable_id"].rsplit("-", 1)[1]))
if not shafts or not route_edges:
raise ValueError("overview needs the resolved shaft primitives and walk route")
points = [point_um(row) for edge in route_edges for row in edge["polyline_um"]]
shaft_x_bounds = [(point_um(room["center_um"])[0]
-room["half_extents_um"][0]/1e6,
point_um(room["center_um"])[0]+room["half_extents_um"][0]/1e6)
for room in shafts]
x_min = min(min(bounds[0] for bounds in shaft_x_bounds),
min(row[0] for row in points))
x_max = max(x + room["half_extents_um"][0] / 1e6
for room in shafts for x in [point_um(room["center_um"])[0]])
x_max = max(x_max, max(row[0] for row in points))
z_min = min(point_um(room["center_um"])[2] - room["half_extents_um"][2] / 1e6
for room in shafts)
z_max = max(point_um(room["center_um"])[2] + room["half_extents_um"][2] / 1e6
for room in shafts)
z_min = min(z_min, min(row[2] for row in points))
z_max = max(z_max, max(row[2] for row in points))
width, height, margin = 1800.0, 760.0, 72.0
sx = (width - 2 * margin) / max(1.0, x_max - x_min)
sz = (height - 2 * margin) / max(1.0, z_max - z_min)
scale = min(sx, sz)
x_offset = margin + ((width - 2 * margin) - (x_max - x_min) * scale) * 0.5
z_offset = margin + ((height - 2 * margin) - (z_max - z_min) * scale) * 0.5
def xy(x: float, z: float) -> tuple[float, float]:
return x_offset + (x - x_min) * scale, height - (z_offset + (z - z_min) * scale)
route_xy = [xy(p[0], p[2]) for p in points]
route = " ".join(("M" if i == 0 else "L") + f"{x:.2f},{y:.2f}"
for i, (x, y) in enumerate(route_xy))
cylinders = []
for room in shafts:
x, _y, z = point_um(room["center_um"])
radius = room["half_extents_um"][0] / 1e6
half_z = room["half_extents_um"][2] / 1e6
left, top = xy(x - radius, z + half_z)
right, bottom = xy(x + radius, z - half_z)
cylinders.append(f'<rect x="{left:.2f}" y="{top:.2f}" width="{max(1.2,right-left):.2f}" '
f'height="{bottom-top:.2f}" rx="{min(7.0,(right-left)/2):.2f}" '
'fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/>')
rx0, ry0 = xy(x_min, 0.0)
rx1, ry1 = xy(x_max, 0.0)
svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="1800" height="760" viewBox="0 0 1800 760">
<rect width="1800" height="760" fill="#07111a"/>
<text x="72" y="42" fill="#edf6fb" font-family="sans-serif" font-size="24">Vertical Shafts — whole-layer side elevation</text>
<text x="72" y="64" fill="#9bb5c5" font-family="sans-serif" font-size="13">Resolved plan • seed 0 • strate 3 • vertical axis and route shown in metres • Y projected out</text>
<line x1="{rx0:.1f}" y1="{ry0:.1f}" x2="{rx1:.1f}" y2="{ry1:.1f}" stroke="#315060" stroke-dasharray="5 8"/>
{''.join(cylinders)}
<path d="{route}" fill="none" stroke="#f4bf68" stroke-width="3.0" stroke-linecap="round" stroke-linejoin="round"/>
<text x="82" y="716" fill="#f4bf68" font-family="sans-serif" font-size="15">walk-only graded spine and landings</text>
<rect x="416" y="704" width="15" height="15" fill="#93dded" fill-opacity="0.65" stroke="#42b8d1"/>
<text x="440" y="716" fill="#b8dce5" font-family="sans-serif" font-size="15">open vertical shaft primitives (53)</text>
<text x="1718" y="716" fill="#9bb5c5" text-anchor="end" font-family="sans-serif" font-size="12">Plan projection, not a density mesh</text>
</svg>'''
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_text(svg, encoding="utf-8")
def main() -> int:
if not DATA.is_file() or not LAYOUT.is_file():
raise SystemExit("missing owner fixture or passing seed-0 resolved layout")
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
views = make_views(layout)
OUT.mkdir(parents=True, exist_ok=True)
ARTIFACTS.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env["LP_NUM_THREADS"] = "12"
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
quality = ROOT / "Standalone/Tools/verify/capture_quality.py"
manifest = []
quality_failures = []
for version, config in VERSIONS.items():
phase = "before" if version.startswith("before") else "after"
for view in views:
name = view["name"]
camera, target = view["camera"], view["target"]
yaw, pitch = angles(camera, target)
mesh_dir = OUT / "meshes" / phase
mesh_dir.mkdir(parents=True, exist_ok=True)
mesh = mesh_dir / f"{name}.vfmesh"
capture_layout = mesh_dir / f"{name}-layout.json"
export_log = OUT / f"{phase}-{name}-export.log"
export = [config["headless"], DATA, "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=12",
"--clip-radius=6", "--max-clip-level=0", "--max-level=0",
"--force-generate", "--seed=0", "--recipe=surface-world",
"--render-distance-chunks=0", f"--camera={fmt((*camera,yaw,pitch))}",
f"--resolved-layout-output={capture_layout}", f"--mesh-export={mesh}"]
stamped_run(f"{phase}-{name}-export", list(map(str, export)), export_log,
config["stamp"], env)
if not mesh.is_file() or mesh.stat().st_size == 0:
raise RuntimeError(f"{phase}-{name}: empty mesh export")
png = OUT / f"{phase}-{name}.png"
jpg = ARTIFACTS / f"{phase}-{name}.jpg"
fov = float(view.get("fov", 78.0))
render = [config["renderer"], mesh, png, "--size=1280x720",
f"--fov-deg={fov:.1f}", f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}",
f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable",
"--headlamp=on", "--height-tint=on", "--height-center-m=-950",
"--height-range-m=60", "--tile-edges=off",
f"--focus-region={fmt(view['focus'])}"]
stamped_run(f"{phase}-{name}-render", list(map(str, render)),
OUT / f"{phase}-{name}-render.log", config["stamp"], env)
jpeg = ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y",
"-i", png, "-frames:v", "1", "-q:v", "4", jpg]
subprocess.run([*LIMIT, "timeout", "120s", *map(str, jpeg)], cwd=ROOT,
env=env, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT,
timeout=125)
check = subprocess.run([*LIMIT, "timeout", "120s", sys.executable,
quality, jpg], cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, check=False, timeout=125)
quality_ok = check.returncode == 0 and "status=PASS" in check.stdout
quality_result = (check.stdout.strip().splitlines()[-1]
if check.stdout.strip() else f"quality command exit={check.returncode}")
if not quality_ok:
quality_failures.append(f"{phase}-{name}")
manifest.append({"phase": phase, "view": name, "file": str(jpg.relative_to(ROOT)),
"bytes": jpg.stat().st_size, "camera_m": camera, "target_m": target,
"yaw_deg": yaw, "pitch_deg": pitch, "fov_deg": fov,
"note": view["note"],
"quality_status": "PASS" if quality_ok else "FAIL",
"quality": quality_result})
print(f"[L139Capture] phase={phase} view={name} file={jpg.relative_to(ROOT)} "
f"bytes={jpg.stat().st_size} quality={'PASS' if quality_ok else 'FAIL'}",
flush=True)
overview = ARTIFACTS / "after-whole-layer-side-elevation.svg"
write_overview(layout, overview)
manifest.append({"phase": "after", "view": "whole-layer-side-elevation",
"file": str(overview.relative_to(ROOT)), "shafts": sum(
room.get("shape_kind") == 1 for room in layout["cavern_rooms"]),
"walk_route_edges": len([edge for edge in layout["cavern_edges"]
if edge["stable_id"].startswith("vertical-shafts/strate-3/route-edge-")]),
"note": "resolved-plan side projection; not a density-mesh capture"})
(ARTIFACTS / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n",
encoding="utf-8")
status = "PASS" if not quality_failures else "FAIL"
print(f"[L139CaptureSummary] spot_views={len(views)} comparisons={len(views)*2} "
f"whole_layer_projection=1 quality_failures={len(quality_failures)} "
f"status={status}", flush=True)
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as error:
print(f"[L139CaptureFailure] {error}", file=sys.stderr, flush=True)
raise SystemExit(1)
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""Compare composed owner hashes and resolved graph identity at W1/W8/W12."""
from __future__ import annotations
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l139/determinism"
DATA = ROOT / "Standalone/data/owner_world.json"
STAMP = "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"
BINARIES = {
"clang": ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
"gcc": ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-gcc",
}
CAMERA = "-20.062500,-5.068359,-900.353516,0,0"
HASH_RE = re.compile(
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
re.M)
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
def main() -> int:
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
raise SystemExit("missing L139 determinism binary or composed owner fixture")
OUT.mkdir(parents=True, exist_ok=True)
rows = []
reference_hashes: tuple[str, ...] | None = None
reference_layout_identity: str | None = None
for compiler, binary in BINARIES.items():
for workers in (1, 8, 12):
name = f"determinism-{compiler}-w{workers}"
log, layout = OUT / f"{name}.log", OUT / f"{name}.json"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(binary), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", f"--workers={workers}",
"--clip-radius=3", "--max-clip-level=8", "--max-level=0",
f"--camera={CAMERA}", "--stream-hash", "--force-generate", "--seed=0",
"--recipe=surface-world", "--render-distance-chunks=0",
"--density-dispatch-report", f"--resolved-layout-output={layout}"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
proc = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
stamp = STAMP_RE.search(content)
hashes = tuple(HASH_RE.findall(content))
current_layout = json.loads(layout.read_text(encoding="utf-8"))
stable_identity = current_layout.get("stable_identity")
dispatch_ok = bool(re.search(
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
r"recipe=Core\.Caves\.PrimitiveField version=1 "
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", content, re.M))
passed = (proc.returncode == 0 and stamp is not None and stamp.group(1) == STAMP
and len(hashes) >= 4 and dispatch_ok and bool(stable_identity))
if reference_hashes is None and passed:
reference_hashes = hashes
reference_layout_identity = stable_identity
else:
passed = (passed and hashes == reference_hashes
and stable_identity == reference_layout_identity)
row = {"compiler": compiler, "workers": workers, "exit": proc.returncode,
"stamp": stamp.group(1) if stamp else None, "hash_lines": len(hashes),
"hashes_match_first": hashes == reference_hashes,
"layout_identity": stable_identity,
"layout_matches_first": stable_identity == reference_layout_identity,
"shaft_dispatch": dispatch_ok, "passed": bool(passed),
"elapsed_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
rows.append(row)
print("[L139DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
if not passed:
raise SystemExit(f"composed owner determinism mismatch; see {log}")
(OUT / "summary.json").write_text(json.dumps({"status": "PASS", "stamp": STAMP,
"layout_identity": reference_layout_identity, "runs": rows}, indent=2) + "\n")
print(f"[L139DeterminismSummary] runs={len(rows)} source_stamp={STAMP} status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,167 @@
#!/usr/bin/env python3
"""Run interleaved L80 timings for L139, fa4417a, and assembler/world."""
from __future__ import annotations
import json
import os
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l139/timing"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"assembler-world": (
ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
"4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"),
"fa4417a": (
ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-clang",
"9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f"),
"l139": (
ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
"b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"),
}
CAMERAS = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
PIPELINE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
NEAR = re.compile(
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
re.M)
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
binary, expected_stamp = BINARIES[version]
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
log = OUT / f"{label}.log"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(binary), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", f"--workers={workers}",
"--clip-radius=3", "--max-clip-level=8", "--max-level=8",
f"--camera={camera}", "--profile-lod-work", "--stream-scheduler-policy=3",
]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
proc = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
pipeline, near, stamp = PIPELINE.search(content), NEAR.search(content), STAMP.search(content)
if (proc.returncode or pipeline is None or near is None or stamp is None
or stamp.group(1) != expected_stamp):
raise SystemExit(f"L80 timing failed or stamp mismatch: {log}; exit={proc.returncode}")
fields = dict(FIELDS.findall(pipeline.group(1)))
row = {
"strate": strate, "workers": workers, "version": version,
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
"lod02_ready_s": float(near.group(3)),
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
"whole_tiles": int(fields["whole_tiles"].split("/")[0]),
"wall_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT)),
}
print("[L139FinalTimingRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def quiet_machine() -> tuple[float, float]:
load_1m = os.getloadavg()[0]
def totals() -> tuple[int, int]:
fields = Path("/proc/stat").read_text().splitlines()[0].split()
values = [int(value) for value in fields[1:]]
return sum(values), values[3] + values[4]
before = totals()
time.sleep(2.0)
after = totals()
idle = 100.0 * (after[1] - before[1]) / max(1, after[0] - before[0])
return load_1m, idle
def main() -> int:
if any(not path.is_file() for path in [DATA, *(entry[0] for entry in BINARIES.values())]):
raise SystemExit("missing L139 timing input")
OUT.mkdir(parents=True, exist_ok=True)
load_1m, idle = quiet_machine()
(OUT / "machine-load-before.txt").write_text(
f"1-minute load: {load_1m:.2f}\n2-second CPU idle: {idle:.1f}%\n")
print(f"[L139FinalTimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True)
if load_1m >= 1.5 or idle < 97.0:
raise SystemExit("timing refused: machine is above the L80 quiet-load gate")
rows = []
for strate in range(4):
for workers in (8, 12):
for version in BINARIES:
run(strate, workers, version, "warmup", 0)
for replicate in range(1, 6):
order = tuple(BINARIES)
order = order[replicate % 3:] + order[:replicate % 3]
if replicate % 2 == 0:
order = tuple(reversed(order))
for version in order:
rows.append(run(strate, workers, version, "measured", replicate))
(OUT / "runs-partial.json").write_text(
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
measured = [row for row in rows if row["phase"] == "measured"]
summary = []
for strate in range(4):
for workers in (8, 12):
group = [row for row in measured
if row["strate"] == strate and row["workers"] == workers]
medians = {
version: {
key: statistics.median(row[key] for row in group
if row["version"] == version)
for key in ("lod02_ready_s", "whole_view_ready_s")
}
for version in BINARIES
}
entry = {"strate": strate, "workers": workers,
"replicates_per_version": 5, "medians": medians}
for key in ("lod02_ready_s", "whole_view_ready_s"):
for baseline in ("fa4417a", "assembler-world"):
entry[f"l139_vs_{baseline}_{key}_delta_pct"] = round(
100.0 * (medians["l139"][key] / medians[baseline][key] - 1.0), 2)
entry[f"l139_vs_fa4417a_{key}_paired_deltas_pct"] = [
round(100.0 * (
next(row[key] for row in group
if row["version"] == "l139" and row["replicate"] == replicate)
/ next(row[key] for row in group
if row["version"] == "fa4417a" and row["replicate"] == replicate)
- 1.0), 3) for replicate in range(1, 6)]
entry[f"l139_vs_assembler-world_{key}_paired_deltas_pct"] = [
round(100.0 * (
next(row[key] for row in group
if row["version"] == "l139" and row["replicate"] == replicate)
/ next(row[key] for row in group
if row["version"] == "assembler-world"
and row["replicate"] == replicate) - 1.0), 3)
for replicate in range(1, 6)]
summary.append(entry)
(OUT / "summary.json").write_text(json.dumps({
"a_a_noise_floor_pct": 1.0,
"fa4417a_commit": "fa4417a",
"timing_protocol": (
"one warmup per binary, five interleaved runs, W8/W12, all four owner layers"),
"runs": rows,
"summary": summary,
}, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
"""Audit the composed owner world, including the basal Vertical Shafts route."""
from __future__ import annotations
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l139/seeds"
BIN = ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang"
DATA = ROOT / "Standalone/data/owner_world.json"
STAMP = "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
REQUIRED = {
"passages": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
"route": re.compile(
r"^\[ResolvedStrateRouteAudit\] strate=3 .*unsupported=0 grade_failures=0 .*status=PASS$",
re.M),
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
"shaft_dispatch": re.compile(
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
r"recipe=Core\.Caves\.PrimitiveField version=1 "
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M),
"shaft_blocked_route": re.compile(
r"^\[VerticalShaftsBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
}
def main() -> int:
if not BIN.is_file() or not DATA.is_file():
raise SystemExit("missing L139 Clang binary or composed owner fixture")
OUT.mkdir(parents=True, exist_ok=True)
results = []
partial = OUT / "runs-partial.json"
if partial.is_file():
for prior in json.loads(partial.read_text(encoding="utf-8")):
seed = len(results)
if prior.get("seed") != seed:
break
log = ROOT / prior.get("log", "")
layout = ROOT / prior.get("layout", "")
if not log.is_file() or not layout.is_file():
break
content = log.read_text(encoding="utf-8", errors="replace")
stamp = STAMP_RE.search(content)
checks = {name: bool(pattern.search(content))
for name, pattern in REQUIRED.items()}
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
checks["blocked_passage_controls"] = len(re.findall(
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3
checks["passage_count"] = len(re.findall(
r"^\[ResolvedPassageAudit\].*walkability=PASS$", content, re.M)) == 3
passed = prior.get("exit") == 0 and all(checks.values())
if checks != prior.get("checks") or passed != prior.get("passed"):
break
results.append(prior)
print(f"[L139SeedSweepResume] reused={len(results)} source_stamp={STAMP}",
flush=True)
for seed in range(len(results), 16):
log = OUT / f"seed{seed}.log"
layout = OUT / f"seed{seed}.json"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BIN), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
"--max-clip-level=8", "--max-level=0", "--force-generate", f"--seed={seed}",
"--recipe=surface-world", "--density-dispatch-report",
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
f"--resolved-layout-output={layout}"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
proc = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
stamp = STAMP_RE.search(content)
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
checks["blocked_passage_controls"] = len(re.findall(
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3
checks["passage_count"] = len(re.findall(
r"^\[ResolvedPassageAudit\].*walkability=PASS$", content, re.M)) == 3
passed = proc.returncode == 0 and all(checks.values())
row = {"seed": seed, "exit": proc.returncode, "passed": passed,
"checks": checks, "elapsed_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
results.append(row)
print("[L139SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
partial.write_text(json.dumps(results, indent=2) + "\n")
passed_count = sum(row["passed"] for row in results)
failed_count = len(results) - passed_count
summary = {"source_stamp": STAMP, "seeds": list(range(len(results))),
"passed": passed_count, "failed": failed_count, "results": results}
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
print(f"[L139SeedSweepSummary] passed={passed_count}/16 failed={failed_count} "
f"status={'PASS' if len(results)==16 and failed_count==0 else 'FAIL'}")
return 0 if len(results) == 16 and failed_count == 0 else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""Render a coarse whole-strate cutaway from the L139 density mesh."""
from __future__ import annotations
import json
import os
from pathlib import Path
import re
import subprocess
import sys
import time
ROOT = Path(__file__).resolve().parents[4]
DATA = ROOT / "Standalone/data/owner_world.json"
BIN = ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang"
RENDERER = ROOT / "Saved/Linux/l139/bin/VoxelForgeOffscreenCapture-l139-clang"
OUT = ROOT / "Saved/Linux/l139/captures"
ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux139/captures"
STAMP = "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
SLICE_Y = -5.0
CAMERA = (-450.0, SLICE_Y - 100.0, -950.0)
YAW, PITCH, FOV = 0.0, 0.0, 70.0
def stamped_run(label: str, command: list[str], log: Path, env: dict[str, str]) -> str:
probe = subprocess.run([*LIMIT, "timeout", "15s", str(command[0]), "--print-source-stamp"],
cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, check=False, timeout=20)
match = STAMP_RE.search(probe.stdout or "")
if probe.returncode != 0 or not match or match.group(1) != STAMP:
raise RuntimeError(f"{label}: source stamp mismatch")
log.parent.mkdir(parents=True, exist_ok=True)
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
timeout=1745, check=False)
content = log.read_text(encoding="utf-8", errors="replace")
emitted = STAMP_RE.search(content)
if result.returncode != 0 or not emitted or emitted.group(1) != STAMP:
raise RuntimeError(f"{label}: process failed or source changed; see {log}")
print(f"[L139WholeLayerCapture] step={label} elapsed_s={time.monotonic()-started:.1f} "
f"log={log.relative_to(ROOT)}", flush=True)
return content
def main() -> int:
if not DATA.is_file() or not BIN.is_file() or not RENDERER.is_file():
raise SystemExit("missing owner fixture or final L139 capture binaries")
OUT.mkdir(parents=True, exist_ok=True)
ARTIFACTS.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env["LP_NUM_THREADS"] = "12"
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
stem = "whole-layer-density-cutaway"
mesh = OUT / f"{stem}.vfmesh"
layout = OUT / f"{stem}-layout.json"
camera_text = ",".join(f"{value:.6f}" for value in (*CAMERA, YAW, PITCH))
export = [BIN, DATA, "--world-assembler-prototype", "--stream-benchmark",
"--stream-pipeline", "--workers=12", "--clip-radius=3", "--max-clip-level=8",
"--max-level=8", "--force-generate", "--seed=0", "--recipe=surface-world",
"--render-distance-chunks=0", "--capture-skip-walkability", "--density-dispatch-report",
f"--camera={camera_text}", f"--resolved-layout-output={layout}", f"--mesh-export={mesh}"]
content = stamped_run("export", export, OUT / f"{stem}-export.log", env)
dispatch = re.search(
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
r"recipe=Core\.Caves\.PrimitiveField version=1 "
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", content, re.M)
if not dispatch or not mesh.is_file() or mesh.stat().st_size == 0:
raise RuntimeError("coarse density export is empty or lacks native shaft dispatch")
png = OUT / f"{stem}.png"
jpg = ARTIFACTS / "after-whole-layer-density-cutaway.jpg"
render = [RENDERER, mesh, png, "--size=2200x320", f"--fov-deg={FOV:.1f}",
"--camera-m=" + ",".join(f"{value:.6f}" for value in CAMERA),
f"--yaw-deg={YAW:.6f}", f"--pitch-deg={PITCH:.6f}",
f"--clip-plane=0,-1,0,{SLICE_Y:.6f}", "--tile-skirts=off", "--shade=readable",
"--headlamp=on", "--height-tint=off", "--height-center-m=-950",
"--height-range-m=80", "--tile-edges=off"]
stamped_run("render", render, OUT / f"{stem}-render.log", env)
subprocess.run([*LIMIT, "timeout", "120s", "ffmpeg", "-nostdin", "-hide_banner",
"-loglevel", "error", "-y", "-i", png, "-frames:v", "1", "-q:v", "3", jpg],
cwd=ROOT, env=env, check=True, stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT, timeout=125)
quality = subprocess.run([*LIMIT, "timeout", "120s", sys.executable,
ROOT / "Standalone/Tools/verify/capture_quality.py", jpg], cwd=ROOT, env=env,
text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
check=False, timeout=125)
quality_line = quality.stdout.strip().splitlines()[-1] if quality.stdout.strip() else ""
ok = quality.returncode == 0 and "status=PASS" in quality_line
manifest_path = ARTIFACTS / "manifest.json"
manifest = json.loads(manifest_path.read_text(encoding="utf-8")) if manifest_path.is_file() else []
manifest = [row for row in manifest if row.get("view") != "whole-layer-density-cutaway"]
manifest.append({"phase": "after", "view": "whole-layer-density-cutaway",
"file": str(jpg.relative_to(ROOT)), "bytes": jpg.stat().st_size,
"camera_m": CAMERA, "target_m": (-450.0, SLICE_Y, -950.0),
"yaw_deg": YAW, "pitch_deg": PITCH, "fov_deg": FOV,
"clip_plane": [0.0, -1.0, 0.0, SLICE_Y],
"note": "outside side cutaway through the coarse LOD density mesh across the whole Vertical Shafts strate at y=-5 m",
"quality_status": "PASS" if ok else "FAIL", "quality": quality_line})
manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
print(f"[L139WholeLayerCaptureSummary] file={jpg.relative_to(ROOT)} "
f"quality={'PASS' if ok else 'FAIL'} {quality_line}", flush=True)
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1800" height="760" viewBox="0 0 1800 760">
<rect width="1800" height="760" fill="#07111a"/>
<text x="72" y="42" fill="#edf6fb" font-family="sans-serif" font-size="24">Vertical Shafts — whole-layer side elevation</text>
<text x="72" y="64" fill="#9bb5c5" font-family="sans-serif" font-size="13">Resolved plan • seed 0 • strate 3 • vertical axis and route shown in metres • Y projected out</text>
<line x1="72.0" y1="-1440.6" x2="1728.0" y2="-1440.6" stroke="#315060" stroke-dasharray="5 8"/>
<rect x="1707.84" y="284.18" width="4.65" height="191.65" rx="2.33" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1667.56" y="288.45" width="5.72" height="187.37" rx="2.86" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1245.04" y="344.01" width="6.24" height="131.81" rx="3.12" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1206.94" y="349.61" width="5.68" height="126.22" rx="2.84" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1188.40" y="352.41" width="4.37" height="123.41" rx="2.18" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1168.07" y="355.22" width="6.64" height="120.60" rx="3.32" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1130.81" y="360.85" width="4.40" height="114.97" rx="2.20" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1092.31" y="366.48" width="4.62" height="109.35" rx="2.31" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1072.90" y="369.29" width="5.04" height="106.54" rx="2.52" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1053.43" y="372.09" width="5.61" height="103.74" rx="2.80" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1034.94" y="374.88" width="4.20" height="100.94" rx="2.10" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1015.20" y="377.67" width="5.28" height="98.16" rx="2.64" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1648.28" y="290.59" width="5.89" height="185.23" rx="2.95" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="995.75" y="380.44" width="5.81" height="95.38" rx="2.90" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="976.77" y="383.20" width="5.37" height="92.62" rx="2.68" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="938.19" y="388.67" width="5.75" height="87.15" rx="2.88" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="918.75" y="391.38" width="6.25" height="84.44" rx="3.13" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="896.89" y="394.44" width="6.22" height="81.38" rx="3.11" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="880.62" y="396.74" width="5.74" height="79.09" rx="2.87" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="862.36" y="399.38" width="3.87" height="76.45" rx="1.94" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="842.60" y="401.99" width="5.01" height="73.83" rx="2.50" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="804.64" y="407.14" width="4.16" height="68.69" rx="2.08" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="785.14" y="409.66" width="4.77" height="66.16" rx="2.39" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1629.77" y="292.79" width="4.53" height="183.04" rx="2.26" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="765.24" y="412.16" width="6.18" height="63.67" rx="3.09" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="746.73" y="414.61" width="4.82" height="61.21" rx="2.41" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="727.40" y="417.04" width="5.08" height="58.79" rx="2.54" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="707.91" y="419.42" width="5.68" height="56.41" rx="2.84" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="669.16" y="424.06" width="6.41" height="51.77" rx="3.20" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="650.97" y="426.31" width="4.40" height="49.51" rx="2.20" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="593.65" y="432.79" width="3.89" height="43.04" rx="1.94" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="573.84" y="434.85" width="5.11" height="40.98" rx="2.56" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="478.04" y="444.31" width="4.78" height="31.52" rx="2.39" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="440.09" y="447.66" width="3.90" height="28.16" rx="1.95" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1590.94" y="297.31" width="5.40" height="178.52" rx="2.70" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="420.88" y="449.24" width="3.93" height="26.58" rx="1.97" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rLine truncated
<path d="M1728.00,290.01 L1716.48,291.19 L1716.48,291.19 L1710.17,291.85 L1710.17,291.85 L1689.61,294.04 L1689.61,294.04 L1670.42,296.13 L1670.42,296.13 L1651.23,298.27 L1651.23,298.27 L1632.03,300.46 L1632.03,300.46 L1612.84,302.70 L1612.84,302.70 L1593.65,304.98 L1593.65,304.98 L1574.45,307.31 L1574.45,307.31 L1555.26,309.68 L1555.26,309.68 L1536.07,312.09 L1536.07,312.09 L1516.87,314.53 L1516.87,314.53 L1497.68,317.01 L1497.68,317.01 L1478.49,319.53 L1478.49,319.53 L1459.29,322.07 L1459.29,322.07 L1440.10,324.65 L1440.10,324.65 L1420.91,327.26 L1420.91,327.26 L1401.71,329.89 L1401.71,329.89 L1382.52,332.55 L1382.52,332.55 L1363.33,335.23 L1363.33,335.23 L1344.13,337.93 L1344.13,337.93 L1324.94,340.65 L1324.94,340.65 L1305.74,343.39 L1305.74,343.39 L1286.55,346.14 L1286.55,346.14 L1267.36,348.91 L1267.36,348.91 L1248.16,351.69 L1248.16,351.69 L1228.97,354.48 L1228.97,354.48 L1209.78,357.28 L1209.78,357.28 L1190.58,360.09 L1190.58,360.09 L1171.39,362.90 L1171.39,362.90 L1152.20,365.71 L1152.20,365.71 L1133.00,368.53 L1133.00,368.53 L1113.81,371.34 L1113.81,371.34 L1094.62,374.16 L1094.62,374.16 L1075.42,376.97 L1075.42,376.97 L1056.23,379.77 L1056.23,379.77 L1037.04,382.56 L1037.04,382.56 L1017.84,385.35 L1017.84,385.35 L998.65,388.12 L998.65,388.12 L979.46,390.88 L979.46,390.88 L960.26,393.62 L960.26,393.62 L941.07,396.35 L941.07,396.35 L921.88,399.06 L921.88,399.06 L902.68,401.75 L902.68,401.75 L900.00,402.12 L900.00,402.12 L883.49,404.41 L883.49,404.41 L864.30,407.05 L864.30,407.05 L845.10,409.67 L845.10,409.67 L825.91,412.26 L825.91,412.26 L806.72,414.82 L806.72,414.82 L787.52,417.34 L787.52,417.34 L768.33,419.83 L768.33,419.83 L749.14,422.29 L749.14,422.29 L729.94,424.71 L729.94,424.71 L710.75,427.09 L710.75,427.09 L691.56,429.44 L691.56,429.44 L672.36,431.73 L672.36,431.73 L653.17,433.99 L653.17,433.99 L633.98,436.20 L633.98,436.20 L614.78,438.36 L614.78,438.36 L595.59,440.47 L595.59,440.47 L576.39,442.53 L576.39,442.53 L557.20,444.53 L557.20,444.53 L538.01,446.48 L538.01,446.48 L518.81,448.37 L518.81,448.37 L499.62,450.21 L499.62,450.21 L480.43,451.98 L480.43,451.98 L461.23,453.69 L461.23,453.69 L442.04,455.34 L442.04,455.34 L422.85,456.92 L422.85,456.92 L403.65,458.43 L403.65,458.43 L384.46,459.88 L384.46,459.88 L365.27,461.25 L365.27,461.25 L346.07,462.55 L346.07,462.55 L326.88,463.77 L326.88,463.77 L307.69,464.91 L307.69,464.91 L288.49,465.98 L288.49,465.98 L269.30,466.97 L269.30,466.97 L250.11,467.87 L250.11,467.87 L230.91,468.69 L230.91,468.69 L211.72,469.42 L211.72,469.42 L192.53,470.06 L192.53,470.06 L173.33,470.62 L173.33,470.62 L154.14,471.08 L154.14,471.08 L134.95,471.45 L134.95,471.45 L115.75,471.73 L115.75,471.73 L96.56,471.90 L96.56,471.90 L89.83,471.94 L89.83,471.94 L83.52,471.97 L83.52,471.97 L77.37,471.98 L77.37,471.98 L72.00,471.99" fill="none" stroke="#f4bf68" stroke-width="3.0" stroke-linecap="round" stroke-linejoin="round"/>
<text x="82" y="716" fill="#f4bf68" font-family="sans-serif" font-size="15">walk-only graded spine and landings</text>
<rect x="416" y="704" width="15" height="15" fill="#93dded" fill-opacity="0.65" stroke="#42b8d1"/>
<text x="440" y="716" fill="#b8dce5" font-family="sans-serif" font-size="15">open vertical shaft primitives (53)</text>
<text x="1718" y="716" fill="#9bb5c5" text-anchor="end" font-family="sans-serif" font-size="12">Plan projection, not a density mesh</text>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,258 @@
[
{
"phase": "before",
"view": "arrival-mouth",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-arrival-mouth.jpg",
"bytes": 21788,
"camera_m": [
-20.0625,
-5.068359,
-900.237499
],
"target_m": [
-30.0625,
-5.068359,
-900.237499
],
"yaw_deg": -90.0,
"pitch_deg": 0.0,
"fov_deg": 78.0,
"note": "player eye at the passage arrival, looking into the connected layer",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-arrival-mouth.jpg pixels=921600 mean_luma=110.102 stddev_luma=3.380 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "before",
"view": "landing-at-shaft-access",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-landing-at-shaft-access.jpg",
"bytes": 15114,
"camera_m": [
-31.354577,
-5.068359,
-901.1982009999999
],
"target_m": [
-31.354577,
-9.889916,
-901.1982009999999
],
"yaw_deg": 180.0,
"pitch_deg": 0.0,
"fov_deg": 78.0,
"note": "walkable route landing and short shaft access tunnel",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-landing-at-shaft-access.jpg pixels=921600 mean_luma=110.563 stddev_luma=3.056 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "before",
"view": "looking-down-shaft",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-down-shaft.jpg",
"bytes": 11005,
"camera_m": [
-31.354577,
-9.889916,
-901.1982009999999
],
"target_m": [
-31.354577,
-10.852462,
-928.6482010000001
],
"yaw_deg": 180.0,
"pitch_deg": -87.99172181188044,
"fov_deg": 35.0,
"note": "player eye at the upper access landing, looking down the open shaft",
"quality_status": "FAIL",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-down-shaft.jpg pixels=921600 mean_luma=112.000 stddev_luma=0.000 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL"
},
{
"phase": "before",
"view": "looking-up-from-ledge",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-up-from-ledge.jpg",
"bytes": 15507,
"camera_m": [
-31.354577,
-10.852462,
-945.198201
],
"target_m": [
-31.354577,
-10.852462,
-900.6482010000001
],
"yaw_deg": 0.0,
"pitch_deg": 90.0,
"fov_deg": 78.0,
"note": "player eye at an internal ledge, looking up; reaching this ledge is a climb tier",
"quality_status": "FAIL",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-up-from-ledge.jpg pixels=921600 mean_luma=111.693 stddev_luma=1.515 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL"
},
{
"phase": "before",
"view": "basal-terminal-landing",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-basal-terminal-landing.jpg",
"bytes": 13933,
"camera_m": [
-884.858259,
-5.068359,
-995.05
],
"target_m": [
-892.858259,
-5.068359,
-995.05
],
"yaw_deg": -90.0,
"pitch_deg": 0.0,
"fov_deg": 78.0,
"note": "walkable terminal landing above the basal seal",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-basal-terminal-landing.jpg pixels=921600 mean_luma=108.996 stddev_luma=6.932 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "after",
"view": "arrival-mouth",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-arrival-mouth.jpg",
"bytes": 19044,
"camera_m": [
-20.0625,
-5.068359,
-900.237499
],
"target_m": [
-30.0625,
-5.068359,
-900.237499
],
"yaw_deg": -90.0,
"pitch_deg": 0.0,
"fov_deg": 78.0,
"note": "player eye at the passage arrival, looking into the connected layer",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-arrival-mouth.jpg pixels=921600 mean_luma=125.004 stddev_luma=16.734 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "after",
"view": "landing-at-shaft-access",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-landing-at-shaft-access.jpg",
"bytes": 15595,
"camera_m": [
-31.354577,
-5.068359,
-901.1982009999999
],
"target_m": [
-31.354577,
-9.889916,
-901.1982009999999
],
"yaw_deg": 180.0,
"pitch_deg": 0.0,
"fov_deg": 78.0,
"note": "walkable route landing and short shaft access tunnel",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-landing-at-shaft-access.jpg pixels=921600 mean_luma=117.622 stddev_luma=14.149 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "after",
"view": "looking-down-shaft",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-down-shaft.jpg",
"bytes": 24396,
"camera_m": [
-31.354577,
-9.889916,
-901.1982009999999
],
"target_m": [
-31.354577,
-10.852462,
-928.6482010000001
],
"yaw_deg": 180.0,
"pitch_deg": -87.99172181188044,
"fov_deg": 35.0,
"note": "player eye at the upper access landing, looking down the open shaft",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-down-shaft.jpg pixels=921600 mean_luma=112.365 stddev_luma=3.113 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "after",
"view": "looking-up-from-ledge",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-up-from-ledge.jpg",
"bytes": 23172,
"camera_m": [
-31.354577,
-10.852462,
-945.198201
],
"target_m": [
-31.354577,
-10.852462,
-900.6482010000001
],
"yaw_deg": 0.0,
"pitch_deg": 90.0,
"fov_deg": 78.0,
"note": "player eye at an internal ledge, looking up; reaching this ledge is a climb tier",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-up-from-ledge.jpg pixels=921600 mean_luma=111.549 stddev_luma=2.538 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "after",
"view": "basal-terminal-landing",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-basal-terminal-landing.jpg",
"bytes": 12527,
"camera_m": [
-884.858259,
-5.068359,
-995.05
],
"target_m": [
-892.858259,
-5.068359,
-995.05
],
"yaw_deg": -90.0,
"pitch_deg": 0.0,
"fov_deg": 78.0,
"note": "walkable terminal landing above the basal seal",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-basal-terminal-landing.jpg pixels=921600 mean_luma=125.238 stddev_luma=3.555 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
},
{
"phase": "after",
"view": "whole-layer-side-elevation",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-whole-layer-side-elevation.svg",
"shafts": 53,
"walk_route_edges": 91,
"note": "resolved-plan side projection; not a density-mesh capture"
},
{
"phase": "after",
"view": "whole-layer-density-cutaway",
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-whole-layer-density-cutaway.jpg",
"bytes": 20280,
"camera_m": [
-450.0,
-105.0,
-950.0
],
"target_m": [
-450.0,
-5.0,
-950.0
],
"yaw_deg": 0.0,
"pitch_deg": 0.0,
"fov_deg": 70.0,
"clip_plane": [
0.0,
-1.0,
0.0,
-5.0
],
"note": "outside side cutaway through the coarse LOD density mesh across the whole Vertical Shafts strate at the denser shaft row",
"quality_status": "PASS",
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-whole-layer-density-cutaway.jpg pixels=704000 mean_luma=113.286 stddev_luma=3.364 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
}
]
+28 -9
View File
@@ -5147,6 +5147,7 @@ namespace StandaloneStreamBenchmark
}
std::uint64_t routePoints=0,routePasses=0,routeWalkPasses=0,routeBindings=0,routeBindingFailures=0;
std::uint64_t tunnelNetworkRoutes=0,tunnelNetworkBlockedControls=0;
std::uint64_t verticalShaftRoutes=0,verticalShaftBlockedControls=0;
for(const auto& stratum:ResolvedLayout->strates)
{
std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> anchors;
@@ -5206,7 +5207,8 @@ namespace StandaloneStreamBenchmark
supportMaximumMetres=routeConstraints->supportSearchDepthSubvoxels*qMetres;
}
}
if(stratum.generatorKind=="TunnelNetwork")
if(stratum.generatorKind=="TunnelNetwork"
||stratum.generatorKind=="VerticalShafts")
{
std::int64_t floorSupportDepth=0;
for(const auto& edge:ResolvedLayout->cavernEdges)
@@ -5297,9 +5299,12 @@ namespace StandaloneStreamBenchmark
stratum.strateIndex,routeAudit.firstUnsupported[i][0],
routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2],
routeAudit.firstUnsupported[i][3]);
if(stratum.generatorKind=="TunnelNetwork")
if(stratum.generatorKind=="TunnelNetwork"
||stratum.generatorKind=="VerticalShafts")
{
++tunnelNetworkRoutes;
const bool verticalShafts=stratum.generatorKind=="VerticalShafts";
if(verticalShafts)++verticalShaftRoutes;
else ++tunnelNetworkRoutes;
auto controlRoute=route;
constexpr double OffBoreControlOffsetMetres=12.0;
const std::size_t controlIndex=controlRoute.size()/2;
@@ -5319,11 +5324,22 @@ namespace StandaloneStreamBenchmark
supportStartMetres,supportStepMetres,supportMaximumMetres,
minimumSupportProbes,supportProbeRadiusMetres);
const bool detected=control.blocked>0&&!control.passed;
tunnelNetworkBlockedControls+=detected;
std::printf("[TunnelNetworkBlockedRouteControl] strate=%d mode=off_bore_lateral_deviation_m=%.1f blocked_capsule_samples=%llu result=%s expected=FAIL status=%s\n",
stratum.strateIndex,OffBoreControlOffsetMetres,
static_cast<unsigned long long>(control.blocked),
control.passed?"PASS":"FAIL",detected?"PASS":"FAIL");
if(verticalShafts)
{
verticalShaftBlockedControls+=detected;
std::printf("[VerticalShaftsBlockedRouteControl] strate=%d mode=off_bore_lateral_deviation_m=%.1f blocked_capsule_samples=%llu result=%s expected=FAIL status=%s\n",
stratum.strateIndex,OffBoreControlOffsetMetres,
static_cast<unsigned long long>(control.blocked),
control.passed?"PASS":"FAIL",detected?"PASS":"FAIL");
}
else
{
tunnelNetworkBlockedControls+=detected;
std::printf("[TunnelNetworkBlockedRouteControl] strate=%d mode=off_bore_lateral_deviation_m=%.1f blocked_capsule_samples=%llu result=%s expected=FAIL status=%s\n",
stratum.strateIndex,OffBoreControlOffsetMetres,
static_cast<unsigned long long>(control.blocked),
control.passed?"PASS":"FAIL",detected?"PASS":"FAIL");
}
}
}
for(std::size_t passageIndex=0;
@@ -5392,8 +5408,9 @@ namespace StandaloneStreamBenchmark
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
&&routeBindingFailures==0
&&tunnelNetworkRoutes==tunnelNetworkBlockedControls
&&verticalShaftRoutes==verticalShaftBlockedControls
&&blockedCompiled&&blockedControls==passageCount;
std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu natural=%llu/%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu tunnel_network_block_controls=%llu/%llu blocked_controls=%llu/%llu status=%s\n",
std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu natural=%llu/%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu tunnel_network_block_controls=%llu/%llu vertical_shaft_block_controls=%llu/%llu blocked_controls=%llu/%llu status=%s\n",
static_cast<unsigned long long>(passageCount),static_cast<unsigned long long>(passagePasses),
static_cast<unsigned long long>(naturalPasses),static_cast<unsigned long long>(passageCount),
static_cast<unsigned long long>(routePasses),static_cast<unsigned long long>(routeWalkPasses),
@@ -5401,6 +5418,8 @@ namespace StandaloneStreamBenchmark
static_cast<unsigned long long>(routeBindings),static_cast<unsigned long long>(routeBindingFailures),
static_cast<unsigned long long>(tunnelNetworkBlockedControls),
static_cast<unsigned long long>(tunnelNetworkRoutes),
static_cast<unsigned long long>(verticalShaftBlockedControls),
static_cast<unsigned long long>(verticalShaftRoutes),
static_cast<unsigned long long>(blockedControls),static_cast<unsigned long long>(passageCount),
auditPassed?"PASS":"FAIL");
if(!auditPassed)
+107 -36
View File
@@ -1517,13 +1517,16 @@ namespace StandaloneWorldAssemblyPrototype
{
using namespace VoxelForgeCore;
if(!owner.Manager)
{error="Tunnel Network cave planner requires the initialized owner director";return false;}
std::vector<const FStrateSlot*> tunnelSlots;
{error="native cave planner requires the initialized owner director";return false;}
std::vector<std::pair<const FStrateSlot*,bool>> plannerSlots;
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition
||owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ))
!=ECaveGeneratorType::TunnelNetwork)continue;
if(!slot.Definition)continue;
const ECaveGeneratorType generatorType=
owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
const bool tunnelNetwork=generatorType==ECaveGeneratorType::TunnelNetwork;
const bool verticalShafts=generatorType==ECaveGeneratorType::VerticalShafts;
if(!tunnelNetwork&&!verticalShafts)continue;
const bool hasIncomingMouth=std::any_of(layout.passages.begin(),
layout.passages.end(),[&](const auto& passage)
{return passage.lowerStrateIndex==slot.StrateIndex
@@ -1532,24 +1535,27 @@ namespace StandaloneWorldAssemblyPrototype
layout.passages.end(),[&](const auto& passage)
{return passage.upperStrateIndex==slot.StrateIndex
&&passage.lowerStrateIndex>slot.StrateIndex;});
// A native planner route requires two mouths. Boundary Tunnel Network
// strates keep their legacy field until they have both connections.
if(!hasIncomingMouth||!hasOutgoingMouth)continue;
// Tunnel Network connects two passages. The basal Vertical Shafts
// stratum is terminal: it inherits its arrival from the preceding
// strate and intentionally has no departure passage.
if((tunnelNetwork&&(!hasIncomingMouth||!hasOutgoingMouth))
||(verticalShafts&&(!hasIncomingMouth||hasOutgoingMouth)))continue;
auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[&](const auto& item){return item.strateIndex==slot.StrateIndex;});
if(stratum==layout.strates.end())
{error="Tunnel Network strate is missing from the resolved layout";return false;}
stratum->generatorKind="TunnelNetwork";
stratum->generatorKind=verticalShafts?"VerticalShafts":"TunnelNetwork";
stratum->recipeId="Core.Caves.PrimitiveField";
stratum->recipeVersion=1;
stratum->stableId=layout.worldIdentity+"/strate/"
+std::to_string(slot.StrateIndex)+"/generator/TunnelNetwork";
+std::to_string(slot.StrateIndex)+"/generator/"
+(verticalShafts?"VerticalShafts":"TunnelNetwork");
const bool alreadyPlanned=std::any_of(layout.cavernRooms.begin(),
layout.cavernRooms.end(),[&](const auto& room)
{return room.strateIndex==slot.StrateIndex;})
&&std::any_of(layout.cavernEdges.begin(),layout.cavernEdges.end(),
[&](const auto& edge){return edge.strateIndex==slot.StrateIndex;});
if(!alreadyPlanned)tunnelSlots.push_back(&slot);
if(!alreadyPlanned)plannerSlots.emplace_back(&slot,verticalShafts);
}
if(!layout.finalize(error))return false;
const auto toMicrometres=[&](double ownerVoxels)
@@ -1562,14 +1568,55 @@ namespace StandaloneWorldAssemblyPrototype
return static_cast<std::int32_t>(std::llround(std::clamp(
static_cast<double>(value),0.0,1.0)*65536.0));
};
if(!tunnelSlots.empty())
if(!plannerSlots.empty())
{
std::vector<ResolvedCavernRoomV1> plannedRooms=layout.cavernRooms;
std::vector<ResolvedCavernEdgeV1> plannedEdges=layout.cavernEdges;
for(const FStrateSlot* slot:tunnelSlots)
for(const auto& [slot,verticalShafts]:plannerSlots)
{
const FStrateGenerationParams& params=slot->Definition->GenerationParams;
ResolvedCavernPlannerSettingsV1 settings;
if(verticalShafts)
{
const FVerticalShaftParams& shaft=slot->Definition->VerticalShaftParams;
settings.verticalShaftMode=true;
settings.roomSpacingMicrometres=std::clamp<std::int64_t>(
toMicrometres(shaft.ShaftSpacing),4000000,100000000);
settings.minimumRoomRadiusMicrometres=std::max<std::int64_t>(
500000,toMicrometres(shaft.ShaftMinRadius));
settings.maximumRoomRadiusMicrometres=std::max(
settings.minimumRoomRadiusMicrometres,
toMicrometres(shaft.ShaftMaxRadius));
settings.destinationRadiusMicrometres=std::max(
settings.maximumRoomRadiusMicrometres,
settings.maximumRoomRadiusMicrometres*135/100);
settings.minimumTunnelRadiusMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.ConnectorRadius));
settings.maximumTunnelRadiusMicrometres=
settings.minimumTunnelRadiusMicrometres;
settings.maximumTunnelLengthMicrometres=std::clamp<std::int64_t>(
settings.roomSpacingMicrometres*2,4000000,100000000);
settings.smoothWidthMicrometres=1500000;
settings.floorSupportDepthMicrometres=2000000;
settings.irregularityAmplitudeMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.SurfaceRoughness));
settings.irregularityWavelengthMicrometres=std::max<std::int64_t>(
500000,static_cast<std::int64_t>(owner.OwnerVoxelSizeMicrons)*4);
settings.roomDensityQ16=toQ16(shaft.ShaftDensity);
settings.tunnelDensityQ16=toQ16(shaft.CrossConnectChance);
settings.roomShapeVarietyQ16=0;
settings.roomHeightRatioQ16=65536;
settings.roomFloorCutMinimumQ16=0;
settings.roomFloorCutMaximumQ16=0;
settings.tunnelHorizontalBiasQ16=65536;
settings.originRoomMaximumConnections=32;
settings.ledgeSpacingMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.LedgeSpacing));
settings.ledgeDepthMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.LedgeDepth));
}
else
{
settings.roomSpacingMicrometres=std::max<std::int64_t>(4000000,
toMicrometres(params.RoomSpacing));
settings.minimumRoomRadiusMicrometres=std::max<std::int64_t>(500000,
@@ -1607,6 +1654,7 @@ namespace StandaloneWorldAssemblyPrototype
settings.tunnelHorizontalBiasQ16=toQ16(params.TunnelHorizontalBias);
settings.originRoomMaximumConnections=params.OriginRoomMaxConnections;
settings.tunnelsFlowTowardOrigin=params.bTunnelsFlowTowardOrigin;
}
std::vector<ResolvedCavernRoomV1> rooms;
std::vector<ResolvedCavernEdgeV1> edges;
if(!PlanResolvedCavernNetworkV1(layout,owner.Seed,slot->StrateIndex,
@@ -1629,24 +1677,27 @@ namespace StandaloneWorldAssemblyPrototype
constexpr std::int64_t PlayerCenterAboveFloorMicrometres=1130000;
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition
||owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ))
!=ECaveGeneratorType::TunnelNetwork)continue;
if(!slot.Definition)continue;
const ECaveGeneratorType generatorType=
owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
const bool verticalShafts=generatorType==ECaveGeneratorType::VerticalShafts;
if(generatorType!=ECaveGeneratorType::TunnelNetwork&&!verticalShafts)continue;
auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[&](const auto& item){return item.strateIndex==slot.StrateIndex;});
if(stratum==layout.strates.end()
||!IsNativeCaveFieldStratum(*stratum))continue;
const std::string routePrefix="tunnel-network/strate-"
const std::string routePrefix=(verticalShafts?"vertical-shafts/strate-"
:"tunnel-network/strate-")
+std::to_string(slot.StrateIndex)+"/";
const bool hasPlannedArrival=std::any_of(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& edge)
{return edge.strateIndex==slot.StrateIndex
&&edge.stableId==routePrefix+"route-edge-0";});
const bool hasPlannedDeparture=std::any_of(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& edge)
{return edge.strateIndex==slot.StrateIndex
const bool hasPlannedDeparture=!verticalShafts
&&std::any_of(layout.cavernEdges.begin(),layout.cavernEdges.end(),
[&](const auto& edge){return edge.strateIndex==slot.StrateIndex
&&edge.stableId==routePrefix+"departure-edge";});
if(!hasPlannedArrival||!hasPlannedDeparture)continue;
if(!hasPlannedArrival||(!verticalShafts&&!hasPlannedDeparture))continue;
// Route-point anchors also drive the passage-mouth floor blend. The
// native cave planner replaces the legacy finite-strate route, so
// retaining its old anchors would join the passage floor to a route
@@ -1663,13 +1714,14 @@ namespace StandaloneWorldAssemblyPrototype
if(passage.upperStrateIndex==slot.StrateIndex
&&passage.lowerStrateIndex>slot.StrateIndex)departure=&passage;
}
if(!arrival||!departure)
{error="Tunnel Network route anchors cannot bind both passage mouths";return false;}
if(!arrival||(!verticalShafts&&!departure))
{error="native cave route anchors cannot bind their required passage mouths";return false;}
const auto appendRoutePoint=[&](std::string id,
const std::array<std::int64_t,3>& point)
{
ResolvedLayoutAnchorV2 anchor;anchor.kind="route_point";
anchor.stableId="tunnel-network/route/"+std::move(id);
anchor.stableId=(verticalShafts?"vertical-shafts/route/"
:"tunnel-network/route/")+std::move(id);
anchor.units="1/256 Core voxel";
anchor.values={point[0],point[1],point[2]};
stratum->anchors.push_back(std::move(anchor));
@@ -1684,14 +1736,23 @@ namespace StandaloneWorldAssemblyPrototype
appendRoutePoint("000-arrival",start);
for(std::size_t edgeIndex=0;edgeIndex<256;++edgeIndex)
{
const std::string stableId="tunnel-network/strate-"
+std::to_string(slot.StrateIndex)+"/route-edge-"
const std::string stableId=routePrefix+"route-edge-"
+std::to_string(edgeIndex);
const auto edge=std::find_if(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& value)
{return value.strateIndex==slot.StrateIndex
&&value.stableId==stableId;});
if(edge==layout.cavernEdges.end())break;
bool terminalRouteEdge=false;
if(verticalShafts)
{
const std::string nextRouteEdge=routePrefix+"route-edge-"
+std::to_string(edgeIndex+1);
terminalRouteEdge=std::none_of(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& candidate)
{return candidate.strateIndex==slot.StrateIndex
&&candidate.stableId==nextRouteEdge;});
}
if(edgeIndex==0)
{
auto inner=edge->controlPointsMicrometres.front();
@@ -1708,18 +1769,20 @@ namespace StandaloneWorldAssemblyPrototype
}
auto point=edge->controlPointsMicrometres.back();
point[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint(std::to_string(edgeIndex+1),
appendRoutePoint(terminalRouteEdge?"999-basal-terminal"
:std::to_string(edgeIndex+1),
{toCoreSubvoxels(point[0]),toCoreSubvoxels(point[1]),
toCoreSubvoxels(point[2])});
}
const std::string departureEdgeId="tunnel-network/strate-"
+std::to_string(slot.StrateIndex)+"/departure-edge";
const auto departureEdge=std::find_if(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& value)
{return value.strateIndex==slot.StrateIndex
&&value.stableId==departureEdgeId;});
if(departureEdge!=layout.cavernEdges.end())
if(departure)
{
const std::string departureEdgeId=routePrefix+"departure-edge";
const auto departureEdge=std::find_if(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& value)
{return value.strateIndex==slot.StrateIndex
&&value.stableId==departureEdgeId;});
if(departureEdge==layout.cavernEdges.end())
{error="native cave route lost its planned departure edge";return false;}
auto inner=departureEdge->controlPointsMicrometres.front();
inner[2]+=PlayerCenterAboveFloorMicrometres;
const std::array<std::int64_t,3> innerPoint={
@@ -1728,9 +1791,17 @@ namespace StandaloneWorldAssemblyPrototype
auto end=departure->upperMouthSubvoxels;
if(!sameRoutePoint(innerPoint,end))
appendRoutePoint("998-departure-inner",innerPoint);
appendRoutePoint("999-departure",departure->upperMouthSubvoxels);
}
else
{
const auto terminal=std::find_if(layout.cavernRooms.begin(),
layout.cavernRooms.end(),[&](const auto& room)
{return room.strateIndex==slot.StrateIndex
&&room.stableId==routePrefix+"basal-landing/primitive-0";});
if(terminal==layout.cavernRooms.end())
{error="Vertical Shafts basal route is missing its terminal landing";return false;}
}
auto end=departure->upperMouthSubvoxels;
appendRoutePoint("999-departure",end);
}
return layout.finalize(error);
}
@@ -0,0 +1,12 @@
[PipelineFullDetailHash] tiles=960 density=0x91931a57a067fd92 mesh=0xa818654be89790ac payload=0x370ff80451105a39 topology=0x06e507557aa70173
[PipelineWholeViewHash] tiles=2370 density=0x7111b4b743e03c95 mesh=0x8c7240939db21b88 payload=0x3981ff5ca7076219 topology=0x07947d2e691b2e55
[PipelineLODHash] lod=0 tiles=512 density=0x2fd4c78685a43cfe mesh=0x50478cff1c1f8ca0 payload=0x54e48e57e0af26de topology=0x860c011cfdb9833a
[PipelineLODHash] lod=1 tiles=448 density=0x4b8d9a6dcd2942ad mesh=0x17e89b7087bfe711 payload=0x2389c109eb780db6 topology=0x7eccd4cf560d0630
[PipelineLODHash] lod=2 tiles=320 density=0x48d4c21a852e6e21 mesh=0x5516a17618d142d5 payload=0x59cda38ab9ca6b7a topology=0x15ae353bd835e6a1
[PipelineLODHash] lod=3 tiles=208 density=0x09b4811098a48c8d mesh=0xbac3fb6d54bca8e9 payload=0xca779555d55be411 topology=0xc63e8ee46890a03a
[PipelineLODHash] lod=4 tiles=240 density=0xfb11591772a3d18e mesh=0xebf2baee8764591b payload=0xe491d7ac9c0061ad topology=0xd618ac33b95ce494
[PipelineLODHash] lod=5 tiles=144 density=0x2f8b7b7fb1e67258 mesh=0x4cd508cbe97344fd payload=0x2b6e96a6050458c9 topology=0x4ff6d0eda8988573
[PipelineLODHash] lod=6 tiles=96 density=0x0114a34c8b8d6113 mesh=0xb1fec4d01ae1fa44 payload=0x3e50cfd429b8e950 topology=0x08090c96d15c1618
[PipelineLODHash] lod=7 tiles=96 density=0x3836ed8cfce73418 mesh=0x40bc6589c5fc1aac payload=0x677349bcae16790f topology=0x36e3aa53192e6a6b
[PipelineLODHash] lod=8 tiles=306 density=0x144f3c1fd722708b mesh=0xef77b28bf7fd9453 payload=0xe3f47b37fcd61489 topology=0x5a3c73e295494e04
[PipelineExactQueryHash] probes=9 density=0x7b7c50ac24ccadcc unclamped=0x7b7c50ac24ccadcc detail=0xefcba20c0c70c415 collision=0xe604823a249029bf path=direct-Core-queries
@@ -0,0 +1,12 @@
[PipelineFullDetailHash] tiles=960 density=0xace56c8f205238ad mesh=0xa9c603a1248de6b8 payload=0xea55f1f4fcc6a894 topology=0x5ea7bc78a1b678ae
[PipelineWholeViewHash] tiles=2370 density=0x826704417f556bb8 mesh=0x3dc31324d910ad2b payload=0x32cd1847a8e05d0a topology=0xb800610aa85336d7
[PipelineLODHash] lod=0 tiles=512 density=0xcd6ab28cfa504aca mesh=0xf03dc80a669fca31 payload=0x397aded1c3d2d297 topology=0xb4a1dc84fabc1fe3
[PipelineLODHash] lod=1 tiles=448 density=0xce6e713828b2a20a mesh=0x1cf408c25d67d18c payload=0x8a2b5d20571c997e topology=0x51cd985fe8e7d100
[PipelineLODHash] lod=2 tiles=320 density=0xd7f003d75d73e5b1 mesh=0x4e9c56918da506ae payload=0x3867c5915b409d6d topology=0x02a2a7c0ee9ac809
[PipelineLODHash] lod=3 tiles=208 density=0xce02d09814028b15 mesh=0xe4c2addc4c54c439 payload=0x7e25d9eee6469c5a topology=0xe603e61c9b9bb733
[PipelineLODHash] lod=4 tiles=240 density=0x0be8798407724eaf mesh=0x131c303cbbe51434 payload=0x2a90ecf0a2411ca0 topology=0xe3e054a371152e08
[PipelineLODHash] lod=5 tiles=144 density=0xf20897ad272103a9 mesh=0x13d00b6253ae1f72 payload=0x0db3880693a962fa topology=0x462bd87a80f15e89
[PipelineLODHash] lod=6 tiles=96 density=0xc040fcac457d6313 mesh=0x1ed132eeb718fb62 payload=0xcd0214e35577c6e3 topology=0x2c56c69e6fdcfd64
[PipelineLODHash] lod=7 tiles=96 density=0xc2d419446355fdb8 mesh=0x4ea44452574fc84b payload=0xfd9779bedfc9ae0f topology=0x8948546e770c9b72
[PipelineLODHash] lod=8 tiles=306 density=0x62db31696f2da831 mesh=0xd373f9e9481532fe payload=0x540a60526e0e3e46 topology=0xc725cb901e6dc335
[PipelineExactQueryHash] probes=9 density=0xc949c4df2ce6f9d9 unclamped=0xc949c4df2ce6f9d9 detail=0x114c501e2d830d94 collision=0xe6011d3a248d4849 path=direct-Core-queries
+4 -4
View File
@@ -125,11 +125,11 @@ RECIPE_RE = re.compile(
def owner_hash_baseline(recipe: str, style: int) -> tuple[Path, str]:
if recipe == "surface-world" and style == 1:
return (BASELINES / "owner-hash/canonical/owner-hash-surface-world-style1-v236.baseline",
"L135-v236-cave-mouth-joints")
return (BASELINES / "owner-hash/canonical/owner-hash-surface-world-style1-v237.baseline",
"L139-v237-vertical-shafts-core-planner")
if recipe == "floating-islands" and style == 2:
return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v236.baseline",
"L135-v236-cave-mouth-joints")
return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v237.baseline",
"L139-v237-vertical-shafts-core-planner")
raise VerificationFailure(f"no pinned owner hash baseline for {recipe} style {style}")