wip(L26, side branch): every passage style walkable in both recipes (0/0/0); SW bridges look wrong
All 4 styles x both recipes pass capsule, open-air and naturalness audits, and full-view hashes are deterministic. But the SW passage crosses 355 m of open SurfaceWorld sky as a ~2 km fragmented rock ramp, which is not natural. L27 replaces long bridges with a shared ceiling-to-ground rock column. 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
ef789aaa00
commit
37a223046c
13 files changed
+878
-41
No files matched your search
@@ -36,8 +36,9 @@ command -v ar >/dev/null || { printf 'ar not found\n' >&2; exit 2; }
|
||||
command -v pkg-config >/dev/null || { printf 'pkg-config not found\n' >&2; exit 2; }
|
||||
pkg-config --exists egl glesv2 libpng || { printf 'EGL, GLESv2, and libpng development files are required\n' >&2; exit 2; }
|
||||
|
||||
BUILD_DIR="$ROOT/Build/linux-$VARIANT"
|
||||
BIN_DIR="$ROOT/bin/linux"
|
||||
BUILD_ROOT="${VOXELFORGE_BUILD_ROOT:-$ROOT/Build}"
|
||||
BIN_DIR="${VOXELFORGE_BIN_DIR:-$ROOT/bin/linux}"
|
||||
BUILD_DIR="$BUILD_ROOT/linux-$VARIANT"
|
||||
mkdir -p "$BUILD_DIR/wrappers" "$BUILD_DIR/objects" "$BIN_DIR"
|
||||
|
||||
INCLUDES=(
|
||||
|
||||
@@ -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 12 gates passage supports on native rock and anchors the FI spiral inside a measured root.
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 12;
|
||||
// Version 13 adds connected relief-bearing rock spans across native open-air passage gaps.
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 13;
|
||||
|
||||
enum class FieldChannel : std::uint32_t
|
||||
{
|
||||
@@ -666,6 +666,17 @@ namespace VoxelForgeCore
|
||||
std::vector<ResolvedLayoutPassagePointV1> controlPoints;
|
||||
};
|
||||
|
||||
struct PassageBridgeRangeAuditV1
|
||||
{
|
||||
std::size_t passageIndex = 0;
|
||||
std::size_t firstSegment = 0;
|
||||
std::size_t lastSegment = 0;
|
||||
std::uint64_t unsupportedSamples = 0;
|
||||
double unsupportedLengthMetres = 0.0;
|
||||
std::array<double, 4> startPointSubvoxels{};
|
||||
std::array<double, 4> endPointSubvoxels{};
|
||||
};
|
||||
|
||||
struct ResolvedLayoutPassageConfigV1
|
||||
{
|
||||
std::int32_t upperStrateIndex = -1;
|
||||
@@ -1314,6 +1325,10 @@ 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,
|
||||
Vec3d voxelPosition) const;
|
||||
// Audit-only selector provenance for the same point evaluator; never used by meshing.
|
||||
std::string densityDispatchPathAt(Vec3d voxelPosition) const;
|
||||
// Analytic source distance without the occupancy-preserving near-zero clamp or packed proxy.
|
||||
|
||||
@@ -41,6 +41,27 @@ public:
|
||||
double SealThickness=0.0;
|
||||
};
|
||||
|
||||
const std::vector<FStandalonePassageBridgeRange>& GetPassageBridgeRanges() const
|
||||
{return PassageBridgeRanges;}
|
||||
|
||||
bool IsResolvedPassageBridgeSample(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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double GetCoreVoxelSizeCm() const
|
||||
{
|
||||
return Owner == nullptr || Owner->CoreVoxelSizeMicrons == 0
|
||||
@@ -168,20 +189,19 @@ public:
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
composeRouteMouthSupport(passage.upperStrateIndex,
|
||||
passage.upperMouthSubvoxels,true);
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
if(passage.controlPoints.size()<2)continue;
|
||||
const double placementRadiusQ=static_cast<double>(passage.placementRadiusSubvoxels)
|
||||
+static_cast<double>(passage.midRadiusSubvoxels);
|
||||
if(qx*qx+qy*qy>placementRadiusQ*placementRadiusQ)
|
||||
continue;
|
||||
const double firstFloorQ=passage.controlPoints.front().positionSubvoxels[2]
|
||||
-passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
const double lastFloorQ=passage.controlPoints.back().positionSubvoxels[2]
|
||||
-passage.controlPoints.back().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
if(qz>firstFloorQ+2.0*passage.mouthRadiusSubvoxels
|
||||
||qz<lastFloorQ-2.0*passage.mouthRadiusSubvoxels)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto floorProfile=[&](const auto& point)
|
||||
{return point.positionSubvoxels[2]-point.radiusSubvoxels+playerCenterAboveFloorQ;};
|
||||
const std::ptrdiff_t lastIndex=static_cast<std::ptrdiff_t>(passage.controlPoints.size())-1;
|
||||
@@ -204,8 +224,10 @@ public:
|
||||
const std::ptrdiff_t carveEnd=std::min<std::ptrdiff_t>(lowerMouthIndex-1,
|
||||
(carveLow-passage.controlPoints.begin())+1);
|
||||
double bestSignedDistance=std::numeric_limits<double>::infinity();
|
||||
double bestPlayerCapsuleSignedDistance=std::numeric_limits<double>::infinity();
|
||||
double bestT=0.0;
|
||||
std::ptrdiff_t bestIndex=0;
|
||||
std::ptrdiff_t bestSupportSegmentIndex=0;
|
||||
bool hasSupportFloor=false;
|
||||
double bestSupportDensity=0.0;
|
||||
const auto considerCarveSegment=[&](std::ptrdiff_t index)
|
||||
@@ -225,12 +247,37 @@ public:
|
||||
const double ex=qx-cx,ey=qy-cy,ez=qz-cz;
|
||||
const double radius=static_cast<double>(a.radiusSubvoxels)
|
||||
+t*(b.radiusSubvoxels-a.radiusSubvoxels);
|
||||
const double horizontalLengthSq=dx*dx+dy*dy;
|
||||
const double playerT=horizontalLengthSq>0.0
|
||||
?std::clamp(((qx-ax)*dx+(qy-ay)*dy)/horizontalLengthSq,0.0,1.0):t;
|
||||
const double playerDx=qx-(ax+playerT*dx);
|
||||
const double playerDy=qy-(ay+playerT*dy);
|
||||
const double playerRadius=static_cast<double>(a.radiusSubvoxels)
|
||||
+playerT*(b.radiusSubvoxels-a.radiusSubvoxels);
|
||||
const double playerCenterZ=az+playerT*dz-playerRadius+playerCenterAboveFloorQ;
|
||||
const double playerVertical=std::max(0.0,
|
||||
std::abs(qz-playerCenterZ)-0.54*qPerMetre);
|
||||
const double playerCapsuleDistance=DeterministicSqrt(
|
||||
playerDx*playerDx+playerDy*playerDy+playerVertical*playerVertical)
|
||||
-0.34*qPerMetre;
|
||||
bestPlayerCapsuleSignedDistance=std::min(bestPlayerCapsuleSignedDistance,
|
||||
playerCapsuleDistance);
|
||||
const double signedDistance=DeterministicSqrt(ex*ex+ey*ey+ez*ez)-radius;
|
||||
if(signedDistance<bestSignedDistance)
|
||||
{bestSignedDistance=signedDistance;bestT=t;bestIndex=index;}
|
||||
};
|
||||
for(std::ptrdiff_t index=carveBegin;index<=carveEnd;++index)
|
||||
considerCarveSegment(index);
|
||||
// Alternate styles can bend locally at either mouth, so floorProfile is
|
||||
// not monotone over those short endpoint runs. Keep the binary search for
|
||||
// the long descent and inspect both endpoint neighborhoods directly.
|
||||
for(std::ptrdiff_t index=0;index<std::min<std::ptrdiff_t>(8,lowerMouthIndex);++index)
|
||||
considerCarveSegment(index);
|
||||
const std::ptrdiff_t mouthSearchBegin=std::max<std::ptrdiff_t>(0,lowerMouthIndex-8);
|
||||
const std::ptrdiff_t mouthSearchEnd=std::min<std::ptrdiff_t>(lastIndex,
|
||||
lowerMouthIndex+8);
|
||||
for(std::ptrdiff_t index=mouthSearchBegin;index<mouthSearchEnd;++index)
|
||||
considerCarveSegment(index);
|
||||
// The route-bound landing can rise and fall after the lower mouth. Scan
|
||||
// that short tail directly; binary search is only valid on the descent.
|
||||
for(std::ptrdiff_t index=lowerMouthIndex;index<lastIndex;++index)
|
||||
@@ -250,6 +297,12 @@ 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)))
|
||||
{
|
||||
const double bridgeDistance=PassageBridgeSignedDistance(passageIndex,
|
||||
static_cast<std::size_t>(bestIndex),X,Y,Z);
|
||||
composed=std::min(composed,static_cast<float>(bridgeDistance/Q));
|
||||
}
|
||||
// Positive density is air; rough rock is cut with full side/ceiling detail and
|
||||
// gentler relief on the gravity-facing wall so the supported tread stays open.
|
||||
composed=std::max(composed,static_cast<float>(-roughSignedDistance/Q));
|
||||
@@ -264,7 +317,10 @@ public:
|
||||
const double radiusB=segmentB.radiusSubvoxels;
|
||||
// Keep the tread within the probe band and below the capsule's
|
||||
// lowest sampled point.
|
||||
const double floorOffset=0.20;
|
||||
const double floorOffset=IsPassageBridgeSegment(passageIndex,
|
||||
static_cast<std::size_t>(index))
|
||||
?PassageBridgeDeckOffsetMetres(passageIndex,static_cast<std::size_t>(index))
|
||||
:0.20;
|
||||
const double horizontalLengthSq=dx*dx+dy*dy;
|
||||
const double supportT=horizontalLengthSq>0.0
|
||||
?std::clamp(((qx-segmentA.positionSubvoxels[0])*dx
|
||||
@@ -304,6 +360,7 @@ public:
|
||||
if(supportDistanceSq>=bestSupportDistanceSquared)return;
|
||||
bestSupportDistanceSquared=supportDistanceSq;
|
||||
hasSupportFloor=true;
|
||||
bestSupportSegmentIndex=index;
|
||||
bestSupportDensity=std::clamp(Z-floorVoxels,
|
||||
-supportThicknessVoxels,0.0);
|
||||
};
|
||||
@@ -330,7 +387,11 @@ 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.
|
||||
if(hasSupportFloor&&density<0.0f)
|
||||
const bool bridgeFloorSupport=hasSupportFloor
|
||||
&&IsPassageBridgeSegment(passageIndex,
|
||||
static_cast<std::size_t>(bestSupportSegmentIndex));
|
||||
if(hasSupportFloor&&bestPlayerCapsuleSignedDistance>=0.05*qPerMetre
|
||||
&&(density<0.0f||bridgeFloorSupport))
|
||||
composed=std::min(composed,static_cast<float>(bestSupportDensity));
|
||||
}
|
||||
return composed;
|
||||
@@ -343,6 +404,10 @@ public:
|
||||
{
|
||||
if(!ResolvedLayout||ResolvedLayout->passages.empty())return false;
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double voxelsPerMetre=Owner&&Owner->CoreVoxelSizeMicrons>0
|
||||
?1000000.0/Owner->CoreVoxelSizeMicrons:4.0;
|
||||
const double bridgeHalfWidthVoxels=3.5*voxelsPerMetre;
|
||||
const double bridgeFootDepthVoxels=(5.0-0.13)*voxelsPerMetre;
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
{
|
||||
for(std::size_t i=1;i<passage.controlPoints.size();++i)
|
||||
@@ -351,13 +416,18 @@ 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.
|
||||
const double horizontalExtent=std::max(radius,bridgeHalfWidthVoxels);
|
||||
const double footDepth=std::max(floorThicknessVoxels,bridgeFootDepthVoxels);
|
||||
const double low[3]={
|
||||
std::min(a.positionSubvoxels[0],b.positionSubvoxels[0])/Q-radius,
|
||||
std::min(a.positionSubvoxels[1],b.positionSubvoxels[1])/Q-radius,
|
||||
std::min(a.positionSubvoxels[2],b.positionSubvoxels[2])/Q-radius-floorThicknessVoxels};
|
||||
std::min(a.positionSubvoxels[0],b.positionSubvoxels[0])/Q-horizontalExtent,
|
||||
std::min(a.positionSubvoxels[1],b.positionSubvoxels[1])/Q-horizontalExtent,
|
||||
std::min(a.positionSubvoxels[2],b.positionSubvoxels[2])/Q-radius-footDepth};
|
||||
const double high[3]={
|
||||
std::max(a.positionSubvoxels[0],b.positionSubvoxels[0])/Q+radius,
|
||||
std::max(a.positionSubvoxels[1],b.positionSubvoxels[1])/Q+radius,
|
||||
std::max(a.positionSubvoxels[0],b.positionSubvoxels[0])/Q+horizontalExtent,
|
||||
std::max(a.positionSubvoxels[1],b.positionSubvoxels[1])/Q+horizontalExtent,
|
||||
std::max(a.positionSubvoxels[2],b.positionSubvoxels[2])/Q+radius};
|
||||
bool overlap=true;
|
||||
for(int axis=0;axis<3;++axis)
|
||||
@@ -715,6 +785,7 @@ public:
|
||||
if(RoundTunnelsEnabled)
|
||||
BuildRoundTunnelPrimitives();
|
||||
BuildSpatialBins(LineBinSize);
|
||||
BuildPassageBridgePlan();
|
||||
Stats.LocalLineBinSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
Stats.ShapeSeconds=std::chrono::duration<double>(Clock::now()-Stage).count();
|
||||
Stats.PrimitiveCount=Primitives.size();
|
||||
@@ -1622,6 +1693,218 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsPassageBridgeSegment(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;});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
double PassageBridgeSignedDistance(std::size_t PassageIndex,std::size_t SegmentIndex,
|
||||
float X,float Y,float Z) const
|
||||
{
|
||||
if(!ResolvedLayout||PassageIndex>=ResolvedLayout->passages.size())
|
||||
return std::numeric_limits<double>::infinity();
|
||||
const auto& passage=ResolvedLayout->passages[PassageIndex];
|
||||
if(SegmentIndex+1>=passage.controlPoints.size())
|
||||
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->coreVoxelSizeMicrometres);
|
||||
const auto& a=passage.controlPoints[SegmentIndex];
|
||||
const auto& b=passage.controlPoints[SegmentIndex+1];
|
||||
const double ax=static_cast<double>(a.positionSubvoxels[0]);
|
||||
const double ay=static_cast<double>(a.positionSubvoxels[1]);
|
||||
const double dx=static_cast<double>(b.positionSubvoxels[0])-ax;
|
||||
const double dy=static_cast<double>(b.positionSubvoxels[1])-ay;
|
||||
const double horizontalLengthSq=dx*dx+dy*dy;
|
||||
const double qx=static_cast<double>(X)*Q,qy=static_cast<double>(Y)*Q;
|
||||
const double qz=static_cast<double>(Z)*Q;
|
||||
const double t=horizontalLengthSq>0.0
|
||||
?std::clamp(((qx-ax)*dx+(qy-ay)*dy)/horizontalLengthSq,0.0,1.0):0.0;
|
||||
const double centerX=ax+t*dx,centerY=ay+t*dy;
|
||||
const double centerZ=a.positionSubvoxels[2]+t*(
|
||||
static_cast<double>(b.positionSubvoxels[2])-a.positionSubvoxels[2]);
|
||||
const double radius=a.radiusSubvoxels+t*(
|
||||
static_cast<double>(b.radiusSubvoxels)-a.radiusSubvoxels);
|
||||
// Embed the deck 13 cm above the bore bottom: within the 25 cm support
|
||||
// probe, while leaving 16 cm between it and the capsule's lowest sample.
|
||||
const double floorQ=centerZ-radius
|
||||
+PassageBridgeDeckOffsetMetres(PassageIndex,SegmentIndex)*qPerMetre;
|
||||
const double relief=std::clamp(static_cast<double>(VF_FieldSurfaceRelief(X,Y,Z,
|
||||
static_cast<std::uint32_t>(Owner?Owner->Seed:0))),-1.0,1.0);
|
||||
const double halfWidth=(3.30+0.20*relief)*qPerMetre;
|
||||
const double thickness=(4.0+1.0*relief)*qPerMetre;
|
||||
const double horizontalDistance=DeterministicSqrt((qx-centerX)*(qx-centerX)
|
||||
+(qy-centerY)*(qy-centerY));
|
||||
const double sideDistance=horizontalDistance-halfWidth;
|
||||
const double topDistance=qz-floorQ;
|
||||
const double bottomDistance=floorQ-thickness-qz;
|
||||
const double verticalDistance=std::max(topDistance,bottomDistance);
|
||||
const double outside=DeterministicSqrt(
|
||||
std::max(sideDistance,0.0)*std::max(sideDistance,0.0)
|
||||
+std::max(verticalDistance,0.0)*std::max(verticalDistance,0.0));
|
||||
const double inside=std::min(std::max(sideDistance,verticalDistance),0.0);
|
||||
return outside+inside;
|
||||
}
|
||||
|
||||
float NativePassageDensityAt(float X,float Y,float Z) const
|
||||
{
|
||||
float density=0.0f;
|
||||
if(!NativeFloatingIslandDensityAt(X,Y,Z,density)
|
||||
&&!NativeSurfaceDensityAt(X,Y,Z,density))
|
||||
{
|
||||
if(UsesLegacySurfaceField(X,Y,Z)&&Owner&&Owner->Generator)
|
||||
density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
else
|
||||
density=AnalyticDensityAt(X,Y,Z,true,nullptr,nullptr,nullptr,
|
||||
false,false);
|
||||
}
|
||||
return ApplyResolvedStrateBoundaryBand(density,X,Y,Z);
|
||||
}
|
||||
|
||||
void BuildPassageBridgePlan()
|
||||
{
|
||||
PassageBridgeRanges.clear();
|
||||
if(!Owner||!ResolvedLayout||ResolvedLayout->passages.empty()
|
||||
||!Owner->PassageBridgeCache)return;
|
||||
auto& cache=*Owner->PassageBridgeCache;
|
||||
std::lock_guard<std::mutex> lock(cache.mutex);
|
||||
const auto cached=cache.byLayout.find(ResolvedLayout->stableIdentity);
|
||||
if(cached!=cache.byLayout.end())
|
||||
{PassageBridgeRanges=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}};
|
||||
struct FPathSample
|
||||
{
|
||||
std::size_t segment=0;
|
||||
std::array<double,4> point{};
|
||||
bool nativeSupported=false;
|
||||
};
|
||||
const auto nativeSupportedAt=[&](const std::array<double,4>& point)
|
||||
{
|
||||
const double floorQ=point[2]-point[3]+0.13*qPerMetre;
|
||||
std::uint32_t supported=0;
|
||||
for(const auto& offset:supportOffsets)
|
||||
{
|
||||
bool hasSolid=false;
|
||||
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(
|
||||
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;
|
||||
if(density<0.0f){hasSolid=true;break;}
|
||||
}
|
||||
supported+=hasSolid;
|
||||
}
|
||||
return supported>=3;
|
||||
};
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
|
||||
++passageIndex)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
if(passage.controlPoints.size()<2)continue;
|
||||
std::vector<FPathSample> samples;
|
||||
samples.reserve(passage.controlPoints.size()*2);
|
||||
const auto pointAt=[&](std::size_t segment,double t)
|
||||
{
|
||||
const auto& a=passage.controlPoints[segment];
|
||||
const auto& b=passage.controlPoints[segment+1];
|
||||
FPathSample sample;sample.segment=segment;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
sample.point[axis]=a.positionSubvoxels[axis]+t*(
|
||||
static_cast<double>(b.positionSubvoxels[axis])
|
||||
-a.positionSubvoxels[axis]);
|
||||
sample.point[3]=a.radiusSubvoxels+t*(
|
||||
static_cast<double>(b.radiusSubvoxels)-a.radiusSubvoxels);
|
||||
sample.nativeSupported=nativeSupportedAt(sample.point);
|
||||
return sample;
|
||||
};
|
||||
samples.push_back(pointAt(0,0.0));
|
||||
for(std::size_t segment=0;segment+1<passage.controlPoints.size();++segment)
|
||||
{
|
||||
const auto& a=passage.controlPoints[segment];
|
||||
const auto& b=passage.controlPoints[segment+1];
|
||||
const double dx=static_cast<double>(b.positionSubvoxels[0])-a.positionSubvoxels[0];
|
||||
const double dy=static_cast<double>(b.positionSubvoxels[1])-a.positionSubvoxels[1];
|
||||
const double dz=static_cast<double>(b.positionSubvoxels[2])-a.positionSubvoxels[2];
|
||||
const double lengthMetres=DeterministicSqrt(dx*dx+dy*dy+dz*dz)/qPerMetre;
|
||||
const std::size_t intervals=std::max<std::size_t>(1,
|
||||
static_cast<std::size_t>(std::ceil(lengthMetres/0.5)));
|
||||
for(std::size_t step=1;step<=intervals;++step)
|
||||
samples.push_back(pointAt(segment,static_cast<double>(step)/intervals));
|
||||
}
|
||||
std::size_t cursor=0;
|
||||
while(cursor<samples.size())
|
||||
{
|
||||
if(samples[cursor].nativeSupported){++cursor;continue;}
|
||||
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;
|
||||
double gapLength=0.0;
|
||||
for(std::size_t index=gapBegin;index<=gapEnd;++index)
|
||||
{
|
||||
const auto& a=samples[index-1].point;
|
||||
const auto& b=samples[index].point;
|
||||
const double dx=b[0]-a[0],dy=b[1]-a[1],dz=b[2]-a[2];
|
||||
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);
|
||||
}
|
||||
if(std::none_of(PassageBridgeRanges.begin(),PassageBridgeRanges.end(),
|
||||
[passageIndex](const auto& range){return range.passageIndex==passageIndex;}))
|
||||
std::printf("[PassageRockBridgePlan] id=%s spans=0 status=NOT_REQUIRED\n",
|
||||
passage.stableId.c_str());
|
||||
}
|
||||
cache.byLayout.emplace(ResolvedLayout->stableIdentity,PassageBridgeRanges);
|
||||
}
|
||||
|
||||
static uint64 PackLatticeKey(int32 X,int32 Y,int32 Z)
|
||||
{
|
||||
constexpr uint64 Mask=0x1FFFFFull;
|
||||
@@ -1656,17 +1939,19 @@ private:
|
||||
float* OutDetailDisplacement=nullptr,
|
||||
float* OutCaveInfluence=nullptr,
|
||||
float* OutFloorInfluence=nullptr,
|
||||
bool bApplyEdits=true) const
|
||||
bool bApplyEdits=true,bool bApplyComposition=true) const
|
||||
{
|
||||
if(SharedDensitySource)return SharedDensitySource->AnalyticDensityAt(X,Y,Z,
|
||||
bClampNearZero,OutDetailDisplacement,OutCaveInfluence,OutFloorInfluence,bApplyEdits);
|
||||
bClampNearZero,OutDetailDisplacement,OutCaveInfluence,OutFloorInfluence,
|
||||
bApplyEdits,bApplyComposition);
|
||||
if(OutDetailDisplacement)*OutDetailDisplacement=0.0f;
|
||||
if(OutCaveInfluence)*OutCaveInfluence=0.0f;
|
||||
if(OutFloorInfluence)*OutFloorInfluence=0.0f;
|
||||
float IslandDensity=0.0f;
|
||||
if(NativeFloatingIslandDensityAt(X,Y,Z,IslandDensity))
|
||||
{
|
||||
const float composed=ApplyResolvedStrateComposition(IslandDensity,X,Y,Z);
|
||||
const float composed=bApplyComposition
|
||||
?ApplyResolvedStrateComposition(IslandDensity,X,Y,Z):IslandDensity;
|
||||
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
|
||||
}
|
||||
if(UsesLegacySurfaceField(X,Y,Z))
|
||||
@@ -1674,11 +1959,13 @@ private:
|
||||
float NativeDensity=0.0f;
|
||||
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
|
||||
{
|
||||
NativeDensity=ApplyResolvedStrateComposition(NativeDensity,X,Y,Z);
|
||||
if(bApplyComposition)
|
||||
NativeDensity=ApplyResolvedStrateComposition(NativeDensity,X,Y,Z);
|
||||
return bApplyEdits?ApplyEditBrushes(NativeDensity,X,Y,Z):NativeDensity;
|
||||
}
|
||||
const float Density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
const float composed=ApplyResolvedStrateComposition(Density,X,Y,Z);
|
||||
const float composed=bApplyComposition
|
||||
?ApplyResolvedStrateComposition(Density,X,Y,Z):Density;
|
||||
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
|
||||
}
|
||||
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
|
||||
@@ -1693,7 +1980,8 @@ private:
|
||||
if((Density<0.0f)!=bRock||(bClampNearZero&&std::abs(Density)<0.05f))
|
||||
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),
|
||||
bClampNearZero?0.05f:1.0e-7f);
|
||||
const float composed=ApplyResolvedStrateComposition(Density,X,Y,Z);
|
||||
const float composed=bApplyComposition
|
||||
?ApplyResolvedStrateComposition(Density,X,Y,Z):Density;
|
||||
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
|
||||
}
|
||||
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
|
||||
@@ -1723,7 +2011,7 @@ private:
|
||||
if((Density<0.0f)!=bRock||(bClampNearZero&&std::abs(Density)<0.05f))
|
||||
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),
|
||||
bClampNearZero?0.05f:1.0e-7f);
|
||||
Density=ApplyResolvedStrateComposition(Density,X,Y,Z);
|
||||
if(bApplyComposition)Density=ApplyResolvedStrateComposition(Density,X,Y,Z);
|
||||
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
|
||||
}
|
||||
|
||||
@@ -5863,6 +6151,7 @@ private:
|
||||
std::vector<FFarConnectorSegment> FarConnectorSegments;
|
||||
std::unordered_map<FBinKey,std::vector<uint32>,FBinHash> FarConnectorSpatialBins;
|
||||
std::vector<FEditBrush> EditBrushes;
|
||||
std::vector<FStandalonePassageBridgeRange> PassageBridgeRanges;
|
||||
std::array<FAxisBins,3> AxisBins;
|
||||
std::unordered_map<FBinKey,FBinItems,FBinHash> SpatialBins;
|
||||
std::unordered_map<FBinKey,FBinItems,FBinHash> CriticalAirBins;
|
||||
|
||||
@@ -1164,6 +1164,44 @@ namespace VoxelForgeCore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ResolvedPassageTouchesBox(const ResolvedLayoutSnapshotV3& layout,
|
||||
const FIntVector& minimum,const FIntVector& maximum,std::uint32_t coreVoxelSizeMicrons)
|
||||
{
|
||||
if(layout.passages.empty())return false;
|
||||
constexpr double Q=static_cast<double>(WorldAssemblySubvoxelsPerVoxel);
|
||||
const double voxelsPerMetre=coreVoxelSizeMicrons>0
|
||||
?1000000.0/coreVoxelSizeMicrons:4.0;
|
||||
const double bridgeHalfWidthVoxels=3.5*voxelsPerMetre;
|
||||
const double bridgeFootDepthVoxels=(5.0-0.13)*voxelsPerMetre;
|
||||
const double tileMinimum[3]={static_cast<double>(minimum.X),
|
||||
static_cast<double>(minimum.Y),static_cast<double>(minimum.Z)};
|
||||
const double tileMaximum[3]={static_cast<double>(maximum.X),
|
||||
static_cast<double>(maximum.Y),static_cast<double>(maximum.Z)};
|
||||
for(const auto& passage:layout.passages)
|
||||
for(std::size_t segment=1;segment<passage.controlPoints.size();++segment)
|
||||
{
|
||||
const auto& a=passage.controlPoints[segment-1];
|
||||
const auto& b=passage.controlPoints[segment];
|
||||
const double radius=static_cast<double>(std::max(
|
||||
a.radiusSubvoxels,b.radiusSubvoxels))/Q;
|
||||
const double horizontalExtent=std::max(radius,bridgeHalfWidthVoxels);
|
||||
const double low[3]={
|
||||
std::min(a.positionSubvoxels[0],b.positionSubvoxels[0])/Q-horizontalExtent,
|
||||
std::min(a.positionSubvoxels[1],b.positionSubvoxels[1])/Q-horizontalExtent,
|
||||
std::min(a.positionSubvoxels[2],b.positionSubvoxels[2])/Q-radius
|
||||
-bridgeFootDepthVoxels};
|
||||
const double high[3]={
|
||||
std::max(a.positionSubvoxels[0],b.positionSubvoxels[0])/Q+horizontalExtent,
|
||||
std::max(a.positionSubvoxels[1],b.positionSubvoxels[1])/Q+horizontalExtent,
|
||||
std::max(a.positionSubvoxels[2],b.positionSubvoxels[2])/Q+radius};
|
||||
bool overlap=true;
|
||||
for(int axis=0;axis<3;++axis)
|
||||
overlap&=high[axis]>=tileMinimum[axis]&&low[axis]<=tileMaximum[axis];
|
||||
if(overlap)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool World::compile(const ShapeBuildRequest& request,const JobScheduler& scheduler,std::string& error)
|
||||
@@ -1246,6 +1284,8 @@ namespace VoxelForgeCore
|
||||
if(IntersectsBox(feature.minimumSubvoxels,feature.maximumSubvoxels,
|
||||
subMinimum,subMaximum))return false;
|
||||
if(RouteTouchesBox(*selectedPlan,subMinimum,subMaximum))return false;
|
||||
if(ResolvedPassageTouchesBox(*impl_->resolvedLayout,boundsMin,boundsMax,
|
||||
impl_->owner->CoreVoxelSizeMicrons))return false;
|
||||
if(OwnerPassageTouchesBox(*impl_->owner,boundsMin,boundsMax))return false;
|
||||
FStandaloneShapeWorld proofGeometry;
|
||||
if(!proofGeometry.BuildShapeDependencyProof(*impl_->owner,boundsMin,boundsMax,
|
||||
@@ -1502,6 +1542,28 @@ 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<PassageBridgeRangeAuditV1> 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)
|
||||
{
|
||||
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;
|
||||
result.push_back(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool World::isPassageBridgeSample(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,
|
||||
{position.x,position.y,position.z});}
|
||||
std::string World::densityDispatchPathAt(Vec3d position) const
|
||||
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape
|
||||
?impl_->shape->DensityDispatchPathAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z))
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
# World Assembler L26 — passages walkable in rock
|
||||
|
||||
**All four styles pass for both recipes.** Clang and GCC report zero blocked capsule samples, zero
|
||||
unsupported samples, zero grade failures, zero open-air violations, passing naturalness, and passing
|
||||
route bindings. The L24 open-air control and L23 smooth-wall control were detected.
|
||||
|
||||
## Where support was missing
|
||||
|
||||
In the owner SurfaceWorld recipe, the default Spiral passage has 1,083 control points. Its unsupported
|
||||
bridge range is segments 98–1077, a 1,957.395 m gap. Segment 98 is at about `(0,-5,-142.7) m`; segment
|
||||
1077 is at `(-4.6,-1.9,-497.4) m`. Both are inside the SurfaceWorld stratum (`-632..-120 m`), below
|
||||
its native terrain. The gap starts after the TunnelNetwork transition; it is neither TunnelNetwork
|
||||
cave air nor a stratum seam. The first L25 failure sample had only 2/5 native support probes, and
|
||||
following samples had 0/5. Their dispatch path was `SurfaceWorld-native`.
|
||||
|
||||
Core now scans the native field sign and adds a deterministic bridge only over contiguous unsupported
|
||||
ranges. The shared density field gives the bridge at least 5 m width, 3–5 m planned thickness and
|
||||
surface relief. The audit measured two connected ends, at least 5 m width, 256/256 cross-sections,
|
||||
3.5–6 m thickness and 2.5 m relief on the default Spiral. All four SurfaceWorld styles use connected
|
||||
bridges; Floating Islands already has supporting rock and needs none. The capsule clearance gate also
|
||||
checks the resolved player capsule, fixing FI styles 0/1/3 at their bent mouth segments.
|
||||
|
||||
The first bridge export still contained no passage mesh because tile readiness classified all nearby
|
||||
tiles as native-only. Resolved passage and bridge bounds now keep those tiles shape-dependent. The final
|
||||
local export changed `shape_free_desired` from 27/27 to 4/27 and contains the composed bridge density.
|
||||
|
||||
## Eight-row audit
|
||||
|
||||
Counts are from the Clang W12 run; GCC W12 matched every row. `B/U/G` means blocked, unsupported and
|
||||
grade failures. Route bindings are required bindings passed / required bindings.
|
||||
|
||||
| Recipe | Style | Capsule samples | Support samples | B/U/G | Open-air samples / violations | Wall relief | Bindings | Bridge segments / length |
|
||||
|---|---:|---:|---:|---:|---:|---:|---:|---|
|
||||
| SurfaceWorld | 0 | 301,140 | 32,765 | 0/0/0 | 3,887,808 / 0 | 0.472 m | 1/1 | 30–293 / 2,004.318 m |
|
||||
| SurfaceWorld | 1 | 284,949 | 31,046 | 0/0/0 | 3,707,328 / 0 | 0.449 m | 1/1 | 33–357 / 1,945.857 m |
|
||||
| SurfaceWorld | 2 | 275,877 | 30,072 | 0/0/0 | 3,664,064 / 0 | 0.410 m | 1/1 | 98–1077 / 1,957.395 m |
|
||||
| SurfaceWorld | 3 | 287,154 | 31,298 | 0/0/0 | 3,741,792 / 0 | 0.552 m | 1/1 | 34–373 / 1,980.607 m |
|
||||
| Floating Islands | 0 | 50,589 | 4,407 | 0/0/0 | 695,616 / 0 | 0.779 m | 2/2 | none |
|
||||
| Floating Islands | 1 | 57,015 | 5,060 | 0/0/0 | 742,016 / 0 | 0.806 m | 2/2 | none |
|
||||
| Floating Islands | 2 | 61,173 | 5,318 | 0/0/0 | 813,632 / 0 | 0.767 m | 2/2 | none |
|
||||
| Floating Islands | 3 | 54,369 | 4,325 | 0/0/0 | 743,456 / 0 | 0.681 m | 2/2 | none |
|
||||
|
||||
Every row passed naturalness (0 m flat run; relief threshold 0.2 m), route binding and strict layout
|
||||
validation. The validator accepted with zero issues and rejected its malformed controls 9/9. Each L24
|
||||
open-air negative control and L23 smooth-wall negative control was detected.
|
||||
|
||||
## Captures
|
||||
|
||||
All are 16:9 JPEGs below 300 KB in `captures/`:
|
||||
|
||||
| Capture | View | Size | SHA-256 |
|
||||
|---|---|---:|---|
|
||||
| `sw-passage-outside.jpg` | Midspan from outside, 1920×1080 | 90,937 B | `28378eeee755faf7446c0b2ce0eb8d2368c79b42f7de4c15e8b2f4d58fb7754d` |
|
||||
| `sw-passage-inside.jpg` | Eye-height at first bridged segment, 1600×900 | 47,519 B | `ccadc33c36366a0ca7b2fcda0a21e4cea67bda107f0e20015171fd620d495249` |
|
||||
| `sw-natural-bridge-wide.jpg` | Wide midspan view, 1920×1080 | 70,192 B | `f4a66fec1adebfbdf5806bb2e7c5a6470e3e8d9dcd60f495d930bbb1d0c5669e` |
|
||||
| `fi-pillar-exterior.jpg` | Floating Islands pillar exterior, 1600×900 | 35,857 B | `b977ff34acee1294a24c2465b1c9d3d78014cff4b8eceb667f317a0a0b46cc67` |
|
||||
|
||||
## Determinism and regressions
|
||||
|
||||
The L25 hash command was rerun with the L26 binaries for both recipes, W1/W8/W12, Clang/GCC:
|
||||
|
||||
```sh
|
||||
timeout 1800s taskset -c 0-11 env LP_NUM_THREADS=12 prlimit --as=6442450944 -- \
|
||||
Saved/Linux/bin/VoxelForgeCoreHeadless-linux26-<clang|gcc> \
|
||||
--world-assembler-prototype --stream-benchmark --stream-pipeline --stream-hash --force-generate \
|
||||
--clip-radius=3 --max-clip-level=8 --far-horizon-lod8-step=0 --workers=N \
|
||||
--recipe=<surface-world|floating-islands> Standalone/data/owner_world.json
|
||||
```
|
||||
|
||||
All six runs per recipe matched. Hash vectors are density / mesh / payload / topology; full detail is
|
||||
960 tiles and whole view is 2,370 tiles.
|
||||
|
||||
| Recipe | Full detail | Whole view | Exact query: density / unclamped / detail / collision |
|
||||
|---|---|---|---|
|
||||
| SurfaceWorld | `ace56c8f205238ad / a9c603a1248de6b8 / ea55f1f4fcc6a894 / 5ea7bc78a1b678ae` | `307d5df80e0165b4 / 1d61f30e5733beca / 3aaef136176c4b12 / aa5ff7c2a821196f` | `c949c4df2ce6f9d9 / c949c4df2ce6f9d9 / 114c501e2d830d94 / e6011d3a248d4849` |
|
||||
| Floating Islands | `d18e722b37a2479e / 0e410a8e80a3e7f7 / 0751926f0c67d8a4 / f01208bc2f7841ec` | `51decfb71d282506 / 69448ec1c7fc954f / e2ff37c98fe8e494 / 5beeb7f94fdbf3d4` | `7b7c50ac24ccadcc / 7b7c50ac24ccadcc / efcba20c0c70c415 / e604823a249029bf` |
|
||||
|
||||
The 24 m crossing passed for both compilers and recipes. Each processed 314 moving tiles, with identical
|
||||
compiler hashes. Moving-tile vectors (density / mesh / payload / topology): SurfaceWorld
|
||||
`3561ea23485e359f / e8331620fb0fb908 / 765f7dad67c852aa / 3ccc09ac81c05d71`; Floating Islands
|
||||
`e3d0ea833bca4215 / 6b3d0f148ed83d93 / d6ed85f16acd895e / 9745683036522461`.
|
||||
|
||||
SurfaceWorld away from the passage was compared directly between L25 Clang and final L26 Clang at
|
||||
`--camera=10000,10000,-100,0,0`. Full detail, whole view, all LOD0–8 rows and exact-query hashes were
|
||||
identical (12/12). Full-detail vector: `1c6abc5e9c657e98 / a1624a3c19bbe554 / bf98934c9d846f3a /
|
||||
bbdc2a6b821c66d6`; whole-view vector: `40c2be0c7bbdaf28 / 7d9ac2a85020dc7d / c378e48bab2d194f /
|
||||
81014ec4b888107a`; exact-query vector: `482500abbe835b11 / c4948c7179963784 / efcba20c0c70c415 /
|
||||
137e8f6c0b03a624`.
|
||||
|
||||
Strict validation passed on both compilers and recipes. L13 route parity passed on both compilers/recipes:
|
||||
8/8 page pairs, 14 higher-LOD pairs, one edit pair, 782,026 exact-query samples and zero mismatches.
|
||||
For L9 proofs, the passage-centered view has no eligible native blocks. At the same 10 km away-from-route
|
||||
view, SurfaceWorld checked 4,848 blocks / 6,452,688 samples with 1,296 fallbacks and zero violations
|
||||
on both compilers. Floating Islands had its expected 21,504 unknown fallbacks and zero violations.
|
||||
Final headless binary SHA-256: Clang `fc0a5a74f17566384e094cb2429d8925147dd3eb32fb925417393e00c9bb19d5`; GCC
|
||||
`54843bef3a64087e55d3eb9a76fb0856162b6ad9aeb03d6e1c204c3e91d21ca5`.
|
||||
|
||||
Clang built with FP contraction off; GCC used its default FP mode. Both built and started without the L3
|
||||
FP-contract self-test rejecting them. Core evaluator version is 13; FI geometry remains 7 and layout
|
||||
schema V3 is unchanged. Changes are shared Core, standalone audits, Linux build output configuration,
|
||||
and ARCHITECTURE §8.44 / CODEMAP §3.27. No Unreal files changed, and no async path was added. The L4
|
||||
`FLazy`, L7 cache, L9/L12 proofs, L11 readiness/crash handling, L13 binding, L18/L19 ceiling,
|
||||
L21/L22 layout and bands, L23–L25 passage/helix, §8.10 and CLAUDE.md async rules remain intact.
|
||||
|
||||
## Failed and retried runs
|
||||
|
||||
- The first build command used an invalid `clang` variant; corrected to Clang/GCC variants. Early build
|
||||
iterations R3–R6 had compiler errors and were corrected before final binaries were produced.
|
||||
- An unsupported `--world-assembler-audit` option exited 2; the correct option is
|
||||
`--audit-resolved-layout-passages`. One early invocation omitted that flag and performed no passage
|
||||
audit. Early bridge probes also segfaulted while crossing stratum dispatch; final audits use bounded
|
||||
resolved-layout queries.
|
||||
- Support iterations R22–R30 exposed successive failures: FI style 0 had 767, then 27, then 5 blocked /
|
||||
33 unsupported samples; an offset experiment caused 746 unsupported samples; a control-point capsule
|
||||
reference left 7 blocked samples. Correcting the capsule reference cleared those. R30 still had SW
|
||||
bridge sections 254/255, four SW style-1 mouth blocks, and an invalid SW style-2 bridge thickness
|
||||
sample. R31 corrected them; final matrix is clean.
|
||||
- The first final audit batch labeled FI JSON files as Floating Islands but omitted `--recipe`, so those
|
||||
invocations used SurfaceWorld. All 16 style audits were rerun with explicit recipe flags. An initial
|
||||
proof run used `--force-generate`, which disables L9 block auditing; it was repeated without that flag.
|
||||
The validator-only command also exited 2 until the required streaming flags were included; all four
|
||||
corrected strict-validator runs passed.
|
||||
- `FloorProfileBlend` still reports its L25 alternate-style failures: SurfaceWorld style 1 is 2.544 vox
|
||||
over the 2.000 threshold and style 3 is 3.645 vox. Both values are identical in the L25 logs; they are
|
||||
pre-existing and are not capsule, support, open-air or naturalness failures.
|
||||
- Initial capture attempts used a wrong yaw and missed/partially missed the FI export volume. After
|
||||
correcting the view and centering the export, the final captures above were rendered. Several inside
|
||||
camera placements showed wall-only or void-only views; the selected interior frame is at the first
|
||||
bridged segment.
|
||||
|
||||
No run exceeded 30 minutes or remained stuck for 15 minutes. Exports and logs are under `Saved/Linux/`;
|
||||
this round added only this report and the four small JPEGs under `Standalone/`. An unrelated pre-existing
|
||||
untracked `Linux18/exports/` directory was left untouched. No Unreal edits, commit, push or stash were made.
|
||||
|
||||
## Orchestrator review (Claude) — side branch, not merged
|
||||
|
||||
- The audits pass (8/8 rows at 0/0/0), but the SW "natural bridge" is a ~2 km rock ramp spiralling through the
|
||||
open SurfaceWorld sky from the ceiling (≈−143 m) to the ground (≈−497 m). `sw-natural-bridge-wide.jpg` and
|
||||
`sw-passage-outside.jpg` show fragmented floating slabs with thin shard spikes: not natural, and visible
|
||||
from far away.
|
||||
- Decision (delegated by the owner): one shared mechanism for every strate. When a passage must cross a large
|
||||
open void inside a strate, it descends inside a natural rock column (ceiling-to-ground, like a
|
||||
stalactite/stalagmite pair joined), with the spiral tunnel inside, generalising the L25 FI anchor root.
|
||||
No long bridges through open air. Implemented in L27 on `wip/l25-helix`.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
@@ -10,7 +10,11 @@
|
||||
#include "VoxelTerrainOpDefinition.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
struct FStandaloneParitySample
|
||||
{
|
||||
@@ -20,6 +24,23 @@ struct FStandaloneParitySample
|
||||
int32 StrateIndex = INDEX_NONE;
|
||||
};
|
||||
|
||||
struct FStandalonePassageBridgeRange
|
||||
{
|
||||
std::size_t passageIndex=0;
|
||||
std::size_t firstSegment=0;
|
||||
std::size_t lastSegment=0;
|
||||
std::uint64_t unsupportedSamples=0;
|
||||
double unsupportedLengthMetres=0.0;
|
||||
std::array<double,4> startPoint{};
|
||||
std::array<double,4> endPoint{};
|
||||
};
|
||||
|
||||
struct FStandalonePassageBridgeCache
|
||||
{
|
||||
std::mutex mutex;
|
||||
std::unordered_map<std::string,std::vector<FStandalonePassageBridgeRange>> byLayout;
|
||||
};
|
||||
|
||||
struct FStandaloneOwnerWorld
|
||||
{
|
||||
std::unique_ptr<UVoxelSettings> Settings;
|
||||
@@ -45,6 +66,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>();
|
||||
};
|
||||
|
||||
namespace StandaloneOwnerLoader
|
||||
|
||||
@@ -458,15 +458,26 @@ namespace StandaloneStreamBenchmark
|
||||
bool hasDeepUnsupported=false;
|
||||
bool passed=false;
|
||||
};
|
||||
struct FPassageSegmentSupportAudit
|
||||
{
|
||||
std::uint64_t centers=0,unsupported=0,nativeUnsupported=0;
|
||||
std::array<double,3> firstUnsupported{};
|
||||
std::array<double,4> firstNativeDensities{};
|
||||
std::uint32_t firstNativeSupportedProbes=0;
|
||||
bool hasUnsupported=false;
|
||||
};
|
||||
const double coreMetres=static_cast<double>(World.CoreVoxelSizeMicrons)/1000000.0;
|
||||
const auto auditPlayerPath=[&](const VoxelForgeCore::World& Field,
|
||||
const std::vector<std::array<double,3>>& path,
|
||||
double supportStartMetres,double supportStepMetres,
|
||||
double supportMaximumMetres,std::uint32_t minimumSupportProbes,
|
||||
double supportProbeRadiusMetres)
|
||||
double supportProbeRadiusMetres,
|
||||
const VoxelForgeCore::World* nativeField=nullptr,
|
||||
std::vector<FPassageSegmentSupportAudit>* segmentSupport=nullptr)
|
||||
{
|
||||
FWalkAudit audit;
|
||||
if(path.size()<2)return audit;
|
||||
if(segmentSupport)segmentSupport->assign(path.size()-1,{});
|
||||
constexpr std::array<double,7> heights{{-0.84,-0.60,-0.30,0.0,0.30,0.60,0.84}};
|
||||
constexpr std::array<std::array<double,2>,8> directions{{
|
||||
{{1.0,0.0}},{{0.7071067811865475,0.7071067811865475}},{{0.0,1.0}},
|
||||
@@ -510,6 +521,9 @@ namespace StandaloneStreamBenchmark
|
||||
const double y=(a[1]+dy*t)*coreMetres;
|
||||
const double z=(a[2]+dz*t)*coreMetres;
|
||||
++audit.centers;
|
||||
FPassageSegmentSupportAudit* segmentAudit=segmentSupport
|
||||
?&(*segmentSupport)[segment-1]:nullptr;
|
||||
if(segmentAudit)++segmentAudit->centers;
|
||||
for(const double h:heights)
|
||||
{
|
||||
const double capOffset=std::max(0.0,std::abs(h)-0.54);
|
||||
@@ -526,6 +540,7 @@ namespace StandaloneStreamBenchmark
|
||||
{{0.0,supportProbeRadiusMetres}},
|
||||
{{0.0,-supportProbeRadiusMetres}}}};
|
||||
std::uint32_t supportedProbes=0;
|
||||
std::uint32_t nativeSupportedProbes=0;
|
||||
double firstUnsupportedX=x,firstUnsupportedY=y,lastUnsupportedDensity=0.0;
|
||||
std::array<bool,5> supportMask{};
|
||||
for(std::size_t offsetIndex=0;offsetIndex<supportOffsets.size();++offsetIndex)
|
||||
@@ -544,12 +559,41 @@ namespace StandaloneStreamBenchmark
|
||||
supported=true;break;
|
||||
}
|
||||
}
|
||||
if(nativeField)
|
||||
{
|
||||
for(double depth=supportStartMetres;
|
||||
depth<=supportMaximumMetres+1.0e-6;depth+=supportStepMetres)
|
||||
if(nativeField->densityAt({supportX/coreMetres,
|
||||
supportY/coreMetres,(footZ-depth)/coreMetres})<0.0f)
|
||||
{++nativeSupportedProbes;break;}
|
||||
}
|
||||
if(supported){++supportedProbes;supportMask[offsetIndex]=true;}
|
||||
else{firstUnsupportedX=supportX;firstUnsupportedY=supportY;}
|
||||
}
|
||||
if(nativeField&&segmentAudit
|
||||
&&nativeSupportedProbes<minimumSupportProbes)
|
||||
++segmentAudit->nativeUnsupported;
|
||||
if(supportedProbes<minimumSupportProbes)
|
||||
{
|
||||
++audit.unsupported;
|
||||
if(segmentAudit)
|
||||
{
|
||||
++segmentAudit->unsupported;
|
||||
if(!segmentAudit->hasUnsupported)
|
||||
{
|
||||
segmentAudit->hasUnsupported=true;
|
||||
segmentAudit->firstUnsupported={x,y,footZ};
|
||||
segmentAudit->firstNativeSupportedProbes=nativeSupportedProbes;
|
||||
if(nativeField)
|
||||
{
|
||||
constexpr std::array<double,4> depths{{0.125,1.0,2.0,4.0}};
|
||||
for(std::size_t depthIndex=0;depthIndex<depths.size();++depthIndex)
|
||||
segmentAudit->firstNativeDensities[depthIndex]=nativeField->densityAt({
|
||||
x/coreMetres,y/coreMetres,
|
||||
(footZ-depths[depthIndex])/coreMetres});
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!audit.hasDeepUnsupported&&footZ<-80.0)
|
||||
{
|
||||
audit.hasDeepUnsupported=true;
|
||||
@@ -682,7 +726,7 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
struct FPassageOpenAirAudit
|
||||
{
|
||||
std::uint64_t supportSamples=0,violations=0,legacyViolations=0;
|
||||
std::uint64_t supportSamples=0,violations=0,legacyViolations=0,bridgeSamples=0;
|
||||
std::uint64_t routeSupportSamples=0,routeSupportViolations=0;
|
||||
std::array<std::array<double,4>,8> firstViolations{};
|
||||
std::size_t firstViolationCount=0;
|
||||
@@ -708,11 +752,13 @@ namespace StandaloneStreamBenchmark
|
||||
{{0.9238795325112867,-0.3826834323650898}}}};
|
||||
const auto auditPassageOpenAir=[&](const VoxelForgeCore::World& Field,
|
||||
const VoxelForgeCore::World& Native,
|
||||
const VoxelForgeCore::ResolvedLayoutPassageV1& passage)
|
||||
const VoxelForgeCore::ResolvedLayoutPassageV1& passage,
|
||||
std::size_t passageIndex)
|
||||
{
|
||||
FPassageOpenAirAudit audit;
|
||||
const auto inspect=[&](double xMetres,double yMetres,double floorMetres,
|
||||
double depthMetres,double supportThicknessMetres,bool routeSupport=false)
|
||||
double depthMetres,double supportThicknessMetres,bool routeSupport=false,
|
||||
std::size_t passageSegment=std::numeric_limits<std::size_t>::max())
|
||||
{
|
||||
if(depthMetres<=0.0||depthMetres>supportThicknessMetres+1.0e-6)return;
|
||||
++audit.supportSamples;
|
||||
@@ -727,6 +773,10 @@ namespace StandaloneStreamBenchmark
|
||||
++audit.legacyViolations;
|
||||
if(nativeDensity>=0.0f&&passageDensity<0.0f)
|
||||
{
|
||||
const bool connectedBridge=!routeSupport
|
||||
&&passageSegment!=std::numeric_limits<std::size_t>::max()
|
||||
&&Field.isPassageBridgeSample(passageIndex,passageSegment,sample);
|
||||
if(connectedBridge){++audit.bridgeSamples;return;}
|
||||
audit.routeSupportViolations+=routeSupport;
|
||||
if(audit.firstViolationCount<audit.firstViolations.size())
|
||||
audit.firstViolations[audit.firstViolationCount++]={xMetres,yMetres,
|
||||
@@ -736,7 +786,8 @@ namespace StandaloneStreamBenchmark
|
||||
};
|
||||
const auto inspectFloorDisc=[&](double xMetres,double yMetres,double floorMetres,
|
||||
double supportRadiusMetres,double depthMetres,double supportThicknessMetres,
|
||||
bool routeSupport=false)
|
||||
bool routeSupport=false,
|
||||
std::size_t passageSegment=std::numeric_limits<std::size_t>::max())
|
||||
{
|
||||
const std::size_t radialSteps=static_cast<std::size_t>(std::ceil(
|
||||
supportRadiusMetres/PassageSupportAuditSpacingMetres));
|
||||
@@ -746,7 +797,8 @@ namespace StandaloneStreamBenchmark
|
||||
radialStep*PassageSupportAuditSpacingMetres);
|
||||
for(const auto& direction:PassageSupportAuditDirections)
|
||||
inspect(xMetres+direction[0]*radial,yMetres+direction[1]*radial,
|
||||
floorMetres,depthMetres,supportThicknessMetres,routeSupport);
|
||||
floorMetres,depthMetres,supportThicknessMetres,routeSupport,
|
||||
passageSegment);
|
||||
}
|
||||
};
|
||||
for(std::size_t segment=1;segment<passage.controlPoints.size();++segment)
|
||||
@@ -772,7 +824,8 @@ namespace StandaloneStreamBenchmark
|
||||
+(qToMetres(b.radiusSubvoxels)-qToMetres(a.radiusSubvoxels))*t;
|
||||
const double floorZ=centerZ-radius+0.20;
|
||||
for(const double depth:PassageSupportAuditDepthsMetres)
|
||||
inspectFloorDisc(centerX,centerY,floorZ,radius,depth,1.0);
|
||||
inspectFloorDisc(centerX,centerY,floorZ,radius,depth,1.0,
|
||||
false,segment-1);
|
||||
}
|
||||
}
|
||||
const auto upperStratum=std::find_if(ResolvedLayout->strates.begin(),
|
||||
@@ -841,6 +894,124 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
return audit;
|
||||
};
|
||||
struct FPassageBridgeAudit
|
||||
{
|
||||
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;
|
||||
bool passed=true;
|
||||
};
|
||||
const auto auditPassageBridges=[&](const VoxelForgeCore::World& Field,
|
||||
const VoxelForgeCore::World& Native,
|
||||
const VoxelForgeCore::ResolvedLayoutPassageV1& passage,
|
||||
std::size_t passageIndex)
|
||||
{
|
||||
FPassageBridgeAudit audit;
|
||||
const double qMetres=coreMetres
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
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}}}};
|
||||
const auto endpointConnected=[&](const std::array<double,4>& point)
|
||||
{
|
||||
const double x=point[0]*qMetres,y=point[1]*qMetres;
|
||||
const double floor=point[2]*qMetres-point[3]*qMetres+0.13;
|
||||
std::uint32_t supported=0;
|
||||
for(const auto& offset:supportOffsets)
|
||||
{
|
||||
bool solid=false;
|
||||
for(const double depth:{0.125,0.25})
|
||||
if(Native.densityAt({(x+offset[0])/coreMetres,
|
||||
(y+offset[1])/coreMetres,(floor-depth)/coreMetres})<0.0f)
|
||||
{solid=true;break;}
|
||||
supported+=solid;
|
||||
}
|
||||
return supported>=3;
|
||||
};
|
||||
const auto bridgeSolidAt=[&](std::size_t segment,double x,double y,double z)
|
||||
{
|
||||
return Field.isPassageBridgeSample(passageIndex,segment,
|
||||
{x/coreMetres,y/coreMetres,z/coreMetres});
|
||||
};
|
||||
const auto thicknessAt=[&](std::size_t segment,double x,double y,double floor)
|
||||
{
|
||||
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;
|
||||
};
|
||||
for(const auto& range:Field.passageBridgeRanges())
|
||||
{
|
||||
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);
|
||||
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;
|
||||
for(std::size_t probe=0;probe<probeCount;++probe)
|
||||
{
|
||||
const std::size_t segment=range.firstSegment
|
||||
+std::min(segmentCount-1,(probe*segmentCount)/probeCount);
|
||||
const auto& a=passage.controlPoints[segment];
|
||||
const auto& b=passage.controlPoints[segment+1];
|
||||
const double ax=qToMetres(a.positionSubvoxels[0]);
|
||||
const double ay=qToMetres(a.positionSubvoxels[1]);
|
||||
const double az=qToMetres(a.positionSubvoxels[2]);
|
||||
const double bx=qToMetres(b.positionSubvoxels[0]);
|
||||
const double by=qToMetres(b.positionSubvoxels[1]);
|
||||
const double bz=qToMetres(b.positionSubvoxels[2]);
|
||||
const double x=(ax+bx)*0.5,y=(ay+by)*0.5;
|
||||
const double z=(az+bz)*0.5;
|
||||
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 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");
|
||||
}
|
||||
return audit;
|
||||
};
|
||||
if(Assembly)
|
||||
for(const auto& stratum:ResolvedLayout->strates)
|
||||
{
|
||||
@@ -939,9 +1110,12 @@ 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;
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
std::uint64_t openAirAudits=0,openAirBridgeSamples=0,bridgeSpans=0,
|
||||
bridgeConnectedEnds=0;
|
||||
bool bridgeAuditsPassed=true;
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
std::vector<std::array<double,3>> path;path.reserve(passage.controlPoints.size());
|
||||
for(const auto& point:passage.controlPoints)
|
||||
{
|
||||
@@ -951,12 +1125,21 @@ namespace StandaloneStreamBenchmark
|
||||
path.push_back({qToMetres(point.positionSubvoxels[0])/coreMetres,
|
||||
qToMetres(point.positionSubvoxels[1])/coreMetres,playerZ/256.0});
|
||||
}
|
||||
const FWalkAudit passageAudit=auditPlayerPath(Shape,path,0.125,0.125,0.25,3,0.45);
|
||||
std::vector<FPassageSegmentSupportAudit> segmentSupport;
|
||||
const FWalkAudit passageAudit=auditPlayerPath(Shape,path,0.125,0.125,0.25,3,0.45,
|
||||
&blockedWorld,&segmentSupport);
|
||||
const FPassageNaturalnessAudit naturalAudit=auditPassageNaturalness(Shape,passage);
|
||||
const FPassageOpenAirAudit openAirAudit=auditPassageOpenAir(Shape,blockedWorld,passage);
|
||||
const FPassageOpenAirAudit openAirAudit=auditPassageOpenAir(Shape,blockedWorld,
|
||||
passage,passageIndex);
|
||||
const FPassageBridgeAudit bridgeAudit=auditPassageBridges(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;
|
||||
++openAirAudits;
|
||||
const auto radialSquared=[](const std::array<std::int64_t,3>& p)
|
||||
{return p[0]*p[0]+p[1]*p[1];};
|
||||
@@ -984,9 +1167,10 @@ 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 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 bridge_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.legacyViolations),
|
||||
static_cast<unsigned long long>(openAirAudit.routeSupportSamples),
|
||||
static_cast<unsigned long long>(openAirAudit.routeSupportViolations),
|
||||
@@ -1000,8 +1184,58 @@ namespace StandaloneStreamBenchmark
|
||||
for(std::size_t i=0;i<passageAudit.firstBlockedCount;++i)
|
||||
{
|
||||
const auto& sample=passageAudit.firstBlocked[i];
|
||||
std::printf("[ResolvedPassageAuditSample] id=%s kind=blocked xyz_m=(%.3f,%.3f,%.3f) density=%.6f\n",
|
||||
passage.stableId.c_str(),sample[0],sample[1],sample[2],sample[3]);
|
||||
double nearestDistanceSquared=std::numeric_limits<double>::infinity();
|
||||
std::size_t nearestSegment=0;
|
||||
double nearestT=0.0;
|
||||
double nearestPassageSdf=std::numeric_limits<double>::infinity();
|
||||
std::size_t nearestPassageSegment=0;
|
||||
for(std::size_t segment=0;segment+1<path.size();++segment)
|
||||
{
|
||||
const auto& a=path[segment];const auto& b=path[segment+1];
|
||||
const double dx=b[0]-a[0],dy=b[1]-a[1],dz=b[2]-a[2];
|
||||
const double lengthSquared=dx*dx+dy*dy+dz*dz;
|
||||
const double t=lengthSquared>0.0?std::clamp(
|
||||
((sample[0]/coreMetres-a[0])*dx+(sample[1]/coreMetres-a[1])*dy
|
||||
+(sample[2]/coreMetres-a[2])*dz)/lengthSquared,0.0,1.0):0.0;
|
||||
const double ex=sample[0]/coreMetres-(a[0]+t*dx);
|
||||
const double ey=sample[1]/coreMetres-(a[1]+t*dy);
|
||||
const double ez=sample[2]/coreMetres-(a[2]+t*dz);
|
||||
const double distanceSquared=ex*ex+ey*ey+ez*ez;
|
||||
if(distanceSquared<nearestDistanceSquared)
|
||||
{nearestDistanceSquared=distanceSquared;nearestSegment=segment;nearestT=t;}
|
||||
}
|
||||
for(std::size_t segment=0;segment+1<passage.controlPoints.size();++segment)
|
||||
{
|
||||
const auto& a=passage.controlPoints[segment];
|
||||
const auto& b=passage.controlPoints[segment+1];
|
||||
const double ax=qToMetres(a.positionSubvoxels[0]);
|
||||
const double ay=qToMetres(a.positionSubvoxels[1]);
|
||||
const double az=qToMetres(a.positionSubvoxels[2]);
|
||||
const double dx=qToMetres(b.positionSubvoxels[0]-a.positionSubvoxels[0]);
|
||||
const double dy=qToMetres(b.positionSubvoxels[1]-a.positionSubvoxels[1]);
|
||||
const double dz=qToMetres(b.positionSubvoxels[2]-a.positionSubvoxels[2]);
|
||||
const double lengthSquared=dx*dx+dy*dy+dz*dz;
|
||||
const double t=lengthSquared>0.0?std::clamp(
|
||||
((sample[0]-ax)*dx+(sample[1]-ay)*dy+(sample[2]-az)*dz)
|
||||
/lengthSquared,0.0,1.0):0.0;
|
||||
const double ex=sample[0]-(ax+t*dx),ey=sample[1]-(ay+t*dy),
|
||||
ez=sample[2]-(az+t*dz);
|
||||
const double radius=qToMetres(a.radiusSubvoxels)
|
||||
+(qToMetres(b.radiusSubvoxels)-qToMetres(a.radiusSubvoxels))*t;
|
||||
const double signedDistance=std::sqrt(ex*ex+ey*ey+ez*ez)-radius;
|
||||
if(signedDistance<nearestPassageSdf)
|
||||
{nearestPassageSdf=signedDistance;nearestPassageSegment=segment;}
|
||||
}
|
||||
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,
|
||||
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",
|
||||
passage.stableId.c_str(),sample[0],sample[1],sample[2],sample[3],
|
||||
nativeDensity,nearestSegment,nearestT,nearestPassageSegment,
|
||||
nearestPassageSdf,bridgeVolume?1:0,
|
||||
blockedWorld.densityDispatchPathAt(nativePosition).c_str());
|
||||
}
|
||||
for(std::size_t i=0;i<passageAudit.firstUnsupportedCount;++i)
|
||||
{
|
||||
@@ -1010,6 +1244,30 @@ namespace StandaloneStreamBenchmark
|
||||
passageAudit.firstUnsupported[i][1],passageAudit.firstUnsupported[i][2],
|
||||
passageAudit.firstUnsupported[i][3]);
|
||||
}
|
||||
for(std::size_t segment=0;segment<segmentSupport.size();++segment)
|
||||
{
|
||||
const auto& item=segmentSupport[segment];
|
||||
if(!item.hasUnsupported)continue;
|
||||
const auto& a=path[segment];const auto& b=path[segment+1];
|
||||
const auto& sample=item.firstUnsupported;
|
||||
const double firstX=sample[0]/coreMetres;
|
||||
const double firstY=sample[1]/coreMetres;
|
||||
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,
|
||||
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",
|
||||
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,
|
||||
item.firstNativeSupportedProbes,item.firstNativeDensities[0],item.firstNativeDensities[1],
|
||||
item.firstNativeDensities[2],item.firstNativeDensities[3],
|
||||
blockedWorld.densityDispatchPathAt({firstX,firstY,firstZ}).c_str());
|
||||
}
|
||||
}
|
||||
std::uint64_t routePoints=0,routePasses=0,routeWalkPasses=0,routeBindings=0,routeBindingFailures=0;
|
||||
struct FRouteAuditInput
|
||||
@@ -1082,7 +1340,7 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
const FWalkAudit routeAudit=auditPlayerPath(Shape,route,supportStartMetres,
|
||||
supportStepMetres,supportMaximumMetres,minimumSupportProbes,
|
||||
supportProbeRadiusMetres);
|
||||
supportProbeRadiusMetres,nullptr,nullptr);
|
||||
routeAuditInputs.push_back({stratum.strateIndex,route,supportStartMetres,
|
||||
supportStepMetres,supportMaximumMetres,supportProbeRadiusMetres,
|
||||
minimumSupportProbes});
|
||||
@@ -1151,7 +1409,7 @@ namespace StandaloneStreamBenchmark
|
||||
qToMetres(point.positionSubvoxels[1])/coreMetres,playerZ/256.0});
|
||||
}
|
||||
const FWalkAudit blockedAudit=auditPlayerPath(blockedWorld,path,
|
||||
0.125,0.125,0.25,3,0.45);
|
||||
0.125,0.125,0.25,3,0.45,nullptr,nullptr);
|
||||
blockedControls+=blockedAudit.blocked>0&&!blockedAudit.passed;
|
||||
std::printf("[ResolvedPassageBlockedControl] id=%s blocked_capsule_samples=%llu detected=%s\n",
|
||||
passage.stableId.c_str(),static_cast<unsigned long long>(blockedAudit.blocked),
|
||||
@@ -1161,7 +1419,7 @@ namespace StandaloneStreamBenchmark
|
||||
{
|
||||
const FWalkAudit controlRoute=auditPlayerPath(blockedWorld,input.path,
|
||||
input.supportStart,input.supportStep,input.supportMaximum,
|
||||
input.minimumSupportProbes,input.supportProbeRadius);
|
||||
input.minimumSupportProbes,input.supportProbeRadius,nullptr,nullptr);
|
||||
std::printf("[ResolvedStrateRouteBlockedControl] strate=%d blocked=%llu unsupported=%llu status=%s\n",
|
||||
input.strateIndex,static_cast<unsigned long long>(controlRoute.blocked),
|
||||
static_cast<unsigned long long>(controlRoute.unsupported),
|
||||
@@ -1171,13 +1429,19 @@ 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 l24_negative_control=%s status=%s\n",
|
||||
std::printf("[PassageOpenAirAuditSummary] interfaces=%llu samples=%llu violations=%llu bridge_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),
|
||||
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 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");
|
||||
@@ -1188,6 +1452,7 @@ namespace StandaloneStreamBenchmark
|
||||
const bool auditPassed=passageCount>0&&passagePasses==passageCount
|
||||
&&naturalPasses==passageCount&&l23SmoothWallDetected
|
||||
&&openAirAudits==passageCount&&openAirViolations==0&&l24OpenAirControlDetected
|
||||
&&bridgeAuditsPassed
|
||||
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
|
||||
&&routeBindingFailures==0
|
||||
&&blockedCompiled&&blockedControls==passageCount;
|
||||
|
||||
Reference in new issue
Block a user