wip(assembler): round L37 -- FI mouth carve-fade fix, no raised bridge (style 0); research R1

The passage mouth fade multiplied the signed carve distance by zero, which turned native rock into an
open surface (the reason earlier rounds stamped walk pads at mouths); it now blends between the
uncarved field and the full signed carve. The FI style-0 route followed a lifted grade envelope over
the crater (the "bridge"); it now runs on the rim and follows the native surface. New built-shape and
crease audits with thresholds frozen before the fix (L36 and box controls fail). 24/24 passage rows,
30/30 determinism, 16/16 invariants, 18/18 interface/ring. Unreal: one recipe setter (uncompiled).
Research R1 (read-only, web): ranked proposals for natural walkable mouths, floor-limited carve first.

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-23 05:51:50 +02:00
1 parent 8be1c4d9ef
commit 3e443bf81c
33 files changed
+2767 -86

No files matched your search

+410 -68
View File
@@ -94,6 +94,9 @@ public:
}
bool hasMouthSupport(std::size_t passageIndex) const
{return passageIndex<passageMouthSupports_.size()&&passageMouthSupports_[passageIndex]!=0;}
bool hasAnyMouthSupport() const
{return std::any_of(passageMouthSupports_.begin(),passageMouthSupports_.end(),
[](std::uint8_t value){return value!=0;});}
bool hasRouteFeatureOverlap() const{return routeFeatureOverlap_;}
bool hasColumnZOverlap() const{return columnZOverlap_;}
bool mayContainResolvedBoundaryBand() const{return mayContainResolvedBoundaryBand_;}
@@ -683,23 +686,36 @@ public:
{
if(!ResolvedLayout||ResolvedLayout->passages.empty()||!std::isfinite(X)
||!std::isfinite(Y)||!std::isfinite(Z))return density;
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double qPerMetre=Q*1000000.0/
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
const double qx=static_cast<double>(X)*Q,qy=static_cast<double>(Y)*Q,
qz=static_cast<double>(Z)*Q;
const auto* passageTileQueries=ActivePassageTileQueries;
const bool passageTileScopeMatches=passageTileQueries
&&passageTileQueries->matches(*this);
bool queryNearUpperMouthSupport=false;
for(const auto& passage:ResolvedLayout->passages)
{
const double dx=qx-static_cast<double>(passage.upperMouthSubvoxels[0]);
const double dy=qy-static_cast<double>(passage.upperMouthSubvoxels[1]);
const double dz=qz-static_cast<double>(passage.upperMouthSubvoxels[2]);
const double reach=15.0*qPerMetre;
if(dx*dx+dy*dy<=reach*reach&&std::abs(dz)<=8.0*qPerMetre)
{queryNearUpperMouthSupport=true;break;}
}
if(passageTileScopeMatches&&density>=0.0f
&&!passageTileQueries->hasRouteFeatureOverlap()
&&!passageTileQueries->hasColumnZOverlap())return density;
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double qx=static_cast<double>(X)*Q,qy=static_cast<double>(Y)*Q,qz=static_cast<double>(Z)*Q;
const double qPerMetre=Q*1000000.0/std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
&&!passageTileQueries->hasColumnZOverlap()
&&!passageTileQueries->hasAnyMouthSupport()
&&!queryNearUpperMouthSupport)return density;
const double floorThicknessVoxels=qPerMetre/Q;
const double playerCenterAboveFloorQ=1.13*qPerMetre;
float composed=density;
const auto composeRouteMouthSupport=[&](std::int32_t strateIndex,
const std::array<std::int64_t,3>& mouth,bool departure)
{
// Support is allowed to reinforce native solid terrain only. Without this
// guard the route-point tread stamps a floating shelf into an open stratum.
// Surface-route treads may reinforce native solid terrain only.
if(!(density<0.0f))return;
const double mouthDx=qx-static_cast<double>(mouth[0]);
const double mouthDy=qy-static_cast<double>(mouth[1]);
@@ -727,7 +743,7 @@ public:
}
if(routeCount<2)return;
const std::size_t lastSegment=routeCount-1;
constexpr double MouthSupportBlendMetres=1.5;
constexpr double MouthSupportBlendMetres=0.45;
const double mouthBlendWidth=MouthSupportBlendMetres*qPerMetre;
double mouthSupportDistance=std::numeric_limits<double>::infinity();
for(std::size_t index=0;index<lastSegment;++index)
@@ -742,11 +758,13 @@ public:
const double centerZ=a.values[2]+t*(static_cast<double>(b.values[2])-a.values[2]);
const double routeRadius=a.values.size()>=4
?a.values[3]+t*(static_cast<double>(b.values[3])-a.values[3]):0.0;
const double halfWidth=std::max(0.75*qPerMetre,routeRadius);
const double halfWidth=std::clamp(routeRadius,
0.42*qPerMetre,0.56*qPerMetre);
const double horizontalX=qx-centerX,horizontalY=qy-centerY;
const double horizontalDistance=DeterministicSqrt(
horizontalX*horizontalX+horizontalY*horizontalY);
const double floorQ=centerZ-1.20*qPerMetre;
const double floorQ=centerZ-(VoxelForgeCore::WorldAssemblyEvaluatorVersion>=184
?playerCenterAboveFloorQ+0.25*qPerMetre:1.20*qPerMetre);
const double sideDistance=horizontalDistance-halfWidth;
const double topDistance=qz-floorQ;
const double bottomDistance=floorQ-qPerMetre-qz;
@@ -756,25 +774,14 @@ public:
std::max(sideDistance,0.0)*std::max(sideDistance,0.0)
+std::max(verticalDistance,0.0)*std::max(verticalDistance,0.0));
const double inside=std::min(std::max(sideDistance,verticalDistance),0.0);
const double signedDistance=outside+inside;
mouthSupportDistance=std::min(mouthSupportDistance,signedDistance);
mouthSupportDistance=std::min(mouthSupportDistance,outside+inside);
}
if(std::isfinite(mouthSupportDistance))
composed=SmoothMinDistance(composed,
static_cast<float>(mouthSupportDistance/Q),
static_cast<float>(mouthBlendWidth/Q));
};
// Keep the native surface route connected to the entrance. Passage carving
// then removes only the parts that overlap the tunnel, whose own tread owns
// the interior ramp.
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
{
const auto& passage=ResolvedLayout->passages[passageIndex];
const bool tileHasSupport=passageTileScopeMatches;
if(!tileHasSupport||ActivePassageTileQueries->hasMouthSupport(passageIndex))
composeRouteMouthSupport(passage.upperStrateIndex,
passage.upperMouthSubvoxels,true);
}
// The connected passage tread continues the surface route into the tunnel.
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
{
const auto& passage=ResolvedLayout->passages[passageIndex];
@@ -789,6 +796,8 @@ public:
};
const auto* supportUpper=supportStratum(passage.upperStrateIndex);
const auto* supportLower=supportStratum(passage.lowerStrateIndex);
const bool floatingIslandPassage=supportUpper
&&supportUpper->generatorKind=="FloatingIslands";
const bool surfaceToTunnelSupport=supportUpper&&supportLower
&&supportUpper->generatorKind=="SurfaceWorld"
&&supportLower->generatorKind=="TunnelNetwork";
@@ -907,8 +916,11 @@ public:
std::ptrdiff_t bestScanIndex=std::numeric_limits<std::ptrdiff_t>::max();
std::ptrdiff_t bestSupportSegmentIndex=0;
bool hasSupportFloor=false;
bool bestSupportIsMouthTread=false;
double bestSupportMouthBlend=1.0;
double bestSupportDistanceSquared=std::numeric_limits<double>::infinity();
double bestSupportDensity=std::numeric_limits<double>::infinity();
double bestSupportNativeBlend=0.0;
double bestSupportFloorQ=0.0;
bool bestSupportUsesSignedDistance=false;
std::ptrdiff_t selectedSupportSegmentIndex=-1;
@@ -1032,18 +1044,175 @@ public:
/distanceToCenter,0.0,1.0):0.0;
const float relief=VF_FieldPassageWallRelief(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),static_cast<std::uint32_t>(Owner?Owner->Seed:0));
const float mouthSurfaceRelief=VF_FieldSurfaceRelief(static_cast<float>(X)*1.5f,
static_cast<float>(Y)*1.5f,static_cast<float>(Z)*1.5f,
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x4D4F5554u);
const float mouthTerrainRelief=VF_FieldSurfaceRelief(static_cast<float>(X)*1.5f,
static_cast<float>(Y)*1.5f,static_cast<float>(Z)*1.5f,
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x4D4F5554u);
const float mouthTreadFieldRelief=VF_FieldSurfaceRelief(static_cast<float>(X)*0.75f,
static_cast<float>(Y)*0.75f,static_cast<float>(Z)*0.75f,
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x54485244u);
const auto routeSegmentLengthMetres=[&](std::ptrdiff_t routeIndex)
{
const auto& before=passage.controlPoints[static_cast<std::size_t>(routeIndex)];
const auto& after=passage.controlPoints[static_cast<std::size_t>(routeIndex+1)];
const double dx=static_cast<double>(after.positionSubvoxels[0])
-before.positionSubvoxels[0];
const double dy=static_cast<double>(after.positionSubvoxels[1])
-before.positionSubvoxels[1];
const double dz=static_cast<double>(after.positionSubvoxels[2])
-before.positionSubvoxels[2];
return DeterministicSqrt(dx*dx+dy*dy+dz*dz)/qPerMetre;
};
double distanceFromOpenMouth=std::numeric_limits<double>::infinity();
if(bestIndex<=2)
{
distanceFromOpenMouth=bestT*routeSegmentLengthMetres(bestIndex);
for(std::ptrdiff_t routeIndex=0;routeIndex<bestIndex;++routeIndex)
distanceFromOpenMouth+=routeSegmentLengthMetres(routeIndex);
}
else if(bestIndex>=lowerMouthIndex-2&&bestIndex<=lowerMouthIndex+2)
{
if(bestIndex<lowerMouthIndex)
{
distanceFromOpenMouth=(1.0-bestT)*routeSegmentLengthMetres(bestIndex);
for(std::ptrdiff_t routeIndex=bestIndex+1;
routeIndex<lowerMouthIndex;++routeIndex)
distanceFromOpenMouth+=routeSegmentLengthMetres(routeIndex);
}
else
{
distanceFromOpenMouth=bestT*routeSegmentLengthMetres(bestIndex);
for(std::ptrdiff_t routeIndex=lowerMouthIndex;routeIndex<bestIndex;++routeIndex)
distanceFromOpenMouth+=routeSegmentLengthMetres(routeIndex);
}
}
double mouthEntryFloorAlignment=0.0;
double mouthEntryPlanDistance=std::numeric_limits<double>::infinity();
double mouthEntryFloorQ=0.0;
bool upperMouthFloorFade=false;
{
// The large mouth bore overlaps several inner coils. Its global
// nearest SDF can belong to a later turn even at the lip, so use the
// projected entrance path to recover the local floor orientation.
double prefixMetres=0.0;
double nearestEntryPlanDistance=std::numeric_limits<double>::infinity();
double projectedEntryDistance=std::numeric_limits<double>::infinity();
for(std::ptrdiff_t routeIndex=0;
routeIndex<std::min<std::ptrdiff_t>(3,lowerMouthIndex);++routeIndex)
{
const auto& before=passage.controlPoints[static_cast<std::size_t>(routeIndex)];
const auto& after=passage.controlPoints[static_cast<std::size_t>(routeIndex+1)];
const double ax=static_cast<double>(before.positionSubvoxels[0]);
const double ay=static_cast<double>(before.positionSubvoxels[1]);
const double az=static_cast<double>(before.positionSubvoxels[2]);
const double dx=static_cast<double>(after.positionSubvoxels[0])-ax;
const double dy=static_cast<double>(after.positionSubvoxels[1])-ay;
const double dz=static_cast<double>(after.positionSubvoxels[2])-az;
const double horizontalLengthSq=dx*dx+dy*dy;
const double t=horizontalLengthSq>0.0?std::clamp(
((qx-ax)*dx+(qy-ay)*dy)/horizontalLengthSq,0.0,1.0):0.0;
double projectedX=ax+t*dx,projectedY=ay+t*dy;
const double horizontalLength=DeterministicSqrt(horizontalLengthSq);
if(horizontalLength>1.0e-6)
{
const double pathRelief=std::clamp(static_cast<double>(
VF_FieldSurfaceRelief(static_cast<float>(projectedX/qPerMetre*1.5),
static_cast<float>(projectedY/qPerMetre*1.5),
static_cast<float>((az+t*dz)/qPerMetre*1.5),
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x4D4F5554u)),
-1.0,1.0);
const double lateralOffset=pathRelief*0.18*qPerMetre;
projectedX-=dy/horizontalLength*lateralOffset;
projectedY+=dx/horizontalLength*lateralOffset;
}
const double planDx=qx-projectedX,planDy=qy-projectedY;
const double planDistance=DeterministicSqrt(planDx*planDx+planDy*planDy);
if(planDistance<nearestEntryPlanDistance)
{
nearestEntryPlanDistance=planDistance;
const double centerZ=az+t*dz;
const double radius=before.radiusSubvoxels+t*(
static_cast<double>(after.radiusSubvoxels)-before.radiusSubvoxels);
mouthEntryFloorQ=centerZ-radius+0.20*qPerMetre;
const double centerDistance=DeterministicSqrt(planDx*planDx
+planDy*planDy+(qz-centerZ)*(qz-centerZ));
mouthEntryFloorAlignment=centerDistance>1.0e-9
?std::clamp(-(qz-centerZ)/centerDistance,0.0,1.0):0.0;
projectedEntryDistance=prefixMetres
+t*routeSegmentLengthMetres(routeIndex);
}
prefixMetres+=routeSegmentLengthMetres(routeIndex);
}
if(nearestEntryPlanDistance<=2.0*qPerMetre
&&projectedEntryDistance<=8.0
&&std::abs(qz-mouthEntryFloorQ)<=4.0*qPerMetre)
{
distanceFromOpenMouth=projectedEntryDistance;
mouthEntryPlanDistance=nearestEntryPlanDistance/qPerMetre;
upperMouthFloorFade=true;
}
}
double mouthFloorBlend=0.0;
if(std::isfinite(distanceFromOpenMouth))
{
const double fade=std::clamp(distanceFromOpenMouth/11.0,0.0,1.0);
const double smooth=fade*fade*(3.0-2.0*fade);
mouthFloorBlend=1.0-smooth;
}
const double floorReliefT=std::clamp((verticalWallAlignment-0.55)/0.45,0.0,1.0);
const double floorReliefWeight=floorReliefT*floorReliefT
*(3.0-2.0*floorReliefT);
const double floorReliefOffset=std::clamp(
static_cast<double>(mouthTerrainRelief),-1.0,1.0)
*0.20*qPerMetre*mouthFloorBlend*floorReliefWeight
*(upperMouthFloorFade?1.0:0.0);
const double wallReliefWeight=0.05+0.95*(1.0-verticalWallAlignment);
const double roughSignedDistance=bestSignedDistance
+static_cast<double>(std::clamp(relief,-1.0f,1.0f))*0.21*qPerMetre*wallReliefWeight;
+static_cast<double>(std::clamp(relief,-1.0f,1.0f))*0.21*qPerMetre*wallReliefWeight
+floorReliefOffset;
if(IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(bestIndex)))
{
const double bridgeDistance=PassageBridgeSignedDistance(passageIndex,
static_cast<std::size_t>(bestIndex),X,Y,Z);
composed=std::min(composed,static_cast<float>(bridgeDistance/Q));
}
// Positive density is air; rough rock is cut with full side/ceiling detail and
// gentler relief on the gravity-facing wall so the supported tread stays open.
composed=std::max(composed,static_cast<float>(-roughSignedDistance/Q));
// Leave native crater ground at the lip. The bore opens through the walls
// immediately, while its floor cut eases in along the passage centerline.
double mouthCarveFade=1.0;
if(upperMouthFloorFade)
{
constexpr double mouthCarveFadeMetres=8.0;
mouthCarveFade=std::clamp(distanceFromOpenMouth/mouthCarveFadeMetres,
0.0,1.0);
mouthCarveFade=mouthCarveFade*mouthCarveFade*(3.0-2.0*mouthCarveFade);
}
const double floorAlignment=std::max(verticalWallAlignment,
mouthEntryFloorAlignment)*std::max(verticalWallAlignment,
mouthEntryFloorAlignment);
double floorBelowWeight=0.0;
if(upperMouthFloorFade)
{
const double belowFloor=(mouthEntryFloorQ-qz)/qPerMetre;
const double supportRamp=std::clamp(belowFloor/0.15,0.0,1.0);
const double supportFade=std::clamp((1.0-belowFloor)/0.35,0.0,1.0);
const double rampSmooth=supportRamp*supportRamp*(3.0-2.0*supportRamp);
const double fadeSmooth=supportFade*supportFade*(3.0-2.0*supportFade);
floorBelowWeight=rampSmooth*fadeSmooth;
}
// Preserve native terrain only along a capsule-scale worn lane. The
// former bore-wide alignment feather left a straight platform-width
// bridge across the FI mouth.
double mouthWalkLaneT=std::clamp((1.05-mouthEntryPlanDistance)/0.60,0.0,1.0);
mouthWalkLaneT=mouthWalkLaneT*mouthWalkLaneT*(3.0-2.0*mouthWalkLaneT);
const double mouthWalkLaneWeight=upperMouthFloorFade?mouthWalkLaneT:0.0;
const double floorCarveWeight=1.0-(1.0-mouthCarveFade)
*floorAlignment*floorAlignment*floorBelowWeight*mouthWalkLaneWeight;
const float fullyCarvedDensity=std::max(composed,
static_cast<float>(-roughSignedDistance/Q));
composed=std::lerp(composed,fullyCarvedDensity,
static_cast<float>(floorCarveWeight));
const auto considerSupportSegment=[&](std::ptrdiff_t index)
{
const auto& segmentA=passage.controlPoints[static_cast<std::size_t>(index)];
@@ -1080,8 +1249,8 @@ public:
?std::min(0.5,2.0*qPerMetre/DeterministicSqrt(horizontalLengthSq)):0.0;
const double supportT=horizontalLengthSq>0.0
?std::clamp(projection,-overlapFraction,1.0+overlapFraction):bestT;
const double centerX=segmentA.positionSubvoxels[0]+supportT*dx;
const double centerY=segmentA.positionSubvoxels[1]+supportT*dy;
double centerX=segmentA.positionSubvoxels[0]+supportT*dx;
double centerY=segmentA.positionSubvoxels[1]+supportT*dy;
double centerZ=segmentA.positionSubvoxels[2]+supportT*(
static_cast<double>(segmentB.positionSubvoxels[2])-segmentA.positionSubvoxels[2]);
double radius=segmentA.radiusSubvoxels+supportT*(
@@ -1105,6 +1274,25 @@ public:
segmentB.radiusSubvoxels,p3.radiusSubvoxels)-radius)
*SmoothSupportHeightWeight;
}
double treadRelief=0.0;
if(entryMouthShoulder
||(index>=lowerMouthIndex-2&&index<=lowerMouthIndex+2))
{
const double horizontalLength=DeterministicSqrt(horizontalLengthSq);
if(horizontalLength>1.0e-6)
{
const double pathRelief=std::clamp(static_cast<double>(
VF_FieldSurfaceRelief(static_cast<float>(centerX/qPerMetre*1.5),
static_cast<float>(centerY/qPerMetre*1.5),
static_cast<float>(centerZ/qPerMetre*1.5),
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x4D4F5554u)),
-1.0,1.0);
treadRelief=pathRelief;
const double lateralOffset=pathRelief*0.18*qPerMetre;
centerX-=dy/horizontalLength*lateralOffset;
centerY+=dx/horizontalLength*lateralOffset;
}
}
const double horizontalX=qx-centerX,horizontalY=qy-centerY;
const double horizontalDistance=DeterministicSqrt(horizontalX*horizontalX
+horizontalY*horizontalY);
@@ -1117,7 +1305,9 @@ public:
// dense routes retain the legacy one-sided tread density.
const double supportSegmentLength=DeterministicSqrt(
horizontalLengthSq+dz*dz)/qPerMetre;
const bool sparseRouteSupport=supportSegmentLength>1.5;
const double sparseRouteThreshold=VoxelForgeCore::WorldAssemblyEvaluatorVersion>=188
&&floatingIslandPassage&&passage.style==0?1.0:1.5;
const bool sparseRouteSupport=supportSegmentLength>sparseRouteThreshold;
const double minimumSupportQueryDepth=index==selectedSupportSegmentIndex
&&sparseRouteSupport?0.65:0.93;
if(supportQueryDepth<minimumSupportQueryDepth*qPerMetre
@@ -1130,15 +1320,75 @@ public:
// capsule on the neighbouring turn.
const bool lowerMouthBodyOverlap=index>=lowerMouthIndex-2
&&supportQueryDepth<0.98*qPerMetre;
const double supportHalfWidthMetres=lowerMouthBodyOverlap?0.90
:entryMouthShoulder?1.20:1.25;
const double supportHalfWidth=std::min(radius,
// Keep a narrow walk core, but place its top on the native surface
// so the support remains a worn ramp instead of a route-height slab.
const bool mouthTread=entryMouthShoulder
&&(!floatingIslandPassage||passage.style==0);
double distanceFromMouthMetres=std::numeric_limits<double>::infinity();
if(entryMouthShoulder)
{
double prefixMetres=0.0;
for(std::ptrdiff_t routeIndex=0;routeIndex<index;++routeIndex)
{
const auto& before=passage.controlPoints[
static_cast<std::size_t>(routeIndex)];
const auto& after=passage.controlPoints[
static_cast<std::size_t>(routeIndex+1)];
const double dx=static_cast<double>(after.positionSubvoxels[0])
-before.positionSubvoxels[0];
const double dy=static_cast<double>(after.positionSubvoxels[1])
-before.positionSubvoxels[1];
const double dz=static_cast<double>(after.positionSubvoxels[2])
-before.positionSubvoxels[2];
prefixMetres+=DeterministicSqrt(dx*dx+dy*dy+dz*dz)/qPerMetre;
}
distanceFromMouthMetres=std::max(0.0,
prefixMetres+supportT*supportSegmentLength);
}
else if(lowerMouthBodyOverlap)
{
double suffixMetres=0.0;
for(std::ptrdiff_t routeIndex=index+1;
routeIndex<lowerMouthIndex;++routeIndex)
{
const auto& before=passage.controlPoints[
static_cast<std::size_t>(routeIndex)];
const auto& after=passage.controlPoints[
static_cast<std::size_t>(routeIndex+1)];
const double dx=static_cast<double>(after.positionSubvoxels[0])
-before.positionSubvoxels[0];
const double dy=static_cast<double>(after.positionSubvoxels[1])
-before.positionSubvoxels[1];
const double dz=static_cast<double>(after.positionSubvoxels[2])
-before.positionSubvoxels[2];
suffixMetres+=DeterministicSqrt(dx*dx+dy*dy+dz*dz)/qPerMetre;
}
distanceFromMouthMetres=std::max(0.0,
suffixMetres+(1.0-supportT)*supportSegmentLength);
}
double mouthReliefT=std::clamp(distanceFromMouthMetres/8.0,0.0,1.0);
mouthReliefT=mouthReliefT*mouthReliefT*(3.0-2.0*mouthReliefT);
double mouthSupportT=std::clamp((distanceFromMouthMetres-0.25)/4.0,0.0,1.0);
mouthSupportT=mouthSupportT*mouthSupportT*(3.0-2.0*mouthSupportT);
const double mouthSupportWidth=std::lerp(0.82,1.00,mouthSupportT);
const double supportHalfWidthMetres=mouthTread?mouthSupportWidth
:lowerMouthBodyOverlap?0.90:1.25;
double supportHalfWidth=std::min(radius,
supportHalfWidthMetres*qPerMetre);
// Keep the walk core within a 0.50 m span with shallow relief, then
// taper the outer shoulder; capsule and probe widths remain supported.
const double supportEdgeCoreMetres=entryMouthShoulder?0.60:0.90;
const double shoulderWarp=mouthTread
?std::clamp(static_cast<double>(mouthSurfaceRelief),-1.0,1.0)
*0.12*qPerMetre:0.0;
supportHalfWidth=mouthTread
?std::clamp(supportHalfWidth+shoulderWarp,
std::lerp(0.52,0.62,mouthSupportT)*qPerMetre,
std::lerp(0.92,1.10,mouthSupportT)*qPerMetre)
:std::max(0.0,supportHalfWidth+shoulderWarp);
// Keep the walk core within the player footprint, then ease the outer
// edge into native ground over a long enough shoulder to avoid a berm.
const double supportEdgeCoreMetres=mouthTread?0.08
:entryMouthShoulder?0.60:0.90;
const double supportEdgeBlendMetres=std::min(
entryMouthShoulder?0.85:0.35,
mouthTread?1.40:entryMouthShoulder?0.85:0.35,
std::max(0.0,supportHalfWidth/qPerMetre-supportEdgeCoreMetres));
const double supportEdgeT=supportEdgeBlendMetres>1.0e-6
?std::clamp((horizontalDistance
@@ -1146,25 +1396,57 @@ public:
/(supportEdgeBlendMetres*qPerMetre),0.0,1.0):0.0;
const double supportEdgeSmooth=supportEdgeT*supportEdgeT
*(3.0-2.0*supportEdgeT);
const double mouthSupportThicknessVoxels=std::lerp(
0.35*supportThicknessVoxels,supportThicknessVoxels,mouthSupportT);
const double localSupportThicknessVoxels=entryMouthShoulder
?supportThicknessVoxels*(1.0-0.72*supportEdgeSmooth)
?mouthSupportThicknessVoxels*(1.0-0.72*supportEdgeSmooth)
:supportThicknessVoxels;
constexpr double supportEdgeDepth=0.35;
const double floorQ=centerZ-radius+floorOffset*qPerMetre
double mouthSupportBlend=1.0;
if(mouthTread&&std::isfinite(distanceFromMouthMetres))
{
const double mouthBlendT=std::clamp(
distanceFromMouthMetres/0.75,0.0,1.0);
mouthSupportBlend=0.75+0.25*mouthBlendT*mouthBlendT
*(3.0-2.0*mouthBlendT);
}
const double supportEdgeDepth=mouthTread?0.25:0.35;
const double mouthFloorOffset=mouthTread
?std::lerp(0.15,0.19,lowerMouthBlend):floorOffset;
const double baseFloorQ=centerZ-radius+mouthFloorOffset*qPerMetre
-supportEdgeSmooth*supportEdgeDepth*qPerMetre;
const double nativeSurfaceQ=static_cast<double>(qz)
-static_cast<double>(density)*Q;
// Keep support near the route floor; visible mouth preservation below
// blends the source field directly instead of extending this SDF top.
const double nativeFieldOffset=std::clamp(nativeSurfaceQ-baseFloorQ,
-0.80*qPerMetre,0.80*qPerMetre);
const double nativeSurfaceBlend=mouthTread?1.0:1.0-mouthReliefT;
const double nativeTreadOffset=std::clamp(
nativeFieldOffset*nativeSurfaceBlend+floorReliefOffset
-std::abs(treadRelief)*0.08*qPerMetre
*(1.0-0.25*mouthReliefT),
-0.80*qPerMetre,0.80*qPerMetre);
const double floorQ=baseFloorQ
+(mouthTread?std::clamp(nativeTreadOffset
+std::clamp(static_cast<double>(mouthTreadFieldRelief),-1.0,1.0)
*0.40*qPerMetre,-0.80*qPerMetre,0.0):0.0);
const double floorVoxels=floorQ/Q;
const double supportPositiveRangeVoxels=sparseRouteSupport
const bool signedDistanceSupport=sparseRouteSupport||mouthTread;
const double supportPositiveRangeVoxels=signedDistanceSupport
?0.35*qPerMetre/Q:1.0/Q;
if(Z<floorVoxels-localSupportThicknessVoxels
||Z>floorVoxels+supportPositiveRangeVoxels)return;
if(horizontalDistance>supportHalfWidth)return;
const double supportQueryHalfWidth=supportHalfWidth
+(mouthTread?0.45*qPerMetre:0.0);
if(horizontalDistance>supportQueryHalfWidth)return;
const double supportDistanceSquared=horizontalDistance*horizontalDistance
+(Z-floorVoxels)*(Z-floorVoxels);
if(supportDistanceSquared>=bestSupportDistanceSquared)return;
bestSupportDistanceSquared=supportDistanceSquared;
hasSupportFloor=true;
bestSupportSegmentIndex=index;
if(sparseRouteSupport)
bestSupportIsMouthTread=mouthTread;
if(signedDistanceSupport)
{
// Sparse support profiles need both sides of a signed distance
// so the sampled zero crossing stays on the tread top.
@@ -1172,11 +1454,10 @@ public:
const double bottomDistance=floorQ-localSupportThicknessVoxels*Q-qz;
const double verticalDistance=std::max(topDistance,bottomDistance);
const double sideDistance=horizontalDistance-supportHalfWidth;
const double outside=DeterministicSqrt(
std::max(sideDistance,0.0)*std::max(sideDistance,0.0)
+std::max(verticalDistance,0.0)*std::max(verticalDistance,0.0));
const double inside=std::min(std::max(sideDistance,verticalDistance),0.0);
bestSupportDensity=(outside+inside)/Q;
bestSupportDensity=SmoothMaxDistance(
static_cast<float>(sideDistance/Q),
static_cast<float>(verticalDistance/Q),
entryMouthShoulder?0.80f:0.20f);
}
else
{
@@ -1184,7 +1465,10 @@ public:
-localSupportThicknessVoxels,0.0);
}
bestSupportFloorQ=floorQ;
bestSupportUsesSignedDistance=sparseRouteSupport;
bestSupportUsesSignedDistance=signedDistanceSupport;
bestSupportMouthBlend=mouthSupportBlend;
bestSupportNativeBlend=mouthTread
?0.85*mouthSupportBlend*(1.0-supportEdgeSmooth):0.0;
};
std::ptrdiff_t supportCenterIndex=bestIndex;
if(bestIndex<lowerMouthIndex)
@@ -1231,12 +1515,46 @@ public:
// Outside bridge and short-span segments, add the tread only where the
// native field already provides a solid envelope; the capsule check keeps
// that support below the player's walk space.
if(hasSupportFloor&&(density<0.0f||passageColumnFloorSupport||shortSpanFloorSupport)
if(hasSupportFloor&&(density<0.0f||passageColumnFloorSupport
||shortSpanFloorSupport)
&&(capsuleAllowsSupport||bestSupportDensity>=0.0))
{
composed=std::min(composed,static_cast<float>(bestSupportDensity));
if(bestSupportIsMouthTread)
{
// Preserve the owner's native ground through the shallow mouth
// carve. Blending back the native field keeps its crater curvature
// and noise. The capsule test keeps neighbouring coils open.
const bool nativeRestoreCapsuleClear=density<0.0f
&&bestSupportNativeBlend>0.0&&capsuleAllowsSupport
&&!PassagePlayerCapsuleBlocksSupport(passageIndex,carveBegin,
carveEnd,lowerMouthIndex,mouthSearchBegin,mouthSearchEnd,
lastIndex,qx,qy,qz,qPerMetre,capsuleClearanceMetres);
if(nativeRestoreCapsuleClear)
{
const double nativeSurfaceDetail=std::clamp(
static_cast<double>(mouthTerrainRelief),-1.0,1.0)
*0.18*qPerMetre/Q;
const float nativeGroundWithRelief=static_cast<float>(density
-nativeSurfaceDetail);
composed=std::lerp(composed,nativeGroundWithRelief,
static_cast<float>(bestSupportNativeBlend));
}
}
else composed=std::min(composed,static_cast<float>(bestSupportDensity));
}
}
// Restore the surface route where the passage cut crossed otherwise solid
// native ground. The support stays native-solid-only, so it cannot float.
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 tileHasSupport=passageTileScopeMatches;
if(!tileHasSupport
||ActivePassageTileQueries->hasMouthSupport(passageIndex)))
composeRouteMouthSupport(passage.upperStrateIndex,
passage.upperMouthSubvoxels,true);
}
return composed;
}
@@ -4519,6 +4837,7 @@ private:
if(!Field.isValid())return false;
const std::int64_t qx=ToFixed(X),qy=ToFixed(Y),qz=ToFixed(Z);
std::int64_t Density=Field.densitySubvoxels(qx,qy,qz);
const std::int64_t NativeDensity=Density;
// The anchor-root field keeps broad lobes, but its shortest authored noise
// wavelength is below one metre. Filter that detail only around a FloatingIslands
// passage mouth so Marching Cubes does not expose voxel-sized chips on the crater
@@ -4529,11 +4848,11 @@ private:
const double densityMetres=std::abs(static_cast<double>(Density))*qToMetres;
if(densityMetres<4.0)
{
bool foundPassageMouth=false;
bool hasResolvedPassageMouth=false;
for(const auto& passage:ResolvedLayout->passages)
{
if(passage.upperStrateIndex!=StrateIndex)continue;
foundPassageMouth=true;
hasResolvedPassageMouth=true;
const double dx=(static_cast<double>(qx)-passage.upperMouthSubvoxels[0])*qToMetres;
const double dy=(static_cast<double>(qy)-passage.upperMouthSubvoxels[1])*qToMetres;
const double dz=(static_cast<double>(qz)-passage.upperMouthSubvoxels[2])*qToMetres;
@@ -4545,7 +4864,7 @@ private:
const double weight=fade*fade*(3.0-2.0*fade);
mouthFilterWeight=std::max(mouthFilterWeight,weight);
}
if(!foundPassageMouth)
if(!hasResolvedPassageMouth)
{
// The passage-free native control retains the stratum route anchors.
// Use its terminal route point as the same local mouth filter origin so
@@ -4574,12 +4893,20 @@ private:
}
}
}
if(mouthFilterWeight>0.0)
double routeNativeBlend=0.0;
const auto mouthPassage=std::find_if(ResolvedLayout->passages.begin(),
ResolvedLayout->passages.end(),[StrateIndex](const auto& passage)
{return passage.upperStrateIndex==StrateIndex;});
const double queryX=static_cast<double>(qx)*qToMetres;
const double queryY=static_cast<double>(qy)*qToMetres;
const double queryZ=static_cast<double>(qz)*qToMetres;
const bool outsideCraterMouth=mouthPassage==ResolvedLayout->passages.end()
||std::hypot(queryX-mouthPassage->upperMouthSubvoxels[0]*qToMetres,
queryY-mouthPassage->upperMouthSubvoxels[1]*qToMetres)>5.5;
if(mouthFilterWeight>0.0&&outsideCraterMouth)
{
// Preserve the resolved walk route while filtering the passage and crater field.
const double queryX=static_cast<double>(qx)*qToMetres;
const double queryY=static_cast<double>(qy)*qToMetres;
const double queryZ=static_cast<double>(qz)*qToMetres;
// Retain the native route only on the outer approach. The crater-mouth
// interior stays with the filtered ground field and cannot become a bridge.
const auto stratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[StrateIndex](const auto& value)
{return value.strateIndex==StrateIndex;});
@@ -4607,15 +4934,20 @@ private:
const double supportRadius=previous->values.size()>=4
?static_cast<double>(previous->values[3])*qToMetres:0.75;
const double floorZ=centerZ-1.20;
const double protectedRadius=supportRadius+0.75;
if((queryX-centerX)*(queryX-centerX)
+(queryY-centerY)*(queryY-centerY)
<protectedRadius*protectedRadius
&&std::abs(queryZ-floorZ)<1.25)
{
mouthFilterWeight=0.0;
break;
}
constexpr double WalkCoreRadiusMetres=0.45;
constexpr double WearShoulderMetres=0.80;
const double protectedRadius=std::min(WalkCoreRadiusMetres,
supportRadius+0.10);
const double routeDistance=std::hypot(queryX-centerX,queryY-centerY);
const double radialT=std::clamp(
(protectedRadius+WearShoulderMetres-routeDistance)
/WearShoulderMetres,0.0,1.0);
const double verticalT=std::clamp(
(3.25-std::abs(queryZ-floorZ))/2.0,0.0,1.0);
const double radialSmooth=radialT*radialT*(3.0-2.0*radialT);
const double verticalSmooth=verticalT*verticalT*(3.0-2.0*verticalT);
routeNativeBlend=std::max(routeNativeBlend,
radialSmooth*verticalSmooth);
}
previous=&anchor;
}
@@ -4638,9 +4970,19 @@ private:
// A separable [1,2,1]^3 kernel over a 2 m footprint filters sub-voxel
// root detail while retaining the signed field on both sides of the surface.
const double filtered=static_cast<double>(weightedSum)/64.0;
const std::int64_t filteredDensity=static_cast<std::int64_t>(std::llround(
std::int64_t filteredDensity=static_cast<std::int64_t>(std::llround(
std::lerp(static_cast<double>(Density),filtered,mouthFilterWeight)));
// Filtering detail at an open mouth may soften the surface, but it cannot
// turn native air into a new solid shelf over the crater.
if(NativeDensity>=0&&filteredDensity<0)filteredDensity=0;
Density=filteredDensity;
if(routeNativeBlend>0.0&&NativeDensity<0)
{
const double routePreserved=std::lerp(static_cast<double>(Density),
static_cast<double>(std::min(Density,NativeDensity)),
routeNativeBlend*0.60);
Density=static_cast<std::int64_t>(std::llround(routePreserved));
}
// Marching Cubes can retain an isolated solid with no two-metre axis
// thickness as a handful of disconnected triangles. Remove only shallow
// negative samples surrounded by air at a one-metre probe distance; a