wip: Tunnel Network cave planner on Core caves (L134, route support still failing)

Seed + 21/47 legacy fields -> 30 compound rooms (72 primitives), 3 branches, destination chamber; mouths fixed;
8.67 deg max grade. Passage composer no longer skips native cave layers (incoming passage 21,425/21,425 open).
Open: route-support audit has 380 unsupported samples near the arrival mouth. Not for merge.

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-31 11:42:04 +01:00
1 parent 0065ee68e6
commit 75c6ad251d
9 files changed
+1008 -47

No files matched your search

+29 -8
View File
@@ -74,6 +74,7 @@ namespace
bool DensityDispatchReport=false;
bool CavesRecipe=false;
bool LatticeRecipe=false;
bool LegacyTunnelNetwork=false;
int LatticeStrateIndex=-1;
bool AuditHeightfieldProfile=false;
bool LandmarkChamberDemo=false;
@@ -1434,6 +1435,7 @@ namespace
" --core-snapshot=FILE (benchmark the exact resolved Core snapshot from Unreal)\n"
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
" --seed=N --recipe=surface-world|floating-islands|caves|lattice\n"
" --legacy-tunnel-network (keep TN on its compiled-analytic comparison path)\n"
" --native-owner-fi (build the existing owner FI plan; leave other strates intact)\n"
" --density-dispatch-report (print bound path per strate and tile hashes)\n"
" --lattice-strate=N (opt one Maze or VerticalShafts strate into Core.Lattice)\n"
@@ -1497,6 +1499,7 @@ namespace
else if(arg=="--native-owner-fi")options.NativeOwnerFloatingIslandsRecipe=true;
else if(arg=="--density-dispatch-report")
{options.DensityDispatchReport=true;options.StreamHash=true;options.StreamHashTiles=true;}
else if(arg=="--legacy-tunnel-network")options.LegacyTunnelNetwork=true;
else if(arg=="--audit-landmark-reachability")options.AuditLandmarkReachability=true;
else if(arg=="--audit-native-caves")options.AuditNativeCaves=true;
else if(arg=="--audit-native-lattice")options.AuditNativeLattice=true;
@@ -1737,7 +1740,8 @@ int main(int argc,char** argv)
options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel,
options.MaxLevel,options.StreamHash?"on":"off",
options.StreamHashAllowUniformSkips?"on":"off",options.ProfileColumnOverlap?"on":"off",
options.ForceGenerate?"on":"off",options.CavesRecipe?"caves"
options.ForceGenerate?"on":"off",options.LegacyTunnelNetwork?"legacy-tunnel-network"
:options.CavesRecipe?"caves"
:options.LatticeRecipe?"lattice"
:options.FloatingIslandsRecipe?"floating-islands":"surface-world",
options.LatticeStrateIndex,
@@ -1858,7 +1862,9 @@ int main(int argc,char** argv)
}
VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout;
{
const std::string variant=options.LatticeRecipe
const std::string variant=options.LegacyTunnelNetwork
?"legacy-tunnel-network"
:options.LatticeRecipe
?"lattice/"+std::to_string(options.LatticeStrateIndex)
:options.CavesRecipe?"caves"
:(options.FloatingIslandsRecipe||options.NativeOwnerFloatingIslandsRecipe)
@@ -1871,6 +1877,11 @@ int main(int argc,char** argv)
*ownerWorld,worldAssembly,variant,resolvedLayout,error);
if(!layoutResolved)
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;}
if(useExportedCorePlan&&!options.NativeOwnerFloatingIslandsRecipe
&&!options.LegacyTunnelNetwork&&!options.CavesRecipe
&&!StandaloneWorldAssemblyPrototype::PlanOwnerTunnelNetworkCaves(
*ownerWorld,resolvedLayout,error))
{std::fprintf(stderr,"Imported owner Tunnel Network cave planning failed: %s\n",error.c_str());return 2;}
if(useExportedCorePlan&&!options.NativeOwnerFloatingIslandsRecipe
&&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner(
*ownerWorld,worldAssembly,resolvedLayout,error))
@@ -1953,8 +1964,11 @@ int main(int argc,char** argv)
{
std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms;
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges;
if(!MakeNativeCavesSpine(resolvedLayout,rooms,edges,error)
||!VoxelForgeCore::ResolveResolvedCavernGraphV1(
if(!MakeNativeCavesSpine(resolvedLayout,rooms,edges,error))
{std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;}
rooms.insert(rooms.end(),resolvedLayout.cavernRooms.begin(),resolvedLayout.cavernRooms.end());
edges.insert(edges.end(),resolvedLayout.cavernEdges.begin(),resolvedLayout.cavernEdges.end());
if(!VoxelForgeCore::ResolveResolvedCavernGraphV1(
resolvedLayout,seed,rooms,edges,error))
{std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;}
}
@@ -1965,8 +1979,11 @@ int main(int argc,char** argv)
std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms;
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges;
if(!MakeNativeLatticeSpine(resolvedLayout,seed,options.LatticeStrateIndex,
options.LatticeOrphanControl,rooms,edges,error)
||!VoxelForgeCore::ResolveResolvedCavernGraphV1(
options.LatticeOrphanControl,rooms,edges,error))
{std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;}
rooms.insert(rooms.end(),resolvedLayout.cavernRooms.begin(),resolvedLayout.cavernRooms.end());
edges.insert(edges.end(),resolvedLayout.cavernEdges.begin(),resolvedLayout.cavernEdges.end());
if(!VoxelForgeCore::ResolveResolvedCavernGraphV1(
resolvedLayout,seed,rooms,edges,error))
{std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;}
}
@@ -2087,12 +2104,16 @@ int main(int argc,char** argv)
const bool hasNativeLatticeRecipe=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
[](const auto& stratum){return VoxelForgeCore::IsNativeLatticeFieldStratum(stratum)
&&stratum.passageCapabilities.hasNativeLatticeField;});
const auto actualRecipe=hasNativeLatticeRecipe?std::string_view("lattice")
:hasNativeCavesRecipe?std::string_view("caves")
const auto actualRecipe=requestedRecipe=="lattice"&&hasNativeLatticeRecipe
?std::string_view("lattice")
:requestedRecipe=="caves"&&hasNativeCavesRecipe
?std::string_view("caves")
:requestedRecipe=="surface-world"&&hasSurfaceWorldPlan
?std::string_view("surface-world")
:requestedRecipe=="floating-islands"&&hasFloatingIslands
?std::string_view("floating-islands")
:hasNativeLatticeRecipe?std::string_view("lattice")
:hasNativeCavesRecipe?std::string_view("caves")
:hasFloatingIslands?std::string_view("floating-islands")
:hasSurfaceWorldPlan?std::string_view("surface-world")
:std::string_view("missing");
+265 -26
View File
@@ -1595,6 +1595,205 @@ namespace StandaloneStreamBenchmark
return passed;
}
inline bool AuditNativeTunnelNetworkField(const VoxelForgeCore::World& shape,
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
std::uint32_t voxelMicrometres)
{
using namespace VoxelForgeCore;
constexpr double Pi=3.14159265358979323846;
const double vm=std::max<std::uint32_t>(1,voxelMicrometres)/1.0e6;
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=="TunnelNetwork"
&&IsNativeCaveFieldStratum(s)&&s.passageCapabilities.hasNativeCaveField;});
if(cave==layout.strates.end()||layout.cavernGraphVocabularyVersion!=1)
{std::printf("[TunnelNetworkCavernAudit] recipe=%s graph=%u status=FAIL\n",
cave==layout.strates.end()?"missing":"present",
layout.cavernGraphVocabularyVersion);return false;}
const ResolvedLayoutPassageV1* arrival=nullptr;
const ResolvedLayoutPassageV1* departure=nullptr;
for(const auto& passage:layout.passages)
{
if(passage.lowerStrateIndex==cave->strateIndex
&&passage.upperStrateIndex<cave->strateIndex)arrival=&passage;
if(passage.upperStrateIndex==cave->strateIndex
&&passage.lowerStrateIndex>cave->strateIndex)departure=&passage;
}
std::vector<const ResolvedCavernRoomV1*> rooms;
std::vector<const ResolvedCavernEdgeV1*> routeEdges;
for(const auto& room:layout.cavernRooms)
if(room.strateIndex==cave->strateIndex)rooms.push_back(&room);
for(const auto& edge:layout.cavernEdges)
if(edge.strateIndex==cave->strateIndex
&&edge.stableId.find("/route-edge-")!=std::string::npos)
routeEdges.push_back(&edge);
const auto routeIndex=[](const ResolvedCavernEdgeV1* edge)
{
const auto marker=edge->stableId.rfind("/route-edge-");
return marker==std::string::npos?std::uint64_t{0}
:static_cast<std::uint64_t>(std::stoull(edge->stableId.substr(marker+12)));
};
std::sort(routeEdges.begin(),routeEdges.end(),[&](const auto* a,const auto* b)
{return routeIndex(a)<routeIndex(b);});
std::unordered_map<std::string,std::size_t> compoundCounts;
std::unordered_map<std::string,double> compoundRadii;
double largestRoomRadius=0.0,largestOtherRoomRadius=0.0;
std::string largestRoomGroup;
for(const auto* room:rooms)
{
const auto marker=room->stableId.rfind("/primitive-");
const std::string group=marker==std::string::npos
?room->stableId:room->stableId.substr(0,marker);
++compoundCounts[group];
const double radius=std::max(room->halfExtentsMicrometres[0],
room->halfExtentsMicrometres[1])/1.0e6;
compoundRadii[group]=std::max(compoundRadii[group],radius);
if(radius>largestRoomRadius)
{largestRoomRadius=radius;largestRoomGroup=group;}
}
for(const auto& [group,radius]:compoundRadii)
if(group!=largestRoomGroup)
largestOtherRoomRadius=std::max(largestOtherRoomRadius,radius);
const bool compoundRooms=!compoundCounts.empty()
&&std::all_of(compoundCounts.begin(),compoundCounts.end(),
[](const auto& row){return row.second>=2&&row.second<=4;});
const bool destination=largestOtherRoomRadius>0.0
&&largestRoomRadius>largestOtherRoomRadius*1.10;
std::vector<const ResolvedLayoutAnchorV2*> routeAnchors;
for(const auto& anchor:cave->anchors)
if(anchor.kind=="route_point"&&anchor.values.size()>=3)
routeAnchors.push_back(&anchor);
double maximumGrade=0.0;
bool gradeOk=routeAnchors.size()>=2;
for(std::size_t i=1;i<routeAnchors.size();++i)
{
const auto& a=routeAnchors[i-1]->values;
const auto& b=routeAnchors[i]->values;
const double run=std::hypot(static_cast<double>(b[0]-a[0]),
static_cast<double>(b[1]-a[1]));
const double rise=std::abs(static_cast<double>(b[2]-a[2]));
if(run<=0.0){gradeOk=false;continue;}
const double grade=std::atan2(rise,run)*180.0/Pi;
maximumGrade=std::max(maximumGrade,grade);
gradeOk=gradeOk&&grade<=15.0+1.0e-6;
}
bool mouthBinding=arrival&&departure&&routeAnchors.size()>=2;
if(mouthBinding)
{
const auto qOffset=static_cast<std::int64_t>(std::llround(
250000.0*WorldAssemblySubvoxelsPerVoxel/layout.coreVoxelSizeMicrometres));
const auto& start=routeAnchors.front()->values;
const auto& end=routeAnchors.back()->values;
mouthBinding=start[0]==arrival->lowerMouthSubvoxels[0]
&&start[1]==arrival->lowerMouthSubvoxels[1]
&&start[2]==arrival->lowerMouthSubvoxels[2]+qOffset
&&end[0]==departure->upperMouthSubvoxels[0]
&&end[1]==departure->upperMouthSubvoxels[1]
&&end[2]==departure->upperMouthSubvoxels[2]+qOffset;
}
std::uint64_t airSamples=0,floorSamples=0,shellSamples=0;
bool centerAir=true,floorSolid=true,shellSolid=true;
for(const auto* edge:routeEdges)
{
const auto& p=edge->controlPointsMicrometres;
const double x=(p[0][0]+3.0*p[1][0]+3.0*p[2][0]+p[3][0])/8.0e6;
const double y=(p[0][1]+3.0*p[1][1]+3.0*p[2][1]+p[3][1])/8.0e6;
const double floor=(p[0][2]+3.0*p[1][2]+3.0*p[2][2]+p[3][2])/8.0e6;
const double bore=edge->boreRadiusMicrometres/1.0e6;
if(density(x,y,floor+1.0)<=0.0)centerAir=false;
++airSamples;
if(density(x,y,floor-0.10)>=0.0)floorSolid=false;
++floorSamples;
const double dx=(p[3][0]-p[0][0])/1.0e6;
const double dy=(p[3][1]-p[0][1])/1.0e6;
const double length=std::hypot(dx,dy);
if(length<=1.0e-6){shellSolid=false;continue;}
const double nx=-dy/length,ny=dx/length;
if(density(x+nx*(bore+0.90),y+ny*(bore+0.90),floor+2.0)>=0.0
||density(x-nx*(bore+0.90),y-ny*(bore+0.90),floor+2.0)>=0.0)
shellSolid=false;
shellSamples+=2;
}
const ResolvedCavernEdgeV1* rippleEdge=nullptr;
double longest=0.0;
for(const auto* edge:routeEdges)
{
const auto& p=edge->controlPointsMicrometres;
const double length=std::hypot(static_cast<double>(p[3][0]-p[0][0]),
static_cast<double>(p[3][1]-p[0][1]));
if(length>longest){longest=length;rippleEdge=edge;}
}
std::vector<double> rippleRadii;
if(rippleEdge)
{
const auto& p=rippleEdge->controlPointsMicrometres;
const double radius=rippleEdge->boreRadiusMicrometres/1.0e6;
constexpr std::array<double,7> stations{{0.18,0.29,0.40,0.51,0.62,0.73,0.84}};
for(double t:stations)
{
const auto bezier=[&](double u,std::size_t axis)
{
const double one=1.0-u;
return (one*one*one*p[0][axis]+3.0*one*one*u*p[1][axis]
+3.0*one*u*u*p[2][axis]+u*u*u*p[3][axis])/1.0e6;
};
const double x=bezier(t,0),y=bezier(t,1),floor=bezier(t,2);
const double tx=bezier(std::min(1.0,t+0.01),0)
-bezier(std::max(0.0,t-0.01),0);
const double ty=bezier(std::min(1.0,t+0.01),1)
-bezier(std::max(0.0,t-0.01),1);
const double tangent=std::hypot(tx,ty);
if(tangent<=1.0e-6)continue;
for(const double side:{-1.0,1.0})
{
double prior=0.20;
double priorDensity=density(x,y,floor+2.0);
bool found=false;
for(double radial=0.25;radial<=radius+1.8;radial+=0.05)
{
const double sampleX=x-side*ty/tangent*radial;
const double sampleY=y+side*tx/tangent*radial;
const double value=density(sampleX,sampleY,floor+2.0);
if(priorDensity>=0.0&&value<0.0)
{
const double fraction=priorDensity/(priorDensity-value);
rippleRadii.push_back(prior+(radial-prior)*fraction);
found=true;break;
}
prior=radial;priorDensity=value;
}
if(!found)continue;
}
}
}
double ripplePeakToPeak=0.0;
if(!rippleRadii.empty())
{
const auto limits=std::minmax_element(rippleRadii.begin(),rippleRadii.end());
ripplePeakToPeak=*limits.second-*limits.first;
}
const bool ripple=rippleRadii.size()>=8&&ripplePeakToPeak>=0.02
&&ripplePeakToPeak<=1.25;
std::printf("[TunnelNetworkCavernAudit] strate=%d recipe=%s rooms=%zu compound_groups=%zu destination_radius_m=%.3f route_edges=%zu route_points=%zu mouth_binding=%s max_grade_deg=%.3f grade_ok=%s center_air=%llu/%llu floor_solid=%llu/%llu shell_solid=%llu/%llu ripple_samples=%zu ripple_peak_to_peak_m=%.4f status=%s\n",
cave->strateIndex,cave->recipeId.c_str(),rooms.size(),compoundCounts.size(),
largestRoomRadius,routeEdges.size(),routeAnchors.size(),mouthBinding?"PASS":"FAIL",
maximumGrade,gradeOk?"PASS":"FAIL",
static_cast<unsigned long long>(airSamples),static_cast<unsigned long long>(airSamples),
static_cast<unsigned long long>(floorSamples),static_cast<unsigned long long>(floorSamples),
static_cast<unsigned long long>(shellSamples),static_cast<unsigned long long>(shellSamples),
rippleRadii.size(),ripplePeakToPeak,
arrival&&departure&&routeEdges.size()>=2&&!rooms.empty()&&compoundRooms
&&destination&&mouthBinding&&gradeOk&&centerAir&&floorSolid&&shellSolid
&&ripple?"PASS":"FAIL");
return arrival&&departure&&routeEdges.size()>=2&&!rooms.empty()&&compoundRooms
&&destination&&mouthBinding&&gradeOk&&centerAir&&floorSolid&&shellSolid&&ripple;
}
inline bool AuditNativeCavesField(const VoxelForgeCore::World& shape,
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::uint32_t voxelMicrometres)
{
@@ -1605,9 +1804,13 @@ namespace StandaloneStreamBenchmark
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
[](const auto& s){return IsNativeCaveFieldStratum(s)
&&s.passageCapabilities.hasNativeCaveField;});
if(cave!=layout.strates.end()&&cave->generatorKind=="TunnelNetwork")
return AuditNativeTunnelNetworkField(shape,layout,voxelMicrometres);
if(cave==layout.strates.end()||cave->generatorKind!="Caves")
{std::printf("[CavesAudit] recipe=missing rooms=%zu edges=%zu status=FAIL\n",
layout.cavernRooms.size(),layout.cavernEdges.size());return false;}
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;}
@@ -4796,6 +4999,7 @@ namespace StandaloneStreamBenchmark
}
}
std::uint64_t routePoints=0,routePasses=0,routeWalkPasses=0,routeBindings=0,routeBindingFailures=0;
std::uint64_t tunnelNetworkRoutes=0,tunnelNetworkBlockedControls=0;
for(const auto& stratum:ResolvedLayout->strates)
{
std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> anchors;
@@ -4859,36 +5063,56 @@ namespace StandaloneStreamBenchmark
supportStepMetres,supportMaximumMetres,minimumSupportProbes,
supportProbeRadiusMetres);
bool binding=true;
const auto equalPosition=[](const auto& a,const auto& b)
{return a[0]==b[0]&&a[1]==b[1]&&a[2]==b[2];};
const auto boundMouthMatches=[&](const auto& authored,const auto& mouth)
{
if(stratum.generatorKind!="TunnelNetwork")
return equalPosition(authored,mouth);
auto expected=mouth;
expected[2]+=static_cast<std::int64_t>(std::llround(
250000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
/World.CoreVoxelSizeMicrons));
return equalPosition(authored,expected);
};
for(const auto& passage:ResolvedLayout->passages)
{
const auto equalPosition=[](const auto& a,const auto& b)
{return a[0]==b[0]&&a[1]==b[1]&&a[2]==b[2];};
if(passage.upperStrateIndex==stratum.strateIndex)
{
const auto rootedMouth=std::find_if(stratum.anchors.begin(),
stratum.anchors.end(),[](const auto& anchor)
{return anchor.kind=="passage_anchor"&&anchor.values.size()>=10;});
const std::array<std::int64_t,3> departure=
rootedMouth!=stratum.anchors.end()
?std::array<std::int64_t,3>{{rootedMouth->values[7],
rootedMouth->values[8],rootedMouth->values[9]}}
:std::array<std::int64_t,3>{{anchors.back()->values[0],
anchors.back()->values[1],anchors.back()->values[2]}};
binding&=equalPosition(passage.upperMouthSubvoxels,
departure);
if(stratum.generatorKind=="TunnelNetwork")
binding&=boundMouthMatches(anchors.back()->values,
passage.upperMouthSubvoxels);
else
{
const auto rootedMouth=std::find_if(stratum.anchors.begin(),
stratum.anchors.end(),[](const auto& anchor)
{return anchor.kind=="passage_anchor"&&anchor.values.size()>=10;});
const std::array<std::int64_t,3> departure=
rootedMouth!=stratum.anchors.end()
?std::array<std::int64_t,3>{{rootedMouth->values[7],
rootedMouth->values[8],rootedMouth->values[9]}}
:std::array<std::int64_t,3>{{anchors.back()->values[0],
anchors.back()->values[1],anchors.back()->values[2]}};
binding&=equalPosition(passage.upperMouthSubvoxels,departure);
}
}
if(passage.lowerStrateIndex==stratum.strateIndex)
{
const auto passageArrival=std::find_if(stratum.anchors.begin(),
stratum.anchors.end(),[](const auto& anchor)
{return anchor.kind=="passage_arrival"&&anchor.values.size()>=3;});
const std::array<std::int64_t,3> arrival=passageArrival!=stratum.anchors.end()
?std::array<std::int64_t,3>{{passageArrival->values[0],
passageArrival->values[1],passageArrival->values[2]}}
:std::array<std::int64_t,3>{{anchors.front()->values[0],
anchors.front()->values[1],anchors.front()->values[2]}};
binding&=equalPosition(passage.lowerMouthSubvoxels,
arrival);
if(stratum.generatorKind=="TunnelNetwork")
binding&=boundMouthMatches(anchors.front()->values,
passage.lowerMouthSubvoxels);
else
{
const auto passageArrival=std::find_if(stratum.anchors.begin(),
stratum.anchors.end(),[](const auto& anchor)
{return anchor.kind=="passage_arrival"&&anchor.values.size()>=3;});
const std::array<std::int64_t,3> arrival=passageArrival!=stratum.anchors.end()
?std::array<std::int64_t,3>{{passageArrival->values[0],
passageArrival->values[1],passageArrival->values[2]}}
:std::array<std::int64_t,3>{{anchors.front()->values[0],
anchors.front()->values[1],anchors.front()->values[2]}};
binding&=equalPosition(passage.lowerMouthSubvoxels,arrival);
}
}
}
++routePasses;routeWalkPasses+=routeAudit.passed;
@@ -4918,6 +5142,18 @@ namespace StandaloneStreamBenchmark
stratum.strateIndex,routeAudit.firstUnsupported[i][0],
routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2],
routeAudit.firstUnsupported[i][3]);
if(stratum.generatorKind=="TunnelNetwork")
{
++tunnelNetworkRoutes;
const FWalkAudit control=auditPlayerPath(blockedWorld,route,
supportStartMetres,supportStepMetres,supportMaximumMetres,
minimumSupportProbes,supportProbeRadiusMetres);
const bool detected=control.blocked>0&&!control.passed;
tunnelNetworkBlockedControls+=detected;
std::printf("[TunnelNetworkBlockedRouteControl] strate=%d arrival_to_departure=true blocked_capsule_samples=%llu result=%s expected=FAIL status=%s\n",
stratum.strateIndex,static_cast<unsigned long long>(control.blocked),
control.passed?"PASS":"FAIL",detected?"PASS":"FAIL");
}
}
for(std::size_t passageIndex=0;
passageIndex<ResolvedLayout->passages.size();++passageIndex)
@@ -4984,13 +5220,16 @@ namespace StandaloneStreamBenchmark
&&shellAuditPassed
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
&&routeBindingFailures==0
&&tunnelNetworkRoutes==tunnelNetworkBlockedControls
&&blockedCompiled&&blockedControls==passageCount;
std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu natural=%llu/%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu blocked_controls=%llu/%llu status=%s\n",
std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu natural=%llu/%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu tunnel_network_block_controls=%llu/%llu blocked_controls=%llu/%llu status=%s\n",
static_cast<unsigned long long>(passageCount),static_cast<unsigned long long>(passagePasses),
static_cast<unsigned long long>(naturalPasses),static_cast<unsigned long long>(passageCount),
static_cast<unsigned long long>(routePasses),static_cast<unsigned long long>(routeWalkPasses),
static_cast<unsigned long long>(routePoints),
static_cast<unsigned long long>(routeBindings),static_cast<unsigned long long>(routeBindingFailures),
static_cast<unsigned long long>(tunnelNetworkBlockedControls),
static_cast<unsigned long long>(tunnelNetworkRoutes),
static_cast<unsigned long long>(blockedControls),static_cast<unsigned long long>(passageCount),
auditPassed?"PASS":"FAIL");
if(!auditPassed)
+167 -2
View File
@@ -1069,6 +1069,166 @@ namespace StandaloneWorldAssemblyPrototype
return "Unknown";
}
inline bool PlanOwnerTunnelNetworkCaves(const FStandaloneOwnerWorld& owner,
VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::string& error)
{
using namespace VoxelForgeCore;
if(!owner.Manager)
{error="Tunnel Network cave planner requires the initialized owner director";return false;}
std::vector<const FStrateSlot*> tunnelSlots;
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition
||owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ))
!=ECaveGeneratorType::TunnelNetwork)continue;
auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[&](const auto& item){return item.strateIndex==slot.StrateIndex;});
if(stratum==layout.strates.end())
{error="Tunnel Network strate is missing from the resolved layout";return false;}
stratum->generatorKind="TunnelNetwork";
stratum->recipeId="Core.Caves.PrimitiveField";
stratum->recipeVersion=1;
stratum->stableId=layout.worldIdentity+"/strate/"
+std::to_string(slot.StrateIndex)+"/generator/TunnelNetwork";
const bool alreadyPlanned=std::any_of(layout.cavernRooms.begin(),
layout.cavernRooms.end(),[&](const auto& room)
{return room.strateIndex==slot.StrateIndex;})
&&std::any_of(layout.cavernEdges.begin(),layout.cavernEdges.end(),
[&](const auto& edge){return edge.strateIndex==slot.StrateIndex;});
if(!alreadyPlanned)tunnelSlots.push_back(&slot);
}
if(!layout.finalize(error))return false;
const auto toMicrometres=[&](double ownerVoxels)
{
return static_cast<std::int64_t>(std::llround(ownerVoxels
*static_cast<double>(owner.OwnerVoxelSizeMicrons)));
};
const auto toQ16=[](float value)
{
return static_cast<std::int32_t>(std::llround(std::clamp(
static_cast<double>(value),0.0,1.0)*65536.0));
};
if(!tunnelSlots.empty())
{
std::vector<ResolvedCavernRoomV1> plannedRooms=layout.cavernRooms;
std::vector<ResolvedCavernEdgeV1> plannedEdges=layout.cavernEdges;
for(const FStrateSlot* slot:tunnelSlots)
{
const FStrateGenerationParams& params=slot->Definition->GenerationParams;
ResolvedCavernPlannerSettingsV1 settings;
settings.roomSpacingMicrometres=std::max<std::int64_t>(4000000,
toMicrometres(params.RoomSpacing));
settings.minimumRoomRadiusMicrometres=std::max<std::int64_t>(500000,
toMicrometres(params.MinRoomRadius));
settings.maximumRoomRadiusMicrometres=std::max(
settings.minimumRoomRadiusMicrometres,toMicrometres(params.MaxRoomRadius));
settings.destinationRadiusMicrometres=std::max(
toMicrometres(params.OriginRoomRadius),
settings.maximumRoomRadiusMicrometres*135/100);
settings.minimumTunnelRadiusMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.TunnelMinRadius));
settings.maximumTunnelRadiusMicrometres=std::max(
settings.minimumTunnelRadiusMicrometres,toMicrometres(params.TunnelMaxRadius));
settings.maximumTunnelLengthMicrometres=std::max<std::int64_t>(4000000,
toMicrometres(params.MaxTunnelLength));
settings.tunnelWarpMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.TunnelWarpStrength));
settings.caveWarpMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.CaveWarpStrength));
settings.smoothWidthMicrometres=std::max<std::int64_t>(1500000,
toMicrometres(params.SDFBlendRadius));
settings.irregularityAmplitudeMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.SurfaceRoughness));
settings.irregularityWavelengthMicrometres=params.RoughnessFrequency>0.0f
?static_cast<std::int64_t>(std::llround(owner.OwnerVoxelSizeMicrons
/static_cast<double>(params.RoughnessFrequency))):0;
settings.roomDensityQ16=toQ16(params.RoomDensity);
settings.tunnelDensityQ16=toQ16(params.TunnelDensity);
settings.roomShapeVarietyQ16=toQ16(params.RoomShapeVariety);
settings.roomHeightRatioQ16=toQ16(params.RoomHeightRatio);
settings.roomFloorCutMinimumQ16=toQ16(params.RoomFloorCutMin);
settings.roomFloorCutMaximumQ16=toQ16(params.RoomFloorCutMax);
settings.tunnelHorizontalBiasQ16=toQ16(params.TunnelHorizontalBias);
settings.originRoomMaximumConnections=params.OriginRoomMaxConnections;
settings.tunnelsFlowTowardOrigin=params.bTunnelsFlowTowardOrigin;
std::vector<ResolvedCavernRoomV1> rooms;
std::vector<ResolvedCavernEdgeV1> edges;
if(!PlanResolvedCavernNetworkV1(layout,owner.Seed,slot->StrateIndex,
settings,rooms,edges,error))return false;
plannedRooms.insert(plannedRooms.end(),
std::make_move_iterator(rooms.begin()),std::make_move_iterator(rooms.end()));
plannedEdges.insert(plannedEdges.end(),
std::make_move_iterator(edges.begin()),std::make_move_iterator(edges.end()));
}
if(!ResolveResolvedCavernGraphV1(layout,owner.Seed,
plannedRooms,plannedEdges,error))return false;
}
const auto toCoreSubvoxels=[&](std::int64_t micrometres)
{
return static_cast<std::int64_t>(std::llround(
static_cast<double>(micrometres)*WorldAssemblySubvoxelsPerVoxel
/layout.coreVoxelSizeMicrometres));
};
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition
||owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ))
!=ECaveGeneratorType::TunnelNetwork)continue;
auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[&](const auto& item){return item.strateIndex==slot.StrateIndex;});
if(stratum==layout.strates.end())continue;
const bool hasRoutePoints=std::count_if(stratum->anchors.begin(),
stratum->anchors.end(),[](const auto& anchor)
{return anchor.kind=="route_point"&&anchor.values.size()>=3;})>=2;
if(hasRoutePoints)continue;
const ResolvedLayoutPassageV1* arrival=nullptr;
const ResolvedLayoutPassageV1* departure=nullptr;
for(const auto& passage:layout.passages)
{
if(passage.lowerStrateIndex==slot.StrateIndex
&&passage.upperStrateIndex<slot.StrateIndex)arrival=&passage;
if(passage.upperStrateIndex==slot.StrateIndex
&&passage.lowerStrateIndex>slot.StrateIndex)departure=&passage;
}
if(!arrival||!departure)
{error="Tunnel Network route anchors cannot bind both passage mouths";return false;}
const auto appendRoutePoint=[&](std::string id,
const std::array<std::int64_t,3>& point)
{
ResolvedLayoutAnchorV2 anchor;anchor.kind="route_point";
anchor.stableId="tunnel-network/route/"+std::move(id);
anchor.units="1/256 Core voxel";
anchor.values={point[0],point[1],point[2]};
stratum->anchors.push_back(std::move(anchor));
};
constexpr std::int64_t PlayerCenterAboveMouthFloorMicrometres=250000;
auto start=arrival->lowerMouthSubvoxels;
start[2]+=toCoreSubvoxels(PlayerCenterAboveMouthFloorMicrometres);
appendRoutePoint("000-arrival",start);
for(std::size_t edgeIndex=0;edgeIndex<256;++edgeIndex)
{
const std::string stableId="tunnel-network/strate-"
+std::to_string(slot.StrateIndex)+"/route-edge-"
+std::to_string(edgeIndex);
const auto edge=std::find_if(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& value)
{return value.strateIndex==slot.StrateIndex
&&value.stableId==stableId;});
if(edge==layout.cavernEdges.end())break;
auto point=edge->controlPointsMicrometres.back();
point[2]+=1000000;
appendRoutePoint(std::to_string(edgeIndex+1),
{toCoreSubvoxels(point[0]),toCoreSubvoxels(point[1]),
toCoreSubvoxels(point[2])});
}
auto end=departure->upperMouthSubvoxels;
end[2]+=toCoreSubvoxels(PlayerCenterAboveMouthFloorMicrometres);
appendRoutePoint("999-departure",end);
}
return layout.finalize(error);
}
inline bool ImportExportedCoreWorldAssembly(const VFJson::Value& root,
VoxelForgeCore::WorldAssemblyManifest& out,std::string& error)
{
@@ -1184,8 +1344,11 @@ namespace StandaloneWorldAssemblyPrototype
if(!slot.Definition)continue;
const auto type=owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
const auto authored=sealAndDensity(*slot.Definition,type);
const bool nativeCaves=recipeVariant=="caves"
const bool nativeSurfaceCaves=recipeVariant=="caves"
&&type==ECaveGeneratorType::SurfaceWorld;
const bool nativeTunnelCaves=type==ECaveGeneratorType::TunnelNetwork
&&recipeVariant!="legacy-tunnel-network"&&recipeVariant!="caves";
const bool nativeCaves=nativeSurfaceCaves||nativeTunnelCaves;
const bool nativeLattice=recipeVariant==("lattice/"+std::to_string(slot.StrateIndex))
&&(type==ECaveGeneratorType::Maze||type==ECaveGeneratorType::VerticalShafts);
const std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*owner.StrateLayoutChunkSizeMicrometres;
@@ -1195,7 +1358,7 @@ namespace StandaloneWorldAssemblyPrototype
if(seal<=0||2*seal>=top-bottom)
{error="strate "+std::to_string(slot.StrateIndex)+" has an invalid BoundarySealThickness for two boundary bands";return false;}
ResolvedLayoutStratumV2 s;s.strateIndex=slot.StrateIndex;
s.generatorKind=nativeCaves?"Caves":GeneratorKind(type);
s.generatorKind=nativeSurfaceCaves?"Caves":GeneratorKind(type);
s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex)
+"/generator/"+s.generatorKind;
s.recipeId=nativeLattice?"Core.Lattice.PrimitiveField"
@@ -1407,6 +1570,8 @@ namespace StandaloneWorldAssemblyPrototype
passageConfigs.push_back(config);
}
if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false;
if(recipeVariant!="legacy-tunnel-network"&&recipeVariant!="caves")
return PlanOwnerTunnelNetworkCaves(owner,out,error);
return out.finalize(error);
}