research: headroom-only landings, band fit, bore exemption (L156; last landing round, kept as patch)
Band fit works (all chambers inside host bands, 16/16 seeds both sweeps); chamber support exemption + open-floor control work; found a metres/subvoxel mix in the shell-opening predicate. Still fails: upper landing evaluated grade 22.85 deg over native ground; TN and VS sweeps 0/16; UE 30 m/s near coverage 39.0% vs 45.5%. Owner: stop forcing landings after this round. 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
63d774bae2
commit
a751a01b11
26 files changed
+2673
No files matched your search
@@ -0,0 +1,841 @@
|
||||
diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
|
||||
index 4799b9e..82bc9e1 100644
|
||||
--- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
|
||||
+++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
|
||||
@@ -512,7 +512,8 @@ namespace VoxelForgeCore
|
||||
// boundary-blended host rock from widening the authored passage wall.
|
||||
// v238 plans one resolved passage spline with explicit landing chambers.
|
||||
// v239 plans their floor planes as the passage's continuous floor profile.
|
||||
- inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 239;
|
||||
+ // v240 treats SurfaceWorld landings as headroom above max(planned floor,native ground).
|
||||
+ inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 240;
|
||||
|
||||
enum class FieldChannel : std::uint32_t
|
||||
{
|
||||
@@ -1212,8 +1213,9 @@ namespace VoxelForgeCore
|
||||
|
||||
struct ResolvedLayoutPassageLandingChamberV1
|
||||
{
|
||||
- // The chamber floor plane passes through this point. Gradient is dz/dx,
|
||||
- // dz/dy in Q16 so the chamber continues the passage's authored h(s) grade.
|
||||
+ // The planned chamber floor passes through this point. SurfaceWorld adds
|
||||
+ // native ground where it lies within the clear-height envelope. Gradient is
|
||||
+ // dz/dx,dy in Q16 and continues the passage's authored h(s) grade.
|
||||
std::array<std::int64_t, 3> floorCenterSubvoxels{};
|
||||
std::array<std::int64_t, 2> floorGradientQ16{};
|
||||
std::int64_t radiusSubvoxels = 0;
|
||||
@@ -2009,6 +2011,9 @@ namespace VoxelForgeCore
|
||||
ShapeBuildStats buildStats() const;
|
||||
|
||||
float densityAt(Vec3d voxelPosition) const;
|
||||
+ // Deterministic native SurfaceWorld or receiving-route ground in Core voxels.
|
||||
+ bool nativeSurfaceGroundHeightAt(Vec3d voxelPosition,
|
||||
+ double& groundHeightVoxels) const;
|
||||
// Companion material query for the bound strate evaluator; coordinates use Core voxels.
|
||||
MaterialSampleV1 materialAt(Vec3d voxelPosition) const;
|
||||
// Measure-only carve estimate. This does not apply an edit or grant inventory.
|
||||
diff --git a/Standalone/Core/src/ResolvedLayoutInternal.h b/Standalone/Core/src/ResolvedLayoutInternal.h
|
||||
index 7ea6c8c..c80b95d 100644
|
||||
--- a/Standalone/Core/src/ResolvedLayoutInternal.h
|
||||
+++ b/Standalone/Core/src/ResolvedLayoutInternal.h
|
||||
@@ -3993,6 +3993,72 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
reach/static_cast<double>(qPerMetre));
|
||||
}
|
||||
}
|
||||
+ // Reserve each landing envelope inside its host band before building h(s).
|
||||
+ // SurfaceWorld can raise its floor by at most the 4.5 m headroom allowance,
|
||||
+ // so include that bounded rise as well as the grade reach and floor shell.
|
||||
+ constexpr std::int64_t MaximumLandingGradeQ16=17560;
|
||||
+ const std::int64_t landingRadius=mouthRadius+3*qPerMetre;
|
||||
+ const std::int64_t landingClearHeight=static_cast<std::int64_t>(
|
||||
+ std::llround(4.5*qPerMetre));
|
||||
+ const std::int64_t landingShell=static_cast<std::int64_t>(
|
||||
+ std::llround(0.75*qPerMetre));
|
||||
+ const std::int64_t landingPlaneReach=(landingRadius
|
||||
+ *MaximumLandingGradeQ16+65535)/65536;
|
||||
+ const std::array<std::int64_t,3> upperMouthBeforeBandFit=upperMouth;
|
||||
+ const std::array<std::int64_t,3> lowerMouthBeforeBandFit=lowerMouth;
|
||||
+ const auto fitMouth=[&](std::array<std::int64_t,3>& mouth,
|
||||
+ const ResolvedLayoutStratumV2& host,std::int64_t playerHeightQ,
|
||||
+ const char* side)
|
||||
+ {
|
||||
+ if(!host.content.presentInCurrentField)return true;
|
||||
+ const std::int64_t minimumFloor=contentZQ(host.content.bottomMicrometres)
|
||||
+ +landingPlaneReach+landingShell;
|
||||
+ const std::int64_t nativeFloorRise=host.generatorKind=="SurfaceWorld"
|
||||
+ ?landingClearHeight:0;
|
||||
+ const std::int64_t maximumFloor=contentZQ(host.content.topMicrometres)
|
||||
+ -landingPlaneReach-landingShell-landingClearHeight-nativeFloorRise;
|
||||
+ if(maximumFloor<minimumFloor)
|
||||
+ {error=passage.stableId+" "+side
|
||||
+ +" landing chamber envelope is taller than its host content band";return false;}
|
||||
+ const std::int64_t requestedFloor=mouth[2]-playerHeightQ;
|
||||
+ const std::int64_t fittedFloor=std::clamp(requestedFloor,
|
||||
+ minimumFloor,maximumFloor);
|
||||
+ if(fittedFloor!=requestedFloor)
|
||||
+ {
|
||||
+ mouth[2]=fittedFloor+playerHeightQ;
|
||||
+ std::printf("[PassageLandingChamberPlacement] id=%s side=%s floor_shift_m=%.3f grade_bound_deg=15.000 clear_height_m=4.500 shell_m=0.750 status=PASS\n",
|
||||
+ passage.stableId.c_str(),side,
|
||||
+ (fittedFloor-requestedFloor)/static_cast<double>(qPerMetre));
|
||||
+ }
|
||||
+ return true;
|
||||
+ };
|
||||
+ if(!fitMouth(upperMouth,upper,upperPlayerCenterAboveFloorQ,"upper")
|
||||
+ ||!fitMouth(lowerMouth,lower,playerCenterAboveFloorQ,"lower"))return false;
|
||||
+ const auto bindFittedMouthAnchor=[&](
|
||||
+ const ResolvedLayoutStratumV2& host,
|
||||
+ const std::array<std::int64_t,3>& before,
|
||||
+ const std::array<std::int64_t,3>& after,const char* side)
|
||||
+ {
|
||||
+ if(before==after)return;
|
||||
+ auto mutableHost=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
+ [&](const auto& stratum)
|
||||
+ {return stratum.strateIndex==host.strateIndex;});
|
||||
+ if(mutableHost!=layout.strates.end())
|
||||
+ for(auto& anchor:mutableHost->anchors)
|
||||
+ if((anchor.kind=="passage_arrival"
|
||||
+ ||anchor.kind=="passage_departure")
|
||||
+ &&anchor.values.size()>=3
|
||||
+ &&anchor.values[0]==after[0]
|
||||
+ &&anchor.values[1]==after[1])
|
||||
+ {
|
||||
+ anchor.values[2]=after[2];
|
||||
+ std::printf("[PassageLandingChamberArrivalBinding] id=%s side=%s anchor=%s status=PASS\n",
|
||||
+ passage.stableId.c_str(),side,anchor.stableId.c_str());
|
||||
+ break;
|
||||
+ }
|
||||
+ };
|
||||
+ bindFittedMouthAnchor(upper,upperMouthBeforeBandFit,upperMouth,"upper");
|
||||
+ bindFittedMouthAnchor(lower,lowerMouthBeforeBandFit,lowerMouth,"lower");
|
||||
if(!radialWithin(upperMouth,placementRadius)||!radialWithin(lowerMouth,placementRadius))
|
||||
{error=passage.stableId+" route mouth is outside the resolved PassageConfig.DistanceMax radius";return false;}
|
||||
passage.upperMouthSubvoxels=upperMouth;passage.lowerMouthSubvoxels=lowerMouth;
|
||||
@@ -5048,6 +5114,91 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
surfaceHorizontalSquared)};
|
||||
}
|
||||
}
|
||||
+ if(host.generatorKind=="FloatingIslands"&&side[0]=='u')
|
||||
+ {
|
||||
+ // The island route is authored as player-center heights. Keep
|
||||
+ // the chamber center bound to passage h(s), while selecting the
|
||||
+ // closest grade plane that leaves the audited lower capsule arc
|
||||
+ // above every receiving route point under the chamber footprint.
|
||||
+ const auto route=routePoints(host);
|
||||
+ const std::int64_t routeRadius=chamber.radiusSubvoxels
|
||||
+ +static_cast<std::int64_t>(std::llround(0.34*qPerMetre));
|
||||
+ const std::int64_t routeRadiusSquared=routeRadius*routeRadius;
|
||||
+ const std::int64_t footClearanceQ=static_cast<std::int64_t>(
|
||||
+ std::llround((0.84+0.025)*qPerMetre));
|
||||
+ struct FRouteFloorConstraint
|
||||
+ {
|
||||
+ std::int64_t dx=0,dy=0,maximumRiseQ16=0;
|
||||
+ };
|
||||
+ std::vector<FRouteFloorConstraint> constraints;
|
||||
+ constraints.reserve(route.size());
|
||||
+ for(const auto& point:route)
|
||||
+ {
|
||||
+ const auto dx=static_cast<std::int64_t>(std::llround(
|
||||
+ point[0]-chamber.floorCenterSubvoxels[0]));
|
||||
+ const auto dy=static_cast<std::int64_t>(std::llround(
|
||||
+ point[1]-chamber.floorCenterSubvoxels[1]));
|
||||
+ if(dx*dx+dy*dy>routeRadiusSquared)continue;
|
||||
+ const auto routeFloorMaximum=static_cast<std::int64_t>(
|
||||
+ std::llround(point[2]))-footClearanceQ;
|
||||
+ constraints.push_back({dx,dy,
|
||||
+ (routeFloorMaximum-chamber.floorCenterSubvoxels[2])*65536});
|
||||
+ }
|
||||
+ const auto originalGradient=chamber.floorGradientQ16;
|
||||
+ for(std::size_t pass=0;pass<256&&!constraints.empty();++pass)
|
||||
+ {
|
||||
+ bool adjusted=false;
|
||||
+ for(const auto& constraint:constraints)
|
||||
+ {
|
||||
+ const std::int64_t distanceSquared=constraint.dx*constraint.dx
|
||||
+ +constraint.dy*constraint.dy;
|
||||
+ if(distanceSquared<=0)continue;
|
||||
+ const std::int64_t rise=
|
||||
+ static_cast<std::int64_t>(chamber.floorGradientQ16[0])
|
||||
+ *constraint.dx
|
||||
+ +static_cast<std::int64_t>(chamber.floorGradientQ16[1])
|
||||
+ *constraint.dy;
|
||||
+ const std::int64_t excess=rise-constraint.maximumRiseQ16;
|
||||
+ if(excess<=0)continue;
|
||||
+ const auto correction=[&](std::int64_t delta)
|
||||
+ {
|
||||
+ if(delta==0)return std::int64_t{0};
|
||||
+ const std::uint64_t product=static_cast<std::uint64_t>(excess)
|
||||
+ *static_cast<std::uint64_t>(std::abs(delta));
|
||||
+ return static_cast<std::int64_t>((product
|
||||
+ +static_cast<std::uint64_t>(distanceSquared)-1)
|
||||
+ /static_cast<std::uint64_t>(distanceSquared));
|
||||
+ };
|
||||
+ const auto correctionX=correction(constraint.dx);
|
||||
+ const auto correctionY=correction(constraint.dy);
|
||||
+ chamber.floorGradientQ16[0]-=static_cast<std::int32_t>(
|
||||
+ constraint.dx<0?-correctionX:correctionX);
|
||||
+ chamber.floorGradientQ16[1]-=static_cast<std::int32_t>(
|
||||
+ constraint.dy<0?-correctionY:correctionY);
|
||||
+ adjusted|=correctionX!=0||correctionY!=0;
|
||||
+ }
|
||||
+ if(!adjusted)break;
|
||||
+ }
|
||||
+ for(const auto& constraint:constraints)
|
||||
+ {
|
||||
+ const std::int64_t rise=
|
||||
+ static_cast<std::int64_t>(chamber.floorGradientQ16[0])
|
||||
+ *constraint.dx
|
||||
+ +static_cast<std::int64_t>(chamber.floorGradientQ16[1])
|
||||
+ *constraint.dy;
|
||||
+ if(rise>constraint.maximumRiseQ16)
|
||||
+ {error=passage.stableId
|
||||
+ +" upper landing floor cannot clear its receiving FloatingIslands route";
|
||||
+ return false;}
|
||||
+ }
|
||||
+ if(chamber.floorGradientQ16!=originalGradient)
|
||||
+ std::printf("[PassageLandingChamberRouteFit] id=%s side=upper host=FloatingIslands route_points=%zu fitted_constraints=%zu source_gradient_q16=(%lld,%lld) fitted_gradient_q16=(%lld,%lld) foot_clearance_m=0.865 max_grade_deg=15.000 status=PASS\n",
|
||||
+ passage.stableId.c_str(),route.size(),constraints.size(),
|
||||
+ static_cast<long long>(originalGradient[0]),
|
||||
+ static_cast<long long>(originalGradient[1]),
|
||||
+ static_cast<long long>(chamber.floorGradientQ16[0]),
|
||||
+ static_cast<long long>(chamber.floorGradientQ16[1]));
|
||||
+ }
|
||||
const std::uint64_t gradientSquared=static_cast<std::uint64_t>(
|
||||
chamber.floorGradientQ16[0]*chamber.floorGradientQ16[0]
|
||||
+chamber.floorGradientQ16[1]*chamber.floorGradientQ16[1]);
|
||||
diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h
|
||||
index 6b1a6af..c20b0ce 100644
|
||||
--- a/Standalone/Core/src/ShapeWorldInternal.h
|
||||
+++ b/Standalone/Core/src/ShapeWorldInternal.h
|
||||
@@ -1280,11 +1280,32 @@ public:
|
||||
double unionDistance=std::numeric_limits<double>::infinity();
|
||||
double unionShellDistance=std::numeric_limits<double>::infinity();
|
||||
double unionFloorShellDistance=std::numeric_limits<double>::infinity();
|
||||
+ double unionLandingChamberDistance=std::numeric_limits<double>::infinity();
|
||||
double unionLandingChamberFloorShellDistance=
|
||||
std::numeric_limits<double>::infinity();
|
||||
double plannedTunnelPassageShellDistance=std::numeric_limits<double>::infinity();
|
||||
const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
|
||||
const auto* queryStratum=FindBoundStratumForManagerZ(Z);
|
||||
+ const auto effectiveLandingFloor=[&](
|
||||
+ const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& chamber,
|
||||
+ double plannedFloorZ,double sampleXSubvoxels,double sampleYSubvoxels)
|
||||
+ {
|
||||
+ if(!std::isfinite(sampleXSubvoxels)||!std::isfinite(sampleYSubvoxels))
|
||||
+ return plannedFloorZ;
|
||||
+ double nativeGroundCoreVoxels=0.0;
|
||||
+ if(!GetNativeSurfaceGroundHeightAt(
|
||||
+ static_cast<float>(sampleXSubvoxels/Q),
|
||||
+ static_cast<float>(sampleYSubvoxels/Q),
|
||||
+ static_cast<float>(plannedFloorZ/Q),nativeGroundCoreVoxels))
|
||||
+ return plannedFloorZ;
|
||||
+ const double clearHeight=static_cast<double>(
|
||||
+ chamber.clearHeightSubvoxels);
|
||||
+ const double nativeFloor=nativeGroundCoreVoxels*Q;
|
||||
+ // A native surface above the chamber's clear-height budget denotes a
|
||||
+ // different layer surface, not a landing floor to clear from this room.
|
||||
+ return nativeFloor>plannedFloorZ&&nativeFloor<=plannedFloorZ+clearHeight
|
||||
+ ?nativeFloor:plannedFloorZ;
|
||||
+ };
|
||||
const bool plannedTunnelStratum=queryStratum&&queryStratum->Resolved
|
||||
&&(queryStratum->Resolved->generatorKind=="TunnelNetwork"
|
||||
||queryStratum->Resolved->generatorKind=="VerticalShafts")
|
||||
@@ -1339,10 +1360,12 @@ public:
|
||||
landingChamber->floorCenterSubvoxels[0]);
|
||||
const double centerY=static_cast<double>(
|
||||
landingChamber->floorCenterSubvoxels[1]);
|
||||
- const double floorCenterZ=static_cast<double>(
|
||||
+ const double plannedFloorCenterZ=static_cast<double>(
|
||||
landingChamber->floorCenterSubvoxels[2])
|
||||
+(landingChamber->floorGradientQ16[0]*(qx-centerX)
|
||||
+landingChamber->floorGradientQ16[1]*(qy-centerY))/65536.0;
|
||||
+ const double floorCenterZ=effectiveLandingFloor(*landingChamber,
|
||||
+ plannedFloorCenterZ,qx,qy);
|
||||
const double radialDistance=DeterministicSqrt(
|
||||
(qx-centerX)*(qx-centerX)+(qy-centerY)*(qy-centerY))
|
||||
-landingChamber->radiusSubvoxels;
|
||||
@@ -1357,6 +1380,8 @@ public:
|
||||
std::max(-floorDistance,floorDistance-shellThickness),
|
||||
radialDistance);
|
||||
unionDistance=std::min(unionDistance,chamberDistance);
|
||||
+ unionLandingChamberDistance=std::min(unionLandingChamberDistance,
|
||||
+ chamberDistance);
|
||||
unionShellDistance=std::min(unionShellDistance,chamberShellDistance);
|
||||
unionFloorShellDistance=std::min(unionFloorShellDistance,
|
||||
chamberFloorShellDistance);
|
||||
@@ -1746,15 +1771,18 @@ public:
|
||||
departureTailDistance)
|
||||
||nearRouteSegment(*lastTwo,*last,PortalWalkLeadMetres,true,0.0);
|
||||
};
|
||||
- const auto connectedSurfaceLandingShellContains=[&]()
|
||||
+ const auto connectedNativeLandingShellContains=[&]()
|
||||
{
|
||||
if(!queryStratum||!queryStratum->Resolved
|
||||
- ||queryStratum->Resolved->generatorKind!="SurfaceWorld")return false;
|
||||
+ ||(queryStratum->Resolved->generatorKind!="SurfaceWorld"
|
||||
+ &&queryStratum->Resolved->generatorKind!="FloatingIslands"))
|
||||
+ return false;
|
||||
const double queryX=qx/qPerMetre,queryY=qy/qPerMetre,queryZ=qz/qPerMetre;
|
||||
constexpr double ChamberShellThicknessMetres=0.75;
|
||||
constexpr double RouteClearanceMetres=1.25;
|
||||
constexpr double RouteHeightClearanceMetres=1.5;
|
||||
- constexpr double PortalWalkLeadMetres=9.0;
|
||||
+ const double portalWalkLeadMetres=
|
||||
+ queryStratum->Resolved->generatorKind=="FloatingIslands"?12.0:9.0;
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
{
|
||||
if(passage.upperStrateIndex!=queryStratum->StrateIndex)continue;
|
||||
@@ -1762,9 +1790,13 @@ public:
|
||||
if(chamber.radiusSubvoxels<=0||chamber.clearHeightSubvoxels<=0)continue;
|
||||
const double cx=static_cast<double>(chamber.floorCenterSubvoxels[0])/qPerMetre;
|
||||
const double cy=static_cast<double>(chamber.floorCenterSubvoxels[1])/qPerMetre;
|
||||
- const double floorZ=static_cast<double>(chamber.floorCenterSubvoxels[2])/qPerMetre
|
||||
+ const double plannedFloorZ=static_cast<double>(
|
||||
+ chamber.floorCenterSubvoxels[2])/qPerMetre
|
||||
+chamber.floorGradientQ16[0]/65536.0*(queryX-cx)
|
||||
+chamber.floorGradientQ16[1]/65536.0*(queryY-cy);
|
||||
+ const double floorZ=effectiveLandingFloor(chamber,
|
||||
+ plannedFloorZ*qPerMetre,queryX*qPerMetre,
|
||||
+ queryY*qPerMetre)/qPerMetre;
|
||||
const double radialDistance=std::hypot(queryX-cx,queryY-cy)
|
||||
-static_cast<double>(chamber.radiusSubvoxels)/qPerMetre;
|
||||
const double floorDistance=floorZ-queryZ;
|
||||
@@ -1784,7 +1816,11 @@ public:
|
||||
const double mouthZ=static_cast<double>(passage.upperMouthSubvoxels[2])
|
||||
/qPerMetre;
|
||||
std::size_t endpoint=route.size();
|
||||
- double endpointDistanceSquared=1.0;
|
||||
+ const double endpointRadius=
|
||||
+ queryStratum->Resolved->generatorKind=="FloatingIslands"?3.0:1.0;
|
||||
+ const double endpointHeight=
|
||||
+ queryStratum->Resolved->generatorKind=="FloatingIslands"?2.0:1.5;
|
||||
+ double endpointDistanceSquared=endpointRadius*endpointRadius;
|
||||
for(std::size_t index=0;index<route.size();++index)
|
||||
{
|
||||
const double x=route[index]->values[0]/qPerMetre;
|
||||
@@ -1793,7 +1829,7 @@ public:
|
||||
const double dx=x-cx,dy=y-cy;
|
||||
const double distanceSquared=dx*dx+dy*dy;
|
||||
if(distanceSquared<endpointDistanceSquared
|
||||
- &&std::abs(z-mouthZ)<=1.5)
|
||||
+ &&std::abs(z-mouthZ)<=endpointHeight)
|
||||
{endpoint=index;endpointDistanceSquared=distanceSquared;}
|
||||
}
|
||||
if(endpoint==route.size())continue;
|
||||
@@ -1821,11 +1857,13 @@ public:
|
||||
const double alongFromEndpoint=std::abs(
|
||||
std::lerp(routeDistance[index-1],routeDistance[index],t)
|
||||
-routeDistance[endpoint]);
|
||||
- if(alongFromEndpoint>PortalWalkLeadMetres)continue;
|
||||
+ if(alongFromEndpoint>portalWalkLeadMetres)continue;
|
||||
const double routeX=ax+t*dx,routeY=ay+t*dy;
|
||||
const double routeZ=az+t*(b.values[2]-a.values[2])/qPerMetre;
|
||||
- if(std::hypot(queryX-routeX,queryY-routeY)<=RouteClearanceMetres
|
||||
- &&std::abs(queryZ-routeZ)<=RouteHeightClearanceMetres)return true;
|
||||
+ const double lateral=std::hypot(queryX-routeX,queryY-routeY);
|
||||
+ const double vertical=std::abs(queryZ-routeZ);
|
||||
+ if(lateral<=RouteClearanceMetres
|
||||
+ &&vertical<=RouteHeightClearanceMetres)return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1887,13 +1925,19 @@ public:
|
||||
}
|
||||
if(nativeFloorSupport!=std::numeric_limits<float>::lowest()
|
||||
&&nativeFloorSupport<0.0f)
|
||||
+ {
|
||||
+ // Host floor support keeps solid ground under a landing chamber, but it
|
||||
+ // cannot refill the chamber air above the selected planned/native floor.
|
||||
+ if(unionLandingChamberDistance<=0.0)
|
||||
+ return passageAirDistance;
|
||||
return std::min(nativeFloorSupport,unionDistance<=0.0
|
||||
?passageAirDistance:density);
|
||||
+ }
|
||||
if(nativeTunnelFieldDensity!=std::numeric_limits<float>::lowest())
|
||||
return std::max(density,passageAirDistance);
|
||||
if(unionDistance>0.0&&unionShellDistance<0.0&&density>=0.0f)
|
||||
{
|
||||
- if(connectedSurfaceLandingShellContains())
|
||||
+ if(connectedNativeLandingShellContains())
|
||||
return std::max(density,passageAirDistance);
|
||||
return static_cast<float>(unionShellDistance/Q);
|
||||
}
|
||||
@@ -2044,11 +2088,12 @@ public:
|
||||
static_cast<double>(chamber.floorGradientQ16[0])/65536.0,
|
||||
static_cast<double>(chamber.floorGradientQ16[1])/65536.0);
|
||||
const double extent=reachCoreVoxels+0.01;
|
||||
+ const double clearHeight=static_cast<double>(
|
||||
+ chamber.clearHeightSubvoxels)/Q;
|
||||
const double low[3]={cx-radius-extent,cy-radius-extent,
|
||||
cz-floorExtent-extent};
|
||||
const double high[3]={cx+radius+extent,cy+radius+extent,
|
||||
- cz+floorExtent+static_cast<double>(
|
||||
- chamber.clearHeightSubvoxels)/Q+extent};
|
||||
+ cz+floorExtent+2.0*clearHeight+extent};
|
||||
return overlaps(low,high);
|
||||
};
|
||||
if(chamberOverlaps(passage.upperLandingChamber)
|
||||
@@ -5488,7 +5533,7 @@ private:
|
||||
cx-radius-extent,cy-radius-extent,cz-floorExtent-extent};
|
||||
const std::array<double,3> maximum{
|
||||
cx+radius+extent,cy+radius+extent,
|
||||
- cz+floorExtent+height+extent};
|
||||
+ cz+floorExtent+2.0*height+extent};
|
||||
FPassageProofBound proof;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
@@ -5859,6 +5904,71 @@ public:
|
||||
return std::isfinite(OutGroundZ)&&std::isfinite(OutCeilingZ);
|
||||
}
|
||||
|
||||
+ bool GetNativeSurfaceGroundHeightAt(float X,float Y,float Z,
|
||||
+ double& OutGroundZ) const
|
||||
+ {
|
||||
+ if(!ResolvedLayout||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))
|
||||
+ return false;
|
||||
+ const auto* bound=FindBoundStratumForManagerZ(Z);
|
||||
+ if(!bound)return false;
|
||||
+ constexpr double Scale=static_cast<double>(
|
||||
+ VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
+ const double maxVoxel=static_cast<double>(std::numeric_limits<std::int64_t>::max())/Scale;
|
||||
+ if(std::abs(static_cast<double>(X))>maxVoxel
|
||||
+ ||std::abs(static_cast<double>(Y))>maxVoxel)return false;
|
||||
+ const auto toFixed=[](float value)
|
||||
+ {return static_cast<std::int64_t>(std::llround(static_cast<double>(value)
|
||||
+ *VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
+ const auto* field=std::get_if<FSurfaceWorldFieldAdapter>(&bound->Evaluator);
|
||||
+ if(field&&field->Evaluator.isValid())
|
||||
+ {
|
||||
+ const auto column=field->Evaluator.point(toFixed(X),toFixed(Y),0);
|
||||
+ OutGroundZ=static_cast<double>(column.groundHeightSubvoxels)/Scale;
|
||||
+ return std::isfinite(OutGroundZ);
|
||||
+ }
|
||||
+ const auto* islands=std::get_if<FFloatingIslandFieldAdapter>(&bound->Evaluator);
|
||||
+ if(!islands||!bound->IslandPlan)return false;
|
||||
+ const auto& route=bound->IslandPlan->manifest.plan.route;
|
||||
+ if(route.size()<2)return false;
|
||||
+ const auto& recipe=bound->IslandPlan->manifest.recipe;
|
||||
+ const std::int64_t routeLift=recipe.routeConstraints.capsuleHeightSubvoxels/2
|
||||
+ +recipe.routeConstraints.minimumClearanceSubvoxels
|
||||
+ +(recipe.geometryVersion>=2
|
||||
+ ?VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel/8:0);
|
||||
+ const std::int64_t supportRadius=recipe.routeConstraints
|
||||
+ .supportProbeRadiusSubvoxels;
|
||||
+ if(routeLift<0||supportRadius<0)return false;
|
||||
+ const std::int64_t queryX=toFixed(X),queryY=toFixed(Y);
|
||||
+ std::uint64_t nearestDistanceSquared=std::numeric_limits<std::uint64_t>::max();
|
||||
+ std::int64_t nearestRouteZ=0;
|
||||
+ bool found=false;
|
||||
+ for(std::size_t index=1;index<route.size();++index)
|
||||
+ {
|
||||
+ const auto& a=route[index-1].positionSubvoxels;
|
||||
+ const auto& b=route[index].positionSubvoxels;
|
||||
+ const std::int64_t dx=b[0]-a[0],dy=b[1]-a[1];
|
||||
+ const std::int64_t lengthSquared=dx*dx+dy*dy;
|
||||
+ if(lengthSquared<=0)continue;
|
||||
+ const std::int64_t projection=std::clamp(
|
||||
+ (queryX-a[0])*dx+(queryY-a[1])*dy,
|
||||
+ std::int64_t{0},lengthSquared);
|
||||
+ const std::int64_t closestX=a[0]+dx*projection/lengthSquared;
|
||||
+ const std::int64_t closestY=a[1]+dy*projection/lengthSquared;
|
||||
+ const std::int64_t offsetX=queryX-closestX,offsetY=queryY-closestY;
|
||||
+ const std::uint64_t distanceSquared=static_cast<std::uint64_t>(
|
||||
+ offsetX*offsetX+offsetY*offsetY);
|
||||
+ if(distanceSquared>static_cast<std::uint64_t>(supportRadius)*supportRadius
|
||||
+ ||distanceSquared>=nearestDistanceSquared)continue;
|
||||
+ const std::int64_t dz=b[2]-a[2];
|
||||
+ nearestRouteZ=a[2]+dz*projection/lengthSquared;
|
||||
+ nearestDistanceSquared=distanceSquared;
|
||||
+ found=true;
|
||||
+ }
|
||||
+ if(!found)return false;
|
||||
+ OutGroundZ=static_cast<double>(nearestRouteZ-routeLift)/Scale;
|
||||
+ return std::isfinite(OutGroundZ);
|
||||
+ }
|
||||
+
|
||||
EVoxelTileClass ClassifyBox(const FIntVector& Origin,int32 Step,int32 Cells) const
|
||||
{ return ClassifyBoxDetailed(Origin,Step,Cells).Verdict; }
|
||||
|
||||
diff --git a/Standalone/Core/src/VoxelForgeCore.cpp b/Standalone/Core/src/VoxelForgeCore.cpp
|
||||
index b139ec4..e40250c 100644
|
||||
--- a/Standalone/Core/src/VoxelForgeCore.cpp
|
||||
+++ b/Standalone/Core/src/VoxelForgeCore.cpp
|
||||
@@ -1811,6 +1811,13 @@ namespace VoxelForgeCore
|
||||
|
||||
float World::densityAt(Vec3d position) const
|
||||
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DensityAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):0.0f; }
|
||||
+ bool World::nativeSurfaceGroundHeightAt(Vec3d position,double& groundHeightVoxels) const
|
||||
+ {
|
||||
+ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape
|
||||
+ &&impl_->shape->GetNativeSurfaceGroundHeightAt(static_cast<float>(position.x),
|
||||
+ static_cast<float>(position.y),static_cast<float>(position.z),
|
||||
+ groundHeightVoxels);
|
||||
+ }
|
||||
MaterialSampleV1 World::materialAt(Vec3d position) const
|
||||
{
|
||||
if(!impl_||!impl_->shapeReady.load(std::memory_order_acquire)||!impl_->shape)
|
||||
diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h
|
||||
index 2fcf4b5..3ed4dd1 100644
|
||||
--- a/Standalone/Source/StreamBenchmarkPipeline.h
|
||||
+++ b/Standalone/Source/StreamBenchmarkPipeline.h
|
||||
@@ -3051,7 +3051,8 @@ namespace StandaloneStreamBenchmark
|
||||
double supportMaximumMetres,std::uint32_t minimumSupportProbes,
|
||||
double supportProbeRadiusMetres,
|
||||
const std::function<bool(double,double,double)>& supportExemption={},
|
||||
- const std::function<bool(double,double,double)>& passageInteriorPredicate={})
|
||||
+ const std::function<bool(double,double,double)>& passageInteriorPredicate={},
|
||||
+ const std::function<float(VoxelForgeCore::Vec3d)>& densityOverride={})
|
||||
{
|
||||
FWalkAudit audit;
|
||||
if(path.size()<2)return audit;
|
||||
@@ -3068,15 +3069,18 @@ namespace StandaloneStreamBenchmark
|
||||
&&passageInteriorPredicate(xMetres,yMetres,zMetres);
|
||||
if(inPassageInterior)++audit.passageInteriorCapsuleSamples;
|
||||
const VoxelForgeCore::Vec3d position{xMetres/coreMetres,yMetres/coreMetres,zMetres/coreMetres};
|
||||
- const float density=Field.densityAt(position);
|
||||
+ const float density=densityOverride
|
||||
+ ?densityOverride(position):Field.densityAt(position);
|
||||
if(density<0.0f)
|
||||
{
|
||||
if(inPassageInterior)++audit.passageInteriorSolidSamples;
|
||||
if(audit.firstBlockedCount<audit.firstBlocked.size())
|
||||
{
|
||||
audit.firstBlocked[audit.firstBlockedCount++]={xMetres,yMetres,zMetres,density};
|
||||
+ const std::string dispatchPath=densityOverride
|
||||
+ ?"synthetic-open-floor":Field.densityDispatchPathAt(position);
|
||||
std::printf("[ResolvedWalkDispatch] kind=blocked xyz_m=(%.3f,%.3f,%.3f) path=%s\n",
|
||||
- xMetres,yMetres,zMetres,Field.densityDispatchPathAt(position).c_str());
|
||||
+ xMetres,yMetres,zMetres,dispatchPath.c_str());
|
||||
}
|
||||
++audit.blocked;return true;
|
||||
}
|
||||
@@ -3149,8 +3153,11 @@ namespace StandaloneStreamBenchmark
|
||||
{
|
||||
++audit.supportSamples;
|
||||
offsetSampleZ=(footZ-depth)/coreMetres;
|
||||
- lastUnsupportedDensity=Field.densityAt({supportX/coreMetres,
|
||||
- supportY/coreMetres,offsetSampleZ});
|
||||
+ const VoxelForgeCore::Vec3d supportPosition{
|
||||
+ supportX/coreMetres,supportY/coreMetres,offsetSampleZ};
|
||||
+ lastUnsupportedDensity=densityOverride
|
||||
+ ?densityOverride(supportPosition)
|
||||
+ :Field.densityAt(supportPosition);
|
||||
if(lastUnsupportedDensity<0.0)
|
||||
{
|
||||
supported=true;break;
|
||||
@@ -3172,11 +3179,16 @@ namespace StandaloneStreamBenchmark
|
||||
audit.firstUnsupported[sample]={
|
||||
firstUnsupportedX,firstUnsupportedY,firstUnsupportedZ,
|
||||
lastUnsupportedDensity};
|
||||
+ const VoxelForgeCore::Vec3d supportPosition{
|
||||
+ firstUnsupportedX/coreMetres,firstUnsupportedY/coreMetres,
|
||||
+ footZ/coreMetres};
|
||||
+ const std::string dispatchPath=densityOverride
|
||||
+ ?"synthetic-open-floor":Field.densityDispatchPathAt(
|
||||
+ supportPosition);
|
||||
std::printf("[ResolvedWalkDispatch] kind=unsupported xyz_m=(%.3f,%.3f,%.3f) support_probes=%u/5 mask=%d%d%d%d%d path=%s\n",
|
||||
firstUnsupportedX,firstUnsupportedY,firstUnsupportedZ,supportedProbes,
|
||||
supportMask[0],supportMask[1],supportMask[2],supportMask[3],supportMask[4],
|
||||
- Field.densityDispatchPathAt({firstUnsupportedX/coreMetres,
|
||||
- firstUnsupportedY/coreMetres,footZ/coreMetres}).c_str());
|
||||
+ dispatchPath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3712,7 +3724,8 @@ namespace StandaloneStreamBenchmark
|
||||
const auto passageInteriorWinsAt=[&](double xMetres,double yMetres,
|
||||
double zMetres,std::size_t ignoredPassage=std::numeric_limits<std::size_t>::max(),
|
||||
std::size_t ignoredSegment=std::numeric_limits<std::size_t>::max(),
|
||||
- std::size_t selectedPassage=std::numeric_limits<std::size_t>::max())
|
||||
+ std::size_t selectedPassage=std::numeric_limits<std::size_t>::max(),
|
||||
+ bool includeLandingChambers=true)
|
||||
{
|
||||
constexpr double CornerBlendMetres=0.50;
|
||||
constexpr double WallReliefMetres=0.21;
|
||||
@@ -3748,8 +3761,9 @@ namespace StandaloneStreamBenchmark
|
||||
+qToMetres(chamber.clearHeightSubvoxels));
|
||||
return std::max({radialDistance,floorDistance,ceilingDistance})<=0.0;
|
||||
};
|
||||
- if(inLandingChamber(passage.upperLandingChamber)
|
||||
- ||inLandingChamber(passage.lowerLandingChamber))return true;
|
||||
+ if(includeLandingChambers
|
||||
+ &&(inLandingChamber(passage.upperLandingChamber)
|
||||
+ ||inLandingChamber(passage.lowerLandingChamber)))return true;
|
||||
const auto& route=allPassageSmoothRoutes[passageIndex];
|
||||
if(route.size()<2)continue;
|
||||
double bestRoundDistance=std::numeric_limits<double>::infinity();
|
||||
@@ -4722,7 +4736,13 @@ namespace StandaloneStreamBenchmark
|
||||
const double mouthZ=qToMetres(upper?passage.upperMouthSubvoxels[2]
|
||||
:passage.lowerMouthSubvoxels[2]);
|
||||
std::size_t endpoint=route.size();
|
||||
- double endpointDistanceSquared=1.0;
|
||||
+ const double endpointRadius=stratum->generatorKind=="FloatingIslands"
|
||||
+ ?3.0:1.0;
|
||||
+ const double endpointHeight=stratum->generatorKind=="FloatingIslands"
|
||||
+ ?2.0:1.5;
|
||||
+ const double portalWalkLead=stratum->generatorKind=="FloatingIslands"
|
||||
+ ?12.0:9.0;
|
||||
+ double endpointDistanceSquared=endpointRadius*endpointRadius;
|
||||
for(std::size_t index=0;index<route.size();++index)
|
||||
{
|
||||
const double dx=qToMetres(route[index]->values[0])-cx;
|
||||
@@ -4730,7 +4750,7 @@ namespace StandaloneStreamBenchmark
|
||||
const double z=qToMetres(route[index]->values[2]);
|
||||
const double distanceSquared=dx*dx+dy*dy;
|
||||
if(distanceSquared<endpointDistanceSquared
|
||||
- &&std::abs(z-mouthZ)<=1.5)
|
||||
+ &&std::abs(z-mouthZ)<=endpointHeight)
|
||||
{endpoint=index;endpointDistanceSquared=distanceSquared;}
|
||||
}
|
||||
if(endpoint==route.size())return false;
|
||||
@@ -4758,7 +4778,7 @@ namespace StandaloneStreamBenchmark
|
||||
const double fromEndpoint=std::abs(std::lerp(
|
||||
routeDistance[index-1],routeDistance[index],t)
|
||||
-routeDistance[endpoint]);
|
||||
- if(fromEndpoint>9.0)continue;
|
||||
+ if(fromEndpoint>portalWalkLead)continue;
|
||||
const double routeX=ax+t*dx,routeY=ay+t*dy;
|
||||
const double routeZ=az+t*qToMetres(b.values[2]-a.values[2]);
|
||||
const double lateralDistance=std::hypot(xMetres-routeX,
|
||||
@@ -5167,6 +5187,21 @@ namespace StandaloneStreamBenchmark
|
||||
const double radius=qToMetres(chamber.radiusSubvoxels);
|
||||
const double gx=chamber.floorGradientQ16[0]/65536.0;
|
||||
const double gy=chamber.floorGradientQ16[1]/65536.0;
|
||||
+ const auto floorAt=[&](double x,double y)
|
||||
+ {
|
||||
+ const double planned=baseFloorZ+gx*(x-centerX)+gy*(y-centerY);
|
||||
+ double nativeGroundVoxels=0.0;
|
||||
+ if(Field.nativeSurfaceGroundHeightAt({x/coreMetres,y/coreMetres,
|
||||
+ planned/coreMetres},nativeGroundVoxels))
|
||||
+ {
|
||||
+ const double nativeGround=nativeGroundVoxels*coreMetres;
|
||||
+ const double clearHeight=qToMetres(
|
||||
+ chamber.clearHeightSubvoxels);
|
||||
+ if(nativeGround>planned&&nativeGround<=planned+clearHeight)
|
||||
+ return nativeGround;
|
||||
+ }
|
||||
+ return planned;
|
||||
+ };
|
||||
for(const double radial:chamberFloorRadii)
|
||||
{
|
||||
if(radial>=radius-1.0)continue;
|
||||
@@ -5177,16 +5212,18 @@ namespace StandaloneStreamBenchmark
|
||||
*static_cast<double>(angleIndex)/ChamberAngularSamples;
|
||||
const double sampleX=centerX+radial*std::cos(angle);
|
||||
const double sampleY=centerY+radial*std::sin(angle);
|
||||
- const double floorZ=baseFloorZ+gx*(sampleX-centerX)
|
||||
- +gy*(sampleY-centerY);
|
||||
+ const double floorZ=floorAt(sampleX,sampleY);
|
||||
for(const double depth:PassageShellAuditDepthsMetres)
|
||||
{
|
||||
const double sampleZ=floorZ-depth;
|
||||
const VoxelForgeCore::Vec3d position{sampleX/coreMetres,
|
||||
sampleY/coreMetres,sampleZ/coreMetres};
|
||||
if(Native.densityAt(position)<0.0f)continue;
|
||||
- if(landingChamberConnectionContains(passage,upper,
|
||||
- sampleX,sampleY,sampleZ))
|
||||
+ if(passageInteriorWinsAt(sampleX,sampleY,sampleZ,
|
||||
+ std::numeric_limits<std::size_t>::max(),
|
||||
+ std::numeric_limits<std::size_t>::max(),passageIndex)
|
||||
+ ||landingChamberConnectionContains(passage,upper,
|
||||
+ sampleX,sampleY,sampleZ))
|
||||
{++audit.chamberConnectionSkips;continue;}
|
||||
++audit.chamberFloorSamples;
|
||||
const float density=Field.densityAt(position);
|
||||
@@ -5211,8 +5248,7 @@ namespace StandaloneStreamBenchmark
|
||||
{
|
||||
const double sampleX=centerX+(radius+depth)*nx;
|
||||
const double sampleY=centerY+(radius+depth)*ny;
|
||||
- const double floorZ=baseFloorZ+gx*(sampleX-centerX)
|
||||
- +gy*(sampleY-centerY);
|
||||
+ const double floorZ=floorAt(sampleX,sampleY);
|
||||
const double sampleZ=floorZ+height;
|
||||
const VoxelForgeCore::Vec3d position{sampleX/coreMetres,
|
||||
sampleY/coreMetres,sampleZ/coreMetres};
|
||||
@@ -5275,6 +5311,25 @@ namespace StandaloneStreamBenchmark
|
||||
{return passageInteriorWinsAt(x,y,z,
|
||||
std::numeric_limits<std::size_t>::max(),
|
||||
std::numeric_limits<std::size_t>::max(),passageIndex);};
|
||||
+ const auto floorAt=[&](double x,double y)
|
||||
+ {
|
||||
+ const double planned=centerZ+gx*(x-centerX)+gy*(y-centerY);
|
||||
+ double nativeGroundVoxels=0.0;
|
||||
+ if(field.nativeSurfaceGroundHeightAt({x/coreMetres,y/coreMetres,
|
||||
+ planned/coreMetres},nativeGroundVoxels))
|
||||
+ {
|
||||
+ const double nativeGround=nativeGroundVoxels*coreMetres;
|
||||
+ const double clearHeight=qToMetres(chamber.clearHeightSubvoxels);
|
||||
+ if(nativeGround>planned&&nativeGround<=planned+clearHeight)
|
||||
+ return nativeGround;
|
||||
+ }
|
||||
+ return planned;
|
||||
+ };
|
||||
+ const std::function<bool(double,double,double)> supportExemption=
|
||||
+ [&](double x,double y,double z)
|
||||
+ {return passageInteriorWinsAt(x,y,z,
|
||||
+ std::numeric_limits<std::size_t>::max(),
|
||||
+ std::numeric_limits<std::size_t>::max(),passageIndex,false);};
|
||||
for(const auto& direction:directions)
|
||||
{
|
||||
std::vector<std::array<double,3>> chamberPath;
|
||||
@@ -5286,12 +5341,12 @@ namespace StandaloneStreamBenchmark
|
||||
const double distance=radius*step/intervals;
|
||||
const double x=centerX+direction[0]*distance;
|
||||
const double y=centerY+direction[1]*distance;
|
||||
- const double floorZ=centerZ+gx*(x-centerX)+gy*(y-centerY);
|
||||
+ const double floorZ=floorAt(x,y);
|
||||
chamberPath.push_back({x/coreMetres,y/coreMetres,
|
||||
(floorZ+1.13)/coreMetres});
|
||||
}
|
||||
const FWalkAudit part=auditPlayerPath(field,chamberPath,
|
||||
- 0.125,0.125,1.125,3,0.45,{},interiorPredicate);
|
||||
+ 0.125,0.125,1.125,3,0.45,supportExemption,interiorPredicate);
|
||||
total.segments+=part.segments;total.centers+=part.centers;
|
||||
total.capsuleSamples+=part.capsuleSamples;
|
||||
total.blocked+=part.blocked;
|
||||
@@ -5305,10 +5360,116 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
return total;
|
||||
};
|
||||
+ const auto auditOpenChamberFloorNegativeControl=[&](
|
||||
+ const VoxelForgeCore::World& field,
|
||||
+ const VoxelForgeCore::ResolvedLayoutPassageV1& passage,
|
||||
+ std::size_t passageIndex,bool upper)
|
||||
+ {
|
||||
+ const auto& chamber=upper?passage.upperLandingChamber
|
||||
+ :passage.lowerLandingChamber;
|
||||
+ const double centerX=qToMetres(chamber.floorCenterSubvoxels[0]);
|
||||
+ const double centerY=qToMetres(chamber.floorCenterSubvoxels[1]);
|
||||
+ const double centerZ=qToMetres(chamber.floorCenterSubvoxels[2]);
|
||||
+ const double gx=chamber.floorGradientQ16[0]/65536.0;
|
||||
+ const double gy=chamber.floorGradientQ16[1]/65536.0;
|
||||
+ const double chamberAuditRadius=std::min(5.75,
|
||||
+ qToMetres(chamber.radiusSubvoxels)-1.5);
|
||||
+ const double mouthRadius=qToMetres(passage.mouthRadiusSubvoxels);
|
||||
+ const double radius=std::min(chamberAuditRadius-0.1,mouthRadius+1.25);
|
||||
+ if(radius<=0.0)
|
||||
+ {
|
||||
+ std::printf("[PassageLandingChamberOpenFloorNegativeControl] id=%s side=%s radius_m=%.3f mouth_radius_m=%.3f unsupported=0 support_overlap_skips=0 expected=FAIL status=FAIL reason=no_outer_floor_sample\n",
|
||||
+ passage.stableId.c_str(),upper?"upper":"lower",radius,mouthRadius);
|
||||
+ return false;
|
||||
+ }
|
||||
+ const auto floorAt=[&](double x,double y)
|
||||
+ {
|
||||
+ const double planned=centerZ+gx*(x-centerX)+gy*(y-centerY);
|
||||
+ double nativeGroundVoxels=0.0;
|
||||
+ if(field.nativeSurfaceGroundHeightAt({x/coreMetres,y/coreMetres,
|
||||
+ planned/coreMetres},nativeGroundVoxels))
|
||||
+ {
|
||||
+ const double nativeGround=nativeGroundVoxels*coreMetres;
|
||||
+ const double clearHeight=qToMetres(chamber.clearHeightSubvoxels);
|
||||
+ if(nativeGround>planned&&nativeGround<=planned+clearHeight)
|
||||
+ return nativeGround;
|
||||
+ }
|
||||
+ return planned;
|
||||
+ };
|
||||
+ const std::function<bool(double,double,double)> interiorPredicate=
|
||||
+ [&](double x,double y,double z)
|
||||
+ {return passageInteriorWinsAt(x,y,z,
|
||||
+ std::numeric_limits<std::size_t>::max(),
|
||||
+ std::numeric_limits<std::size_t>::max(),passageIndex);};
|
||||
+ const std::function<bool(double,double,double)> supportExemption=
|
||||
+ [&](double x,double y,double z)
|
||||
+ {return passageInteriorWinsAt(x,y,z,
|
||||
+ std::numeric_limits<std::size_t>::max(),
|
||||
+ std::numeric_limits<std::size_t>::max(),passageIndex,false);};
|
||||
+ constexpr std::array<std::array<double,2>,5> supportOffsets{{
|
||||
+ {{0.0,0.0}},{{0.45,0.0}},{{-0.45,0.0}},
|
||||
+ {{0.0,0.45}},{{0.0,-0.45}}}};
|
||||
+ std::vector<std::array<double,3>> path;
|
||||
+ double selectedAngle=0.0;
|
||||
+ bool foundOutsideBore=false;
|
||||
+ for(std::size_t directionIndex=0;directionIndex<32&&!foundOutsideBore;
|
||||
+ ++directionIndex)
|
||||
+ {
|
||||
+ selectedAngle=2.0*3.14159265358979323846
|
||||
+ *static_cast<double>(directionIndex)/32.0;
|
||||
+ const double nx=std::cos(selectedAngle),ny=std::sin(selectedAngle);
|
||||
+ const double tangentX=-ny,tangentY=nx;
|
||||
+ const double x0=centerX+radius*nx-0.25*tangentX;
|
||||
+ const double y0=centerY+radius*ny-0.25*tangentY;
|
||||
+ const double x1=centerX+radius*nx+0.25*tangentX;
|
||||
+ const double y1=centerY+radius*ny+0.25*tangentY;
|
||||
+ const double floor0=floorAt(x0,y0),floor1=floorAt(x1,y1);
|
||||
+ const std::array<std::array<double,3>,2> candidate{{
|
||||
+ {{x0,y0,floor0+1.13}},{{x1,y1,floor1+1.13}}}};
|
||||
+ bool samplesOutsideBore=true;
|
||||
+ for(const auto& point:candidate)
|
||||
+ {
|
||||
+ const double supportZ=point[2]-0.88-0.125;
|
||||
+ for(const auto& offset:supportOffsets)
|
||||
+ if(supportExemption(point[0]+offset[0],
|
||||
+ point[1]+offset[1],supportZ))
|
||||
+ {samplesOutsideBore=false;break;}
|
||||
+ if(!samplesOutsideBore)break;
|
||||
+ }
|
||||
+ if(samplesOutsideBore)
|
||||
+ {
|
||||
+ path={{candidate[0][0]/coreMetres,candidate[0][1]/coreMetres,
|
||||
+ candidate[0][2]/coreMetres},
|
||||
+ {candidate[1][0]/coreMetres,candidate[1][1]/coreMetres,
|
||||
+ candidate[1][2]/coreMetres}};
|
||||
+ foundOutsideBore=true;
|
||||
+ }
|
||||
+ }
|
||||
+ if(!foundOutsideBore)
|
||||
+ {
|
||||
+ std::printf("[PassageLandingChamberOpenFloorNegativeControl] id=%s side=%s radius_m=%.3f mouth_radius_m=%.3f unsupported=0 support_overlap_skips=0 expected=FAIL status=FAIL reason=no_bore_clear_spoke\n",
|
||||
+ passage.stableId.c_str(),upper?"upper":"lower",radius,mouthRadius);
|
||||
+ return false;
|
||||
+ }
|
||||
+ const std::function<float(VoxelForgeCore::Vec3d)> openDensity=
|
||||
+ [](VoxelForgeCore::Vec3d){return 1.0f;};
|
||||
+ const FWalkAudit control=auditPlayerPath(field,path,0.125,0.125,
|
||||
+ 1.125,3,0.45,supportExemption,interiorPredicate,openDensity);
|
||||
+ const bool passed=!control.passed&&control.unsupported>0
|
||||
+ &&control.supportOverlapSkips==0;
|
||||
+ std::printf("[PassageLandingChamberOpenFloorNegativeControl] id=%s side=%s radius_m=%.3f mouth_radius_m=%.3f angle_deg=%.1f unsupported=%llu support_overlap_skips=%llu result=%s expected=FAIL status=%s\n",
|
||||
+ passage.stableId.c_str(),upper?"upper":"lower",radius,mouthRadius,
|
||||
+ selectedAngle*180.0/3.14159265358979323846,
|
||||
+ static_cast<unsigned long long>(control.unsupported),
|
||||
+ static_cast<unsigned long long>(control.supportOverlapSkips),
|
||||
+ control.passed?"PASS":"FAIL",passed?"PASS":"FAIL");
|
||||
+ return passed;
|
||||
+ };
|
||||
std::uint64_t passageCount=0,passagePasses=0,blockedControls=0,naturalPasses=0;
|
||||
std::uint64_t passageInteriorCapsuleSamples=0,passageInteriorSolidSamples=0;
|
||||
std::uint64_t passageInteriorControls=0;
|
||||
std::uint64_t landingChamberCount=0,landingChamberNegativeControls=0;
|
||||
+ std::uint64_t openChamberFloorNegativeControls=0;
|
||||
std::uint64_t mouthProfilePasses=0,ripplePasses=0,rippleControlsDetected=0;
|
||||
std::uint64_t floorRippleControlsDetected=0,floorStepRippleControlsDetected=0;
|
||||
std::uint64_t shellAudits=0,shellFloorSamples=0,shellFloorViolations=0;
|
||||
@@ -5346,6 +5507,9 @@ namespace StandaloneStreamBenchmark
|
||||
passage,passageIndex,true);
|
||||
const FWalkAudit lowerChamberAudit=auditLandingChamber(Shape,
|
||||
passage,passageIndex,false);
|
||||
+ const bool openChamberFloorControl=auditOpenChamberFloorNegativeControl(
|
||||
+ Shape,passage,passageIndex,true);
|
||||
+ openChamberFloorNegativeControls+=openChamberFloorControl;
|
||||
const bool landingChambersWalkable=upperChamberAudit.passed
|
||||
&&lowerChamberAudit.passed;
|
||||
const bool passageInteriorAirPassed=
|
||||
@@ -5889,6 +6053,10 @@ namespace StandaloneStreamBenchmark
|
||||
static_cast<unsigned long long>(landingChamberCount),
|
||||
static_cast<unsigned long long>(landingChamberCount
|
||||
-landingChamberNegativeControls));
|
||||
+ std::printf("[PassageLandingChamberOpenFloorNegativeControlSummary] expected_fail_detected=%llu/%llu status=%s\n",
|
||||
+ static_cast<unsigned long long>(openChamberFloorNegativeControls),
|
||||
+ static_cast<unsigned long long>(passageCount),
|
||||
+ openChamberFloorNegativeControls==passageCount?"PASS":"FAIL");
|
||||
const bool shellAuditPassed=shellAudits==passageCount
|
||||
&&shellFloorViolations==0&&shellWallViolations==0
|
||||
&&shellChamberFloorViolations==0&&shellChamberWallViolations==0
|
||||
@@ -5939,6 +6107,7 @@ namespace StandaloneStreamBenchmark
|
||||
&&mouthProfilePasses==passageCount&&l23TerraceControlDetected
|
||||
&&ripplePasses==passageCount
|
||||
&&shellAuditPassed
|
||||
+ &&openChamberFloorNegativeControls==passageCount
|
||||
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
|
||||
&&routeBindingFailures==0
|
||||
&&tunnelNetworkRoutes==tunnelNetworkBlockedControls
|
||||
Reference in new issue
Block a user