wip(L27, side branch): passages cross open voids inside shared natural rock columns
Unsupported passage runs > 12.5 m become a solid relief-bearing column (ceiling/island to ground, flared base) with the helix inside, for every recipe; FI anchor uses the same path. All 4 styles x both recipes: 0/0/0, 0 open-air, naturalness pass; no-long-bridge audit (L26 fails it). Deterministic, Clang = GCC. Look approved; cost +35% SW / +19% FI blocks merge (L28). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
37a223046c
commit
07b3fe2852
14 files changed
+771
-200
No files matched your search
@@ -485,7 +485,7 @@ bool BuildFloatingIslandStratumPlan(const UVoxelStrateManager* StrateManager,
|
||||
IslandPlan.topChunkZ = Slot.TopChunkZ;
|
||||
IslandPlan.bottomChunkZ = Slot.BottomChunkZ;
|
||||
auto& Recipe = IslandPlan.manifest.recipe;
|
||||
Recipe.geometryVersion = 7;
|
||||
Recipe.geometryVersion = 8;
|
||||
Recipe.layoutChunkWidthSubvoxels = LayoutChunkWidthSubvoxels;
|
||||
Recipe.seed = static_cast<std::uint32_t>(StrateManager->GetWorldSeed());
|
||||
Recipe.ownerVoxelSizeMicrons = static_cast<std::uint32_t>(std::llround(OwnerVoxelSizeCm * 10000.0));
|
||||
|
||||
@@ -249,8 +249,8 @@ namespace VoxelForgeCore
|
||||
// L'assembleur v1 encode coordonnees et scalaires en 1/256 de voxel, independamment des tuiles.
|
||||
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
|
||||
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 2;
|
||||
// Version 13 adds connected relief-bearing rock spans across native open-air passage gaps.
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 13;
|
||||
// Version 14 adds shared passage columns for long native open-air crossings.
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 14;
|
||||
|
||||
enum class FieldChannel : std::uint32_t
|
||||
{
|
||||
@@ -666,7 +666,7 @@ namespace VoxelForgeCore
|
||||
std::vector<ResolvedLayoutPassagePointV1> controlPoints;
|
||||
};
|
||||
|
||||
struct PassageBridgeRangeAuditV1
|
||||
struct PassageVoidCrossingAuditV1
|
||||
{
|
||||
std::size_t passageIndex = 0;
|
||||
std::size_t firstSegment = 0;
|
||||
@@ -675,6 +675,17 @@ namespace VoxelForgeCore
|
||||
double unsupportedLengthMetres = 0.0;
|
||||
std::array<double, 4> startPointSubvoxels{};
|
||||
std::array<double, 4> endPointSubvoxels{};
|
||||
bool needsColumn = false;
|
||||
bool connectedAtStart = false;
|
||||
bool connectedAtEnd = false;
|
||||
std::array<std::int64_t, 2> columnAxisSubvoxels{};
|
||||
std::int64_t columnTopZSubvoxels = 0;
|
||||
std::int64_t columnBottomZSubvoxels = 0;
|
||||
std::int64_t columnMinimumRadiusSubvoxels = 0;
|
||||
std::int64_t columnNominalRadiusSubvoxels = 0;
|
||||
std::int64_t columnMaximumRadiusSubvoxels = 0;
|
||||
double maximumRouteOffsetMetres = 0.0;
|
||||
double maximumBoreRadiusMetres = 0.0;
|
||||
};
|
||||
|
||||
struct ResolvedLayoutPassageConfigV1
|
||||
@@ -1325,9 +1336,11 @@ namespace VoxelForgeCore
|
||||
ShapeBuildStats buildStats() const;
|
||||
|
||||
float densityAt(Vec3d voxelPosition) const;
|
||||
// Audit-only view of connected rock spans planned for passage support.
|
||||
std::vector<PassageBridgeRangeAuditV1> passageBridgeRanges() const;
|
||||
bool isPassageBridgeSample(std::size_t passageIndex, std::size_t segmentIndex,
|
||||
// Audit-only view of density-field columns and short spans supporting passage voids.
|
||||
std::vector<PassageVoidCrossingAuditV1> passageVoidCrossings() const;
|
||||
bool isPassageColumnSample(std::size_t passageIndex, std::size_t segmentIndex,
|
||||
Vec3d voxelPosition) const;
|
||||
bool isPassageShortSpanSample(std::size_t passageIndex, std::size_t segmentIndex,
|
||||
Vec3d voxelPosition) const;
|
||||
// Audit-only selector provenance for the same point evaluator; never used by meshing.
|
||||
std::string densityDispatchPathAt(Vec3d voxelPosition) const;
|
||||
|
||||
@@ -281,7 +281,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 > 7
|
||||
if (recipe.schemaVersion != 1 || recipe.geometryVersion < 1 || recipe.geometryVersion > 8
|
||||
|| (recipe.geometryVersion == 1
|
||||
&& (recipe.crustDepthBonusQ16 != 0 || recipe.rootDepthRatioQ16 != 0))
|
||||
|| recipe.unitsPerVoxel != Q
|
||||
@@ -753,7 +753,8 @@ namespace floating_island_detail
|
||||
const double rootBlend = std::clamp(radius * 0.12, 2.0 * Q, 5.0 * Q);
|
||||
for (std::int32_t root = 0; root < rootCount; ++root)
|
||||
sdf = SmoothMin(sdf, RootLobeDensity(recipe, island, root, x, y, z), rootBlend);
|
||||
if (recipe.geometryVersion >= 5 && island.ordinal == recipe.anchorIslandOrdinal
|
||||
if (recipe.geometryVersion >= 5 && recipe.geometryVersion < 8
|
||||
&& island.ordinal == recipe.anchorIslandOrdinal
|
||||
&& recipe.anchorRootBottomZSubvoxels < island.bottomZSubvoxels)
|
||||
{
|
||||
// The central buttress must meet the island crust underside. IslandBottom
|
||||
@@ -944,14 +945,14 @@ namespace floating_island_detail
|
||||
FloatingIslandAnchorV1& island)
|
||||
{
|
||||
const std::int64_t pad = IslandBlendMargin(recipe) + recipe.roughnessSubvoxels;
|
||||
const bool isAnchor=recipe.geometryVersion>=5
|
||||
const bool isAnchorRoot=recipe.geometryVersion>=5&&recipe.geometryVersion<8
|
||||
&&island.ordinal==recipe.anchorIslandOrdinal;
|
||||
const std::int64_t reach = std::max(island.radiusSubvoxels,
|
||||
isAnchor?recipe.anchorRootRadiusSubvoxels:std::int64_t{0})
|
||||
isAnchorRoot?recipe.anchorRootRadiusSubvoxels:std::int64_t{0})
|
||||
+ recipe.contourWarpSubvoxels + pad;
|
||||
island.influenceMinimumSubvoxels = {island.centerSubvoxels[0] - reach,
|
||||
island.centerSubvoxels[1] - reach,
|
||||
(isAnchor?std::min(island.bottomZSubvoxels,recipe.anchorRootBottomZSubvoxels)
|
||||
(isAnchorRoot?std::min(island.bottomZSubvoxels,recipe.anchorRootBottomZSubvoxels)
|
||||
:island.bottomZSubvoxels)-pad};
|
||||
island.influenceMaximumSubvoxels = {island.centerSubvoxels[0] + reach,
|
||||
island.centerSubvoxels[1] + reach, island.topZSubvoxels + pad};
|
||||
@@ -1026,14 +1027,15 @@ namespace floating_island_detail
|
||||
{ error = "floating-island anchor has invalid coordinates or an inverted bound"; return false; }
|
||||
}
|
||||
const std::int64_t pad = IslandBlendMargin(recipe) + recipe.roughnessSubvoxels;
|
||||
const bool hasAnchorRoot=recipe.geometryVersion>=5&&recipe.geometryVersion<8&&isAnchor;
|
||||
const std::int64_t reach = std::max(island.radiusSubvoxels,
|
||||
isAnchor?recipe.anchorRootRadiusSubvoxels:std::int64_t{0})
|
||||
hasAnchorRoot?recipe.anchorRootRadiusSubvoxels:std::int64_t{0})
|
||||
+ recipe.contourWarpSubvoxels + pad;
|
||||
if (island.influenceMinimumSubvoxels[0] > island.centerSubvoxels[0] - reach
|
||||
|| island.influenceMaximumSubvoxels[0] < island.centerSubvoxels[0] + reach
|
||||
|| island.influenceMinimumSubvoxels[1] > island.centerSubvoxels[1] - reach
|
||||
|| island.influenceMaximumSubvoxels[1] < island.centerSubvoxels[1] + reach
|
||||
|| island.influenceMinimumSubvoxels[2] > (isAnchor
|
||||
|| island.influenceMinimumSubvoxels[2] > (hasAnchorRoot
|
||||
?std::min(island.bottomZSubvoxels,recipe.anchorRootBottomZSubvoxels)
|
||||
:island.bottomZSubvoxels) - pad
|
||||
|| island.influenceMaximumSubvoxels[2] < island.topZSubvoxels + pad
|
||||
|
||||
@@ -41,27 +41,31 @@ public:
|
||||
double SealThickness=0.0;
|
||||
};
|
||||
|
||||
const std::vector<FStandalonePassageBridgeRange>& GetPassageBridgeRanges() const
|
||||
{return PassageBridgeRanges;}
|
||||
const std::vector<FStandalonePassageVoidCrossing>& GetPassageVoidCrossings() const
|
||||
{return PassageVoidCrossings;}
|
||||
|
||||
bool IsResolvedPassageBridgeSample(std::size_t PassageIndex,std::size_t SegmentIndex,
|
||||
bool IsResolvedPassageColumnSample(std::size_t PassageIndex,std::size_t SegmentIndex,
|
||||
const VoxelForgeCore::Vec3d& Position) const
|
||||
{
|
||||
if(IsPassageBridgeSegment(PassageIndex,SegmentIndex)
|
||||
&&PassageBridgeSignedDistance(PassageIndex,SegmentIndex,
|
||||
static_cast<float>(Position.x),static_cast<float>(Position.y),
|
||||
static_cast<float>(Position.z))<=0.0)return true;
|
||||
for(const auto& range:PassageBridgeRanges)
|
||||
(void)SegmentIndex;
|
||||
for(const auto& range:PassageVoidCrossings)
|
||||
{
|
||||
if(range.passageIndex!=PassageIndex)continue;
|
||||
for(std::size_t segment=range.firstSegment;segment<=range.lastSegment;++segment)
|
||||
if(segment!=SegmentIndex&&PassageBridgeSignedDistance(PassageIndex,segment,
|
||||
static_cast<float>(Position.x),static_cast<float>(Position.y),
|
||||
static_cast<float>(Position.z))<=0.0)return true;
|
||||
if(!range.needsColumn||range.passageIndex!=PassageIndex)continue;
|
||||
if(PassageColumnSignedDistance(range,static_cast<float>(Position.x),
|
||||
static_cast<float>(Position.y),static_cast<float>(Position.z))<=0.0)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsResolvedPassageShortSpanSample(std::size_t PassageIndex,std::size_t SegmentIndex,
|
||||
const VoxelForgeCore::Vec3d& Position) const
|
||||
{
|
||||
return IsPassageShortSpanSegment(PassageIndex,SegmentIndex)
|
||||
&&PassageBridgeSignedDistance(PassageIndex,SegmentIndex,
|
||||
static_cast<float>(Position.x),static_cast<float>(Position.y),
|
||||
static_cast<float>(Position.z))<=0.0;
|
||||
}
|
||||
|
||||
double GetCoreVoxelSizeCm() const
|
||||
{
|
||||
return Owner == nullptr || Owner->CoreVoxelSizeMicrons == 0
|
||||
@@ -197,6 +201,13 @@ public:
|
||||
-passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
const double lastFloorQ=passage.controlPoints.back().positionSubvoxels[2]
|
||||
-passage.controlPoints.back().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
for(const auto& crossing:PassageVoidCrossings)
|
||||
{
|
||||
if(!crossing.needsColumn||crossing.passageIndex!=passageIndex
|
||||
||qz>crossing.topZSubvoxels||qz<crossing.bottomZSubvoxels)continue;
|
||||
const double columnDistance=PassageColumnSignedDistance(crossing,X,Y,Z);
|
||||
composed=std::min(composed,static_cast<float>(columnDistance/Q));
|
||||
}
|
||||
if(qz>firstFloorQ+2.0*passage.mouthRadiusSubvoxels
|
||||
||qz<lastFloorQ-2.0*passage.mouthRadiusSubvoxels)
|
||||
{
|
||||
@@ -297,7 +308,7 @@ public:
|
||||
const double wallReliefWeight=1.0-0.80*verticalWallAlignment;
|
||||
const double roughSignedDistance=bestSignedDistance
|
||||
+static_cast<double>(std::clamp(relief,-1.0f,1.0f))*0.24*qPerMetre*wallReliefWeight;
|
||||
if(IsPassageBridgeSegment(passageIndex,static_cast<std::size_t>(bestIndex)))
|
||||
if(IsPassageShortSpanSegment(passageIndex,static_cast<std::size_t>(bestIndex)))
|
||||
{
|
||||
const double bridgeDistance=PassageBridgeSignedDistance(passageIndex,
|
||||
static_cast<std::size_t>(bestIndex),X,Y,Z);
|
||||
@@ -317,8 +328,9 @@ public:
|
||||
const double radiusB=segmentB.radiusSubvoxels;
|
||||
// Keep the tread within the probe band and below the capsule's
|
||||
// lowest sampled point.
|
||||
const double floorOffset=IsPassageBridgeSegment(passageIndex,
|
||||
const double floorOffset=IsPassageShortSpanSegment(passageIndex,
|
||||
static_cast<std::size_t>(index))
|
||||
||IsPassageColumnSegment(passageIndex,static_cast<std::size_t>(index))
|
||||
?PassageBridgeDeckOffsetMetres(passageIndex,static_cast<std::size_t>(index))
|
||||
:0.20;
|
||||
const double horizontalLengthSq=dx*dx+dy*dy;
|
||||
@@ -387,11 +399,14 @@ public:
|
||||
considerSupportSegment(index);
|
||||
// Keep route treads inside the native rock envelope. Carving may reopen the
|
||||
// floor above this point, but a support cannot create a new surface in air.
|
||||
const bool bridgeFloorSupport=hasSupportFloor
|
||||
&&IsPassageBridgeSegment(passageIndex,
|
||||
const bool passageColumnFloorSupport=hasSupportFloor
|
||||
&&IsPassageColumnSegment(passageIndex,
|
||||
static_cast<std::size_t>(bestSupportSegmentIndex));
|
||||
const bool shortSpanFloorSupport=hasSupportFloor
|
||||
&&IsPassageShortSpanSegment(passageIndex,
|
||||
static_cast<std::size_t>(bestSupportSegmentIndex));
|
||||
if(hasSupportFloor&&bestPlayerCapsuleSignedDistance>=0.05*qPerMetre
|
||||
&&(density<0.0f||bridgeFloorSupport))
|
||||
&&(density<0.0f||passageColumnFloorSupport||shortSpanFloorSupport))
|
||||
composed=std::min(composed,static_cast<float>(bestSupportDensity));
|
||||
}
|
||||
return composed;
|
||||
@@ -416,9 +431,8 @@ public:
|
||||
const double radius=static_cast<double>(std::max(a.radiusSubvoxels,b.radiusSubvoxels))/Q;
|
||||
const double floorThicknessVoxels=Owner&&Owner->CoreVoxelSizeMicrons>0
|
||||
?1000000.0/Owner->CoreVoxelSizeMicrons:4.0;
|
||||
// A resolved passage bridge extends to a 3.5 m half-width and almost 5 m
|
||||
// below the bore floor. Keep those cells shape-dependent for classification
|
||||
// and streaming even before its bridge ranges have been compiled.
|
||||
// Short support spans can extend to a 3.5 m half-width and almost 5 m
|
||||
// below the bore floor. Keep those cells shape-dependent for classification.
|
||||
const double horizontalExtent=std::max(radius,bridgeHalfWidthVoxels);
|
||||
const double footDepth=std::max(floorThicknessVoxels,bridgeFootDepthVoxels);
|
||||
const double low[3]={
|
||||
@@ -435,6 +449,19 @@ public:
|
||||
if(overlap)return true;
|
||||
}
|
||||
}
|
||||
for(const auto& crossing:PassageVoidCrossings)
|
||||
{
|
||||
if(!crossing.needsColumn)continue;
|
||||
const double radius=static_cast<double>(crossing.maximumRadiusSubvoxels)/Q;
|
||||
const double low[3]={crossing.centerXSubvoxels/Q-radius,
|
||||
crossing.centerYSubvoxels/Q-radius,crossing.bottomZSubvoxels/Q};
|
||||
const double high[3]={crossing.centerXSubvoxels/Q+radius,
|
||||
crossing.centerYSubvoxels/Q+radius,crossing.topZSubvoxels/Q};
|
||||
bool overlap=true;
|
||||
for(int axis=0;axis<3;++axis)
|
||||
overlap&=high[axis]>=minimum[axis]&&low[axis]<=maximum[axis];
|
||||
if(overlap)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -785,8 +812,10 @@ public:
|
||||
if(RoundTunnelsEnabled)
|
||||
BuildRoundTunnelPrimitives();
|
||||
BuildSpatialBins(LineBinSize);
|
||||
BuildPassageBridgePlan();
|
||||
Stats.LocalLineBinSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
const auto SharedStage=Clock::now();
|
||||
RebuildSharedFieldPrimitives();
|
||||
Stats.SharedFieldSeconds=std::chrono::duration<double>(Clock::now()-SharedStage).count();
|
||||
Stats.ShapeSeconds=std::chrono::duration<double>(Clock::now()-Stage).count();
|
||||
Stats.PrimitiveCount=Primitives.size();
|
||||
if(Primitives.empty())
|
||||
@@ -797,6 +826,10 @@ public:
|
||||
RockWords.assign(static_cast<size_t>(Size[0])*Size[1]*WordsPerZ,0);
|
||||
SurfaceDistanceQ=std::make_unique_for_overwrite<int16[]>(static_cast<size_t>(NodeCount));
|
||||
BuildPackedRockIntervals(Stats.RockIntervalLines);
|
||||
// Native density queries for non-heightfield strates use the exact interval
|
||||
// evaluator. Build those native intervals before scanning passage support;
|
||||
// the scan itself still completes before any resolved column is composed.
|
||||
BuildPassageVoidCrossingPlan();
|
||||
BuildCandidateBands(Stats.CandidateAxisLines,Stats.CandidateMergeLines,
|
||||
Stats.CandidateMergeSeconds);
|
||||
if(!BuildGpuVisualDetailNodeSets(InGpuVisualDetailTiles,InCpuVisualDetailTiles,Error))
|
||||
@@ -820,9 +853,6 @@ public:
|
||||
return false;
|
||||
}
|
||||
Stats.PassageValidationSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
Substage=Clock::now();
|
||||
RebuildSharedFieldPrimitives();
|
||||
Stats.SharedFieldSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
Built=true;
|
||||
if(bIncludeFarWorld)BuildFarWorld();
|
||||
const uint64 WordBytes=static_cast<uint64>(RockWords.size()+CandidateWords.size())*sizeof(uint64);
|
||||
@@ -1693,23 +1723,27 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsPassageBridgeSegment(std::size_t PassageIndex,std::size_t SegmentIndex) const
|
||||
bool IsPassageColumnSegment(std::size_t PassageIndex,std::size_t SegmentIndex) const
|
||||
{
|
||||
return std::any_of(PassageBridgeRanges.begin(),PassageBridgeRanges.end(),
|
||||
[PassageIndex,SegmentIndex](const auto& range)
|
||||
{return range.passageIndex==PassageIndex
|
||||
&&SegmentIndex>=range.firstSegment&&SegmentIndex<=range.lastSegment;});
|
||||
return std::any_of(PassageVoidCrossings.begin(),PassageVoidCrossings.end(),
|
||||
[PassageIndex,SegmentIndex](const auto& crossing)
|
||||
{return crossing.needsColumn&&crossing.passageIndex==PassageIndex
|
||||
&&SegmentIndex>=crossing.firstSegment&&SegmentIndex<=crossing.lastSegment;});
|
||||
}
|
||||
|
||||
bool IsPassageShortSpanSegment(std::size_t PassageIndex,std::size_t SegmentIndex) const
|
||||
{
|
||||
return std::any_of(PassageVoidCrossings.begin(),PassageVoidCrossings.end(),
|
||||
[PassageIndex,SegmentIndex](const auto& crossing)
|
||||
{return !crossing.needsColumn&&crossing.passageIndex==PassageIndex
|
||||
&&SegmentIndex>=crossing.firstSegment&&SegmentIndex<=crossing.lastSegment;});
|
||||
}
|
||||
|
||||
double PassageBridgeDeckOffsetMetres(std::size_t PassageIndex,
|
||||
std::size_t SegmentIndex) const
|
||||
{
|
||||
for(const auto& range:PassageBridgeRanges)
|
||||
{
|
||||
if(range.passageIndex!=PassageIndex||SegmentIndex>range.lastSegment)continue;
|
||||
if(SegmentIndex>=range.firstSegment)return 0.13;
|
||||
}
|
||||
return 0.20;
|
||||
(void)PassageIndex;(void)SegmentIndex;
|
||||
return 0.13;
|
||||
}
|
||||
|
||||
double PassageBridgeSignedDistance(std::size_t PassageIndex,std::size_t SegmentIndex,
|
||||
@@ -1760,6 +1794,109 @@ private:
|
||||
return outside+inside;
|
||||
}
|
||||
|
||||
double PassageColumnSignedDistance(const FStandalonePassageVoidCrossing& crossing,
|
||||
float X,float Y,float Z) const
|
||||
{
|
||||
if(crossing.maximumRadiusSubvoxels<=0||crossing.topZSubvoxels
|
||||
<=crossing.bottomZSubvoxels)return std::numeric_limits<double>::infinity();
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double qPerMetre=Q*1000000.0/std::max<std::uint32_t>(1,
|
||||
ResolvedLayout?ResolvedLayout->coreVoxelSizeMicrometres:250000u);
|
||||
const double qx=static_cast<double>(X)*Q,qy=static_cast<double>(Y)*Q,
|
||||
qz=static_cast<double>(Z)*Q;
|
||||
const double dx=qx-crossing.centerXSubvoxels;
|
||||
const double dy=qy-crossing.centerYSubvoxels;
|
||||
const double maximumRadius=static_cast<double>(crossing.maximumRadiusSubvoxels);
|
||||
const double horizontalSquared=dx*dx+dy*dy;
|
||||
if(horizontalSquared>maximumRadius*maximumRadius)
|
||||
return DeterministicSqrt(horizontalSquared)-maximumRadius;
|
||||
if(qz>crossing.topZSubvoxels)
|
||||
return qz-crossing.topZSubvoxels;
|
||||
if(qz<crossing.bottomZSubvoxels)
|
||||
return crossing.bottomZSubvoxels-qz;
|
||||
|
||||
// The lower-strate route is already walkable in its native field and binds
|
||||
// directly to the passage mouth. Preserve that player capsule as the column
|
||||
// flares into the ground, so the pillar joins the route instead of sealing it.
|
||||
if(ResolvedLayout&&crossing.passageIndex<ResolvedLayout->passages.size()
|
||||
&&qz<crossing.bottomZSubvoxels+18.0*qPerMetre)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[crossing.passageIndex];
|
||||
const auto lower=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[&](const auto& value)
|
||||
{return value.strateIndex==passage.lowerStrateIndex;});
|
||||
if(lower!=ResolvedLayout->strates.end())
|
||||
{
|
||||
const double capsuleRadius=0.37*qPerMetre;
|
||||
const double capsuleHalfHeight=0.86*qPerMetre;
|
||||
const double capsuleRadiusSquared=capsuleRadius*capsuleRadius;
|
||||
const VoxelForgeCore::ResolvedLayoutAnchorV2* previous=nullptr;
|
||||
for(const auto& anchor:lower->anchors)
|
||||
{
|
||||
if(anchor.kind!="route_point"||anchor.values.size()<3)continue;
|
||||
if(previous)
|
||||
{
|
||||
const double ax=previous->values[0],ay=previous->values[1];
|
||||
const double dx=static_cast<double>(anchor.values[0])-ax;
|
||||
const double dy=static_cast<double>(anchor.values[1])-ay;
|
||||
const double lengthSquared=dx*dx+dy*dy;
|
||||
const double t=lengthSquared>0.0?std::clamp(
|
||||
((qx-ax)*dx+(qy-ay)*dy)/lengthSquared,0.0,1.0):0.0;
|
||||
const double routeX=ax+t*dx,routeY=ay+t*dy;
|
||||
const double routeZ=previous->values[2]+t*(
|
||||
static_cast<double>(anchor.values[2])-previous->values[2]);
|
||||
const double routeDx=qx-routeX,routeDy=qy-routeY;
|
||||
if(routeDx*routeDx+routeDy*routeDy<=capsuleRadiusSquared
|
||||
&&std::abs(qz-routeZ)<=capsuleHalfHeight)
|
||||
return maximumRadius+qPerMetre;
|
||||
}
|
||||
previous=&anchor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double height=static_cast<double>(crossing.topZSubvoxels
|
||||
-crossing.bottomZSubvoxels);
|
||||
const double t=std::clamp((crossing.topZSubvoxels-qz)/height,0.0,1.0);
|
||||
const auto smoothStep=[](double value)
|
||||
{
|
||||
value=std::clamp(value,0.0,1.0);
|
||||
return value*value*(3.0-2.0*value);
|
||||
};
|
||||
const float z=static_cast<float>(Z);
|
||||
const float scaledZ=z*0.12f;
|
||||
const float driftX=VF_FieldSurfaceRelief(0.0f,0.0f,scaledZ,
|
||||
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x434f4c58u);
|
||||
const float driftY=VF_FieldSurfaceRelief(0.0f,0.0f,scaledZ,
|
||||
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x434f4c59u);
|
||||
const double axisX=crossing.centerXSubvoxels
|
||||
+std::clamp(static_cast<double>(driftX),-1.0,1.0)*0.35*qPerMetre;
|
||||
const double axisY=crossing.centerYSubvoxels
|
||||
+std::clamp(static_cast<double>(driftY),-1.0,1.0)*0.35*qPerMetre;
|
||||
const double centerDx=qx-axisX,centerDy=qy-axisY;
|
||||
const double radialDistance=DeterministicSqrt(centerDx*centerDx+centerDy*centerDy);
|
||||
const float broadRelief=VF_FieldSurfaceRelief(X*0.12f,Y*0.12f,Z*0.12f,
|
||||
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x434f4c52u);
|
||||
const float surfaceRelief=VF_FieldSurfaceRelief(X,Y,Z,
|
||||
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x434f4c53u);
|
||||
const double baseFlare=6.0*qPerMetre*smoothStep((t-0.82)/0.18);
|
||||
const double ceilingFlare=2.0*qPerMetre*(1.0-smoothStep(t/0.075));
|
||||
const double irregularity=1.25*qPerMetre
|
||||
+std::clamp(static_cast<double>(broadRelief),-1.0,1.0)*1.10*qPerMetre
|
||||
+std::clamp(static_cast<double>(surfaceRelief),-1.0,1.0)*0.35*qPerMetre;
|
||||
const double radius=static_cast<double>(crossing.minimumRadiusSubvoxels)
|
||||
+std::max(0.15*qPerMetre,irregularity)+baseFlare+ceilingFlare;
|
||||
const double radialDistanceField=radialDistance-radius;
|
||||
const double topDistance=qz-crossing.topZSubvoxels;
|
||||
const double bottomDistance=crossing.bottomZSubvoxels-qz;
|
||||
const double verticalDistance=std::max(topDistance,bottomDistance);
|
||||
const double outside=DeterministicSqrt(
|
||||
std::max(radialDistanceField,0.0)*std::max(radialDistanceField,0.0)
|
||||
+std::max(verticalDistance,0.0)*std::max(verticalDistance,0.0));
|
||||
const double inside=std::min(std::max(radialDistanceField,verticalDistance),0.0);
|
||||
return outside+inside;
|
||||
}
|
||||
|
||||
float NativePassageDensityAt(float X,float Y,float Z) const
|
||||
{
|
||||
float density=0.0f;
|
||||
@@ -1775,33 +1912,25 @@ private:
|
||||
return ApplyResolvedStrateBoundaryBand(density,X,Y,Z);
|
||||
}
|
||||
|
||||
void BuildPassageBridgePlan()
|
||||
void BuildPassageVoidCrossingPlan()
|
||||
{
|
||||
PassageBridgeRanges.clear();
|
||||
PassageVoidCrossings.clear();
|
||||
if(!Owner||!ResolvedLayout||ResolvedLayout->passages.empty()
|
||||
||!Owner->PassageBridgeCache)return;
|
||||
auto& cache=*Owner->PassageBridgeCache;
|
||||
||!Owner->PassageVoidCrossingCache)return;
|
||||
auto& cache=*Owner->PassageVoidCrossingCache;
|
||||
std::lock_guard<std::mutex> lock(cache.mutex);
|
||||
const auto cached=cache.byLayout.find(ResolvedLayout->stableIdentity);
|
||||
const std::string cacheKey=ResolvedLayout->stableIdentity+"/passage-void-columns-v1";
|
||||
const auto cached=cache.byLayout.find(cacheKey);
|
||||
if(cached!=cache.byLayout.end())
|
||||
{PassageBridgeRanges=cached->second;return;}
|
||||
{PassageVoidCrossings=cached->second;return;}
|
||||
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double qPerMetre=Q*1000000.0/std::max<std::uint32_t>(1,
|
||||
ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const auto inSurfaceWorldStratum=[&](double zSubvoxels)
|
||||
{
|
||||
const double zMicrometres=zSubvoxels
|
||||
*ResolvedLayout->coreVoxelSizeMicrometres/Q;
|
||||
return std::any_of(ResolvedLayout->strates.begin(),ResolvedLayout->strates.end(),
|
||||
[zMicrometres](const auto& stratum)
|
||||
{return stratum.generatorKind=="SurfaceWorld"
|
||||
&&zMicrometres>=stratum.interiorBottomMicrometres
|
||||
&&zMicrometres<stratum.interiorTopMicrometres;});
|
||||
};
|
||||
constexpr std::array<std::array<double,2>,5> supportOffsets{{
|
||||
{{0.0,0.0}},{{0.45,0.0}},{{-0.45,0.0}},{{0.0,0.45}},{{0.0,-0.45}}}};
|
||||
constexpr std::array<double,2> supportDepthsMetres{{0.125,0.25}};
|
||||
constexpr double shortSpanMaximumMetres=12.5; // 2.5 x the 5 m legacy support thickness.
|
||||
struct FPathSample
|
||||
{
|
||||
std::size_t segment=0;
|
||||
@@ -1818,14 +1947,10 @@ private:
|
||||
for(const double depth:supportDepthsMetres)
|
||||
{
|
||||
const double sampleZ=floorQ-depth*qPerMetre;
|
||||
// Bridge spans address the measured SurfaceWorld void. Other native
|
||||
// strata retain their own route/cave composition and are excluded here.
|
||||
if(!inSurfaceWorldStratum(sampleZ)){hasSolid=true;break;}
|
||||
float density=1.0f;
|
||||
if(!NativeSurfaceDensityAt(
|
||||
const float density=NativePassageDensityAt(
|
||||
static_cast<float>((point[0]+offset[0]*qPerMetre)/Q),
|
||||
static_cast<float>((point[1]+offset[1]*qPerMetre)/Q),
|
||||
static_cast<float>(sampleZ/Q),density))continue;
|
||||
static_cast<float>(sampleZ/Q));
|
||||
if(density<0.0f){hasSolid=true;break;}
|
||||
}
|
||||
supported+=hasSolid;
|
||||
@@ -1874,9 +1999,10 @@ private:
|
||||
const std::size_t gapBegin=cursor;
|
||||
while(cursor<samples.size()&&!samples[cursor].nativeSupported)++cursor;
|
||||
const std::size_t gapEnd=cursor-1;
|
||||
if(gapBegin==0||cursor>=samples.size())continue;
|
||||
const std::size_t startSample=gapBegin>0?gapBegin-1:gapBegin;
|
||||
const std::size_t endSample=cursor<samples.size()?cursor:samples.size()-1;
|
||||
double gapLength=0.0;
|
||||
for(std::size_t index=gapBegin;index<=gapEnd;++index)
|
||||
for(std::size_t index=startSample+1;index<=endSample;++index)
|
||||
{
|
||||
const auto& a=samples[index-1].point;
|
||||
const auto& b=samples[index].point;
|
||||
@@ -1884,25 +2010,105 @@ private:
|
||||
gapLength+=DeterministicSqrt(dx*dx+dy*dy+dz*dz)/qPerMetre;
|
||||
}
|
||||
if(gapLength<1.0)continue;
|
||||
FStandalonePassageBridgeRange range;
|
||||
range.passageIndex=passageIndex;
|
||||
range.firstSegment=samples[gapBegin-1].segment;
|
||||
range.lastSegment=samples[cursor].segment;
|
||||
range.unsupportedSamples=gapEnd-gapBegin+1;
|
||||
range.unsupportedLengthMetres=gapLength;
|
||||
range.startPoint=samples[gapBegin-1].point;
|
||||
range.endPoint=samples[cursor].point;
|
||||
PassageBridgeRanges.push_back(range);
|
||||
std::printf("[PassageRockBridgePlan] id=%s first_segment=%zu last_segment=%zu unsupported_samples=%llu gap_length_m=%.3f connected_start=1 connected_end=1 width_m=6.000 thickness_m=3.000..5.000 status=PLANNED\n",
|
||||
passage.stableId.c_str(),range.firstSegment,range.lastSegment,
|
||||
static_cast<unsigned long long>(range.unsupportedSamples),gapLength);
|
||||
FStandalonePassageVoidCrossing crossing;
|
||||
crossing.passageIndex=passageIndex;
|
||||
crossing.firstSegment=samples[startSample].segment;
|
||||
crossing.lastSegment=samples[endSample].segment;
|
||||
crossing.unsupportedSamples=gapEnd-gapBegin+1;
|
||||
crossing.unsupportedLengthMetres=gapLength;
|
||||
crossing.startPoint=samples[startSample].point;
|
||||
crossing.endPoint=samples[endSample].point;
|
||||
crossing.connectedAtStart=gapBegin>0&&samples[startSample].nativeSupported;
|
||||
crossing.connectedAtEnd=cursor<samples.size()&&samples[endSample].nativeSupported;
|
||||
crossing.needsColumn=gapLength>shortSpanMaximumMetres;
|
||||
if(crossing.needsColumn&&passage.controlPoints.size()>1)
|
||||
crossing.lastSegment=std::min(crossing.lastSegment+1,
|
||||
passage.controlPoints.size()-2);
|
||||
if(crossing.needsColumn)
|
||||
{
|
||||
std::int64_t sumX=0,sumY=0;
|
||||
std::uint64_t centerSamples=0;
|
||||
for(std::size_t index=gapBegin;index<=gapEnd;++index)
|
||||
{
|
||||
sumX+=static_cast<std::int64_t>(std::llround(samples[index].point[0]));
|
||||
sumY+=static_cast<std::int64_t>(std::llround(samples[index].point[1]));
|
||||
++centerSamples;
|
||||
}
|
||||
if(std::none_of(PassageBridgeRanges.begin(),PassageBridgeRanges.end(),
|
||||
crossing.centerXSubvoxels=centerSamples>0
|
||||
?static_cast<std::int64_t>(std::llround(static_cast<double>(sumX)/centerSamples)):0;
|
||||
crossing.centerYSubvoxels=centerSamples>0
|
||||
?static_cast<std::int64_t>(std::llround(static_cast<double>(sumY)/centerSamples)):0;
|
||||
for(const auto& stratum:ResolvedLayout->strates)
|
||||
if(stratum.strateIndex==passage.upperStrateIndex
|
||||
&&stratum.generatorKind=="FloatingIslands")
|
||||
for(const auto& anchor:stratum.anchors)
|
||||
if(anchor.kind=="passage_anchor"&&anchor.values.size()>=2)
|
||||
{
|
||||
crossing.centerXSubvoxels=anchor.values[0];
|
||||
crossing.centerYSubvoxels=anchor.values[1];
|
||||
break;
|
||||
}
|
||||
double maximumOffsetSubvoxels=0.0;
|
||||
double maximumBoreSubvoxels=0.0;
|
||||
for(std::size_t index=startSample;index<=endSample;++index)
|
||||
{
|
||||
const auto& point=samples[index].point;
|
||||
const double dx=point[0]-crossing.centerXSubvoxels;
|
||||
const double dy=point[1]-crossing.centerYSubvoxels;
|
||||
maximumOffsetSubvoxels=std::max(maximumOffsetSubvoxels,
|
||||
DeterministicSqrt(dx*dx+dy*dy));
|
||||
maximumBoreSubvoxels=std::max(maximumBoreSubvoxels,point[3]);
|
||||
}
|
||||
const double maximumOffsetMetres=maximumOffsetSubvoxels/qPerMetre;
|
||||
const double maximumBoreMetres=maximumBoreSubvoxels/qPerMetre;
|
||||
const double minimumRadiusMetres=maximumOffsetMetres+maximumBoreMetres+2.0;
|
||||
const double nominalRadiusMetres=minimumRadiusMetres+1.25;
|
||||
crossing.maximumRouteOffsetMetres=maximumOffsetMetres;
|
||||
crossing.maximumBoreRadiusMetres=maximumBoreMetres;
|
||||
crossing.minimumRadiusSubvoxels=static_cast<std::int64_t>(std::ceil(
|
||||
minimumRadiusMetres*qPerMetre));
|
||||
crossing.nominalRadiusSubvoxels=static_cast<std::int64_t>(std::llround(
|
||||
nominalRadiusMetres*qPerMetre));
|
||||
crossing.maximumRadiusSubvoxels=crossing.minimumRadiusSubvoxels
|
||||
+static_cast<std::int64_t>(std::ceil(11.25*qPerMetre));
|
||||
const double startZ=crossing.startPoint[2];
|
||||
const double endZ=crossing.endPoint[2];
|
||||
crossing.topZSubvoxels=static_cast<std::int64_t>(std::llround(
|
||||
std::max(startZ,endZ)+5.0*qPerMetre));
|
||||
crossing.bottomZSubvoxels=static_cast<std::int64_t>(std::llround(
|
||||
std::min(startZ,endZ)-6.0*qPerMetre));
|
||||
}
|
||||
PassageVoidCrossings.push_back(crossing);
|
||||
if(crossing.needsColumn)
|
||||
{
|
||||
const double coreRadius=crossing.nominalRadiusSubvoxels/qPerMetre;
|
||||
const double minimumWall=coreRadius-crossing.maximumRouteOffsetMetres
|
||||
-crossing.maximumBoreRadiusMetres;
|
||||
std::printf("[PassageVoidColumnPlan] id=%s segments=%zu..%zu unsupported_samples=%llu span_m=%.3f threshold_m=%.3f connected=%d/%d axis_m=(%.3f,%.3f) z_m=%.3f..%.3f route_radius_m=%.3f bore_radius_m=%.3f min_radius_m=%.3f nominal_radius_m=%.3f max_radius_m=%.3f nominal_wall_m=%.3f flare_m=6.000 status=%s\n",
|
||||
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
|
||||
static_cast<unsigned long long>(crossing.unsupportedSamples),gapLength,
|
||||
shortSpanMaximumMetres,static_cast<int>(crossing.connectedAtStart),
|
||||
static_cast<int>(crossing.connectedAtEnd),
|
||||
crossing.centerXSubvoxels/qPerMetre,crossing.centerYSubvoxels/qPerMetre,
|
||||
crossing.topZSubvoxels/qPerMetre,crossing.bottomZSubvoxels/qPerMetre,
|
||||
crossing.maximumRouteOffsetMetres,crossing.maximumBoreRadiusMetres,
|
||||
crossing.minimumRadiusSubvoxels/qPerMetre,coreRadius,
|
||||
crossing.maximumRadiusSubvoxels/qPerMetre,minimumWall,
|
||||
crossing.connectedAtStart&&crossing.connectedAtEnd?"PLANNED":"UNATTACHED");
|
||||
}
|
||||
else
|
||||
std::printf("[PassageShortGroundedSpanPlan] id=%s segments=%zu..%zu span_m=%.3f threshold_m=%.3f connected=%d/%d width_m=6.000 thickness_m=3.000..5.000 status=%s\n",
|
||||
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
|
||||
gapLength,shortSpanMaximumMetres,static_cast<int>(crossing.connectedAtStart),
|
||||
static_cast<int>(crossing.connectedAtEnd),
|
||||
crossing.connectedAtStart&&crossing.connectedAtEnd?"PLANNED":"UNATTACHED");
|
||||
}
|
||||
if(std::none_of(PassageVoidCrossings.begin(),PassageVoidCrossings.end(),
|
||||
[passageIndex](const auto& range){return range.passageIndex==passageIndex;}))
|
||||
std::printf("[PassageRockBridgePlan] id=%s spans=0 status=NOT_REQUIRED\n",
|
||||
std::printf("[PassageVoidColumnPlan] id=%s crossings=0 status=NOT_REQUIRED\n",
|
||||
passage.stableId.c_str());
|
||||
}
|
||||
cache.byLayout.emplace(ResolvedLayout->stableIdentity,PassageBridgeRanges);
|
||||
cache.byLayout.emplace(cacheKey,PassageVoidCrossings);
|
||||
}
|
||||
|
||||
static uint64 PackLatticeKey(int32 X,int32 Y,int32 Z)
|
||||
@@ -6151,7 +6357,7 @@ private:
|
||||
std::vector<FFarConnectorSegment> FarConnectorSegments;
|
||||
std::unordered_map<FBinKey,std::vector<uint32>,FBinHash> FarConnectorSpatialBins;
|
||||
std::vector<FEditBrush> EditBrushes;
|
||||
std::vector<FStandalonePassageBridgeRange> PassageBridgeRanges;
|
||||
std::vector<FStandalonePassageVoidCrossing> PassageVoidCrossings;
|
||||
std::array<FAxisBins,3> AxisBins;
|
||||
std::unordered_map<FBinKey,FBinItems,FBinHash> SpatialBins;
|
||||
std::unordered_map<FBinKey,FBinItems,FBinHash> CriticalAirBins;
|
||||
|
||||
@@ -1542,27 +1542,43 @@ namespace VoxelForgeCore
|
||||
|
||||
float World::densityAt(Vec3d position) const
|
||||
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DensityAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):0.0f; }
|
||||
std::vector<PassageBridgeRangeAuditV1> World::passageBridgeRanges() const
|
||||
std::vector<PassageVoidCrossingAuditV1> World::passageVoidCrossings() const
|
||||
{
|
||||
std::vector<PassageBridgeRangeAuditV1> result;
|
||||
std::vector<PassageVoidCrossingAuditV1> result;
|
||||
if(!impl_||!impl_->shapeReady.load(std::memory_order_acquire)||!impl_->shape)return result;
|
||||
const auto& ranges=impl_->shape->GetPassageBridgeRanges();
|
||||
result.reserve(ranges.size());
|
||||
for(const auto& range:ranges)
|
||||
const auto& crossings=impl_->shape->GetPassageVoidCrossings();
|
||||
result.reserve(crossings.size());
|
||||
for(const auto& crossing:crossings)
|
||||
{
|
||||
PassageBridgeRangeAuditV1 value;
|
||||
value.passageIndex=range.passageIndex;value.firstSegment=range.firstSegment;
|
||||
value.lastSegment=range.lastSegment;value.unsupportedSamples=range.unsupportedSamples;
|
||||
value.unsupportedLengthMetres=range.unsupportedLengthMetres;
|
||||
value.startPointSubvoxels=range.startPoint;value.endPointSubvoxels=range.endPoint;
|
||||
PassageVoidCrossingAuditV1 value;
|
||||
value.passageIndex=crossing.passageIndex;value.firstSegment=crossing.firstSegment;
|
||||
value.lastSegment=crossing.lastSegment;value.unsupportedSamples=crossing.unsupportedSamples;
|
||||
value.unsupportedLengthMetres=crossing.unsupportedLengthMetres;
|
||||
value.startPointSubvoxels=crossing.startPoint;value.endPointSubvoxels=crossing.endPoint;
|
||||
value.needsColumn=crossing.needsColumn;
|
||||
value.connectedAtStart=crossing.connectedAtStart;
|
||||
value.connectedAtEnd=crossing.connectedAtEnd;
|
||||
value.columnAxisSubvoxels={crossing.centerXSubvoxels,crossing.centerYSubvoxels};
|
||||
value.columnTopZSubvoxels=crossing.topZSubvoxels;
|
||||
value.columnBottomZSubvoxels=crossing.bottomZSubvoxels;
|
||||
value.columnMinimumRadiusSubvoxels=crossing.minimumRadiusSubvoxels;
|
||||
value.columnNominalRadiusSubvoxels=crossing.nominalRadiusSubvoxels;
|
||||
value.columnMaximumRadiusSubvoxels=crossing.maximumRadiusSubvoxels;
|
||||
value.maximumRouteOffsetMetres=crossing.maximumRouteOffsetMetres;
|
||||
value.maximumBoreRadiusMetres=crossing.maximumBoreRadiusMetres;
|
||||
result.push_back(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool World::isPassageBridgeSample(std::size_t passageIndex,std::size_t segmentIndex,
|
||||
bool World::isPassageColumnSample(std::size_t passageIndex,std::size_t segmentIndex,
|
||||
Vec3d position) const
|
||||
{return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape
|
||||
&&impl_->shape->IsResolvedPassageBridgeSample(passageIndex,segmentIndex,
|
||||
&&impl_->shape->IsResolvedPassageColumnSample(passageIndex,segmentIndex,
|
||||
{position.x,position.y,position.z});}
|
||||
bool World::isPassageShortSpanSample(std::size_t passageIndex,std::size_t segmentIndex,
|
||||
Vec3d position) const
|
||||
{return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape
|
||||
&&impl_->shape->IsResolvedPassageShortSpanSample(passageIndex,segmentIndex,
|
||||
{position.x,position.y,position.z});}
|
||||
std::string World::densityDispatchPathAt(Vec3d position) const
|
||||
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
# VoxelForge L27 — passage voids inside rock columns
|
||||
|
||||
## Owner summary
|
||||
|
||||
The SurfaceWorld passage now drops from the ceiling (about −138 to −143 m) to terrain near
|
||||
−505 m inside one continuous, relief-bearing rock column. Its helix is cut inside the column; the
|
||||
~2 km route no longer crosses the open sky as a bridge. FloatingIslands uses the same field and
|
||||
planner path: the passage descends inside a column from its island to the bedrock floor. Styles 1
|
||||
and 3 have a second, smaller cap crossing, so they get a second column. The FI style-0 anchor stays
|
||||
at the L25 axis and remains connected. Its shared column is slimmer (11.25 m nominal radius vs
|
||||
L25's 13.25 m) with a larger measured wall margin (3.25 m vs 1.938 m), visible in the new
|
||||
exterior capture.
|
||||
|
||||
All four styles in both recipes pass on Clang and GCC: 0 blocked, 0 unsupported, 0 grade failures,
|
||||
0 open-air violations, and naturalness passes. The new no-long-bridge audit detects the L26
|
||||
SurfaceWorld bridge as its negative control.
|
||||
|
||||
## Column rule and size
|
||||
|
||||
The shared planner samples native support every 0.5 m. A contiguous unsupported route run longer
|
||||
than **12.5 m** becomes one column; this is 2.5 times the former 5 m support-thickness limit. A
|
||||
shorter span is allowed only while grounded. The column is a solid signed-distance union in the
|
||||
shared composition field, so terrain queries, collision and meshes all see the same rock. The
|
||||
recipe still chooses passage style; the helix applies inside a column and that style continues
|
||||
outside it.
|
||||
|
||||
The minimum planned outer radius is maximum route offset + bore radius + 2 m; the nominal profile
|
||||
adds 1.25 m. The base flares by up to 6 m and the ceiling end by up to 2 m; bounds extend 6 m
|
||||
below and 5 m above the unsupported run to overlap the native rock. Measured wall margin is 1.75 m
|
||||
minimum in SW and 3.0 m minimum in FI, above the 1.5 m requirement. SW columns span about
|
||||
363–368 m vertically and have nominal radii 19.744–27.027 m. FI main columns span about 53–57 m
|
||||
with nominal radii 9.251–12.746 m; styles 1 and 3 also have ~14 m cap columns.
|
||||
|
||||
| Recipe / style | Unsupported route run(s) | Column nominal radius; audited profile radius | Minimum wall |
|
||||
|---|---:|---:|---:|
|
||||
| SW 0 Straight | 2,004.815 m; z −142.687 to −505.109 m | 27.027 m; 25.527–27.777 m | 1.75 m |
|
||||
| SW 1 Worm | 1,946.355 m; z −138.668 to −505.109 m | 24.187 m; 22.687–24.937 m | 1.75 m |
|
||||
| SW 2 Spiral | 1,957.892 m; z −137.967 to −505.138 m | 19.744 m; 18.244–20.744 m | 1.75 m |
|
||||
| SW 3 Cascading | 1,981.103 m; z −137.484 to −505.112 m | 22.791 m; 21.291–23.541 m | 1.75 m |
|
||||
| FI 0 Straight | 249.982 m; z −65.846 to −122.390 m | 11.250 m; 11.250–12.500 m | 3.25 m |
|
||||
| FI 1 Worm | 17.042 m and 249.965 m; z −61.574 to −122.416 m | 12.719 m and 12.746 m; 12.968–16.468 m and 12.746–13.996 m | 3.25 m |
|
||||
| FI 2 Spiral | 244.479 m; z −68.891 to −122.382 m | 9.251 m; 9.000–10.500 m | 3.00 m |
|
||||
| FI 3 Cascading | 17.955 m and 231.814 m; z −65.805 to −122.436 m | 11.250 m each; 11.250–14.000 m and 11.250–12.500 m | 3.25 m |
|
||||
|
||||
The helix path measurements are length / turns / mean path radius / vertical pitch. SW styles 0–3
|
||||
measure **2,015.1 m / 16.88 / 18.96 m / 21.00 m**, **1,956.1 / 20.69 / 14.99 / 17.32 m**,
|
||||
**1,963.0 / 61.62 / 5.02 / 5.81 m**, and **1,987.2 / 21.63 / 14.50 / 16.59 m**. FI styles 0–3
|
||||
measure **254.0 m / 8.00 / 5.00 m / 5.78 m**, **254.0 / 7.99 / 4.97 / 5.72 m**,
|
||||
**246.9 / 12.95 / 3.00 / 3.31 m**, and **235.5 / 7.94 / 4.50 / 5.37 m**. FI styles 1 and 3
|
||||
also have short cap descents: respectively 22.1 m / 0.59 turns / 5.70 m radius / 6.71 m pitch,
|
||||
and 22.2 m / 0.75 turns / 4.54 m radius / 5.37 m pitch. Main helix grades are about 10.0–10.5°;
|
||||
the walkability audit reports zero grade failures.
|
||||
|
||||
## Audits
|
||||
|
||||
Clang and GCC each ran all eight recipe/style combinations. Every row has 0 blocked capsule
|
||||
samples, 0 unsupported samples, 0 grade failures, 0 open-air violations, connected column ends
|
||||
(2/2), 128/128 route-support samples for each main crossing, and passing walkability. The naturalness wall-relief audit measured 0.509–0.806 m against
|
||||
the 0.200 m minimum; flat runs were 0 m against the 8 m limit. The strict V3 layout validator
|
||||
accepted with zero issues and rejected its malformed controls 9/9.
|
||||
|
||||
The no-long-bridge threshold is 12.5 m. SW has 0 m uncolumned span in all styles. FI's longest
|
||||
short grounded span is 4.464 m (style 0); every other span is shorter. The L26 SW bridge control
|
||||
(1,946–2,005 m unsupported) was detected in all four SW styles. The L24 ungated-support and L23
|
||||
smooth-wall naturalness controls were detected in all eight recipe/style rows. The 16 final matrix logs are
|
||||
`Saved/Linux/linux27-final-audit-{surface-world,floating-islands}-style{0,1,2,3}-{clang,gcc}.log`.
|
||||
|
||||
## Captures
|
||||
|
||||
All five JPEGs are 16:9 and below 300 KB in `captures/`.
|
||||
|
||||
| Capture | View | Size | SHA-256 |
|
||||
|---|---|---:|---|
|
||||
| [sw-column-wide.jpg](captures/sw-column-wide.jpg) | Wide side section: ceiling, full column and local ground; 1280×720 | 27 KB | `63e621794a48747c5892b74d3313da1df93e95bc1fb5e23c511211f992023acf` |
|
||||
| [sw-column-base.jpg](captures/sw-column-base.jpg) | Close base flare into terrain; 1280×720 | 27 KB | `c4dbf7bcbd4962cf0e2c4f567cceec798deac8686a85cd02716ebd3c21db43bf` |
|
||||
| [sw-column-top.jpg](captures/sw-column-top.jpg) | Close ceiling blend and upper helix; 1280×720 | 30 KB | `fe83fbb0bf90ea51696f9f63129738eb55812c01bbbec65e7430b8a7e6b512ed` |
|
||||
| [sw-column-cutaway.jpg](captures/sw-column-cutaway.jpg) | Full-height cutaway through the helix; 1920×1080 | 60 KB | `8233e80035aac69dec7fd6d2e77a46e5798f627aefe216671d884bf84b339c40` |
|
||||
| [fi-anchor-pillar.jpg](captures/fi-anchor-pillar.jpg) | L27 FI anchor pillar from outside; 1280×720 | 21 KB | `5bb8ad6a0729818577764596d3fa9ccff9621b31217ee9d8e34401098e6b4ef6` |
|
||||
|
||||
## Cost, determinism and compatibility
|
||||
|
||||
The whole-view timing comparison alternated two L24 and two L27 Clang W12 runs per recipe. No
|
||||
prebuilt L24 executable was present, so I rebuilt it from local commit `cb9236c` with Clang 19 and
|
||||
FP contraction off under `Saved/Linux/l24-source/bin/linux/`.
|
||||
|
||||
| Recipe | L24 whole-view seconds, two runs (mean) | L27 whole-view seconds, two runs (mean) | Change | Tiles, both versions |
|
||||
|---|---:|---:|---:|---:|
|
||||
| SurfaceWorld | 1.644 / 1.663 (1.654) | 2.207 / 2.252 (2.230) | +0.576 s (+34.8%) | 2,370 total; 960 full detail |
|
||||
| Floating Islands | 15.907 / 16.848 (16.378) | 19.403 / 19.649 (19.526) | +3.149 s (+19.2%) | 2,370 total; 960 full detail |
|
||||
|
||||
No tiles were added. The L24 and L27 per-run logs are `Saved/Linux/linux27-cost-interleaved-*.log`.
|
||||
|
||||
The L25 whole-view command produced identical hashes across Clang/GCC and W1/W8/W12. Hash order
|
||||
is density / mesh / payload / topology:
|
||||
|
||||
| Recipe | L27 whole-view hash |
|
||||
|---|---|
|
||||
| SurfaceWorld | `117547e5e18c6847 / db5fa276315ff2d4 / b4e9d01d70413fea / 03caf881fa047f1c` |
|
||||
| Floating Islands | `70ab5bc87dd174d2 / 6f357f9085a496f0 / 08ce7d4e036da65a / eb5eef04a5cb85d9` |
|
||||
|
||||
Full-detail hashes also match across the matrix: SW `ace56c8f205238ad / a9c603a1248de6b8 /
|
||||
ea55f1f4fcc6a894 / 5ea7bc78a1b678ae`; FI `ed550e0c8b182695 / 0e410a8e80a3e7f7 /
|
||||
0751926f0c67d8a4 / f01208bc2f7841ec`. Exact-query hashes (density / unclamped / detail / collision)
|
||||
are SW `c949c4df2ce6f9d9 / c949c4df2ce6f9d9 / 114c501e2d830d94 / e6011d3a248d4849` and FI
|
||||
`7b7c50ac24ccadcc / 7b7c50ac24ccadcc / efcba20c0c70c415 / e604823a249029bf`.
|
||||
|
||||
The 24 m crossing processed 314 moving tiles and matched across both compilers:
|
||||
|
||||
| Recipe | Moving-tile hash: density / mesh / payload / topology |
|
||||
|---|---|
|
||||
| SurfaceWorld | `fe83368d746bf44d / d4379e0f86fa5780 / 0d1d6659a251dcaf / 73940f21b0fdaf14` |
|
||||
| Floating Islands | `e3d0ea833bca4215 / f4b27f4bd1adbb07 / 8aedcdc86e5f21a2 / 323f880d952306e9` |
|
||||
|
||||
L13 parity passed for both compilers and recipes: 8 page pairs, 14 higher-LOD pairs, one edit pair,
|
||||
782,026 exact-query samples, zero mismatches. At a 10 km off-route view, L9 proofs checked 6,896
|
||||
blocks / 9,178,576 samples with zero violations on both compilers. SW had 1,296 fallbacks and no
|
||||
unknown blocks; FI had 18,704 fallbacks, 17,408 unknown blocks and zero violations.
|
||||
|
||||
At a 100 km SW view, L26 and L27 were bit-identical on Clang and GCC: full-detail hash
|
||||
`3148a6af10901e4d / 7025b8fc8d4636cd / ca0a8081022340bf / 1c49e72a6636ae99`, whole-view hash
|
||||
`a212129dc5f2bfcf / e9b3980b69c789bf / 785a394605885d1f / 8f7e3a55bf429175`, exact-query hash
|
||||
`482500abbe835b11 / c4948c7179963784 / efcba20c0c70c415 / 137e8f6c0b03a624`; LOD0–8 also match.
|
||||
At 10 km, the whole-view hash changes because the new column enters LOD8; full detail, exact queries
|
||||
and LOD0–7 remain identical.
|
||||
|
||||
Headless binary SHA-256: Clang `81ae5a03765eb3937f53c10e8f8b82cf1b38d3e3b6240511a57e5d66d99a9755`,
|
||||
GCC `30c8927eee3c8c2ec0e7ff72228872435d19527719bedbb2399e6dcaff4819a4`. Rebuilt L24 Clang
|
||||
binary: `29bb68ed425b4770b1b4ddcc68996c5d680665014e7ab7ffb3c307523015cf0b`.
|
||||
|
||||
## Changes and invariants
|
||||
|
||||
The deterministic crossing planner and field composition are shared in
|
||||
`Standalone/Core/src/ShapeWorldInternal.h`. `VoxelForgeCore.h/.cpp` expose the void-crossing audit;
|
||||
`StreamBenchmarkPipeline.h` adds column, open-air and no-long-bridge audits with the L26 negative
|
||||
control. `FloatingIslandsInternal.h` retires the old special-case root for geometry 8, while
|
||||
`OwnerWorldLoader.h` and `WorldAssemblyPrototype.h` use the shared crossing plan and version.
|
||||
Geometry/evaluator versions are now 8/14; resolved-layout schema V3 is unchanged. The only Unreal
|
||||
edit is the matching geometry-version bump in `Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp`;
|
||||
Unreal was not available to compile here.
|
||||
L4 lazy caches, L7 shared cache, L9/L12 proofs, L11 readiness handling, L13 route parity, L18/L19
|
||||
ceilings and L21/L22 layout behavior remain unchanged. No async path was added.
|
||||
|
||||
## Failed and retried runs
|
||||
|
||||
- Two temporary Clang backtrace builds failed on `signal` namespace qualification; corrected before
|
||||
the instrumentation was removed. The first SW style-0 audit segfaulted because the planner read
|
||||
shared-field intervals before they were built; build order was corrected. An early SW style-1
|
||||
iteration had 3,816 open-air violations and 869 blocked samples at the lower mouth; the carve
|
||||
guard fixed it.
|
||||
- Early FI audit revisions mis-tagged segments and reported false open-air spans; corrected to use
|
||||
the actual route ranges and physical field. FI style 2 briefly had two unsupported samples; the
|
||||
seam extension fixed them. A 3 m cap left 13 FI style-0 open-air samples; the 5 m cap cleared
|
||||
them. A relaxed tread guard introduced one SW style-1 blocked sample and was reverted.
|
||||
- A layout-only invocation without the required streaming flags exited 2. During capture setup,
|
||||
`--hash` and `--data=` were rejected; the correct switches are `--stream-hash` and a positional
|
||||
data path. Those were corrected. A helix-metrics helper first failed while formatting a list and
|
||||
was rerun successfully.
|
||||
- The first close SW export at max clip level 1 hit the 160-million-node interval safety bound.
|
||||
Two finer endpoint exports then grew past 1.2 GB without a final pipeline summary and were
|
||||
stopped; the already-complete full-column cutaway mesh supplied the final base/top views. The
|
||||
first wide camera hid the base; a cut-plane side view fixed it. The first FI close camera cropped
|
||||
the endpoints and was moved back.
|
||||
- The first FI max-clip-level-3 export grew past 1.6 GB without a summary and was stopped. The
|
||||
max-clip-level-8 retry completed in 21.7 s with a 104 MB mesh. The final capture is from that
|
||||
L27 mesh. All rejected, stopped and successful export logs are in `Saved/Linux/`.
|
||||
|
||||
No run exceeded 30 minutes or remained stuck for 15 minutes. Only the requested small JPEGs and
|
||||
this report were written under `Standalone/Research/WorldAssemblerM3/Linux27/`; exports and logs
|
||||
remain in `Saved/Linux/`. The pre-existing untracked `Linux18/exports/` directory was left alone.
|
||||
No commit, push or stash was made.
|
||||
|
||||
## Orchestrator review (Claude) — side branch
|
||||
|
||||
- Look approved by me: `sw-column-wide.jpg` and `sw-column-cutaway.jpg` show one slender ceiling-to-ground
|
||||
column with the helix inside; `sw-column-base.jpg` shows the base meeting the terrain with a natural crack
|
||||
exit; `fi-anchor-pillar.jpg` shows a textured pillar with a flared base under the island.
|
||||
- **Blocking merge: cost.** Whole view +34.8% SW (1.654 → 2.230 s) and +19.2% FI with no new tiles. A local
|
||||
feature must cost locally. L28: bound column/helix evaluation spatially (feature lists / AABB culling), then
|
||||
merge `wip/l25-helix` into `assembler/world`.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -24,7 +24,7 @@ struct FStandaloneParitySample
|
||||
int32 StrateIndex = INDEX_NONE;
|
||||
};
|
||||
|
||||
struct FStandalonePassageBridgeRange
|
||||
struct FStandalonePassageVoidCrossing
|
||||
{
|
||||
std::size_t passageIndex=0;
|
||||
std::size_t firstSegment=0;
|
||||
@@ -33,12 +33,24 @@ struct FStandalonePassageBridgeRange
|
||||
double unsupportedLengthMetres=0.0;
|
||||
std::array<double,4> startPoint{};
|
||||
std::array<double,4> endPoint{};
|
||||
bool needsColumn=false;
|
||||
bool connectedAtStart=false;
|
||||
bool connectedAtEnd=false;
|
||||
std::int64_t centerXSubvoxels=0;
|
||||
std::int64_t centerYSubvoxels=0;
|
||||
std::int64_t topZSubvoxels=0;
|
||||
std::int64_t bottomZSubvoxels=0;
|
||||
std::int64_t minimumRadiusSubvoxels=0;
|
||||
std::int64_t nominalRadiusSubvoxels=0;
|
||||
std::int64_t maximumRadiusSubvoxels=0;
|
||||
double maximumRouteOffsetMetres=0.0;
|
||||
double maximumBoreRadiusMetres=0.0;
|
||||
};
|
||||
|
||||
struct FStandalonePassageBridgeCache
|
||||
struct FStandalonePassageVoidCrossingCache
|
||||
{
|
||||
std::mutex mutex;
|
||||
std::unordered_map<std::string,std::vector<FStandalonePassageBridgeRange>> byLayout;
|
||||
std::unordered_map<std::string,std::vector<FStandalonePassageVoidCrossing>> byLayout;
|
||||
};
|
||||
|
||||
struct FStandaloneOwnerWorld
|
||||
@@ -66,8 +78,8 @@ struct FStandaloneOwnerWorld
|
||||
std::uint32_t CoreVoxelSizeMicrons = 250000;
|
||||
std::uint32_t StrateLayoutChunkSizeMicrometres = 8000000;
|
||||
std::int64_t StrateLayoutChunkWidthSubvoxels = 8192;
|
||||
std::shared_ptr<FStandalonePassageBridgeCache> PassageBridgeCache =
|
||||
std::make_shared<FStandalonePassageBridgeCache>();
|
||||
std::shared_ptr<FStandalonePassageVoidCrossingCache> PassageVoidCrossingCache =
|
||||
std::make_shared<FStandalonePassageVoidCrossingCache>();
|
||||
};
|
||||
|
||||
namespace StandaloneOwnerLoader
|
||||
|
||||
@@ -726,7 +726,8 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
struct FPassageOpenAirAudit
|
||||
{
|
||||
std::uint64_t supportSamples=0,violations=0,legacyViolations=0,bridgeSamples=0;
|
||||
std::uint64_t supportSamples=0,violations=0,legacyViolations=0;
|
||||
std::uint64_t columnSamples=0,shortSpanSamples=0;
|
||||
std::uint64_t routeSupportSamples=0,routeSupportViolations=0;
|
||||
std::array<std::array<double,4>,8> firstViolations{};
|
||||
std::size_t firstViolationCount=0;
|
||||
@@ -773,10 +774,14 @@ namespace StandaloneStreamBenchmark
|
||||
++audit.legacyViolations;
|
||||
if(nativeDensity>=0.0f&&passageDensity<0.0f)
|
||||
{
|
||||
const bool connectedBridge=!routeSupport
|
||||
const bool connectedColumn=!routeSupport
|
||||
&&passageSegment!=std::numeric_limits<std::size_t>::max()
|
||||
&&Field.isPassageBridgeSample(passageIndex,passageSegment,sample);
|
||||
if(connectedBridge){++audit.bridgeSamples;return;}
|
||||
&&Field.isPassageColumnSample(passageIndex,passageSegment,sample);
|
||||
if(connectedColumn){++audit.columnSamples;return;}
|
||||
const bool groundedShortSpan=!routeSupport
|
||||
&&passageSegment!=std::numeric_limits<std::size_t>::max()
|
||||
&&Field.isPassageShortSpanSample(passageIndex,passageSegment,sample);
|
||||
if(groundedShortSpan){++audit.shortSpanSamples;return;}
|
||||
audit.routeSupportViolations+=routeSupport;
|
||||
if(audit.firstViolationCount<audit.firstViolations.size())
|
||||
audit.firstViolations[audit.firstViolationCount++]={xMetres,yMetres,
|
||||
@@ -894,20 +899,23 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
return audit;
|
||||
};
|
||||
struct FPassageBridgeAudit
|
||||
struct FPassageColumnAudit
|
||||
{
|
||||
std::uint64_t spans=0,connectedEnds=0,crossSections=0,crossSectionFailures=0;
|
||||
double minimumThicknessMetres=std::numeric_limits<double>::infinity();
|
||||
double maximumThicknessMetres=0.0;
|
||||
double thicknessReliefPeakToPeakMetres=0.0;
|
||||
std::uint64_t columns=0,shortSpans=0,connectedEnds=0,routeSections=0;
|
||||
std::uint64_t routeSectionFailures=0,wallSections=0,wallFailures=0;
|
||||
double minimumWallMetres=std::numeric_limits<double>::infinity();
|
||||
double minimumSurfaceRadiusMetres=std::numeric_limits<double>::infinity();
|
||||
double maximumSurfaceRadiusMetres=0.0;
|
||||
double surfaceRadiusReliefMetres=0.0;
|
||||
double longestShortSpanMetres=0.0;
|
||||
bool passed=true;
|
||||
};
|
||||
const auto auditPassageBridges=[&](const VoxelForgeCore::World& Field,
|
||||
const auto auditPassageColumns=[&](const VoxelForgeCore::World& Field,
|
||||
const VoxelForgeCore::World& Native,
|
||||
const VoxelForgeCore::ResolvedLayoutPassageV1& passage,
|
||||
std::size_t passageIndex)
|
||||
{
|
||||
FPassageBridgeAudit audit;
|
||||
FPassageColumnAudit audit;
|
||||
const double qMetres=coreMetres
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
constexpr std::array<std::array<double,2>,5> supportOffsets{{
|
||||
@@ -928,37 +936,67 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
return supported>=3;
|
||||
};
|
||||
const auto bridgeSolidAt=[&](std::size_t segment,double x,double y,double z)
|
||||
const auto columnSolidAt=[&](std::size_t segment,double x,double y,double z)
|
||||
{
|
||||
return Field.isPassageBridgeSample(passageIndex,segment,
|
||||
return Field.isPassageColumnSample(passageIndex,segment,
|
||||
{x/coreMetres,y/coreMetres,z/coreMetres});
|
||||
};
|
||||
const auto thicknessAt=[&](std::size_t segment,double x,double y,double floor)
|
||||
const auto shortSpanSolidAt=[&](std::size_t segment,double x,double y,double z)
|
||||
{
|
||||
double thickness=0.0;
|
||||
for(double depth=0.125;depth<=6.0+1.0e-6;depth+=0.125)
|
||||
{
|
||||
if(!bridgeSolidAt(segment,x,y,floor-depth))break;
|
||||
thickness=depth;
|
||||
}
|
||||
return thickness;
|
||||
return Field.isPassageShortSpanSample(passageIndex,segment,
|
||||
{x/coreMetres,y/coreMetres,z/coreMetres});
|
||||
};
|
||||
for(const auto& range:Field.passageBridgeRanges())
|
||||
const auto crossings=Field.passageVoidCrossings();
|
||||
for(const auto& crossing:crossings)
|
||||
{
|
||||
if(range.passageIndex!=passageIndex
|
||||
||range.lastSegment+1>=passage.controlPoints.size())continue;
|
||||
++audit.spans;
|
||||
const bool connectedStart=endpointConnected(range.startPointSubvoxels);
|
||||
const bool connectedEnd=endpointConnected(range.endPointSubvoxels);
|
||||
if(crossing.passageIndex!=passageIndex)continue;
|
||||
const bool connectedStart=endpointConnected(crossing.startPointSubvoxels);
|
||||
const bool connectedEnd=endpointConnected(crossing.endPointSubvoxels);
|
||||
if(!crossing.needsColumn)
|
||||
{
|
||||
++audit.shortSpans;
|
||||
audit.longestShortSpanMetres=std::max(audit.longestShortSpanMetres,
|
||||
crossing.unsupportedLengthMetres);
|
||||
const bool shortSpanPass=connectedStart&&connectedEnd
|
||||
&&crossing.unsupportedLengthMetres<=12.5
|
||||
&&crossing.lastSegment+1<passage.controlPoints.size();
|
||||
audit.passed&=shortSpanPass;
|
||||
std::printf("[PassageGroundedShortSpanAudit] id=%s segments=%zu..%zu length_m=%.3f threshold_m=12.500 connected=%d/2 status=%s\n",
|
||||
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
|
||||
crossing.unsupportedLengthMetres,
|
||||
static_cast<int>(connectedStart)+static_cast<int>(connectedEnd),
|
||||
shortSpanPass?"PASS":"FAIL");
|
||||
const std::size_t segment=std::min(crossing.firstSegment,
|
||||
passage.controlPoints.size()-2);
|
||||
const auto& a=passage.controlPoints[segment];
|
||||
const auto& b=passage.controlPoints[segment+1];
|
||||
const double x=(qToMetres(a.positionSubvoxels[0])
|
||||
+qToMetres(b.positionSubvoxels[0]))*0.5;
|
||||
const double y=(qToMetres(a.positionSubvoxels[1])
|
||||
+qToMetres(b.positionSubvoxels[1]))*0.5;
|
||||
const double z=(qToMetres(a.positionSubvoxels[2])
|
||||
+qToMetres(b.positionSubvoxels[2]))*0.5
|
||||
-qToMetres(a.radiusSubvoxels+b.radiusSubvoxels)*0.5+0.13;
|
||||
audit.passed&=shortSpanSolidAt(segment,x,y,z-0.25);
|
||||
continue;
|
||||
}
|
||||
if(crossing.lastSegment+1>=passage.controlPoints.size())
|
||||
{audit.passed=false;continue;}
|
||||
++audit.columns;
|
||||
audit.connectedEnds+=connectedStart+connectedEnd;
|
||||
const std::size_t segmentCount=range.lastSegment-range.firstSegment+1;
|
||||
const std::size_t probeCount=std::min<std::size_t>(256,segmentCount);
|
||||
double spanMinimumThickness=std::numeric_limits<double>::infinity();
|
||||
double spanMaximumThickness=0.0;
|
||||
std::uint64_t spanCrossSections=0,spanCrossSectionFailures=0;
|
||||
const double axisX=qToMetres(crossing.columnAxisSubvoxels[0]);
|
||||
const double axisY=qToMetres(crossing.columnAxisSubvoxels[1]);
|
||||
const double minimumRadius=qToMetres(crossing.columnMinimumRadiusSubvoxels);
|
||||
const double maximumRadius=qToMetres(crossing.columnMaximumRadiusSubvoxels);
|
||||
const double minimumWall=minimumRadius-crossing.maximumRouteOffsetMetres
|
||||
-crossing.maximumBoreRadiusMetres-0.50;
|
||||
audit.minimumWallMetres=std::min(audit.minimumWallMetres,minimumWall);
|
||||
const std::size_t segmentCount=crossing.lastSegment-crossing.firstSegment+1;
|
||||
const std::size_t probeCount=std::min<std::size_t>(128,segmentCount);
|
||||
std::uint64_t crossingRouteFailures=0,crossingWallFailures=0;
|
||||
for(std::size_t probe=0;probe<probeCount;++probe)
|
||||
{
|
||||
const std::size_t segment=range.firstSegment
|
||||
const std::size_t segment=crossing.firstSegment
|
||||
+std::min(segmentCount-1,(probe*segmentCount)/probeCount);
|
||||
const auto& a=passage.controlPoints[segment];
|
||||
const auto& b=passage.controlPoints[segment+1];
|
||||
@@ -973,42 +1011,96 @@ namespace StandaloneStreamBenchmark
|
||||
const double radius=(qToMetres(a.radiusSubvoxels)
|
||||
+qToMetres(b.radiusSubvoxels))*0.5;
|
||||
const double floor=z-radius+0.13;
|
||||
const double thickness=thicknessAt(segment,x,y,floor);
|
||||
spanMinimumThickness=std::min(spanMinimumThickness,thickness);
|
||||
spanMaximumThickness=std::max(spanMaximumThickness,thickness);
|
||||
audit.minimumThicknessMetres=std::min(audit.minimumThicknessMetres,thickness);
|
||||
audit.maximumThicknessMetres=std::max(audit.maximumThicknessMetres,thickness);
|
||||
const double dx=bx-ax,dy=by-ay,horizontal=std::hypot(dx,dy);
|
||||
if(horizontal<=1.0e-6)continue;
|
||||
const double nx=dy/horizontal,ny=-dx/horizontal;
|
||||
bool crossSectionSolid=true;
|
||||
for(const double offset:{-2.5,0.0,2.5})
|
||||
crossSectionSolid&=bridgeSolidAt(segment,x+nx*offset,
|
||||
y+ny*offset,floor-2.0);
|
||||
++audit.crossSections;
|
||||
++spanCrossSections;
|
||||
spanCrossSectionFailures+=!crossSectionSolid;
|
||||
audit.crossSectionFailures+=!crossSectionSolid;
|
||||
const VoxelForgeCore::Vec3d supportPoint{
|
||||
x/coreMetres,y/coreMetres,(floor-2.0)/coreMetres};
|
||||
const bool solidUnderRoute=columnSolidAt(segment,x,y,floor-2.0)
|
||||
||Native.densityAt(supportPoint)<0.0f;
|
||||
++audit.routeSections;
|
||||
audit.routeSectionFailures+=!solidUnderRoute;
|
||||
crossingRouteFailures+=!solidUnderRoute;
|
||||
const double radial=std::hypot(x-axisX,y-axisY);
|
||||
const double wallRadius=radial+radius+1.5;
|
||||
const double outwardX=radial>1.0e-6?(x-axisX)/radial:1.0;
|
||||
const double outwardY=radial>1.0e-6?(y-axisY)/radial:0.0;
|
||||
const bool wallSolid=columnSolidAt(segment,
|
||||
axisX+outwardX*wallRadius,axisY+outwardY*wallRadius,z);
|
||||
++audit.wallSections;
|
||||
audit.wallFailures+=!wallSolid;
|
||||
crossingWallFailures+=!wallSolid;
|
||||
}
|
||||
const double relief=std::isfinite(spanMinimumThickness)
|
||||
?spanMaximumThickness-spanMinimumThickness:0.0;
|
||||
audit.thicknessReliefPeakToPeakMetres=std::max(
|
||||
audit.thicknessReliefPeakToPeakMetres,relief);
|
||||
const bool spanPass=connectedStart&&connectedEnd
|
||||
&&spanCrossSections>=3&&spanCrossSectionFailures==0
|
||||
&&spanMinimumThickness>=2.75
|
||||
&&spanMaximumThickness<=6.5
|
||||
&&audit.thicknessReliefPeakToPeakMetres>=0.20;
|
||||
audit.passed&=spanPass;
|
||||
std::printf("[PassageRockBridgeAudit] id=%s segments=%zu..%zu gap_m=%.3f connected_ends=%d/2 width_at_least_m=5.000 cross_sections=%llu/%llu thickness_m=%.3f..%.3f relief_p2p_m=%.3f status=%s\n",
|
||||
passage.stableId.c_str(),range.firstSegment,range.lastSegment,
|
||||
range.unsupportedLengthMetres,static_cast<int>(connectedStart)
|
||||
+static_cast<int>(connectedEnd),
|
||||
static_cast<unsigned long long>(spanCrossSections-spanCrossSectionFailures),
|
||||
static_cast<unsigned long long>(spanCrossSections),
|
||||
std::isfinite(spanMinimumThickness)?spanMinimumThickness:0.0,
|
||||
spanMaximumThickness,relief,
|
||||
spanPass?"PASS":"FAIL");
|
||||
double crossingMinimumSurfaceRadius=std::numeric_limits<double>::infinity();
|
||||
double crossingMaximumSurfaceRadius=0.0;
|
||||
std::array<double,6> meanRadiusByHeight{};
|
||||
std::size_t heightSamples=0;
|
||||
constexpr double RayStepMetres=0.25;
|
||||
constexpr std::array<double,6> HeightFractions{{0.25,0.35,0.45,0.55,0.65,0.75}};
|
||||
constexpr std::size_t DirectionCount=16;
|
||||
const double topMetres=qToMetres(crossing.columnTopZSubvoxels);
|
||||
const double bottomMetres=qToMetres(crossing.columnBottomZSubvoxels);
|
||||
const double rayStart=crossing.maximumRouteOffsetMetres
|
||||
+crossing.maximumBoreRadiusMetres+0.75;
|
||||
for(const double heightFraction:HeightFractions)
|
||||
{
|
||||
const double z=topMetres+(bottomMetres-topMetres)*heightFraction;
|
||||
double heightMeanRadius=0.0;
|
||||
std::uint64_t heightRayCount=0;
|
||||
for(std::size_t direction=0;direction<DirectionCount;++direction)
|
||||
{
|
||||
const double angle=2.0*3.14159265358979323846
|
||||
*direction/DirectionCount;
|
||||
double lastSolidRadius=0.0;
|
||||
bool enteredColumn=false;
|
||||
const std::size_t raySteps=static_cast<std::size_t>(std::ceil(
|
||||
(maximumRadius+2.0-rayStart)/RayStepMetres));
|
||||
for(std::size_t step=0;step<=raySteps;++step)
|
||||
{
|
||||
const double rayRadius=rayStart+step*RayStepMetres;
|
||||
const VoxelForgeCore::Vec3d point{
|
||||
(axisX+rayRadius*std::cos(angle))/coreMetres,
|
||||
(axisY+rayRadius*std::sin(angle))/coreMetres,
|
||||
z/coreMetres};
|
||||
if(Field.densityAt(point)<0.0f)
|
||||
{enteredColumn=true;lastSolidRadius=rayRadius;}
|
||||
else if(enteredColumn)break;
|
||||
}
|
||||
if(!enteredColumn)continue;
|
||||
heightMeanRadius+=lastSolidRadius;
|
||||
++heightRayCount;
|
||||
crossingMinimumSurfaceRadius=std::min(crossingMinimumSurfaceRadius,
|
||||
lastSolidRadius);
|
||||
crossingMaximumSurfaceRadius=std::max(crossingMaximumSurfaceRadius,
|
||||
lastSolidRadius);
|
||||
}
|
||||
if(heightRayCount==0)continue;
|
||||
meanRadiusByHeight[heightSamples++]=heightMeanRadius/heightRayCount;
|
||||
}
|
||||
const double crossingSurfaceRelief=heightSamples>=2
|
||||
?*std::max_element(meanRadiusByHeight.begin(),
|
||||
meanRadiusByHeight.begin()+heightSamples)
|
||||
-*std::min_element(meanRadiusByHeight.begin(),
|
||||
meanRadiusByHeight.begin()+heightSamples):0.0;
|
||||
audit.minimumSurfaceRadiusMetres=std::min(audit.minimumSurfaceRadiusMetres,
|
||||
crossingMinimumSurfaceRadius);
|
||||
audit.maximumSurfaceRadiusMetres=std::max(audit.maximumSurfaceRadiusMetres,
|
||||
crossingMaximumSurfaceRadius);
|
||||
audit.surfaceRadiusReliefMetres=std::max(audit.surfaceRadiusReliefMetres,
|
||||
crossingSurfaceRelief);
|
||||
const double measuredWall=crossingMinimumSurfaceRadius
|
||||
-crossing.maximumRouteOffsetMetres-crossing.maximumBoreRadiusMetres;
|
||||
const bool columnPass=connectedStart&&connectedEnd
|
||||
&&minimumWall>=1.50&&crossingRouteFailures==0
|
||||
&&crossingWallFailures==0&&std::isfinite(measuredWall)
|
||||
&&measuredWall>=1.50&&crossingSurfaceRelief>=0.20;
|
||||
audit.passed&=columnPass;
|
||||
std::printf("[PassageRockColumnAudit] id=%s segments=%zu..%zu span_m=%.3f axis_m=(%.3f,%.3f) connected=%d/2 route_support=%llu/%llu wall_margin_m=%.3f threshold_m=1.500 radii_m=%.3f..%.3f relief_p2p_m=%.3f nominal_radius_m=%.3f status=%s\n",
|
||||
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
|
||||
crossing.unsupportedLengthMetres,axisX,axisY,
|
||||
static_cast<int>(connectedStart)+static_cast<int>(connectedEnd),
|
||||
static_cast<unsigned long long>(probeCount-crossingRouteFailures),
|
||||
static_cast<unsigned long long>(probeCount),measuredWall,
|
||||
crossingMinimumSurfaceRadius,crossingMaximumSurfaceRadius,
|
||||
crossingSurfaceRelief,qToMetres(crossing.columnNominalRadiusSubvoxels),
|
||||
columnPass?"PASS":"FAIL");
|
||||
}
|
||||
return audit;
|
||||
};
|
||||
@@ -1022,6 +1114,7 @@ namespace StandaloneStreamBenchmark
|
||||
Assembly->floatingIslands.end(),[&](const auto& value)
|
||||
{return value.strateIndex==stratum.strateIndex;});
|
||||
if(anchor==stratum.anchors.end()||islandPlan==Assembly->floatingIslands.end())continue;
|
||||
if(islandPlan->manifest.recipe.geometryVersion>=8)continue;
|
||||
const auto ordinal=islandPlan->manifest.recipe.anchorIslandOrdinal;
|
||||
if(ordinal>=islandPlan->manifest.plan.islands.size())continue;
|
||||
const auto& anchorIsland=islandPlan->manifest.plan.islands[ordinal];
|
||||
@@ -1110,9 +1203,34 @@ namespace StandaloneStreamBenchmark
|
||||
};
|
||||
std::uint64_t passageCount=0,passagePasses=0,blockedControls=0,naturalPasses=0;
|
||||
std::uint64_t openAirViolations=0,openAirLegacyViolations=0,openAirSamples=0;
|
||||
std::uint64_t openAirAudits=0,openAirBridgeSamples=0,bridgeSpans=0,
|
||||
bridgeConnectedEnds=0;
|
||||
bool bridgeAuditsPassed=true;
|
||||
std::uint64_t openAirAudits=0,openAirColumnSamples=0,openAirShortSpanSamples=0;
|
||||
std::uint64_t columnCrossings=0,shortGroundedSpans=0,columnConnectedEnds=0;
|
||||
std::uint64_t columnRouteFailures=0,columnWallFailures=0;
|
||||
double longestUncolumnedSpanMetres=0.0,longestShortSpanMetres=0.0;
|
||||
double l26LegacyBridgeSpanMetres=0.0;
|
||||
bool columnAuditsPassed=true,noLongBridgeAuditPassed=true;
|
||||
bool l26NegativeControlApplicable=false;
|
||||
const auto plannedVoidCrossings=Shape.passageVoidCrossings();
|
||||
for(const auto& crossing:plannedVoidCrossings)
|
||||
{
|
||||
if(crossing.needsColumn)continue;
|
||||
longestUncolumnedSpanMetres=std::max(longestUncolumnedSpanMetres,
|
||||
crossing.unsupportedLengthMetres);
|
||||
}
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
|
||||
++passageIndex)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
const auto* upper=findStratum(passage.upperStrateIndex);
|
||||
const auto* lower=findStratum(passage.lowerStrateIndex);
|
||||
if(!upper||!lower||lower->generatorKind!="SurfaceWorld"
|
||||
||upper->generatorKind=="FloatingIslands")continue;
|
||||
l26NegativeControlApplicable=true;
|
||||
for(const auto& crossing:plannedVoidCrossings)
|
||||
if(crossing.passageIndex==passageIndex)
|
||||
l26LegacyBridgeSpanMetres=std::max(l26LegacyBridgeSpanMetres,
|
||||
crossing.unsupportedLengthMetres);
|
||||
}
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
@@ -1131,15 +1249,21 @@ namespace StandaloneStreamBenchmark
|
||||
const FPassageNaturalnessAudit naturalAudit=auditPassageNaturalness(Shape,passage);
|
||||
const FPassageOpenAirAudit openAirAudit=auditPassageOpenAir(Shape,blockedWorld,
|
||||
passage,passageIndex);
|
||||
const FPassageBridgeAudit bridgeAudit=auditPassageBridges(Shape,blockedWorld,
|
||||
const FPassageColumnAudit columnAudit=auditPassageColumns(Shape,blockedWorld,
|
||||
passage,passageIndex);
|
||||
openAirSamples+=openAirAudit.supportSamples;
|
||||
openAirViolations+=openAirAudit.violations;
|
||||
openAirLegacyViolations+=openAirAudit.legacyViolations;
|
||||
openAirBridgeSamples+=openAirAudit.bridgeSamples;
|
||||
bridgeSpans+=bridgeAudit.spans;
|
||||
bridgeConnectedEnds+=bridgeAudit.connectedEnds;
|
||||
bridgeAuditsPassed&=bridgeAudit.passed;
|
||||
openAirColumnSamples+=openAirAudit.columnSamples;
|
||||
openAirShortSpanSamples+=openAirAudit.shortSpanSamples;
|
||||
columnCrossings+=columnAudit.columns;
|
||||
shortGroundedSpans+=columnAudit.shortSpans;
|
||||
columnConnectedEnds+=columnAudit.connectedEnds;
|
||||
columnRouteFailures+=columnAudit.routeSectionFailures;
|
||||
columnWallFailures+=columnAudit.wallFailures;
|
||||
longestShortSpanMetres=std::max(longestShortSpanMetres,
|
||||
columnAudit.longestShortSpanMetres);
|
||||
columnAuditsPassed&=columnAudit.passed;
|
||||
++openAirAudits;
|
||||
const auto radialSquared=[](const std::array<std::int64_t,3>& p)
|
||||
{return p[0]*p[0]+p[1]*p[1];};
|
||||
@@ -1167,10 +1291,11 @@ namespace StandaloneStreamBenchmark
|
||||
static_cast<unsigned long long>(naturalAudit.wallSamples),
|
||||
static_cast<unsigned long long>(naturalAudit.missingWallSamples),
|
||||
naturalAudit.passed?"PASS":"FAIL");
|
||||
std::printf("[PassageOpenAirAudit] id=%s support_samples=%llu violations=%llu bridge_samples=%llu l24_ungated_violations=%llu route_support_samples=%llu route_support_violations=%llu spacing_m=%.3f status=%s\n",
|
||||
std::printf("[PassageOpenAirAudit] id=%s support_samples=%llu violations=%llu column_samples=%llu short_span_samples=%llu l24_ungated_violations=%llu route_support_samples=%llu route_support_violations=%llu spacing_m=%.3f status=%s\n",
|
||||
passage.stableId.c_str(),static_cast<unsigned long long>(openAirAudit.supportSamples),
|
||||
static_cast<unsigned long long>(openAirAudit.violations),
|
||||
static_cast<unsigned long long>(openAirAudit.bridgeSamples),
|
||||
static_cast<unsigned long long>(openAirAudit.columnSamples),
|
||||
static_cast<unsigned long long>(openAirAudit.shortSpanSamples),
|
||||
static_cast<unsigned long long>(openAirAudit.legacyViolations),
|
||||
static_cast<unsigned long long>(openAirAudit.routeSupportSamples),
|
||||
static_cast<unsigned long long>(openAirAudit.routeSupportViolations),
|
||||
@@ -1229,12 +1354,12 @@ namespace StandaloneStreamBenchmark
|
||||
const VoxelForgeCore::Vec3d nativePosition{sample[0]/coreMetres,
|
||||
sample[1]/coreMetres,sample[2]/coreMetres};
|
||||
const float nativeDensity=blockedWorld.densityAt(nativePosition);
|
||||
const bool bridgeVolume=Shape.isPassageBridgeSample(passageIndex,
|
||||
const bool passageColumn=Shape.isPassageColumnSample(passageIndex,
|
||||
nearestSegment,nativePosition);
|
||||
std::printf("[ResolvedPassageAuditSample] id=%s kind=blocked xyz_m=(%.3f,%.3f,%.3f) density=%.6f native_density=%.6f segment=%zu t=%.3f passage_segment=%zu passage_sdf_m=%.3f bridge_volume=%d native_path=%s\n",
|
||||
std::printf("[ResolvedPassageAuditSample] id=%s kind=blocked xyz_m=(%.3f,%.3f,%.3f) density=%.6f native_density=%.6f segment=%zu t=%.3f passage_segment=%zu passage_sdf_m=%.3f passage_column=%d native_path=%s\n",
|
||||
passage.stableId.c_str(),sample[0],sample[1],sample[2],sample[3],
|
||||
nativeDensity,nearestSegment,nearestT,nearestPassageSegment,
|
||||
nearestPassageSdf,bridgeVolume?1:0,
|
||||
nearestPassageSdf,passageColumn?1:0,
|
||||
blockedWorld.densityDispatchPathAt(nativePosition).c_str());
|
||||
}
|
||||
for(std::size_t i=0;i<passageAudit.firstUnsupportedCount;++i)
|
||||
@@ -1255,15 +1380,15 @@ namespace StandaloneStreamBenchmark
|
||||
const double firstZ=(sample[2]-0.125)/coreMetres;
|
||||
const VoxelForgeCore::Vec3d bridgeProbe{sample[0]/coreMetres,
|
||||
sample[1]/coreMetres,(sample[2]-0.25)/coreMetres};
|
||||
const bool bridgeAtSupport=Shape.isPassageBridgeSample(passageIndex,
|
||||
const bool columnAtSupport=Shape.isPassageColumnSample(passageIndex,
|
||||
segment,bridgeProbe);
|
||||
std::printf("[ResolvedPassageSupportSegment] id=%s segment=%zu route_z_m=%.3f..%.3f centers=%llu unsupported=%llu native_unsupported=%llu first_foot_m=(%.3f,%.3f,%.3f) bridge_at_d025=%d native_support_probes=%u/5 native_density_d0125_d1_d2_d4=(%.6f,%.6f,%.6f,%.6f) native_path=%s\n",
|
||||
std::printf("[ResolvedPassageSupportSegment] id=%s segment=%zu route_z_m=%.3f..%.3f centers=%llu unsupported=%llu native_unsupported=%llu first_foot_m=(%.3f,%.3f,%.3f) column_at_d025=%d native_support_probes=%u/5 native_density_d0125_d1_d2_d4=(%.6f,%.6f,%.6f,%.6f) native_path=%s\n",
|
||||
passage.stableId.c_str(),segment,
|
||||
std::min(a[2],b[2])*coreMetres,std::max(a[2],b[2])*coreMetres,
|
||||
static_cast<unsigned long long>(item.centers),
|
||||
static_cast<unsigned long long>(item.unsupported),
|
||||
static_cast<unsigned long long>(item.nativeUnsupported),
|
||||
sample[0],sample[1],sample[2],bridgeAtSupport?1:0,
|
||||
sample[0],sample[1],sample[2],columnAtSupport?1:0,
|
||||
item.firstNativeSupportedProbes,item.firstNativeDensities[0],item.firstNativeDensities[1],
|
||||
item.firstNativeDensities[2],item.firstNativeDensities[3],
|
||||
blockedWorld.densityDispatchPathAt({firstX,firstY,firstZ}).c_str());
|
||||
@@ -1429,19 +1554,37 @@ namespace StandaloneStreamBenchmark
|
||||
std::printf("[PassageOpenAirNegativeControl] source=L24_ungated_support unsupported_samples=%llu detected=%s\n",
|
||||
static_cast<unsigned long long>(openAirLegacyViolations),
|
||||
l24OpenAirControlDetected?"true":"false");
|
||||
std::printf("[PassageOpenAirAuditSummary] interfaces=%llu samples=%llu violations=%llu bridge_samples=%llu l24_negative_control=%s status=%s\n",
|
||||
std::printf("[PassageOpenAirAuditSummary] interfaces=%llu samples=%llu violations=%llu column_samples=%llu short_span_samples=%llu l24_negative_control=%s status=%s\n",
|
||||
static_cast<unsigned long long>(openAirAudits),
|
||||
static_cast<unsigned long long>(openAirSamples),
|
||||
static_cast<unsigned long long>(openAirViolations),
|
||||
static_cast<unsigned long long>(openAirBridgeSamples),
|
||||
static_cast<unsigned long long>(openAirColumnSamples),
|
||||
static_cast<unsigned long long>(openAirShortSpanSamples),
|
||||
l24OpenAirControlDetected?"DETECTED":"MISSED",
|
||||
openAirAudits==passageCount&&openAirViolations==0&&l24OpenAirControlDetected
|
||||
?"PASS":"FAIL");
|
||||
std::printf("[PassageRockBridgeAuditSummary] spans=%llu connected_ends=%llu/%llu status=%s\n",
|
||||
static_cast<unsigned long long>(bridgeSpans),
|
||||
static_cast<unsigned long long>(bridgeConnectedEnds),
|
||||
static_cast<unsigned long long>(bridgeSpans*2),
|
||||
bridgeAuditsPassed?"PASS":"FAIL");
|
||||
const bool l26NoLongBridgeControlDetected=l26NegativeControlApplicable
|
||||
&&l26LegacyBridgeSpanMetres>12.5;
|
||||
const bool l26NoLongBridgeControlPass=!l26NegativeControlApplicable
|
||||
||l26NoLongBridgeControlDetected;
|
||||
noLongBridgeAuditPassed=longestUncolumnedSpanMetres<=12.5
|
||||
&&l26NoLongBridgeControlPass;
|
||||
std::printf("[PassageNoLongBridgeNegativeControl] source=L26_SurfaceWorld_bridge applicable=%s longest_uncolumned_m=%.3f threshold_m=12.500 detected=%s\n",
|
||||
l26NegativeControlApplicable?"true":"false",l26LegacyBridgeSpanMetres,
|
||||
l26NoLongBridgeControlDetected?"true":"false");
|
||||
std::printf("[PassageNoLongBridgeAudit] columns=%llu short_grounded_spans=%llu longest_uncolumned_m=%.3f threshold_m=12.500 longest_short_span_m=%.3f l26_negative_control=%s status=%s\n",
|
||||
static_cast<unsigned long long>(columnCrossings),
|
||||
static_cast<unsigned long long>(shortGroundedSpans),longestUncolumnedSpanMetres,
|
||||
longestShortSpanMetres,l26NegativeControlApplicable
|
||||
?(l26NoLongBridgeControlDetected?"DETECTED":"MISSED"):"NOT_APPLICABLE",
|
||||
noLongBridgeAuditPassed?"PASS":"FAIL");
|
||||
std::printf("[PassageRockColumnAuditSummary] columns=%llu connected_ends=%llu/%llu short_spans=%llu wall_or_support_failures=%llu status=%s\n",
|
||||
static_cast<unsigned long long>(columnCrossings),
|
||||
static_cast<unsigned long long>(columnConnectedEnds),
|
||||
static_cast<unsigned long long>(columnCrossings*2),
|
||||
static_cast<unsigned long long>(shortGroundedSpans),
|
||||
static_cast<unsigned long long>(columnRouteFailures+columnWallFailures),
|
||||
columnAuditsPassed?"PASS":"FAIL");
|
||||
const bool l23SmoothWallDetected=!looksNatural(0.0,0.0);
|
||||
std::printf("[PassageNaturalnessNegativeControl] source=L23_smooth_unrelieved_wall relief_p2p_m=0.000 threshold_m=%.3f detected=%s\n",
|
||||
MinimumWallReliefMetres,l23SmoothWallDetected?"true":"false");
|
||||
@@ -1452,7 +1595,7 @@ namespace StandaloneStreamBenchmark
|
||||
const bool auditPassed=passageCount>0&&passagePasses==passageCount
|
||||
&&naturalPasses==passageCount&&l23SmoothWallDetected
|
||||
&&openAirAudits==passageCount&&openAirViolations==0&&l24OpenAirControlDetected
|
||||
&&bridgeAuditsPassed
|
||||
&&columnAuditsPassed&&noLongBridgeAuditPassed
|
||||
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
|
||||
&&routeBindingFailures==0
|
||||
&&blockedCompiled&&blockedControls==passageCount;
|
||||
|
||||
@@ -540,7 +540,7 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
FIntVector(0,0,islandSlot->TopChunkZ));
|
||||
const FStratePassageConfig& passage=islandSlot->Definition->PassageConfig;
|
||||
recipe.seed=static_cast<std::uint32_t>(owner.Seed);
|
||||
recipe.geometryVersion=7;
|
||||
recipe.geometryVersion=8;
|
||||
recipe.ownerVoxelSizeMicrons=owner.OwnerVoxelSizeMicrons;
|
||||
recipe.coreVoxelSizeMicrons=owner.CoreVoxelSizeMicrons;
|
||||
recipe.ownerToCoreScaleNumerator=owner.OwnerVoxelSizeMicrons/static_cast<std::uint32_t>(divisor);
|
||||
|
||||
Reference in new issue
Block a user