feat(core): native Core caves generator, slice 1 -- spine + room, whole view 0.73 s (L54); research R23

Core.Caves.PrimitiveField v1, opt-in per strate (legacy TunnelNetwork assets stay legacy): solid host rock
and one smooth union of two elliptical spine tubes and an ellipsoid room, each intersected with its rising
floor by cubic smooth-max (W 2.4 m, measured fillet 0.70 m, no trailing floor cap). Bound in the L40 field
table and L42 capabilities, BVH feature candidates, isTileShapeFree aware, exact interval boxes (0 misses).
Capsule flood fill arrival -> departure passes (headroom >= 0.3 m, grade <= 15 deg); the generalized
whole-surface audit passes with its controls failing. Cold whole view 0.727 s, near ring 0.19 s. Graph
ordering/serialization changes are scoped to native caves (the flagged L47 demo is unchanged); verify
tiers write captures under --out only. fast/medium/full PASS, Clang and GCC.
Research R23 (deepened): per-tile LOD0 kernel speed plan.

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-25 04:10:19 +01:00
1 parent 20f6c98ec5
commit 9458217d34
24 files changed
+471136 -59

No files matched your search

+322 -1
View File
@@ -1089,6 +1089,320 @@ namespace StandaloneStreamBenchmark
return passed;
}
inline bool AuditNativeCavesField(const VoxelForgeCore::World& shape,
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::uint32_t voxelMicrometres)
{
using namespace VoxelForgeCore;
const double vm=std::max<std::uint32_t>(1,voxelMicrometres)/1e6;
const auto metres=[](const std::array<std::int64_t,3>& p)
{return std::array<double,3>{p[0]/1e6,p[1]/1e6,p[2]/1e6};};
const auto density=[&](double x,double y,double z)
{return static_cast<double>(shape.densityAt({x/vm,y/vm,z/vm}))*vm;};
const auto cave=std::find_if(layout.strates.begin(),layout.strates.end(),
[](const auto& s){return s.generatorKind=="Caves"
&&s.recipeId=="Core.Caves.PrimitiveField"&&s.recipeVersion==1
&&s.passageCapabilities.hasNativeCaveField;});
if(cave==layout.strates.end()||layout.cavernRooms.size()!=1||layout.cavernEdges.size()!=2)
{std::printf("[CavesAudit] recipe=%s rooms=%zu edges=%zu status=FAIL\n",
cave==layout.strates.end()?"missing":"present",layout.cavernRooms.size(),layout.cavernEdges.size());return false;}
std::vector<const ResolvedLayoutPassageV1*> mouths;
for(const auto& p:layout.passages)if(p.lowerStrateIndex==cave->strateIndex)mouths.push_back(&p);
std::sort(mouths.begin(),mouths.end(),[](auto* a,auto* b){return a->stableId<b->stableId;});
if(mouths.size()!=2)return false;
std::array<const ResolvedCavernEdgeV1*,2> edges{};
for(std::size_t i=0;i<2;++i)
{
const auto it=std::find_if(layout.cavernEdges.begin(),layout.cavernEdges.end(),
[&](const auto& e){return e.startAnchorStableId==mouths[i]->stableId+"/lower-mouth";});
if(it==layout.cavernEdges.end()||it->polylineMicrometres.size()<2
||it->strateIndex!=cave->strateIndex)return false;
edges[i]=&*it;
}
const auto& room=layout.cavernRooms.front();
const auto rc=metres(room.centerMicrometres);
const double floor=room.floorMicrometres/1e6;
struct Segment {std::array<double,3> a,b;};
std::vector<Segment> segments;
std::array<double,4> bounds={1e30,-1e30,1e30,-1e30};
double minFloor=floor,maxFloor=floor;
for(const auto* edge:edges)
{
for(std::size_t i=0;i<edge->polylineMicrometres.size();++i)
{
const auto p=metres(edge->polylineMicrometres[i]);
bounds[0]=std::min(bounds[0],p[0]);bounds[1]=std::max(bounds[1],p[0]);
bounds[2]=std::min(bounds[2],p[1]);bounds[3]=std::max(bounds[3],p[1]);
minFloor=std::min(minFloor,p[2]);maxFloor=std::max(maxFloor,p[2]);
if(i)segments.push_back({metres(edge->polylineMicrometres[i-1]),p});
}
}
const double roomX=room.halfExtentsMicrometres[0]/1e6;
const double roomY=room.halfExtentsMicrometres[1]/1e6;
bounds[0]=std::min(bounds[0],rc[0]-roomX);bounds[1]=std::max(bounds[1],rc[0]+roomX);
bounds[2]=std::min(bounds[2],rc[1]-roomY);bounds[3]=std::max(bounds[3],rc[1]+roomY);
constexpr double H=0.50,GradeLimit=0.2679491924311227;
bounds[0]-=4.0;bounds[1]+=4.0;bounds[2]-=4.0;bounds[3]+=4.0;
const int minX=static_cast<int>(std::floor(bounds[0]/H));
const int maxX=static_cast<int>(std::ceil(bounds[1]/H));
const int minY=static_cast<int>(std::floor(bounds[2]/H));
const int maxY=static_cast<int>(std::ceil(bounds[3]/H));
const int nx=maxX-minX+1,ny=maxY-minY+1;
if(nx<=0||ny<=0||static_cast<std::uint64_t>(nx)*ny>4000000)return false;
struct Node {int gx=0,gy=0;double x=0.0,y=0.0,floor=0.0;bool walkable=false;};
std::vector<Node> nodes;
std::vector<std::vector<int>> columns(static_cast<std::size_t>(nx)*ny);
const auto columnIndex=[&](int x,int y){return static_cast<std::size_t>(y-minY)*nx+(x-minX);};
const double bore=edges[0]->boreRadiusMicrometres/1e6;
std::uint64_t capsules=0,supports=0,floorQueries=0;
const auto testCapsule=[&](double x,double y,double surface)
{
bool clear=true;constexpr double radius=0.34,height=1.76;
for(double h=0.08;h<=height+1e-9;h+=0.18)
{
double cap=0;if(h<radius)cap=radius-h;else if(h>height-radius)cap=h-(height-radius);
const double radial=std::sqrt(std::max(0.0,radius*radius-cap*cap));
for(double oy=-radial;oy<=radial+1e-9;oy+=0.17)
for(double ox=-radial;ox<=radial+1e-9;ox+=0.17)
{if(ox*ox+oy*oy>radial*radial+1e-9)continue;++capsules;
if(density(x+ox,y+oy,surface+0.12+h)<0.025)clear=false;}
}
constexpr std::array<std::array<double,2>,5> head={{{0,0},{0.24,0},{-0.24,0},{0,0.24},{0,-0.24}}};
for(auto o:head)for(double h=0;h<=0.30+1e-9;h+=0.05)
if(density(x+o[0],y+o[1],surface+0.12+height+h)<0)clear=false;
constexpr std::array<std::array<double,2>,5> foot={{{0,0},{0.28,0},{-0.28,0},{0,0.28},{0,-0.28}}};
unsigned support=0;
for(auto o:foot){++supports;bool hit=false;for(double d=0.1;d<=0.55;d+=0.08)
if(density(x+o[0],y+o[1],surface-d)<0){hit=true;break;}if(hit)++support;}
return clear&&support>=3;
};
for(int gy=minY;gy<=maxY;++gy)for(int gx=minX;gx<=maxX;++gx)
{
const double x=gx*H,y=gy*H;double nearest=1e30;
for(const auto& s:segments)
{
const double dx=s.b[0]-s.a[0],dy=s.b[1]-s.a[1],l2=dx*dx+dy*dy;
const double t=l2>0?std::clamp(((x-s.a[0])*dx+(y-s.a[1])*dy)/l2,0.0,1.0):0.0;
nearest=std::min(nearest,std::hypot(x-(s.a[0]+t*dx),y-(s.a[1]+t*dy)));
}
const bool roomXY=std::abs(x-rc[0])<=roomX+0.5&&std::abs(y-rc[1])<=roomY+0.5;
if(nearest>bore+0.9&&!roomXY)continue;
double priorZ=minFloor-0.75,priorDensity=density(x,y,priorZ);
for(double z=priorZ+0.05;z<=maxFloor+0.75+1e-9;z+=0.05)
{
const double value=density(x,y,z);++floorQueries;
if(priorDensity<0.0&&value>=0.0)
{
double lo=z-0.05,hi=z;
for(int q=0;q<12;++q){const double mid=(lo+hi)*0.5;if(density(x,y,mid)<0.0)lo=mid;else hi=mid;}
const double surface=(lo+hi)*0.5;
Node node;node.gx=gx;node.gy=gy;node.x=x;node.y=y;node.floor=surface;
node.walkable=testCapsule(x,y,surface);
const int id=static_cast<int>(nodes.size());nodes.push_back(node);
columns[columnIndex(gx,gy)].push_back(id);
}
priorZ=z;priorDensity=value;
}
}
const auto mouth=[&](const ResolvedLayoutPassageV1& p)
{return std::array<double,3>{p.lowerMouthSubvoxels[0]*vm/WorldAssemblySubvoxelsPerVoxel,
p.lowerMouthSubvoxels[1]*vm/WorldAssemblySubvoxelsPerVoxel,
p.lowerMouthSubvoxels[2]*vm/WorldAssemblySubvoxelsPerVoxel};};
const auto start=mouth(*mouths[0]),end=mouth(*mouths[1]);
std::vector<int> starts,goals;
double nearestStart=std::numeric_limits<double>::infinity(),nearestEnd=nearestStart;
int nearestStartNode=-1,nearestEndNode=-1;
for(std::size_t id=0;id<nodes.size();++id)
{
const auto& n=nodes[id];if(!n.walkable)continue;
const double ds=std::hypot(std::hypot(n.x-start[0],n.y-start[1]),n.floor-start[2]);
const double de=std::hypot(std::hypot(n.x-end[0],n.y-end[1]),n.floor-end[2]);
if(ds<nearestStart){nearestStart=ds;nearestStartNode=static_cast<int>(id);}
if(de<nearestEnd){nearestEnd=de;nearestEndNode=static_cast<int>(id);}
if(ds<=2.0)starts.push_back(static_cast<int>(id));
if(de<=2.0)goals.push_back(static_cast<int>(id));
}
constexpr std::array<std::array<int,2>,8> dirs={{{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}}};
const auto flood=[&](bool enforceGrade,std::uint64_t* rejected)
{
std::vector<int> parent(nodes.size(),-1);std::queue<int> open;
for(int id:starts){parent[static_cast<std::size_t>(id)]=-2;open.push(id);}
while(!open.empty())
{
const int current=open.front();open.pop();const auto& a=nodes[static_cast<std::size_t>(current)];
for(const auto& d:dirs)
{
const int gx=a.gx+d[0],gy=a.gy+d[1];if(gx<minX||gx>maxX||gy<minY||gy>maxY)continue;
const double run=H*std::hypot(d[0],d[1]);
for(const int next:columns[columnIndex(gx,gy)])
{
if(parent[static_cast<std::size_t>(next)]!=-1)continue;
const auto& b=nodes[static_cast<std::size_t>(next)];if(!b.walkable)continue;
if(enforceGrade&&std::abs(b.floor-a.floor)/run>GradeLimit)
{if(rejected)++*rejected;continue;}
parent[static_cast<std::size_t>(next)]=current;open.push(next);
}
}
}
return parent;
};
auto rawParent=flood(false,nullptr);std::uint64_t gradeRejected=0;
auto parent=flood(true,&gradeRejected);
const auto reachableGoal=[&](const std::vector<int>& parents)
{
int selected=-1;double distance=std::numeric_limits<double>::infinity();
for(int candidate:goals)if(parents[static_cast<std::size_t>(candidate)]!=-1)
{
const auto& n=nodes[static_cast<std::size_t>(candidate)];
const double d=std::hypot(std::hypot(n.x-end[0],n.y-end[1]),n.floor-end[2]);
if(d<distance){distance=d;selected=candidate;}
}
return std::pair<int,double>{selected,distance};
};
const auto [rawGoal,rawGoalD]=reachableGoal(rawParent);
const auto [goal,goalD]=reachableGoal(parent);
const bool rawReached=rawGoal>=0;
std::size_t gradedReachable=0,roomWalkable=0,roomReachable=0;
double reachableFloorMin=std::numeric_limits<double>::infinity();
double reachableFloorMax=-std::numeric_limits<double>::infinity();
for(std::size_t id=0;id<nodes.size();++id)
{
const auto& n=nodes[id];if(!n.walkable)continue;
const bool insideRoom=std::abs(n.x-rc[0])<=roomX+0.5
&&std::abs(n.y-rc[1])<=roomY+0.5;
if(insideRoom)++roomWalkable;
if(parent[id]!=-1)
{
++gradedReachable;reachableFloorMin=std::min(reachableFloorMin,n.floor);
reachableFloorMax=std::max(reachableFloorMax,n.floor);
if(insideRoom)++roomReachable;
}
}
std::printf("[CavesFloodFillGraph] graded_reachable=%zu room_walkable=%zu room_reachable=%zu reachable_floor_m=%.3f..%.3f arrival_floor_m=%.3f departure_floor_m=%.3f\n",
gradedReachable,roomWalkable,roomReachable,reachableFloorMin,reachableFloorMax,
nearestStartNode>=0?nodes[static_cast<std::size_t>(nearestStartNode)].floor:0.0,
nearestEndNode>=0?nodes[static_cast<std::size_t>(nearestEndNode)].floor:0.0);
for(std::size_t edgeIndex=0;edgeIndex<edges.size();++edgeIndex)
{
const auto& polyline=edges[edgeIndex]->polylineMicrometres;
std::vector<std::array<double,3>> linePoints;linePoints.reserve(polyline.size());
std::vector<double> cumulativeLength(polyline.size(),0.0);
for(const auto& p:polyline)linePoints.push_back(metres(p));
for(std::size_t i=1;i<linePoints.size();++i)
cumulativeLength[i]=cumulativeLength[i-1]+std::hypot(
std::hypot(linePoints[i][0]-linePoints[i-1][0],
linePoints[i][1]-linePoints[i-1][1]),
linePoints[i][2]-linePoints[i-1][2]);
std::printf("[CavesFloodFillProgress] edge=%s",
edges[edgeIndex]->stableId.c_str());
for(int sample=0;sample<=10;++sample)
{
const double distance=cumulativeLength.back()*sample/10.0;
const auto upper=std::lower_bound(cumulativeLength.begin(),
cumulativeLength.end(),distance);
const std::size_t upperIndex=std::clamp<std::size_t>(
static_cast<std::size_t>(upper-cumulativeLength.begin()),1,
cumulativeLength.size()-1);
const std::size_t lowerIndex=upperIndex-1;
const double span=cumulativeLength[upperIndex]-cumulativeLength[lowerIndex];
const double t=span>0.0?(distance-cumulativeLength[lowerIndex])/span:0.0;
const std::array<double,3> target={
linePoints[lowerIndex][0]+(linePoints[upperIndex][0]-linePoints[lowerIndex][0])*t,
linePoints[lowerIndex][1]+(linePoints[upperIndex][1]-linePoints[lowerIndex][1])*t,
linePoints[lowerIndex][2]+(linePoints[upperIndex][2]-linePoints[lowerIndex][2])*t};
double nearestWalkable=std::numeric_limits<double>::infinity();
double nearestReachable=nearestWalkable;
int nearestWalkableNode=-1,nearestReachableNode=-1;
for(std::size_t id=0;id<nodes.size();++id)
{
const auto& n=nodes[id];if(!n.walkable)continue;
const double d=std::hypot(std::hypot(n.x-target[0],n.y-target[1]),
n.floor-target[2]);
if(d<nearestWalkable){nearestWalkable=d;nearestWalkableNode=static_cast<int>(id);}
if(parent[id]!=-1&&d<nearestReachable)
{nearestReachable=d;nearestReachableNode=static_cast<int>(id);}
}
const double fieldFloor=density(target[0],target[1],target[2]);
const double fieldAbove=density(target[0],target[1],target[2]+0.3);
const double walkFloor=nearestWalkableNode>=0
?nodes[static_cast<std::size_t>(nearestWalkableNode)].floor:0.0;
const double reachFloor=nearestReachableNode>=0
?nodes[static_cast<std::size_t>(nearestReachableNode)].floor:0.0;
std::printf(" t%d=%.2f/%.2f(f%.2f/r%.2f):%.2f/%.2f",sample,
nearestWalkable,nearestReachable,walkFloor,reachFloor,fieldFloor,fieldAbove);
}
std::printf("\n");
}
double rawMaximumGrade=0.0;std::size_t rawPathCells=0;
if(rawReached)for(int p=rawGoal;p>=0;){++rawPathCells;const int prior=rawParent[static_cast<std::size_t>(p)];if(prior==-2)break;
const auto& a=nodes[static_cast<std::size_t>(p)];const auto& b=nodes[static_cast<std::size_t>(prior)];
const double run=H*std::hypot(a.gx-b.gx,a.gy-b.gy);rawMaximumGrade=std::max(rawMaximumGrade,
std::atan2(std::abs(a.floor-b.floor),run)*180.0/3.14159265358979323846);p=prior;}
const bool reached=goal>=0;std::vector<int> path;
if(reached){for(int p=goal;p>=0;){path.push_back(p);const int prior=parent[static_cast<std::size_t>(p)];if(prior==-2)break;p=prior;}std::reverse(path.begin(),path.end());}
double headroom=std::numeric_limits<double>::infinity();
for(int id:path){const auto& n=nodes[static_cast<std::size_t>(id)];double h=0.30;
for(double rise=0;rise<=0.30+1e-9;rise+=0.05)if(density(n.x,n.y,n.floor+0.12+1.76+rise)<0){h=std::max(0.0,rise-0.05);break;}
headroom=std::min(headroom,h);}
std::printf("[CavesFloodFillReachability] ungraded_connected=%s ungraded_path_cells=%zu ungraded_max_grade_deg=%.3f grade_rejected_edges=%llu\n",
rawReached?"yes":"no",rawPathCells,rawMaximumGrade,static_cast<unsigned long long>(gradeRejected));
std::printf("[CavesFloodFillEndpoints] start_seeds=%zu departure_candidates=%zu raw_goal=%s graded_goal=%s nearest_arrival_m=%.3f nearest_departure_m=%.3f selected_departure_m=%.3f raw_selected_departure_m=%.3f\n",
starts.size(),goals.size(),rawGoal>=0?"yes":"no",goal>=0?"yes":"no",nearestStart,nearestEnd,goalD,rawGoalD);
// Fit the actual zero contour in the wall-foot cross section; the local quadratic
// curvature is the fillet radius, W/(2 sqrt(2)) for this cubic smooth-max.
const auto& edge=*edges[0];const auto p0=metres(edge.polylineMicrometres[edge.polylineMicrometres.size()/2-1]);
const auto p1=metres(edge.polylineMicrometres[edge.polylineMicrometres.size()/2]);
const double tx=p1[0]-p0[0],ty=p1[1]-p0[1],tl=std::hypot(tx,ty),width=edge.smoothWidthMicrometres/1e6;
double measured=0;
const double horizontal=std::hypot(tx,ty),grade=horizontal>1e-9?(p1[2]-p0[2])/horizontal:0.0;
if(tl>1e-9&&horizontal>1e-9){const double nx=-ty/horizontal,ny=tx/horizontal;
const double upX=-grade*tx/horizontal/std::sqrt(1.0+grade*grade);
const double upY=-grade*ty/horizontal/std::sqrt(1.0+grade*grade);
const double upZ=1.0/std::sqrt(1.0+grade*grade);
const double cx=(p0[0]+p1[0])*0.5,cy=(p0[1]+p1[1])*0.5,cz=(p0[2]+p1[2])*0.5,r=edge.boreRadiusMicrometres/1e6;
const auto contour=[&](double v){double lo=0,hi=width;const auto f=[&](double u){const double in=(u+v)/std::sqrt(2.0),rise=(u-v)/std::sqrt(2.0);return density(cx+nx*(r-in)+upX*rise,cy+ny*(r-in)+upY*rise,cz+upZ*rise);};
if(!(f(lo)<0&&f(hi)>=0))return std::numeric_limits<double>::quiet_NaN();for(int i=0;i<28;++i){double m=(lo+hi)*0.5;if(f(m)<0)lo=m;else hi=m;}return (lo+hi)*0.5;};
constexpr double delta=0.02;const double a=contour(-delta),b=contour(0),c=contour(delta);if(std::isfinite(a)&&std::isfinite(b)&&std::isfinite(c))measured=delta*delta/(a+c-2*b);}
const double expected=width/(2.0*std::sqrt(2.0));
std::printf("[CavesFilletAudit] smooth_max=cubic width_m=%.6f measured_composed_radius_m=%.6f isolated_formula_radius_m=%.6f status=%s\n",
width,measured,expected,measured>=0.5?"PASS":"FAIL");
struct Check {const char* name;std::array<double,3> p;};
const auto lineStart=metres(edge.polylineMicrometres.front());
const auto lineEnd=metres(edge.polylineMicrometres.back());
const std::array<double,3> mid={
(lineStart[0]+lineEnd[0])*0.5,(lineStart[1]+lineEnd[1])*0.5,
(lineStart[2]+lineEnd[2])*0.5};
const std::array<Check,6> checks={{{"host-rock",{bounds[1]+1.5,rc[1],floor+2}},
{"spine-air",{mid[0],mid[1],mid[2]+1.6}},
{"room-air-center",{rc[0],rc[1],floor+2}},
{"room-air-east",{rc[0]+2.0,rc[1],floor+2}},
{"room-air-north",{rc[0],rc[1]+2.0,floor+2}},
{"wall-foot",{mid[0],mid[1]+bore-0.25,mid[2]+0.25}}}};
unsigned air=0,solid=0,miss=0,wallMixed=0;bool falseAir=false,falseSolid=false;
for(const auto& c:checks){const Vec3i o{static_cast<int>(std::llround(c.p[0]/vm)),static_cast<int>(std::llround(c.p[1]/vm)),static_cast<int>(std::llround(c.p[2]/vm))};
const auto verdict=shape.classifyBox(o,1,1);bool allAir=true,allSolid=true;std::uint64_t samples=0;
for(int z=0;z<=6;++z)for(int y=0;y<=6;++y)for(int x=0;x<=6;++x){const Vec3d q{o.x-1.0+0.5*x,o.y-1.0+0.5*y,o.z-1.0+0.5*z};const double d=density(q.x*vm,q.y*vm,q.z*vm);allAir&=d>=0;allSolid&=d<0;++samples;}
const bool bad=(verdict==TileClass::allAir&&!allAir)||(verdict==TileClass::allSolid&&!allSolid);miss+=bad;air+=verdict==TileClass::allAir;solid+=verdict==TileClass::allSolid;
if(std::string_view(c.name)=="host-rock")falseAir=verdict==TileClass::allSolid&&allSolid;
if(std::string_view(c.name).starts_with("room-air"))
falseSolid|=verdict==TileClass::allAir&&allAir;
if(std::string_view(c.name)=="wall-foot")wallMixed=verdict==TileClass::mixed;
std::printf("[CavesIntervalBox] id=%s proof=%s brute_air=%s brute_solid=%s samples=%llu miss=%s\n",c.name,
verdict==TileClass::allAir?"all-air":verdict==TileClass::allSolid?"all-solid":"mixed",allAir?"yes":"no",allSolid?"yes":"no",static_cast<unsigned long long>(samples),bad?"yes":"no");}
const bool intervalOk=air>=2&&solid>=1&&miss==0&&wallMixed&&falseAir&&falseSolid;
const bool reachable=reached&&path.size()>=2&&headroom>=0.30;
std::printf("[CavesCapsuleFloodFill] arrival=%s departure=%s grid_m=%.2f candidates=%zu walkable=%zu path_cells=%zu floor_queries=%llu capsule_samples=%llu support_samples=%llu max_grade_deg=15.000 min_headroom_m=%.3f required_headroom_m=0.300 reachable=%s status=%s\n",
mouths[0]->stableId.c_str(),mouths[1]->stableId.c_str(),H,nodes.size(),
std::count_if(nodes.begin(),nodes.end(),[](const auto& n){return n.walkable;}),path.size(),
static_cast<unsigned long long>(floorQueries),static_cast<unsigned long long>(capsules),
static_cast<unsigned long long>(supports),headroom,
reachable?"yes":"no",reachable?"PASS":"FAIL");
std::printf("[CavesIntervalOracle] boxes=%zu all_air=%u all_solid=%u misses=%u false_all_air=%s false_all_solid=%s wall_foot=%s status=%s\n",
checks.size(),air,solid,miss,falseAir?"REJECTED":"FAILED",falseSolid?"REJECTED":"FAILED",wallMixed?"mixed":"not-mixed",intervalOk?"PASS":"FAIL");
return reachable&&intervalOk&&measured>=0.5;
}
inline int RunPipelinedStreamingBenchmark(std::shared_ptr<FStandaloneOwnerWorld> OwnerWorld,
const VoxelForgeCore::ResolvedWorldSnapshot& CoreSnapshot,
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
@@ -1108,7 +1422,7 @@ namespace StandaloneStreamBenchmark
bool bAuditStrateTransitions=false,
const VoxelForgeCore::ResolvedLayoutSnapshotV3* ResolvedLayout=nullptr,
bool bPrintTileHashes=false,bool bAuditLandmarkReachability=false,
bool bAuditOreVeinYield=false)
bool bAuditOreVeinYield=false,bool bAuditNativeCaves=false)
{
using Clock=std::chrono::steady_clock;
FStandaloneOwnerWorld& World=*OwnerWorld;
@@ -1293,6 +1607,13 @@ namespace StandaloneStreamBenchmark
Streamer.Stop();
std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;
}
if(bAuditNativeCaves)
{
Streamer.Stop();
if(!ResolvedLayout||!CoreSnapshot.resolvedLayout())
{std::fprintf(stderr,"[CavesAudit] missing resolved layout\n");return 2;}
return AuditNativeCavesField(Shape,*ResolvedLayout,World.CoreVoxelSizeMicrons)?0:14;
}
if(bAuditOreVeinYield)
{
if(!ResolvedLayout||ResolvedLayout->oreVeins.empty())