wip(unreal): round 5 (stopped early) -- all 15 passage/strate probes walkable

Luna, round 5 goal 1 only (stopped by owner decision before the SurfaceWorld port):
- Worm and Cascading passages now pass walkability; the wide floor stays clipped while the
  narrow route core stays open.
- Hidden third failure found and fixed: long VerticalShafts diagonal legs were dropped from
  Core's spatial bins by a 100,000-bin safety cutoff (invisible to density queries);
  conservative bins restore them (18,570/18,570).
- All 15 saved probes pass a walkability-only gate; build-time walkability gate added.
NOT re-verified before the stop: W1/W8/W16 hashes, BuildHost UBT/actor run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-08 22:50:54 +02:00
1 parent d696463c68
commit 7661fe9d24
3 files changed
+120 -14

No files matched your search

+99 -13
View File
@@ -341,7 +341,11 @@ public:
// Route validation is world-wide. Detail regions reuse the same authored route data and
// do not need to repeat the 5,642-point world audit for every entering tile group.
Substage=Clock::now();
if(bValidatePassageRoutes)ValidatePassageRoutes();
if(bValidatePassageRoutes&&!ValidatePassageRoutes())
{
Error="compiled shape world contains a solid passage centreline sample";
return false;
}
Stats.PassageValidationSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
Substage=Clock::now();
RebuildSharedFieldPrimitives();
@@ -2640,14 +2644,16 @@ private:
const float CoreRadiusB=std::min(2.5f,RadiusB*0.45f);
const double CoreSlopeA=CoreRadiusA<2.5f-1.0e-4?RadiusSlopeA*0.45:0.0;
const double CoreSlopeB=CoreRadiusB<2.5f-1.0e-4?RadiusSlopeB*0.45:0.0;
// Keep a narrow, un-clipped air core through every authored control segment.
// The larger outer tube owns the D-section floor; its interpolated floor curve
// can bow above the path on sharp drops, so clipping this safety bore would
// close the passage precisely where Cascading/Worm need the continuity repair.
// Garder un petit noyau d'air sans coupe de plancher dans chaque segment.
// Le tube large porte le plancher en D; sa courbe peut depasser l'axe dans les
// chutes brusques et fermer la route si ce noyau herite de la meme coupe.
AddTaperedCapsule(Points[I],Points[I+1],CoreRadiusA,
CoreRadiusB,true,true,true,ZA,ZB,
CoreRadiusB,true,true,false,ZA,ZB,
CoreExtendStart,CoreExtendEnd);
SetCurvedFloorClip(Primitives.back(),Points[I],Points[I+1],ZA,ZB,
FloorTangents[static_cast<size_t>(I)],FloorTangents[static_cast<size_t>(I+1)]);
++Stats.CurvedFloorSegments;
Stats.MaxFloorCurveDeviation=std::max(Stats.MaxFloorCurveDeviation,
Primitives.back().FloorCurveMaxDeviation);
FitCapsuleRadiiAtEndpoints(Primitives.back(),Points[I],CoreRadiusA,
Points[I+1],CoreRadiusB);
SetCapsuleRadiusProfile(Primitives.back(),Points[I],CoreRadiusA,CoreSlopeA,
@@ -2869,7 +2875,59 @@ private:
const int32 X0=FloorDiv(P.Min[0]-DetailBand,BinResolution),X1=FloorDiv(P.Max[0]+DetailBand,BinResolution);
const int32 Y0=FloorDiv(P.Min[1]-DetailBand,BinResolution),Y1=FloorDiv(P.Max[1]+DetailBand,BinResolution);
const int32 Z0=FloorDiv(P.Min[2]-DetailBand,BinResolution),Z1=FloorDiv(P.Max[2]+DetailBand,BinResolution);
if(static_cast<int64>(X1-X0+1)*(Y1-Y0+1)*(Z1-Z0+1)>100000)continue;
const int64 BoundingBinCount=static_cast<int64>(X1-X0+1)*(Y1-Y0+1)*(Z1-Z0+1);
if(BoundingBinCount>100000)
{
// Long diagonal passages have a huge AABB even though their capsule is narrow.
// Rasterize the centerline with a conservative bin halo instead of dropping the
// primitive from analytic queries outside the packed near region.
// Les longues galeries diagonales ont une grande AABB mais un tube fin.
// Echantillonner l'axe avec une marge prudente garde les requetes analytiques.
if(!P.bCapsule)continue;
const double BinWidth=static_cast<double>(BinResolution);
const double AxisStep=std::max(1.0,BinWidth*0.5);
const int32 AxisSamples=std::max(1,static_cast<int32>(std::ceil(
P.CapsuleAxisLength/AxisStep)));
const double CapsuleRadius=std::max(P.CapsuleStartRadius,P.CapsuleEndRadius);
const int32 Halo=std::clamp(static_cast<int32>(std::ceil(
(CapsuleRadius+DetailBand)/BinWidth))+1,1,8);
std::vector<FBinKey> PrimitiveBins;
const int64 HaloWidth=2*Halo+1;
const uint64 Reserve=std::min<uint64>(
static_cast<uint64>(AxisSamples+1)*HaloWidth*HaloWidth*HaloWidth,
1000000ull);
PrimitiveBins.reserve(static_cast<size_t>(Reserve));
for(int32 Sample=0;Sample<=AxisSamples;++Sample)
{
const double T=static_cast<double>(Sample)/AxisSamples;
const int32 CX=static_cast<int32>(std::floor(P.CapsuleStartWorld[0]
+P.CapsuleAxisDelta[0]*T));
const int32 CY=static_cast<int32>(std::floor(P.CapsuleStartWorld[1]
+P.CapsuleAxisDelta[1]*T));
const int32 CZ=static_cast<int32>(std::floor(P.CapsuleStartWorld[2]
+P.CapsuleAxisDelta[2]*T));
const int32 BX=FloorDiv(CX,BinResolution),BY=FloorDiv(CY,BinResolution);
const int32 BZ=FloorDiv(CZ,BinResolution);
for(int32 DZ=-Halo;DZ<=Halo;++DZ)
for(int32 DY=-Halo;DY<=Halo;++DY)
for(int32 DX=-Halo;DX<=Halo;++DX)
PrimitiveBins.push_back({BX+DX,BY+DY,BZ+DZ});
}
std::sort(PrimitiveBins.begin(),PrimitiveBins.end(),[](const FBinKey& A,const FBinKey& B)
{
if(A.X!=B.X)return A.X<B.X;
if(A.Y!=B.Y)return A.Y<B.Y;
return A.Z<B.Z;
});
PrimitiveBins.erase(std::unique(PrimitiveBins.begin(),PrimitiveBins.end()),
PrimitiveBins.end());
for(const FBinKey& Key:PrimitiveBins)
{
SpatialBins[Key].push_back(Id);
if(bCriticalAir)CriticalAirBins[Key].push_back(Id);
}
continue;
}
for(int32 Z=Z0;Z<=Z1;++Z)for(int32 Y=Y0;Y<=Y1;++Y)for(int32 X=X0;X<=X1;++X)
{
const FBinKey Key{X,Y,Z};
@@ -4660,28 +4718,55 @@ private:
return RockDistance>=0.0f;
}
void ValidatePassageRoutes()
bool ValidatePassageRoutes()
{
constexpr double SampleStep=0.5;
uint32 PassageIndex=0;
for(const FVoxelPassage& P:Owner->Manager->GetPassages())
{
const auto& Points=P.ControlPoints;
if(Points.Num()<2)continue;
if(Points.Num()<2){++PassageIndex;continue;}
uint64 RouteSamples=0,RouteFailures=0,RouteCritical=0;
int32 FirstFailure[3]={0,0,0};bool bFirstFailureCritical=false;
for(int32 I=0;I+1<Points.Num();++I)
{
const FVector A=Points[I],B=Points[I+1];
const double L=std::sqrt((B.X-A.X)*(B.X-A.X)+(B.Y-A.Y)*(B.Y-A.Y)+(B.Z-A.Z)*(B.Z-A.Z));
const int32 Count=std::max(1,static_cast<int32>(std::ceil(L/SampleStep)));
uint64 SegmentSamples=0,SegmentFailures=0,SegmentCritical=0;
for(int32 J=0;J<=Count;++J)
{
const double T=static_cast<double>(J)/Count;
const double X=A.X+(B.X-A.X)*T,Y=A.Y+(B.Y-A.Y)*T,Z=A.Z+(B.Z-A.Z)*T;
++Stats.WalkSamples;
if(IsRock(static_cast<int32>(std::lround(X)),
static_cast<int32>(std::lround(Y)),static_cast<int32>(std::lround(Z))))
++Stats.WalkFailures;
++RouteSamples;++SegmentSamples;
const int32 VX=static_cast<int32>(std::lround(X));
const int32 VY=static_cast<int32>(std::lround(Y));
const int32 VZ=static_cast<int32>(std::lround(Z));
const bool bCritical=IsInsideCriticalAir(VX,VY,VZ);
if(bCritical){++RouteCritical;++SegmentCritical;}
if(IsRock(VX,VY,VZ))
{
++Stats.WalkFailures;++RouteFailures;++SegmentFailures;
if(RouteFailures==1)
{FirstFailure[0]=VX;FirstFailure[1]=VY;FirstFailure[2]=VZ;bFirstFailureCritical=bCritical;}
}
}
if(SegmentFailures)
std::fprintf(stderr,"[WalkabilitySegment] passage=%u segment=%d failures=%llu/%llu critical=%llu length=%.2f start=(%.2f,%.2f,%.2f) end=(%.2f,%.2f,%.2f)\n",
PassageIndex,I,static_cast<unsigned long long>(SegmentFailures),
static_cast<unsigned long long>(SegmentSamples),
static_cast<unsigned long long>(SegmentCritical),L,
A.X,A.Y,A.Z,B.X,B.Y,B.Z);
}
if(RouteFailures)
std::fprintf(stderr,"[WalkabilityRoute] passage=%u type=%u points=%d failures=%llu/%llu critical_centerline=%llu first_solid=(%d,%d,%d) critical=%u\n",
PassageIndex,static_cast<uint32>(P.PassageType),Points.Num(),
static_cast<unsigned long long>(RouteFailures),
static_cast<unsigned long long>(RouteSamples),
static_cast<unsigned long long>(RouteCritical),FirstFailure[0],
FirstFailure[1],FirstFailure[2],bFirstFailureCritical?1u:0u);
++PassageIndex;
}
if(Stats.WalkFailures)
std::fprintf(stderr,"[Walkability] FAIL %llu/%llu passage centerline samples are solid\n",
@@ -4689,6 +4774,7 @@ private:
else
std::printf("[Walkability] PASS %llu/%llu passage centerline samples remain open; native landing floors retained\n",
static_cast<unsigned long long>(Stats.WalkSamples),static_cast<unsigned long long>(Stats.WalkSamples));
return Stats.WalkFailures==0;
}
static double DetailAutocorrelation(EDetailVariant Variant)