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:
Fr0zkaandClaude Opus 5.5 committed 2026-10-21 02:39:44 +02:00
1 parent ef789aaa00
commit 37a223046c
13 files changed
+878 -41

No files matched your search

+308 -19
View File
@@ -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;
+62
View File
@@ -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))