Compare commits

..
Author SHA1 Message Date
Fr0zkaandClaude Opus 5.5 f8cba7c36a research: native floating islands in the owner world (L133, stopped; kept as patch)
v9 plan binds the resolved mouths and passes layout, 3/3 interfaces, shell, ripple; the composed FI walk still
fails (2 capsule collisions where passage/0-1 and 1-2 tubes carve the island route, 40 unsupported centers, a
departure landing with 0/5 support). Owner stays on the compiled-analytic FI path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 03:40:15 +01:00
13 changed files with 606 additions and 1374 deletions

No files matched your search

@@ -496,10 +496,8 @@ namespace VoxelForgeCore
// continuous floor while relaxing the spline chord tolerance and bounding shell work. // continuous floor while relaxing the spline chord tolerance and bounding shell work.
// Version 226 unifies the passage influence reach in metres and converts it to core // Version 226 unifies the passage influence reach in metres and converts it to core
// voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max // voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max
// support width/6 when deriving that reach. Version 228 composes deterministic cave // support width/6 when deriving that reach.
// graphs from fixed passage mouths and the owner Tunnel Network settings. Version 229 inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 227;
// budgets cave floor support; 230 records a shared backbone floor profile and joint cap.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 230;
enum class FieldChannel : std::uint32_t enum class FieldChannel : std::uint32_t
{ {
@@ -894,7 +892,6 @@ namespace VoxelForgeCore
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV2 = 2; inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV2 = 2;
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV3 = 3; inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV3 = 3;
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV1 = 1; inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV1 = 1;
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV2 = 2;
inline constexpr std::uint32_t ResolvedLayoutOreVeinVocabularyVersionV1 = 1; inline constexpr std::uint32_t ResolvedLayoutOreVeinVocabularyVersionV1 = 1;
enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict }; enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict };
@@ -959,11 +956,6 @@ namespace VoxelForgeCore
std::array<std::int64_t, 3> centerMicrometres{}; std::array<std::int64_t, 3> centerMicrometres{};
std::array<std::int64_t, 3> halfExtentsMicrometres{}; std::array<std::int64_t, 3> halfExtentsMicrometres{};
std::int64_t floorMicrometres = 0; std::int64_t floorMicrometres = 0;
// Optional shared backbone profile reference. The room floor plane is
// floorMicrometres plus this Q16 XY gradient from centerMicrometres.
std::string floorProfileStableId;
std::int64_t floorProfileDistanceMicrometres = -1;
std::array<std::int32_t, 2> floorGradientQ16{};
std::int64_t smoothWidthMicrometres = 0; std::int64_t smoothWidthMicrometres = 0;
// Optional feature-to-feature union width. Zero preserves the historical // Optional feature-to-feature union width. Zero preserves the historical
// behavior where smoothWidthMicrometres also controls the graph union. // behavior where smoothWidthMicrometres also controls the graph union.
@@ -983,11 +975,6 @@ namespace VoxelForgeCore
std::int32_t strateIndex = -1; std::int32_t strateIndex = -1;
// A single cubic Bezier in XY. Its four Z values are the cubic floor profile. // A single cubic Bezier in XY. Its four Z values are the cubic floor profile.
std::array<std::array<std::int64_t, 3>, 4> controlPointsMicrometres{}; std::array<std::array<std::int64_t, 3>, 4> controlPointsMicrometres{};
// Edges with the same profile ID are adjacent pieces of one shared h(s).
// Distances are signed along that profile, so a terminal edge may be reversed.
std::string floorProfileStableId;
std::int64_t floorProfileStartDistanceMicrometres = -1;
std::int64_t floorProfileEndDistanceMicrometres = -1;
// Resolve-time, error-bounded approximation of the cubic centerline. Each // Resolve-time, error-bounded approximation of the cubic centerline. Each
// consecutive pair is a physical segment used by the native cave field. // consecutive pair is a physical segment used by the native cave field.
std::vector<std::array<std::int64_t, 3>> polylineMicrometres; std::vector<std::array<std::int64_t, 3>> polylineMicrometres;
@@ -1004,37 +991,6 @@ namespace VoxelForgeCore
std::array<std::int64_t, 3> boundsMaximumMicrometres{}; std::array<std::int64_t, 3> boundsMaximumMicrometres{};
}; };
// Inputs to the reusable, plan-time cave graph composer. All distances are
// integer micrometres and unit-interval values are Q16 so the resolved graph
// does not depend on worker count or floating-point random distributions.
struct ResolvedCavernPlannerSettingsV1
{
std::int64_t roomSpacingMicrometres = 16000000;
std::int64_t minimumRoomRadiusMicrometres = 2000000;
std::int64_t maximumRoomRadiusMicrometres = 5000000;
std::int64_t destinationRadiusMicrometres = 6000000;
// Solid floor backing below the planned edge floor, preserved after graph
// unions and passage composition. This is separate from bore height.
std::int64_t floorSupportDepthMicrometres = 750000;
std::int64_t minimumTunnelRadiusMicrometres = 750000;
std::int64_t maximumTunnelRadiusMicrometres = 1000000;
std::int64_t maximumTunnelLengthMicrometres = 45000000;
std::int64_t tunnelWarpMicrometres = 0;
std::int64_t caveWarpMicrometres = 0;
std::int64_t smoothWidthMicrometres = 500000;
std::int64_t irregularityAmplitudeMicrometres = 0;
std::int64_t irregularityWavelengthMicrometres = 0;
std::int32_t roomDensityQ16 = 22938;
std::int32_t tunnelDensityQ16 = 26214;
std::int32_t roomShapeVarietyQ16 = 32768;
std::int32_t roomHeightRatioQ16 = 55706;
std::int32_t roomFloorCutMinimumQ16 = 45875;
std::int32_t roomFloorCutMaximumQ16 = 58982;
std::int32_t tunnelHorizontalBiasQ16 = 32768;
std::int32_t originRoomMaximumConnections = 4;
bool tunnelsFlowTowardOrigin = true;
};
// Immutable material-only deposits. The capsule and its conservative bounds are in // Immutable material-only deposits. The capsule and its conservative bounds are in
// micrometres; bounds include irregularity and the blend envelope. // micrometres; bounds include irregularity and the blend envelope.
struct ResolvedLayoutOreVeinRequestV1 struct ResolvedLayoutOreVeinRequestV1
@@ -1139,7 +1095,7 @@ namespace VoxelForgeCore
inline bool IsNativeCaveFieldStratum(const ResolvedLayoutStratumV2& stratum) noexcept inline bool IsNativeCaveFieldStratum(const ResolvedLayoutStratumV2& stratum) noexcept
{ {
return (stratum.generatorKind=="Caves"||stratum.generatorKind=="TunnelNetwork") return stratum.generatorKind=="Caves"
&&stratum.recipeId=="Core.Caves.PrimitiveField" &&stratum.recipeId=="Core.Caves.PrimitiveField"
&&stratum.recipeVersion==1; &&stratum.recipeVersion==1;
} }
@@ -1300,17 +1256,6 @@ namespace VoxelForgeCore
std::span<const ResolvedCavernRoomV1> rooms, std::span<const ResolvedCavernRoomV1> rooms,
std::span<const ResolvedCavernEdgeV1> edges, std::span<const ResolvedCavernEdgeV1> edges,
std::string& error); std::string& error);
// Build a deterministic, walkable room-and-tunnel graph between the two
// passage mouths that terminate in strateIndex. Compound rooms are emitted
// as overlapping room primitives; the route is planned below a 15 degree
// walking grade and every corridor carries a 4.5 m vertical clearance.
VFCORE_API bool PlanResolvedCavernNetworkV1(
const ResolvedLayoutSnapshotV3& layout, std::int32_t seed,
std::int32_t strateIndex,
const ResolvedCavernPlannerSettingsV1& settings,
std::vector<ResolvedCavernRoomV1>& rooms,
std::vector<ResolvedCavernEdgeV1>& edges,
std::string& error);
VFCORE_API bool ResolveResolvedLayoutOreVeinsV1( VFCORE_API bool ResolveResolvedLayoutOreVeinsV1(
ResolvedLayoutSnapshotV3& layout, std::int32_t seed, ResolvedLayoutSnapshotV3& layout, std::int32_t seed,
std::span<const ResolvedLayoutOreVeinRequestV1> requests, std::span<const ResolvedLayoutOreVeinRequestV1> requests,
+2 -683
View File
@@ -621,10 +621,6 @@ namespace
<<"],\"half_extents_um\":["<<room.halfExtentsMicrometres[0]<<',' <<"],\"half_extents_um\":["<<room.halfExtentsMicrometres[0]<<','
<<room.halfExtentsMicrometres[1]<<','<<room.halfExtentsMicrometres[2] <<room.halfExtentsMicrometres[1]<<','<<room.halfExtentsMicrometres[2]
<<"],\"floor_um\":"<<room.floorMicrometres <<"],\"floor_um\":"<<room.floorMicrometres
<<",\"floor_profile_id\":"<<LayoutEscape(room.floorProfileStableId)
<<",\"floor_profile_distance_um\":"<<room.floorProfileDistanceMicrometres
<<",\"floor_gradient_q16\":["<<room.floorGradientQ16[0]<<','
<<room.floorGradientQ16[1]<<']'
<<",\"smooth_width_um\":"<<room.smoothWidthMicrometres <<",\"smooth_width_um\":"<<room.smoothWidthMicrometres
; ;
if(room.unionWidthMicrometres>0) if(room.unionWidthMicrometres>0)
@@ -651,11 +647,6 @@ namespace
out<<'['<<p[0]<<','<<p[1]<<','<<p[2]<<']'; out<<'['<<p[0]<<','<<p[1]<<','<<p[2]<<']';
} }
out<<']'; out<<']';
out<<",\"floor_profile_id\":"<<LayoutEscape(edge.floorProfileStableId)
<<",\"floor_profile_start_distance_um\":"
<<edge.floorProfileStartDistanceMicrometres
<<",\"floor_profile_end_distance_um\":"
<<edge.floorProfileEndDistanceMicrometres;
if(!edge.polylineMicrometres.empty()) if(!edge.polylineMicrometres.empty())
{ {
out<<",\"polyline_um\":["; out<<",\"polyline_um\":[";
@@ -1244,8 +1235,7 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
add("cavern_graph_vocabulary_version", add("cavern_graph_vocabulary_version",
"cavern graph vocabulary version is set without graph records"); "cavern graph vocabulary version is set without graph records");
} }
else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1 else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1)
&&cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV2)
add("cavern_graph_vocabulary_version", add("cavern_graph_vocabulary_version",
"cavern graph records require vocabulary version 1"); "cavern graph records require vocabulary version 1");
if(cavernGraphVocabularyVersion!=0) if(cavernGraphVocabularyVersion!=0)
@@ -1278,16 +1268,6 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
||(room.irregularityAmplitudeMicrometres>0 ||(room.irregularityAmplitudeMicrometres>0
&&room.irregularityWavelengthMicrometres<=0)) &&room.irregularityWavelengthMicrometres<=0))
add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters"); add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters");
const std::int64_t gradientSquared=static_cast<std::int64_t>(room.floorGradientQ16[0])
*room.floorGradientQ16[0]+static_cast<std::int64_t>(room.floorGradientQ16[1])
*room.floorGradientQ16[1];
if((room.floorProfileStableId.empty()
&&room.floorProfileDistanceMicrometres!=-1)
||(!room.floorProfileStableId.empty()
&&room.floorProfileDistanceMicrometres<0)
||gradientSquared>static_cast<std::int64_t>(13108)*13108)
add("cavern_room_floor_profile",room.stableId+
" has an invalid shared profile reference or floor grade");
const std::int64_t pad=room.smoothWidthMicrometres const std::int64_t pad=room.smoothWidthMicrometres
+room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres; +room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres;
for(std::size_t axis=0;axis<3;++axis) for(std::size_t axis=0;axis<3;++axis)
@@ -1328,15 +1308,6 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
||(edge.irregularityAmplitudeMicrometres>0 ||(edge.irregularityAmplitudeMicrometres>0
&&edge.irregularityWavelengthMicrometres<=0)) &&edge.irregularityWavelengthMicrometres<=0))
add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters"); add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters");
const bool hasFloorProfile=!edge.floorProfileStableId.empty();
if((hasFloorProfile&&(edge.floorProfileStartDistanceMicrometres<0
||edge.floorProfileEndDistanceMicrometres<0
||edge.floorProfileStartDistanceMicrometres
==edge.floorProfileEndDistanceMicrometres))
||(!hasFloorProfile&&(edge.floorProfileStartDistanceMicrometres!=-1
||edge.floorProfileEndDistanceMicrometres!=-1)))
add("cavern_edge_floor_profile",edge.stableId+
" has an invalid shared profile distance interval");
std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0]; std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0];
std::array<std::int64_t,3> high=low; std::array<std::int64_t,3> high=low;
if(nativeLattice&&!edge.polylineMicrometres.empty()) if(nativeLattice&&!edge.polylineMicrometres.empty())
@@ -1392,114 +1363,6 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
add("cavern_edge_polyline",edge.stableId+ add("cavern_edge_polyline",edge.stableId+
" has no bounded resolve-time polyline with matching endpoints"); " has no bounded resolve-time polyline with matching endpoints");
} }
std::vector<std::string> floorProfileIds;
for(const auto& edge:cavernEdges)
if(!edge.floorProfileStableId.empty()
&&std::find(floorProfileIds.begin(),floorProfileIds.end(),
edge.floorProfileStableId)==floorProfileIds.end())
floorProfileIds.push_back(edge.floorProfileStableId);
for(const std::string& profileId:floorProfileIds)
{
std::vector<const ResolvedCavernEdgeV1*> profileEdges;
for(const auto& edge:cavernEdges)
if(edge.floorProfileStableId==profileId)profileEdges.push_back(&edge);
std::sort(profileEdges.begin(),profileEdges.end(),[](const auto* a,const auto* b)
{
return std::min(a->floorProfileStartDistanceMicrometres,
a->floorProfileEndDistanceMicrometres)
<std::min(b->floorProfileStartDistanceMicrometres,
b->floorProfileEndDistanceMicrometres);
});
const auto endpointZ=[](const ResolvedCavernEdgeV1& edge,bool atMinimum)
{
const bool forward=edge.floorProfileStartDistanceMicrometres
<edge.floorProfileEndDistanceMicrometres;
const std::size_t point=(atMinimum==forward)?0:3;
return edge.controlPointsMicrometres[point][2];
};
const auto endpointSlope=[](const ResolvedCavernEdgeV1& edge,bool atMinimum)
{
const double span=static_cast<double>(edge.floorProfileEndDistanceMicrometres
-edge.floorProfileStartDistanceMicrometres);
if(span==0.0)return std::numeric_limits<double>::infinity();
const bool forward=edge.floorProfileStartDistanceMicrometres
<edge.floorProfileEndDistanceMicrometres;
const std::size_t control=atMinimum==forward?0:2;
return 3.0*(edge.controlPointsMicrometres[control+1][2]
-edge.controlPointsMicrometres[control][2])/span;
};
const ResolvedCavernEdgeV1* previous=nullptr;
for(const auto* edge:profileEdges)
{
const std::int64_t low=std::min(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
const std::int64_t high=std::max(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
const double slope0=endpointSlope(*edge,true);
const double slope1=endpointSlope(*edge,false);
if(std::max(std::abs(slope0),std::abs(slope1))>0.200001)
add("cavern_floor_profile_grade",edge->stableId+
" exceeds the shared backbone 1:5 grade limit");
if(previous)
{
const std::int64_t previousHigh=std::max(
previous->floorProfileStartDistanceMicrometres,
previous->floorProfileEndDistanceMicrometres);
if(low!=previousHigh
||std::abs(endpointZ(*previous,false)-endpointZ(*edge,true))>1000
||std::abs(endpointSlope(*previous,false)-slope0)>0.000001)
add("cavern_floor_profile_joint",profileId+
" has a gap, height step, or slope step between adjacent edges");
}
previous=edge;
(void)high;
}
for(const auto& room:cavernRooms)
{
if(room.floorProfileStableId!=profileId)continue;
const ResolvedCavernEdgeV1* containing=nullptr;
for(const auto* edge:profileEdges)
{
const auto low=std::min(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
const auto high=std::max(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
if(room.floorProfileDistanceMicrometres>=low
&&room.floorProfileDistanceMicrometres<=high)
{containing=edge;break;}
}
if(!containing)
{
add("cavern_room_floor_profile",room.stableId+
" does not lie on its referenced continuous floor profile");
continue;
}
const auto& edge=*containing;
const double t=static_cast<double>(room.floorProfileDistanceMicrometres
-edge.floorProfileStartDistanceMicrometres)
/(edge.floorProfileEndDistanceMicrometres
-edge.floorProfileStartDistanceMicrometres);
const double one=1.0-t;
const double expected=one*one*one*edge.controlPointsMicrometres[0][2]
+3.0*one*one*t*edge.controlPointsMicrometres[1][2]
+3.0*one*t*t*edge.controlPointsMicrometres[2][2]
+t*t*t*edge.controlPointsMicrometres[3][2];
std::array<double,2> routeXY{};
for(std::size_t axis=0;axis<2;++axis)
routeXY[axis]=one*one*one*edge.controlPointsMicrometres[0][axis]
+3.0*one*one*t*edge.controlPointsMicrometres[1][axis]
+3.0*one*t*t*edge.controlPointsMicrometres[2][axis]
+t*t*t*edge.controlPointsMicrometres[3][axis];
const double roomFloorAtProfile=room.floorMicrometres
+room.floorGradientQ16[0]/65536.0
*(routeXY[0]-room.centerMicrometres[0])
+room.floorGradientQ16[1]/65536.0
*(routeXY[1]-room.centerMicrometres[1]);
if(std::abs(expected-roomFloorAtProfile)>1000.0)
add("cavern_room_floor_profile",room.stableId+
" floor does not match its referenced h(s)");
}
}
} }
if(oreVeins.empty()) if(oreVeins.empty())
{ {
@@ -1663,12 +1526,6 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
readTriplet("bounds_min_um",room.boundsMinimumMicrometres); readTriplet("bounds_min_um",room.boundsMinimumMicrometres);
readTriplet("bounds_max_um",room.boundsMaximumMicrometres); readTriplet("bounds_max_um",room.boundsMaximumMicrometres);
room.floorMicrometres=LayoutInt(v,"floor_um"); room.floorMicrometres=LayoutInt(v,"floor_um");
room.floorProfileStableId=LayoutString(v,"floor_profile_id");
if(v.Get("floor_profile_distance_um").IsNumber())
room.floorProfileDistanceMicrometres=LayoutInt(v,"floor_profile_distance_um");
const auto& floorGradient=v.Get("floor_gradient_q16").AsArray();
for(std::size_t axis=0;axis<2&&axis<floorGradient.size();++axis)
room.floorGradientQ16[axis]=static_cast<std::int32_t>(floorGradient[axis].Integer());
room.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um"); room.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um");
room.unionWidthMicrometres=LayoutInt(v,"union_width_um"); room.unionWidthMicrometres=LayoutInt(v,"union_width_um");
room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um"); room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
@@ -1682,13 +1539,6 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
edge.startAnchorStableId=LayoutString(v,"start_anchor_stable_id"); edge.startAnchorStableId=LayoutString(v,"start_anchor_stable_id");
edge.endRoomStableId=LayoutString(v,"end_room_stable_id"); edge.endRoomStableId=LayoutString(v,"end_room_stable_id");
edge.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index")); edge.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index"));
edge.floorProfileStableId=LayoutString(v,"floor_profile_id");
if(v.Get("floor_profile_start_distance_um").IsNumber())
edge.floorProfileStartDistanceMicrometres=
LayoutInt(v,"floor_profile_start_distance_um");
if(v.Get("floor_profile_end_distance_um").IsNumber())
edge.floorProfileEndDistanceMicrometres=
LayoutInt(v,"floor_profile_end_distance_um");
const auto& points=v.Get("control_points_um").AsArray(); const auto& points=v.Get("control_points_um").AsArray();
for(std::size_t i=0;i<edge.controlPointsMicrometres.size()&&i<points.size();++i) for(std::size_t i=0;i<edge.controlPointsMicrometres.size()&&i<points.size();++i)
{ {
@@ -2029,7 +1879,7 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
if(!candidate.finalize(error))return false; if(!candidate.finalize(error))return false;
layout=std::move(candidate);return true; layout=std::move(candidate);return true;
} }
candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV2; candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV1;
candidate.cavernRooms.assign(rooms.begin(),rooms.end()); candidate.cavernRooms.assign(rooms.begin(),rooms.end());
candidate.cavernEdges.assign(edges.begin(),edges.end()); candidate.cavernEdges.assign(edges.begin(),edges.end());
const auto isNativeCavesStrate=[&candidate](std::int32_t strateIndex) const auto isNativeCavesStrate=[&candidate](std::int32_t strateIndex)
@@ -2181,537 +2031,6 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
layout=std::move(candidate);return true; layout=std::move(candidate);return true;
} }
bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
std::int32_t seed,std::int32_t strateIndex,
const ResolvedCavernPlannerSettingsV1& settings,
std::vector<ResolvedCavernRoomV1>& rooms,
std::vector<ResolvedCavernEdgeV1>& edges,std::string& error)
{
rooms.clear();edges.clear();error.clear();
const auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[strateIndex](const auto& value){return value.strateIndex==strateIndex;});
if(stratum==layout.strates.end()||!IsNativeCaveFieldStratum(*stratum))
{error="cavern planner target is not a resolved native cave stratum";return false;}
if(settings.roomSpacingMicrometres<=0
||settings.minimumRoomRadiusMicrometres<500000
||settings.maximumRoomRadiusMicrometres<settings.minimumRoomRadiusMicrometres
||settings.maximumRoomRadiusMicrometres>100000000
||settings.destinationRadiusMicrometres<settings.maximumRoomRadiusMicrometres
||settings.destinationRadiusMicrometres>100000000
||settings.floorSupportDepthMicrometres<250000
||settings.floorSupportDepthMicrometres>2000000
||settings.maximumTunnelLengthMicrometres<=0
||settings.minimumTunnelRadiusMicrometres<0
||settings.maximumTunnelRadiusMicrometres<settings.minimumTunnelRadiusMicrometres
||settings.smoothWidthMicrometres<0
||settings.smoothWidthMicrometres>10000000
||settings.irregularityAmplitudeMicrometres<0
||settings.irregularityAmplitudeMicrometres>10000000
||(settings.irregularityAmplitudeMicrometres>0
&&settings.irregularityWavelengthMicrometres<=0))
{error="cavern planner settings are outside the bounded room, tunnel or relief ranges";return false;}
const ResolvedLayoutPassageV1* arrivalPassage=nullptr;
const ResolvedLayoutPassageV1* departurePassage=nullptr;
for(const auto& passage:layout.passages)
{
if(passage.lowerStrateIndex==strateIndex&&passage.upperStrateIndex<strateIndex
&&(!arrivalPassage||passage.stableId<arrivalPassage->stableId))
arrivalPassage=&passage;
if(passage.upperStrateIndex==strateIndex&&passage.lowerStrateIndex>strateIndex
&&(!departurePassage||passage.stableId<departurePassage->stableId))
departurePassage=&passage;
}
if(!arrivalPassage||!departurePassage)
{error="cavern planner needs an incoming lower mouth and an outgoing upper mouth in the target strate";return false;}
if(layout.coreVoxelSizeMicrometres==0)
{error="cavern planner requires a nonzero Core voxel size";return false;}
using FPoint=std::array<std::int64_t,3>;
constexpr std::int64_t MicrometresPerMetre=1000000;
constexpr std::int64_t MinimumBoreRadius=1750000;
constexpr std::int64_t MinimumBoreHeight=2200000;
constexpr std::int64_t PlannedBoreHeight=4500000;
constexpr std::int64_t MouthOverlap=3000000;
constexpr std::int64_t MaximumBoreRadius=10000000;
constexpr std::int64_t MaximumWalkGradeDenominator=5; // 1:5 = 11.31 degrees.
const auto integerSqrt=[](std::uint64_t value)
{
std::uint64_t result=0;
std::uint64_t bit=std::uint64_t{1}<<62;
while(bit>value)bit>>=2;
while(bit!=0)
{
if(value>=result+bit)
{value-=result+bit;result=(result>>1)+bit;}
else result>>=1;
bit>>=2;
}
return result;
};
const auto distance2d=[&](const FPoint& a,const FPoint& b)
{
const std::int64_t dx=b[0]-a[0],dy=b[1]-a[1];
const auto squared=static_cast<std::uint64_t>(dx*dx+dy*dy);
return static_cast<std::int64_t>(integerSqrt(squared));
};
const auto qToMicrometres=[&](std::int64_t q)
{return q*static_cast<std::int64_t>(layout.coreVoxelSizeMicrometres)
/WorldAssemblySubvoxelsPerVoxel;};
const auto mouthInnerPoint=[&](const ResolvedLayoutPassageV1& passage,
bool lowerSide,FPoint& point)
{
for(std::size_t axis=0;axis<3;++axis)
point[axis]=qToMicrometres(lowerSide?passage.lowerMouthSubvoxels[axis]
:passage.upperMouthSubvoxels[axis]);
if(passage.controlPoints.size()<2)return false;
const std::size_t mouthIndex=lowerSide
?std::min(passage.lowerMouthControlIndex,passage.controlPoints.size()-1):0;
const std::size_t tangentIndex=lowerSide
?(mouthIndex>0?mouthIndex-1:1):1;
const std::int64_t tx=passage.controlPoints[mouthIndex].positionSubvoxels[0]
-passage.controlPoints[tangentIndex].positionSubvoxels[0];
const std::int64_t ty=passage.controlPoints[mouthIndex].positionSubvoxels[1]
-passage.controlPoints[tangentIndex].positionSubvoxels[1];
const auto tangentLength=static_cast<std::int64_t>(integerSqrt(
static_cast<std::uint64_t>(tx*tx+ty*ty)));
if(tangentLength<=0)return false;
point[0]+=tx*MouthOverlap/tangentLength;
point[1]+=ty*MouthOverlap/tangentLength;
return true;
};
FPoint routeStart{},routeEnd{};
if(!mouthInnerPoint(*arrivalPassage,true,routeStart)
||!mouthInnerPoint(*departurePassage,false,routeEnd))
{error="cavern passage mouth is missing a usable horizontal route tangent";return false;}
const std::int64_t dx=routeEnd[0]-routeStart[0];
const std::int64_t dy=routeEnd[1]-routeStart[1];
const std::int64_t dz=routeEnd[2]-routeStart[2];
const std::int64_t straightLength=distance2d(routeStart,routeEnd);
const std::int64_t horizontalBias=std::clamp<std::int64_t>(
settings.tunnelHorizontalBiasQ16,0,65536);
const std::int64_t planGradeDenominator=MaximumWalkGradeDenominator
+horizontalBias/32768;
const std::int64_t requiredLength=std::abs(dz)*planGradeDenominator+1000;
std::int64_t lateral=std::max<std::int64_t>(1000000,
std::max(settings.tunnelWarpMicrometres,settings.caveWarpMicrometres)/2);
const std::uint64_t routeHash=LayoutStampSeed(seed,strateIndex,
"tunnel-network/main-route");
if((routeHash&1u)!=0)lateral=-lateral;
std::int64_t pathAxisX=dx,pathAxisY=dy;
const auto makeRoute=[&](std::int64_t lateralOffset)
{
std::array<FPoint,4> points{};
points[0]=routeStart;points[3]=routeEnd;
std::int64_t axisX=dx,axisY=dy;
std::int64_t axisLength=straightLength;
if(axisLength<=0)
{
axisX=1;axisY=0;axisLength=1;
}
pathAxisX=axisX;pathAxisY=axisY;
const std::int64_t perpX=-axisY*lateralOffset/axisLength;
const std::int64_t perpY= axisX*lateralOffset/axisLength;
for(std::size_t axis=0;axis<3;++axis)
{
const std::int64_t delta=routeEnd[axis]-routeStart[axis];
points[1][axis]=routeStart[axis]+delta/3;
points[2][axis]=routeStart[axis]+(delta*2)/3;
}
points[1][0]+=perpX;points[1][1]+=perpY;
points[2][0]-=perpX;points[2][1]-=perpY;
return points;
};
const auto routeLength=[&](const std::array<FPoint,4>& points)
{
std::int64_t length=0;
for(std::size_t i=1;i<points.size();++i)length+=distance2d(points[i-1],points[i]);
return length;
};
auto routePoints=makeRoute(lateral);
for(int attempt=0;routeLength(routePoints)<requiredLength&&attempt<32;++attempt)
{
if(lateral>0)lateral=std::min<std::int64_t>(lateral*2,1000000000ll);
else lateral=std::max<std::int64_t>(lateral*2,-1000000000ll);
routePoints=makeRoute(lateral);
}
const std::int64_t totalRouteLength=routeLength(routePoints);
if(totalRouteLength<=0||totalRouteLength<requiredLength)
{error="cavern planner cannot fit the fixed mouth floor difference under the 1:5 grade budget";return false;}
std::array<std::int64_t,4> cumulative{};
for(std::size_t i=1;i<routePoints.size();++i)
cumulative[i]=cumulative[i-1]+distance2d(routePoints[i-1],routePoints[i]);
if(cumulative.back()!=totalRouteLength)
{error="cavern planner route length accounting failed";return false;}
const std::string backboneFloorProfileId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/backbone-floor";
const auto routeFloorAt=[&](std::int64_t distance)
{
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
return routeStart[2]+dz*distance/totalRouteLength;
};
const auto routeFloorGradientAt=[&](std::int64_t distance)
{
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
std::size_t segment=1;
while(segment<cumulative.size()-1&&distance>cumulative[segment])++segment;
const auto unitTangent=[&](std::size_t index)
{
const double tx=static_cast<double>(routePoints[index][0]
-routePoints[index-1][0]);
const double ty=static_cast<double>(routePoints[index][1]
-routePoints[index-1][1]);
const double length=std::hypot(tx,ty);
return length>0.0?std::array<double,2>{tx/length,ty/length}
:std::array<double,2>{0.0,0.0};
};
auto tangent=unitTangent(segment);
if(segment+1<cumulative.size()&&distance==cumulative[segment])
{
const auto next=unitTangent(segment+1);
tangent[0]+=next[0];tangent[1]+=next[1];
const double length=std::hypot(tangent[0],tangent[1]);
if(length>1.0e-12){tangent[0]/=length;tangent[1]/=length;}
}
constexpr double Q16=65536.0;
const double profileSlope=static_cast<double>(dz)/totalRouteLength;
return std::array<std::int32_t,2>{
static_cast<std::int32_t>(std::llround(tangent[0]*profileSlope*Q16)),
static_cast<std::int32_t>(std::llround(tangent[1]*profileSlope*Q16))};
};
const auto pointAt=[&](std::int64_t distance)
{
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
std::size_t segment=1;
while(segment<cumulative.size()-1&&distance>cumulative[segment])++segment;
const std::int64_t startDistance=cumulative[segment-1];
const std::int64_t segmentLength=std::max<std::int64_t>(1,
cumulative[segment]-startDistance);
const std::int64_t local=distance-startDistance;
FPoint point{};
for(std::size_t axis=0;axis<2;++axis)
{
const std::int64_t delta=routePoints[segment][axis]
-routePoints[segment-1][axis];
point[axis]=routePoints[segment-1][axis]+delta*local/segmentLength;
}
point[2]=routeFloorAt(distance);
return point;
};
std::int64_t spacing=std::min(std::max<std::int64_t>(settings.roomSpacingMicrometres,
4000000),settings.maximumTunnelLengthMicrometres);
spacing=std::max<std::int64_t>(4000000,spacing);
if(spacing>settings.maximumTunnelLengthMicrometres)
spacing=settings.maximumTunnelLengthMicrometres;
if(spacing<=0)
{error="cavern planner has no positive route spacing after applying tunnel length limits";return false;}
std::vector<std::int64_t> nodeDistances;
for(std::size_t leg=1;leg<routePoints.size();++leg)
{
const std::int64_t legStart=cumulative[leg-1];
const std::int64_t legLength=cumulative[leg]-legStart;
const std::int64_t segments=std::max<std::int64_t>(1,
(legLength+spacing-1)/spacing);
if(segments>256)
{error="cavern planner would exceed the 256-node bounded backbone";return false;}
for(std::int64_t part=1;part<=segments;++part)
{
const std::int64_t distance=legStart+legLength*part/segments;
if(distance>0&&distance<totalRouteLength)nodeDistances.push_back(distance);
}
}
nodeDistances.push_back(totalRouteLength/2);
std::sort(nodeDistances.begin(),nodeDistances.end());
nodeDistances.erase(std::unique(nodeDistances.begin(),nodeDistances.end()),nodeDistances.end());
if(nodeDistances.empty())nodeDistances.push_back(totalRouteLength/2);
if(nodeDistances.size()>256)
{error="cavern planner would exceed the 256-node bounded backbone";return false;}
const auto clampQ16=[](std::int32_t value)
{return std::clamp<std::int64_t>(value,0,65536);};
const std::int64_t smooth=std::clamp<std::int64_t>(
std::max<std::int64_t>(1500000,settings.smoothWidthMicrometres),
50000,10000000);
const std::int64_t irregularity=std::clamp<std::int64_t>(
settings.irregularityAmplitudeMicrometres,0,500000);
const std::int64_t routeBoreRadius=std::clamp<std::int64_t>(
std::max(MinimumBoreRadius,settings.minimumTunnelRadiusMicrometres),
MinimumBoreRadius,MaximumBoreRadius);
const std::int64_t maximumRouteBoreRadius=std::clamp<std::int64_t>(
std::max(routeBoreRadius,settings.maximumTunnelRadiusMicrometres),
routeBoreRadius,MaximumBoreRadius);
const auto selectedBoreRadius=[&](const std::string& stableId)
{
const std::uint64_t span=static_cast<std::uint64_t>(
maximumRouteBoreRadius-routeBoreRadius);
return routeBoreRadius+(span==0?0:static_cast<std::int64_t>(
LayoutStampSeed(seed,strateIndex,stableId)%(span+1)));
};
const auto passageBoreRadius=[&](const ResolvedLayoutPassageV1& passage,bool lowerSide)
{
std::int64_t radius=qToMicrometres(passage.mouthRadiusSubvoxels);
if(!passage.controlPoints.empty())
{
const auto control=lowerSide?std::min(passage.lowerMouthControlIndex,
passage.controlPoints.size()-1):std::size_t{0};
radius=std::max(radius,qToMicrometres(passage.controlPoints[control].radiusSubvoxels));
}
return std::clamp<std::int64_t>(std::max(routeBoreRadius,radius),
routeBoreRadius,MaximumBoreRadius);
};
const auto fractional=[](std::int64_t value,std::int64_t q16)
{return value*std::clamp<std::int64_t>(q16,0,65536)/65536;};
struct FRoomNode
{
std::string id;
FPoint point{};
std::string floorProfileStableId;
std::int64_t floorProfileDistanceMicrometres=-1;
std::array<std::int32_t,2> floorGradientQ16{};
std::int64_t radiusMicrometres=0;
bool destination=false;
};
std::vector<FRoomNode> nodes;nodes.reserve(nodeDistances.size());
const std::int64_t floorCutMinimum=clampQ16(settings.roomFloorCutMinimumQ16);
const std::int64_t floorCutMaximum=std::max(floorCutMinimum,
clampQ16(settings.roomFloorCutMaximumQ16));
const std::int64_t floorRange=floorCutMaximum-floorCutMinimum;
const std::int64_t heightRatio=std::clamp<std::int64_t>(
settings.roomHeightRatioQ16,6554,65536);
const std::int64_t baseRoomSpan=settings.maximumRoomRadiusMicrometres
-settings.minimumRoomRadiusMicrometres;
const std::int64_t mainRouteMidpoint=totalRouteLength/2;
for(std::size_t nodeIndex=0;nodeIndex<nodeDistances.size();++nodeIndex)
{
const std::int64_t distance=nodeDistances[nodeIndex];
FRoomNode node;node.point=pointAt(distance);
node.floorProfileStableId=backboneFloorProfileId;
node.floorProfileDistanceMicrometres=distance;
node.floorGradientQ16=routeFloorGradientAt(distance);
node.destination=distance==mainRouteMidpoint;
node.id="tunnel-network/strate-"+std::to_string(strateIndex)+"/room-"
+std::to_string(nodeIndex);
const std::uint64_t hash=LayoutStampSeed(seed,strateIndex,node.id);
node.radiusMicrometres=node.destination?settings.destinationRadiusMicrometres
:settings.minimumRoomRadiusMicrometres
+static_cast<std::int64_t>(hash%static_cast<std::uint64_t>(baseRoomSpan+1));
nodes.push_back(std::move(node));
}
const auto makeCompound=[&](const FRoomNode& node,bool destination,
std::vector<ResolvedCavernRoomV1>& outRooms)
{
const std::uint64_t hash=LayoutStampSeed(seed,strateIndex,node.id+"/compound");
const std::int64_t variety=clampQ16(settings.roomShapeVarietyQ16);
const std::size_t additionalPrimitiveCount=static_cast<std::size_t>(
variety*2/65536);
const std::size_t primitiveCount=2+static_cast<std::size_t>(
hash%(additionalPrimitiveCount+1));
std::uint64_t state=hash;
const std::int64_t radius=node.radiusMicrometres;
const std::int64_t floorCutBase=clampQ16(settings.roomFloorCutMinimumQ16);
const std::int64_t componentCount=static_cast<std::int64_t>(primitiveCount);
for(std::int64_t component=0;component<componentCount;++component)
{
const std::string stableId=node.id+"/primitive-"+std::to_string(component);
const std::uint64_t componentHash=LayoutStampNext(state);
const std::int64_t componentScaleQ16=component==0?65536
:42000+static_cast<std::int64_t>(componentHash%22001);
const std::int64_t componentRadius=std::max<std::int64_t>(500000,
fractional(radius,componentScaleQ16));
const std::int64_t axisScaleQ16=component==0?65536
:47000+static_cast<std::int64_t>((componentHash>>8)%19001);
const bool swapAxes=((componentHash>>24)&1u)!=0;
const std::int64_t halfX=swapAxes?fractional(componentRadius,axisScaleQ16)
:componentRadius;
const std::int64_t halfY=swapAxes?componentRadius
:fractional(componentRadius,axisScaleQ16);
const std::int64_t halfZ=std::max<std::int64_t>(500000,
fractional(componentRadius,heightRatio));
const std::int64_t floorCut=floorCutBase
+(floorRange==0?0:static_cast<std::int64_t>(componentHash%(
static_cast<std::uint64_t>(floorRange+1))));
const std::int64_t offsetMagnitude=component==0?0
:fractional(radius,12000+static_cast<std::int64_t>(componentHash%11001));
const std::int64_t routeLengthXY=std::max<std::int64_t>(1,
static_cast<std::int64_t>(integerSqrt(static_cast<std::uint64_t>(
pathAxisX*pathAxisX+pathAxisY*pathAxisY))));
const std::int64_t centerOffset=(component%2==0?1:-1)*offsetMagnitude;
ResolvedCavernRoomV1 room;room.stableId=stableId;
room.strateIndex=strateIndex;
room.floorProfileStableId=node.floorProfileStableId;
room.floorProfileDistanceMicrometres=node.floorProfileDistanceMicrometres;
room.floorGradientQ16=node.floorGradientQ16;
room.halfExtentsMicrometres={halfX,halfY,halfZ};
room.centerMicrometres={node.point[0]+(-pathAxisY)*centerOffset/routeLengthXY,
node.point[1]+pathAxisX*centerOffset/routeLengthXY,
node.point[2]+fractional(halfZ,floorCut)};
if(component>0)
{
const std::int64_t headroom=std::max<std::int64_t>(0,
halfZ-fractional(halfZ,floorCut));
const std::int64_t jitterRange=std::min(headroom,halfZ/5);
const std::int64_t ceilingJitter=jitterRange>0
?static_cast<std::int64_t>((componentHash>>32)
%static_cast<std::uint64_t>(jitterRange+1)):0;
room.centerMicrometres[2]+=ceilingJitter;
}
room.floorMicrometres=node.point[2]
-(static_cast<std::int64_t>(room.floorGradientQ16[0])
*(room.centerMicrometres[0]-node.point[0])
+static_cast<std::int64_t>(room.floorGradientQ16[1])
*(room.centerMicrometres[1]-node.point[1]))/65536;
room.smoothWidthMicrometres=smooth;
room.irregularityAmplitudeMicrometres=irregularity;
room.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
outRooms.push_back(std::move(room));
}
(void)destination;
};
for(const auto& node:nodes)makeCompound(node,node.destination,rooms);
const auto componentRoomId=[](const FRoomNode& node)
{return node.id+"/primitive-0";};
const auto makeEdge=[&](std::string id,std::string startAnchor,
const FPoint& start,const FRoomNode& endNode,std::int64_t boreRadius,
std::int64_t profileStartDistance,std::int64_t profileEndDistance)
{
ResolvedCavernEdgeV1 edge;edge.stableId=std::move(id);
edge.startAnchorStableId=std::move(startAnchor);
edge.endRoomStableId=componentRoomId(endNode);edge.strateIndex=strateIndex;
edge.boreRadiusMicrometres=boreRadius;
edge.boreHeightMicrometres=std::max(PlannedBoreHeight,MinimumBoreHeight);
edge.floorSupportDepthMicrometres=settings.floorSupportDepthMicrometres;
if(profileStartDistance>=0&&profileEndDistance>=0)
{
edge.floorProfileStableId=backboneFloorProfileId;
edge.floorProfileStartDistanceMicrometres=profileStartDistance;
edge.floorProfileEndDistanceMicrometres=profileEndDistance;
}
edge.smoothWidthMicrometres=smooth;
edge.irregularityAmplitudeMicrometres=irregularity;
edge.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
const FPoint end={endNode.point[0],endNode.point[1],endNode.point[2]};
for(std::size_t axis=0;axis<3;++axis)
{
const std::int64_t delta=end[axis]-start[axis];
edge.controlPointsMicrometres[0][axis]=start[axis];
edge.controlPointsMicrometres[1][axis]=start[axis]+delta/3;
edge.controlPointsMicrometres[2][axis]=start[axis]+(delta*2)/3;
edge.controlPointsMicrometres[3][axis]=end[axis];
}
if(profileStartDistance>=0&&profileEndDistance>=0)
{
const std::int64_t floorStart=routeFloorAt(profileStartDistance);
const std::int64_t floorEnd=routeFloorAt(profileEndDistance);
const std::int64_t floorDelta=floorEnd-floorStart;
edge.controlPointsMicrometres[0][2]=floorStart;
edge.controlPointsMicrometres[1][2]=floorStart+floorDelta/3;
edge.controlPointsMicrometres[2][2]=floorStart+(floorDelta*2)/3;
edge.controlPointsMicrometres[3][2]=floorEnd;
}
return edge;
};
const auto arrivalPoint=routeStart;
FPoint previousPoint=arrivalPoint;
std::int64_t previousProfileDistance=0;
std::string previousAnchor=arrivalPassage->stableId+"/lower-mouth";
for(std::size_t nodeIndex=0;nodeIndex<nodes.size();++nodeIndex)
{
const auto& node=nodes[nodeIndex];
const bool portal=nodeIndex==0;
const std::string edgeId="tunnel-network/strate-"+std::to_string(strateIndex)
+"/route-edge-"+std::to_string(nodeIndex);
edges.push_back(makeEdge(edgeId,previousAnchor,previousPoint,node,
portal?passageBoreRadius(*arrivalPassage,true):selectedBoreRadius(edgeId),
previousProfileDistance,node.floorProfileDistanceMicrometres));
previousPoint={node.point[0],node.point[1],node.point[2]};
previousProfileDistance=node.floorProfileDistanceMicrometres;
previousAnchor=componentRoomId(node);
}
if(!nodes.empty())
{
const std::string departureEdgeId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/departure-edge";
edges.push_back(makeEdge(departureEdgeId,departurePassage->stableId+"/upper-mouth",
routeEnd,nodes.back(),passageBoreRadius(*departurePassage,false),
totalRouteLength,nodes.back().floorProfileDistanceMicrometres));
}
const std::int64_t roomDensity=clampQ16(settings.roomDensityQ16);
const std::int64_t tunnelDensity=clampQ16(settings.tunnelDensityQ16);
const std::int64_t branchThreshold=roomDensity*tunnelDensity/65536;
const std::size_t branchLimit=settings.originRoomMaximumConnections<=0
?32u:static_cast<std::size_t>(std::clamp(settings.originRoomMaximumConnections,0,32));
std::size_t branchCount=0;
for(std::size_t nodeIndex=1;nodeIndex+1<nodes.size()
&&branchCount<branchLimit&&rooms.size()<2048;++nodeIndex)
{
const auto& parent=nodes[nodeIndex];
const std::uint64_t branchHash=LayoutStampSeed(seed,strateIndex,
parent.id+"/side-branch");
if(static_cast<std::int64_t>(branchHash&0xffffu)>=branchThreshold)continue;
const std::int64_t branchLength=std::max<std::int64_t>(
routeBoreRadius*3,std::min(spacing/2,settings.maximumTunnelLengthMicrometres/2));
const std::int64_t direction=(branchHash&0x10000u)?1:-1;
FRoomNode branch;branch.id=parent.id+"/side-"+std::to_string(branchCount);
branch.radiusMicrometres=settings.minimumRoomRadiusMicrometres
+static_cast<std::int64_t>((branchHash>>17)
%static_cast<std::uint64_t>(baseRoomSpan+1));
const std::int64_t pathAxisLength=std::max<std::int64_t>(1,
static_cast<std::int64_t>(integerSqrt(static_cast<std::uint64_t>(
pathAxisX*pathAxisX+pathAxisY*pathAxisY))));
branch.point={parent.point[0]+(-pathAxisY)*branchLength/pathAxisLength*direction,
parent.point[1]+pathAxisX*branchLength/pathAxisLength*direction,
parent.point[2]};
branch.floorProfileStableId=parent.floorProfileStableId;
branch.floorProfileDistanceMicrometres=parent.floorProfileDistanceMicrometres;
branch.floorGradientQ16=parent.floorGradientQ16;
makeCompound(branch,false,rooms);
const std::string branchEdgeId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/side-edge-"+std::to_string(branchCount);
ResolvedCavernEdgeV1 branchEdge;
if(settings.tunnelsFlowTowardOrigin)
branchEdge=makeEdge(branchEdgeId,componentRoomId(branch),
branch.point,parent,selectedBoreRadius(branchEdgeId),-1,-1);
else
branchEdge=makeEdge(branchEdgeId,componentRoomId(parent),
parent.point,branch,selectedBoreRadius(branchEdgeId),-1,-1);
// Side branches retain their cave floor SDF, but they do not cap the
// descending backbone when their tubes overlap it near a junction.
branchEdge.floorSupportDepthMicrometres=0;
edges.push_back(std::move(branchEdge));
++branchCount;
}
if(nodes.empty()||rooms.empty()||edges.empty())
{error="cavern planner emitted an empty arrival-to-departure graph";return false;}
double maximumGradeDegrees=0.0;
for(const auto& edge:edges)
{
const auto& a=edge.controlPointsMicrometres.front();
const auto& b=edge.controlPointsMicrometres.back();
const std::int64_t run=static_cast<std::int64_t>(integerSqrt(
static_cast<std::uint64_t>((b[0]-a[0])*(b[0]-a[0])
+(b[1]-a[1])*(b[1]-a[1]))));
const std::int64_t rise=std::abs(b[2]-a[2]);
if(run<=0&&rise>0)
{error="cavern planner produced a vertical route edge with no walking run";return false;}
if(run>0&&rise*MaximumWalkGradeDenominator>run)
{error="cavern planner emitted an edge beyond its 1:5 route grade budget";return false;}
if(run>0)maximumGradeDegrees=std::max(maximumGradeDegrees,
std::atan2(static_cast<double>(rise),static_cast<double>(run))
*180.0/3.14159265358979323846);
}
std::printf("[CavernPlannerV1] strate=%d seed=%d mouths=%s->%s route_nodes=%zu room_primitives=%zu edges=%zu branches=%zu route_horizontal_m=%.3f floor_delta_m=%.3f max_grade_deg=%.3f grade_limit_deg=11.310 clearance_radius_m=%.3f clearance_height_m=%.3f destination_radius_m=%.3f status=PASS\n",
strateIndex,seed,arrivalPassage->stableId.c_str(),departurePassage->stableId.c_str(),
nodes.size(),rooms.size(),edges.size(),branchCount,totalRouteLength/1.0e6,
std::abs(dz)/1.0e6,maximumGradeDegrees,routeBoreRadius/1.0e6,PlannedBoreHeight/1.0e6,
settings.destinationRadiusMicrometres/1.0e6);
return true;
}
bool ResolveResolvedLayoutOreVeinsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed, bool ResolveResolvedLayoutOreVeinsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed,
std::span<const ResolvedLayoutOreVeinRequestV1> requests,std::string& error) std::span<const ResolvedLayoutOreVeinRequestV1> requests,std::string& error)
{ {
+46 -117
View File
@@ -1221,15 +1221,16 @@ public:
double unionDistance=std::numeric_limits<double>::infinity(); double unionDistance=std::numeric_limits<double>::infinity();
double unionShellDistance=std::numeric_limits<double>::infinity(); double unionShellDistance=std::numeric_limits<double>::infinity();
const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0); const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
const auto* queryStratum=FindBoundStratumForManagerZ(Z);
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size(); for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
++passageIndex) ++passageIndex)
{ {
const auto& passage=ResolvedLayout->passages[passageIndex]; const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue; if(passage.controlPoints.size()<2)continue;
// Native graph fields own their local rooms and tunnels, not the if(queryStratum&&queryStratum->Resolved
// inter-strate connection above their mouth. Union the shared passage &&queryStratum->Resolved->passageCapabilities.hasNativeCaveField
// field here as well so the connector remains open through the boundary &&passage.lowerStrateIndex==queryStratum->StrateIndex)
// band for both native caves and future native lattice layers. continue;
if(passageProfile)++passageProfile->passageIterations; if(passageProfile)++passageProfile->passageIterations;
if(passageTileScopeMatches&&!passageTileQueries->hasRouteFeature(passageIndex)) if(passageTileScopeMatches&&!passageTileQueries->hasRouteFeature(passageIndex))
continue; continue;
@@ -1513,13 +1514,13 @@ public:
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
{ {
float graphDensity=0.0f; float graphDensity=0.0f;
float nativeGraphFloorSupport=std::numeric_limits<float>::lowest(); float nativeLatticeFloorSupport=std::numeric_limits<float>::lowest();
{ {
VoxelForgeCore::Linux77Profiler::FScopedPhase l77GraphPhase( VoxelForgeCore::Linux77Profiler::FScopedPhase l77GraphPhase(
VoxelForgeCore::Linux77Profiler::EPhase::CompositionGraph); VoxelForgeCore::Linux77Profiler::EPhase::CompositionGraph);
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerCavern); FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerCavern);
graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z, graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z,
&nativeGraphFloorSupport); &nativeLatticeFloorSupport);
} }
const auto* bound=FindBoundStratumForManagerZ(Z); const auto* bound=FindBoundStratumForManagerZ(Z);
const bool nativeLattice=bound&&bound->Resolved const bool nativeLattice=bound&&bound->Resolved
@@ -1573,7 +1574,7 @@ public:
const float nativeTunnelFieldDensity=nativeTunnelField const float nativeTunnelFieldDensity=nativeTunnelField
?graphDensity:std::numeric_limits<float>::lowest(); ?graphDensity:std::numeric_limits<float>::lowest();
stamped=ApplyResolvedStratePassageCarves(stamped,X,Y,Z, stamped=ApplyResolvedStratePassageCarves(stamped,X,Y,Z,
nativeTunnelFieldDensity,nativeGraphFloorSupport); nativeTunnelFieldDensity,nativeLatticeFloorSupport);
} }
if(bTraceTnComposition) if(bTraceTnComposition)
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g native=1\n", std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g native=1\n",
@@ -3583,7 +3584,6 @@ private:
double minimumFloorMetres=std::numeric_limits<double>::infinity(); double minimumFloorMetres=std::numeric_limits<double>::infinity();
double maximumFloorMetres=-std::numeric_limits<double>::infinity(); double maximumFloorMetres=-std::numeric_limits<double>::infinity();
double maximumSegmentGrade=0.0; double maximumSegmentGrade=0.0;
std::array<double,2> floorGradient{};
std::array<float,3> centerVoxels{},halfExtentsVoxels{}; std::array<float,3> centerVoxels{},halfExtentsVoxels{};
std::array<std::array<float,3>,4> controlPointsVoxels{}; std::array<std::array<float,3>,4> controlPointsVoxels{};
float floorVoxels=0.0f; float floorVoxels=0.0f;
@@ -4012,10 +4012,8 @@ private:
{ {
CavernRuntime.clear();CavernSpatialNodes.clear();CavernSpatialOrder.clear(); CavernRuntime.clear();CavernSpatialNodes.clear();CavernSpatialOrder.clear();
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0 if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0
||(ResolvedLayout->cavernGraphVocabularyVersion ||ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1 !=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1)return;
&&ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV2))return;
const auto isNativeGraphStrate=[this](std::int32_t strateIndex) const auto isNativeGraphStrate=[this](std::int32_t strateIndex)
{ {
const auto found=std::find_if(ResolvedLayout->strates.begin(), const auto found=std::find_if(ResolvedLayout->strates.begin(),
@@ -4045,8 +4043,6 @@ private:
} }
runtime.floorVoxels=static_cast<float>( runtime.floorVoxels=static_cast<float>(
static_cast<double>(room.floorMicrometres)/micronsPerVoxel); static_cast<double>(room.floorMicrometres)/micronsPerVoxel);
runtime.floorGradient={room.floorGradientQ16[0]/65536.0,
room.floorGradientQ16[1]/65536.0};
runtime.smoothWidthVoxels=static_cast<float>( runtime.smoothWidthVoxels=static_cast<float>(
static_cast<double>(room.smoothWidthMicrometres)/micronsPerVoxel); static_cast<double>(room.smoothWidthMicrometres)/micronsPerVoxel);
runtime.irregularityAmplitudeVoxels=static_cast<float>( runtime.irregularityAmplitudeVoxels=static_cast<float>(
@@ -5289,7 +5285,11 @@ public:
FMetricInterval caveVoid{std::numeric_limits<double>::infinity(), FMetricInterval caveVoid{std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity(),true}; std::numeric_limits<double>::infinity(),true};
bool haveFeature=false; bool haveFeature=false;
double nativeFloorSupportLowerBound=std::numeric_limits<double>::infinity(); double latticeFloorSupportLowerBound=std::numeric_limits<double>::infinity();
const auto* resolvedStratum=FindResolvedLayoutStratum(strateIndex);
const bool nativeLattice=resolvedStratum
&&VoxelForgeCore::IsNativeLatticeFieldStratum(*resolvedStratum)
&&resolvedStratum->passageCapabilities.hasNativeLatticeField;
for(const std::uint32_t index:candidates) for(const std::uint32_t index:candidates)
{ {
if(index>=CavernRuntime.size())continue; if(index>=CavernRuntime.size())continue;
@@ -5319,18 +5319,7 @@ public:
(std::sqrt(normalizedMinimumSquared)-1.0)*meanRadius, (std::sqrt(normalizedMinimumSquared)-1.0)*meanRadius,
(std::sqrt(normalizedMaximumSquared)-1.0)*meanRadius,true}; (std::sqrt(normalizedMaximumSquared)-1.0)*meanRadius,true};
const double floorZ=feature.room->floorMicrometres/1000000.0; const double floorZ=feature.room->floorMicrometres/1000000.0;
const double centerX=feature.room->centerMicrometres[0]/1000000.0; const FMetricInterval floor={floorZ-hi[2],floorZ-lo[2],true};
const double centerY=feature.room->centerMicrometres[1]/1000000.0;
const double gx=feature.room->floorGradientQ16[0]/65536.0;
const double gy=feature.room->floorGradientQ16[1]/65536.0;
const double x0=lo[0]-centerX,x1=hi[0]-centerX;
const double y0=lo[1]-centerY,y1=hi[1]-centerY;
const double gradientMinimum=(gx>=0.0?gx*x0:gx*x1)
+(gy>=0.0?gy*y0:gy*y1);
const double gradientMaximum=(gx>=0.0?gx*x1:gx*x0)
+(gy>=0.0?gy*y1:gy*y0);
const FMetricInterval floor={floorZ-hi[2]+gradientMinimum,
floorZ-lo[2]+gradientMaximum,true};
const double width=feature.room->smoothWidthMicrometres/1000000.0; const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width), featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width),
SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true}; SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true};
@@ -5452,19 +5441,21 @@ public:
SmoothMaxMetres(tube.maximum,floor.maximum,width),true}; SmoothMaxMetres(tube.maximum,floor.maximum,width),true};
} }
else return {0.0,0.0,false}; else return {0.0,0.0,false};
if(feature.edge) if(nativeLattice)
{ {
const double supportDepth= const double supportDepth=feature.edge
feature.edge->floorSupportDepthMicrometres/1000000.0; ?feature.edge->floorSupportDepthMicrometres/1000000.0:0.0;
const double floorMinimum=feature.minimumFloorMetres; const double floorMinimum=feature.edge
const double floorMaximum=feature.maximumFloorMetres; ?feature.minimumFloorMetres:std::numeric_limits<double>::infinity();
const double floorMaximum=feature.edge
?feature.maximumFloorMetres:-std::numeric_limits<double>::infinity();
const bool overlapsXY=hi[0]>=feature.boundsMinimumMicrometres[0]/1.0e6 const bool overlapsXY=hi[0]>=feature.boundsMinimumMicrometres[0]/1.0e6
&&lo[0]<=feature.boundsMaximumMicrometres[0]/1.0e6 &&lo[0]<=feature.boundsMaximumMicrometres[0]/1.0e6
&&hi[1]>=feature.boundsMinimumMicrometres[1]/1.0e6 &&hi[1]>=feature.boundsMinimumMicrometres[1]/1.0e6
&&lo[1]<=feature.boundsMaximumMicrometres[1]/1.0e6; &&lo[1]<=feature.boundsMaximumMicrometres[1]/1.0e6;
if(supportDepth>0.0&&overlapsXY if(supportDepth>0.0&&overlapsXY
&&hi[2]>=floorMinimum-supportDepth&&lo[2]<=floorMaximum) &&hi[2]>=floorMinimum-supportDepth&&lo[2]<=floorMaximum)
nativeFloorSupportLowerBound=std::min(nativeFloorSupportLowerBound, latticeFloorSupportLowerBound=std::min(latticeFloorSupportLowerBound,
std::max(-supportDepth,lo[2]-floorMaximum)); std::max(-supportDepth,lo[2]-floorMaximum));
} }
if(!std::isfinite(featureVoid.minimum)||!std::isfinite(featureVoid.maximum)) if(!std::isfinite(featureVoid.minimum)||!std::isfinite(featureVoid.maximum))
@@ -5493,11 +5484,11 @@ public:
result.minimum=std::max(HostRockMetres,-caveVoid.maximum); result.minimum=std::max(HostRockMetres,-caveVoid.maximum);
result.maximum=std::max(HostRockMetres,-caveVoid.minimum); result.maximum=std::max(HostRockMetres,-caveVoid.minimum);
} }
// Native graph edges may own a support cap after feature union. // The final lattice field owns an extra support cap after feature union.
// Lower only the interval minimum when a box could touch that cap; the // Lower only the interval minimum when a box could touch that cap; the
// untouched maximum remains a sound bound when only part of the box does. // untouched maximum remains a sound bound when only part of the box does.
if(std::isfinite(nativeFloorSupportLowerBound)) if(std::isfinite(latticeFloorSupportLowerBound))
result.minimum=std::min(result.minimum,nativeFloorSupportLowerBound); result.minimum=std::min(result.minimum,latticeFloorSupportLowerBound);
// Cover floating-point rounding in interval endpoints and the field's fixed-point // Cover floating-point rounding in interval endpoints and the field's fixed-point
// coordinate conversion while keeping the interval tight compared with one micron. // coordinate conversion while keeping the interval tight compared with one micron.
constexpr double OutwardPaddingMetres=1.0e-6; constexpr double OutwardPaddingMetres=1.0e-6;
@@ -7475,11 +7466,10 @@ private:
candidates=&localCandidates; candidates=&localCandidates;
} }
double caveVoid=std::numeric_limits<double>::infinity(); double caveVoid=std::numeric_limits<double>::infinity();
double nativeFloorSupportMinimum=std::numeric_limits<double>::infinity(); double latticeFloorSupportMinimum=std::numeric_limits<double>::infinity();
double floorSupportScore=std::numeric_limits<double>::infinity(); const bool nativeLattice=bound.Resolved
double floorSupportDepth=0.0,floorSupportFloor=0.0,floorSupportGrade=0.0; &&VoxelForgeCore::IsNativeLatticeFieldStratum(*bound.Resolved)
const std::string* floorSupportEdgeId=nullptr; &&bound.Resolved->passageCapabilities.hasNativeLatticeField;
const std::string* floorSupportProfileId=nullptr;
for(const std::uint32_t index:*candidates) for(const std::uint32_t index:*candidates)
{ {
if(index>=CavernRuntime.size())continue; if(index>=CavernRuntime.size())continue;
@@ -7498,19 +7488,8 @@ private:
const double delta=(point[axis]-feature.centerMetres[axis])/radius; const double delta=(point[axis]-feature.centerMetres[axis])/radius;
normalizedSquared+=delta*delta;meanRadius+=radius/3.0; normalizedSquared+=delta*delta;meanRadius+=radius/3.0;
} }
double ellipsoid=(std::sqrt(normalizedSquared)-1.0)*meanRadius; const double ellipsoid=(std::sqrt(normalizedSquared)-1.0)*meanRadius;
if(feature.irregularityAmplitudeVoxels>0.0f) const double floor=feature.room->floorMicrometres/1000000.0-point[2];
{
const float fade=SmoothUnit((Z-feature.floorVoxels)/0.8f);
ellipsoid+=StampValueNoise(
X-feature.centerVoxels[0],Y-feature.centerVoxels[1],
Z-feature.centerVoxels[2],
feature.irregularityWavelengthVoxels,feature.irregularitySeed)
*feature.irregularityAmplitudeVoxels*metresPerVoxel*fade;
}
const double floor=feature.room->floorMicrometres/1000000.0-point[2]
+feature.floorGradient[0]*(point[0]-feature.centerMetres[0])
+feature.floorGradient[1]*(point[1]-feature.centerMetres[1]);
const double width=feature.room->smoothWidthMicrometres/1000000.0; const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(ellipsoid,floor,width); featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
} }
@@ -7547,17 +7526,7 @@ private:
} }
const double normalized=std::sqrt(radialSquared/(radius*radius) const double normalized=std::sqrt(radialSquared/(radius*radius)
+vertical*vertical/(height*height)); +vertical*vertical/(height*height));
double distance=(normalized-1.0)*std::min(radius,height); const double distance=(normalized-1.0)*std::min(radius,height);
if(feature.irregularityAmplitudeVoxels>0.0f)
{
const float floorVoxels=static_cast<float>(nearestFloor/metresPerVoxel);
const float fade=SmoothUnit((Z-floorVoxels)/0.8f);
distance+=StampValueNoise(
X-feature.controlPointsVoxels[0][0],
Y-feature.controlPointsVoxels[0][1],Z-floorVoxels,
feature.irregularityWavelengthVoxels,feature.irregularitySeed)
*feature.irregularityAmplitudeVoxels*metresPerVoxel*fade;
}
const bool clearlyCloser=distance<tube-SegmentDistanceTieToleranceMetres; const bool clearlyCloser=distance<tube-SegmentDistanceTieToleranceMetres;
const bool stableTie=std::abs(distance-tube) const bool stableTie=std::abs(distance-tube)
<=SegmentDistanceTieToleranceMetres&&segmentIndex<nearestSegment; <=SegmentDistanceTieToleranceMetres&&segmentIndex<nearestSegment;
@@ -7611,47 +7580,11 @@ private:
} }
else for(std::uint32_t segment=0;segment<feature.segments.size();++segment) else for(std::uint32_t segment=0;segment<feature.segments.size();++segment)
evaluateSegment(segment); evaluateSegment(segment);
const double candidateSupportDepth= const double supportDepth=feature.edge->floorSupportDepthMicrometres/1000000.0;
feature.edge->floorSupportDepthMicrometres/1000000.0; if(nativeLattice&&supportDepth>0.0&&nearestRadial<radius-0.12
if(candidateSupportDepth>0.0&&nearestRadial<radius-0.12 &&point[2]<nearestFloor&&nearestFloor-point[2]<=supportDepth)
&&std::abs(point[2]-nearestFloor)<=height) latticeFloorSupportMinimum=std::min(latticeFloorSupportMinimum,
{ (point[2]-nearestFloor)/std::sqrt(1.0+nearestGrade*nearestGrade));
const double deltaZ=point[2]-nearestFloor;
const double candidateScore=nearestRadial*nearestRadial+deltaZ*deltaZ;
if(!feature.edge->floorProfileStableId.empty())
{
const bool sameProfile=floorSupportProfileId
&&*floorSupportProfileId==feature.edge->floorProfileStableId;
if(!floorSupportProfileId||sameProfile)
{
// Backbone edges share h(s). At a geometric joint their
// cave volumes overlap, so the backing top is the lower
// of the local floors, never the closer higher segment.
if(!floorSupportProfileId||nearestFloor<floorSupportFloor
||(nearestFloor==floorSupportFloor
&&feature.edge->stableId<*floorSupportEdgeId))
{
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
}
floorSupportProfileId=&feature.edge->floorProfileStableId;
}
}
else if(!floorSupportProfileId
&&(candidateScore<floorSupportScore
||(candidateScore==floorSupportScore
&&(!floorSupportEdgeId
||feature.edge->stableId<*floorSupportEdgeId))))
{
floorSupportScore=candidateScore;
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
}
}
const double floor=(nearestFloor-point[2]) const double floor=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade); /std::sqrt(1.0+nearestGrade*nearestGrade);
const double width=feature.edge->smoothWidthMicrometres/1000000.0; const double width=feature.edge->smoothWidthMicrometres/1000000.0;
@@ -7668,10 +7601,6 @@ private:
?feature.edge->unionWidthMicrometres ?feature.edge->unionWidthMicrometres
:feature.edge->smoothWidthMicrometres)/1000000.0); :feature.edge->smoothWidthMicrometres)/1000000.0);
} }
if(std::isfinite(floorSupportScore)&&point[2]<floorSupportFloor
&&floorSupportFloor-point[2]<=floorSupportDepth)
nativeFloorSupportMinimum=(point[2]-floorSupportFloor)
/std::sqrt(1.0+floorSupportGrade*floorSupportGrade);
// A versioned native caves recipe owns a solid host-rock field. The // A versioned native caves recipe owns a solid host-rock field. The
// resolved interval oracle uses the same finite -32 m solid value; using // resolved interval oracle uses the same finite -32 m solid value; using
// the underlying legacy generator density here would leak its unrelated // the underlying legacy generator density here would leak its unrelated
@@ -7680,13 +7609,13 @@ private:
const double hostDistance=HostRockMetres; const double hostDistance=HostRockMetres;
double composed=std::isfinite(caveVoid) double composed=std::isfinite(caveVoid)
?SmoothMaxMetres(hostDistance,-caveVoid,0.0):hostDistance; ?SmoothMaxMetres(hostDistance,-caveVoid,0.0):hostDistance;
// Preserve each native graph edge's planned floor backing after smooth // Keep the floor support owned by the lattice route after smooth-unioning
// unions. The record's finite depth keeps independent routes from sealing // neighboring room and corridor voids. This cap is limited to each record's
// one another when they pass at different elevations. // explicit support depth so separated helix turns remain independent.
if(std::isfinite(nativeFloorSupportMinimum)) if(std::isfinite(latticeFloorSupportMinimum))
composed=std::min(composed,nativeFloorSupportMinimum); composed=std::min(composed,latticeFloorSupportMinimum);
if(nativeFloorSupport&&std::isfinite(nativeFloorSupportMinimum)) if(nativeFloorSupport&&std::isfinite(latticeFloorSupportMinimum))
*nativeFloorSupport=static_cast<float>(nativeFloorSupportMinimum/metresPerVoxel); *nativeFloorSupport=static_cast<float>(latticeFloorSupportMinimum/metresPerVoxel);
return static_cast<float>(composed/metresPerVoxel); return static_cast<float>(composed/metresPerVoxel);
} }
@@ -0,0 +1,7 @@
# L133 — native floating islands
Stopped after the second route design. The native v9 plan binds the owner’s resolved mouths and passes raw route audit, strict layout (0 issues), 3/3 interface audits, shell (0 floor violations), and ripple (3/3). The candidate dispatches FI natively.
The composed FI walk still fails: 2 capsule collisions, 40 unsupported centers, 0 grade failures; mouth binding passes. The return lane is carved by `passage/0-1/0` near `(19.755, 9.605, -733.553) m` (4.25 m from its 4.5 m tube). At `passage/1-2/0`, the approach has collisions at `(14.402, -10.552, -732.556) m` and `(14.242, -10.712, -732.556) m`; the departure landing probe `(19.166, -14.650, -733.331) m` gets 0/5 support probes. The exact endpoints alone are not enough to place a route clear of both composed passage volumes and retain a supported landing.
No captures, timing matrix, compiler determinism matrix, baseline update, or UE host build were run after the required composed walk gate failed. The owner’s default import remains on its legacy FI fallback. Logs and candidate fixtures are under `Saved/Linux/l133/`.
@@ -0,0 +1,511 @@
diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
index cb2b2dd..5a94ab8 100644
--- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
+++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
@@ -496,8 +496,9 @@ namespace VoxelForgeCore
// continuous floor while relaxing the spline chord tolerance and bounding shell work.
// Version 226 unifies the passage influence reach in metres and converts it to core
// voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max
- // support width/6 when deriving that reach.
- inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 227;
+ // support width/6 when deriving that reach. Version 228 permits the native FI route
+ // planner to bind its fixed endpoints to the resolved passage mouths.
+ inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 228;
enum class FieldChannel : std::uint32_t
{
@@ -788,6 +789,15 @@ namespace VoxelForgeCore
std::uint64_t contentHash = 0;
};
+ // The two resolved inter-strate walk points owned by a FloatingIslands slot.
+ // The route planner treats these as fixed player-capsule centers and places
+ // the island chain between them.
+ struct FloatingIslandRouteEndpointsV1
+ {
+ std::array<std::int64_t, 3> arrivalSubvoxels{};
+ std::array<std::int64_t, 3> departureSubvoxels{};
+ };
+
struct WorldAssemblyFeatureGroupBoundsV1
{
std::array<std::int64_t, 3> minimumSubvoxels{};
@@ -1377,6 +1387,9 @@ namespace VoxelForgeCore
// Planner output is ordinary reviewed data; clients can load it without re-running the planner.
VFCORE_API std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
const FloatingIslandRecipeV1& recipe, std::string& error);
+ VFCORE_API std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
+ const FloatingIslandRecipeV1& recipe,
+ const FloatingIslandRouteEndpointsV1& endpoints, std::string& error);
VFCORE_API FloatingIslandBoundsAuditResult AuditFloatingIslandBounds(
const FloatingIslandFieldV1& field, std::uint32_t boxCount = 256,
std::uint64_t seed = 0x49534c414e444231ull);
diff --git a/Standalone/Core/src/FloatingIslandsInternal.h b/Standalone/Core/src/FloatingIslandsInternal.h
index 33d97d6..42f855a 100644
--- a/Standalone/Core/src/FloatingIslandsInternal.h
+++ b/Standalone/Core/src/FloatingIslandsInternal.h
@@ -285,7 +285,7 @@ namespace floating_island_detail
? recipe.ownerVoxelSizeMicrons / ScaleGcd : 0;
const std::uint32_t ExpectedDenominator = ScaleGcd > 0
? recipe.coreVoxelSizeMicrons / ScaleGcd : 0;
- if (recipe.schemaVersion != 1 || recipe.geometryVersion < 1 || recipe.geometryVersion > 8
+ if (recipe.schemaVersion != 1 || recipe.geometryVersion < 1 || recipe.geometryVersion > 9
|| (recipe.geometryVersion == 1
&& (recipe.crustDepthBonusQ16 != 0 || recipe.rootDepthRatioQ16 != 0))
|| recipe.unitsPerVoxel != Q
@@ -1473,7 +1473,8 @@ namespace floating_island_detail
static_cast<double>(anchorIsland->centerSubvoxels[0]),
static_cast<double>(anchorIsland->centerSubvoxels[1]));
if(anchorDistance>recipe.anchorPlacementRadiusSubvoxels
- ||recipe.anchorRootBottomZSubvoxels>=anchorIsland->bottomZSubvoxels
+ ||(recipe.geometryVersion<8
+ &&recipe.anchorRootBottomZSubvoxels>=anchorIsland->bottomZSubvoxels)
||recipe.anchorMouthOffsetSubvoxels+recipe.routeConstraints.capsuleRadiusSubvoxels
>=anchorIsland->radiusSubvoxels)
{error="floating-island anchor island or its mouth leaves the passage radius or rock envelope";return false;}
@@ -1607,7 +1608,7 @@ namespace floating_island_detail
||to.ordinal==recipe.anchorIslandOrdinal);
const bool westernSupportConnector=WorldAssemblyEvaluatorVersion>=178
&&((from.ordinal==0&&to.ordinal==1)||(from.ordinal==1&&to.ordinal==0));
- const double routeDeckLift=recipe.geometryVersion>=2
+ const double routeDeckLift=recipe.geometryVersion>=9?0.0:recipe.geometryVersion>=2
?(WorldAssemblyEvaluatorVersion>=178
?(westernSupportConnector
?(WorldAssemblyEvaluatorVersion>=181?-1.0
@@ -2264,6 +2265,335 @@ std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
return manifest;
}
+std::optional<FloatingIslandManifestV1> PlanFloatingIslandCluster(
+ const FloatingIslandRecipeV1& inputRecipe,
+ const FloatingIslandRouteEndpointsV1& endpoints, std::string& error)
+{
+ FloatingIslandManifestV1 manifest;
+ manifest.recipe = inputRecipe;
+ auto& recipe = manifest.recipe;
+ recipe.geometryVersion = 9;
+ recipe.primaryIslandCount = 9;
+ recipe.anchorIslandOrdinal = recipe.primaryIslandCount - 1;
+ constexpr std::int64_t step = 192 * floating_island_detail::Q;
+ recipe.spacingSubvoxels = step;
+ if (recipe.routeConstraints.version == 0)
+ {
+ recipe.routeConstraints.version = 1;
+ recipe.routeConstraints.capsuleRadiusSubvoxels = 348;
+ recipe.routeConstraints.capsuleHeightSubvoxels = 1802;
+ recipe.routeConstraints.maximumGradeQ16 = 17560;
+ recipe.routeConstraints.sweepSpacingSubvoxels = 128;
+ recipe.routeConstraints.supportProbeRadiusSubvoxels = 230;
+ recipe.routeConstraints.supportSearchDepthSubvoxels = 512;
+ recipe.routeConstraints.minimumClearanceSubvoxels = 128;
+ recipe.routeConstraints.minimumSupportProbes = 3;
+ }
+ for (const auto& value : endpoints.arrivalSubvoxels)
+ if (!floating_island_detail::ValidScalar(value))
+ { error = "resolved floating-island arrival mouth is outside the fixed-point range"; return std::nullopt; }
+ for (const auto& value : endpoints.departureSubvoxels)
+ if (!floating_island_detail::ValidScalar(value))
+ { error = "resolved floating-island departure mouth is outside the fixed-point range"; return std::nullopt; }
+
+ const double qPerMetre = static_cast<double>(floating_island_detail::Q)
+ * 1000000.0 / recipe.coreVoxelSizeMicrons;
+ const double dx = static_cast<double>(endpoints.departureSubvoxels[0]
+ - endpoints.arrivalSubvoxels[0]);
+ const double dy = static_cast<double>(endpoints.departureSubvoxels[1]
+ - endpoints.arrivalSubvoxels[1]);
+ const double endpointDistance = std::hypot(dx, dy);
+ const std::int64_t maximumMouthOffset = 64 * floating_island_detail::Q
+ - recipe.routeConstraints.capsuleRadiusSubvoxels - floating_island_detail::Q;
+ const std::int64_t desiredMouthOffset = std::max(recipe.anchorMouthOffsetSubvoxels,
+ static_cast<std::int64_t>(std::llround(qPerMetre * 6.6)));
+ recipe.anchorMouthOffsetSubvoxels = std::min({desiredMouthOffset,
+ maximumMouthOffset, static_cast<std::int64_t>(std::llround(endpointDistance))});
+ if (recipe.anchorMouthOffsetSubvoxels <= 0)
+ { error = "resolved island mouths cannot provide a bounded departure approach"; return std::nullopt; }
+ recipe.anchorRootRadiusSubvoxels = std::max(recipe.anchorRootRadiusSubvoxels,
+ recipe.anchorMouthOffsetSubvoxels + recipe.routeConstraints.capsuleRadiusSubvoxels
+ + floating_island_detail::Q);
+ recipe.anchorIslandMinimumRadiusSubvoxels = std::max(recipe.minimumRadiusSubvoxels,
+ recipe.anchorMouthOffsetSubvoxels + recipe.routeConstraints.capsuleRadiusSubvoxels
+ + floating_island_detail::Q + 1);
+ // Keep the resolved fixed mouth on a capsule-grade ramp at the anchor island lip.
+ recipe.topFlattenQ16 = std::max<std::int64_t>(recipe.topFlattenQ16, 52429);
+ recipe.bridgeHalfWidthSubvoxels = std::max(recipe.bridgeHalfWidthSubvoxels,
+ recipe.routeConstraints.capsuleRadiusSubvoxels + 2 * floating_island_detail::Q);
+ if (!floating_island_detail::ValidateRecipe(recipe, error)) return std::nullopt;
+
+ auto& islands = manifest.plan.islands;
+ auto& edges = manifest.plan.clusterEdges;
+ auto& route = manifest.plan.route;
+ constexpr std::uint32_t count = 9;
+ double tangentX = endpointDistance > 1.0
+ ? dx / endpointDistance : 1.0;
+ double tangentY = endpointDistance > 1.0
+ ? dy / endpointDistance : 0.0;
+ if (endpointDistance <= 1.0)
+ {
+ const double angle = static_cast<double>(floating_island_detail::UnitQ16(
+ recipe.seed, 0x900000000ull, 0x524f555445ull))
+ * (2.0 * floating_island_detail::Pi / 65536.0);
+ tangentX = std::cos(angle);
+ tangentY = std::sin(angle);
+ }
+ const double normalX = -tangentY;
+ const double normalY = tangentX;
+ const auto localPoint = [&](double along, double side)
+ {
+ return std::array<std::int64_t, 2>{
+ floating_island_detail::RoundQ(
+ static_cast<double>(endpoints.arrivalSubvoxels[0])
+ + tangentX * along + normalX * side),
+ floating_island_detail::RoundQ(
+ static_cast<double>(endpoints.arrivalSubvoxels[1])
+ + tangentY * along + normalY * side)};
+ };
+ std::array<std::array<std::int64_t, 2>, count> primaryXY{};
+ primaryXY[0] = {endpoints.arrivalSubvoxels[0], endpoints.arrivalSubvoxels[1]};
+ primaryXY[1] = localPoint(0.0, static_cast<double>(step));
+ primaryXY[2] = localPoint(static_cast<double>(step), static_cast<double>(step));
+ primaryXY[3] = localPoint(static_cast<double>(step), 0.0);
+ primaryXY[4] = localPoint(static_cast<double>(step), -static_cast<double>(step));
+ primaryXY[5] = localPoint(0.0, -static_cast<double>(step));
+ primaryXY[6] = localPoint(-static_cast<double>(step), -static_cast<double>(step));
+ // Keep the return lane outside the arrival passage's spiral and on the negative
+ // side of the first connector, so the two bridges cannot cross in plan view.
+ // Keep the final approach radial at the passage mouth. This lets the shared
+ // passage resolver keep its authored route inside the placement disk.
+ primaryXY[7] = localPoint(-0.85 * static_cast<double>(step),
+ -0.23 * static_cast<double>(step));
+ const double departureRadial = std::hypot(
+ static_cast<double>(endpoints.departureSubvoxels[0]),
+ static_cast<double>(endpoints.departureSubvoxels[1]));
+ const double anchorScale = departureRadial > 1.0
+ ? std::max(0.0, (departureRadial - recipe.anchorMouthOffsetSubvoxels) / departureRadial)
+ : 0.0;
+ primaryXY[8] = {
+ floating_island_detail::RoundQ(endpoints.departureSubvoxels[0] * anchorScale),
+ floating_island_detail::RoundQ(endpoints.departureSubvoxels[1] * anchorScale)};
+
+ std::array<double, count> cumulative{};
+ for (std::uint32_t i = 1; i < count; ++i)
+ {
+ const double segmentX = static_cast<double>(primaryXY[i][0] - primaryXY[i - 1][0]);
+ const double segmentY = static_cast<double>(primaryXY[i][1] - primaryXY[i - 1][1]);
+ const double length = std::hypot(segmentX, segmentY);
+ if (!(length > 0.0) || length > static_cast<double>(256 * floating_island_detail::Q))
+ { error = "resolved island mouths require an unsupported serpentine segment length"; return std::nullopt; }
+ cumulative[i] = cumulative[i - 1] + length;
+ }
+ const double totalLength = cumulative.back();
+ if (!(totalLength > 0.0))
+ { error = "resolved floating-island mouths do not define a route"; return std::nullopt; }
+ const std::int64_t capsuleHalf = recipe.routeConstraints.capsuleHeightSubvoxels / 2;
+ const std::int64_t routeOffset = capsuleHalf
+ + recipe.routeConstraints.minimumClearanceSubvoxels
+ + floating_island_detail::Q / 8;
+ const std::int64_t startTop = endpoints.arrivalSubvoxels[2] - routeOffset;
+ const std::int64_t endTop = endpoints.departureSubvoxels[2] - routeOffset;
+ const std::uint64_t anchorSite = 0x700000000ull + recipe.anchorIslandOrdinal;
+ auto anchorShape = floating_island_detail::MakeIsland(recipe,
+ recipe.anchorIslandOrdinal, primaryXY.back()[0], primaryXY.back()[1], endTop, anchorSite);
+ anchorShape.radiusSubvoxels = std::max(anchorShape.radiusSubvoxels,
+ recipe.anchorIslandMinimumRadiusSubvoxels);
+ anchorShape.topHalfSubvoxels = std::max<std::int64_t>(1,
+ anchorShape.radiusSubvoxels / 5);
+ anchorShape.centerSubvoxels[2] = endTop - anchorShape.topHalfSubvoxels;
+ std::int64_t anchorTop = endTop;
+ for (int iteration = 0; iteration < 4; ++iteration)
+ {
+ anchorShape.topZSubvoxels = anchorTop;
+ anchorShape.centerSubvoxels[2] = anchorTop - anchorShape.topHalfSubvoxels;
+ const double mouthSurface = floating_island_detail::IslandTopSurface(recipe,
+ anchorShape, static_cast<double>(endpoints.departureSubvoxels[0]),
+ static_cast<double>(endpoints.departureSubvoxels[1]));
+ anchorTop = endTop + floating_island_detail::RoundQ(
+ static_cast<double>(anchorTop) - mouthSurface);
+ }
+ std::array<std::int64_t, count> tops{};
+ for (std::uint32_t i = 0; i < count; ++i)
+ {
+ const double t = cumulative[i] / totalLength;
+ tops[i] = floating_island_detail::RoundQ(static_cast<double>(startTop)
+ + static_cast<double>(anchorTop - startTop) * t);
+ }
+
+ islands.reserve(count);
+ for (std::uint32_t i = 0; i < count; ++i)
+ {
+ const std::uint64_t site = 0x700000000ull + i;
+ auto island = floating_island_detail::MakeIsland(recipe, i,
+ primaryXY[i][0], primaryXY[i][1], tops[i], site);
+ if (i == recipe.anchorIslandOrdinal)
+ {
+ island.radiusSubvoxels = std::max(island.radiusSubvoxels,
+ recipe.anchorIslandMinimumRadiusSubvoxels);
+ }
+ const std::int64_t interiorBottom = recipe.layoutMinimumZSubvoxels
+ + recipe.boundarySealSubvoxels;
+ while (island.radiusSubvoxels > recipe.minimumRadiusSubvoxels
+ && floating_island_detail::IslandBottom(recipe, tops[i], island.radiusSubvoxels)
+ < interiorBottom)
+ --island.radiusSubvoxels;
+ const std::int64_t requiredRadius = i == recipe.anchorIslandOrdinal
+ ? recipe.anchorIslandMinimumRadiusSubvoxels : recipe.minimumRadiusSubvoxels;
+ if (island.radiusSubvoxels < requiredRadius
+ || floating_island_detail::IslandBottom(recipe, tops[i], island.radiusSubvoxels)
+ < interiorBottom)
+ {
+ error = "resolved-mouth island " + std::to_string(i) + " cannot fit above sealed bedrock"
+ + " (radius=" + std::to_string(island.radiusSubvoxels)
+ + ", required=" + std::to_string(requiredRadius)
+ + ", top=" + std::to_string(tops[i])
+ + ", bottom=" + std::to_string(floating_island_detail::IslandBottom(
+ recipe, tops[i], island.radiusSubvoxels))
+ + ", interior_bottom=" + std::to_string(interiorBottom)
+ + ", anchor_top=" + std::to_string(anchorTop)
+ + ", mouth_offset=" + std::to_string(recipe.anchorMouthOffsetSubvoxels) + ")";
+ return std::nullopt;
+ }
+ island.topHalfSubvoxels = std::max<std::int64_t>(1, island.radiusSubvoxels / 5);
+ island.topZSubvoxels = tops[i];
+ island.centerSubvoxels[2] = tops[i] - island.topHalfSubvoxels;
+ island.bottomZSubvoxels = floating_island_detail::IslandBottom(recipe,
+ island.topZSubvoxels, island.radiusSubvoxels);
+ floating_island_detail::SetIslandBounds(recipe, island);
+ islands.push_back(std::move(island));
+ }
+ for (std::uint32_t i = 0; i + 1 < count; ++i)
+ edges.push_back(floating_island_detail::MakeConnector(recipe,
+ islands[i], islands[i + 1], 0x600000000ull + i));
+
+ FloatingIslandFieldV1 planningField(recipe, manifest.plan);
+ if (!planningField.isValid())
+ { error = "resolved-mouth island field did not pass the Core field contract"; return std::nullopt; }
+ const std::int64_t routeRadius = 4 * floating_island_detail::Q;
+ std::uint32_t ordinal = 0;
+ for (std::uint32_t segment = 0; segment + 1 < count; ++segment)
+ {
+ const double segmentX = static_cast<double>(primaryXY[segment + 1][0]
+ - primaryXY[segment][0]);
+ const double segmentY = static_cast<double>(primaryXY[segment + 1][1]
+ - primaryXY[segment][1]);
+ const double length = std::hypot(segmentX, segmentY);
+ const std::uint32_t pieces = static_cast<std::uint32_t>(std::max(1.0,
+ std::ceil(length / (4.0 * floating_island_detail::Q))));
+ for (std::uint32_t piece = segment == 0 ? 0 : 1; piece <= pieces; ++piece)
+ {
+ const double t = static_cast<double>(piece) / pieces;
+ const auto x = floating_island_detail::RoundQ(
+ static_cast<double>(primaryXY[segment][0]) + segmentX * t);
+ const auto y = floating_island_detail::RoundQ(
+ static_cast<double>(primaryXY[segment][1]) + segmentY * t);
+ const auto surface = floating_island_detail::FindCapsuleFootingTopZ(
+ recipe, manifest.plan, x, y);
+ if (!surface)
+ { error = "resolved-mouth island chain has no capsule-width support along a connector"; return std::nullopt; }
+ WorldAssemblyRoutePointV1 point;
+ point.stableFeatureId = floating_island_detail::StableId(recipe.seed,
+ 0x800000000ull + ordinal, 0x524f555445ull);
+ point.positionSubvoxels = {x, y, *surface + routeOffset};
+ point.radiusSubvoxels = routeRadius;
+ if (segment == 0 && piece == 0)
+ point.positionSubvoxels = endpoints.arrivalSubvoxels;
+ route.push_back(point);
+ ++ordinal;
+ }
+ }
+ const double mouthLegX = static_cast<double>(endpoints.departureSubvoxels[0]
+ - primaryXY.back()[0]);
+ const double mouthLegY = static_cast<double>(endpoints.departureSubvoxels[1]
+ - primaryXY.back()[1]);
+ const double mouthLegLength = std::hypot(mouthLegX, mouthLegY);
+ const std::uint32_t mouthLegPieces = static_cast<std::uint32_t>(std::max(1.0,
+ std::ceil(mouthLegLength / (4.0 * floating_island_detail::Q))));
+ for (std::uint32_t piece = 1; piece <= mouthLegPieces; ++piece)
+ {
+ const double t = static_cast<double>(piece) / mouthLegPieces;
+ const auto x = floating_island_detail::RoundQ(static_cast<double>(primaryXY.back()[0])
+ + mouthLegX * t);
+ const auto y = floating_island_detail::RoundQ(static_cast<double>(primaryXY.back()[1])
+ + mouthLegY * t);
+ const auto surface = floating_island_detail::FindCapsuleFootingTopZ(
+ recipe, manifest.plan, x, y);
+ if (!surface)
+ { error = "resolved island passage mouth has no capsule-width support along its anchor landing at x="
+ + std::to_string(x) + ", y=" + std::to_string(y) + ", piece="
+ + std::to_string(piece) + "/" + std::to_string(mouthLegPieces)
+ + ", anchor_radius=" + std::to_string(anchorShape.radiusSubvoxels); return std::nullopt; }
+ WorldAssemblyRoutePointV1 point;
+ point.stableFeatureId = floating_island_detail::StableId(recipe.seed,
+ 0x800000000ull + ordinal, 0x524f555445ull);
+ point.positionSubvoxels = {x, y, *surface + routeOffset};
+ point.radiusSubvoxels = routeRadius;
+ if (piece == mouthLegPieces)
+ point.positionSubvoxels = endpoints.departureSubvoxels;
+ route.push_back(point);
+ ++ordinal;
+ }
+
+ const double maximumGrade = floating_island_detail::Q16ToDouble(
+ recipe.routeConstraints.maximumGradeQ16) * 0.95;
+ std::vector<std::int64_t> baseHeights;
+ std::vector<double> routeDistance(route.size(), 0.0);
+ baseHeights.reserve(route.size());
+ for (std::size_t i = 0; i < route.size(); ++i)
+ {
+ baseHeights.push_back(route[i].positionSubvoxels[2]);
+ if (i > 0)
+ {
+ const double dx = static_cast<double>(route[i].positionSubvoxels[0]
+ - route[i - 1].positionSubvoxels[0]);
+ const double dy = static_cast<double>(route[i].positionSubvoxels[1]
+ - route[i - 1].positionSubvoxels[1]);
+ routeDistance[i] = routeDistance[i - 1] + std::hypot(dx, dy);
+ }
+ }
+ for (std::size_t i = 0; i < route.size(); ++i)
+ {
+ double profile = static_cast<double>(baseHeights[i]);
+ std::size_t profileSource = i;
+ for (std::size_t j = 0; j < route.size(); ++j)
+ {
+ const double pathDistance = std::abs(routeDistance[i] - routeDistance[j]);
+ const double candidate = static_cast<double>(baseHeights[j])
+ - maximumGrade * pathDistance;
+ if (candidate > profile)
+ {
+ profile = candidate;
+ profileSource = j;
+ }
+ }
+ const std::int64_t plannedHeight = floating_island_detail::RoundQ(profile);
+ const bool fixedEndpoint = i == 0 || i + 1 == route.size();
+ if (fixedEndpoint && plannedHeight != baseHeights[i])
+ {
+ error = "resolved fixed island mouth endpoint " + std::to_string(i)
+ + " needs " + std::to_string(plannedHeight - baseHeights[i])
+ + " extra subvoxels from route sample " + std::to_string(profileSource)
+ + " at z=" + std::to_string(baseHeights[profileSource])
+ + " and path distance=" + std::to_string(static_cast<std::int64_t>(
+ std::abs(routeDistance[i] - routeDistance[profileSource])))
+ + " (arrival_z="
+ + std::to_string(endpoints.arrivalSubvoxels[2]) + ", departure_z="
+ + std::to_string(endpoints.departureSubvoxels[2]) + ", path_length="
+ + std::to_string(static_cast<std::int64_t>(routeDistance.back())) + ")";
+ return std::nullopt;
+ }
+ route[i].positionSubvoxels[2] = plannedHeight;
+ }
+ if (!manifest.finalize(error)) return std::nullopt;
+ const std::string encoded = manifest.ToJson();
+ const auto roundTrip = FloatingIslandManifestV1::FromJson(encoded, error);
+ if (!roundTrip || roundTrip->ToJson() != encoded
+ || roundTrip->contentHash != manifest.contentHash)
+ { if (error.empty()) error = "resolved-mouth floating-island plan did not canonical-round-trip"; return std::nullopt; }
+ return manifest;
+}
+
FloatingIslandFieldV1::FloatingIslandFieldV1(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& plan) noexcept : recipe_(&recipe), plan_(&plan)
{}
diff --git a/Standalone/Source/StreamBenchmarkHeadless.cpp b/Standalone/Source/StreamBenchmarkHeadless.cpp
index a0cb0a7..468e315 100644
--- a/Standalone/Source/StreamBenchmarkHeadless.cpp
+++ b/Standalone/Source/StreamBenchmarkHeadless.cpp
@@ -1826,12 +1826,59 @@ int main(int argc,char** argv)
else if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype(
*ownerWorld,ownerJsonRoot,error,{},&worldAssembly))
{std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;}
- if(((!useExportedCorePlan&&options.FloatingIslandsRecipe)
- ||(options.NativeOwnerFloatingIslandsRecipe
- &&worldAssembly.floatingIslands.empty()))
- &&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
- *ownerWorld,ownerJsonRoot,worldAssembly,error))
- {std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;}
+ const bool attachFloatingIslandPlan=(!useExportedCorePlan&&options.FloatingIslandsRecipe)
+ ||(options.NativeOwnerFloatingIslandsRecipe&&worldAssembly.floatingIslands.empty());
+ if(attachFloatingIslandPlan)
+ {
+ const FStrateSlot* islandSlot=nullptr;
+ if(ownerWorld->Manager)
+ for(const FStrateSlot& slot:ownerWorld->Manager->GetLayout())
+ if(slot.Definition&&slot.Definition->GeneratorType==ECaveGeneratorType::FloatingIslands)
+ {islandSlot=&slot;break;}
+ if(!islandSlot)
+ {std::fprintf(stderr,"Floating-island endpoint resolution found no FloatingIslands strate.\n");return 2;}
+ VoxelForgeCore::ResolvedLayoutSnapshotV3 mouthLayout;
+ if(!StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot(
+ *ownerWorld,worldAssembly,"floating-islands",mouthLayout,error))
+ {std::fprintf(stderr,"Pre-plan passage layout failed: %s\n",error.c_str());return 2;}
+ VoxelForgeCore::FloatingIslandRouteEndpointsV1 endpoints;
+ bool hasArrival=false,hasDeparture=false;
+ for(const auto& passage:mouthLayout.passages)
+ {
+ const std::size_t lastSlash=passage.stableId.rfind('/');
+ if(lastSlash==std::string::npos||passage.stableId.substr(lastSlash+1)!="0")continue;
+ if(passage.lowerStrateIndex==islandSlot->StrateIndex&&!hasArrival)
+ {
+ endpoints.arrivalSubvoxels=passage.lowerMouthSubvoxels;
+ hasArrival=true;
+ }
+ if(passage.upperStrateIndex==islandSlot->StrateIndex&&!hasDeparture)
+ {
+ endpoints.departureSubvoxels=passage.upperMouthSubvoxels;
+ hasDeparture=true;
+ }
+ }
+ if(!hasArrival||!hasDeparture)
+ {std::fprintf(stderr,"Resolved passage layout does not provide both FI route mouths (arrival=%s departure=%s).\n",
+ hasArrival?"yes":"no",hasDeparture?"yes":"no");return 2;}
+ std::printf("[FloatingIslandResolvedMouths] strate=%d arrival_m=(%.3f,%.3f,%.3f) departure_m=(%.3f,%.3f,%.3f) status=PASS\n",
+ islandSlot->StrateIndex,
+ endpoints.arrivalSubvoxels[0]*ownerWorld->CoreVoxelSizeMicrons
+ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel),
+ endpoints.arrivalSubvoxels[1]*ownerWorld->CoreVoxelSizeMicrons
+ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel),
+ endpoints.arrivalSubvoxels[2]*ownerWorld->CoreVoxelSizeMicrons
+ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel),
+ endpoints.departureSubvoxels[0]*ownerWorld->CoreVoxelSizeMicrons
+ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel),
+ endpoints.departureSubvoxels[1]*ownerWorld->CoreVoxelSizeMicrons
+ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel),
+ endpoints.departureSubvoxels[2]*ownerWorld->CoreVoxelSizeMicrons
+ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));
+ if(!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
+ *ownerWorld,ownerJsonRoot,worldAssembly,endpoints,error))
+ {std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;}
+ }
if(!options.WorldAssemblyManifestOutputPath.empty())
{
const std::string manifestText=worldAssembly.ToJson();
diff --git a/Standalone/Source/WorldAssemblyPrototype.h b/Standalone/Source/WorldAssemblyPrototype.h
index dbe2a7c..9790f2f 100644
--- a/Standalone/Source/WorldAssemblyPrototype.h
+++ b/Standalone/Source/WorldAssemblyPrototype.h
@@ -923,7 +923,8 @@ namespace StandaloneWorldAssemblyPrototype
}
inline bool AttachFloatingIslandWorldAssembly(FStandaloneOwnerWorld& owner,
- VFJson::Value& root,VoxelForgeCore::WorldAssemblyManifest& assembly,std::string& error)
+ VFJson::Value& root,VoxelForgeCore::WorldAssemblyManifest& assembly,
+ const VoxelForgeCore::FloatingIslandRouteEndpointsV1& endpoints,std::string& error)
{
if(!owner.Manager)
{error="floating-island assembly needs the resolved standalone strate manager";return false;}
@@ -996,7 +997,7 @@ namespace StandaloneWorldAssemblyPrototype
recipe.anchorRootBottomZSubvoxels=recipe.layoutMinimumZSubvoxels
+recipe.boundarySealSubvoxels;
- auto planned=VoxelForgeCore::PlanFloatingIslandCluster(recipe,error);
+ auto planned=VoxelForgeCore::PlanFloatingIslandCluster(recipe,endpoints,error);
if(!planned)return false;
const VoxelForgeCore::FloatingIslandFieldV1 routeField(
planned->recipe,planned->plan);
@@ -1217,6 +1218,7 @@ namespace StandaloneWorldAssemblyPrototype
:it->manifest.plan.route.size()-1;
std::size_t resolvedRoutePointCount=it->manifest.plan.route.size();
if(it->manifest.recipe.geometryVersion>=5
+ &&it->manifest.recipe.geometryVersion<9
&&!it->manifest.plan.route.empty()
&&it->manifest.recipe.anchorIslandOrdinal<it->manifest.plan.islands.size())
{
@@ -1,13 +0,0 @@
# Linux134 — Tunnel Network cave planner
Tunnel Network dispatches `Caves-native` (`Core.Caves.PrimitiveField` v1). The seed-0 plan is strict-valid: 30 compound rooms, 72 primitives, 31 edges, 3 side branches, and one larger destination chamber. The arrival-to-departure route is 387.067 m long, has a 59.021 m floor delta, and reaches 8.670° maximum grade. The planner maps 21 of the 47 legacy generation fields; the other 26 do not map to resolved room or edge record fields.
The second design fixed the incoming passage at its source. Shared passage composition now applies passages when the lower stratum has a native cave field; the unchanged walkability audit passes 21,425/21,425 samples.
The third design aligned the arrival-edge floor to the passage mouth, added a 0.75 m backbone floor-support budget to the resolved edge records, and raised route anchors to follow the planned edge floor. The route audit now reports 0 unsupported samples and 0 grade failures. It still fails clearance: 196/50,967 capsule samples are solid. The first is `(-14.548,-104.858,-768.115) m`, density `-0.042135`, at the route-edge-6/7 junction. Its closest planned floor is route-edge-6 at approximately `z=-768.104 m`; the probe is about 1.1 cm below that floor. The player center interpolated along the next route segment is only about 0.79 m above this higher neighboring floor, while the capsule samples extend 0.84 m below center. The native field is returning the intended solid floor backing, which intrudes into the capsule near the overlapping descending edges. The remaining issue is clearance planning across neighboring edge floors, not missing floor support, a room floor cut, or the inter-strate passage.
I stopped at this failed route gate, as requested. Shell, floor, ripple, and blocked-route negative controls pass; the route's blocked control also detects the blockage. The rest of the bar was not run: seams, fast tier, Clang/GCC W1/W8/W12 determinism and baselines, captures, L80 timing, and the post-timing UE host build. Clang headless build passed; source stamp `9845de5d3d614db620bf99aa3bd7b4fd66a440d76676e38ccad3d198b851e476`.
Logs: `Saved/Linux/l134/build-clang-support-fix.log` and `Saved/Linux/l134/walk-gate-support-fix.log`. Resolved layout: `layout-seed0-final-gate.json`.
Legacy field mapping: `RoomSpacing`, `RoomDensity`, `MinRoomRadius`, `MaxRoomRadius`, `RoomHeightRatio`, `RoomFloorCutMin`, `RoomFloorCutMax`, `RoomShapeVariety`, `OriginRoomRadius`, `OriginRoomMaxConnections`, `TunnelMinRadius`, `TunnelMaxRadius`, `TunnelDensity`, `MaxTunnelLength`, `TunnelWarpStrength`, `TunnelHorizontalBias`, `bTunnelsFlowTowardOrigin`, `CaveWarpStrength`, `SurfaceRoughness`, `RoughnessFrequency`, and `SDFBlendRadius`. Unmapped: `BaseDensity`, `BoundarySealThickness`, `CaveWarpFrequency`, `DomainWarpFrequency`, `DomainWarpStrength`, `FloorBias`, `FloorReliefFrequency`, `FloorReliefStrength`, `RoomMouthRiseBlendVoxels`, `RoomMouthRiseStrength`, `TunnelEndpointZOffset`, `TunnelFloorGentleSlopeThreshold`, `TunnelFloorLedgeCountPreference`, `TunnelFloorMaxLedgeHeight`, `TunnelFloorMaxLedges`, `TunnelFloorTerraceStepHeight`, `VerticalScale`, `WaterLevelRelative`, `WormFrequency`, `WormHorizontalBias`, `WormNetworkRange`, `WormStrength`, `WormThreshold`, `bTunnelFloorEnabled`, `bTunnelFloorTerracingEnabled`, and `RoughnessNoiseType`.
@@ -1 +0,0 @@
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
@@ -1 +0,0 @@
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
@@ -1 +0,0 @@
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
+8 -29
View File
@@ -74,7 +74,6 @@ namespace
bool DensityDispatchReport=false; bool DensityDispatchReport=false;
bool CavesRecipe=false; bool CavesRecipe=false;
bool LatticeRecipe=false; bool LatticeRecipe=false;
bool LegacyTunnelNetwork=false;
int LatticeStrateIndex=-1; int LatticeStrateIndex=-1;
bool AuditHeightfieldProfile=false; bool AuditHeightfieldProfile=false;
bool LandmarkChamberDemo=false; bool LandmarkChamberDemo=false;
@@ -1435,7 +1434,6 @@ namespace
" --core-snapshot=FILE (benchmark the exact resolved Core snapshot from Unreal)\n" " --core-snapshot=FILE (benchmark the exact resolved Core snapshot from Unreal)\n"
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n" " --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
" --seed=N --recipe=surface-world|floating-islands|caves|lattice\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" " --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" " --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" " --lattice-strate=N (opt one Maze or VerticalShafts strate into Core.Lattice)\n"
@@ -1499,7 +1497,6 @@ namespace
else if(arg=="--native-owner-fi")options.NativeOwnerFloatingIslandsRecipe=true; else if(arg=="--native-owner-fi")options.NativeOwnerFloatingIslandsRecipe=true;
else if(arg=="--density-dispatch-report") else if(arg=="--density-dispatch-report")
{options.DensityDispatchReport=true;options.StreamHash=true;options.StreamHashTiles=true;} {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-landmark-reachability")options.AuditLandmarkReachability=true;
else if(arg=="--audit-native-caves")options.AuditNativeCaves=true; else if(arg=="--audit-native-caves")options.AuditNativeCaves=true;
else if(arg=="--audit-native-lattice")options.AuditNativeLattice=true; else if(arg=="--audit-native-lattice")options.AuditNativeLattice=true;
@@ -1740,8 +1737,7 @@ int main(int argc,char** argv)
options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel, options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel,
options.MaxLevel,options.StreamHash?"on":"off", options.MaxLevel,options.StreamHash?"on":"off",
options.StreamHashAllowUniformSkips?"on":"off",options.ProfileColumnOverlap?"on":"off", options.StreamHashAllowUniformSkips?"on":"off",options.ProfileColumnOverlap?"on":"off",
options.ForceGenerate?"on":"off",options.LegacyTunnelNetwork?"legacy-tunnel-network" options.ForceGenerate?"on":"off",options.CavesRecipe?"caves"
:options.CavesRecipe?"caves"
:options.LatticeRecipe?"lattice" :options.LatticeRecipe?"lattice"
:options.FloatingIslandsRecipe?"floating-islands":"surface-world", :options.FloatingIslandsRecipe?"floating-islands":"surface-world",
options.LatticeStrateIndex, options.LatticeStrateIndex,
@@ -1862,9 +1858,7 @@ int main(int argc,char** argv)
} }
VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout; VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout;
{ {
const std::string variant=options.LegacyTunnelNetwork const std::string variant=options.LatticeRecipe
?"legacy-tunnel-network"
:options.LatticeRecipe
?"lattice/"+std::to_string(options.LatticeStrateIndex) ?"lattice/"+std::to_string(options.LatticeStrateIndex)
:options.CavesRecipe?"caves" :options.CavesRecipe?"caves"
:(options.FloatingIslandsRecipe||options.NativeOwnerFloatingIslandsRecipe) :(options.FloatingIslandsRecipe||options.NativeOwnerFloatingIslandsRecipe)
@@ -1877,11 +1871,6 @@ int main(int argc,char** argv)
*ownerWorld,worldAssembly,variant,resolvedLayout,error); *ownerWorld,worldAssembly,variant,resolvedLayout,error);
if(!layoutResolved) if(!layoutResolved)
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;} {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 if(useExportedCorePlan&&!options.NativeOwnerFloatingIslandsRecipe
&&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner( &&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner(
*ownerWorld,worldAssembly,resolvedLayout,error)) *ownerWorld,worldAssembly,resolvedLayout,error))
@@ -1964,11 +1953,8 @@ int main(int argc,char** argv)
{ {
std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms; std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms;
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges; std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges;
if(!MakeNativeCavesSpine(resolvedLayout,rooms,edges,error)) if(!MakeNativeCavesSpine(resolvedLayout,rooms,edges,error)
{std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;} ||!VoxelForgeCore::ResolveResolvedCavernGraphV1(
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)) resolvedLayout,seed,rooms,edges,error))
{std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;}
} }
@@ -1979,11 +1965,8 @@ int main(int argc,char** argv)
std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms; std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms;
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges; std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges;
if(!MakeNativeLatticeSpine(resolvedLayout,seed,options.LatticeStrateIndex, if(!MakeNativeLatticeSpine(resolvedLayout,seed,options.LatticeStrateIndex,
options.LatticeOrphanControl,rooms,edges,error)) options.LatticeOrphanControl,rooms,edges,error)
{std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;} ||!VoxelForgeCore::ResolveResolvedCavernGraphV1(
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)) resolvedLayout,seed,rooms,edges,error))
{std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;}
} }
@@ -2104,16 +2087,12 @@ int main(int argc,char** argv)
const bool hasNativeLatticeRecipe=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(), const bool hasNativeLatticeRecipe=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
[](const auto& stratum){return VoxelForgeCore::IsNativeLatticeFieldStratum(stratum) [](const auto& stratum){return VoxelForgeCore::IsNativeLatticeFieldStratum(stratum)
&&stratum.passageCapabilities.hasNativeLatticeField;}); &&stratum.passageCapabilities.hasNativeLatticeField;});
const auto actualRecipe=requestedRecipe=="lattice"&&hasNativeLatticeRecipe const auto actualRecipe=hasNativeLatticeRecipe?std::string_view("lattice")
?std::string_view("lattice") :hasNativeCavesRecipe?std::string_view("caves")
:requestedRecipe=="caves"&&hasNativeCavesRecipe
?std::string_view("caves")
:requestedRecipe=="surface-world"&&hasSurfaceWorldPlan :requestedRecipe=="surface-world"&&hasSurfaceWorldPlan
?std::string_view("surface-world") ?std::string_view("surface-world")
:requestedRecipe=="floating-islands"&&hasFloatingIslands :requestedRecipe=="floating-islands"&&hasFloatingIslands
?std::string_view("floating-islands") ?std::string_view("floating-islands")
:hasNativeLatticeRecipe?std::string_view("lattice")
:hasNativeCavesRecipe?std::string_view("caves")
:hasFloatingIslands?std::string_view("floating-islands") :hasFloatingIslands?std::string_view("floating-islands")
:hasSurfaceWorldPlan?std::string_view("surface-world") :hasSurfaceWorldPlan?std::string_view("surface-world")
:std::string_view("missing"); :std::string_view("missing");
+26 -265
View File
@@ -1595,205 +1595,6 @@ namespace StandaloneStreamBenchmark
return passed; 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(
1000000.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, inline bool AuditNativeCavesField(const VoxelForgeCore::World& shape,
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::uint32_t voxelMicrometres) const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::uint32_t voxelMicrometres)
{ {
@@ -1804,13 +1605,9 @@ namespace StandaloneStreamBenchmark
const auto density=[&](double x,double y,double z) const auto density=[&](double x,double y,double z)
{return static_cast<double>(shape.densityAt({x/vm,y/vm,z/vm}))*vm;}; {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 cave=std::find_if(layout.strates.begin(),layout.strates.end(),
[](const auto& s){return IsNativeCaveFieldStratum(s) [](const auto& s){return s.generatorKind=="Caves"
&&s.recipeId=="Core.Caves.PrimitiveField"&&s.recipeVersion==1
&&s.passageCapabilities.hasNativeCaveField;}); &&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) 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", {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;} cave==layout.strates.end()?"missing":"present",layout.cavernRooms.size(),layout.cavernEdges.size());return false;}
@@ -4999,7 +4796,6 @@ namespace StandaloneStreamBenchmark
} }
} }
std::uint64_t routePoints=0,routePasses=0,routeWalkPasses=0,routeBindings=0,routeBindingFailures=0; 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) for(const auto& stratum:ResolvedLayout->strates)
{ {
std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> anchors; std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> anchors;
@@ -5063,56 +4859,36 @@ namespace StandaloneStreamBenchmark
supportStepMetres,supportMaximumMetres,minimumSupportProbes, supportStepMetres,supportMaximumMetres,minimumSupportProbes,
supportProbeRadiusMetres); supportProbeRadiusMetres);
bool binding=true; 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(
1000000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
/World.CoreVoxelSizeMicrons));
return equalPosition(authored,expected);
};
for(const auto& passage:ResolvedLayout->passages) 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) if(passage.upperStrateIndex==stratum.strateIndex)
{ {
if(stratum.generatorKind=="TunnelNetwork") const auto rootedMouth=std::find_if(stratum.anchors.begin(),
binding&=boundMouthMatches(anchors.back()->values, stratum.anchors.end(),[](const auto& anchor)
passage.upperMouthSubvoxels); {return anchor.kind=="passage_anchor"&&anchor.values.size()>=10;});
else const std::array<std::int64_t,3> departure=
{ rootedMouth!=stratum.anchors.end()
const auto rootedMouth=std::find_if(stratum.anchors.begin(), ?std::array<std::int64_t,3>{{rootedMouth->values[7],
stratum.anchors.end(),[](const auto& anchor) rootedMouth->values[8],rootedMouth->values[9]}}
{return anchor.kind=="passage_anchor"&&anchor.values.size()>=10;}); :std::array<std::int64_t,3>{{anchors.back()->values[0],
const std::array<std::int64_t,3> departure= anchors.back()->values[1],anchors.back()->values[2]}};
rootedMouth!=stratum.anchors.end() binding&=equalPosition(passage.upperMouthSubvoxels,
?std::array<std::int64_t,3>{{rootedMouth->values[7], departure);
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) if(passage.lowerStrateIndex==stratum.strateIndex)
{ {
if(stratum.generatorKind=="TunnelNetwork") const auto passageArrival=std::find_if(stratum.anchors.begin(),
binding&=boundMouthMatches(anchors.front()->values, stratum.anchors.end(),[](const auto& anchor)
passage.lowerMouthSubvoxels); {return anchor.kind=="passage_arrival"&&anchor.values.size()>=3;});
else const std::array<std::int64_t,3> arrival=passageArrival!=stratum.anchors.end()
{ ?std::array<std::int64_t,3>{{passageArrival->values[0],
const auto passageArrival=std::find_if(stratum.anchors.begin(), passageArrival->values[1],passageArrival->values[2]}}
stratum.anchors.end(),[](const auto& anchor) :std::array<std::int64_t,3>{{anchors.front()->values[0],
{return anchor.kind=="passage_arrival"&&anchor.values.size()>=3;}); anchors.front()->values[1],anchors.front()->values[2]}};
const std::array<std::int64_t,3> arrival=passageArrival!=stratum.anchors.end() binding&=equalPosition(passage.lowerMouthSubvoxels,
?std::array<std::int64_t,3>{{passageArrival->values[0], arrival);
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; ++routePasses;routeWalkPasses+=routeAudit.passed;
@@ -5142,18 +4918,6 @@ namespace StandaloneStreamBenchmark
stratum.strateIndex,routeAudit.firstUnsupported[i][0], stratum.strateIndex,routeAudit.firstUnsupported[i][0],
routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2], routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2],
routeAudit.firstUnsupported[i][3]); 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; for(std::size_t passageIndex=0;
passageIndex<ResolvedLayout->passages.size();++passageIndex) passageIndex<ResolvedLayout->passages.size();++passageIndex)
@@ -5220,16 +4984,13 @@ namespace StandaloneStreamBenchmark
&&shellAuditPassed &&shellAuditPassed
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings &&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
&&routeBindingFailures==0 &&routeBindingFailures==0
&&tunnelNetworkRoutes==tunnelNetworkBlockedControls
&&blockedCompiled&&blockedControls==passageCount; &&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 tunnel_network_block_controls=%llu/%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 blocked_controls=%llu/%llu status=%s\n",
static_cast<unsigned long long>(passageCount),static_cast<unsigned long long>(passagePasses), 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>(naturalPasses),static_cast<unsigned long long>(passageCount),
static_cast<unsigned long long>(routePasses),static_cast<unsigned long long>(routeWalkPasses), static_cast<unsigned long long>(routePasses),static_cast<unsigned long long>(routeWalkPasses),
static_cast<unsigned long long>(routePoints), static_cast<unsigned long long>(routePoints),
static_cast<unsigned long long>(routeBindings),static_cast<unsigned long long>(routeBindingFailures), 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), static_cast<unsigned long long>(blockedControls),static_cast<unsigned long long>(passageCount),
auditPassed?"PASS":"FAIL"); auditPassed?"PASS":"FAIL");
if(!auditPassed) if(!auditPassed)
+2 -191
View File
@@ -1069,190 +1069,6 @@ namespace StandaloneWorldAssemblyPrototype
return "Unknown"; 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.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));
};
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 PlayerCenterAboveFloorMicrometres=1000000;
auto start=arrival->lowerMouthSubvoxels;
start[2]+=toCoreSubvoxels(PlayerCenterAboveFloorMicrometres);
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;
if(edgeIndex==0)
{
auto inner=edge->controlPointsMicrometres.front();
inner[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint("000-edge-start",
{toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
toCoreSubvoxels(inner[2])});
}
auto point=edge->controlPointsMicrometres.back();
point[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint(std::to_string(edgeIndex+1),
{toCoreSubvoxels(point[0]),toCoreSubvoxels(point[1]),
toCoreSubvoxels(point[2])});
}
const std::string departureEdgeId="tunnel-network/strate-"
+std::to_string(slot.StrateIndex)+"/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())
{
auto inner=departureEdge->controlPointsMicrometres.front();
inner[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint("998-departure-inner",
{toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
toCoreSubvoxels(inner[2])});
}
auto end=departure->upperMouthSubvoxels;
end[2]+=toCoreSubvoxels(PlayerCenterAboveFloorMicrometres);
appendRoutePoint("999-departure",end);
}
return layout.finalize(error);
}
inline bool ImportExportedCoreWorldAssembly(const VFJson::Value& root, inline bool ImportExportedCoreWorldAssembly(const VFJson::Value& root,
VoxelForgeCore::WorldAssemblyManifest& out,std::string& error) VoxelForgeCore::WorldAssemblyManifest& out,std::string& error)
{ {
@@ -1368,11 +1184,8 @@ namespace StandaloneWorldAssemblyPrototype
if(!slot.Definition)continue; if(!slot.Definition)continue;
const auto type=owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ)); const auto type=owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
const auto authored=sealAndDensity(*slot.Definition,type); const auto authored=sealAndDensity(*slot.Definition,type);
const bool nativeSurfaceCaves=recipeVariant=="caves" const bool nativeCaves=recipeVariant=="caves"
&&type==ECaveGeneratorType::SurfaceWorld; &&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)) const bool nativeLattice=recipeVariant==("lattice/"+std::to_string(slot.StrateIndex))
&&(type==ECaveGeneratorType::Maze||type==ECaveGeneratorType::VerticalShafts); &&(type==ECaveGeneratorType::Maze||type==ECaveGeneratorType::VerticalShafts);
const std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*owner.StrateLayoutChunkSizeMicrometres; const std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*owner.StrateLayoutChunkSizeMicrometres;
@@ -1382,7 +1195,7 @@ namespace StandaloneWorldAssemblyPrototype
if(seal<=0||2*seal>=top-bottom) if(seal<=0||2*seal>=top-bottom)
{error="strate "+std::to_string(slot.StrateIndex)+" has an invalid BoundarySealThickness for two boundary bands";return false;} {error="strate "+std::to_string(slot.StrateIndex)+" has an invalid BoundarySealThickness for two boundary bands";return false;}
ResolvedLayoutStratumV2 s;s.strateIndex=slot.StrateIndex; ResolvedLayoutStratumV2 s;s.strateIndex=slot.StrateIndex;
s.generatorKind=nativeSurfaceCaves?"Caves":GeneratorKind(type); s.generatorKind=nativeCaves?"Caves":GeneratorKind(type);
s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex) s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex)
+"/generator/"+s.generatorKind; +"/generator/"+s.generatorKind;
s.recipeId=nativeLattice?"Core.Lattice.PrimitiveField" s.recipeId=nativeLattice?"Core.Lattice.PrimitiveField"
@@ -1594,8 +1407,6 @@ namespace StandaloneWorldAssemblyPrototype
passageConfigs.push_back(config); passageConfigs.push_back(config);
} }
if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false; 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); return out.finalize(error);
} }
+1 -15
View File
@@ -538,19 +538,6 @@ def run_density_dispatch_report(runner: VerificationRunner, binary: Path) -> Non
voxel_size_um = int(strata[0]["voxel_size_um"]) voxel_size_um = int(strata[0]["voxel_size_um"])
if voxel_size_um <= 0: if voxel_size_um <= 0:
raise ValueError(f"invalid voxel_size_um={voxel_size_um}") raise ValueError(f"invalid voxel_size_um={voxel_size_um}")
tunnel_network = strata[2]
if (tunnel_network.get("generator_kind") != "TunnelNetwork"
or tunnel_network.get("recipe_id") != "Core.Caves.PrimitiveField"
or int(tunnel_network.get("recipe_version", 0)) != 1):
failures.append(f"{name}: Tunnel Network did not bind Core cave recipe v1: "
f"generator={tunnel_network.get('generator_kind')} "
f"recipe={tunnel_network.get('recipe_id')} "
f"version={tunnel_network.get('recipe_version')}")
if dispatch.get(2) != ("Caves-native", "TunnelNetwork",
"Core.Caves.PrimitiveField", 1,
"FCavesFieldAdapter"):
failures.append(f"{name}: Tunnel Network dispatch is not native caves: "
f"{dispatch.get(2)}")
except (OSError, json.JSONDecodeError, KeyError, TypeError, ValueError) as error: except (OSError, json.JSONDecodeError, KeyError, TypeError, ValueError) as error:
failures.append(f"{name}: cannot classify tiles from resolved layout: {error}") failures.append(f"{name}: cannot classify tiles from resolved layout: {error}")
continue continue
@@ -617,8 +604,7 @@ def run_density_dispatch_report(runner: VerificationRunner, binary: Path) -> Non
failures.append("no tile centers were attributed to any bound path") failures.append("no tile centers were attributed to any bound path")
runner.require("owner-density-dispatch-report", not failures, runner.require("owner-density-dispatch-report", not failures,
"; ".join(failures) if failures else "; ".join(failures) if failures else
"four owner poses reported per-strate bound paths and tile-center path shares; " "four owner poses reported per-strate bound paths and tile-center path shares")
"TN binds Core.Caves.PrimitiveField v1 through FCavesFieldAdapter")
def run_landmark_reachability(runner: VerificationRunner, def run_landmark_reachability(runner: VerificationRunner,