wip(assembler): round L33 -- SurfaceWorld can be an upper strate; FloorProfileBlend measures seams
Route planning for an upper SurfaceWorld failed on long candidate chords over steep relief; chords are now capped to the arrival/departure radii on an 8 m centre grid with an origin-proximity cost (21.5 m walk, about 4 deg). The departure passage starts at the SW ground and tunnels down through rock. FI -> SW -> TN fixture: all 4 styles pass every passage audit on both compilers. FloorProfileBlend now checks endpoint height/tangent seams (step and tangent-step negative controls detected). Owner world hashes unchanged; 30/30 determinism; 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:
1 parent
d0158c6019
commit
b7a47bef95
17 files changed
+1116
-32
No files matched your search
@@ -788,10 +788,13 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
cfg->distanceMaximumSubvoxels):8*qPerMetre;
|
||||
const std::int64_t placementRadius=std::max<std::int64_t>(1,outer);
|
||||
// Leave room for the capsule and bounded wall relief at the floor/wall seam.
|
||||
const std::int64_t mouthRadius=std::max<std::int64_t>(static_cast<std::int64_t>(std::llround(1.5*qPerMetre)),
|
||||
const bool upperIsSurfaceWorld=upper.generatorKind=="SurfaceWorld";
|
||||
const std::int64_t minimumMouthRadius=static_cast<std::int64_t>(std::llround(
|
||||
(upperIsSurfaceWorld?2.0:1.5)*qPerMetre));
|
||||
const std::int64_t mouthRadius=std::max<std::int64_t>(minimumMouthRadius,
|
||||
cfg?cfg->mouthRadiusSubvoxels:0);
|
||||
const std::int64_t midRadius=std::max<std::int64_t>(
|
||||
static_cast<std::int64_t>(std::llround(1.5*qPerMetre)),
|
||||
static_cast<std::int64_t>(std::llround((upperIsSurfaceWorld?3.5:1.5)*qPerMetre)),
|
||||
cfg?cfg->midRadiusSubvoxels:mouthRadius);
|
||||
const std::int64_t reachMinimum=cfg?std::max<std::int64_t>(0,cfg->reachMinimumSubvoxels):8*qPerMetre;
|
||||
const std::int64_t reachMaximum=cfg?std::max<std::int64_t>(reachMinimum,cfg->reachMaximumSubvoxels):24*qPerMetre;
|
||||
@@ -813,6 +816,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
std::array<std::int64_t,3> upperMouth{},lowerMouth{};
|
||||
const bool hasUpperRoute=connection==0&&routeAnchor(upper,true,upperMouth);
|
||||
const bool hasLowerRoute=connection==0&&routeAnchor(lower,false,lowerMouth);
|
||||
const bool surfaceGroundDeparture=hasUpperRoute&&upperIsSurfaceWorld;
|
||||
const ResolvedLayoutAnchorV2* passageAnchor=nullptr;
|
||||
if(connection==0)
|
||||
for(const auto& anchor:upper.anchors)
|
||||
@@ -870,6 +874,17 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
std::max<std::int64_t>(0,cfg?cfg->cascadeLedgeSubvoxels:0)
|
||||
*static_cast<std::int64_t>(cfg?cfg->cascadeSteps:4)),
|
||||
mouthRadius+2*qPerMetre,outerRingRadius);
|
||||
if(surfaceGroundDeparture)
|
||||
{
|
||||
const double departureRadius=std::hypot(static_cast<double>(upperMouth[0]),
|
||||
static_cast<double>(upperMouth[1]));
|
||||
const std::int64_t clearRingRadius=static_cast<std::int64_t>(std::ceil(
|
||||
departureRadius+mouthRadius+0.5*qPerMetre));
|
||||
const std::int64_t maximumRingRadius=std::max<std::int64_t>(1,
|
||||
placementRadius-qPerMetre);
|
||||
ringRadius=std::min(maximumRingRadius,
|
||||
std::max(ringRadius,clearRingRadius));
|
||||
}
|
||||
if(anchoredIslandPassage)
|
||||
ringRadius=std::min(ringRadius,mouthRadius+qPerMetre/2);
|
||||
std::uint32_t phase=static_cast<std::uint32_t>(PassageSeed(seed,
|
||||
@@ -890,6 +905,23 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
{bestAlignment=alignment;phase=candidate;}
|
||||
}
|
||||
}
|
||||
if(hasUpperRoute&&upperIsSurfaceWorld)
|
||||
{
|
||||
const auto& directionAtPhase=directions[phase];
|
||||
const std::int64_t radialAlignment=upperMouth[0]*directionAtPhase[0]
|
||||
+upperMouth[1]*directionAtPhase[1];
|
||||
if(radialAlignment<0)
|
||||
{
|
||||
std::int64_t bestOutwardAlignment=std::numeric_limits<std::int64_t>::min();
|
||||
for(std::uint32_t candidate=0;candidate<directions.size();++candidate)
|
||||
{
|
||||
const std::int64_t alignment=upperMouth[0]*directions[candidate][0]
|
||||
+upperMouth[1]*directions[candidate][1];
|
||||
if(alignment>bestOutwardAlignment)
|
||||
{bestOutwardAlignment=alignment;phase=candidate;}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool clockwise=(PassageSeed(seed,upper.strateIndex,lower.strateIndex,
|
||||
connection,0x44495245u)&1u)!=0;
|
||||
if(anchoredSpiral&&passageAnchor&&hasUpperTangent)
|
||||
@@ -1159,10 +1191,23 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
if(hasAnchorHelix&&helixHorizontal==0)
|
||||
{error=passage.stableId+" anchor spiral has no horizontal reach";return false;}
|
||||
passage.controlPoints.reserve(xy.size());
|
||||
const std::int64_t surfacePortalRadius=surfaceGroundDeparture
|
||||
?std::min(mouthRadius,static_cast<std::int64_t>(std::llround(1.5*qPerMetre)))
|
||||
:mouthRadius;
|
||||
const std::uint64_t surfaceMouthBlendLength=static_cast<std::uint64_t>(
|
||||
std::max<std::int64_t>(8*qPerMetre,2*mouthRadius));
|
||||
for(std::size_t i=0;i<xy.size();++i)
|
||||
{
|
||||
const std::int64_t radius=(i<=1||i==lowerMouthIndex
|
||||
std::int64_t radius=(i<=1||i==lowerMouthIndex
|
||||
||i+1>=xy.size()-1)?mouthRadius:midRadius;
|
||||
if(surfaceGroundDeparture&&i<lowerMouthIndex)
|
||||
{
|
||||
const double t=std::clamp(static_cast<double>(cumulative[i])
|
||||
/surfaceMouthBlendLength,0.0,1.0);
|
||||
const double smooth=t*t*(3.0-2.0*t);
|
||||
radius=surfacePortalRadius+static_cast<std::int64_t>(std::llround(
|
||||
(midRadius-surfacePortalRadius)*smooth));
|
||||
}
|
||||
std::int64_t playerZ=0;
|
||||
if(i>lowerMouthIndex)
|
||||
playerZ=lowerLandingPoints[i-lowerMouthIndex-1][2];
|
||||
@@ -1183,9 +1228,30 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
point.positionSubvoxels={xy[i][0],xy[i][1],playerZ+radius-playerCenterAboveFloorQ};
|
||||
point.radiusSubvoxels=radius;passage.controlPoints.push_back(point);
|
||||
}
|
||||
passage.controlPoints.front().positionSubvoxels[2]=upperMouth[2]+mouthRadius-playerCenterAboveFloorQ;
|
||||
passage.controlPoints[lowerMouthIndex].positionSubvoxels[2]=
|
||||
lowerMouth[2]+mouthRadius-playerCenterAboveFloorQ;
|
||||
if(surfaceGroundDeparture)
|
||||
std::printf("[ResolvedSurfaceWorldDepartureMouth] passage=%s ground_floor_z_m=%.3f portal_radius_m=%.3f tunnel_radius_m=%.3f blend_length_m=%.3f departure_xy_m=(%.3f,%.3f) first_tunnel_xy_m=(%.3f,%.3f) first_tunnel_floor_z_m=%.3f first_tunnel_radius_m=%.3f\n",
|
||||
passage.stableId.c_str(),(upperMouth[2]-static_cast<std::int64_t>(std::llround(0.88*qPerMetre)))/static_cast<double>(qPerMetre),
|
||||
surfacePortalRadius/static_cast<double>(qPerMetre),
|
||||
midRadius/static_cast<double>(qPerMetre),
|
||||
surfaceMouthBlendLength/static_cast<double>(qPerMetre),
|
||||
upperMouth[0]/static_cast<double>(qPerMetre),
|
||||
upperMouth[1]/static_cast<double>(qPerMetre),
|
||||
passage.controlPoints.size()>1?passage.controlPoints[1].positionSubvoxels[0]
|
||||
/static_cast<double>(qPerMetre):0.0,
|
||||
passage.controlPoints.size()>1?passage.controlPoints[1].positionSubvoxels[1]
|
||||
/static_cast<double>(qPerMetre):0.0,
|
||||
passage.controlPoints.size()>1?
|
||||
(passage.controlPoints[1].positionSubvoxels[2]
|
||||
-passage.controlPoints[1].radiusSubvoxels+playerCenterAboveFloorQ
|
||||
-static_cast<std::int64_t>(std::llround(0.88*qPerMetre)))
|
||||
/static_cast<double>(qPerMetre):0.0,
|
||||
passage.controlPoints.size()>1?passage.controlPoints[1].radiusSubvoxels
|
||||
/static_cast<double>(qPerMetre):0.0);
|
||||
passage.controlPoints.front().positionSubvoxels[2]=upperMouth[2]
|
||||
+passage.controlPoints.front().radiusSubvoxels-playerCenterAboveFloorQ;
|
||||
passage.controlPoints[lowerMouthIndex].positionSubvoxels[2]=lowerMouth[2]
|
||||
+passage.controlPoints[lowerMouthIndex].radiusSubvoxels
|
||||
-playerCenterAboveFloorQ;
|
||||
layout.passages.push_back(std::move(passage));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user