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

@@ -252,8 +252,124 @@ namespace VoxelForgeCore
// 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;
// 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;
// version 23 uses signed-distance support for sparse SurfaceWorld-to-TunnelNetwork treads;
// version 24 narrows passage-mouth support and filters native ground detail;
// version 25 joins open-mouth treads to native terrain relief before flattening inward;
// version 26 tapers mouth width and bounds the support to the native terrain surface;
// version 27 keeps the native blend through the entrance ramp and narrows its filter;
// version 28 tapers the open end to a worn path and blends it into walkable support;
// version 29 curves the supported and carved path through both mouth shoulders;
// version 30 narrows the flat core and blends support through a noisy natural shoulder;
// version 31 adds shared, capsule-safe field relief to both open-mouth walking surfaces;
// version 32 lowers the mouth core into the bore and removes thin filtered-field remnants;
// version 33 fades relief before small detached field remnants;
// version 34 keeps the open-mouth tread within native terrain curvature and noise;
// version 35 follows the native heightfield farther into the mouth ramp;
// version 36 protects the walking line with a gradual filter fade;
// version 37 lengthens that fade and restores the last capsule margin;
// version 38 keeps native ground at the lip and blends the tread in below it;
// version 39 bends the mouth tread across the native field and restores the open bore;
// version 40 uses broad route-only filtering and smooth, low-frequency tread relief;
// version 41 rounds the mouth tread and removes filter route masks;
// version 42 clips the mouth bore at the native walking surface;
// version 43 localizes the mouth field filter without masking the surface route;
// version 44 widens the natural shoulder blend and follows lower-frequency terrain;
// version 45 tests the passage tread independently of route-anchor support;
// version 46 fades the native filter broadly along the authored surface path;
// version 47 carries the full winding route with a narrow native-height tread;
// version 48 fades route support out before the surface mouth;
// version 49 keeps the mouth filtered and restores detail on the outer route;
// version 50 limits route-field restoration to 65% of the convolution weight;
// version 51 narrows the route fade to 8 m horizontal and 6 m vertical;
// version 52 evaluates mouth support in positive-density route tiles;
// version 53 fades the supported passage tread in from native ground at both mouths;
// version 54 tapers mouth-support width and depth;
// version 55 blends that tread from native ground and applies route support after carving;
// version 56 seats route support at the audited floor;
// version 57's raised lip overfilled the mouth;
// version 58 restores the native-height lip and explicitly covers route-support queries;
// version 59 fixes route-support distance units and lengthens the worn-mouth taper;
// version 60 brings the narrow terrain-following route tread in before the outer mouth lip;
// version 61 finishes that tread by five metres and preserves its exact walking boundary;
// version 62 fades the route tread along the authored path and seats it below the capsule;
// version 63 only adds that tread through native-air gaps and fades it over two path metres;
// version 64 rounds the path-to-ground join while keeping its top below the capsule;
// version 65 tests rounded tread cross-sections and softer native-height following;
// version 66 fades the mouth carve and support together so native crater ground forms the lip;
// version 67 follows the native route height more closely and completes the entrance fade sooner;
// version 68 narrows the flat walk core and eases a shallow worn ramp into the cave floor;
// version 69 lets capsule-cleared mouth treads bridge positive-density floor gaps;
// version 70 lowers the mouth tread enough to clear the capsule's toe margin;
// version 71 keeps its rounded top inside the route's shallow support-probe band;
// version 72 adds mouth support only below its signed floor, avoiding capsule-space fill;
// version 73 carries the terrain-following route tread to the actual passage mouth;
// version 74 seats that short connection in the audited shallow support band;
// version 75 keeps the rounded path edge outside the capsule's shallow floor probes;
// version 76 carries the endpoint tread through the passage's first support-probe depth;
// version 77 joins the mouth floor to native ground through a noisy walk ramp;
// version 78 rounds the ramp shoulder and clears its initial capsule envelope;
// version 79 tested the unbuilt native-ground mouth;
// version 80 narrows the walk tread;
// version 81 adds bounded path relief and a wavering, worn shoulder;
// version 82 lowers the relief into the tread to keep its capsule clearance;
// version 83 increases the natural edge warp and recessed surface variation;
// version 84 moves that variation to the actual mouth and restores support width;
// version 85 keeps the established lower-mouth capsule profile while shaping entry mouths;
// version 86 restricts route reinforcement to native solid and recesses the worn top;
// version 87 restores its pre-carve route placement.
// version 88 removes the independent mouth cap; the native crater surface forms the entry ramp.
// version 89 removes the one-sided entry tread band as well.
// version 90 lengthens the entrance floor-carve fade to retain native ground beneath the route.
// version 91 lets a narrow mouth support follow the full native surface relief.
// version 92 derives mouth floor alignment from the entrance path for first-segment samples too.
// version 93 blends the carve against native density so a zero fade really preserves ground.
// version 94 applies the mouth floor fade only beneath the walking surface.
// version 95 seats retained native ground just below the capsule's foot clearance.
// version 96 limits that preservation to the shallow support band, away from inner coils.
// version 97 extends the native support band down to the sampled rock envelope.
// version 98 raises the support band back to the route floor while keeping it below the capsule.
// version 99 restores native-solid route support after the passage carve;
// version 100 restores a shallow, noise-shaped entrance tread under its capsule gate;
// version 101 preserves the native FI field around the resolved walking line;
// version 102 tapers the mouth filter from its 1.5 m core to the same 12.5 m edge;
// version 103 blends route protection over a metre-scale shoulder;
// version 104 composes overlapping route masks once, by maximum influence;
// version 105 widens the route blend to soften the native/filter seam;
// version 106 preserves raw negative rock under the route without unfiltering its crown;
// version 107 softens that restore and broadens the narrow upper-coil walk core;
// version 108 deepens relief and lowers route restoration to soften the remaining crease;
// version 109 blended 60% of the two-metre mouth field convolution;
// version 110 restores the full filtered contour and widens the upper walk core;
// version 111 permits the capsule-cleared signed tread to bridge the upper mouth gap;
// version 112 adds a tenth-metre margin to the rounded upper tread edge.
// version 139 keeps native ground only in a capsule-scale mouth lane and carries
// the seeded terrain contour through the full upper-mouth support;
// versions 140-145 tested short-span decks, mouth fade length, route phase and tread SDF;
// versions 146-148 tested a sunk chord, near-side phase and all-style FI spiral.
// version 149 routes non-spiral FI mouths into a tight near-side ring.
// version 150 widened that ring but left a central pillar.
// version 151 keeps the ring half a metre outside the passage bore.
// version 152 fills capsule-clear positive-density gaps under the narrow mouth tread.
// version 153 tested a one-metre entry-turn sink; it left the raised route visible and rippled the floor.
// version 154 tightens the FI ring and uses a steeper, still walkable descent to separate hidden coils.
// version 155's tighter coil removed the fin but made sharp fragments and route collisions.
// version 156 keeps the 3 m ring, overlaps the bore by 0.25 m, and seats the native tread below the capsule.
// version 157 joins the near-side FI ring at a tangent so its entrance does not kink across the crater.
// version 158 tested a descending entry coil; its exposed canopy is rejected.
// version 159 keeps the tangent and uses a steeper sub-15-degree FI descent to separate its coils.
// version 160 removed the mouth walk lane; it left the bridge and lost one support sample.
// version 161 tapered the anchored entry radius to 3 m; a fin remained at the crater lip.
// version 162 disabled native-tread restoration alone; the carve fade still preserved the ridge.
// version 163 disabled both passage restorations; it removed the path support.
// version 164 narrowed the first two inner FI route points; it enlarged the lip fin.
// version 165 disabled native-rock blending around the FI stratum route; the ridge remained.
// version 166 disabled the FI mouth support handoff; route support failed on the island approach.
// version 167 tested centerline footing on the FI approach; its bridge remained and floor ripple rose.
// version 168 removed the connector's 1 m lift; the crater crossing remained exposed.
// version 169 sank every connector 2 m; it left four FI route support probes low.
// version 170 sinks only connectors touching the anchored root by 1 m.
// Version 208 keeps the crater tread scoped to style-zero FI mouths.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 208;
enum class FieldChannel : std::uint32_t
{
@@ -447,6 +563,8 @@ namespace VoxelForgeCore
std::int64_t anchorIslandMinimumRadiusSubvoxels = 0;
std::int64_t anchorRootBottomZSubvoxels = 0;
std::int64_t anchorMouthOffsetSubvoxels = 0;
// Style-zero mouths follow the sampled island surface into the passage lip.
bool terrainFollowingRoute = false;
WorldAssemblyRouteConstraintsV1 routeConstraints;
std::uint64_t contentHash = 0;
};
@@ -671,6 +789,8 @@ namespace VoxelForgeCore
std::string stableId;
std::int32_t upperStrateIndex = -1;
std::int32_t lowerStrateIndex = -1;
// EVoxelPassageStyle numeric value used by style-specific passage support.
std::uint32_t style = 1;
std::int64_t placementRadiusSubvoxels = 0;
std::array<std::int64_t, 3> upperMouthSubvoxels{};
std::array<std::int64_t, 3> lowerMouthSubvoxels{};
+75 -7
View File
@@ -203,6 +203,7 @@ namespace floating_island_detail
HashWord(hash, static_cast<std::uint64_t>(recipe.boundarySealSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.bridgeHalfWidthSubvoxels), 8);
HashWord(hash, static_cast<std::uint64_t>(recipe.bridgeThicknessSubvoxels), 8);
if(recipe.terrainFollowingRoute)HashWord(hash,1,1);
HashWord(hash, HashRouteConstraints(recipe.routeConstraints), 8);
return hash;
}
@@ -805,9 +806,39 @@ namespace floating_island_detail
const double t = std::clamp(along / length, 0.0, 1.0);
if (alongFraction) *alongFraction = t;
const double deck = az + (static_cast<double>(edge.endSubvoxels[2]) - az) * t;
const std::uint64_t westernEdgeId = StableId(recipe.seed, 0x200000000ull,
0x425249444745ull);
const bool westernSupportConnector=edge.stableId==westernEdgeId;
if(recipe.geometryVersion>=2&&WorldAssemblyEvaluatorVersion>=183
&&westernSupportConnector)
{
const double phase=static_cast<double>(Roll(recipe.seed,edge.stableId,0x5749445448ull)
&0xffffffffull)*(2.0*Pi/4294967296.0);
const double localHalfWidth=std::max(
static_cast<double>(recipe.routeConstraints.capsuleRadiusSubvoxels+Q),
static_cast<double>(edge.halfWidthSubvoxels)
+0.48*static_cast<double>(Q)*std::sin(6.0*Pi*t+phase));
const double localThickness=static_cast<double>(edge.thicknessSubvoxels);
const double rounding=std::min({1.5*static_cast<double>(Q),
localHalfWidth*0.6,localThickness*0.45});
const double roundedX=side-(localHalfWidth-rounding);
const double roundedY=std::abs(along-0.5*length)-(0.5*length-rounding);
const double roundedZ=std::abs(z-(deck-0.5*localThickness))
-(0.5*localThickness-rounding);
const double roundedOutside=std::sqrt(
std::pow(std::max(roundedX,0.0),2)
+std::pow(std::max(roundedY,0.0),2)
+std::pow(std::max(roundedZ,0.0),2));
const double roundedInside=std::min(std::max(roundedX,
std::max(roundedY,roundedZ)),0.0);
if(topSurface)*topSurface=z-deck>=std::max(horizontal,
deck-localThickness-z);
return roundedOutside+roundedInside-rounding;
}
const double arch = recipe.geometryVersion >= 2
? 2.0 * static_cast<double>(edge.thicknessSubvoxels) * (4.0 * t * (1.0 - t))
: 0.0;
&& WorldAssemblyEvaluatorVersion < 172
? 2.0 * static_cast<double>(edge.thicknessSubvoxels) * (4.0 * t * (1.0 - t))
: 0.0;
const double upper = z - deck;
const double lower = deck - static_cast<double>(edge.thicknessSubvoxels) - arch - z;
if (topSurface) *topSurface = upper >= std::max(horizontal, lower);
@@ -1174,8 +1205,8 @@ namespace floating_island_detail
// Land each connector well inside the broad top so the full capsule footprint remains
// supported across the bridge-to-island transition, including bounded surface roughness.
// Les connecteurs recouvrent le plateau pour soutenir toute la capsule au raccord.
// Keep the narrow span exposed to open sky; a smaller fraction buries most of it in the island tops.
// Garder la passerelle etroite visible dans le vide ; une fraction plus faible l'enfouit dans les plateaux.
// Keep the capsule footprint supported while seating L37 connectors just below the native island tops.
// Garder l'appui de la capsule tout en enfouissant les connecteurs L37 juste sous les plateaux.
const double landingFraction = recipe.geometryVersion >= 2 ? 0.52 : 0.62;
const double fromReach = static_cast<double>(from.radiusSubvoxels) * landingFraction;
const double toReach = static_cast<double>(to.radiusSubvoxels) * landingFraction;
@@ -1183,7 +1214,19 @@ namespace floating_island_detail
const double sy = from.centerSubvoxels[1] + uy * fromReach;
const double ex = to.centerSubvoxels[0] - ux * toReach;
const double ey = to.centerSubvoxels[1] - uy * toReach;
const double routeDeckLift = recipe.geometryVersion >= 2 ? 1.0 * Q : 0.0;
const bool craterMouthConnector=WorldAssemblyEvaluatorVersion>=174
&&(from.ordinal==recipe.anchorIslandOrdinal
||to.ordinal==recipe.anchorIslandOrdinal);
const bool westernSupportConnector=WorldAssemblyEvaluatorVersion>=178
&&((from.ordinal==0&&to.ordinal==1)||(from.ordinal==1&&to.ordinal==0));
const double routeDeckLift=recipe.geometryVersion>=2
?(WorldAssemblyEvaluatorVersion>=178
?(westernSupportConnector
?(WorldAssemblyEvaluatorVersion>=181?-1.0
:(WorldAssemblyEvaluatorVersion>=180?-1.25
:(WorldAssemblyEvaluatorVersion>=179?-1.5:-1.0))):-2.0)
:(craterMouthConnector?-2.0
:(WorldAssemblyEvaluatorVersion>=172?-1.0:1.0)))*Q:0.0;
const double sz = IslandTopSurface(recipe, from, sx, sy) + routeDeckLift;
const double ez = IslandTopSurface(recipe, to, ex, ey) + routeDeckLift;
edge.startSubvoxels = {RoundQ(sx), RoundQ(sy), RoundQ(sz)};
@@ -1336,6 +1379,7 @@ std::optional<FloatingIslandManifestV1> FloatingIslandManifestV1::FromJson(
manifest.recipe.anchorMouthOffsetSubvoxels))
{error="floating-island geometry version 7 is missing a passage-rooted anchor field";return std::nullopt;}
}
manifest.recipe.terrainFollowingRoute=recipeValue.Get("terrain_following_route").Boolean(false);
if (!floating_island_detail::ReadU32(root.Get("schema_version"), schema)
|| !floating_island_detail::ReadHash(root.Get("content_hash"), manifestHash)
|| !floating_island_detail::ReadU32(recipeValue.Get("schema_version"), manifest.recipe.schemaVersion)
@@ -1495,6 +1539,7 @@ std::string FloatingIslandManifestV1::ToJson() const
<< ",\"anchor_island_minimum_radius_q\":" << recipe.anchorIslandMinimumRadiusSubvoxels
<< ",\"anchor_root_bottom_z_q\":" << recipe.anchorRootBottomZSubvoxels
<< ",\"anchor_mouth_offset_q\":" << recipe.anchorMouthOffsetSubvoxels;
if(recipe.terrainFollowingRoute)out << ",\"terrain_following_route\":true";
out << ",\"blend_radius_q\":" << recipe.blendRadiusSubvoxels
<< ",\"contour_warp_q\":" << recipe.contourWarpSubvoxels
<< ",\"roughness_q\":" << recipe.roughnessSubvoxels
@@ -1746,6 +1791,26 @@ std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
const std::int64_t y = floating_island_detail::RoundQ(
static_cast<double>(a.centerSubvoxels[1]) + dy * t);
std::int64_t routeX=x,routeY=y;
if(WorldAssemblyEvaluatorVersion>=192&&recipe.terrainFollowingRoute
&&recipe.geometryVersion>=5
&&segment+2==count&&pieces>1)
{
const auto& anchor=islands[recipe.anchorIslandOrdinal];
const std::uint32_t side=static_cast<std::uint32_t>(
floating_island_detail::UnitQ16(recipe.seed,anchor.stableId,
0x414e43484f524d50ull)&1u);
const double length=std::hypot(dx,dy);
const double bend=10.0*floating_island_detail::Q
*std::pow(std::sin(floating_island_detail::Pi*t),2.0);
const double sign=side?-1.0:1.0;
if(length>0.0)
{
routeX=floating_island_detail::RoundQ(
static_cast<double>(routeX)+sign*(-dy/length)*bend);
routeY=floating_island_detail::RoundQ(
static_cast<double>(routeY)+sign*(dx/length)*bend);
}
}
if(recipe.geometryVersion>=5&&segment+2==count&&piece==pieces)
{
const auto& anchor=islands[recipe.anchorIslandOrdinal];
@@ -1762,8 +1827,11 @@ std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
routeX=floating_island_detail::RoundQ(mouthX);
routeY=floating_island_detail::RoundQ(mouthY);
}
const auto surface = floating_island_detail::FindCapsuleFootingTopZ(
recipe, manifest.plan, routeX, routeY);
const auto surface = WorldAssemblyEvaluatorVersion>=199
&&recipe.terrainFollowingRoute
?floating_island_detail::FindSurfaceZ(recipe,manifest.plan,routeX,routeY)
:floating_island_detail::FindCapsuleFootingTopZ(
recipe,manifest.plan,routeX,routeY);
if (!surface)
{ error = "island planner could not find a full capsule-width support surface along its connector chain"; return std::nullopt; }
WorldAssemblyRoutePointV1 point;
+69 -9
View File
@@ -221,6 +221,7 @@ namespace
out<<"{\"stable_id\":"<<LayoutEscape(p.stableId)
<<",\"upper_strate_index\":"<<p.upperStrateIndex
<<",\"lower_strate_index\":"<<p.lowerStrateIndex
<<",\"style\":"<<p.style
<<",\"placement_radius_q\":"<<p.placementRadiusSubvoxels
<<",\"upper_mouth_q\":["<<p.upperMouthSubvoxels[0]<<','
<<p.upperMouthSubvoxels[1]<<','<<p.upperMouthSubvoxels[2]
@@ -683,6 +684,8 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
ResolvedLayoutPassageV1 p;p.stableId=LayoutString(v,"stable_id");
p.upperStrateIndex=static_cast<std::int32_t>(LayoutInt(v,"upper_strate_index"));
p.lowerStrateIndex=static_cast<std::int32_t>(LayoutInt(v,"lower_strate_index"));
if(v.Get("style").IsNumber())
p.style=static_cast<std::uint32_t>(LayoutInt(v,"style"));
p.placementRadiusSubvoxels=LayoutInt(v,"placement_radius_q");
p.mouthRadiusSubvoxels=LayoutInt(v,"mouth_radius_q");p.midRadiusSubvoxels=LayoutInt(v,"mid_radius_q");
const auto& upper=v.Get("upper_mouth_q").AsArray();
@@ -808,6 +811,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
for(std::uint32_t connection=0;connection<connectionCount;++connection)
{
ResolvedLayoutPassageV1 passage;
passage.style=style;
passage.stableId="passage/"+std::to_string(upper.strateIndex)+"-"
+std::to_string(lower.strateIndex)+"/"+std::to_string(connection);
passage.upperStrateIndex=upper.strateIndex;passage.lowerStrateIndex=lower.strateIndex;
@@ -886,22 +890,45 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
std::max(ringRadius,clearRingRadius));
}
if(anchoredIslandPassage)
ringRadius=std::min(ringRadius,mouthRadius+qPerMetre/2);
{
const std::int64_t craterRimRouteRadius=VoxelForgeCore::WorldAssemblyEvaluatorVersion>=185
&&style==0&&!anchoredSpiral&&passageAnchor&&passageAnchor->values.size()>3
?std::max<std::int64_t>(midRadius,
passageAnchor->values[3]+2*qPerMetre)
:mouthRadius+qPerMetre/2;
ringRadius=std::min(ringRadius,anchoredSpiral
?mouthRadius+qPerMetre/2:craterRimRouteRadius);
}
std::uint32_t phase=static_cast<std::uint32_t>(PassageSeed(seed,
upper.strateIndex,lower.strateIndex,connection,0x50484153u)%16);
if(hasUpperRoute)
{
std::int64_t bestAlignment=std::numeric_limits<std::int64_t>::min();
const std::uint32_t hashedPhase=phase;
const std::int64_t phaseX=style==0&&anchoredIslandPassage
&&!anchoredSpiral&&passageAnchor
?upperMouth[0]-passageAnchor->values[0]
:(hasUpperTangent?upperTangent[0]:upperMouth[0]);
const std::int64_t phaseY=style==0&&anchoredIslandPassage
&&!anchoredSpiral&&passageAnchor
?upperMouth[1]-passageAnchor->values[1]
:(hasUpperTangent?upperTangent[1]:upperMouth[1]);
for(std::uint32_t offset=0;offset<16;++offset)
{
const std::uint32_t candidate=static_cast<std::uint32_t>(
(hashedPhase+offset)%16);
const std::int64_t alignment=(hasUpperTangent?upperTangent[0]:upperMouth[0])
*directions[candidate][0]
+(hasUpperTangent?upperTangent[1]:upperMouth[1])
*directions[candidate][1];
if(alignment>bestAlignment)
const std::int64_t alignment=phaseX*directions[candidate][0]
+phaseY*directions[candidate][1];
if(style==0&&anchoredIslandPassage&&!anchoredSpiral&&passageAnchor)
{
const std::int64_t targetProjection=ringRadius*65536;
if(alignment<=0)continue;
const std::int64_t tangentError=std::abs(alignment-targetProjection);
if(tangentError<bestAlignment
||bestAlignment==std::numeric_limits<std::int64_t>::min())
{bestAlignment=tangentError;phase=candidate;}
}
else if(alignment>bestAlignment)
{bestAlignment=alignment;phase=candidate;}
}
}
@@ -924,7 +951,18 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
}
bool clockwise=(PassageSeed(seed,upper.strateIndex,lower.strateIndex,
connection,0x44495245u)&1u)!=0;
if(anchoredSpiral&&passageAnchor&&hasUpperTangent)
if(style==0&&anchoredIslandPassage&&!anchoredSpiral&&passageAnchor)
{
const auto& ringDirection=directions[phase];
const double radiusX=static_cast<double>(ringRadius)*ringDirection[0]/65536.0;
const double radiusY=static_cast<double>(ringRadius)*ringDirection[1]/65536.0;
const double anchorX=static_cast<double>(passageAnchor->values[0]);
const double anchorY=static_cast<double>(passageAnchor->values[1]);
const double connectorX=anchorX+radiusX-upperMouth[0];
const double connectorY=anchorY+radiusY-upperMouth[1];
clockwise=connectorX*(-radiusY)+connectorY*radiusX>=0.0;
}
else if(anchoredSpiral&&passageAnchor&&hasUpperTangent)
{
const double radialX=static_cast<double>(upperMouth[0]-passageAnchor->values[0]);
const double radialY=static_cast<double>(upperMouth[1]-passageAnchor->values[1]);
@@ -1068,7 +1106,8 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
hasAnchorHelix=helixEndIndex>0;
}
const std::int64_t routeGradeQ16=hasAnchorHelix
&&lower.generatorKind=="SurfaceWorld"?8000:TargetGradeQ16;
&&lower.generatorKind=="SurfaceWorld"?8000
:style==0&&anchoredIslandPassage&&!anchoredSpiral?15500:TargetGradeQ16;
const std::array<std::int64_t,2> firstRing=ringPoint(phase);
if(hasAnchorHelix&&pathStyle==1u)
{
@@ -1168,7 +1207,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
pushXY(ringPoint(lowerPhase));
}
pushXY({lowerMouth[0],lowerMouth[1]});totalHorizontal=measurePath();
const std::size_t lowerMouthIndex=xy.size()-1;
std::size_t lowerMouthIndex=xy.size()-1;
std::vector<std::array<std::int64_t,3>> lowerLandingPoints;
if(hasLowerTangent)
{
@@ -1202,6 +1241,27 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
if(lowerLandingPoints.empty())
{error=passage.stableId+" lower route has no landing length inside PassageConfig.DistanceMax";return false;}
}
if(VoxelForgeCore::WorldAssemblyEvaluatorVersion>=188
&&style==0&&anchoredIslandPassage&&!anchoredSpiral&&xy.size()>4)
{
std::vector<std::array<std::int64_t,2>> refined;
refined.reserve(xy.size()*2);
for(std::size_t index=0;index+1<xy.size();++index)
{
refined.push_back(xy[index]);
if(index<3)continue;
refined.push_back({
static_cast<std::int64_t>(std::llround(0.5*(
static_cast<double>(xy[index][0])
+static_cast<double>(xy[index+1][0])))),
static_cast<std::int64_t>(std::llround(0.5*(
static_cast<double>(xy[index][1])
+static_cast<double>(xy[index+1][1]))))});
}
refined.push_back(xy.back());
if(lowerMouthIndex>3)lowerMouthIndex+=lowerMouthIndex-3;
xy.swap(refined);
}
totalHorizontal=measurePath();
if(loops>=256||totalHorizontal==0)
{error=passage.stableId+" could not fit a supported grade inside the resolved placement radius";return false;}
+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