feat(core): reachable SW landmark chamber demo with a reachability audit (L45); research R12
Stamp vocabulary v2; the flagged SW demo opens through a side-facing portal and a bent ramp (max 14 deg, two landings) into a 17 x 15 m chamber with a rock dais. New reachability audit: a capsule surface search from walkable ground near the arrival mouth to the chamber floor (66 samples, 0 blocked/unsupported); the buried L43 layout fails it. Demo off: owner hashes identical to L44. fast/medium/full PASS (Clang+GCC). Research R12 (read-only): composite cavern-graph generator design and slices. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
036bd530fc
commit
f13b145f23
14 files changed
+1404
-45
No files matched your search
@@ -56,6 +56,7 @@ namespace
|
||||
int RenderDistanceOverride=0;
|
||||
bool FloatingIslandsRecipe=false;
|
||||
bool LandmarkChamberDemo=false;
|
||||
bool AuditLandmarkReachability=false;
|
||||
bool TlsProbeRequested=false;
|
||||
int TlsProbeWorkers=0;
|
||||
std::string TlsProbeTouch="none";
|
||||
@@ -111,6 +112,7 @@ namespace
|
||||
|
||||
bool MakeLandmarkChamberDemoRequests(
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& worldAssembly,
|
||||
std::int32_t seed,
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1>& requests,
|
||||
std::string& error)
|
||||
@@ -147,49 +149,371 @@ namespace
|
||||
chamber.stableId="landmark/sw-arrival-chamber-v1";
|
||||
chamber.strateIndex=surface->strateIndex;
|
||||
chamber.kind=ResolvedSdfStampKindV1::carveEllipsoid;
|
||||
// Anchor the chamber horizontally beside the arrival passage. Its height is
|
||||
// set from the local SurfaceWorld ground sample below so the carve keeps a
|
||||
// real rock roof instead of breaking through a low patch of terrain.
|
||||
chamber.anchorPositionMicrometres={
|
||||
arrivalMicrometres[0]+static_cast<std::int64_t>(std::llround(sideX*28000000.0)),
|
||||
arrivalMicrometres[1]+static_cast<std::int64_t>(std::llround(sideY*28000000.0)),
|
||||
arrivalMicrometres[2]-14000000};
|
||||
chamber.placementRadiusMicrometres=2000000;
|
||||
chamber.halfExtentsMicrometres={8500000,7500000,6000000};
|
||||
chamber.smoothWidthMicrometres=800000;
|
||||
chamber.irregularityAmplitudeMicrometres=700000;
|
||||
chamber.irregularityWavelengthMicrometres=4800000;
|
||||
arrivalMicrometres[2]-13000000};
|
||||
chamber.placementRadiusMicrometres=0;
|
||||
chamber.halfExtentsMicrometres={8500000,7500000,3000000};
|
||||
chamber.smoothWidthMicrometres=600000;
|
||||
chamber.irregularityAmplitudeMicrometres=600000;
|
||||
chamber.irregularityWavelengthMicrometres=3800000;
|
||||
|
||||
ResolvedLayoutStampRequestV1 dais;
|
||||
dais.stableId="landmark/sw-arrival-chamber-dais-v1";
|
||||
// Resolved stamps compose in stable-ID order. Keep the raised fills after
|
||||
// chamber-v1 so that the enclosing carve cannot cut them away.
|
||||
dais.stableId="landmark/sw-arrival-chamber-v1-dais";
|
||||
dais.strateIndex=surface->strateIndex;
|
||||
// The version-2 ellipsoid fill rounds the platform into the chamber floor
|
||||
// instead of leaving the long, straight wall of a flat-topped pillar.
|
||||
dais.kind=ResolvedSdfStampKindV1::fillDais;
|
||||
dais.anchorPositionMicrometres=chamber.anchorPositionMicrometres;
|
||||
dais.anchorPositionMicrometres[2]-=2600000;
|
||||
dais.placementRadiusMicrometres=1000000;
|
||||
dais.halfExtentsMicrometres={3200000,2800000,900000};
|
||||
dais.smoothWidthMicrometres=300000;
|
||||
// Keep the raised platform on the broad filled floor rather than merging
|
||||
// it into the chamber's outer slope. Leave a capsule-width landing around
|
||||
// the ramp's centerline at the chamber entrance.
|
||||
dais.anchorPositionMicrometres[0]+=static_cast<std::int64_t>(std::llround(sideX*2600000.0));
|
||||
dais.anchorPositionMicrometres[1]+=static_cast<std::int64_t>(std::llround(sideY*2600000.0));
|
||||
dais.anchorPositionMicrometres[2]-=400000;
|
||||
dais.placementRadiusMicrometres=0;
|
||||
dais.halfExtentsMicrometres={2100000,1800000,1300000};
|
||||
dais.smoothWidthMicrometres=450000;
|
||||
dais.irregularityAmplitudeMicrometres=180000;
|
||||
dais.irregularityWavelengthMicrometres=2200000;
|
||||
dais.irregularityWavelengthMicrometres=1800000;
|
||||
|
||||
const double entranceDirectionX=sideX,entranceDirectionY=sideY;
|
||||
ResolvedLayoutStampRequestV1 entrance;
|
||||
entrance.stableId="landmark/sw-arrival-chamber-entrance-v1";
|
||||
entrance.strateIndex=surface->strateIndex;
|
||||
entrance.kind=ResolvedSdfStampKindV1::carveArch;
|
||||
entrance.anchorPositionMicrometres={
|
||||
chamber.anchorPositionMicrometres[0]
|
||||
+static_cast<std::int64_t>(std::llround(entranceDirectionX*6500000.0)),
|
||||
chamber.anchorPositionMicrometres[1]
|
||||
+static_cast<std::int64_t>(std::llround(entranceDirectionY*6500000.0)),
|
||||
chamber.anchorPositionMicrometres[2]};
|
||||
entrance.placementRadiusMicrometres=1000000;
|
||||
entrance.halfExtentsMicrometres={2600000,4200000,3200000};
|
||||
entrance.smoothWidthMicrometres=350000;
|
||||
entrance.irregularityAmplitudeMicrometres=250000;
|
||||
entrance.irregularityWavelengthMicrometres=2600000;
|
||||
if(std::abs(entranceDirectionX)>std::abs(entranceDirectionY))
|
||||
entrance.yawQuarterTurns=entranceDirectionX>0.0?3:1;
|
||||
else
|
||||
entrance.yawQuarterTurns=entranceDirectionY>0.0?0:2;
|
||||
requests={std::move(chamber),std::move(entrance),std::move(dais)};
|
||||
ResolvedLayoutStampRequestV1 chamberFloor;
|
||||
chamberFloor.stableId="landmark/sw-arrival-chamber-v1-floor";
|
||||
chamberFloor.strateIndex=surface->strateIndex;
|
||||
chamberFloor.kind=ResolvedSdfStampKindV1::fillDais;
|
||||
chamberFloor.anchorPositionMicrometres=chamber.anchorPositionMicrometres;
|
||||
chamberFloor.anchorPositionMicrometres[2]-=2500000;
|
||||
chamberFloor.placementRadiusMicrometres=0;
|
||||
chamberFloor.halfExtentsMicrometres={5800000,5200000,2000000};
|
||||
chamberFloor.smoothWidthMicrometres=250000;
|
||||
chamberFloor.irregularityAmplitudeMicrometres=80000;
|
||||
chamberFloor.irregularityWavelengthMicrometres=2200000;
|
||||
|
||||
const auto surfacePlan=std::find_if(worldAssembly.plan.strates.begin(),
|
||||
worldAssembly.plan.strates.end(),[&](const auto& value)
|
||||
{return value.strateIndex==surface->strateIndex;});
|
||||
if(surfacePlan==worldAssembly.plan.strates.end())
|
||||
{error="landmark chamber demo needs its SurfaceWorld heightfield plan";return false;}
|
||||
const Heightfield2D field(worldAssembly.recipe,*surfacePlan);
|
||||
if(!field.isValid())
|
||||
{error="landmark chamber demo SurfaceWorld heightfield is invalid";return false;}
|
||||
|
||||
const double coreMicrometres=std::max<std::uint32_t>(1,layout.coreVoxelSizeMicrometres);
|
||||
const double qToMicrometres=coreMicrometres/WorldAssemblySubvoxelsPerVoxel;
|
||||
const auto qToMetres=[&](std::int64_t value)
|
||||
{return static_cast<double>(value)*qToMicrometres/1000000.0;};
|
||||
const auto metresToQ=[&](double value)
|
||||
{return static_cast<std::int64_t>(std::llround(value*1000000.0/qToMicrometres));};
|
||||
const double startX=qToMetres(mouth[0])+sideX*20.0;
|
||||
const double startY=qToMetres(mouth[1])+sideY*20.0;
|
||||
const auto surfaceSample=field.point(metresToQ(startX),metresToQ(startY),0);
|
||||
const std::int64_t surfaceHeightQ=surfaceSample.groundHeightSubvoxels;
|
||||
const double surfaceHeight=qToMetres(surfaceHeightQ);
|
||||
|
||||
// Search beside the arrival corridor for a chamber site whose actual terrain
|
||||
// height lets the player descend from this surface entrance at a walkable grade.
|
||||
// The earlier fixed XY site lay over a deep valley, leaving no room for a roof
|
||||
// and asking the ramp to drop 37 m in 71 m.
|
||||
double chamberSideOffset=0.0,chamberTangentOffset=0.0;
|
||||
double chamberGroundHeight=0.0,bestChamberSiteScore=std::numeric_limits<double>::infinity();
|
||||
for(double sideOffset=28.0;sideOffset<=52.0;sideOffset+=2.0)
|
||||
for(double tangentOffset=-32.0;tangentOffset<=32.0;tangentOffset+=2.0)
|
||||
{
|
||||
const double x=qToMetres(mouth[0])+sideX*sideOffset+dx/length*tangentOffset;
|
||||
const double y=qToMetres(mouth[1])+sideY*sideOffset+dy/length*tangentOffset;
|
||||
const double distanceFromEntrance=std::hypot(x-startX,y-startY);
|
||||
if(distanceFromEntrance<16.0)continue;
|
||||
const auto sample=field.point(metresToQ(x),metresToQ(y),0);
|
||||
const double ground=qToMetres(sample.groundHeightSubvoxels);
|
||||
if(ground<surfaceHeight-3.5||ground>surfaceHeight+4.0)continue;
|
||||
const double sideDelta=sideOffset-42.0;
|
||||
const double tangentDelta=tangentOffset-2.0;
|
||||
const double score=sideDelta*sideDelta+tangentDelta*tangentDelta
|
||||
+4.0*(ground-surfaceHeight)*(ground-surfaceHeight);
|
||||
if(score>=bestChamberSiteScore)continue;
|
||||
bestChamberSiteScore=score;chamberSideOffset=sideOffset;
|
||||
chamberTangentOffset=tangentOffset;chamberGroundHeight=ground;
|
||||
chamber.anchorPositionMicrometres[0]=static_cast<std::int64_t>(
|
||||
std::llround(x*1000000.0));
|
||||
chamber.anchorPositionMicrometres[1]=static_cast<std::int64_t>(
|
||||
std::llround(y*1000000.0));
|
||||
}
|
||||
if(!std::isfinite(bestChamberSiteScore))
|
||||
{error="landmark chamber has no nearby surface site with a walkable descent grade";return false;}
|
||||
constexpr double ChamberRoofCoverMetres=0.5;
|
||||
const double chamberCarveTopReach=
|
||||
static_cast<double>(chamber.halfExtentsMicrometres[2]
|
||||
+chamber.smoothWidthMicrometres
|
||||
+chamber.irregularityAmplitudeMicrometres)/1000000.0;
|
||||
const double chamberCenterZ=chamberGroundHeight-chamberCarveTopReach
|
||||
-ChamberRoofCoverMetres;
|
||||
chamber.anchorPositionMicrometres[2]=static_cast<std::int64_t>(
|
||||
std::llround(chamberCenterZ*1000000.0));
|
||||
dais.anchorPositionMicrometres[0]=chamber.anchorPositionMicrometres[0]
|
||||
+static_cast<std::int64_t>(std::llround(sideX*2600000.0));
|
||||
dais.anchorPositionMicrometres[1]=chamber.anchorPositionMicrometres[1]
|
||||
+static_cast<std::int64_t>(std::llround(sideY*2600000.0));
|
||||
dais.anchorPositionMicrometres[2]=chamber.anchorPositionMicrometres[2]-400000;
|
||||
chamberFloor.anchorPositionMicrometres[0]=chamber.anchorPositionMicrometres[0];
|
||||
chamberFloor.anchorPositionMicrometres[1]=chamber.anchorPositionMicrometres[1];
|
||||
chamberFloor.anchorPositionMicrometres[2]=chamber.anchorPositionMicrometres[2]-2500000;
|
||||
|
||||
struct FPathPoint { double x=0.0,y=0.0; };
|
||||
const FPathPoint start{startX,startY};
|
||||
const FPathPoint firstTurn{
|
||||
qToMetres(mouth[0])+sideX*34000000.0/1000000.0+dx/length*14000000.0/1000000.0,
|
||||
qToMetres(mouth[1])+sideY*34000000.0/1000000.0+dy/length*14000000.0/1000000.0};
|
||||
const FPathPoint end{
|
||||
chamber.anchorPositionMicrometres[0]/1000000.0,
|
||||
chamber.anchorPositionMicrometres[1]/1000000.0};
|
||||
const std::array<FPathPoint,3> waypoints={start,firstTurn,end};
|
||||
std::vector<FPathPoint> densePath;
|
||||
const auto appendLine=[&](FPathPoint from,FPathPoint to)
|
||||
{
|
||||
const double distance=std::hypot(to.x-from.x,to.y-from.y);
|
||||
const int steps=std::max(1,static_cast<int>(std::ceil(distance/0.25)));
|
||||
for(int i=0;i<=steps;++i)
|
||||
{
|
||||
const double t=static_cast<double>(i)/steps;
|
||||
const FPathPoint point{from.x+(to.x-from.x)*t,from.y+(to.y-from.y)*t};
|
||||
if(densePath.empty()||std::hypot(point.x-densePath.back().x,
|
||||
point.y-densePath.back().y)>1.0e-6)densePath.push_back(point);
|
||||
}
|
||||
};
|
||||
constexpr double BendRadius=6.0;
|
||||
FPathPoint current=waypoints.front();
|
||||
for(std::size_t i=1;i+1<waypoints.size();++i)
|
||||
{
|
||||
const auto& previous=waypoints[i-1];const auto& corner=waypoints[i];
|
||||
const auto& next=waypoints[i+1];
|
||||
const double inLength=std::hypot(corner.x-previous.x,corner.y-previous.y);
|
||||
const double outLength=std::hypot(next.x-corner.x,next.y-corner.y);
|
||||
if(inLength<=2.0*BendRadius||outLength<=2.0*BendRadius)
|
||||
{error="landmark cave ramp has a segment too short to round";return false;}
|
||||
const FPathPoint inDirection{(corner.x-previous.x)/inLength,
|
||||
(corner.y-previous.y)/inLength};
|
||||
const FPathPoint outDirection{(next.x-corner.x)/outLength,
|
||||
(next.y-corner.y)/outLength};
|
||||
const FPathPoint before{corner.x-inDirection.x*BendRadius,
|
||||
corner.y-inDirection.y*BendRadius};
|
||||
const FPathPoint after{corner.x+outDirection.x*BendRadius,
|
||||
corner.y+outDirection.y*BendRadius};
|
||||
appendLine(current,before);
|
||||
constexpr int ArcSteps=24;
|
||||
for(int step=0;step<=ArcSteps;++step)
|
||||
{
|
||||
const double t=static_cast<double>(step)/ArcSteps,oneMinus=1.0-t;
|
||||
const FPathPoint point{
|
||||
oneMinus*oneMinus*before.x+2.0*oneMinus*t*corner.x+t*t*after.x,
|
||||
oneMinus*oneMinus*before.y+2.0*oneMinus*t*corner.y+t*t*after.y};
|
||||
if(densePath.empty()||std::hypot(point.x-densePath.back().x,
|
||||
point.y-densePath.back().y)>1.0e-6)densePath.push_back(point);
|
||||
}
|
||||
current=after;
|
||||
}
|
||||
appendLine(current,waypoints.back());
|
||||
if(densePath.size()<2)
|
||||
{error="landmark cave ramp path is degenerate";return false;}
|
||||
std::vector<double> arcLength(densePath.size(),0.0);
|
||||
for(std::size_t i=1;i<densePath.size();++i)
|
||||
arcLength[i]=arcLength[i-1]+std::hypot(densePath[i].x-densePath[i-1].x,
|
||||
densePath[i].y-densePath[i-1].y);
|
||||
const double totalLength=arcLength.back();
|
||||
double firstTurnDistance=0.0;
|
||||
double firstTurnDistanceError=std::numeric_limits<double>::infinity();
|
||||
for(std::size_t i=0;i<densePath.size();++i)
|
||||
{
|
||||
const double distance=std::hypot(densePath[i].x-firstTurn.x,
|
||||
densePath[i].y-firstTurn.y);
|
||||
if(distance<firstTurnDistanceError)
|
||||
{firstTurnDistanceError=distance;firstTurnDistance=arcLength[i];}
|
||||
}
|
||||
// Put the first tunnel center one floor radius above grade so its lower boundary
|
||||
// meets the outdoor ground cleanly, while keeping a walkable cave-sized headroom.
|
||||
constexpr double RampRadiusMetres=1.6;
|
||||
const double startZ=surfaceHeight+RampRadiusMetres;
|
||||
// Join the chamber's filled floor at its top surface.
|
||||
const double endZ=chamberCenterZ+1.1;
|
||||
const double descent=endZ-startZ;
|
||||
constexpr double EntryLandingLength=5.0;
|
||||
constexpr double TurnLandingHalfLength=1.5;
|
||||
const double firstTurnLandingStart=std::max(EntryLandingLength,
|
||||
firstTurnDistance-TurnLandingHalfLength);
|
||||
const double firstTurnLandingEnd=std::min(totalLength,
|
||||
firstTurnDistance+TurnLandingHalfLength);
|
||||
const double firstTurnLandingLength=firstTurnLandingEnd-firstTurnLandingStart;
|
||||
const double slopedLength=std::max(0.0,totalLength-EntryLandingLength
|
||||
-firstTurnLandingLength);
|
||||
ResolvedLayoutStampRequestV1 mouthStamp;
|
||||
mouthStamp.stableId="landmark/sw-arrival-chamber-mouth-v1";
|
||||
mouthStamp.strateIndex=surface->strateIndex;
|
||||
mouthStamp.kind=ResolvedSdfStampKindV1::carveEllipsoid;
|
||||
mouthStamp.anchorPositionMicrometres={
|
||||
static_cast<std::int64_t>(std::llround(startX*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround(startY*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround((startZ+1.0)*1000000.0))};
|
||||
mouthStamp.placementRadiusMicrometres=0;
|
||||
mouthStamp.halfExtentsMicrometres={4200000,3700000,2800000};
|
||||
mouthStamp.smoothWidthMicrometres=450000;
|
||||
// A broad, rounded upper flare blends the side-facing portal into the slope.
|
||||
mouthStamp.irregularityAmplitudeMicrometres=250000;
|
||||
mouthStamp.irregularityWavelengthMicrometres=2400000;
|
||||
std::printf("[LandmarkChamberPlan] center_m=(%.3f,%.3f,%.3f) local_ground_m=%.3f carve_top_reach_m=%.3f roof_cover_m=%.3f\n",
|
||||
end.x,end.y,chamberCenterZ,chamberGroundHeight,chamberCarveTopReach,
|
||||
chamberGroundHeight-(chamberCenterZ+chamberCarveTopReach));
|
||||
std::printf("[LandmarkChamberSite] side_offset_m=%.3f tangent_offset_m=%.3f entrance_ground_m=%.3f chamber_ground_m=%.3f\n",
|
||||
chamberSideOffset,chamberTangentOffset,surfaceHeight,chamberGroundHeight);
|
||||
std::printf("[LandmarkRampPlan] entry_m=(%.3f,%.3f,%.3f) ground_m=%.3f cap_m=%.3f end_m=(%.3f,%.3f,%.3f) horizontal_m=%.3f entry_landing_m=%.3f turn_landing_m=%.3f turn_distance_m=%.3f sloped_m=%.3f descent_m=%.3f grade_deg=%.3f required_length_m=%.3f\n",
|
||||
startX,startY,startZ,surfaceHeight,
|
||||
qToMetres(surfaceSample.capHeightSubvoxels),end.x,end.y,endZ,totalLength,
|
||||
EntryLandingLength,firstTurnLandingLength,firstTurnDistance,
|
||||
slopedLength,descent,
|
||||
std::atan2(std::abs(descent),slopedLength)*180.0/3.14159265358979323846,
|
||||
std::abs(descent)/std::tan(15.0*3.14159265358979323846/180.0));
|
||||
if(!(slopedLength>0.0)||std::atan2(std::abs(descent),slopedLength)
|
||||
>15.0*3.14159265358979323846/180.0)
|
||||
{error="landmark cave ramp cannot meet the 15 degree walk grade";return false;}
|
||||
|
||||
const double entryPathLength=std::hypot(firstTurn.x-start.x,
|
||||
firstTurn.y-start.y);
|
||||
const double entryPathX=(firstTurn.x-start.x)/entryPathLength;
|
||||
const double entryPathY=(firstTurn.y-start.y)/entryPathLength;
|
||||
const double mouthNormalX=-entryPathY,mouthNormalY=entryPathX;
|
||||
constexpr double MouthPortalDepth=3.8;
|
||||
constexpr double MouthPortalHalfWidth=2.8;
|
||||
constexpr double MouthPortalCenterOffset=2.6;
|
||||
const double mouthPortalX=startX+mouthNormalX*MouthPortalCenterOffset;
|
||||
const double mouthPortalY=startY+mouthNormalY*MouthPortalCenterOffset;
|
||||
ResolvedLayoutStampRequestV1 mouthPortal;
|
||||
mouthPortal.stableId="landmark/sw-arrival-chamber-mouth-approach-v1";
|
||||
mouthPortal.strateIndex=surface->strateIndex;
|
||||
mouthPortal.kind=ResolvedSdfStampKindV1::carveEllipsoid;
|
||||
mouthPortal.anchorPositionMicrometres={
|
||||
static_cast<std::int64_t>(std::llround(mouthPortalX*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround(mouthPortalY*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround(startZ*1000000.0))};
|
||||
mouthPortal.placementRadiusMicrometres=0;
|
||||
mouthPortal.halfExtentsMicrometres={3800000,2800000,2000000};
|
||||
mouthPortal.smoothWidthMicrometres=450000;
|
||||
mouthPortal.irregularityAmplitudeMicrometres=180000;
|
||||
mouthPortal.irregularityWavelengthMicrometres=2000000;
|
||||
|
||||
ResolvedLayoutStampRequestV1 mouthLanding;
|
||||
mouthLanding.stableId="landmark/sw-arrival-z-mouth-landing-v1";
|
||||
mouthLanding.strateIndex=surface->strateIndex;
|
||||
mouthLanding.kind=ResolvedSdfStampKindV1::fillPillar;
|
||||
mouthLanding.anchorPositionMicrometres={
|
||||
static_cast<std::int64_t>(std::llround(mouthPortalX*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround(mouthPortalY*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround((startZ-RampRadiusMetres-1.5)
|
||||
*1000000.0))};
|
||||
mouthLanding.placementRadiusMicrometres=0;
|
||||
mouthLanding.halfExtentsMicrometres={3800000,2800000,1500000};
|
||||
mouthLanding.smoothWidthMicrometres=180000;
|
||||
mouthLanding.irregularityAmplitudeMicrometres=0;
|
||||
mouthLanding.irregularityWavelengthMicrometres=2000000;
|
||||
std::printf("[LandmarkMouthPortal] center_m=(%.3f,%.3f,%.3f) normal=(%.3f,%.3f) depth_m=%.2f half_width_m=%.2f landing_top_m=%.3f\n",
|
||||
mouthPortalX,mouthPortalY,startZ,mouthNormalX,mouthNormalY,
|
||||
MouthPortalDepth,MouthPortalHalfWidth,surfaceHeight);
|
||||
|
||||
const auto atDistance=[&](double distance)
|
||||
{
|
||||
distance=std::clamp(distance,0.0,totalLength);
|
||||
const auto upper=std::lower_bound(arcLength.begin(),arcLength.end(),distance);
|
||||
const std::size_t hi=std::min<std::size_t>(densePath.size()-1,
|
||||
static_cast<std::size_t>(upper-arcLength.begin()));
|
||||
const std::size_t lo=hi?hi-1:0;
|
||||
const double span=arcLength[hi]-arcLength[lo];
|
||||
const double t=span>0.0?(distance-arcLength[lo])/span:0.0;
|
||||
return FPathPoint{densePath[lo].x+(densePath[hi].x-densePath[lo].x)*t,
|
||||
densePath[lo].y+(densePath[hi].y-densePath[lo].y)*t};
|
||||
};
|
||||
const auto centerlineHeight=[&](double distance)
|
||||
{
|
||||
const double beforeFirstTurn=std::clamp(distance-EntryLandingLength,0.0,
|
||||
firstTurnLandingStart-EntryLandingLength);
|
||||
const double afterFirstTurn=distance>firstTurnLandingEnd
|
||||
?std::clamp(distance-firstTurnLandingEnd,0.0,
|
||||
totalLength-firstTurnLandingEnd):0.0;
|
||||
return startZ+descent*std::clamp((beforeFirstTurn+afterFirstTurn)
|
||||
/slopedLength,0.0,1.0);
|
||||
};
|
||||
const double pointSpacing=0.6;
|
||||
const std::size_t rampCount=static_cast<std::size_t>(std::ceil(totalLength/pointSpacing));
|
||||
std::vector<ResolvedLayoutStampRequestV1> rampCarves;
|
||||
std::vector<ResolvedLayoutStampRequestV1> rampSupports;
|
||||
rampCarves.reserve(rampCount+1);
|
||||
// Footfall supports are denser and narrower than the tunnel stamps. This
|
||||
// keeps their overlapping flat caps from forming broad downhill shelves.
|
||||
const double supportSpacing=0.3;
|
||||
const std::size_t supportCount=static_cast<std::size_t>(std::ceil(
|
||||
totalLength/supportSpacing));
|
||||
rampSupports.reserve(supportCount+1);
|
||||
for(std::size_t i=0;i<=rampCount;++i)
|
||||
{
|
||||
const double distance=std::min(totalLength,static_cast<double>(i)*pointSpacing);
|
||||
const FPathPoint point=atDistance(distance);
|
||||
ResolvedLayoutStampRequestV1 ramp;
|
||||
const std::size_t number=i;
|
||||
ramp.stableId=std::string("landmark/sw-arrival-chamber-ramp-")+
|
||||
(number<10?"00":number<100?"0":"")+std::to_string(number);
|
||||
ramp.strateIndex=surface->strateIndex;
|
||||
ramp.kind=ResolvedSdfStampKindV1::carveEllipsoid;
|
||||
const double centerZ=centerlineHeight(distance);
|
||||
ramp.anchorPositionMicrometres={
|
||||
static_cast<std::int64_t>(std::llround(point.x*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround(point.y*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround(centerZ*1000000.0))};
|
||||
ramp.placementRadiusMicrometres=0;
|
||||
ramp.halfExtentsMicrometres={2600000,2600000,2000000};
|
||||
ramp.smoothWidthMicrometres=650000;
|
||||
// Keep the footfall surface smooth enough for the capsule grade limit;
|
||||
// the chamber and entrance carry the larger natural wall variation.
|
||||
ramp.irregularityAmplitudeMicrometres=250000;
|
||||
ramp.irregularityWavelengthMicrometres=2400000;
|
||||
rampCarves.push_back(std::move(ramp));
|
||||
}
|
||||
for(std::size_t i=0;i<=supportCount;++i)
|
||||
{
|
||||
const double distance=std::min(totalLength,static_cast<double>(i)*supportSpacing);
|
||||
const FPathPoint point=atDistance(distance);
|
||||
const double floorZ=centerlineHeight(distance)-RampRadiusMetres;
|
||||
ResolvedLayoutStampRequestV1 support;
|
||||
support.stableId=std::string("landmark/sw-arrival-ramp-support-")+
|
||||
(i<10?"00":i<100?"0":"")+std::to_string(i);
|
||||
support.strateIndex=surface->strateIndex;
|
||||
support.kind=ResolvedSdfStampKindV1::fillPillar;
|
||||
support.anchorPositionMicrometres={
|
||||
static_cast<std::int64_t>(std::llround(point.x*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround(point.y*1000000.0)),
|
||||
static_cast<std::int64_t>(std::llround((floorZ-1.5)*1000000.0))};
|
||||
support.placementRadiusMicrometres=0;
|
||||
// The first support widens into a buried entry landing under the open
|
||||
// mouth, so the approach surface stays supported at its carved rim.
|
||||
support.halfExtentsMicrometres=i==0
|
||||
?std::array<std::int64_t,3>{3500000,3000000,1500000}
|
||||
:std::array<std::int64_t,3>{550000,550000,1500000};
|
||||
support.smoothWidthMicrometres=180000;
|
||||
support.irregularityAmplitudeMicrometres=0;
|
||||
support.irregularityWavelengthMicrometres=2200000;
|
||||
rampSupports.push_back(std::move(support));
|
||||
}
|
||||
requests.push_back(std::move(mouthStamp));
|
||||
requests.push_back(std::move(mouthPortal));
|
||||
requests.insert(requests.end(),std::make_move_iterator(rampCarves.begin()),
|
||||
std::make_move_iterator(rampCarves.end()));
|
||||
requests.push_back(std::move(chamber));
|
||||
requests.insert(requests.end(),std::make_move_iterator(rampSupports.begin()),
|
||||
std::make_move_iterator(rampSupports.end()));
|
||||
requests.push_back(std::move(mouthLanding));
|
||||
requests.push_back(std::move(chamberFloor));
|
||||
requests.push_back(std::move(dais));
|
||||
(void)seed;
|
||||
return true;
|
||||
}
|
||||
@@ -247,13 +571,14 @@ namespace
|
||||
" --mesh-export=FILE (write the exact generated tile meshes for offline rendering)\n"
|
||||
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
|
||||
" --seed=N --recipe=surface-world|floating-islands\n"
|
||||
" --landmark-chamber-demo (opt-in version-1 SDF chamber and entry arch)\n"
|
||||
" --landmark-chamber-demo (opt-in natural SW landmark chamber and ramp)\n"
|
||||
" --resolved-layout-output=FILE (canonical plan-time layout snapshot)\n"
|
||||
" --resolved-layout-only (exit after plan and snapshot validation)\n"
|
||||
" --audit-resolved-layout-boundaries (query Core at each recorded endpoint)\n"
|
||||
" --audit-strate-transitions (measure interface crossing, band thickness and holes)\n"
|
||||
" --transition-mode=hard|gradient|interleaved --transition-width-chunks=N (fixture override)\n"
|
||||
" --audit-resolved-layout-passages (capsule, support, route-binding and blocked-control audit)\n"
|
||||
" --audit-landmark-reachability (capsule, support, grade and buried-L43 control)\n"
|
||||
" --audit-surface-clearance (sample biome terrain/caps against the resolved ceiling)\n"
|
||||
" --auto-surface-camera (place the 27-tile cube on this seed's surface)\n"
|
||||
" --mesh-coverage (report non-empty tiles and indexed triangles)\n"
|
||||
@@ -275,6 +600,7 @@ namespace
|
||||
else if(arg=="--resolved-layout-only")options.ResolvedLayoutOnly=true;
|
||||
else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true;
|
||||
else if(arg=="--audit-resolved-layout-passages")options.AuditResolvedLayoutPassages=true;
|
||||
else if(arg=="--audit-landmark-reachability")options.AuditLandmarkReachability=true;
|
||||
else if(arg=="--audit-surface-clearance")options.AuditSurfaceClearance=true;
|
||||
else if(arg=="--audit-strate-transitions")options.AuditStrateTransitions=true;
|
||||
else if(arg.starts_with("--transition-mode="))
|
||||
@@ -494,8 +820,8 @@ int main(int argc,char** argv)
|
||||
if(options.LandmarkChamberDemo)
|
||||
{
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1> requests;
|
||||
if(!MakeLandmarkChamberDemoRequests(resolvedLayout,seed,requests,error)
|
||||
||!VoxelForgeCore::ResolveResolvedLayoutStampsV1(
|
||||
if(!MakeLandmarkChamberDemoRequests(resolvedLayout,worldAssembly,seed,requests,error)
|
||||
||!VoxelForgeCore::ResolveResolvedLayoutStampsV2(
|
||||
resolvedLayout,seed,requests,error))
|
||||
{std::fprintf(stderr,"Landmark chamber stamp resolution failed: %s\n",error.c_str());return 2;}
|
||||
const auto audit=VoxelForgeCore::AuditResolvedLayoutStampPlacementV1(resolvedLayout);
|
||||
@@ -798,5 +1124,5 @@ int main(int argc,char** argv)
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence,
|
||||
options.AuditRouteParity,options.AuditResolvedLayoutBoundaries,
|
||||
options.AuditResolvedLayoutPassages,options.AuditStrateTransitions,
|
||||
&resolvedLayout,options.StreamHashTiles);
|
||||
&resolvedLayout,options.StreamHashTiles,options.AuditLandmarkReachability);
|
||||
}
|
||||
Reference in new issue
Block a user