core: cave planner holds for 16 seeds; mouth joints planned from the resolved mouth (L135, v236)

Mouth floor/profile carried into the first/last edges (fixes seeds 1 and 2); fixed a Q16/Q32 scale bug in the mouth
clearance scorer; shortest grade-safe handles; bounded cave AABBs and hoisted edge-BVH normalisation. Seeds 0-15 pass
all composed audits and negative controls; fast tier, Clang/GCC W1/W8/W12 green. Near-ready still +3.9..+13.7% vs
assembler/world (Surface World +3.9..+8.2% with no caves). Not for merge.

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-10-31 23:39:01 +01:00
1 parent a3c8e3687d
commit 2d89023933
25 files changed
+2516 -235

No files matched your search

@@ -138,6 +138,9 @@ namespace VoxelForgeCore
bool passageComposerProfiled = false;
std::uint64_t passageTileFeatureCount = 0;
std::uint64_t passageTileRouteSegmentCandidates = 0;
std::uint64_t nativeCaveFieldSamples = 0;
std::uint64_t cavernRoomCandidates = 0;
std::uint64_t cavernEdgeCandidates = 0;
std::uint64_t resolvedBoundaryBandQueries = 0;
std::uint64_t passageComposerQueries = 0;
std::uint64_t passageIterations = 0;
@@ -498,8 +501,16 @@ namespace VoxelForgeCore
// voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max
// support width/6 when deriving that reach. Version 228 composes deterministic cave
// graphs from fixed passage mouths and the owner Tunnel Network settings. Version 229
// budgets cave floor support; 230 records a shared backbone floor profile and joint cap.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 230;
// budgets cave floor support; 230 records a shared backbone floor profile and joint cap;
// 231 carries passage tangents and floor slopes through cave portal lead-ins and keeps
// floor backing local to the walk footprint, with depth measured along the floor normal;
// 233 bounds passage ring jitter to the authored placement disk; 234 aligns route mouth
// anchors with capsule foot height and carries support through the SurfaceWorld mouth;
// 235 plans the cave backbone as a sampled curve with both passage mouth tangents and
// 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;
enum class FieldChannel : std::uint32_t
{
File diff suppressed because it is too large. Load diff
+380 -68
View File
@@ -255,6 +255,7 @@ public:
{
double signedDistance=std::numeric_limits<double>::infinity();
double shellSignedDistance=std::numeric_limits<double>::infinity();
double floorShellSignedDistance=std::numeric_limits<double>::infinity();
double roundSignedDistance=std::numeric_limits<double>::infinity();
double centerZSubvoxels=0.0,floorCenterZSubvoxels=0.0;
double radiusSubvoxels=0.0,floorDepthSubvoxels=0.0;
@@ -308,6 +309,14 @@ public:
stampIndices_=shape.BuildStampTileList(minimumSubvoxels_,maximumSubvoxels_);
cavernFeatureIndices_=shape.BuildCavernFeatureTileList(
minimumSubvoxels_,maximumSubvoxels_);
if(profileStats_)
for(const std::uint32_t index:cavernFeatureIndices_)
{
if(index>=shape.CavernRuntime.size())continue;
const auto& feature=shape.CavernRuntime[index];
if(feature.room)++profileStats_->cavernRoomCandidates;
else if(feature.edge)++profileStats_->cavernEdgeCandidates;
}
passageRoutes_.assign(shape.ResolvedLayout
?shape.ResolvedLayout->passages.size():0,0);
passageRouteSegments_.resize(passageRoutes_.size());
@@ -1206,11 +1215,15 @@ public:
auto* passageProfile=passageTileScopeMatches
?passageTileQueries->profileStats():nullptr;
if(passageProfile)++passageProfile->passageComposerQueries;
if(passageProfile&&nativeTunnelFieldDensity
!=std::numeric_limits<float>::lowest())
++passageProfile->nativeCaveFieldSamples;
struct FNearestTubeSample
{
double signedDistance=std::numeric_limits<double>::infinity();
double shellSignedDistance=std::numeric_limits<double>::infinity();
double floorShellSignedDistance=std::numeric_limits<double>::infinity();
double roundSignedDistance=std::numeric_limits<double>::infinity();
double centerZ=0.0,floorCenterZ=0.0,radius=0.0,floorDepth=0.0;
double t=0.0,floorT=0.0;
@@ -1220,13 +1233,32 @@ public:
};
double unionDistance=std::numeric_limits<double>::infinity();
double unionShellDistance=std::numeric_limits<double>::infinity();
double unionFloorShellDistance=std::numeric_limits<double>::infinity();
double plannedTunnelPassageShellDistance=std::numeric_limits<double>::infinity();
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->recipeId=="Core.Caves.PrimitiveField"
&&queryStratum->Resolved->recipeVersion==1;
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
++passageIndex)
{
const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue;
const bool surfaceDepartureLanding=queryStratum&&queryStratum->Resolved
&&queryStratum->Resolved->lowerInterfaceCapabilities.surfaceToTunnelLandingSupport
&&passage.upperStrateIndex==queryStratum->StrateIndex;
const auto floorThicknessWithLanding=[&](double floorThickness,
double smoothFloorBlend,double alongMetres)
{
if(!surfaceDepartureLanding)return floorThickness;
const double fade=std::clamp(1.0-alongMetres/8.0,0.0,1.0);
const double smoothFade=fade*fade*(3.0-2.0*fade);
const double landingThickness=0.50*qPerMetre
*smoothFloorBlend*smoothFade;
return std::max(floorThickness,landingThickness);
};
const bool passageTargetsNativeCaveField=queryStratum&&queryStratum->Resolved
&&queryStratum->Resolved->passageCapabilities.hasNativeCaveField
&&passage.lowerStrateIndex==queryStratum->StrateIndex;
@@ -1235,6 +1267,8 @@ public:
&&queryStratum->Resolved->generatorKind=="TunnelNetwork"
&&queryStratum->Resolved->recipeId=="Core.Caves.PrimitiveField"
&&queryStratum->Resolved->recipeVersion==1;
const double mouthShellBlendMetres=passageTargetsPlannedTunnelNetwork
?2.0:MouthShellBlendMetres;
// Legacy native cave fields own the floor and mouth carve on their
// lower-strate boundary. The planner's TunnelNetwork field instead
// needs the shared passage composer to carry its incoming mouth open.
@@ -1390,7 +1424,7 @@ public:
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
constants.totalPathLengthMetres-alongMetres));
const double shellFade=std::clamp(mouthDistanceMetres/
MouthShellBlendMetres,0.0,1.0);
mouthShellBlendMetres,0.0,1.0);
const double smoothShellFade=shellFade*shellFade
*(3.0-2.0*shellFade);
const double floorDistanceMetres=(floorCenterZ-floorDepth-qz)
@@ -1408,12 +1442,16 @@ public:
const double pastMouthMetres=std::max(0.0,alongMetres
-constants.totalPathLengthMetres);
const double extensionFade=std::clamp(1.0
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
-pastMouthMetres/mouthShellBlendMetres,0.0,1.0);
floorExtensionFade=extensionFade*extensionFade
*(3.0-2.0*extensionFade);
}
const double floorShellThickness=smoothFloorBlend*shellThickness
*floorExtensionFade*smoothShellFade;
const double floorShellThickness=floorThicknessWithLanding(
smoothFloorBlend*(shellThickness
+(passageTargetsPlannedTunnelNetwork?0.25*qPerMetre:0.0))
*floorExtensionFade
*smoothShellFade,
smoothFloorBlend,alongMetres);
const double wallShellDistance=PassageRoundedWallShellDistance(
flatDistance,wallElevationMetres*qPerMetre,
OpenAirSideWallHeightMetres*qPerMetre,mouthWallThickness);
@@ -1421,8 +1459,14 @@ public:
static_cast<double>(flatDistance)-floorShellThickness);
const double localShellDistance=std::min(wallShellDistance,
floorShellDistance);
if(passageTargetsPlannedTunnelNetwork
&&mouthDistanceMetres>=mouthShellBlendMetres)
plannedTunnelPassageShellDistance=std::min(
plannedTunnelPassageShellDistance,localShellDistance);
nearest.shellSignedDistance=std::min(nearest.shellSignedDistance,
localShellDistance);
nearest.floorShellSignedDistance=std::min(
nearest.floorShellSignedDistance,floorShellDistance);
};
double shellFloorSearchBoundSq=-1.0;
VisitNearestPassageSegmentRange(passageIndex,0,lastIndex-1,
@@ -1439,6 +1483,14 @@ public:
const double crestFade=std::clamp((OpenAirSideWallHeightMetres
-wallElevationMetres)/0.25,0.0,1.0);
const double smoothCrestFade=crestFade*crestFade*(3.0-2.0*crestFade);
const auto& segment=PassageSegmentData[passageIndex][nearest.segmentIndex];
const double alongMetres=segment.authoredAlongMetres
+nearest.t*segment.authoredLengthMetres;
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
constants.totalPathLengthMetres-alongMetres));
const double shellFade=std::clamp(mouthDistanceMetres/
mouthShellBlendMetres,0.0,1.0);
const double smoothShellFade=shellFade*shellFade*(3.0-2.0*shellFade);
const double wallReliefWeight=(0.05+0.95*(1.0-verticalWallAlignment))
*smoothCrestFade;
float relief=0.0f;
@@ -1457,15 +1509,7 @@ public:
const double shapedDistance=PassageFlatBottomDistance(
static_cast<float>(roughRoundDistance),nearest.floorCenterZ,qz,
nearest.floorDepth,qPerMetre);
const auto& segment=PassageSegmentData[passageIndex][nearest.segmentIndex];
const auto& floorSegment=PassageSegmentData[passageIndex][nearest.floorSegmentIndex];
const double alongMetres=segment.authoredAlongMetres
+nearest.t*segment.authoredLengthMetres;
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
constants.totalPathLengthMetres-alongMetres));
const double shellFade=std::clamp(mouthDistanceMetres/
MouthShellBlendMetres,0.0,1.0);
const double smoothShellFade=shellFade*shellFade*(3.0-2.0*shellFade);
// Ease both wall and floor shell into the host ground over five metres.
// The floor shell remains below the floor plane throughout that blend.
const double floorDistanceMetres=(nearest.floorCenterZ
@@ -1483,18 +1527,26 @@ public:
const double pastMouthMetres=std::max(0.0,floorAlongMetres
-constants.totalPathLengthMetres);
const double extensionFade=std::clamp(1.0
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
-pastMouthMetres/mouthShellBlendMetres,0.0,1.0);
floorExtensionFade=extensionFade*extensionFade
*(3.0-2.0*extensionFade);
}
const double floorShellThickness=smoothFloorBlend*shellThickness
*floorExtensionFade*smoothShellFade;
const double floorShellThickness=floorThicknessWithLanding(
smoothFloorBlend*(shellThickness
+(passageTargetsPlannedTunnelNetwork?0.25*qPerMetre:0.0))
*floorExtensionFade
*smoothShellFade,
smoothFloorBlend,alongMetres);
const double wallShellDistance=PassageRoundedWallShellDistance(
shapedDistance,wallElevationMetres*qPerMetre,
OpenAirSideWallHeightMetres*qPerMetre,mouthWallThickness);
const double floorShellDistance=std::max(-static_cast<double>(shapedDistance),
static_cast<double>(shapedDistance)-floorShellThickness);
nearest.floorShellSignedDistance=std::min(
nearest.floorShellSignedDistance,floorShellDistance);
unionDistance=std::min(unionDistance,static_cast<double>(shapedDistance));
unionFloorShellDistance=std::min(unionFloorShellDistance,
nearest.floorShellSignedDistance);
const double localShellDistance=std::min(wallShellDistance,
floorShellDistance);
unionShellDistance=std::min(unionShellDistance,
@@ -1503,12 +1555,131 @@ public:
if(!std::isfinite(unionDistance))return density;
const float passageAirDistance=static_cast<float>(-unionDistance/Q);
const auto plannedPortalWalkCorridorContains=[&]()
{
if(!queryStratum||!queryStratum->Resolved
||queryStratum->Resolved->generatorKind!="TunnelNetwork"
||queryStratum->Resolved->recipeId!="Core.Caves.PrimitiveField"
||queryStratum->Resolved->recipeVersion!=1)return false;
const VoxelForgeCore::ResolvedLayoutAnchorV2* first=nullptr;
const VoxelForgeCore::ResolvedLayoutAnchorV2* second=nullptr;
const VoxelForgeCore::ResolvedLayoutAnchorV2* third=nullptr;
const VoxelForgeCore::ResolvedLayoutAnchorV2* lastThree=nullptr;
const VoxelForgeCore::ResolvedLayoutAnchorV2* lastTwo=nullptr;
const VoxelForgeCore::ResolvedLayoutAnchorV2* last=nullptr;
std::size_t routeCount=0;
for(const auto& anchor:queryStratum->Resolved->anchors)
{
if(anchor.kind!="route_point"||anchor.values.size()<3)continue;
if(routeCount==0)first=&anchor;
else if(routeCount==1)second=&anchor;
else if(routeCount==2)third=&anchor;
lastThree=lastTwo;lastTwo=last;last=&anchor;
++routeCount;
}
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")
||!last->stableId.ends_with("999-departure"))return false;
const double metresPerSubvoxel=1.0/qPerMetre;
const auto position=[&](const VoxelForgeCore::ResolvedLayoutAnchorV2& anchor)
{
return std::array<double,3>{anchor.values[0]*metresPerSubvoxel,
anchor.values[1]*metresPerSubvoxel,
anchor.values[2]*metresPerSubvoxel};
};
const double queryX=X*Q/qPerMetre;
const double queryY=Y*Q/qPerMetre;
const double queryZ=Z*Q/qPerMetre;
const auto nearRouteSegment=[&](const VoxelForgeCore::ResolvedLayoutAnchorV2& start,
const VoxelForgeCore::ResolvedLayoutAnchorV2& end,double limit,bool fromDeparture,
double distanceToDepartureAfterEnd)
{
const auto a=position(start),b=position(end);
const double dx=b[0]-a[0],dy=b[1]-a[1];
const double horizontalLength=std::hypot(dx,dy);
if(horizontalLength<=1.0e-9)return false;
const double t=std::clamp(((queryX-a[0])*dx
+(queryY-a[1])*dy)/(horizontalLength*horizontalLength),0.0,1.0);
const double along=fromDeparture
?distanceToDepartureAfterEnd+(1.0-t)*horizontalLength
:distanceToDepartureAfterEnd+t*horizontalLength;
if(along>limit)return false;
const double routeX=a[0]+t*dx,routeY=a[1]+t*dy;
if(std::hypot(queryX-routeX,queryY-routeY)>0.80)return false;
const double routeFloor=std::lerp(a[2],b[2],t)-1.13;
return queryZ>=routeFloor-0.30&&queryZ<=routeFloor+2.25;
};
constexpr double PortalWalkLeadMetres=8.0;
const auto arrival=position(*first),arrivalInner=position(*second);
const double arrivalInnerDistance=std::hypot(
arrivalInner[0]-arrival[0],arrivalInner[1]-arrival[1]);
if(nearRouteSegment(*first,*second,PortalWalkLeadMetres,false,0.0)
||nearRouteSegment(*second,*third,PortalWalkLeadMetres,false,
arrivalInnerDistance))return true;
const auto departureInner=position(*lastTwo),departure=position(*last);
const double departureTailDistance=std::hypot(
departure[0]-departureInner[0],departure[1]-departureInner[1]);
return nearRouteSegment(*lastThree,*lastTwo,PortalWalkLeadMetres,true,
departureTailDistance)
||nearRouteSegment(*lastTwo,*last,PortalWalkLeadMetres,true,0.0);
};
if(plannedTunnelStratum
&&nativeTunnelFieldDensity!=std::numeric_limits<float>::lowest()
&&nativeTunnelFieldDensity<0.0f)
{
const double metresPerVoxel=Q/qPerMetre;
const double queryZMetres=Z*metresPerVoxel;
const double contentBottomMetres=static_cast<double>(
queryStratum->Resolved->content.bottomMicrometres)/1000000.0;
const double contentTopMetres=static_cast<double>(
queryStratum->Resolved->content.topMicrometres)/1000000.0;
const double boundaryDistance=std::min(
std::abs(queryZMetres-contentBottomMetres),
std::abs(queryZMetres-contentTopMetres));
const double boundaryJoinBand=std::max(2.0,
static_cast<double>(queryStratum->Resolved->transitionWidthMicrometres)
/2000000.0);
if(boundaryDistance<=boundaryJoinBand
&&std::abs(static_cast<double>(passageAirDistance)*metresPerVoxel)
<=FloorShellThicknessMetres)
// A neighboring stratum can be open at a boundary sample even
// though the native graph owns solid host rock there. Keep the
// authored passage signed distance through that transition band;
// otherwise the neighboring field can widen its wall by a whole
// shell thickness. Native graph air still wins above via the
// positive nativeTunnelFieldDensity path.
return passageAirDistance;
}
// Air is the union of the native graph and passage voids. Keeping the
// signed passage distance outside the void as well gives marching cubes
// a real negative distance through the floor instead of the terrain's
// saturated deep-rock value.
if(nativeTunnelFieldDensity!=std::numeric_limits<float>::lowest()
&&plannedTunnelPassageShellDistance<0.0&&unionDistance>0.0)
{
if(nativeTunnelFieldDensity>=0.0f&&plannedPortalWalkCorridorContains())
return std::max(nativeTunnelFieldDensity,passageAirDistance);
return std::min(density,
static_cast<float>(plannedTunnelPassageShellDistance/Q));
}
if(nativeTunnelFieldDensity>=0.0f)
{
// The planner's passage shell remains authoritative through native
// cave voids after the explicit mouth fade has ended. Keep this scoped
// to the planner-crossing passage shell so nearby portal joints stay open.
if(plannedTunnelPassageShellDistance<0.0&&unionDistance>0.0)
return std::min(nativeTunnelFieldDensity,
static_cast<float>(plannedTunnelPassageShellDistance/Q));
constexpr float NativeCaveAirMarginVoxels=0.75f;
if(unionDistance>0.0
&&nativeTunnelFieldDensity<NativeCaveAirMarginVoxels
&&unionFloorShellDistance<0.0)
return std::min(nativeTunnelFieldDensity,
static_cast<float>(unionFloorShellDistance/Q));
return std::max(nativeTunnelFieldDensity,passageAirDistance);
}
if(nativeFloorSupport!=std::numeric_limits<float>::lowest()
&&nativeFloorSupport<0.0f)
return std::min(nativeFloorSupport,unionDistance<=0.0
@@ -2274,6 +2445,10 @@ public:
SurfaceDistanceQ=std::make_unique_for_overwrite<int16[]>(static_cast<size_t>(NodeCount));
Substage=Clock::now();
BuildPackedRockIntervals(Stats.RockIntervalLines);
const auto criticalAirInitialStart=Clock::now();
const uint64 criticalAirVoxelsBeforeDetail=ApplyCriticalAirPackedSigns();
const double criticalAirInitialSeconds=
std::chrono::duration<double>(Clock::now()-criticalAirInitialStart).count();
Stats.PackedSignBuildSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
// Native density queries for non-heightfield strates use the exact interval
// evaluator. Build those native intervals before preparing the tunnel sweeps.
@@ -2296,8 +2471,16 @@ public:
ApplyDetailBand(Stats.DetailBandLines);
Stats.DetailApplySeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
Substage=Clock::now();
const uint64 criticalAirVoxelsAfterDetail=ApplyCriticalAirPackedSigns();
const double criticalAirRepairSeconds=
std::chrono::duration<double>(Clock::now()-Substage).count();
Substage=Clock::now();
Stats.SurfaceProxyNodes+=CompleteSignEdgeDistances();
Stats.SignEdgeDistanceSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
Substage=Clock::now();
const uint64 criticalAirVoxelsAfterSignRepair=ApplyCriticalAirPackedSigns();
const double criticalAirSignEdgeRepairSeconds=
std::chrono::duration<double>(Clock::now()-Substage).count();
Stats.SurfaceSeconds=std::chrono::duration<double>(Clock::now()-Stage).count();
// Route validation is world-wide. Detail regions reuse the same authored route data and
// do not need to repeat the 5,642-point world audit for every entering tile group.
@@ -2329,8 +2512,13 @@ public:
Stats.CandidateMergeLines.WallSeconds*1000.0,Stats.CandidateMergeLines.WorkerSeconds*1000.0,
Stats.CandidateMergeSeconds*1000.0,Stats.DetailBandLines.WallSeconds*1000.0,
Stats.DetailBandLines.WorkerSeconds*1000.0);
std::printf("[ShapeSubstage] detail_apply=%.3fms sign_edge_distances=%.3fms passage_validation=%.3fms shared_field_primitives=%.3fms far_parents=%.3fms\n",
Stats.DetailApplySeconds*1000.0,Stats.SignEdgeDistanceSeconds*1000.0,
std::printf("[ShapeSubstage] detail_apply=%.3fms critical_air_sign_repair=%.3fms critical_air_voxels=%llu/%llu/%llu sign_edge_distances=%.3fms passage_validation=%.3fms shared_field_primitives=%.3fms far_parents=%.3fms\n",
Stats.DetailApplySeconds*1000.0,criticalAirInitialSeconds*1000.0
+criticalAirRepairSeconds*1000.0+criticalAirSignEdgeRepairSeconds*1000.0,
static_cast<unsigned long long>(criticalAirVoxelsBeforeDetail),
static_cast<unsigned long long>(criticalAirVoxelsAfterDetail),
static_cast<unsigned long long>(criticalAirVoxelsAfterSignRepair),
Stats.SignEdgeDistanceSeconds*1000.0,
Stats.PassageValidationSeconds*1000.0,Stats.SharedFieldSeconds*1000.0,
Stats.FarShapeSeconds*1000.0);
std::printf("[ShapeBreakdown] owner_shapes=%.3fms owner_shape_worker=%.3fms owner_bins=%.3fms packed_signs=%.3fms passage_prep=%.3fms candidate_sweeps=%.3fms visual_detail_prep=%.3fms detail_band=%.3fms sign_edge_repair=%.3fms route_validation=%.3fms far_parent_shapes=%.3fms far_shared_primitives=%.3fms far_bins=%.3fms\n",
@@ -3402,6 +3590,7 @@ private:
{
double floorDepthMetres=0.0,totalPathLengthMetres=0.0;
double authoredTotalPathLengthMetres=0.0;
double upperMouthPlayerCenterHeightMetres=1.13;
std::size_t routeProfileSegmentCount=0;
double upperMouthX=0.0,upperMouthY=0.0;
double upperMouthPlannedFloor=0.0;
@@ -3475,7 +3664,8 @@ private:
plannedFloor,constants.upperMouthX,constants.upperMouthY,
constants.upperMouthPlannedFloor,
constants.upperMouthTangentX,constants.upperMouthTangentY,
VoxelForgeCore::PassageSpline::UpperMouthFloorBlendRadiusMetres,1.13,
VoxelForgeCore::PassageSpline::UpperMouthFloorBlendRadiusMetres,
constants.upperMouthPlayerCenterHeightMetres,
qPerMetre);
const double lowerBlendFloor=VoxelForgeCore::PassageSpline::BlendUpperMouthFloorAtXY(
constants.lowerMouthReceivingRoute,queryXSubvoxels,queryYSubvoxels,
@@ -3594,7 +3784,7 @@ private:
};
struct FSegmentNode
{
std::array<double,3> minimum{},maximum{};
std::array<double,3> minimum{},maximum{},minimumNormalized{},maximumNormalized{};
std::size_t first=0,end=0;
int left=-1,right=-1;
};
@@ -4174,6 +4364,16 @@ private:
return nodeIndex;
};
buildSegmentNode(buildSegmentNode,0,runtime.segmentOrder.size());
const double radius=edge.boreRadiusMicrometres/1000000.0;
const double height=edge.boreHeightMicrometres/1000000.0;
if(radius>0.0&&height>0.0)
for(auto& node:runtime.segmentNodes)
for(std::size_t axis=0;axis<3;++axis)
{
const double scale=axis==2?height:radius;
node.minimumNormalized[axis]=node.minimum[axis]/scale;
node.maximumNormalized[axis]=node.maximum[axis]/scale;
}
}
CavernRuntime.push_back(runtime);
}
@@ -4623,6 +4823,12 @@ private:
passage.lowerMouthControlIndex,1,passage.controlPoints.size()-1);
auto& constants=PassageComposerConstants[passageIndex];
constants.routeEndControlIndex=routeEnd;
const auto upperStratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.upperStrateIndex;});
if(upperStratum!=ResolvedLayout->strates.end()
&&upperStratum->generatorKind=="SurfaceWorld")
constants.upperMouthPlayerCenterHeightMetres=0.98;
using FRawPassageSegment=VoxelForgeCore::PassageSpline::FSegment;
std::vector<VoxelForgeCore::PassageSpline::FPoint> controls;
@@ -4640,11 +4846,9 @@ private:
}
const double authoredUpperMouthFloorDepth=std::max(0.0,
static_cast<double>(passage.controlPoints.front().positionSubvoxels[2]
-passage.upperMouthSubvoxels[2])/qPerMetre+1.13);
-passage.upperMouthSubvoxels[2])/qPerMetre
+constants.upperMouthPlayerCenterHeightMetres);
double upperMouthFloorDepth=authoredUpperMouthFloorDepth;
const auto upperStratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.upperStrateIndex;});
if(upperStratum!=ResolvedLayout->strates.end()
&&(upperStratum->generatorKind=="FloatingIslands"
||upperStratum->generatorKind=="TunnelNetwork"))
@@ -4664,7 +4868,8 @@ private:
&&distanceSquared<=std::pow(controls.front()[3]+2.0*qPerMetre,2.0))
{
upperMouthFloorDepth=std::max(upperMouthFloorDepth,
std::max(0.0,(controls.front()[2]-routeHeight)/qPerMetre+1.13));
std::max(0.0,(controls.front()[2]-routeHeight)/qPerMetre
+constants.upperMouthPlayerCenterHeightMetres));
if(upperStratum->generatorKind=="TunnelNetwork")
VoxelForgeCore::PassageSpline::BlendEndpointFloorToRoute(
controls,plannedFloorProfile,0,route,qPerMetre);
@@ -4761,6 +4966,26 @@ private:
constants.upperMouthTangentY=sampledSegments.front().b[1]
-sampledSegments.front().a[1];
}
const std::string lowerMouthAnchor=passage.stableId+"/lower-mouth";
const std::string upperMouthAnchor=passage.stableId+"/upper-mouth";
for(const auto& edge:ResolvedLayout->cavernEdges)
{
if(edge.controlPointsMicrometres.size()<2)continue;
if(edge.startAnchorStableId==lowerMouthAnchor)
{
constants.lowerMouthTangentX=edge.controlPointsMicrometres[1][0]
-edge.controlPointsMicrometres[0][0];
constants.lowerMouthTangentY=edge.controlPointsMicrometres[1][1]
-edge.controlPointsMicrometres[0][1];
}
if(edge.startAnchorStableId==upperMouthAnchor)
{
constants.upperMouthTangentX=edge.controlPointsMicrometres[0][0]
-edge.controlPointsMicrometres[1][0];
constants.upperMouthTangentY=edge.controlPointsMicrometres[0][1]
-edge.controlPointsMicrometres[1][1];
}
}
std::vector<FRawPassageSegment> rawSegments=sampledSegments;
std::vector<std::array<double,2>> routeRadiusSamples;
routeRadiusSamples.push_back({0.0,controls.front()[3]/qPerMetre});
@@ -7563,6 +7788,8 @@ private:
double floorSupportDepth=0.0,floorSupportFloor=0.0,floorSupportGrade=0.0;
const std::string* floorSupportEdgeId=nullptr;
const std::string* floorSupportProfileId=nullptr;
double lowestOpenEdgeFloor=std::numeric_limits<double>::infinity();
const std::string* lowestOpenEdgeProfileId=nullptr;
for(const std::uint32_t index:*candidates)
{
if(index>=CavernRuntime.size())continue;
@@ -7570,7 +7797,26 @@ private:
const std::int32_t strateIndex=feature.room?feature.room->strateIndex
:feature.edge?feature.edge->strateIndex:-1;
if(strateIndex!=bound.StrateIndex)continue;
// Tile culling keeps the candidate list small, but a tile can still
// contain millions of sample points outside a feature's authored
// bounds. Reject those points before evaluating room SDFs or walking
// an edge's segment BVH. Keep a 1 mm guard for float voxel-coordinate
// rounding; the serialized bounds already include all SDF, union,
// irregularity, bore, and floor-support reach.
constexpr double FeatureBoundsGuardMetres=0.001;
bool insideFeatureBounds=true;
for(std::size_t axis=0;axis<3;++axis)
{
const double minimum=feature.boundsMinimumMicrometres[axis]/1000000.0
-FeatureBoundsGuardMetres;
const double maximum=feature.boundsMaximumMicrometres[axis]/1000000.0
+FeatureBoundsGuardMetres;
if(point[axis]<minimum||point[axis]>maximum)
{insideFeatureBounds=false;break;}
}
if(!insideFeatureBounds)continue;
double featureVoid=std::numeric_limits<double>::infinity();
double edgeFloor=0.0;
if(feature.room)
{
double normalizedSquared=0.0,meanRadius=0.0;
@@ -7655,15 +7901,16 @@ private:
?(segment.end[2]-segment.start[2])/horizontal:0.0;
}
};
const std::array<double,3> normalizedPoint={
point[0]/radius,point[1]/radius,point[2]/height};
const auto nodeLowerBound=[&](const FCavernRuntimeFeature::FSegmentNode& node)
{
double gapSquared=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double scale=axis==2?height:radius;
const double q=point[axis]/scale;
const double low=node.minimum[axis]/scale;
const double high=node.maximum[axis]/scale;
const double q=normalizedPoint[axis];
const double low=node.minimumNormalized[axis];
const double high=node.maximumNormalized[axis];
const double gap=q<low?low-q:q>high?q-high:0.0;
gapSquared+=gap*gap;
}
@@ -7696,52 +7943,67 @@ private:
evaluateSegment(segment);
const double candidateSupportDepth=
feature.edge->floorSupportDepthMicrometres/1000000.0;
if(candidateSupportDepth>0.0&&nearestRadial<radius-0.12
constexpr double MinimumBackingDepthMetres=0.65;
constexpr double FloorSupportFootprintRadiusMetres=0.95;
// Portal edges join a wider authored passage whose floor spans the
// full bore. Carry support across that shared mouth footprint;
// ordinary backbone and branch edges retain the narrow support
// strip that prevents nearby routes from sealing one another.
const bool portalMouthEdge=feature.edge->startAnchorStableId.ends_with(
"-mouth");
const double floorSupportRadius=portalMouthEdge?radius:
std::min(radius,FloorSupportFootprintRadiusMetres);
const double candidateNormalDepth=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade);
if(candidateSupportDepth>=MinimumBackingDepthMetres
&&candidateNormalDepth>=MinimumBackingDepthMetres
&&candidateNormalDepth<=candidateSupportDepth
&&nearestRadial<floorSupportRadius-0.12
&&std::abs(point[2]-nearestFloor)<=height)
{
const double deltaZ=point[2]-nearestFloor;
const double candidateScore=nearestRadial*nearestRadial+deltaZ*deltaZ;
if(!feature.edge->floorProfileStableId.empty())
const double candidateScore=nearestRadial*nearestRadial;
const bool hasProfile=!feature.edge->floorProfileStableId.empty();
const bool sameProfile=floorSupportProfileId&&hasProfile
&&*floorSupportProfileId==feature.edge->floorProfileStableId;
if(!floorSupportProfileId||sameProfile)
{
const bool sameProfile=floorSupportProfileId
&&*floorSupportProfileId==feature.edge->floorProfileStableId;
if(!floorSupportProfileId||sameProfile)
{
// Backbone edges share h(s). At a geometric joint their
// cave volumes overlap, so the backing top is the lower
// of the local floors, never the closer higher segment.
if(!floorSupportProfileId||nearestFloor<floorSupportFloor
||(nearestFloor==floorSupportFloor
&&feature.edge->stableId<*floorSupportEdgeId))
{
floorSupportScore=candidateScore;
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
}
floorSupportProfileId=&feature.edge->floorProfileStableId;
}
}
else if(!floorSupportProfileId
&&(candidateScore<floorSupportScore
// For a shared backbone, support follows the closest
// horizontal centerline. Vertical separation must not make
// a nearby branch take support away from this path.
if(!floorSupportEdgeId||candidateScore<floorSupportScore
||(candidateScore==floorSupportScore
&&(!floorSupportEdgeId
||feature.edge->stableId<*floorSupportEdgeId))))
{
floorSupportScore=candidateScore;
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
&&(nearestFloor<floorSupportFloor
||(nearestFloor==floorSupportFloor
&&feature.edge->stableId<*floorSupportEdgeId))))
{
floorSupportScore=candidateScore;
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
}
if(hasProfile)
floorSupportProfileId=&feature.edge->floorProfileStableId;
}
}
edgeFloor=nearestFloor;
const double floor=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade);
const double width=feature.edge->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(tube,floor,width);
}
if(!std::isfinite(featureVoid))continue;
if(feature.edge&&feature.edge->floorSupportDepthMicrometres>=650000
&&featureVoid<0.0)
{
if(edgeFloor<lowestOpenEdgeFloor
||(edgeFloor==lowestOpenEdgeFloor&&feature.edge->floorProfileStableId
<(lowestOpenEdgeProfileId?*lowestOpenEdgeProfileId:std::string{})))
{
lowestOpenEdgeFloor=edgeFloor;
lowestOpenEdgeProfileId=&feature.edge->floorProfileStableId;
}
}
if(!std::isfinite(caveVoid))caveVoid=featureVoid;
else caveVoid=SmoothMinMetres(caveVoid,featureVoid,
feature.room
@@ -7752,8 +8014,10 @@ private:
?feature.edge->unionWidthMicrometres
:feature.edge->smoothWidthMicrometres)/1000000.0);
}
const double floorSupportNormalDepth=(floorSupportFloor-point[2])
/std::sqrt(1.0+floorSupportGrade*floorSupportGrade);
if(std::isfinite(floorSupportScore)&&point[2]<floorSupportFloor
&&floorSupportFloor-point[2]<=floorSupportDepth)
&&floorSupportNormalDepth<=floorSupportDepth)
nativeFloorSupportMinimum=(point[2]-floorSupportFloor)
/std::sqrt(1.0+floorSupportGrade*floorSupportGrade);
// A versioned native caves recipe owns a solid host-rock field. The
@@ -7767,7 +8031,15 @@ private:
// Preserve each native graph edge's planned floor backing after smooth
// unions. The record's finite depth keeps independent routes from sealing
// one another when they pass at different elevations.
if(std::isfinite(nativeFloorSupportMinimum))
// Backing from a higher route edge must not seal a lower overlapping bore.
// Room voids and same-level route joins still retain their authored support.
const bool sameContinuousFloorProfile=floorSupportProfileId
&&lowestOpenEdgeProfileId&&!floorSupportProfileId->empty()
&&*floorSupportProfileId==*lowestOpenEdgeProfileId;
const bool sealsOpenLowerEdge=std::isfinite(lowestOpenEdgeFloor)
&&lowestOpenEdgeFloor<floorSupportFloor-0.50
&&point[2]>lowestOpenEdgeFloor&&!sameContinuousFloorProfile;
if(std::isfinite(nativeFloorSupportMinimum)&&!sealsOpenLowerEdge)
composed=std::min(composed,nativeFloorSupportMinimum);
if(nativeFloorSupport&&std::isfinite(nativeFloorSupportMinimum))
*nativeFloorSupport=static_cast<float>(nativeFloorSupportMinimum/metresPerVoxel);
@@ -9748,6 +10020,32 @@ private:
},&Timing,nullptr,16);
}
uint64 ApplyCriticalAirPackedSigns()
{
const int32 NX=Size[0],NY=Size[1];
std::array<uint64,16> workerVoxels{};
ParallelLines(static_cast<size_t>(NX)*NY,[&](size_t Line,int32 Worker)
{
const int32 X=Min[0]+static_cast<int32>(Line%NX);
const int32 Y=Min[1]+static_cast<int32>(Line/NX);
const size_t Base=Line*WordsPerZ;
for(const uint32 Id:GetLineCandidates(2,X,Y))
{
const FPrimitive& primitive=Primitives[Id];
if(!primitive.bCarve||!primitive.bCritical)continue;
const int32 lo=std::max(primitive.Min[2],Min[2]);
const int32 hi=std::min(primitive.Max[2],Max[2]);
for(int32 Z=lo;Z<=hi;++Z)
{
if(!ShapeContains(primitive,X,Y,Z))continue;
FillBits(RockWords,Base,Z-Min[2],Z-Min[2],false);
++workerVoxels[static_cast<size_t>(Worker)];
}
}
},nullptr,nullptr,16);
return std::accumulate(workerVoxels.begin(),workerVoxels.end(),uint64{0});
}
void BuildCandidateBands(FParallelTiming& AxisTiming,FParallelTiming& MergeTiming,
double& MergeSeconds)
{
@@ -11491,7 +11789,8 @@ private:
const int32 VZ=static_cast<int32>(std::lround(Z));
const bool bCritical=IsInsideCriticalAir(VX,VY,VZ);
if(bCritical){++RouteCritical;++SegmentCritical;}
if(IsRock(VX,VY,VZ))
const bool bRock=IsRock(VX,VY,VZ);
if(bRock)
{
++Stats.WalkFailures;++RouteFailures;++SegmentFailures;
if(RouteFailures==1)
@@ -11705,7 +12004,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
?shape_->ResolvedLayout->coreVoxelSizeMicrometres:250000);
const double shellThickness=VF_PassageShellThicknessMetres*qPerMetre;
const double stampedOutsideDistance=sweep->influenceSubvoxels+1.0;
constexpr double MouthShellBlendMetres=5.0;
constexpr double DefaultMouthShellBlendMetres=5.0;
const double wallReliefShell=0.41*qPerMetre;
const double floorDetailShell=1.0*qPerMetre;
if(profile)
@@ -11730,6 +12029,19 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
{
const auto& candidates=passageRouteSegments_[passageIndex];
if(candidates.empty())continue;
double mouthShellBlendMetres=DefaultMouthShellBlendMetres;
if(shape_->ResolvedLayout&&passageIndex<shape_->ResolvedLayout->passages.size())
{
const auto& passage=shape_->ResolvedLayout->passages[passageIndex];
const auto lower=std::find_if(shape_->ResolvedLayout->strates.begin(),
shape_->ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.lowerStrateIndex;});
if(lower!=shape_->ResolvedLayout->strates.end()
&&lower->generatorKind=="TunnelNetwork"
&&lower->recipeId=="Core.Caves.PrimitiveField"
&&lower->recipeVersion==1)
mouthShellBlendMetres=2.0;
}
const std::int32_t sweepIndex=static_cast<std::int32_t>(sweep->passages.size());
sweep->passageToSweep[passageIndex]=sweepIndex;
auto& passageSamples=sweep->passages.emplace_back();
@@ -11883,7 +12195,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
constants.totalPathLengthMetres-alongMetres));
const double shellFade=std::clamp(mouthDistanceMetres/
MouthShellBlendMetres,0.0,1.0);
mouthShellBlendMetres,0.0,1.0);
const double smoothShellFade=shellFade*shellFade
*(3.0-2.0*shellFade);
const double floorDistanceMetres=(floorCenterZ-floorDepth-qz)/qPerMetre;
@@ -11900,7 +12212,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
const double pastMouthMetres=std::max(0.0,alongMetres
-constants.totalPathLengthMetres);
const double extensionFade=std::clamp(1.0
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
-pastMouthMetres/mouthShellBlendMetres,0.0,1.0);
floorExtensionFade=extensionFade*extensionFade
*(3.0-2.0*extensionFade);
}
@@ -0,0 +1,32 @@
# Linux135 — Tunnel Network planner
Source v236 (`8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5`) on `wip/l134-cave-planner`.
## Route audits
Seeds 0–15 pass the composed passage, seam, shell, naturalness, mouth-profile, ripple, and blocked-route controls (16/16). The negative control still detects all three blocked routes. No audit threshold changed.
The mouth joint now takes its floor/profile from the resolved mouth geometry and carries that shared profile into the planner's first and last edges. This fixes seed 1's blocked arrival and seed 2's unsupported arrival. For seeds 0 and 3, the first 648 regular route candidates exceeded the grade bound; the bounded deterministic fallback finds a route. “No sweepable route candidates” was a planner search miss, not an empty cave.
## Cost
L80 medians are near-ready / whole-view percent deltas. The serialized 144-run matrix started at load 0.81 / 98.4% CPU idle. Each cell gives `assembler/world` first, then exact L134 (`a3c8e36`).
| Layer | W8 | W12 |
|---|---:|---:|
| 0 | +3.88 / +0.27%; +1.79 / -0.21% | +8.22 / +4.09%; +3.89 / +2.49% |
| 1 | +8.82 / +4.96%; +4.14 / +1.71% | +11.83 / +5.35%; +4.10 / +2.09% |
| 2 | +13.33 / +3.77%; +2.34 / -0.09% | +12.56 / +5.20%; +1.40 / +0.61% |
| 3 | +13.65 / +3.99%; -0.25 / +0.44% | +12.71 / +5.14%; +2.97 / +2.12% |
The two speed changes were bounded cave AABBs with a 1 mm float guard, and normalized edge-BVH bounds with one normalization per sample. This improves on L134, but misses the `assembler/world` target: near-ready remains 3.88–13.65% slower. Layers 1–3 overlap the native cave volume and spend time on roughly 4.95–5.76 million near-LOD cave samples per view; layer 0 has none at LOD0–3 and is still up 3.88% at W8, showing some generic composer/classification cost as well.
Planning is about 3.75–4.19 ms per view (cavern planning 0.46–0.49 ms). At LOD0–3, room/edge candidates average 0.39/0.23 per composer tile on layer 1, 0.47/0.27 on layer 2, and 0.06/0.04 on layer 3. Tile proofs skip work at LOD0–3; measured cave views have no tile skips at LOD4–8. The reported 180–320 ns density-call estimate includes all shape fields, not cave-only time. Profile: `Saved/Linux/l135/profile-summary-speed2.json`.
## Gates and pictures
Fast tier passes in 301.7 s. Clang and GCC each match 13 hash lines at W1/W8/W12. Route seams and the negative controls pass; canonical baselines are v236. The post-timing VFHost Linux Development build succeeds: 27/27 actions, 77.70 s, using `-NoUBA -MaxParallelActions=4`.
The seven-view gallery has four planner views at the planned floor plus 1.6 m and three legacy views at legacy coordinates. Open `captures/COMPARE.html`; camera coordinates and renderer settings are in `captures/capture-index.json`.
Sweep: `Saved/Linux/l135/optimized-seed-sweep.json`. Timing: `Saved/Linux/l135/timing/summary.json`. Determinism: `Saved/Linux/l135/determinism-summary.json`. Host build log: `Saved/Linux/l135/ubt-l135-console.log`.
@@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""Capture eye-height planner interiors and legacy interiors at legacy coordinates."""
from __future__ import annotations
import json
import math
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Standalone/Research/WorldAssemblerM3/Linux135/captures"
SAVED = ROOT / "Saved/Linux/l135"
DATA = ROOT / "Standalone/data/owner_world.json"
LAYOUT = SAVED / "capture-default-layout.json"
HEADLESS = SAVED / "bin/VoxelForgeCoreHeadless-clang-l135"
RENDERER = SAVED / "bin/VoxelForgeOffscreenCapture-clang-l135"
STAMP = "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5"
EYE_METRES = 1.6
def run(command: list[str], log_path: Path) -> None:
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", *command]
with log_path.open("w", encoding="utf-8") as log:
result = subprocess.run(wrapped, cwd=ROOT, stdout=log,
stderr=subprocess.STDOUT, timeout=1740,
check=False)
if result.returncode:
raise SystemExit(f"command failed ({result.returncode}): {log_path}")
def xyz(value: list[int] | list[float]) -> tuple[float, float, float]:
return tuple(component / 1_000_000.0 for component in value) # type: ignore[return-value]
def yaw_to(dx: float, dy: float) -> float:
# The renderer's yaw zero points along +Y (Forward = sin(yaw), cos(yaw), 0).
return math.degrees(math.atan2(dx, dy))
def camera_arg(camera: tuple[float, float, float], yaw: float) -> str:
return ",".join(f"{part:.9f}" for part in (*camera, yaw, 0.0))
def make_view(name: str, recipe: str, camera: tuple[float, float, float],
yaw: float) -> dict[str, object]:
OUT.mkdir(parents=True, exist_ok=True)
mesh = SAVED / f"capture-{name}.vfmesh"
image = OUT / f"{name}.png"
mesh_log = SAVED / f"capture-{name}-mesh.log"
render_log = SAVED / f"capture-{name}-render.log"
command = [str(HEADLESS), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=8",
"--clip-radius=1", "--max-clip-level=0", "--max-level=0",
f"--camera={camera_arg(camera, yaw)}", f"--mesh-export={mesh}",
"--capture-skip-walkability", "--force-generate",
"--render-distance-chunks=0"]
# The checked-in fixture is already seed 0. Keeping its Unreal-resolved owner
# plan avoids rebuilding the broad SurfaceWorld spawn search for each camera.
if recipe == "legacy-tunnel-network":
command.append("--legacy-tunnel-network")
else:
command.append("--recipe=surface-world")
run(command, mesh_log)
render_command = [str(RENDERER), str(mesh), str(image), "--size=1600x900",
"--shade=readable", "--height-tint=on", "--headlamp=off"]
run(render_command, render_log)
mesh_output = mesh_log.read_text(encoding="utf-8", errors="replace")
render_output = render_log.read_text(encoding="utf-8", errors="replace")
if STAMP not in mesh_output or STAMP not in render_output:
raise SystemExit(f"source stamp mismatch for {name}")
if "status=PASS" not in render_output:
raise SystemExit(f"render failed for {name}")
return {"name": name, "recipe": recipe, "camera_m": list(camera),
"yaw_deg": yaw,
"camera_height_basis": "floor + player eye offset" if recipe == "surface-world"
else ("legacy room floor + player eye offset" if name.endswith("room")
else "near the legacy tunnel centerline"),
"eye_offset_m": EYE_METRES
if recipe == "surface-world" or name.endswith("room") else None,
"mesh": str(mesh.relative_to(ROOT)), "image": str(image.relative_to(ROOT)),
"mesh_log": str(mesh_log.relative_to(ROOT)),
"render_log": str(render_log.relative_to(ROOT))}
def main() -> int:
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
rooms = {room["stable_id"]: room for room in layout["cavern_rooms"]}
edges = {edge["stable_id"]: edge for edge in layout["cavern_edges"]}
views: list[dict[str, object]] = []
arrival = rooms["tunnel-network/strate-2/room-0/primitive-0"]
room1 = rooms["tunnel-network/strate-2/room-1/primitive-0"]
arrival_center = xyz(arrival["center_um"])
arrival_floor = arrival["floor_um"] / 1_000_000.0
views.append(make_view("planner-arrival-room", "surface-world",
(arrival_center[0], arrival_center[1], arrival_floor + EYE_METRES),
yaw_to(room1["center_um"][0] - arrival["center_um"][0],
room1["center_um"][1] - arrival["center_um"][1])))
tunnel = edges["tunnel-network/strate-2/route-edge-1"]["polyline_um"]
tunnel_start, tunnel_end = xyz(tunnel[0]), xyz(tunnel[-1])
tunnel_floor = tunnel[1][2] / 1_000_000.0
views.append(make_view("planner-tunnel", "surface-world",
((tunnel_start[0] + tunnel_end[0]) * 0.5,
(tunnel_start[1] + tunnel_end[1]) * 0.5,
tunnel_floor + EYE_METRES),
yaw_to(tunnel_end[0] - tunnel_start[0], tunnel_end[1] - tunnel_start[1])))
compound = rooms["tunnel-network/strate-2/room-19/primitive-0"]
compound_center = xyz(compound["center_um"])
compound_next = rooms["tunnel-network/strate-2/room-20/primitive-0"]
views.append(make_view("planner-compound-room", "surface-world",
(compound_center[0], compound_center[1],
compound["floor_um"] / 1_000_000.0 + EYE_METRES),
yaw_to(compound_next["center_um"][0] - compound["center_um"][0],
compound_next["center_um"][1] - compound["center_um"][1])))
destination = rooms["tunnel-network/strate-2/room-33/primitive-0"]
destination_center = xyz(destination["center_um"])
upstream = rooms["tunnel-network/strate-2/room-32/primitive-0"]
views.append(make_view("planner-destination-chamber", "surface-world",
(destination_center[0], destination_center[1],
destination["floor_um"] / 1_000_000.0 + EYE_METRES),
yaw_to(upstream["center_um"][0] - destination["center_um"][0],
upstream["center_um"][1] - destination["center_um"][1])))
# The compiled legacy network has a deterministic origin chamber at (0, 0).
# Its room floor is hash-rolled from the owner fixture; these are legacy world
# coordinates, independent of the native planner's passage route.
legacy_origin_floor = -819.9169921875
legacy_origin_eye = legacy_origin_floor + EYE_METRES
legacy_neighbor = (39.327854, 5.115777, -797.724731)
views.append(make_view("legacy-origin-room", "legacy-tunnel-network",
(0.0, 0.0, legacy_origin_eye), yaw_to(legacy_neighbor[0], legacy_neighbor[1])))
# Hash-selected (cell 1,0) is the origin graph's nearest connected room. Its
# cut-plane floor estimate is -801.0 m before the authored local relief.
legacy_neighbor_floor = -800.9986572265625
legacy_neighbor_eye = legacy_neighbor_floor + EYE_METRES
views.append(make_view("legacy-connected-room", "legacy-tunnel-network",
(legacy_neighbor[0], legacy_neighbor[1], legacy_neighbor_eye),
yaw_to(-legacy_neighbor[0], -legacy_neighbor[1])))
legacy_tunnel_mid = (legacy_neighbor[0] * 0.5, legacy_neighbor[1] * 0.5,
(-812.0 + legacy_neighbor[2]) * 0.5)
views.append(make_view("legacy-origin-tunnel", "legacy-tunnel-network",
legacy_tunnel_mid, yaw_to(legacy_neighbor[0], legacy_neighbor[1])))
manifest = {"source_stamp": STAMP, "seed": 0,
"renderer": "readable, height tint on, headlamp off; 1600x900",
"views": views}
(OUT / "capture-index.json").write_text(
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
cards = "\n".join(
f'<figure><a href="{Path(str(view["image"])).name}">'
f'<img src="{Path(str(view["image"])).name}" alt="{view["name"]}"></a>'
f'<figcaption>{view["name"]}</figcaption></figure>' for view in views)
(OUT / "COMPARE.html").write_text(
"<!doctype html><meta charset=\"utf-8\"><title>L135 interior views</title>\n"
"<style>body{font:16px sans-serif;background:#202329;color:#eee;margin:24px}"
"h1{font-size:22px}section{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:18px}"
"figure{margin:0}img{width:100%;height:auto;display:block}figcaption{padding:6px 0}</style>\n"
"<h1>Planner and legacy interiors, seed 0</h1><section>\n"
+ cards + "\n</section>\n", encoding="utf-8")
print(f"[L135CaptureSummary] views={len(views)} status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""Check Clang/GCC cave-path pipeline hashes at 1, 8, and 12 workers."""
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/l135"
DATA = ROOT / "Standalone/data/regression/owner_world_2strate_l61.json"
BINARIES = {
"clang": OUT / "bin/VoxelForgeCoreHeadless-clang-l135",
"gcc": OUT / "bin/VoxelForgeCoreHeadless-gcc-l135",
}
CAMERA = "-4.688214,38.662384,-505.781342,-28.446,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 L135 determinism binary or owner fixture")
OUT.mkdir(parents=True, exist_ok=True)
rows = []
baseline: tuple[str, ...] | None = None
stamp: str | None = None
for compiler, binary in BINARIES.items():
for workers in (1, 8, 12):
label = f"determinism-{compiler}-w{workers}"
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=0",
"--max-level=0", f"--camera={CAMERA}", "--stream-hash-tiles",
"--force-generate", "--recipe=surface-world", "--render-distance-chunks=0",
"--landmark-chamber-demo", "--ore-veins-demo"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
result = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
found_stamp = STAMP_RE.search(content)
hashes = tuple(HASH_RE.findall(content))
passed = result.returncode == 0 and found_stamp is not None and len(hashes) >= 4
if found_stamp is not None:
if stamp is None:
stamp = found_stamp.group(1)
passed &= stamp == found_stamp.group(1)
if baseline is None and passed:
baseline = hashes
elif baseline is not None:
passed &= hashes == baseline
row = {"compiler": compiler, "workers": workers,
"exit": result.returncode, "stamp": found_stamp.group(1)
if found_stamp else None,
"hash_lines": len(hashes), "hashes_match_first": hashes == baseline,
"passed": bool(passed), "elapsed_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT))}
rows.append(row)
print("[L135DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
if not passed:
(OUT / "determinism-summary.json").write_text(json.dumps(
{"status": "FAIL", "runs": rows}, indent=2) + "\n")
raise SystemExit(f"determinism mismatch; see {log}")
summary = {"status": "PASS", "stamp": stamp, "runs": rows}
(OUT / "determinism-summary.json").write_text(
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
print(f"[L135DeterminismSummary] runs={len(rows)} stamp={stamp} status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""Collect final-stamp cave, candidate, proof, and startup counters by stratum."""
from __future__ import annotations
import json
import re
import subprocess
import time
from collections import defaultdict
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l135"
BIN = OUT / "bin/VoxelForgeCoreHeadless-clang-l135"
DATA = ROOT / "Standalone/data/owner_world.json"
STAMP = "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5"
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),
}
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
PLAN_RE = re.compile(r"^\[WorldAssemblerPlanTiming\] total_ms=([0-9.]+) status=([^ ]+)$", re.M)
CAVE_RE = re.compile(r"^\[CavernPlannerV1\].* plan_ms=([0-9.]+) status=([^ ]+)$", re.M)
TILE_RE = re.compile(r"^\[WorldAssemblerTilePipeline\] (.*)$", re.M)
CLASS_RE = re.compile(r"^\[WorldAssemblerLODClassification\] (.*)$", re.M)
COMPOSER_RE = re.compile(r"^\[PassageComposerTile\] (.*)$", re.M)
FIELDS_RE = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
def fields(line: str) -> dict[str, str]:
return dict(FIELDS_RE.findall(line))
def main() -> int:
if not BIN.is_file() or not DATA.is_file():
raise SystemExit("missing L135 profile binary or owner fixture")
rows = []
for strate, xyz in CAMERAS.items():
camera = ",".join(f"{value:.12f}" for value in xyz) + ",0,0"
log = OUT / f"profile-final-speed2-s{strate}.log"
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=8",
f"--camera={camera}", "--profile-lod-work", "--profile-passage-composer",
"--stream-scheduler-policy=3"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
result = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
found_stamp = STAMP_RE.search(content)
if result.returncode or found_stamp is None or found_stamp.group(1) != STAMP:
raise SystemExit(f"final-stamp profile failed: {log}, exit={result.returncode}")
plan, cave = PLAN_RE.search(content), CAVE_RE.search(content)
tile = TILE_RE.search(content)
if plan is None or cave is None or tile is None:
raise SystemExit(f"profile counters missing: {log}")
aggregate = defaultdict(lambda: defaultdict(int))
for match in CLASS_RE.finditer(content):
v = fields(match.group(1)); lod = int(v["lod"])
for key in ("tiles", "tile_skips", "density_point_calls", "field_eval_sample_points",
"proof_air_cells", "proof_solid_cells"):
aggregate[lod][key] = int(v.get(key, "0"))
aggregate[lod]["field_eval_ms_est"] = float(v.get("field_eval_ms_est", "0"))
for match in COMPOSER_RE.finditer(content):
v = fields(match.group(1)); lod = int(v["key"].split("lod", 1)[1].rstrip(")"))
aggregate[lod]["composer_tiles"] += 1
for key in ("samples", "native_cave_samples", "room_candidates", "edge_candidates",
"features", "route_candidates", "passage_iterations"):
aggregate[lod][key] += int(v.get(key, "0"))
aggregate[lod]["composer_worker_ms"] += float(v.get("worker_ms", "0"))
for lod, values in aggregate.items():
calls = values.get("density_point_calls", 0)
values["field_eval_ns_per_density_call_est"] = round(
values.get("field_eval_ms_est", 0.0) * 1.0e6 / calls, 1) if calls else None
composer_tiles = values.get("composer_tiles", 0)
values["room_candidates_per_composer_tile"] = round(
values.get("room_candidates", 0) / composer_tiles, 3) if composer_tiles else None
values["edge_candidates_per_composer_tile"] = round(
values.get("edge_candidates", 0) / composer_tiles, 3) if composer_tiles else None
tile_fields = fields(tile.group(1))
tile_summary = {}
for key, value in tile_fields.items():
try:
tile_summary[key] = int(value)
except ValueError:
try:
tile_summary[key] = float(value)
except ValueError:
continue
row = {"strate": strate, "source_stamp": found_stamp.group(1),
"world_assembler_plan_ms": float(plan.group(1)),
"cavern_planner_ms": float(cave.group(1)),
"tile_pipeline": tile_summary,
"lod": {str(k): dict(v) for k, v in sorted(aggregate.items())},
"wall_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT))}
rows.append(row)
print("[L135ProfileRun] " + json.dumps(row, sort_keys=True), flush=True)
summary = {"status": "PASS", "source_stamp": STAMP, "strata": rows,
"field_eval_ns_per_density_call_note":
"Estimate includes all shape-field density calls at this view, not native-cave-exclusive time.",
"tile_skip_note":
"tile_pipeline.whole_tile_skips and lod.tile_skips are aggregate classifiers; they are not cave-only attribution."}
(OUT / "profile-summary-speed2.json").write_text(
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
print("[L135ProfileSummary] strata=4 status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""Run the L135 composed cave-passage and tile-seam audits for fixed seeds."""
from __future__ import annotations
import json
import re
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l135"
BIN = OUT / "bin/VoxelForgeCoreHeadless-clang-l135"
DATA = ROOT / "Standalone/data/owner_world.json"
REQUIRED = {
"passage": re.compile(r"^\[ResolvedPassageAuditSummary\].*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),
"route_negative_control": re.compile(
r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
}
def main() -> int:
seeds = [int(value) for value in sys.argv[1:]] or list(range(16))
if not BIN.is_file() or not DATA.is_file():
raise SystemExit("missing L135 headless binary or owner fixture")
results = []
for seed in seeds:
log = OUT / f"optimized-seed{seed}.log"
layout = OUT / f"optimized-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",
"--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:
result = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
blocked_controls = re.findall(
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)
checks["three_blocked_controls"] = len(blocked_controls) == 3
passed = result.returncode == 0 and all(checks.values())
row = {"seed": seed, "exit": result.returncode, "passed": passed,
"checks": checks, "blocked_controls": len(blocked_controls),
"elapsed_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
results.append(row)
print("[L135SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
if not passed:
raise SystemExit(f"seed {seed} failed a composed audit; see {log}")
summary = {"seeds": seeds, "passed": len(results), "failed": 0,
"results": results}
(OUT / "optimized-seed-sweep.json").write_text(
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
print(f"[L135SeedSweepSummary] passed={len(results)}/{len(seeds)} status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""Five interleaved L80 timings against assembler/world and the L134 revision."""
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/l135/timing"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"assembler-world": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang",
"a3c8e36-v230": ROOT / "Saved/Linux/l134/bin/VoxelForgeCoreHeadless-l134-clang",
"l135-v236": ROOT / "Saved/Linux/l135/bin/VoxelForgeCoreHeadless-clang-l135",
}
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 = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
FIELDS_RE = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
NEAR_RE = re.compile(
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
re.M)
PLAN_RE = re.compile(r"^\[WorldAssemblerPlanTiming\] total_ms=([0-9.]+) status=([^ ]+)$", re.M)
CAVE_RE = re.compile(r"^\[CavernPlannerV1\].* plan_ms=([0-9.]+) status=([^ ]+)$", re.M)
def run(strate: int, workers: int, version: str, phase: str,
replicate: int) -> dict:
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}-l135"
log = OUT / f"{label}.log"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[version]), 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:
result = 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 = PIPELINE_RE.search(content), NEAR_RE.search(content)
if result.returncode or pipeline is None or near is None:
raise RuntimeError(f"timing run failed: {log}; exit={result.returncode}")
fields = dict(FIELDS_RE.findall(pipeline.group(1)))
plan, cave = PLAN_RE.search(content), CAVE_RE.search(content)
row = {"strate": strate, "workers": workers, "version": version,
"phase": phase, "replicate": replicate,
"lod02_ready_s": float(near.group(3)),
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
"plan_ms": float(plan.group(1)) if plan else None,
"cave_plan_ms": float(cave.group(1)) if cave else None,
"wall_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT))}
print("[L135TimingRun] " + 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, *BINARIES.values())):
raise SystemExit("missing L135 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"[L135TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True)
if load_1m >= 0.9 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:
rows.append(run(strate, workers, version, "warmup", 0))
for replicate in range(1, 6):
order = tuple(BINARIES) if replicate % 2 else tuple(reversed(BINARIES))
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):
item = {"strate": strate, "workers": workers,
"replicates_per_version": 5}
medians = {}
for version in BINARIES:
group = [row for row in measured if row["strate"] == strate
and row["workers"] == workers and row["version"] == version]
medians[version] = {}
for key in ("lod02_ready_s", "whole_view_ready_s"):
values = [row[key] for row in group]
medians[version][key] = statistics.median(values)
item[f"{version}_{key}_values"] = values
item[f"{version}_{key}_median"] = medians[version][key]
for baseline in ("assembler-world", "a3c8e36-v230"):
for key in ("lod02_ready_s", "whole_view_ready_s"):
item[f"vs_{baseline}_{key}_delta_pct"] = round(
100.0 * (medians["l135-v236"][key]
/ medians[baseline][key] - 1.0), 2)
summary.append(item)
(OUT / "summary.json").write_text(json.dumps(
{"runs": rows, "summary": summary}, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,11 @@
<!doctype html><meta charset="utf-8"><title>L135 interior views</title>
<style>body{font:16px sans-serif;background:#202329;color:#eee;margin:24px}h1{font-size:22px}section{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:18px}figure{margin:0}img{width:100%;height:auto;display:block}figcaption{padding:6px 0}</style>
<h1>Planner and legacy interiors, seed 0</h1><section>
<figure><a href="planner-arrival-room.png"><img src="planner-arrival-room.png" alt="planner-arrival-room"></a><figcaption>planner-arrival-room</figcaption></figure>
<figure><a href="planner-tunnel.png"><img src="planner-tunnel.png" alt="planner-tunnel"></a><figcaption>planner-tunnel</figcaption></figure>
<figure><a href="planner-compound-room.png"><img src="planner-compound-room.png" alt="planner-compound-room"></a><figcaption>planner-compound-room</figcaption></figure>
<figure><a href="planner-destination-chamber.png"><img src="planner-destination-chamber.png" alt="planner-destination-chamber"></a><figcaption>planner-destination-chamber</figcaption></figure>
<figure><a href="legacy-origin-room.png"><img src="legacy-origin-room.png" alt="legacy-origin-room"></a><figcaption>legacy-origin-room</figcaption></figure>
<figure><a href="legacy-connected-room.png"><img src="legacy-connected-room.png" alt="legacy-connected-room"></a><figcaption>legacy-connected-room</figcaption></figure>
<figure><a href="legacy-origin-tunnel.png"><img src="legacy-origin-tunnel.png" alt="legacy-origin-tunnel"></a><figcaption>legacy-origin-tunnel</figcaption></figure>
</section>
@@ -0,0 +1,119 @@
{
"source_stamp": "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5",
"seed": 0,
"renderer": "readable, height tint on, headlamp off; 1600x900",
"views": [
{
"name": "planner-arrival-room",
"recipe": "surface-world",
"camera_m": [
35.593114,
-21.55801,
-755.864011
],
"yaw_deg": 114.47628370565772,
"camera_height_basis": "floor + player eye offset",
"eye_offset_m": 1.6,
"mesh": "Saved/Linux/l135/capture-planner-arrival-room.vfmesh",
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-arrival-room.png",
"mesh_log": "Saved/Linux/l135/capture-planner-arrival-room-mesh.log",
"render_log": "Saved/Linux/l135/capture-planner-arrival-room-render.log"
},
{
"name": "planner-tunnel",
"recipe": "surface-world",
"camera_m": [
42.874177,
-24.8725425,
-757.112565
],
"yaw_deg": 114.47628370565774,
"camera_height_basis": "floor + player eye offset",
"eye_offset_m": 1.6,
"mesh": "Saved/Linux/l135/capture-planner-tunnel.vfmesh",
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-tunnel.png",
"mesh_log": "Saved/Linux/l135/capture-planner-tunnel-mesh.log",
"render_log": "Saved/Linux/l135/capture-planner-tunnel-render.log"
},
{
"name": "planner-compound-room",
"recipe": "surface-world",
"camera_m": [
-39.519627,
-52.685596,
-785.85419
],
"yaw_deg": -74.782578137959,
"camera_height_basis": "floor + player eye offset",
"eye_offset_m": 1.6,
"mesh": "Saved/Linux/l135/capture-planner-compound-room.vfmesh",
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-compound-room.png",
"mesh_log": "Saved/Linux/l135/capture-planner-compound-room-mesh.log",
"render_log": "Saved/Linux/l135/capture-planner-compound-room-render.log"
},
{
"name": "planner-destination-chamber",
"recipe": "surface-world",
"camera_m": [
-28.634259,
-4.85816,
-810.5142579999999
],
"yaw_deg": -89.33032962272898,
"camera_height_basis": "floor + player eye offset",
"eye_offset_m": 1.6,
"mesh": "Saved/Linux/l135/capture-planner-destination-chamber.vfmesh",
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-destination-chamber.png",
"mesh_log": "Saved/Linux/l135/capture-planner-destination-chamber-mesh.log",
"render_log": "Saved/Linux/l135/capture-planner-destination-chamber-render.log"
},
{
"name": "legacy-origin-room",
"recipe": "legacy-tunnel-network",
"camera_m": [
0.0,
0.0,
-818.3169921875
],
"yaw_deg": 82.58856646117444,
"camera_height_basis": "legacy room floor + player eye offset",
"eye_offset_m": 1.6,
"mesh": "Saved/Linux/l135/capture-legacy-origin-room.vfmesh",
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/legacy-origin-room.png",
"mesh_log": "Saved/Linux/l135/capture-legacy-origin-room-mesh.log",
"render_log": "Saved/Linux/l135/capture-legacy-origin-room-render.log"
},
{
"name": "legacy-connected-room",
"recipe": "legacy-tunnel-network",
"camera_m": [
39.327854,
5.115777,
-799.3986572265625
],
"yaw_deg": -97.41143353882556,
"camera_height_basis": "legacy room floor + player eye offset",
"eye_offset_m": 1.6,
"mesh": "Saved/Linux/l135/capture-legacy-connected-room.vfmesh",
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/legacy-connected-room.png",
"mesh_log": "Saved/Linux/l135/capture-legacy-connected-room-mesh.log",
"render_log": "Saved/Linux/l135/capture-legacy-connected-room-render.log"
},
{
"name": "legacy-origin-tunnel",
"recipe": "legacy-tunnel-network",
"camera_m": [
19.663927,
2.5578885,
-804.8623655
],
"yaw_deg": 82.58856646117444,
"camera_height_basis": "near the legacy tunnel centerline",
"eye_offset_m": null,
"mesh": "Saved/Linux/l135/capture-legacy-origin-tunnel.vfmesh",
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/legacy-origin-tunnel.png",
"mesh_log": "Saved/Linux/l135/capture-legacy-origin-tunnel-mesh.log",
"render_log": "Saved/Linux/l135/capture-legacy-origin-tunnel-render.log"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

+124 -37
View File
@@ -3176,13 +3176,15 @@ namespace StandaloneStreamBenchmark
const auto passageUpperMouthFloorDepth=[&](
const VoxelForgeCore::ResolvedLayoutPassageV1& passage)
{
const double controlZ=qToMetres(
passage.controlPoints.front().positionSubvoxels[2]);
const double authoredMouthDepth=std::max(0.0,controlZ
-qToMetres(passage.upperMouthSubvoxels[2])+1.13);
const auto upperStratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.upperStrateIndex;});
const double mouthCenterHeight=upperStratum!=ResolvedLayout->strates.end()
&&upperStratum->generatorKind=="SurfaceWorld"?0.98:1.13;
const double controlZ=qToMetres(
passage.controlPoints.front().positionSubvoxels[2]);
const double authoredMouthDepth=std::max(0.0,controlZ
-qToMetres(passage.upperMouthSubvoxels[2])+mouthCenterHeight);
if(upperStratum==ResolvedLayout->strates.end()
||(upperStratum->generatorKind!="FloatingIslands"
&&upperStratum->generatorKind!="TunnelNetwork"))
@@ -3202,7 +3204,8 @@ namespace StandaloneStreamBenchmark
||distanceSquared>std::pow(
qToMetres(passage.controlPoints.front().radiusSubvoxels)+2.0,2.0))
return authoredMouthDepth;
const double routeAlignedDepth=std::max(0.0,controlZ-routeHeight+1.13);
const double routeAlignedDepth=std::max(0.0,
controlZ-routeHeight+mouthCenterHeight);
return std::max(authoredMouthDepth,routeAlignedDepth);
};
const auto passageSplineControls=[&](
@@ -3349,61 +3352,124 @@ namespace StandaloneStreamBenchmark
};
std::vector<double> allPassageBodyFloorDepths;
std::vector<std::vector<FPassageAuditRoutePoint>> allPassageSmoothRoutes;
struct FUpperMouthFloorBlend
struct FMouthFloorBlend
{
std::vector<std::array<double,3>> receivingRoute;
double mouthX=0.0,mouthY=0.0,mouthFloor=0.0;
double tangentX=0.0,tangentY=0.0;
};
std::vector<FUpperMouthFloorBlend> allUpperMouthFloorBlends;
struct FPassageFloorBlends
{
FMouthFloorBlend upper,lower;
};
std::vector<FPassageFloorBlends> allPassageFloorBlends;
allPassageBodyFloorDepths.reserve(ResolvedLayout->passages.size());
allPassageSmoothRoutes.reserve(ResolvedLayout->passages.size());
allUpperMouthFloorBlends.reserve(ResolvedLayout->passages.size());
allPassageFloorBlends.reserve(ResolvedLayout->passages.size());
for(const auto& passage:ResolvedLayout->passages)
{
const double bodyDepth=passageBodyFloorDepth(passage);
allPassageBodyFloorDepths.push_back(bodyDepth);
allPassageSmoothRoutes.push_back(passageSmoothRoute(passage,bodyDepth));
FUpperMouthFloorBlend blend;
FPassageFloorBlends blends;
const auto controls=passageSplineControls(passage);
const auto floorProfile=passageSplineFloorProfile(passage);
const std::size_t routeEnd=std::clamp<std::size_t>(
passage.lowerMouthControlIndex,1,passage.controlPoints.size()-1);
const auto segments=VoxelForgeCore::PassageSpline::BuildSegments(
controls,routeEnd,8.0,1.0,floorProfile,0.100);
const auto upperStratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.upperStrateIndex;});
if(upperStratum!=ResolvedLayout->strates.end()
&&upperStratum->generatorKind=="FloatingIslands")
&&(upperStratum->generatorKind=="FloatingIslands"
||upperStratum->generatorKind=="TunnelNetwork"))
{
for(const auto& anchor:upperStratum->anchors)
if(anchor.kind=="route_point"&&anchor.values.size()>=3)
blend.receivingRoute.push_back({
blends.upper.receivingRoute.push_back({
qToMetres(anchor.values[0]),qToMetres(anchor.values[1]),
qToMetres(anchor.values[2])});
const auto controls=passageSplineControls(passage);
const auto floorProfile=passageSplineFloorProfile(passage);
const std::size_t routeEnd=std::clamp<std::size_t>(
passage.lowerMouthControlIndex,1,passage.controlPoints.size()-1);
const auto segments=VoxelForgeCore::PassageSpline::BuildSegments(
controls,routeEnd,8.0,1.0,floorProfile,0.100);
if(upperStratum->generatorKind=="TunnelNetwork"
&&blends.upper.receivingRoute.size()>2)
blends.upper.receivingRoute.erase(
blends.upper.receivingRoute.begin(),
blends.upper.receivingRoute.end()-2);
if(!segments.empty())
{
blend.mouthX=segments.front().a[0];
blend.mouthY=segments.front().a[1];
blend.mouthFloor=floorProfile.front();
blend.tangentX=segments.front().b[0]-segments.front().a[0];
blend.tangentY=segments.front().b[1]-segments.front().a[1];
blends.upper.mouthX=controls.front()[0];
blends.upper.mouthY=controls.front()[1];
blends.upper.mouthFloor=floorProfile.front();
blends.upper.tangentX=segments.front().b[0]-segments.front().a[0];
blends.upper.tangentY=segments.front().b[1]-segments.front().a[1];
}
if(upperStratum->generatorKind=="TunnelNetwork")
for(const auto& edge:ResolvedLayout->cavernEdges)
if(edge.startAnchorStableId==passage.stableId+"/upper-mouth"
&&edge.controlPointsMicrometres.size()>=2)
{
blends.upper.tangentX=
(edge.controlPointsMicrometres[0][0]
-edge.controlPointsMicrometres[1][0])/1000000.0;
blends.upper.tangentY=
(edge.controlPointsMicrometres[0][1]
-edge.controlPointsMicrometres[1][1])/1000000.0;
}
}
allUpperMouthFloorBlends.push_back(std::move(blend));
const auto lowerStratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.lowerStrateIndex;});
if(lowerStratum!=ResolvedLayout->strates.end()
&&lowerStratum->generatorKind=="TunnelNetwork"
&&!controls.empty()&&routeEnd<controls.size())
{
for(const auto& anchor:lowerStratum->anchors)
if(anchor.kind=="route_point"&&anchor.values.size()>=3)
blends.lower.receivingRoute.push_back({
qToMetres(anchor.values[0]),qToMetres(anchor.values[1]),
qToMetres(anchor.values[2])});
if(blends.lower.receivingRoute.size()>2)
blends.lower.receivingRoute.resize(2);
blends.lower.mouthX=controls[routeEnd][0];
blends.lower.mouthY=controls[routeEnd][1];
blends.lower.mouthFloor=floorProfile[routeEnd];
if(!segments.empty())
{
blends.lower.tangentX=controls[routeEnd][0]
-controls[routeEnd-1][0];
blends.lower.tangentY=controls[routeEnd][1]
-controls[routeEnd-1][1];
}
for(const auto& edge:ResolvedLayout->cavernEdges)
if(edge.startAnchorStableId==passage.stableId+"/lower-mouth"
&&edge.controlPointsMicrometres.size()>=2)
{
blends.lower.tangentX=
(edge.controlPointsMicrometres[1][0]
-edge.controlPointsMicrometres[0][0])/1000000.0;
blends.lower.tangentY=
(edge.controlPointsMicrometres[1][1]
-edge.controlPointsMicrometres[0][1])/1000000.0;
}
}
allPassageFloorBlends.push_back(std::move(blends));
}
const auto passageFloorAtXY=[&](std::size_t passageIndex,double x,double y,
double plannedFloor)
{
if(passageIndex>=allUpperMouthFloorBlends.size())return plannedFloor;
const auto& blend=allUpperMouthFloorBlends[passageIndex];
if(passageIndex>=allPassageFloorBlends.size())return plannedFloor;
const auto& blends=allPassageFloorBlends[passageIndex];
double floor=plannedFloor;
floor=VoxelForgeCore::PassageSpline::BlendUpperMouthFloorAtXY(
blends.upper.receivingRoute,x,y,floor,blends.upper.mouthX,
blends.upper.mouthY,blends.upper.mouthFloor,
blends.upper.tangentX,blends.upper.tangentY,
VoxelForgeCore::PassageSpline::UpperMouthFloorBlendRadiusMetres,1.13);
return VoxelForgeCore::PassageSpline::BlendUpperMouthFloorAtXY(
blend.receivingRoute,x,y,plannedFloor,blend.mouthX,blend.mouthY,
blend.mouthFloor,
blend.tangentX,blend.tangentY,
VoxelForgeCore::PassageSpline::UpperMouthFloorBlendRadiusMetres,
1.13);
blends.lower.receivingRoute,x,y,floor,blends.lower.mouthX,
blends.lower.mouthY,blends.lower.mouthFloor,
-blends.lower.tangentX,-blends.lower.tangentY,
VoxelForgeCore::PassageSpline::UpperMouthFloorBlendRadiusMetres,1.13);
};
const auto interpolatePassageRoute=[](
const std::vector<FPassageAuditRoutePoint>& route,double distance)
@@ -4361,7 +4427,8 @@ namespace StandaloneStreamBenchmark
// The probes sit 0.18 m inside the authored radius. Add that
// radius back to the signed wall distance so radius-carried ribs
// remain visible; the cubic detrend removes the smooth mouth flare.
radii.push_back(sampleRadius+0.5*(left+right));
const double measuredRadius=sampleRadius+0.5*(left+right);
radii.push_back(measuredRadius);
++audit.samples;
}
if(radii.size()<static_cast<std::size_t>(WindowLengthMetres/
@@ -4494,6 +4561,18 @@ namespace StandaloneStreamBenchmark
constexpr double PassageShellAuditSpacingMetres=0.25;
constexpr std::array<double,2> PassageShellAuditDepthsMetres{{0.125,0.375}};
constexpr float PassageShellDensityTolerance=1.0e-6f;
std::vector<bool> surfaceLandingPassages(ResolvedLayout->passages.size(),false);
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
++passageIndex)
{
const auto& passage=ResolvedLayout->passages[passageIndex];
const auto upper=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[&](const auto& stratum)
{return stratum.strateIndex==passage.upperStrateIndex;});
surfaceLandingPassages[passageIndex]=upper!=ResolvedLayout->strates.end()
&&upper->generatorKind=="SurfaceWorld"
&&upper->lowerInterfaceCapabilities.surfaceToTunnelLandingSupport;
}
const auto expectedPassageShellAt=[&](double xMetres,double yMetres,
double zMetres)
{
@@ -4639,7 +4718,17 @@ namespace StandaloneStreamBenchmark
const double floorBlend=std::clamp(floorDistance/0.25,0.0,1.0);
const double smoothFloorBlend=floorBlend*floorBlend
*(3.0-2.0*floorBlend);
const double thickness=0.75*smoothFade*smoothFloorBlend;
double thickness=0.75*smoothFade*smoothFloorBlend;
if(candidatePassage<surfaceLandingPassages.size()
&&surfaceLandingPassages[candidatePassage])
{
const double landingFade=std::clamp(1.0-along/8.0,
0.0,1.0);
const double smoothLandingFade=landingFade*landingFade
*(3.0-2.0*landingFade);
thickness=std::max(thickness,
0.50*smoothLandingFade*smoothFloorBlend);
}
const double shellDistance=std::max(-flatDistance,
flatDistance-thickness);
minimumShellDistance=std::min(minimumShellDistance,
@@ -5137,11 +5226,9 @@ namespace StandaloneStreamBenchmark
{
if(stratum.generatorKind!="TunnelNetwork")
return equalPosition(authored,mouth);
auto expected=mouth;
expected[2]+=static_cast<std::int64_t>(std::llround(
1130000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
/World.CoreVoxelSizeMicrons));
return equalPosition(authored,expected);
// Passage mouths already encode the player center 1.13 m above
// their floor, matching the route-point convention directly.
return equalPosition(authored,mouth);
};
for(const auto& passage:ResolvedLayout->passages)
{
+4 -1
View File
@@ -1580,10 +1580,13 @@ private:
static_cast<unsigned long long>(D.islandSharedCacheConflictReplacements),
static_cast<double>(D.islandSharedCacheLockWaitNanoseconds)/1000.0);
if(D.passageComposerProfiled)
std::printf("[PassageComposerTile] key=(%d,%d,%d,lod%d) worker_ms=%.3f samples=%llu native_field_calls=%llu features=%llu route_candidates=%llu boundary_band_queries=%llu queries=%llu passage_iterations=%llu carve_segment_visits=%llu relief_field_calls=%llu sweep_segments=%llu sweep_pairs=%llu sweep_prepare_ms=%.3f sweep_outside_pairs=%llu sweep_shell_support_skips=%llu sweep_min_updates=%llu sweep_base_samples=%llu sweep_detail_samples=%llu sweep_grid_samples=%llu sweep_no_influence_samples=%llu sweep_lookups=%llu sweep_cache_hits=%llu sweep_base_hits=%llu sweep_outside_hits=%llu sweep_missing=%llu\n",
std::printf("[PassageComposerTile] key=(%d,%d,%d,lod%d) worker_ms=%.3f samples=%llu field_calls=%llu native_cave_samples=%llu room_candidates=%llu edge_candidates=%llu features=%llu route_candidates=%llu boundary_band_queries=%llu queries=%llu passage_iterations=%llu carve_segment_visits=%llu relief_field_calls=%llu sweep_segments=%llu sweep_pairs=%llu sweep_prepare_ms=%.3f sweep_outside_pairs=%llu sweep_shell_support_skips=%llu sweep_min_updates=%llu sweep_base_samples=%llu sweep_detail_samples=%llu sweep_grid_samples=%llu sweep_no_influence_samples=%llu sweep_lookups=%llu sweep_cache_hits=%llu sweep_base_hits=%llu sweep_outside_hits=%llu sweep_missing=%llu\n",
Event.Key.Coord.X,Event.Key.Coord.Y,Event.Key.Coord.Z,Event.Key.Level,
Event.WorkerMilliseconds,static_cast<unsigned long long>(Event.DensitySamples),
static_cast<unsigned long long>(D.fieldCalls),
static_cast<unsigned long long>(D.nativeCaveFieldSamples),
static_cast<unsigned long long>(D.cavernRoomCandidates),
static_cast<unsigned long long>(D.cavernEdgeCandidates),
static_cast<unsigned long long>(D.passageTileFeatureCount),
static_cast<unsigned long long>(D.passageTileRouteSegmentCandidates),
static_cast<unsigned long long>(D.resolvedBoundaryBandQueries),
+466 -8
View File
@@ -11,9 +11,11 @@
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
#include <numeric>
#include <queue>
#include <string>
#include <vector>
@@ -283,6 +285,330 @@ namespace StandaloneWorldAssemblyPrototype
}
}
}
std::uint64_t terrainPathCount=0,terrainGridSamples=0;
std::int32_t terrainGridStepQ=0;
const auto appendTerrainFallback=[&](std::int32_t stepQ)
{
if(!fallbackSearch
||!((arrivalRadiusSubvoxels>0)!=(departureRadiusSubvoxels>0)))
return;
{
// A bounded one-mouth world can have no walkable straight chord even
// when a contour route exists. Link real ground columns only when their
// grade meets the same limit, then use the ordinary capsule and support
// sweep to accept or reject each resulting path.
struct FGroundNode
{
std::int64_t ground=0;
bool supportReachable=false;
};
struct FQueueEntry
{
std::uint64_t cost=0;
std::size_t index=0;
bool operator>(const FQueueEntry& other) const
{return cost>other.cost||(cost==other.cost&&index>other.index);}
};
constexpr std::array<std::array<std::int64_t,2>,5> supportOffsets{{
{{0,0}},{{ProbeRadiusQ,0}},{{-ProbeRadiusQ,0}},
{{0,ProbeRadiusQ}},{{0,-ProbeRadiusQ}}}};
constexpr std::int64_t SupportStepQ=WorldAssemblySubvoxelsPerVoxel/4;
const std::int64_t routeBaseLiftQ=constraints.minimumClearanceSubvoxels/4;
const auto hasRouteSupportAt=[&](std::int64_t centerX,std::int64_t centerY,
std::int64_t footZ)
{
std::uint32_t reachable=0;
for(const auto& offset:supportOffsets)
{
for(std::int64_t depth=SupportStepQ;depth<=SupportDepthQ;
depth+=SupportStepQ)
{
if(finalField.point(centerX+offset[0],centerY+offset[1],
footZ-depth).densitySubvoxels<0)
{++reachable;break;}
}
}
return reachable>=constraints.minimumSupportProbes;
};
const auto tryTerrainPaths=[&](std::int32_t terrainStepQ)
{
terrainPathCount=0;terrainGridSamples=0;terrainGridStepQ=0;
const std::int64_t boundedRadius=std::max(arrivalRadiusSubvoxels,
departureRadiusSubvoxels);
const std::int32_t boundRadiusVoxels=static_cast<std::int32_t>(
(boundedRadius+WorldAssemblySubvoxelsPerVoxel-1)
/WorldAssemblySubvoxelsPerVoxel);
const std::int32_t searchRadiusVoxels=std::clamp(
boundRadiusVoxels+64,64,256);
const std::int64_t searchRadiusQ=static_cast<std::int64_t>(
searchRadiusVoxels)*WorldAssemblySubvoxelsPerVoxel;
const std::int32_t radiusSteps=static_cast<std::int32_t>(
searchRadiusQ/terrainStepQ);
const std::int32_t dimension=radiusSteps*2+1;
const std::size_t nodeCount=static_cast<std::size_t>(dimension)*dimension;
const auto indexOf=[dimension](std::int32_t x,std::int32_t y)
{return static_cast<std::size_t>(y)*dimension+x;};
std::vector<FGroundNode> grid(nodeCount);
const auto safeGroundAt=[&](std::int64_t centerX,std::int64_t centerY)
{
constexpr std::int64_t FootprintStepQ=WorldAssemblySubvoxelsPerVoxel/2;
std::int64_t highestGround=std::numeric_limits<std::int64_t>::min();
for(std::int64_t oy=-PlayerRadiusQ;oy<=PlayerRadiusQ;
oy+=FootprintStepQ)
for(std::int64_t ox=-PlayerRadiusQ;ox<=PlayerRadiusQ;
ox+=FootprintStepQ)
{
if(ox*ox+oy*oy>PlayerRadiusQ*PlayerRadiusQ)continue;
highestGround=std::max(highestGround,finalField.point(
centerX+ox,centerY+oy,0).groundHeightSubvoxels);
}
return FGroundNode{highestGround,
hasRouteSupportAt(centerX,centerY,
highestGround+routeBaseLiftQ)};
};
for(std::int32_t gy=0;gy<dimension;++gy)
for(std::int32_t gx=0;gx<dimension;++gx)
{
const std::int64_t xQ=static_cast<std::int64_t>(gx-radiusSteps)
*terrainStepQ;
const std::int64_t yQ=static_cast<std::int64_t>(gy-radiusSteps)
*terrainStepQ;
grid[indexOf(gx,gy)]=safeGroundAt(xQ,yQ);
++terrainGridSamples;
}
for(std::size_t directionIndex=0;directionIndex<1;++directionIndex)
{
std::vector<std::uint64_t> costs(nodeCount,
std::numeric_limits<std::uint64_t>::max());
std::vector<std::size_t> parent(nodeCount,nodeCount);
std::priority_queue<FQueueEntry,std::vector<FQueueEntry>,
std::greater<FQueueEntry>> open;
std::vector<std::int8_t> edgeSupportCache(nodeCount*4,-1);
for(std::int32_t gy=0;gy<dimension;++gy)
for(std::int32_t gx=0;gx<dimension;++gx)
{
if(gx!=0&&gy!=0&&gx!=dimension-1&&gy!=dimension-1)continue;
const std::size_t start=indexOf(gx,gy);
if(!grid[start].supportReachable)continue;
costs[start]=0;
open.push({0,start});
}
if(open.empty())continue;
std::size_t goal=nodeCount;
std::uint64_t goalCost=std::numeric_limits<std::uint64_t>::max();
std::uint64_t goalDistanceSquared=std::numeric_limits<std::uint64_t>::max();
while(!open.empty())
{
const FQueueEntry current=open.top();open.pop();
if(current.cost!=costs[current.index])continue;
if(goalCost!=std::numeric_limits<std::uint64_t>::max()
&&current.cost>goalCost)break;
const std::int32_t gx=static_cast<std::int32_t>(current.index%dimension);
const std::int32_t gy=static_cast<std::int32_t>(current.index/dimension);
if(parent[current.index]!=nodeCount)
{
const std::int64_t xQ=static_cast<std::int64_t>(gx-radiusSteps)
*terrainStepQ;
const std::int64_t yQ=static_cast<std::int64_t>(gy-radiusSteps)
*terrainStepQ;
const std::uint64_t distanceSquared=static_cast<std::uint64_t>(
xQ*xQ+yQ*yQ);
if(distanceSquared<=static_cast<std::uint64_t>(
boundedRadius*boundedRadius)
&&(current.cost<goalCost||(current.cost==goalCost
&&distanceSquared<goalDistanceSquared)))
{goal=current.index;goalCost=current.cost;
goalDistanceSquared=distanceSquared;}
}
for(std::int32_t oy=-1;oy<=1;++oy)
for(std::int32_t ox=-1;ox<=1;++ox)
{
if(ox==0&&oy==0)continue;
const std::int32_t nx=gx+ox,ny=gy+oy;
if(nx<0||ny<0||nx>=dimension||ny>=dimension)continue;
const std::size_t next=indexOf(nx,ny);
if(!grid[next].supportReachable)continue;
const std::int64_t horizontalQ=static_cast<std::int64_t>(
std::llround(terrainStepQ*std::hypot(static_cast<double>(ox),
static_cast<double>(oy))));
const std::int64_t riseQ=std::abs(grid[next].ground
-grid[current.index].ground);
if(horizontalQ<=0||riseQ*65536>horizontalQ*GradeQ16)continue;
const std::int32_t edgeDx=nx-gx,edgeDy=ny-gy;
std::int32_t baseX=gx,baseY=gy,edgeDirection=0;
if(edgeDx==0)
{baseY=std::min(gy,ny);edgeDirection=1;}
else if(edgeDy==0)
{baseX=std::min(gx,nx);edgeDirection=0;}
else if(edgeDx==edgeDy)
{baseX=std::min(gx,nx);baseY=std::min(gy,ny);edgeDirection=2;}
else
{baseX=std::max(gx,nx);baseY=std::min(gy,ny);edgeDirection=3;}
const std::size_t edgeCacheIndex=indexOf(baseX,baseY)*4
+static_cast<std::size_t>(edgeDirection);
if(edgeSupportCache[edgeCacheIndex]<0)
{
const std::uint64_t intervals=std::max<std::uint64_t>(2,
(static_cast<std::uint64_t>(horizontalQ)
+WorldAssemblySubvoxelsPerVoxel/8-1)
/(WorldAssemblySubvoxelsPerVoxel/8));
bool supported=true;
const std::int64_t fromX=static_cast<std::int64_t>(
gx-radiusSteps)*terrainStepQ;
const std::int64_t fromY=static_cast<std::int64_t>(
gy-radiusSteps)*terrainStepQ;
const std::int64_t toX=static_cast<std::int64_t>(
nx-radiusSteps)*terrainStepQ;
const std::int64_t toY=static_cast<std::int64_t>(
ny-radiusSteps)*terrainStepQ;
for(std::uint64_t sample=1;sample<intervals;++sample)
{
const std::int64_t x=fromX+(toX-fromX)
*static_cast<std::int64_t>(sample)
/static_cast<std::int64_t>(intervals);
const std::int64_t y=fromY+(toY-fromY)
*static_cast<std::int64_t>(sample)
/static_cast<std::int64_t>(intervals);
const std::int64_t footZ=grid[current.index].ground
+(grid[next].ground-grid[current.index].ground)
*static_cast<std::int64_t>(sample)
/static_cast<std::int64_t>(intervals)
+routeBaseLiftQ;
if(!hasRouteSupportAt(x,y,footZ))
{supported=false;break;}
}
edgeSupportCache[edgeCacheIndex]=supported?1:0;
}
if(edgeSupportCache[edgeCacheIndex]==0)continue;
const std::uint64_t edgeCost=static_cast<std::uint64_t>(
horizontalQ+riseQ*4);
if(current.cost>std::numeric_limits<std::uint64_t>::max()-edgeCost)
continue;
const std::uint64_t nextCost=current.cost+edgeCost;
if(nextCost>=costs[next])continue;
costs[next]=nextCost;parent[next]=current.index;
open.push({nextCost,next});
}
}
if(goal==nodeCount)continue;
std::vector<std::size_t> path;
for(std::size_t cursor=goal;cursor!=nodeCount;cursor=parent[cursor])
{
path.push_back(cursor);
if(parent[cursor]==nodeCount)break;
if(path.size()>nodeCount)break;
}
if(path.size()<2||parent[path.back()]!=nodeCount)continue;
std::reverse(path.begin(),path.end());
if(arrivalRadiusSubvoxels>0)
std::reverse(path.begin(),path.end());
Candidate candidate;
candidate.centerX=static_cast<std::int32_t>(
(static_cast<std::int32_t>(path.front()%dimension)-radiusSteps)
*terrainStepQ);
candidate.centerY=static_cast<std::int32_t>(
(static_cast<std::int32_t>(path.front()/dimension)-radiusSteps)
*terrainStepQ);
candidate.direction=static_cast<std::int32_t>(directionIndex);
candidate.score=static_cast<std::int64_t>(goalCost/path.size());
candidate.endpointProximityCost=goalDistanceSquared
*(arrivalRadiusSubvoxels>0?16ull:1ull);
candidate.points.reserve(path.size());
bool gradePass=true;
for(std::size_t pointIndex=0;pointIndex<path.size();++pointIndex)
{
const std::size_t index=path[pointIndex];
const std::int32_t gx=static_cast<std::int32_t>(index%dimension);
const std::int32_t gy=static_cast<std::int32_t>(index/dimension);
const std::int64_t xQ=static_cast<std::int64_t>(gx-radiusSteps)
*terrainStepQ;
const std::int64_t yQ=static_cast<std::int64_t>(gy-radiusSteps)
*terrainStepQ;
WorldAssemblyRoutePointV1 point;
point.positionSubvoxels={xQ,yQ,
grid[index].ground+routeBaseLiftQ+HalfHeightQ};
point.radiusSubvoxels=RouteRadiusQ;
point.stableFeatureId=WorldAssemblyFeatureId(recipe.seed,
xQ,yQ,
0x52550000u+static_cast<std::uint32_t>(pointIndex));
if(pointIndex>0)
{
const auto& previous=candidate.points.back().positionSubvoxels;
const std::int64_t dx=point.positionSubvoxels[0]-previous[0];
const std::int64_t dy=point.positionSubvoxels[1]-previous[1];
const std::int64_t dz=std::abs(point.positionSubvoxels[2]-previous[2]);
const std::int64_t run=static_cast<std::int64_t>(std::llround(
std::hypot(static_cast<double>(dx),static_cast<double>(dy))));
if(run<=0||dz*65536>run*GradeQ16)gradePass=false;
}
candidate.points.push_back(point);
}
if(!gradePass)continue;
++terrainPathCount;
const std::array<std::array<std::int64_t,2>,8> lateralNudges{{
{{-ProbeRadiusQ,-ProbeRadiusQ}},{{-ProbeRadiusQ,0}},
{{0,-ProbeRadiusQ}},{{ProbeRadiusQ,-ProbeRadiusQ}},
{{-ProbeRadiusQ,ProbeRadiusQ}},{{0,ProbeRadiusQ}},
{{ProbeRadiusQ,0}},{{ProbeRadiusQ,ProbeRadiusQ}}}};
for(std::size_t nudgeIndex=0;nudgeIndex<lateralNudges.size();++nudgeIndex)
{
Candidate shifted=candidate;
shifted.direction=candidate.direction*9
+static_cast<std::int32_t>(nudgeIndex+1);
shifted.score+=static_cast<std::int64_t>(nudgeIndex+1);
bool shiftedGradePass=true;
for(std::size_t pointIndex=0;pointIndex<shifted.points.size();++pointIndex)
{
const std::int64_t tQ16=shifted.points.size()>1
?static_cast<std::int64_t>(pointIndex)*65536
/static_cast<std::int64_t>(shifted.points.size()-1):0;
const std::int64_t t2Q16=tQ16*tQ16/65536;
const std::int64_t oneMinusQ16=65536-tQ16;
const std::int64_t oneMinus2Q16=
oneMinusQ16*oneMinusQ16/65536;
const std::int64_t weightQ16=std::min<std::int64_t>(65536,
16*t2Q16*oneMinus2Q16/65536);
auto& point=shifted.points[pointIndex];
point.positionSubvoxels[0]+=lateralNudges[nudgeIndex][0]
*weightQ16/65536;
point.positionSubvoxels[1]+=lateralNudges[nudgeIndex][1]
*weightQ16/65536;
const auto ground=safeGroundAt(point.positionSubvoxels[0],
point.positionSubvoxels[1]);
if(!ground.supportReachable)shiftedGradePass=false;
point.positionSubvoxels[2]=ground.ground+routeBaseLiftQ
+HalfHeightQ;
point.stableFeatureId=WorldAssemblyFeatureId(recipe.seed,
point.positionSubvoxels[0],point.positionSubvoxels[1],
0x52550000u+static_cast<std::uint32_t>(
(nudgeIndex+1)*0x10000u+pointIndex));
if(pointIndex==0)continue;
const auto& previous=shifted.points[pointIndex-1]
.positionSubvoxels;
const std::int64_t dx=point.positionSubvoxels[0]-previous[0];
const std::int64_t dy=point.positionSubvoxels[1]-previous[1];
const std::int64_t dz=std::abs(point.positionSubvoxels[2]-previous[2]);
const std::int64_t run=static_cast<std::int64_t>(std::llround(
std::hypot(static_cast<double>(dx),static_cast<double>(dy))));
if(run<=0||dz*65536>run*GradeQ16)shiftedGradePass=false;
}
if(!shiftedGradePass)continue;
candidates.push_back(std::move(shifted));
++terrainPathCount;
}
candidates.push_back(std::move(candidate));
}
if(terrainPathCount>0)terrainGridStepQ=terrainStepQ;
};
tryTerrainPaths(stepQ);
tested+=terrainPathCount;
std::printf("[WorldAssemblerRouteTerrainSearch] grid_step_m=%.3f grid_samples=%llu candidate_paths=%llu grade_limit_deg=15.000 status=%s\n",
terrainGridStepQ*coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel,
static_cast<unsigned long long>(terrainGridSamples),
static_cast<unsigned long long>(terrainPathCount),
terrainPathCount>0?"FOUND":"EMPTY");
}
};
if(gradeRejected>0||placementRejected>0)
{
const double metresPerSubvoxel=static_cast<double>(coreVoxelSizeMicrons)
@@ -318,8 +644,33 @@ namespace StandaloneWorldAssemblyPrototype
});
std::uint64_t sweepAttempt=0,repairAttempt=0;
VoxelForgeCore::WorldAssemblyRouteAuditResult lastAudit{};
for(auto& candidate:candidates)
std::size_t candidateCursor=0;
constexpr std::array<std::int32_t,4> TerrainFallbackSteps{{1024,512,256,128}};
std::size_t terrainFallbackStep=0;
for(;;)
{
if(candidateCursor>=candidates.size())
{
if(terrainFallbackStep<TerrainFallbackSteps.size()&&fallbackSearch
&&((arrivalRadiusSubvoxels>0)!=(departureRadiusSubvoxels>0)))
{
const std::size_t firstTerrainCandidate=candidates.size();
appendTerrainFallback(TerrainFallbackSteps[terrainFallbackStep++]);
std::stable_sort(candidates.begin()+firstTerrainCandidate,
candidates.end(),[](const Candidate& a,const Candidate& b)
{
if(a.endpointProximityCost!=b.endpointProximityCost)
return a.endpointProximityCost<b.endpointProximityCost;
if(a.score!=b.score)return a.score<b.score;
if(a.centerY!=b.centerY)return a.centerY<b.centerY;
if(a.centerX!=b.centerX)return a.centerX<b.centerX;
return a.direction<b.direction;
});
continue;
}
break;
}
auto& candidate=candidates[candidateCursor++];
stratum.route=candidate.points;
std::int64_t liftQ=0;
constexpr std::int64_t RepairStepQ=64;
@@ -349,6 +700,42 @@ namespace StandaloneWorldAssemblyPrototype
static_cast<long long>(candidate.score));
return true;
}
if(liftQ==0&&audit.blockedCapsuleSamples==0
&&audit.unsupportedCenterSamples>0)
{
constexpr std::int64_t SupportAlignmentStepQ=16;
const std::int64_t lowerLimit=std::max<std::int64_t>(0,
audit.minimumClearanceSubvoxels
-constraints.minimumClearanceSubvoxels);
for(std::int64_t lowerQ=SupportAlignmentStepQ;
lowerQ<=lowerLimit;lowerQ+=SupportAlignmentStepQ)
{
for(std::size_t pointIndex=0;pointIndex<stratum.route.size();++pointIndex)
stratum.route[pointIndex].positionSubvoxels[2]
=candidate.points[pointIndex].positionSubvoxels[2]-lowerQ;
const auto loweredAudit=AuditHeightfieldRoute(candidateField);
lastAudit=loweredAudit;++repairAttempt;
if(loweredAudit.passed)
{
std::printf("[WorldAssemblerRouteRepaired] uniform_lower_q=%lld support_limit_q=%lld\n",
static_cast<long long>(lowerQ),
static_cast<long long>(lowerLimit));
const double metresPerSubvoxel=coreVoxelMetres
/WorldAssemblySubvoxelsPerVoxel;
std::printf("[WorldAssemblerRouteAccepted] arrival_xy_m=(%.3f,%.3f) departure_xy_m=(%.3f,%.3f) endpoint_proximity_cost=%llu average_grade_q16=%lld\n",
candidate.points.front().positionSubvoxels[0]*metresPerSubvoxel,
candidate.points.front().positionSubvoxels[1]*metresPerSubvoxel,
candidate.points.back().positionSubvoxels[0]*metresPerSubvoxel,
candidate.points.back().positionSubvoxels[1]*metresPerSubvoxel,
static_cast<unsigned long long>(candidate.endpointProximityCost),
static_cast<long long>(candidate.score));
return true;
}
if(loweredAudit.blockedCapsuleSamples>0
||loweredAudit.minimumClearanceSubvoxels
<constraints.minimumClearanceSubvoxels)break;
}
}
if(audit.blockedCapsuleSamples==0||liftQ>=MaximumRepairLiftQ)break;
const std::int64_t needed=std::max<std::int64_t>(1,
constraints.minimumClearanceSubvoxels-audit.minimumClearanceSubvoxels);
@@ -358,6 +745,62 @@ namespace StandaloneWorldAssemblyPrototype
liftQ=nextLift;
++repairAttempt;
}
if(lastAudit.unsupportedCenterSamples>0&&sweepAttempt<5)
{
const Heightfield2D diagnosticField(recipe,stratum);
const std::int64_t sampleSpacing=std::min<std::int64_t>(
constraints.sweepSpacingSubvoxels,
WorldAssemblySubvoxelsPerVoxel/2);
const std::int64_t supportSpacing=std::min<std::int64_t>(sampleSpacing,
WorldAssemblySubvoxelsPerVoxel/4);
constexpr std::array<std::array<std::int64_t,2>,5> supportOffsets{{
{{0,0}},{{ProbeRadiusQ,0}},{{-ProbeRadiusQ,0}},
{{0,ProbeRadiusQ}},{{0,-ProbeRadiusQ}}}};
for(std::size_t segment=1;segment<stratum.route.size();++segment)
{
const auto& from=stratum.route[segment-1].positionSubvoxels;
const auto& to=stratum.route[segment].positionSubvoxels;
const std::int64_t dx=to[0]-from[0],dy=to[1]-from[1],dz=to[2]-from[2];
const std::uint64_t length=std::max<std::uint64_t>(1,
static_cast<std::uint64_t>(std::llround(std::sqrt(
static_cast<double>(dx*dx+dy*dy+dz*dz)))));
const std::uint64_t intervals=std::max<std::uint64_t>(1,
(length+sampleSpacing-1)/sampleSpacing);
for(std::uint64_t step=segment==1?0:1;step<=intervals;++step)
{
const auto interpolate=[&](std::size_t axis)
{return from[axis]+(to[axis]-from[axis])
*static_cast<std::int64_t>(step)/static_cast<std::int64_t>(intervals);};
const std::int64_t x=interpolate(0),y=interpolate(1),z=interpolate(2);
const std::int64_t footZ=z-PlayerHeightQ/2;
std::array<std::int64_t,5> reachedDepth{};
for(std::size_t probe=0;probe<supportOffsets.size();++probe)
for(std::int64_t depth=supportSpacing;
depth<=constraints.supportSearchDepthSubvoxels;
depth+=supportSpacing)
{
if(diagnosticField.point(x+supportOffsets[probe][0],
y+supportOffsets[probe][1],footZ-depth)
.densitySubvoxels<0)
{reachedDepth[probe]=depth;break;}
}
const auto supported=static_cast<std::uint32_t>(std::count_if(
reachedDepth.begin(),reachedDepth.end(),[](auto depth){return depth>0;}));
if(supported>=constraints.minimumSupportProbes)continue;
const double metresPerSubvoxel=coreVoxelMetres
/WorldAssemblySubvoxelsPerVoxel;
std::printf("[WorldAssemblerRouteSupportSample] ordinal=%llu x_m=%.3f y_m=%.3f center_z_m=%.3f supported=%u/5 depths_q=(%lld,%lld,%lld,%lld,%lld)\n",
static_cast<unsigned long long>(sweepAttempt),x*metresPerSubvoxel,
y*metresPerSubvoxel,z*metresPerSubvoxel,supported,
static_cast<long long>(reachedDepth[0]),
static_cast<long long>(reachedDepth[1]),
static_cast<long long>(reachedDepth[2]),
static_cast<long long>(reachedDepth[3]),
static_cast<long long>(reachedDepth[4]));
segment=stratum.route.size();break;
}
}
}
if(sweepAttempt<3)
std::printf("[WorldAssemblerRouteCandidateRejected] ordinal=%llu blocked=%llu unsupported=%llu grade_failures=%llu minimum_clearance_q=%lld\n",
static_cast<unsigned long long>(sweepAttempt),
@@ -1183,6 +1626,7 @@ namespace StandaloneWorldAssemblyPrototype
static_cast<double>(micrometres)*WorldAssemblySubvoxelsPerVoxel
/layout.coreVoxelSizeMicrometres));
};
constexpr std::int64_t PlayerCenterAboveFloorMicrometres=1130000;
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition
@@ -1192,10 +1636,24 @@ namespace StandaloneWorldAssemblyPrototype
[&](const auto& item){return item.strateIndex==slot.StrateIndex;});
if(stratum==layout.strates.end()
||!IsNativeCaveFieldStratum(*stratum))continue;
const bool hasRoutePoints=std::count_if(stratum->anchors.begin(),
const std::string routePrefix="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
&&edge.stableId==routePrefix+"departure-edge";});
if(!hasPlannedArrival||!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
// that is no longer present in the composed cave field.
stratum->anchors.erase(std::remove_if(stratum->anchors.begin(),
stratum->anchors.end(),[](const auto& anchor)
{return anchor.kind=="route_point"&&anchor.values.size()>=3;})>=2;
if(hasRoutePoints)continue;
{return anchor.kind=="route_point";}),stratum->anchors.end());
const ResolvedLayoutPassageV1* arrival=nullptr;
const ResolvedLayoutPassageV1* departure=nullptr;
for(const auto& passage:layout.passages)
@@ -1222,9 +1680,7 @@ namespace StandaloneWorldAssemblyPrototype
if(std::abs(a[axis]-b[axis])>2)return false;
return true;
};
constexpr std::int64_t PlayerCenterAboveFloorMicrometres=1130000;
auto start=arrival->lowerMouthSubvoxels;
start[2]+=toCoreSubvoxels(PlayerCenterAboveFloorMicrometres);
appendRoutePoint("000-arrival",start);
for(std::size_t edgeIndex=0;edgeIndex<256;++edgeIndex)
{
@@ -1239,6 +1695,10 @@ namespace StandaloneWorldAssemblyPrototype
if(edgeIndex==0)
{
auto inner=edge->controlPointsMicrometres.front();
// Keep the exact authored mouth anchor and use the planner
// edge's floor height for the first cave-side walking point.
// Passage composition uses these same two points to blend the
// floor profile into the native cave route.
inner[2]+=PlayerCenterAboveFloorMicrometres;
const std::array<std::int64_t,3> point={
toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
@@ -1266,12 +1726,10 @@ namespace StandaloneWorldAssemblyPrototype
toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
toCoreSubvoxels(inner[2])};
auto end=departure->upperMouthSubvoxels;
end[2]+=toCoreSubvoxels(PlayerCenterAboveFloorMicrometres);
if(!sameRoutePoint(innerPoint,end))
appendRoutePoint("998-departure-inner",innerPoint);
}
auto end=departure->upperMouthSubvoxels;
end[2]+=toCoreSubvoxels(PlayerCenterAboveFloorMicrometres);
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-v230.baseline",
"L134-v230-continuous-backbone")
return (BASELINES / "owner-hash/canonical/owner-hash-surface-world-style1-v236.baseline",
"L135-v236-cave-mouth-joints")
if recipe == "floating-islands" and style == 2:
return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v230.baseline",
"L134-v230-continuous-backbone")
return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v236.baseline",
"L135-v236-cave-mouth-joints")
raise VerificationFailure(f"no pinned owner hash baseline for {recipe} style {style}")