wip(assembler): round L36 -- tunnel floor without washboard; FI mouth without shards (evaluator v23)

The walk tread was built per segment as a one-sided density clamp, so its crossing with the native
field moved at segment joins (L35: 2.25-2.48 cm washboard). Treads now overlap up to 2 m as bounded
signed-distance slabs (0.06 cm residual). The FI mouth's crater detail and tread met without a smooth
join; v23 filters the local field, drops thin isolated solids and smooth-unions the tread with the
terrain. New floor ripple and mouth fragment/crease audits with L35 as failing controls. 24/24
passage rows, 30/30 determinism, 16/16 invariants, LOD0-8 rings; cost within noise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-22 13:01:54 +02:00
1 parent ba017077d1
commit 8be1c4d9ef
27 files changed
+1787 -55

No files matched your search

@@ -250,8 +250,10 @@ namespace VoxelForgeCore
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 2;
// Version 14 adds shared passage columns; version 16 smooths owner Gradient faces;
// version 20 restores full Gradient amplitude and uses a continuous signed-distance band union.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 21;
// version 20 restores full Gradient amplitude and uses a continuous signed-distance band union;
// version 22 overlaps tread sections and smooths FloatingIslands passage-mouth features;
// version 23 uses signed-distance support for sparse SurfaceWorld-to-TunnelNetwork treads.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 23;
enum class FieldChannel : std::uint32_t
{
+288 -48
View File
@@ -727,8 +727,9 @@ public:
}
if(routeCount<2)return;
const std::size_t lastSegment=routeCount-1;
const double floorThickness=1000000.0/std::max<std::uint32_t>(1,
ResolvedLayout->coreVoxelSizeMicrometres);
constexpr double MouthSupportBlendMetres=1.5;
const double mouthBlendWidth=MouthSupportBlendMetres*qPerMetre;
double mouthSupportDistance=std::numeric_limits<double>::infinity();
for(std::size_t index=0;index<lastSegment;++index)
{
const auto& a=*route[index];const auto& b=*route[index+1];
@@ -743,12 +744,25 @@ public:
?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 horizontalX=qx-centerX,horizontalY=qy-centerY;
if(horizontalX*horizontalX+horizontalY*horizontalY>halfWidth*halfWidth)continue;
const double floorVoxels=(centerZ-1.20*qPerMetre)/Q;
if(Z<floorVoxels-floorThickness||Z>floorVoxels+1.0/Q)continue;
composed=std::min(composed,static_cast<float>(
std::clamp(Z-floorVoxels,-floorThickness,0.0)));
const double horizontalDistance=DeterministicSqrt(
horizontalX*horizontalX+horizontalY*horizontalY);
const double floorQ=centerZ-1.20*qPerMetre;
const double sideDistance=horizontalDistance-halfWidth;
const double topDistance=qz-floorQ;
const double bottomDistance=floorQ-qPerMetre-qz;
const double verticalDistance=std::max(topDistance,bottomDistance);
if(sideDistance>mouthBlendWidth||verticalDistance>mouthBlendWidth)continue;
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);
const double signedDistance=outside+inside;
mouthSupportDistance=std::min(mouthSupportDistance,signedDistance);
}
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
@@ -765,6 +779,19 @@ public:
{
const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue;
const auto supportStratum=[&](std::int32_t strateIndex)
->const VoxelForgeCore::ResolvedLayoutStratumV2*
{
const auto it=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[strateIndex](const auto& stratum)
{return stratum.strateIndex==strateIndex;});
return it==ResolvedLayout->strates.end()?nullptr:&*it;
};
const auto* supportUpper=supportStratum(passage.upperStrateIndex);
const auto* supportLower=supportStratum(passage.lowerStrateIndex);
const bool surfaceToTunnelSupport=supportUpper&&supportLower
&&supportUpper->generatorKind=="SurfaceWorld"
&&supportLower->generatorKind=="TunnelNetwork";
const double firstFloorQ=passage.controlPoints.front().positionSubvoxels[2]
-passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ;
const double lastFloorQ=passage.controlPoints.back().positionSubvoxels[2]
@@ -880,7 +907,20 @@ public:
std::ptrdiff_t bestScanIndex=std::numeric_limits<std::ptrdiff_t>::max();
std::ptrdiff_t bestSupportSegmentIndex=0;
bool hasSupportFloor=false;
double bestSupportDensity=0.0;
double bestSupportDistanceSquared=std::numeric_limits<double>::infinity();
double bestSupportDensity=std::numeric_limits<double>::infinity();
double bestSupportFloorQ=0.0;
bool bestSupportUsesSignedDistance=false;
std::ptrdiff_t selectedSupportSegmentIndex=-1;
const double lowerMouthDx=qx-passage.lowerMouthSubvoxels[0];
const double lowerMouthDy=qy-passage.lowerMouthSubvoxels[1];
const double lowerMouthDz=qz-passage.lowerMouthSubvoxels[2];
const double lowerMouthDistance=DeterministicSqrt(
lowerMouthDx*lowerMouthDx+lowerMouthDy*lowerMouthDy
+lowerMouthDz*lowerMouthDz)/qPerMetre;
const double lowerMouthT=std::clamp((20.0-lowerMouthDistance)/12.0,0.0,1.0);
const double lowerMouthBlend=lowerMouthT*lowerMouthT
*(3.0-2.0*lowerMouthT);
const auto considerCarveSegment=[&](std::size_t segmentIndex,int scanPhase)
{
const std::ptrdiff_t index=static_cast<std::ptrdiff_t>(segmentIndex);
@@ -992,7 +1032,7 @@ 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 double wallReliefWeight=1.0-0.80*verticalWallAlignment;
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;
if(IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(bestIndex)))
@@ -1004,38 +1044,48 @@ public:
// 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));
double bestSupportDistanceSquared=std::numeric_limits<double>::infinity();
const auto considerSupportSegment=[&](std::ptrdiff_t index)
{
const auto& segmentA=passage.controlPoints[static_cast<std::size_t>(index)];
const auto& segmentB=passage.controlPoints[static_cast<std::size_t>(index+1)];
const double dx=static_cast<double>(segmentB.positionSubvoxels[0])-segmentA.positionSubvoxels[0];
const double dy=static_cast<double>(segmentB.positionSubvoxels[1])-segmentA.positionSubvoxels[1];
const double dz=static_cast<double>(segmentB.positionSubvoxels[2])-segmentA.positionSubvoxels[2];
const double radiusA=segmentA.radiusSubvoxels;
const double radiusB=segmentB.radiusSubvoxels;
const bool entryMouthShoulder=index<=2;
// Keep the tread within the probe band and below the capsule's
// lowest sampled point.
const double floorOffset=IsPassageShortSpanSegment(passageIndex,
const bool bridgeDeck=IsPassageShortSpanSegment(passageIndex,
static_cast<std::size_t>(index))
||IsPassageColumnSegment(passageIndex,static_cast<std::size_t>(index))
||IsPassageColumnSegment(passageIndex,static_cast<std::size_t>(index));
// Keep a small capsule margin at regular treads and restore most of
// the nominal height smoothly as a tread approaches its landing.
double floorOffset=bridgeDeck
?PassageBridgeDeckOffsetMetres(passageIndex,static_cast<std::size_t>(index))
:0.20;
:std::lerp(0.15,0.20,lowerMouthBlend);
const double horizontalLengthSq=dx*dx+dy*dy;
const bool overlapSupportJoin=index+12<lastIndex
&&!IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(index))
&&(index==0||(!IsPassageShortSpanSegment(passageIndex,
static_cast<std::size_t>(index-1))));
const bool smoothSupportJoin=index>0&&overlapSupportJoin
&&!IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(index+1));
const double projection=horizontalLengthSq>0.0
?((qx-segmentA.positionSubvoxels[0])*dx
+(qy-segmentA.positionSubvoxels[1])*dy)/horizontalLengthSq:bestT;
// Extend tread segments by a fixed distance through route knots. A
// fractional extension made the support width change with segment length.
const double overlapFraction=overlapSupportJoin&&horizontalLengthSq>0.0
?std::min(0.5,2.0*qPerMetre/DeterministicSqrt(horizontalLengthSq)):0.0;
const double supportT=horizontalLengthSq>0.0
?std::clamp(((qx-segmentA.positionSubvoxels[0])*dx
+(qy-segmentA.positionSubvoxels[1])*dy)/horizontalLengthSq,0.0,1.0)
:bestT;
?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 centerZ=segmentA.positionSubvoxels[2]+supportT*(
static_cast<double>(segmentB.positionSubvoxels[2])-segmentA.positionSubvoxels[2]);
double radius=segmentA.radiusSubvoxels+supportT*(
static_cast<double>(segmentB.radiusSubvoxels)-segmentA.radiusSubvoxels);
const bool smoothSupportJoin=index>0&&index+12<lastIndex
&&!IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(index-1))
&&!IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(index))
&&!IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(index+1));
if(smoothSupportJoin)
{
const auto& p0=passage.controlPoints[static_cast<std::size_t>(index-1)];
@@ -1063,7 +1113,15 @@ public:
// Support belongs just below the avatar's foot plane. Queries within
// the capsule body, or a neighbouring coil's overlapping bore, must
// not turn that tread into a ceiling or mid-air shelf.
if(supportQueryDepth<0.93*qPerMetre||supportQueryDepth>1.30*qPerMetre)return;
// Sparse authored routes use the full signed distance around the top;
// 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 minimumSupportQueryDepth=index==selectedSupportSegmentIndex
&&sparseRouteSupport?0.65:0.93;
if(supportQueryDepth<minimumSupportQueryDepth*qPerMetre
||supportQueryDepth>1.30*qPerMetre)return;
const bool fallbackSupport=qz>=supportPlayerCenterZ-1.50*qPerMetre;
const double supportThicknessVoxels=fallbackSupport
?qPerMetre/Q:0.5*qPerMetre/Q;
@@ -1072,12 +1130,8 @@ public:
// capsule on the neighbouring turn.
const bool lowerMouthBodyOverlap=index>=lowerMouthIndex-2
&&supportQueryDepth<0.98*qPerMetre;
const double supportEdgeNoise=entryMouthShoulder?std::clamp(
static_cast<double>(VF_FieldPassageGroundGrain(static_cast<float>(X),
static_cast<float>(Y),
static_cast<std::uint32_t>(Owner?Owner->Seed:0))),-1.0,1.0):0.0;
const double supportHalfWidthMetres=lowerMouthBodyOverlap?0.90
:entryMouthShoulder?1.20+0.20*supportEdgeNoise:1.25;
:entryMouthShoulder?1.20:1.25;
const double supportHalfWidth=std::min(radius,
supportHalfWidthMetres*qPerMetre);
// Keep the walk core within a 0.50 m span with shallow relief, then
@@ -1095,30 +1149,50 @@ public:
const double localSupportThicknessVoxels=entryMouthShoulder
?supportThicknessVoxels*(1.0-0.72*supportEdgeSmooth)
:supportThicknessVoxels;
const double supportEdgeDepth=entryMouthShoulder
?0.35+0.15*supportEdgeNoise:0.35;
constexpr double supportEdgeDepth=0.35;
const double floorQ=centerZ-radius+floorOffset*qPerMetre
+supportEdgeNoise*(horizontalDistance>0.60*qPerMetre
?0.35:0.14)*qPerMetre
-supportEdgeSmooth*supportEdgeDepth*qPerMetre;
const double floorVoxels=floorQ/Q;
if(Z<floorVoxels-localSupportThicknessVoxels||Z>floorVoxels+1.0/Q)return;
const double supportPositiveRangeVoxels=sparseRouteSupport
?0.35*qPerMetre/Q:1.0/Q;
if(Z<floorVoxels-localSupportThicknessVoxels
||Z>floorVoxels+supportPositiveRangeVoxels)return;
if(horizontalDistance>supportHalfWidth)return;
const double supportDistanceSq=horizontalDistance*horizontalDistance
const double supportDistanceSquared=horizontalDistance*horizontalDistance
+(Z-floorVoxels)*(Z-floorVoxels);
if(supportDistanceSq>=bestSupportDistanceSquared)return;
bestSupportDistanceSquared=supportDistanceSq;
if(supportDistanceSquared>=bestSupportDistanceSquared)return;
bestSupportDistanceSquared=supportDistanceSquared;
hasSupportFloor=true;
bestSupportSegmentIndex=index;
bestSupportDensity=std::clamp(Z-floorVoxels,
-localSupportThicknessVoxels,0.0);
if(sparseRouteSupport)
{
// Sparse support profiles need both sides of a signed distance
// so the sampled zero crossing stays on the tread top.
const double topDistance=qz-floorQ;
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;
}
else
{
bestSupportDensity=std::clamp(Z-floorVoxels,
-localSupportThicknessVoxels,0.0);
}
bestSupportFloorQ=floorQ;
bestSupportUsesSignedDistance=sparseRouteSupport;
};
std::ptrdiff_t supportCenterIndex=bestIndex;
if(bestIndex<lowerMouthIndex)
{
// A helix can have multiple centerlines at one XY. Select the tread
// whose floor is just below this density query, not the closest wall.
const double supportProfileTarget=qz+0.93*qPerMetre;
const double supportProfileOffset=surfaceToTunnelSupport?1.13:0.93;
const double supportProfileTarget=qz+supportProfileOffset*qPerMetre;
const auto supportProfile=std::lower_bound(passage.controlPoints.begin(),mainEnd,
supportProfileTarget,[&](const auto& point,double value)
{return floorProfile(point)>value;});
@@ -1132,6 +1206,8 @@ public:
const std::ptrdiff_t supportBegin=std::max<std::ptrdiff_t>(0,supportCenterIndex-1);
const std::ptrdiff_t supportEnd=std::min<std::ptrdiff_t>(lastIndex-1,
supportCenterIndex+1);
selectedSupportSegmentIndex=supportCenterIndex>0
?supportCenterIndex-1:supportCenterIndex;
for(std::ptrdiff_t index=supportBegin;index<=supportEnd;++index)
considerSupportSegment(index);
// Keep route treads inside the native rock envelope. Carving may reopen the
@@ -1142,13 +1218,24 @@ public:
const bool shortSpanFloorSupport=hasSupportFloor
&&IsPassageShortSpanSegment(passageIndex,
static_cast<std::size_t>(bestSupportSegmentIndex));
const bool capsuleAllowsSupport=passageTileScopeMatches
const double capsuleClearanceMetres=bestSupportUsesSignedDistance?0.045:0.05;
const double capsuleQueryZ=bestSupportUsesSignedDistance?bestSupportFloorQ:qz;
const bool supportCapsuleProfileAvailable=passageIndex<PassageNearestNodes.size()
&&!PassageNearestNodes[passageIndex].empty();
const bool capsuleAllowsSupport=(passageTileScopeMatches
||(bestSupportUsesSignedDistance&&supportCapsuleProfileAvailable))
?!PassagePlayerCapsuleBlocksSupport(passageIndex,carveBegin,carveEnd,
lowerMouthIndex,mouthSearchBegin,mouthSearchEnd,lastIndex,qx,qy,qz,qPerMetre)
:bestPlayerCapsuleSignedDistance>=0.05*qPerMetre;
lowerMouthIndex,mouthSearchBegin,mouthSearchEnd,lastIndex,qx,qy,
capsuleQueryZ,qPerMetre,capsuleClearanceMetres)
:bestPlayerCapsuleSignedDistance>=capsuleClearanceMetres*qPerMetre;
// 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)
&&capsuleAllowsSupport)
&&(capsuleAllowsSupport||bestSupportDensity>=0.0))
{
composed=std::min(composed,static_cast<float>(bestSupportDensity));
}
}
return composed;
}
@@ -2990,7 +3077,8 @@ private:
}
bool PassagePlayerCapsuleBlocksSupportNode(std::size_t passageIndex,int nodeIndex,
std::size_t first,std::size_t end,double qx,double qy,double qz,double qPerMetre) const
std::size_t first,std::size_t end,double qx,double qy,double qz,double qPerMetre,
double minimumClearanceMetres) const
{
if(passageIndex>=PassageNearestNodes.size()||nodeIndex<0)return false;
const auto& nodes=PassageNearestNodes[passageIndex];
@@ -3026,26 +3114,26 @@ private:
const double vertical=std::max(0.0,std::abs(qz-centerZ)-0.54*qPerMetre);
const double distance=DeterministicSqrt(horizontalX*horizontalX+horizontalY*horizontalY
+vertical*vertical)-0.34*qPerMetre;
if(distance<0.05*qPerMetre)return true;
if(distance<minimumClearanceMetres*qPerMetre)return true;
}
return false;
}
return PassagePlayerCapsuleBlocksSupportNode(passageIndex,node.left,first,end,
qx,qy,qz,qPerMetre)
qx,qy,qz,qPerMetre,minimumClearanceMetres)
||PassagePlayerCapsuleBlocksSupportNode(passageIndex,node.right,first,end,
qx,qy,qz,qPerMetre);
qx,qy,qz,qPerMetre,minimumClearanceMetres);
}
bool PassagePlayerCapsuleBlocksSupport(std::size_t passageIndex,
std::ptrdiff_t carveBegin,std::ptrdiff_t carveEnd,std::ptrdiff_t lowerMouthIndex,
std::ptrdiff_t mouthSearchBegin,std::ptrdiff_t mouthSearchEnd,std::ptrdiff_t lastIndex,
double qx,double qy,double qz,double qPerMetre) const
double qx,double qy,double qz,double qPerMetre,double minimumClearanceMetres) const
{
const auto blocked=[&](std::ptrdiff_t first,std::ptrdiff_t last)
{
return first<=last&&PassagePlayerCapsuleBlocksSupportNode(passageIndex,0,
static_cast<std::size_t>(first),static_cast<std::size_t>(last+1),
qx,qy,qz,qPerMetre);
qx,qy,qz,qPerMetre,minimumClearanceMetres);
};
return blocked(carveBegin,carveEnd)
||blocked(0,std::min<std::ptrdiff_t>(8,lowerMouthIndex)-1)
@@ -4429,7 +4517,159 @@ private:
const VoxelForgeCore::FloatingIslandFieldV1 Field(
Plan->manifest.recipe,Plan->manifest.plan);
if(!Field.isValid())return false;
std::int64_t Density=Field.densitySubvoxels(ToFixed(X),ToFixed(Y),ToFixed(Z));
const std::int64_t qx=ToFixed(X),qy=ToFixed(Y),qz=ToFixed(Z);
std::int64_t Density=Field.densitySubvoxels(qx,qy,qz);
// 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
// wall. The broad root profile is unchanged outside this local mouth envelope.
double mouthFilterWeight=0.0;
const double qToMetres=static_cast<double>(ResolvedLayout->coreVoxelSizeMicrometres)
/(static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)*1000000.0);
const double densityMetres=std::abs(static_cast<double>(Density))*qToMetres;
if(densityMetres<4.0)
{
bool foundPassageMouth=false;
for(const auto& passage:ResolvedLayout->passages)
{
if(passage.upperStrateIndex!=StrateIndex)continue;
foundPassageMouth=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;
const double distance=std::sqrt(dx*dx+dy*dy+dz*dz);
constexpr double FilterCoreRadiusMetres=8.5;
constexpr double FilterOuterRadiusMetres=12.5;
const double fade=std::clamp((FilterOuterRadiusMetres-distance)
/(FilterOuterRadiusMetres-FilterCoreRadiusMetres),0.0,1.0);
const double weight=fade*fade*(3.0-2.0*fade);
mouthFilterWeight=std::max(mouthFilterWeight,weight);
}
if(!foundPassageMouth)
{
// The passage-free native control retains the stratum route anchors.
// Use its terminal route point as the same local mouth filter origin so
// both native and composed fields receive the same root smoothing.
const auto stratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[StrateIndex](const auto& value)
{return value.strateIndex==StrateIndex;});
if(stratum!=ResolvedLayout->strates.end())
{
const VoxelForgeCore::ResolvedLayoutAnchorV2* terminal=nullptr;
for(const auto& anchor:stratum->anchors)
if(anchor.kind=="route_point"&&anchor.values.size()>=3)
terminal=&anchor;
if(terminal)
{
const double dx=static_cast<double>(qx-terminal->values[0])*qToMetres;
const double dy=static_cast<double>(qy-terminal->values[1])*qToMetres;
const double dz=static_cast<double>(qz-terminal->values[2])*qToMetres;
const double distance=std::sqrt(dx*dx+dy*dy+dz*dz);
constexpr double FilterCoreRadiusMetres=8.5;
constexpr double FilterOuterRadiusMetres=12.5;
const double fade=std::clamp((FilterOuterRadiusMetres-distance)
/(FilterOuterRadiusMetres-FilterCoreRadiusMetres),0.0,1.0);
mouthFilterWeight=fade*fade*(3.0-2.0*fade);
}
}
}
}
if(mouthFilterWeight>0.0)
{
// 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;
const auto stratum=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[StrateIndex](const auto& value)
{return value.strateIndex==StrateIndex;});
if(stratum!=ResolvedLayout->strates.end())
{
const VoxelForgeCore::ResolvedLayoutAnchorV2* previous=nullptr;
for(const auto& anchor:stratum->anchors)
{
if(anchor.kind!="route_point"||anchor.values.size()<3)continue;
if(previous)
{
const double ax=static_cast<double>(previous->values[0])*qToMetres;
const double ay=static_cast<double>(previous->values[1])*qToMetres;
const double az=static_cast<double>(previous->values[2])*qToMetres;
const double dx=(static_cast<double>(anchor.values[0])
-previous->values[0])*qToMetres;
const double dy=(static_cast<double>(anchor.values[1])
-previous->values[1])*qToMetres;
const double lengthSquared=dx*dx+dy*dy;
const double t=lengthSquared>0.0?std::clamp(
((queryX-ax)*dx+(queryY-ay)*dy)/lengthSquared,0.0,1.0):0.0;
const double centerX=ax+t*dx,centerY=ay+t*dy;
const double centerZ=az+t*(static_cast<double>(anchor.values[2])
-previous->values[2])*qToMetres;
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;
}
}
previous=&anchor;
}
}
}
if(mouthFilterWeight>0.0)
{
constexpr std::int64_t FilterStep=8*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
std::int64_t weightedSum=0;
for(int dz=-1;dz<=1;++dz)
for(int dy=-1;dy<=1;++dy)
for(int dx=-1;dx<=1;++dx)
{
const std::int64_t weight=(dx==0?2:1)*(dy==0?2:1)*(dz==0?2:1);
const std::int64_t sample=(dx==0&&dy==0&&dz==0)?Density:
Field.densitySubvoxels(qx+dx*FilterStep,qy+dy*FilterStep,
qz+dz*FilterStep);
weightedSum+=sample*weight;
}
// 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::lerp(static_cast<double>(Density),filtered,mouthFilterWeight)));
Density=filteredDensity;
// 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
// walk floor or wall has solid material on at least one side of this test.
constexpr double FragmentProbeMetres=1.0;
const std::int64_t fragmentStep=static_cast<std::int64_t>(std::llround(
FragmentProbeMetres/qToMetres));
if(mouthFilterWeight>=0.25&&Density<0
&&static_cast<double>(-Density)*qToMetres<FragmentProbeMetres)
{
bool surroundedByAir=true;
std::int64_t minimumAirDensity=std::numeric_limits<std::int64_t>::max();
for(std::size_t axis=0;axis<3&&surroundedByAir;++axis)
for(const int sign:{-1,1})
{
std::array<std::int64_t,3> sample{qx,qy,qz};
sample[axis]+=sign*fragmentStep;
const std::int64_t neighbor=Field.densitySubvoxels(
sample[0],sample[1],sample[2]);
if(neighbor<=0)
{
surroundedByAir=false;
break;
}
minimumAirDensity=std::min(minimumAirDensity,neighbor);
}
if(surroundedByAir)Density=std::max(Density,minimumAirDensity);
}
}
const std::int64_t Bottom=Plan->manifest.recipe.layoutMinimumZSubvoxels;
const std::int64_t Top=Plan->manifest.recipe.layoutMaximumZSubvoxels;
const std::int64_t Seal=Plan->manifest.recipe.boundarySealSubvoxels;