wip(assembler): round L23 -- inter-strate passages planned from the resolved layout and carved

After L22 the FI -> SW passage no longer connected the strates (legacy owner-voxel coordinates,
lower end 23 m above SW), and Core never carved passages through the bands. Now the director
plans passages from the resolved layout (PlanResolvedLayoutPassagesV1; deterministic hash of
seed/strates/connection; placement disk from PassageConfig, 8-24 m around (0,0)); shared
composition applies generator field -> bands -> bounded passage carve and support shelf, opening
the bands only along the tunnel. Mouths bind to the strate routes. Capsule audit: 0 blocked /
0 unsupported (325,962 samples FI, 284,319 TunnelNetwork), negative controls detected; all 8
generator kinds exercised. SW away from the passage is bit-identical. Layout V3, evaluator 9.
Open: look not yet natural (stepped tube in FI void, terraced SW arrival); SW-as-upper route
planning fails; Unreal adapter uncompiled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-20 18:22:12 +02:00
1 parent 55c26a05a7
commit 71d09a8c52
14 files changed
+1465 -112

No files matched your search

+188 -22
View File
@@ -33,7 +33,7 @@
class FStandaloneShapeWorld
{
public:
const VoxelForgeCore::ResolvedLayoutSnapshotV2* ResolvedLayout=nullptr;
const VoxelForgeCore::ResolvedLayoutSnapshotV3* ResolvedLayout=nullptr;
struct FResolvedStratumBounds
{
double BottomZ=0.0;
@@ -97,6 +97,170 @@ public:
return -std::max(0.05f,static_cast<float>(band->solidDensityQ16)/65536.0f);
}
float ApplyResolvedStratePassageCarves(float density,float X,float Y,float Z) const
{
if(!ResolvedLayout||ResolvedLayout->passages.empty()||!std::isfinite(X)
||!std::isfinite(Y)||!std::isfinite(Z))return density;
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double qx=static_cast<double>(X)*Q,qy=static_cast<double>(Y)*Q,qz=static_cast<double>(Z)*Q;
const double qPerMetre=Q*1000000.0/std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
const double floorThicknessVoxels=qPerMetre/Q;
const double playerCenterAboveFloorQ=1.13*qPerMetre;
float composed=density;
for(const auto& passage:ResolvedLayout->passages)
{
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;
const std::ptrdiff_t lowerMouthIndex=std::clamp<std::ptrdiff_t>(
static_cast<std::ptrdiff_t>(passage.lowerMouthControlIndex),1,lastIndex);
const auto mainEnd=passage.controlPoints.begin()+lowerMouthIndex+1;
const auto carveFound=std::lower_bound(passage.controlPoints.begin(),mainEnd,qz,
[&](const auto& point,double value){return floorProfile(point)>value;});
const std::ptrdiff_t carveCenter=carveFound-passage.controlPoints.begin();
const std::ptrdiff_t carveBegin=std::max<std::ptrdiff_t>(0,carveCenter-3);
const std::ptrdiff_t carveEnd=std::min<std::ptrdiff_t>(lowerMouthIndex-1,carveCenter+2);
double bestSignedDistance=std::numeric_limits<double>::infinity();
double bestT=0.0;
std::ptrdiff_t bestIndex=0;
bool hasSupportFloor=false;
double bestSupportFloorVoxels=-std::numeric_limits<double>::infinity();
double bestSupportDensity=0.0;
const auto considerCarveSegment=[&](std::ptrdiff_t index)
{
const auto& a=passage.controlPoints[static_cast<std::size_t>(index)];
const auto& b=passage.controlPoints[static_cast<std::size_t>(index+1)];
const double ax=static_cast<double>(a.positionSubvoxels[0]);
const double ay=static_cast<double>(a.positionSubvoxels[1]);
const double az=static_cast<double>(a.positionSubvoxels[2]);
const double dx=static_cast<double>(b.positionSubvoxels[0])-ax;
const double dy=static_cast<double>(b.positionSubvoxels[1])-ay;
const double dz=static_cast<double>(b.positionSubvoxels[2])-az;
const double lengthSq=dx*dx+dy*dy+dz*dz;
if(lengthSq<=0.0)return;
const double t=std::clamp(((qx-ax)*dx+(qy-ay)*dy+(qz-az)*dz)/lengthSq,0.0,1.0);
const double cx=ax+t*dx,cy=ay+t*dy,cz=az+t*dz;
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 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);
// 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)
considerCarveSegment(index);
if(!std::isfinite(bestSignedDistance))continue;
// Positive density is air; the passage SDF is applied after both owner bands.
composed=std::max(composed,static_cast<float>(-bestSignedDistance/Q));
const auto& nearestA=passage.controlPoints[static_cast<std::size_t>(bestIndex)];
const auto& nearestB=passage.controlPoints[static_cast<std::size_t>(bestIndex+1)];
const double nearestRadius=nearestA.radiusSubvoxels+bestT*(
static_cast<double>(nearestB.radiusSubvoxels)-nearestA.radiusSubvoxels);
const double nearestPlayerCenterZ=nearestA.positionSubvoxels[2]+bestT*(
static_cast<double>(nearestB.positionSubvoxels[2])-nearestA.positionSubvoxels[2])
-nearestRadius+playerCenterAboveFloorQ;
// Only compose a support shelf below the capsule feet. This keeps support
// probes on the matching floor while capsule samples remain air at bends.
if(qz<nearestPlayerCenterZ-0.90*qPerMetre)
{
const double supportCenterQueryQ=qz+playerCenterAboveFloorQ;
const auto supportFound=std::lower_bound(passage.controlPoints.begin(),
mainEnd,supportCenterQueryQ,
[&](const auto& point,double value){return floorProfile(point)>value;});
const std::ptrdiff_t supportCenter=supportFound-passage.controlPoints.begin();
const std::ptrdiff_t supportBegin=std::max<std::ptrdiff_t>(0,supportCenter-3);
const std::ptrdiff_t supportEnd=std::min<std::ptrdiff_t>(lowerMouthIndex-1,supportCenter+2);
const auto considerSupportSegment=[&](std::ptrdiff_t index)
{
const auto& segmentA=passage.controlPoints[static_cast<std::size_t>(index)];
const auto& segmentB=passage.controlPoints[static_cast<std::size_t>(index+1)];
const double dx=static_cast<double>(segmentB.positionSubvoxels[0])-segmentA.positionSubvoxels[0];
const double dy=static_cast<double>(segmentB.positionSubvoxels[1])-segmentA.positionSubvoxels[1];
const double horizontalLengthSq=dx*dx+dy*dy;
const double supportT=horizontalLengthSq>0.0
?std::clamp(((qx-segmentA.positionSubvoxels[0])*dx
+(qy-segmentA.positionSubvoxels[1])*dy)/horizontalLengthSq,0.0,1.0)
:bestT;
const double supportCenterX=segmentA.positionSubvoxels[0]+supportT*dx;
const double supportCenterY=segmentA.positionSubvoxels[1]+supportT*dy;
const double supportCenterZ=segmentA.positionSubvoxels[2]+supportT*(
static_cast<double>(segmentB.positionSubvoxels[2])-segmentA.positionSubvoxels[2]);
const double supportRadius=segmentA.radiusSubvoxels+supportT*(
static_cast<double>(segmentB.radiusSubvoxels)-segmentA.radiusSubvoxels);
// Leave a 12 cm settle gap below the capsule feet while staying inside
// the route support probes' search depth.
const double floorQ=supportCenterZ-supportRadius+0.12*qPerMetre;
const double floorVoxels=floorQ/Q;
const double horizontalX=qx-supportCenterX,horizontalY=qy-supportCenterY;
const double horizontalDistance=DeterministicSqrt(horizontalX*horizontalX+horizontalY*horizontalY);
const double supportHalfWidth=std::max(0.0,supportRadius-0.15*qPerMetre);
if(horizontalDistance<=supportHalfWidth&&Z>=floorVoxels-floorThicknessVoxels
&&Z<=floorVoxels+1.0/Q)
{
if(!hasSupportFloor||floorVoxels>bestSupportFloorVoxels)
{
bestSupportFloorVoxels=floorVoxels;hasSupportFloor=true;
bestSupportDensity=std::clamp(Z-floorVoxels,-floorThicknessVoxels,0.0);
}
}
};
for(std::ptrdiff_t index=supportBegin;index<=supportEnd;++index)
considerSupportSegment(index);
for(std::ptrdiff_t index=lowerMouthIndex;index<lastIndex;++index)
considerSupportSegment(index);
}
if(hasSupportFloor)
composed=std::min(composed,static_cast<float>(bestSupportDensity));
}
return composed;
}
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
{return ApplyResolvedStratePassageCarves(ApplyResolvedStrateBoundaryBand(density,X,Y,Z),X,Y,Z);}
bool ResolvedPassageIntersectsBox(const int32 minimum[3],const int32 maximum[3]) const
{
if(!ResolvedLayout||ResolvedLayout->passages.empty())return false;
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
for(const auto& passage:ResolvedLayout->passages)
{
for(std::size_t i=1;i<passage.controlPoints.size();++i)
{
const auto& a=passage.controlPoints[i-1];const auto& b=passage.controlPoints[i];
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;
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};
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[2],b.positionSubvoxels[2])/Q+radius};
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;
}
static bool MakeResolvedStratumBounds(double BottomZ,double TopZ,double SealThickness,
FResolvedStratumBounds& Out)
{
@@ -410,7 +574,7 @@ public:
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InGpuVisualDetailTiles={},
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InCpuVisualDetailTiles={},
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly=nullptr,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout=nullptr)
const VoxelForgeCore::ResolvedLayoutSnapshotV3* InResolvedLayout=nullptr)
{
using Clock=std::chrono::steady_clock;
const auto TotalStart=Clock::now();
@@ -571,7 +735,7 @@ public:
const FIntVector& ClipMaxVoxels,EDetailVariant Variant,
bool bInRoundTunnels,
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout,
const VoxelForgeCore::ResolvedLayoutSnapshotV3* InResolvedLayout,
std::string& Error)
{
using Clock=std::chrono::steady_clock;
@@ -611,7 +775,7 @@ public:
void InitializeNativeSurfaceOnly(FStandaloneOwnerWorld& World,
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout,
const VoxelForgeCore::ResolvedLayoutSnapshotV3* InResolvedLayout,
const FIntVector& InMinimum,const FIntVector& InMaximum)
{
Owner=&World;WorldAssembly=InWorldAssembly;ResolvedLayout=InResolvedLayout;
@@ -622,7 +786,7 @@ public:
bool BuildShapeDependencyProof(FStandaloneOwnerWorld& World,
const FIntVector& MinVoxels,const FIntVector& MaxVoxels,bool bInRoundTunnels,
const VoxelForgeCore::ResolvedLayoutSnapshotV2* InResolvedLayout=nullptr)
const VoxelForgeCore::ResolvedLayoutSnapshotV3* InResolvedLayout=nullptr)
{
Owner=&World;ResolvedLayout=InResolvedLayout;WorkerCount=1;RoundTunnelsEnabled=bInRoundTunnels;
bAnalyticOnly=true;bNativeOnly=false;Stats={};
@@ -1129,19 +1293,19 @@ public:
float NativeDensity=0.0f;
if(!NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{RecordNativeOnlyViolation();return 0.0f;}
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(NativeDensity,X,Y,Z),X,Y,Z);
}
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(X,Y,Z,IslandDensity))
{
++ThreadClassificationStats().NativeFloatFieldCalls;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(IslandDensity,X,Y,Z),X,Y,Z);
}
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{
++ThreadClassificationStats().NativeFloatFieldCalls;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(NativeDensity,X,Y,Z),X,Y,Z);
}
if(UsesLegacySurfaceField(X,Y,Z))
return LegacySurfaceDensityAt(X,Y,Z);
@@ -1191,21 +1355,21 @@ public:
if(!NativeSurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),NativeDensity))
{RecordNativeOnlyViolation();return 0.0f;}
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(NativeDensity,X,Y,Z),X,Y,Z);
}
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),IslandDensity))
{
++ThreadClassificationStats().NativeLatticeFieldCalls;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(IslandDensity,X,Y,Z),X,Y,Z);
}
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),NativeDensity))
{
++ThreadClassificationStats().NativeLatticeFieldCalls;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(NativeDensity,X,Y,Z),X,Y,Z);
}
if(UsesLegacySurfaceField(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z)))
@@ -1229,19 +1393,19 @@ public:
float NativeDensity=0.0f;
if(!NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{RecordNativeOnlyViolation();return 0.0f;}
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(NativeDensity,X,Y,Z),X,Y,Z);
}
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(X,Y,Z,IslandDensity))
{
++ThreadClassificationStats().NativeUnclampedFieldCalls;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(IslandDensity,X,Y,Z),X,Y,Z);
}
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{
++ThreadClassificationStats().NativeUnclampedFieldCalls;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z),X,Y,Z);
return ApplyEditBrushes(ApplyResolvedStrateComposition(NativeDensity,X,Y,Z),X,Y,Z);
}
if(UsesLegacySurfaceField(X,Y,Z))
return LegacySurfaceDensityAt(X,Y,Z);
@@ -1371,13 +1535,13 @@ private:
float Density=static_cast<float>(Proxy)*(1.0f/256.0f);
if((Density<0.0f)!=bRock)
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),1.0f/256.0f);
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(Density,FX,FY,FZ),FX,FY,FZ);
return ApplyEditBrushes(ApplyResolvedStrateComposition(Density,FX,FY,FZ),FX,FY,FZ);
}
// Match the fixed fallback to the edge of the stored proxy band. The
// displacement is tapered to zero there, so this avoids a magnitude
// jump along the candidate bit-mask boundary.
constexpr float ProxyBandEdge=DetailSupremum+0.125f;
return ApplyEditBrushes(ApplyResolvedStrateBoundaryBand(
return ApplyEditBrushes(ApplyResolvedStrateComposition(
bRock?-ProxyBandEdge:ProxyBandEdge,FX,FY,FZ),FX,FY,FZ);
}
@@ -1395,7 +1559,7 @@ private:
float IslandDensity=0.0f;
if(NativeFloatingIslandDensityAt(X,Y,Z,IslandDensity))
{
const float composed=ApplyResolvedStrateBoundaryBand(IslandDensity,X,Y,Z);
const float composed=ApplyResolvedStrateComposition(IslandDensity,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
}
if(UsesLegacySurfaceField(X,Y,Z))
@@ -1403,11 +1567,11 @@ private:
float NativeDensity=0.0f;
if(NativeSurfaceDensityAt(X,Y,Z,NativeDensity))
{
NativeDensity=ApplyResolvedStrateBoundaryBand(NativeDensity,X,Y,Z);
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=ApplyResolvedStrateBoundaryBand(Density,X,Y,Z);
const float composed=ApplyResolvedStrateComposition(Density,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
}
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
@@ -1422,7 +1586,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);
const float composed=ApplyResolvedStrateBoundaryBand(Density,X,Y,Z);
const float composed=ApplyResolvedStrateComposition(Density,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
}
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
@@ -1452,7 +1616,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=ApplyResolvedStrateBoundaryBand(Density,X,Y,Z);
Density=ApplyResolvedStrateComposition(Density,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
}
@@ -1590,6 +1754,8 @@ public:
else ++Counts.MixedBoxes;
return Result;
};
if(ResolvedPassageIntersectsBox(BMin,BMax))
return Finish(EVoxelTileClass::Mixed,true);
if(!EditBrushes.empty()&&EditBoundsMin.X<=BMax[0]&&EditBoundsMax.X>=BMin[0]
&&EditBoundsMin.Y<=BMax[1]&&EditBoundsMax.Y>=BMin[1]
&&EditBoundsMin.Z<=BMax[2]&&EditBoundsMax.Z>=BMin[2])
@@ -2319,7 +2485,7 @@ private:
// SurfaceWorld consumes biome overrides, terrain ops, water level and its sky cap in
// the legacy stack. Reuse that immutable evaluator as an exact temporary bridge.
// SurfaceWorld lit biomes, ops de terrain, niveau d'eau et plafond; reutiliser ce champ.
const float density=ApplyResolvedStrateBoundaryBand(Owner->Generator->GetDensityAt(X,Y,Z),X,Y,Z);
const float density=ApplyResolvedStrateComposition(Owner->Generator->GetDensityAt(X,Y,Z),X,Y,Z);
return ApplyEditBrushes(density,X,Y,Z);
}