wip: cave planner squash-merged into wip/combined (L173, RED - not for main)

Five conflicts resolved (CURRENT-STATE, ResolvedLayoutInternal.h, StreamBenchmarkHeadless.cpp,
StreamBenchmarkPipeline.h, WorldAssemblyPrototype.h) keeping liminal + cave planner; L156 band fit only; evaluator v239
with v239 baselines (18/18 Clang/GCC W1/W8/W12). Fast tier FAILS (FI upper arrival: 40 unsupported chamber samples,
10 chamber-floor shell violations); TN and VS composed sweeps 0/16 (passage, seam, shell gates). Living plane loads at
PostConfigInit (UE smoke passes). Source/: VoxelForgeCoreAdapter.cpp; VoxelForge.uplugin.

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-11-03 02:28:34 +01:00
1 parent 82eb5b1c66
commit b09728354d
243 files changed
+23019 -307

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;
@@ -1436,6 +1437,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"
@@ -1505,6 +1507,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;
@@ -1745,7 +1748,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,
@@ -1850,7 +1854,9 @@ int main(int argc,char** argv)
const VFJson::Value& coreLiminalConfig=ownerJsonRoot.Get("core_liminal_field");
const bool hasCoreLiminalField=!std::holds_alternative<std::nullptr_t>(
coreLiminalConfig.Data);
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)
@@ -1864,6 +1870,11 @@ int main(int argc,char** argv)
!hasCoreLiminalField);
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))
@@ -2218,8 +2229,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;}
}
@@ -2230,8 +2244,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;}
}
@@ -2352,12 +2369,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");
File diff suppressed because it is too large. Load diff
+4 -1
View File
@@ -1580,10 +1580,13 @@ private:
static_cast<unsigned long long>(D.islandSharedCacheConflictReplacements),
static_cast<double>(D.islandSharedCacheLockWaitNanoseconds)/1000.0);
if(D.passageComposerProfiled)
std::printf("[PassageComposerTile] key=(%d,%d,%d,lod%d) worker_ms=%.3f samples=%llu native_field_calls=%llu features=%llu route_candidates=%llu boundary_band_queries=%llu queries=%llu passage_iterations=%llu carve_segment_visits=%llu relief_field_calls=%llu sweep_segments=%llu sweep_pairs=%llu sweep_prepare_ms=%.3f sweep_outside_pairs=%llu sweep_shell_support_skips=%llu sweep_min_updates=%llu sweep_base_samples=%llu sweep_detail_samples=%llu sweep_grid_samples=%llu sweep_no_influence_samples=%llu sweep_lookups=%llu sweep_cache_hits=%llu sweep_base_hits=%llu sweep_outside_hits=%llu sweep_missing=%llu\n",
std::printf("[PassageComposerTile] key=(%d,%d,%d,lod%d) worker_ms=%.3f samples=%llu field_calls=%llu native_cave_samples=%llu room_candidates=%llu edge_candidates=%llu features=%llu route_candidates=%llu boundary_band_queries=%llu queries=%llu passage_iterations=%llu carve_segment_visits=%llu relief_field_calls=%llu sweep_segments=%llu sweep_pairs=%llu sweep_prepare_ms=%.3f sweep_outside_pairs=%llu sweep_shell_support_skips=%llu sweep_min_updates=%llu sweep_base_samples=%llu sweep_detail_samples=%llu sweep_grid_samples=%llu sweep_no_influence_samples=%llu sweep_lookups=%llu sweep_cache_hits=%llu sweep_base_hits=%llu sweep_outside_hits=%llu sweep_missing=%llu\n",
Event.Key.Coord.X,Event.Key.Coord.Y,Event.Key.Coord.Z,Event.Key.Level,
Event.WorkerMilliseconds,static_cast<unsigned long long>(Event.DensitySamples),
static_cast<unsigned long long>(D.fieldCalls),
static_cast<unsigned long long>(D.nativeCaveFieldSamples),
static_cast<unsigned long long>(D.cavernRoomCandidates),
static_cast<unsigned long long>(D.cavernEdgeCandidates),
static_cast<unsigned long long>(D.passageTileFeatureCount),
static_cast<unsigned long long>(D.passageTileRouteSegmentCandidates),
static_cast<unsigned long long>(D.resolvedBoundaryBandQueries),
+752 -3
View File
@@ -11,9 +11,11 @@
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
#include <numeric>
#include <queue>
#include <string>
#include <vector>
@@ -283,6 +285,330 @@ namespace StandaloneWorldAssemblyPrototype
}
}
}
std::uint64_t terrainPathCount=0,terrainGridSamples=0;
std::int32_t terrainGridStepQ=0;
const auto appendTerrainFallback=[&](std::int32_t stepQ)
{
if(!fallbackSearch
||!((arrivalRadiusSubvoxels>0)!=(departureRadiusSubvoxels>0)))
return;
{
// A bounded one-mouth world can have no walkable straight chord even
// when a contour route exists. Link real ground columns only when their
// grade meets the same limit, then use the ordinary capsule and support
// sweep to accept or reject each resulting path.
struct FGroundNode
{
std::int64_t ground=0;
bool supportReachable=false;
};
struct FQueueEntry
{
std::uint64_t cost=0;
std::size_t index=0;
bool operator>(const FQueueEntry& other) const
{return cost>other.cost||(cost==other.cost&&index>other.index);}
};
constexpr std::array<std::array<std::int64_t,2>,5> supportOffsets{{
{{0,0}},{{ProbeRadiusQ,0}},{{-ProbeRadiusQ,0}},
{{0,ProbeRadiusQ}},{{0,-ProbeRadiusQ}}}};
constexpr std::int64_t SupportStepQ=WorldAssemblySubvoxelsPerVoxel/4;
const std::int64_t routeBaseLiftQ=constraints.minimumClearanceSubvoxels/4;
const auto hasRouteSupportAt=[&](std::int64_t centerX,std::int64_t centerY,
std::int64_t footZ)
{
std::uint32_t reachable=0;
for(const auto& offset:supportOffsets)
{
for(std::int64_t depth=SupportStepQ;depth<=SupportDepthQ;
depth+=SupportStepQ)
{
if(finalField.point(centerX+offset[0],centerY+offset[1],
footZ-depth).densitySubvoxels<0)
{++reachable;break;}
}
}
return reachable>=constraints.minimumSupportProbes;
};
const auto tryTerrainPaths=[&](std::int32_t terrainStepQ)
{
terrainPathCount=0;terrainGridSamples=0;terrainGridStepQ=0;
const std::int64_t boundedRadius=std::max(arrivalRadiusSubvoxels,
departureRadiusSubvoxels);
const std::int32_t boundRadiusVoxels=static_cast<std::int32_t>(
(boundedRadius+WorldAssemblySubvoxelsPerVoxel-1)
/WorldAssemblySubvoxelsPerVoxel);
const std::int32_t searchRadiusVoxels=std::clamp(
boundRadiusVoxels+64,64,256);
const std::int64_t searchRadiusQ=static_cast<std::int64_t>(
searchRadiusVoxels)*WorldAssemblySubvoxelsPerVoxel;
const std::int32_t radiusSteps=static_cast<std::int32_t>(
searchRadiusQ/terrainStepQ);
const std::int32_t dimension=radiusSteps*2+1;
const std::size_t nodeCount=static_cast<std::size_t>(dimension)*dimension;
const auto indexOf=[dimension](std::int32_t x,std::int32_t y)
{return static_cast<std::size_t>(y)*dimension+x;};
std::vector<FGroundNode> grid(nodeCount);
const auto safeGroundAt=[&](std::int64_t centerX,std::int64_t centerY)
{
constexpr std::int64_t FootprintStepQ=WorldAssemblySubvoxelsPerVoxel/2;
std::int64_t highestGround=std::numeric_limits<std::int64_t>::min();
for(std::int64_t oy=-PlayerRadiusQ;oy<=PlayerRadiusQ;
oy+=FootprintStepQ)
for(std::int64_t ox=-PlayerRadiusQ;ox<=PlayerRadiusQ;
ox+=FootprintStepQ)
{
if(ox*ox+oy*oy>PlayerRadiusQ*PlayerRadiusQ)continue;
highestGround=std::max(highestGround,finalField.point(
centerX+ox,centerY+oy,0).groundHeightSubvoxels);
}
return FGroundNode{highestGround,
hasRouteSupportAt(centerX,centerY,
highestGround+routeBaseLiftQ)};
};
for(std::int32_t gy=0;gy<dimension;++gy)
for(std::int32_t gx=0;gx<dimension;++gx)
{
const std::int64_t xQ=static_cast<std::int64_t>(gx-radiusSteps)
*terrainStepQ;
const std::int64_t yQ=static_cast<std::int64_t>(gy-radiusSteps)
*terrainStepQ;
grid[indexOf(gx,gy)]=safeGroundAt(xQ,yQ);
++terrainGridSamples;
}
for(std::size_t directionIndex=0;directionIndex<1;++directionIndex)
{
std::vector<std::uint64_t> costs(nodeCount,
std::numeric_limits<std::uint64_t>::max());
std::vector<std::size_t> parent(nodeCount,nodeCount);
std::priority_queue<FQueueEntry,std::vector<FQueueEntry>,
std::greater<FQueueEntry>> open;
std::vector<std::int8_t> edgeSupportCache(nodeCount*4,-1);
for(std::int32_t gy=0;gy<dimension;++gy)
for(std::int32_t gx=0;gx<dimension;++gx)
{
if(gx!=0&&gy!=0&&gx!=dimension-1&&gy!=dimension-1)continue;
const std::size_t start=indexOf(gx,gy);
if(!grid[start].supportReachable)continue;
costs[start]=0;
open.push({0,start});
}
if(open.empty())continue;
std::size_t goal=nodeCount;
std::uint64_t goalCost=std::numeric_limits<std::uint64_t>::max();
std::uint64_t goalDistanceSquared=std::numeric_limits<std::uint64_t>::max();
while(!open.empty())
{
const FQueueEntry current=open.top();open.pop();
if(current.cost!=costs[current.index])continue;
if(goalCost!=std::numeric_limits<std::uint64_t>::max()
&&current.cost>goalCost)break;
const std::int32_t gx=static_cast<std::int32_t>(current.index%dimension);
const std::int32_t gy=static_cast<std::int32_t>(current.index/dimension);
if(parent[current.index]!=nodeCount)
{
const std::int64_t xQ=static_cast<std::int64_t>(gx-radiusSteps)
*terrainStepQ;
const std::int64_t yQ=static_cast<std::int64_t>(gy-radiusSteps)
*terrainStepQ;
const std::uint64_t distanceSquared=static_cast<std::uint64_t>(
xQ*xQ+yQ*yQ);
if(distanceSquared<=static_cast<std::uint64_t>(
boundedRadius*boundedRadius)
&&(current.cost<goalCost||(current.cost==goalCost
&&distanceSquared<goalDistanceSquared)))
{goal=current.index;goalCost=current.cost;
goalDistanceSquared=distanceSquared;}
}
for(std::int32_t oy=-1;oy<=1;++oy)
for(std::int32_t ox=-1;ox<=1;++ox)
{
if(ox==0&&oy==0)continue;
const std::int32_t nx=gx+ox,ny=gy+oy;
if(nx<0||ny<0||nx>=dimension||ny>=dimension)continue;
const std::size_t next=indexOf(nx,ny);
if(!grid[next].supportReachable)continue;
const std::int64_t horizontalQ=static_cast<std::int64_t>(
std::llround(terrainStepQ*std::hypot(static_cast<double>(ox),
static_cast<double>(oy))));
const std::int64_t riseQ=std::abs(grid[next].ground
-grid[current.index].ground);
if(horizontalQ<=0||riseQ*65536>horizontalQ*GradeQ16)continue;
const std::int32_t edgeDx=nx-gx,edgeDy=ny-gy;
std::int32_t baseX=gx,baseY=gy,edgeDirection=0;
if(edgeDx==0)
{baseY=std::min(gy,ny);edgeDirection=1;}
else if(edgeDy==0)
{baseX=std::min(gx,nx);edgeDirection=0;}
else if(edgeDx==edgeDy)
{baseX=std::min(gx,nx);baseY=std::min(gy,ny);edgeDirection=2;}
else
{baseX=std::max(gx,nx);baseY=std::min(gy,ny);edgeDirection=3;}
const std::size_t edgeCacheIndex=indexOf(baseX,baseY)*4
+static_cast<std::size_t>(edgeDirection);
if(edgeSupportCache[edgeCacheIndex]<0)
{
const std::uint64_t intervals=std::max<std::uint64_t>(2,
(static_cast<std::uint64_t>(horizontalQ)
+WorldAssemblySubvoxelsPerVoxel/8-1)
/(WorldAssemblySubvoxelsPerVoxel/8));
bool supported=true;
const std::int64_t fromX=static_cast<std::int64_t>(
gx-radiusSteps)*terrainStepQ;
const std::int64_t fromY=static_cast<std::int64_t>(
gy-radiusSteps)*terrainStepQ;
const std::int64_t toX=static_cast<std::int64_t>(
nx-radiusSteps)*terrainStepQ;
const std::int64_t toY=static_cast<std::int64_t>(
ny-radiusSteps)*terrainStepQ;
for(std::uint64_t sample=1;sample<intervals;++sample)
{
const std::int64_t x=fromX+(toX-fromX)
*static_cast<std::int64_t>(sample)
/static_cast<std::int64_t>(intervals);
const std::int64_t y=fromY+(toY-fromY)
*static_cast<std::int64_t>(sample)
/static_cast<std::int64_t>(intervals);
const std::int64_t footZ=grid[current.index].ground
+(grid[next].ground-grid[current.index].ground)
*static_cast<std::int64_t>(sample)
/static_cast<std::int64_t>(intervals)
+routeBaseLiftQ;
if(!hasRouteSupportAt(x,y,footZ))
{supported=false;break;}
}
edgeSupportCache[edgeCacheIndex]=supported?1:0;
}
if(edgeSupportCache[edgeCacheIndex]==0)continue;
const std::uint64_t edgeCost=static_cast<std::uint64_t>(
horizontalQ+riseQ*4);
if(current.cost>std::numeric_limits<std::uint64_t>::max()-edgeCost)
continue;
const std::uint64_t nextCost=current.cost+edgeCost;
if(nextCost>=costs[next])continue;
costs[next]=nextCost;parent[next]=current.index;
open.push({nextCost,next});
}
}
if(goal==nodeCount)continue;
std::vector<std::size_t> path;
for(std::size_t cursor=goal;cursor!=nodeCount;cursor=parent[cursor])
{
path.push_back(cursor);
if(parent[cursor]==nodeCount)break;
if(path.size()>nodeCount)break;
}
if(path.size()<2||parent[path.back()]!=nodeCount)continue;
std::reverse(path.begin(),path.end());
if(arrivalRadiusSubvoxels>0)
std::reverse(path.begin(),path.end());
Candidate candidate;
candidate.centerX=static_cast<std::int32_t>(
(static_cast<std::int32_t>(path.front()%dimension)-radiusSteps)
*terrainStepQ);
candidate.centerY=static_cast<std::int32_t>(
(static_cast<std::int32_t>(path.front()/dimension)-radiusSteps)
*terrainStepQ);
candidate.direction=static_cast<std::int32_t>(directionIndex);
candidate.score=static_cast<std::int64_t>(goalCost/path.size());
candidate.endpointProximityCost=goalDistanceSquared
*(arrivalRadiusSubvoxels>0?16ull:1ull);
candidate.points.reserve(path.size());
bool gradePass=true;
for(std::size_t pointIndex=0;pointIndex<path.size();++pointIndex)
{
const std::size_t index=path[pointIndex];
const std::int32_t gx=static_cast<std::int32_t>(index%dimension);
const std::int32_t gy=static_cast<std::int32_t>(index/dimension);
const std::int64_t xQ=static_cast<std::int64_t>(gx-radiusSteps)
*terrainStepQ;
const std::int64_t yQ=static_cast<std::int64_t>(gy-radiusSteps)
*terrainStepQ;
WorldAssemblyRoutePointV1 point;
point.positionSubvoxels={xQ,yQ,
grid[index].ground+routeBaseLiftQ+HalfHeightQ};
point.radiusSubvoxels=RouteRadiusQ;
point.stableFeatureId=WorldAssemblyFeatureId(recipe.seed,
xQ,yQ,
0x52550000u+static_cast<std::uint32_t>(pointIndex));
if(pointIndex>0)
{
const auto& previous=candidate.points.back().positionSubvoxels;
const std::int64_t dx=point.positionSubvoxels[0]-previous[0];
const std::int64_t dy=point.positionSubvoxels[1]-previous[1];
const std::int64_t dz=std::abs(point.positionSubvoxels[2]-previous[2]);
const std::int64_t run=static_cast<std::int64_t>(std::llround(
std::hypot(static_cast<double>(dx),static_cast<double>(dy))));
if(run<=0||dz*65536>run*GradeQ16)gradePass=false;
}
candidate.points.push_back(point);
}
if(!gradePass)continue;
++terrainPathCount;
const std::array<std::array<std::int64_t,2>,8> lateralNudges{{
{{-ProbeRadiusQ,-ProbeRadiusQ}},{{-ProbeRadiusQ,0}},
{{0,-ProbeRadiusQ}},{{ProbeRadiusQ,-ProbeRadiusQ}},
{{-ProbeRadiusQ,ProbeRadiusQ}},{{0,ProbeRadiusQ}},
{{ProbeRadiusQ,0}},{{ProbeRadiusQ,ProbeRadiusQ}}}};
for(std::size_t nudgeIndex=0;nudgeIndex<lateralNudges.size();++nudgeIndex)
{
Candidate shifted=candidate;
shifted.direction=candidate.direction*9
+static_cast<std::int32_t>(nudgeIndex+1);
shifted.score+=static_cast<std::int64_t>(nudgeIndex+1);
bool shiftedGradePass=true;
for(std::size_t pointIndex=0;pointIndex<shifted.points.size();++pointIndex)
{
const std::int64_t tQ16=shifted.points.size()>1
?static_cast<std::int64_t>(pointIndex)*65536
/static_cast<std::int64_t>(shifted.points.size()-1):0;
const std::int64_t t2Q16=tQ16*tQ16/65536;
const std::int64_t oneMinusQ16=65536-tQ16;
const std::int64_t oneMinus2Q16=
oneMinusQ16*oneMinusQ16/65536;
const std::int64_t weightQ16=std::min<std::int64_t>(65536,
16*t2Q16*oneMinus2Q16/65536);
auto& point=shifted.points[pointIndex];
point.positionSubvoxels[0]+=lateralNudges[nudgeIndex][0]
*weightQ16/65536;
point.positionSubvoxels[1]+=lateralNudges[nudgeIndex][1]
*weightQ16/65536;
const auto ground=safeGroundAt(point.positionSubvoxels[0],
point.positionSubvoxels[1]);
if(!ground.supportReachable)shiftedGradePass=false;
point.positionSubvoxels[2]=ground.ground+routeBaseLiftQ
+HalfHeightQ;
point.stableFeatureId=WorldAssemblyFeatureId(recipe.seed,
point.positionSubvoxels[0],point.positionSubvoxels[1],
0x52550000u+static_cast<std::uint32_t>(
(nudgeIndex+1)*0x10000u+pointIndex));
if(pointIndex==0)continue;
const auto& previous=shifted.points[pointIndex-1]
.positionSubvoxels;
const std::int64_t dx=point.positionSubvoxels[0]-previous[0];
const std::int64_t dy=point.positionSubvoxels[1]-previous[1];
const std::int64_t dz=std::abs(point.positionSubvoxels[2]-previous[2]);
const std::int64_t run=static_cast<std::int64_t>(std::llround(
std::hypot(static_cast<double>(dx),static_cast<double>(dy))));
if(run<=0||dz*65536>run*GradeQ16)shiftedGradePass=false;
}
if(!shiftedGradePass)continue;
candidates.push_back(std::move(shifted));
++terrainPathCount;
}
candidates.push_back(std::move(candidate));
}
if(terrainPathCount>0)terrainGridStepQ=terrainStepQ;
};
tryTerrainPaths(stepQ);
tested+=terrainPathCount;
std::printf("[WorldAssemblerRouteTerrainSearch] grid_step_m=%.3f grid_samples=%llu candidate_paths=%llu grade_limit_deg=15.000 status=%s\n",
terrainGridStepQ*coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel,
static_cast<unsigned long long>(terrainGridSamples),
static_cast<unsigned long long>(terrainPathCount),
terrainPathCount>0?"FOUND":"EMPTY");
}
};
if(gradeRejected>0||placementRejected>0)
{
const double metresPerSubvoxel=static_cast<double>(coreVoxelSizeMicrons)
@@ -318,8 +644,33 @@ namespace StandaloneWorldAssemblyPrototype
});
std::uint64_t sweepAttempt=0,repairAttempt=0;
VoxelForgeCore::WorldAssemblyRouteAuditResult lastAudit{};
for(auto& candidate:candidates)
std::size_t candidateCursor=0;
constexpr std::array<std::int32_t,4> TerrainFallbackSteps{{1024,512,256,128}};
std::size_t terrainFallbackStep=0;
for(;;)
{
if(candidateCursor>=candidates.size())
{
if(terrainFallbackStep<TerrainFallbackSteps.size()&&fallbackSearch
&&((arrivalRadiusSubvoxels>0)!=(departureRadiusSubvoxels>0)))
{
const std::size_t firstTerrainCandidate=candidates.size();
appendTerrainFallback(TerrainFallbackSteps[terrainFallbackStep++]);
std::stable_sort(candidates.begin()+firstTerrainCandidate,
candidates.end(),[](const Candidate& a,const Candidate& b)
{
if(a.endpointProximityCost!=b.endpointProximityCost)
return a.endpointProximityCost<b.endpointProximityCost;
if(a.score!=b.score)return a.score<b.score;
if(a.centerY!=b.centerY)return a.centerY<b.centerY;
if(a.centerX!=b.centerX)return a.centerX<b.centerX;
return a.direction<b.direction;
});
continue;
}
break;
}
auto& candidate=candidates[candidateCursor++];
stratum.route=candidate.points;
std::int64_t liftQ=0;
constexpr std::int64_t RepairStepQ=64;
@@ -349,6 +700,42 @@ namespace StandaloneWorldAssemblyPrototype
static_cast<long long>(candidate.score));
return true;
}
if(liftQ==0&&audit.blockedCapsuleSamples==0
&&audit.unsupportedCenterSamples>0)
{
constexpr std::int64_t SupportAlignmentStepQ=16;
const std::int64_t lowerLimit=std::max<std::int64_t>(0,
audit.minimumClearanceSubvoxels
-constraints.minimumClearanceSubvoxels);
for(std::int64_t lowerQ=SupportAlignmentStepQ;
lowerQ<=lowerLimit;lowerQ+=SupportAlignmentStepQ)
{
for(std::size_t pointIndex=0;pointIndex<stratum.route.size();++pointIndex)
stratum.route[pointIndex].positionSubvoxels[2]
=candidate.points[pointIndex].positionSubvoxels[2]-lowerQ;
const auto loweredAudit=AuditHeightfieldRoute(candidateField);
lastAudit=loweredAudit;++repairAttempt;
if(loweredAudit.passed)
{
std::printf("[WorldAssemblerRouteRepaired] uniform_lower_q=%lld support_limit_q=%lld\n",
static_cast<long long>(lowerQ),
static_cast<long long>(lowerLimit));
const double metresPerSubvoxel=coreVoxelMetres
/WorldAssemblySubvoxelsPerVoxel;
std::printf("[WorldAssemblerRouteAccepted] arrival_xy_m=(%.3f,%.3f) departure_xy_m=(%.3f,%.3f) endpoint_proximity_cost=%llu average_grade_q16=%lld\n",
candidate.points.front().positionSubvoxels[0]*metresPerSubvoxel,
candidate.points.front().positionSubvoxels[1]*metresPerSubvoxel,
candidate.points.back().positionSubvoxels[0]*metresPerSubvoxel,
candidate.points.back().positionSubvoxels[1]*metresPerSubvoxel,
static_cast<unsigned long long>(candidate.endpointProximityCost),
static_cast<long long>(candidate.score));
return true;
}
if(loweredAudit.blockedCapsuleSamples>0
||loweredAudit.minimumClearanceSubvoxels
<constraints.minimumClearanceSubvoxels)break;
}
}
if(audit.blockedCapsuleSamples==0||liftQ>=MaximumRepairLiftQ)break;
const std::int64_t needed=std::max<std::int64_t>(1,
constraints.minimumClearanceSubvoxels-audit.minimumClearanceSubvoxels);
@@ -358,6 +745,62 @@ namespace StandaloneWorldAssemblyPrototype
liftQ=nextLift;
++repairAttempt;
}
if(lastAudit.unsupportedCenterSamples>0&&sweepAttempt<5)
{
const Heightfield2D diagnosticField(recipe,stratum);
const std::int64_t sampleSpacing=std::min<std::int64_t>(
constraints.sweepSpacingSubvoxels,
WorldAssemblySubvoxelsPerVoxel/2);
const std::int64_t supportSpacing=std::min<std::int64_t>(sampleSpacing,
WorldAssemblySubvoxelsPerVoxel/4);
constexpr std::array<std::array<std::int64_t,2>,5> supportOffsets{{
{{0,0}},{{ProbeRadiusQ,0}},{{-ProbeRadiusQ,0}},
{{0,ProbeRadiusQ}},{{0,-ProbeRadiusQ}}}};
for(std::size_t segment=1;segment<stratum.route.size();++segment)
{
const auto& from=stratum.route[segment-1].positionSubvoxels;
const auto& to=stratum.route[segment].positionSubvoxels;
const std::int64_t dx=to[0]-from[0],dy=to[1]-from[1],dz=to[2]-from[2];
const std::uint64_t length=std::max<std::uint64_t>(1,
static_cast<std::uint64_t>(std::llround(std::sqrt(
static_cast<double>(dx*dx+dy*dy+dz*dz)))));
const std::uint64_t intervals=std::max<std::uint64_t>(1,
(length+sampleSpacing-1)/sampleSpacing);
for(std::uint64_t step=segment==1?0:1;step<=intervals;++step)
{
const auto interpolate=[&](std::size_t axis)
{return from[axis]+(to[axis]-from[axis])
*static_cast<std::int64_t>(step)/static_cast<std::int64_t>(intervals);};
const std::int64_t x=interpolate(0),y=interpolate(1),z=interpolate(2);
const std::int64_t footZ=z-PlayerHeightQ/2;
std::array<std::int64_t,5> reachedDepth{};
for(std::size_t probe=0;probe<supportOffsets.size();++probe)
for(std::int64_t depth=supportSpacing;
depth<=constraints.supportSearchDepthSubvoxels;
depth+=supportSpacing)
{
if(diagnosticField.point(x+supportOffsets[probe][0],
y+supportOffsets[probe][1],footZ-depth)
.densitySubvoxels<0)
{reachedDepth[probe]=depth;break;}
}
const auto supported=static_cast<std::uint32_t>(std::count_if(
reachedDepth.begin(),reachedDepth.end(),[](auto depth){return depth>0;}));
if(supported>=constraints.minimumSupportProbes)continue;
const double metresPerSubvoxel=coreVoxelMetres
/WorldAssemblySubvoxelsPerVoxel;
std::printf("[WorldAssemblerRouteSupportSample] ordinal=%llu x_m=%.3f y_m=%.3f center_z_m=%.3f supported=%u/5 depths_q=(%lld,%lld,%lld,%lld,%lld)\n",
static_cast<unsigned long long>(sweepAttempt),x*metresPerSubvoxel,
y*metresPerSubvoxel,z*metresPerSubvoxel,supported,
static_cast<long long>(reachedDepth[0]),
static_cast<long long>(reachedDepth[1]),
static_cast<long long>(reachedDepth[2]),
static_cast<long long>(reachedDepth[3]),
static_cast<long long>(reachedDepth[4]));
segment=stratum.route.size();break;
}
}
}
if(sweepAttempt<3)
std::printf("[WorldAssemblerRouteCandidateRejected] ordinal=%llu blocked=%llu unsupported=%llu grade_failures=%llu minimum_clearance_q=%lld\n",
static_cast<unsigned long long>(sweepAttempt),
@@ -1733,6 +2176,300 @@ namespace StandaloneWorldAssemblyPrototype
return "Unknown";
}
inline bool PlanOwnerTunnelNetworkCaves(const FStandaloneOwnerWorld& owner,
VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::string& error)
{
using namespace VoxelForgeCore;
if(!owner.Manager)
{error="native cave planner requires the initialized owner director";return false;}
std::vector<std::pair<const FStrateSlot*,bool>> plannerSlots;
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition)continue;
const ECaveGeneratorType generatorType=
owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
const bool tunnelNetwork=generatorType==ECaveGeneratorType::TunnelNetwork;
const bool verticalShafts=generatorType==ECaveGeneratorType::VerticalShafts;
if(!tunnelNetwork&&!verticalShafts)continue;
const bool hasIncomingMouth=std::any_of(layout.passages.begin(),
layout.passages.end(),[&](const auto& passage)
{return passage.lowerStrateIndex==slot.StrateIndex
&&passage.upperStrateIndex<slot.StrateIndex;});
const bool hasOutgoingMouth=std::any_of(layout.passages.begin(),
layout.passages.end(),[&](const auto& passage)
{return passage.upperStrateIndex==slot.StrateIndex
&&passage.lowerStrateIndex>slot.StrateIndex;});
// Tunnel Network connects two passages. The basal Vertical Shafts
// stratum is terminal: it inherits its arrival from the preceding
// strate and intentionally has no departure passage.
if((tunnelNetwork&&(!hasIncomingMouth||!hasOutgoingMouth))
||(verticalShafts&&(!hasIncomingMouth||hasOutgoingMouth)))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=verticalShafts?"VerticalShafts":"TunnelNetwork";
stratum->recipeId="Core.Caves.PrimitiveField";
stratum->recipeVersion=1;
stratum->stableId=layout.worldIdentity+"/strate/"
+std::to_string(slot.StrateIndex)+"/generator/"
+(verticalShafts?"VerticalShafts":"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)plannerSlots.emplace_back(&slot,verticalShafts);
}
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(!plannerSlots.empty())
{
std::vector<ResolvedCavernRoomV1> plannedRooms=layout.cavernRooms;
std::vector<ResolvedCavernEdgeV1> plannedEdges=layout.cavernEdges;
for(const auto& [slot,verticalShafts]:plannerSlots)
{
const FStrateGenerationParams& params=slot->Definition->GenerationParams;
ResolvedCavernPlannerSettingsV1 settings;
if(verticalShafts)
{
const FVerticalShaftParams& shaft=slot->Definition->VerticalShaftParams;
settings.verticalShaftMode=true;
settings.roomSpacingMicrometres=std::clamp<std::int64_t>(
toMicrometres(shaft.ShaftSpacing),4000000,100000000);
settings.minimumRoomRadiusMicrometres=std::max<std::int64_t>(
500000,toMicrometres(shaft.ShaftMinRadius));
settings.maximumRoomRadiusMicrometres=std::max(
settings.minimumRoomRadiusMicrometres,
toMicrometres(shaft.ShaftMaxRadius));
settings.destinationRadiusMicrometres=std::max(
settings.maximumRoomRadiusMicrometres,
settings.maximumRoomRadiusMicrometres*135/100);
settings.minimumTunnelRadiusMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.ConnectorRadius));
settings.maximumTunnelRadiusMicrometres=
settings.minimumTunnelRadiusMicrometres;
settings.maximumTunnelLengthMicrometres=std::clamp<std::int64_t>(
settings.roomSpacingMicrometres*2,4000000,100000000);
settings.smoothWidthMicrometres=1500000;
settings.floorSupportDepthMicrometres=2000000;
settings.irregularityAmplitudeMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.SurfaceRoughness));
settings.irregularityWavelengthMicrometres=std::max<std::int64_t>(
500000,static_cast<std::int64_t>(owner.OwnerVoxelSizeMicrons)*4);
settings.roomDensityQ16=toQ16(shaft.ShaftDensity);
settings.tunnelDensityQ16=toQ16(shaft.CrossConnectChance);
settings.roomShapeVarietyQ16=0;
settings.roomHeightRatioQ16=65536;
settings.roomFloorCutMinimumQ16=0;
settings.roomFloorCutMaximumQ16=0;
settings.tunnelHorizontalBiasQ16=65536;
settings.originRoomMaximumConnections=32;
settings.ledgeSpacingMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.LedgeSpacing));
settings.ledgeDepthMicrometres=std::max<std::int64_t>(0,
toMicrometres(shaft.LedgeDepth));
}
else
{
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.floorSupportDepthMicrometres=std::clamp<std::int64_t>(
settings.smoothWidthMicrometres/2,750000,2000000);
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));
};
constexpr std::int64_t PlayerCenterAboveFloorMicrometres=1130000;
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition)continue;
const ECaveGeneratorType generatorType=
owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
const bool verticalShafts=generatorType==ECaveGeneratorType::VerticalShafts;
if(generatorType!=ECaveGeneratorType::TunnelNetwork&&!verticalShafts)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()
||!IsNativeCaveFieldStratum(*stratum))continue;
const std::string routePrefix=(verticalShafts?"vertical-shafts/strate-"
:"tunnel-network/strate-")
+std::to_string(slot.StrateIndex)+"/";
const bool hasPlannedArrival=std::any_of(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& edge)
{return edge.strateIndex==slot.StrateIndex
&&edge.stableId==routePrefix+"route-edge-0";});
const bool hasPlannedDeparture=!verticalShafts
&&std::any_of(layout.cavernEdges.begin(),layout.cavernEdges.end(),
[&](const auto& edge){return edge.strateIndex==slot.StrateIndex
&&edge.stableId==routePrefix+"departure-edge";});
if(!hasPlannedArrival||(!verticalShafts&&!hasPlannedDeparture))continue;
// Route-point anchors also drive the passage-mouth floor blend. The
// native cave planner replaces the legacy finite-strate route, so
// retaining its old anchors would join the passage floor to a route
// that is no longer present in the composed cave field.
stratum->anchors.erase(std::remove_if(stratum->anchors.begin(),
stratum->anchors.end(),[](const auto& anchor)
{return anchor.kind=="route_point";}),stratum->anchors.end());
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||(!verticalShafts&&!departure))
{error="native cave route anchors cannot bind their required 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=(verticalShafts?"vertical-shafts/route/"
:"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));
};
const auto sameRoutePoint=[](const auto& a,const auto& b)
{
for(std::size_t axis=0;axis<3;++axis)
if(std::abs(a[axis]-b[axis])>2)return false;
return true;
};
auto start=arrival->lowerMouthSubvoxels;
appendRoutePoint("000-arrival",start);
for(std::size_t edgeIndex=0;edgeIndex<256;++edgeIndex)
{
const std::string stableId=routePrefix+"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;
bool terminalRouteEdge=false;
if(verticalShafts)
{
const std::string nextRouteEdge=routePrefix+"route-edge-"
+std::to_string(edgeIndex+1);
terminalRouteEdge=std::none_of(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& candidate)
{return candidate.strateIndex==slot.StrateIndex
&&candidate.stableId==nextRouteEdge;});
}
if(edgeIndex==0)
{
auto inner=edge->controlPointsMicrometres.front();
// Keep the exact authored mouth anchor and use the planner
// edge's floor height for the first cave-side walking point.
// Passage composition uses these same two points to blend the
// floor profile into the native cave route.
inner[2]+=PlayerCenterAboveFloorMicrometres;
const std::array<std::int64_t,3> point={
toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
toCoreSubvoxels(inner[2])};
if(!sameRoutePoint(point,start))
appendRoutePoint("000-edge-start",point);
}
auto point=edge->controlPointsMicrometres.back();
point[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint(terminalRouteEdge?"999-basal-terminal"
:std::to_string(edgeIndex+1),
{toCoreSubvoxels(point[0]),toCoreSubvoxels(point[1]),
toCoreSubvoxels(point[2])});
}
if(departure)
{
const std::string departureEdgeId=routePrefix+"departure-edge";
const auto departureEdge=std::find_if(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& value)
{return value.strateIndex==slot.StrateIndex
&&value.stableId==departureEdgeId;});
if(departureEdge==layout.cavernEdges.end())
{error="native cave route lost its planned departure edge";return false;}
auto inner=departureEdge->controlPointsMicrometres.front();
inner[2]+=PlayerCenterAboveFloorMicrometres;
const std::array<std::int64_t,3> innerPoint={
toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
toCoreSubvoxels(inner[2])};
auto end=departure->upperMouthSubvoxels;
if(!sameRoutePoint(innerPoint,end))
appendRoutePoint("998-departure-inner",innerPoint);
appendRoutePoint("999-departure",departure->upperMouthSubvoxels);
}
else
{
const auto terminal=std::find_if(layout.cavernRooms.begin(),
layout.cavernRooms.end(),[&](const auto& room)
{return room.strateIndex==slot.StrateIndex
&&room.stableId==routePrefix+"basal-landing/primitive-0";});
if(terminal==layout.cavernRooms.end())
{error="Vertical Shafts basal route is missing its terminal landing";return false;}
}
}
return layout.finalize(error);
}
inline bool ImportExportedCoreWorldAssembly(const VFJson::Value& root,
VoxelForgeCore::WorldAssemblyManifest& out,std::string& error)
{
@@ -1885,8 +2622,18 @@ 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 hasLowerStrate=std::any_of(owner.Manager->GetLayout().begin(),
owner.Manager->GetLayout().end(),[&](const FStrateSlot& candidate)
{return candidate.StrateIndex<slot.StrateIndex;});
const bool hasUpperStrate=std::any_of(owner.Manager->GetLayout().begin(),
owner.Manager->GetLayout().end(),[&](const FStrateSlot& candidate)
{return candidate.StrateIndex>slot.StrateIndex;});
const bool nativeTunnelCaves=type==ECaveGeneratorType::TunnelNetwork
&&recipeVariant!="legacy-tunnel-network"&&recipeVariant!="caves"
&&hasLowerStrate&&hasUpperStrate;
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;
@@ -1896,7 +2643,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"
@@ -2083,6 +2830,8 @@ namespace StandaloneWorldAssemblyPrototype
{
const auto passageConfigs=BuildResolvedLayoutPassageConfigs(owner);
if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false;
if(recipeVariant!="legacy-tunnel-network"&&recipeVariant!="caves"
&&!PlanOwnerTunnelNetworkCaves(owner,out,error))return false;
}
return out.finalize(error);
}