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

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);
}