feat(core): native Core caves generator, slice 1 -- spine + room, whole view 0.73 s (L54); research R23

Core.Caves.PrimitiveField v1, opt-in per strate (legacy TunnelNetwork assets stay legacy): solid host rock
and one smooth union of two elliptical spine tubes and an ellipsoid room, each intersected with its rising
floor by cubic smooth-max (W 2.4 m, measured fillet 0.70 m, no trailing floor cap). Bound in the L40 field
table and L42 capabilities, BVH feature candidates, isTileShapeFree aware, exact interval boxes (0 misses).
Capsule flood fill arrival -> departure passes (headroom >= 0.3 m, grade <= 15 deg); the generalized
whole-surface audit passes with its controls failing. Cold whole view 0.727 s, near ring 0.19 s. Graph
ordering/serialization changes are scoped to native caves (the flagged L47 demo is unchanged); verify
tiers write captures under --out only. fast/medium/full PASS, Clang and GCC.
Research R23 (deepened): per-tile LOD0 kernel speed plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-25 04:10:19 +01:00
1 parent 20f6c98ec5
commit 9458217d34
24 files changed
+471136 -59

No files matched your search

+553 -34
View File
@@ -133,13 +133,28 @@ public:
}
};
struct FCavesFieldAdapter
{
std::uint32_t CoreVoxelSizeMicrometres=0;
bool densityAt(float X,float Y,float Z,float& OutDensity) const noexcept
{
if(CoreVoxelSizeMicrometres==0||!std::isfinite(X)
||!std::isfinite(Y)||!std::isfinite(Z))return false;
const double metresPerVoxel=CoreVoxelSizeMicrometres/1000000.0;
// A negative metre distance denotes host rock. Cave features are composed
// into this solid adapter once, in ApplyResolvedCavernGraph.
OutDensity=static_cast<float>(-32.0/metresPerVoxel);
return std::isfinite(OutDensity);
}
};
using FGeneratorFieldAdapterV1=std::variant<std::monostate,
FSurfaceWorldFieldAdapter,FFloatingIslandFieldAdapter,
FLegacyGeneratorFieldAdapter>;
FCavesFieldAdapter,FLegacyGeneratorFieldAdapter>;
struct FBoundStratumField
{
enum class EKind : uint8 { Legacy, SurfaceWorld, FloatingIslands };
enum class EKind : uint8 { Legacy, SurfaceWorld, FloatingIslands, Caves };
const VoxelForgeCore::ResolvedLayoutStratumV2* Resolved=nullptr;
const VoxelForgeCore::WorldAssemblyStratumPlanV1* SurfacePlan=nullptr;
const VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1* IslandPlan=nullptr;
@@ -362,6 +377,15 @@ public:
}
}
}
else if(Stratum.generatorKind=="Caves"
&&Stratum.recipeId=="Core.Caves.PrimitiveField"
&&Stratum.recipeVersion==1
&&Stratum.passageCapabilities.hasNativeCaveField)
{
Bound.Kind=FBoundStratumField::EKind::Caves;
Bound.Evaluator.emplace<FCavesFieldAdapter>(
FCavesFieldAdapter{ResolvedLayout->coreVoxelSizeMicrometres});
}
if(std::holds_alternative<std::monostate>(Bound.Evaluator))
Bound.Evaluator.emplace<FLegacyGeneratorFieldAdapter>(
FLegacyGeneratorFieldAdapter{Owner?Owner->Generator.get():nullptr});
@@ -1791,8 +1815,14 @@ public:
const float composed=ApplyResolvedStratePassageCarves(
ApplyResolvedStrateBoundaryBand(graphDensity,X,Y,Z),X,Y,Z);
const float stamped=StampRuntime.empty()?composed:ApplyResolvedSdfStamps(composed,X,Y,Z);
// Keep the explicit graph floor cap after smooth unions, relief, passages,
// and decorative stamps so nothing can reopen the tunnel floor below profile.
const auto* bound=FindBoundStratumForManagerZ(Z);
if(bound&&bound->Kind==FBoundStratumField::EKind::Caves
&&bound->Resolved&&bound->Resolved->recipeId=="Core.Caves.PrimitiveField"
&&bound->Resolved->recipeVersion==1
&&bound->Resolved->passageCapabilities.hasNativeCaveField)
return stamped;
// The legacy landmark demo retains its compatibility floor cap. Native caves
// already combine each floor with its wall before the single void carve above.
return ApplyResolvedCavernFloorCaps(stamped,X,Y,Z);
}
@@ -2999,9 +3029,11 @@ public:
if(SharedDensitySource)return SharedDensitySource->DensityDispatchPathAt(X,Y,Z);
float Density=0.0f;
if(NativeFloatingIslandDensityAt(X,Y,Z,Density))return "FloatingIslands-native";
const auto* Bound=FindBoundStratumForZ(Z);
if(Bound&&Bound->Kind==FBoundStratumField::EKind::Caves
&&NativeSurfaceDensityAt(X,Y,Z,Density))return "Caves-native";
if(NativeSurfaceDensityAt(X,Y,Z,Density))return "SurfaceWorld-native";
if(UsesLegacySurfaceField(X,Y,Z))return "SurfaceWorld-legacy";
const FBoundStratumField* Bound=FindBoundStratumForZ(Z);
if(Bound&&Bound->Resolved)
{
const std::string& Kind=Bound->Resolved->generatorKind;
@@ -3121,6 +3153,7 @@ public:
}
bool bHasLegacySurfaceSample=false;
bool bHasNativeIslandSample=false;
bool bHasNativeCavesSample=false;
std::array<float,8> NativeIslandDensity{};
std::array<bool,8> HasNativeIslandSample{};
for(int32 Lane=0;Lane<Count;++Lane)
@@ -3129,11 +3162,14 @@ public:
const float SampleY=static_cast<float>(Y);
const float SampleZ=static_cast<float>(Z);
bHasLegacySurfaceSample|=UsesLegacySurfaceField(SampleX,SampleY,SampleZ);
const auto* bound=FindBoundStratumForZ(SampleZ);
bHasNativeCavesSample|=bound
&&bound->Kind==FBoundStratumField::EKind::Caves;
HasNativeIslandSample[static_cast<std::size_t>(Lane)]=NativeFloatingIslandDensityAt(
SampleX,SampleY,SampleZ,NativeIslandDensity[static_cast<std::size_t>(Lane)]);
bHasNativeIslandSample|=HasNativeIslandSample[static_cast<std::size_t>(Lane)];
}
if(bHasLegacySurfaceSample||bHasNativeIslandSample)
if(bHasLegacySurfaceSample||bHasNativeIslandSample||bHasNativeCavesSample)
{
for(int32 Lane=0;Lane<Count;++Lane)
{
@@ -3269,7 +3305,14 @@ private:
{
const VoxelForgeCore::ResolvedCavernRoomV1* room=nullptr;
const VoxelForgeCore::ResolvedCavernEdgeV1* edge=nullptr;
bool nativeCaves=false;
std::array<std::int64_t,3> boundsMinimumMicrometres{},boundsMaximumMicrometres{};
std::array<double,3> centerMetres{},halfExtentsMetres{};
struct FSegment
{
std::array<double,3> start{},end{},minimum{},maximum{};
};
std::vector<FSegment> segments;
std::array<float,3> centerVoxels{},halfExtentsVoxels{};
std::array<std::array<float,3>,4> controlPointsVoxels{};
float floorVoxels=0.0f;
@@ -3280,6 +3323,17 @@ private:
float irregularityWavelengthVoxels=0.0f;
std::uint64_t irregularitySeed=0;
};
struct FCavernSpatialNode
{
std::array<std::int64_t,3> minimumMicrometres{},maximumMicrometres{};
std::size_t first=0,end=0;
int left=-1,right=-1;
};
struct FMetricInterval
{
double minimum=0.0,maximum=0.0;
bool known=true;
};
int BuildStampSpatialNode(std::size_t first,std::size_t end)
{
@@ -3682,18 +3736,32 @@ private:
void BuildCavernSpatialIndex()
{
CavernRuntime.clear();
CavernRuntime.clear();CavernSpatialNodes.clear();CavernSpatialOrder.clear();
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0
||ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1)return;
const auto isNativeCavesStrate=[this](std::int32_t strateIndex)
{
const auto found=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[strateIndex](const auto& stratum)
{return stratum.strateIndex==strateIndex;});
return found!=ResolvedLayout->strates.end()
&&found->generatorKind=="Caves"
&&found->recipeId=="Core.Caves.PrimitiveField"
&&found->recipeVersion==1
&&found->passageCapabilities.hasNativeCaveField;
};
const float micronsPerVoxel=static_cast<float>(Owner->CoreVoxelSizeMicrons);
for(const auto& room:ResolvedLayout->cavernRooms)
{
FCavernRuntimeFeature runtime;runtime.room=&room;
runtime.nativeCaves=isNativeCavesStrate(room.strateIndex);
runtime.boundsMinimumMicrometres=room.boundsMinimumMicrometres;
runtime.boundsMaximumMicrometres=room.boundsMaximumMicrometres;
for(std::size_t axis=0;axis<3;++axis)
{
runtime.centerMetres[axis]=room.centerMicrometres[axis]/1000000.0;
runtime.halfExtentsMetres[axis]=room.halfExtentsMicrometres[axis]/1000000.0;
runtime.centerVoxels[axis]=static_cast<float>(
static_cast<double>(room.centerMicrometres[axis])/micronsPerVoxel);
runtime.halfExtentsVoxels[axis]=static_cast<float>(
@@ -3713,6 +3781,7 @@ private:
for(const auto& edge:ResolvedLayout->cavernEdges)
{
FCavernRuntimeFeature runtime;runtime.edge=&edge;
runtime.nativeCaves=isNativeCavesStrate(edge.strateIndex);
runtime.boundsMinimumMicrometres=edge.boundsMinimumMicrometres;
runtime.boundsMaximumMicrometres=edge.boundsMaximumMicrometres;
for(std::size_t point=0;point<4;++point)
@@ -3733,8 +3802,115 @@ private:
runtime.irregularityWavelengthVoxels=static_cast<float>(
static_cast<double>(edge.irregularityWavelengthMicrometres)/micronsPerVoxel);
runtime.irregularitySeed=edge.irregularitySeed;
runtime.segments.reserve(edge.polylineMicrometres.size()>1
?edge.polylineMicrometres.size()-1:0);
for(std::size_t point=1;point<edge.polylineMicrometres.size();++point)
{
FCavernRuntimeFeature::FSegment segment;
for(std::size_t axis=0;axis<3;++axis)
{
segment.start[axis]=edge.polylineMicrometres[point-1][axis]/1000000.0;
segment.end[axis]=edge.polylineMicrometres[point][axis]/1000000.0;
segment.minimum[axis]=std::min(segment.start[axis],segment.end[axis]);
segment.maximum[axis]=std::max(segment.start[axis],segment.end[axis]);
}
runtime.segments.push_back(segment);
}
CavernRuntime.push_back(runtime);
}
std::stable_sort(CavernRuntime.begin(),CavernRuntime.end(),[](const auto& a,const auto& b)
{
const std::int32_t leftStrate=a.room?a.room->strateIndex
:a.edge?a.edge->strateIndex:-1;
const std::int32_t rightStrate=b.room?b.room->strateIndex
:b.edge?b.edge->strateIndex:-1;
if(leftStrate!=rightStrate)return leftStrate<rightStrate;
if(a.nativeCaves!=b.nativeCaves)return a.nativeCaves;
if(!a.nativeCaves)return false;
const std::string& left=a.room?a.room->stableId:a.edge->stableId;
const std::string& right=b.room?b.room->stableId:b.edge->stableId;
return left<right;
});
CavernSpatialOrder.resize(CavernRuntime.size());
for(std::size_t i=0;i<CavernSpatialOrder.size();++i)
CavernSpatialOrder[i]=static_cast<std::uint32_t>(i);
if(!CavernSpatialOrder.empty())BuildCavernSpatialNode(0,CavernSpatialOrder.size());
}
int BuildCavernSpatialNode(std::size_t first,std::size_t end)
{
FCavernSpatialNode node;node.first=first;node.end=end;
node.minimumMicrometres.fill(std::numeric_limits<std::int64_t>::max());
node.maximumMicrometres.fill(std::numeric_limits<std::int64_t>::min());
for(std::size_t offset=first;offset<end;++offset)
{
const auto& feature=CavernRuntime[CavernSpatialOrder[offset]];
for(std::size_t axis=0;axis<3;++axis)
{
node.minimumMicrometres[axis]=std::min(node.minimumMicrometres[axis],
feature.boundsMinimumMicrometres[axis]);
node.maximumMicrometres[axis]=std::max(node.maximumMicrometres[axis],
feature.boundsMaximumMicrometres[axis]);
}
}
const int index=static_cast<int>(CavernSpatialNodes.size());
CavernSpatialNodes.push_back(node);
if(end-first>8)
{
std::size_t axis=0;
for(std::size_t candidate=1;candidate<3;++candidate)
if(static_cast<long double>(node.maximumMicrometres[candidate])
-node.minimumMicrometres[candidate]
>static_cast<long double>(node.maximumMicrometres[axis])
-node.minimumMicrometres[axis])axis=candidate;
const std::size_t middle=first+(end-first)/2;
std::nth_element(CavernSpatialOrder.begin()+first,
CavernSpatialOrder.begin()+middle,CavernSpatialOrder.begin()+end,
[this,axis](std::uint32_t leftIndex,std::uint32_t rightIndex)
{
const auto& left=CavernRuntime[leftIndex];
const auto& right=CavernRuntime[rightIndex];
const long double leftCenter=static_cast<long double>(
left.boundsMinimumMicrometres[axis])
+left.boundsMaximumMicrometres[axis];
const long double rightCenter=static_cast<long double>(
right.boundsMinimumMicrometres[axis])
+right.boundsMaximumMicrometres[axis];
const std::string& leftId=left.room?left.room->stableId:left.edge->stableId;
const std::string& rightId=right.room?right.room->stableId:right.edge->stableId;
return leftCenter==rightCenter?leftId<rightId:leftCenter<rightCenter;
});
CavernSpatialNodes[index].left=BuildCavernSpatialNode(first,middle);
CavernSpatialNodes[index].right=BuildCavernSpatialNode(middle,end);
}
return index;
}
void QueryCavernSpatialNode(int nodeIndex,
const std::array<std::int64_t,3>& minimum,
const std::array<std::int64_t,3>& maximum,
std::vector<std::uint32_t>& result) const
{
if(nodeIndex<0)return;
const auto& node=CavernSpatialNodes[static_cast<std::size_t>(nodeIndex)];
for(std::size_t axis=0;axis<3;++axis)
if(node.maximumMicrometres[axis]<minimum[axis]
||node.minimumMicrometres[axis]>maximum[axis])return;
if(node.left<0)
{
for(std::size_t offset=node.first;offset<node.end;++offset)
{
const auto index=CavernSpatialOrder[offset];
const auto& feature=CavernRuntime[index];bool overlaps=true;
for(std::size_t axis=0;axis<3;++axis)
overlaps=overlaps&&feature.boundsMinimumMicrometres[axis]<=maximum[axis]
&&feature.boundsMaximumMicrometres[axis]>=minimum[axis];
if(overlaps)result.push_back(index);
}
return;
}
QueryCavernSpatialNode(node.left,minimum,maximum,result);
QueryCavernSpatialNode(node.right,minimum,maximum,result);
}
std::vector<std::uint32_t> BuildCavernFeatureTileList(
@@ -3742,7 +3918,7 @@ private:
const std::array<std::int64_t,3>& maximumSubvoxels) const
{
std::vector<std::uint32_t> result;
if(CavernRuntime.empty())return result;
if(CavernRuntime.empty()||CavernSpatialNodes.empty())return result;
if(!Owner||Owner->CoreVoxelSizeMicrons==0)
{
result.resize(CavernRuntime.size());
@@ -3750,7 +3926,7 @@ private:
return result;
}
constexpr long double Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
std::array<long double,3> minimumMicrometres{},maximumMicrometres{};
std::array<std::int64_t,3> minimumMicrometres{},maximumMicrometres{};
for(std::size_t axis=0;axis<3;++axis)
{
if(minimumSubvoxels[axis]>maximumSubvoxels[axis])
@@ -3760,21 +3936,17 @@ private:
result[i]=static_cast<std::uint32_t>(i);
return result;
}
minimumMicrometres[axis]=static_cast<long double>(minimumSubvoxels[axis])
*Owner->CoreVoxelSizeMicrons/Q;
maximumMicrometres[axis]=static_cast<long double>(maximumSubvoxels[axis])
*Owner->CoreVoxelSizeMicrons/Q;
}
for(std::size_t index=0;index<CavernRuntime.size();++index)
{
const auto& feature=CavernRuntime[index];bool overlap=true;
for(std::size_t axis=0;axis<3;++axis)
overlap=overlap&&feature.boundsMaximumMicrometres[axis]
>=minimumMicrometres[axis]
&&feature.boundsMinimumMicrometres[axis]
<=maximumMicrometres[axis];
if(overlap)result.push_back(static_cast<std::uint32_t>(index));
minimumMicrometres[axis]=static_cast<std::int64_t>(std::floor(
static_cast<long double>(minimumSubvoxels[axis])
*Owner->CoreVoxelSizeMicrons/Q));
maximumMicrometres[axis]=static_cast<std::int64_t>(std::ceil(
static_cast<long double>(maximumSubvoxels[axis])
*Owner->CoreVoxelSizeMicrons/Q));
}
QueryCavernSpatialNode(0,minimumMicrometres,maximumMicrometres,result);
// Runtime order is legacy insertion order for legacy cavern graphs and
// canonical stable-ID order within native caves strates.
std::sort(result.begin(),result.end());
return result;
}
@@ -4892,6 +5064,7 @@ public:
static_cast<std::int64_t>(BMax)*Q};
if(!BuildStampTileList(minimum,maximum).empty())return false;
}
std::vector<std::uint32_t> caveCandidates;
if(!CavernRuntime.empty())
{
constexpr std::int64_t Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
@@ -4912,9 +5085,20 @@ public:
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
{
const auto* Candidate=FindBoundStratumForChunk(ChunkZ);
if(!Candidate||Candidate->Kind!=FBoundStratumField::EKind::SurfaceWorld
||!Candidate->SurfacePlan||ChunkZ<Candidate->SurfacePlan->bottomChunkZ
||ChunkZ>Candidate->SurfacePlan->topChunkZ)return false;
if(!Candidate)return false;
if(Candidate->Kind==FBoundStratumField::EKind::SurfaceWorld)
{
if(!Candidate->SurfacePlan||ChunkZ<Candidate->SurfacePlan->bottomChunkZ
||ChunkZ>Candidate->SurfacePlan->topChunkZ)return false;
}
else if(Candidate->Kind==FBoundStratumField::EKind::Caves)
{
if(!Candidate->Resolved||Candidate->Resolved->recipeId!="Core.Caves.PrimitiveField"
||Candidate->Resolved->recipeVersion!=1
||!Candidate->Resolved->passageCapabilities.hasNativeCaveField
||!std::get_if<FCavesFieldAdapter>(&Candidate->Evaluator))return false;
}
else return false;
if(BoundPlan&&BoundPlan!=Candidate)return false;
BoundPlan=Candidate;
}
@@ -4932,6 +5116,178 @@ public:
return BoundPlan!=nullptr;
}
FMetricInterval ClassifyNativeCaveFieldBox(const int32 BMin[3],const int32 BMax[3],
std::int32_t strateIndex,const std::vector<std::uint32_t>& candidates) const
{
const double metresPerVoxel=Owner&&Owner->CoreVoxelSizeMicrons>0
?Owner->CoreVoxelSizeMicrons/1000000.0:0.0;
if(!(metresPerVoxel>0.0))return {0.0,0.0,false};
std::array<double,3> lo{},hi{};
for(std::size_t axis=0;axis<3;++axis)
{
lo[axis]=static_cast<double>(BMin[axis])*metresPerVoxel;
hi[axis]=static_cast<double>(BMax[axis])*metresPerVoxel;
}
FMetricInterval caveVoid{std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity(),true};
bool haveFeature=false;
for(const std::uint32_t index:candidates)
{
if(index>=CavernRuntime.size())continue;
const auto& feature=CavernRuntime[index];
const std::int32_t featureStrate=feature.room?feature.room->strateIndex
:feature.edge?feature.edge->strateIndex:-1;
if(featureStrate!=strateIndex)continue;
FMetricInterval featureVoid;
if(feature.room)
{
double normalizedMinimumSquared=0.0,normalizedMaximumSquared=0.0;
double meanRadius=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double radius=feature.halfExtentsMetres[axis];
if(!(radius>0.0))return {0.0,0.0,false};
const double center=feature.centerMetres[axis];
const double deltaMinimum=center<lo[axis]?lo[axis]-center
:center>hi[axis]?center-hi[axis]:0.0;
const double deltaMaximum=std::max(std::abs(lo[axis]-center),
std::abs(hi[axis]-center));
normalizedMinimumSquared+=(deltaMinimum/radius)*(deltaMinimum/radius);
normalizedMaximumSquared+=(deltaMaximum/radius)*(deltaMaximum/radius);
meanRadius+=radius/3.0;
}
const FMetricInterval ellipsoid{
(std::sqrt(normalizedMinimumSquared)-1.0)*meanRadius,
(std::sqrt(normalizedMaximumSquared)-1.0)*meanRadius,true};
const double floorZ=feature.room->floorMicrometres/1000000.0;
const FMetricInterval floor={floorZ-hi[2],floorZ-lo[2],true};
const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width),
SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true};
}
else if(feature.edge&&!feature.segments.empty())
{
const double radius=feature.edge->boreRadiusMicrometres/1000000.0;
const double height=feature.edge->boreHeightMicrometres/1000000.0;
if(!(radius>0.0&&height>0.0))return {0.0,0.0,false};
double tubeMinimum=std::numeric_limits<double>::infinity();
double tubeMaximum=std::numeric_limits<double>::infinity();
double floorMinimum=std::numeric_limits<double>::infinity();
double floorMaximum=-std::numeric_limits<double>::infinity();
double maximumGrade=0.0;
for(const auto& segment:feature.segments)
{
const std::array<double,3> inverseScale={1.0/radius,1.0/radius,
1.0/height};
std::array<double,3> boxMinimum{},boxMaximum{},segmentStart{},segmentEnd{};
for(std::size_t axis=0;axis<3;++axis)
{
boxMinimum[axis]=lo[axis]*inverseScale[axis];
boxMaximum[axis]=hi[axis]*inverseScale[axis];
segmentStart[axis]=segment.start[axis]*inverseScale[axis];
segmentEnd[axis]=segment.end[axis]*inverseScale[axis];
}
double gapSquared=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double segmentMinimum=std::min(segmentStart[axis],segmentEnd[axis]);
const double segmentMaximum=std::max(segmentStart[axis],segmentEnd[axis]);
const double gap=segmentMaximum<boxMinimum[axis]
?boxMinimum[axis]-segmentMaximum
:segmentMinimum>boxMaximum[axis]
?segmentMinimum-boxMaximum[axis]:0.0;
gapSquared+=gap*gap;
}
const double distanceLower=std::sqrt(gapSquared);
const std::array<double,3> boxCenter={
(boxMinimum[0]+boxMaximum[0])*0.5,
(boxMinimum[1]+boxMaximum[1])*0.5,
(boxMinimum[2]+boxMaximum[2])*0.5};
double lengthSquared=0.0,dot=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double delta=segmentEnd[axis]-segmentStart[axis];
lengthSquared+=delta*delta;
dot+=(boxCenter[axis]-segmentStart[axis])*delta;
}
const double t=lengthSquared>0.0
?std::clamp(dot/lengthSquared,0.0,1.0):0.0;
double farthestSquared=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double fixedPoint=segmentStart[axis]
+t*(segmentEnd[axis]-segmentStart[axis]);
const double farthest=std::max(
std::abs(boxMinimum[axis]-fixedPoint),
std::abs(boxMaximum[axis]-fixedPoint));
farthestSquared+=farthest*farthest;
}
const double distanceUpper=std::sqrt(farthestSquared);
const double scale=std::min(radius,height);
tubeMinimum=std::min(tubeMinimum,(distanceLower-1.0)*scale);
tubeMaximum=std::min(tubeMaximum,(distanceUpper-1.0)*scale);
floorMinimum=std::min(floorMinimum,
std::min(segment.start[2],segment.end[2]));
floorMaximum=std::max(floorMaximum,
std::max(segment.start[2],segment.end[2]));
const double horizontal=std::hypot(segment.end[0]-segment.start[0],
segment.end[1]-segment.start[1]);
if(horizontal>1.0e-9)
maximumGrade=std::max(maximumGrade,
std::abs((segment.end[2]-segment.start[2])/horizontal));
}
if(!std::isfinite(tubeMinimum)||!std::isfinite(tubeMaximum)
||!std::isfinite(floorMinimum)||!std::isfinite(floorMaximum))
return {0.0,0.0,false};
const double scaleMinimum=1.0/std::sqrt(1.0+maximumGrade*maximumGrade);
const double numeratorMinimum=floorMinimum-hi[2];
const double numeratorMaximum=floorMaximum-lo[2];
const std::array<double,4> products={numeratorMinimum*scaleMinimum,
numeratorMinimum,numeratorMaximum*scaleMinimum,numeratorMaximum};
const FMetricInterval floor={
*std::min_element(products.begin(),products.end()),
*std::max_element(products.begin(),products.end()),true};
const FMetricInterval tube={tubeMinimum,tubeMaximum,true};
const double width=feature.edge->smoothWidthMicrometres/1000000.0;
featureVoid={SmoothMaxMetres(tube.minimum,floor.minimum,width),
SmoothMaxMetres(tube.maximum,floor.maximum,width),true};
}
else return {0.0,0.0,false};
if(!std::isfinite(featureVoid.minimum)||!std::isfinite(featureVoid.maximum))
return {0.0,0.0,false};
if(!haveFeature)
{
caveVoid=featureVoid;haveFeature=true;
}
else
{
const double width=feature.room
?feature.room->smoothWidthMicrometres/1000000.0
:feature.edge->smoothWidthMicrometres/1000000.0;
caveVoid.minimum=SmoothMinMetres(caveVoid.minimum,featureVoid.minimum,width);
caveVoid.maximum=SmoothMinMetres(caveVoid.maximum,featureVoid.maximum,width);
}
}
constexpr double HostRockMetres=-32.0;
FMetricInterval result{HostRockMetres,HostRockMetres,true};
if(haveFeature)
{
result.minimum=std::max(HostRockMetres,-caveVoid.maximum);
result.maximum=std::max(HostRockMetres,-caveVoid.minimum);
}
// Cover floating-point rounding in interval endpoints and the field's fixed-point
// coordinate conversion while keeping the interval tight compared with one micron.
constexpr double OutwardPaddingMetres=1.0e-6;
result.minimum=std::nextafter(result.minimum-OutwardPaddingMetres,
-std::numeric_limits<double>::infinity());
result.maximum=std::nextafter(result.maximum+OutwardPaddingMetres,
std::numeric_limits<double>::infinity());
result.known=std::isfinite(result.minimum)&&std::isfinite(result.maximum)
&&result.minimum<=result.maximum;
return result;
}
FBoxClassification ClassifyBoxDetailed(const FIntVector& Origin,int32 Step,int32 Cells,
bool bUseNativeLatticeProof=false) const
{
@@ -4946,8 +5302,11 @@ public:
const VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1* IslandPlan=nullptr;
const FSurfaceWorldFieldAdapter* SurfaceField=nullptr;
const FFloatingIslandFieldAdapter* IslandField=nullptr;
bool bSurfaceOverlap=false,bIslandOverlap=false,bOtherStrateOverlap=false;
const FBoundStratumField* CaveBound=nullptr;
bool bSurfaceOverlap=false,bIslandOverlap=false,bCavesOverlap=false;
bool bOtherStrateOverlap=false,bLegacyOverlap=false,bCaveOtherStrateOverlap=false;
bool bIslandOtherStrateOverlap=false,bBadPlan=false;
std::vector<std::uint32_t> caveCandidates;
if(Owner&&ResolvedLayout)
{
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
@@ -4978,14 +5337,28 @@ public:
IslandField=std::get_if<FFloatingIslandFieldAdapter>(
&Bound->Evaluator);
}
else if(Bound&&Bound->Kind==FBoundStratumField::EKind::Caves)
{
bCavesOverlap=true;bOtherStrateOverlap=true;
bIslandOtherStrateOverlap=true;
if(CaveBound&&CaveBound!=Bound)bBadPlan=true;
CaveBound=Bound;
if(!Bound->Resolved||Bound->Resolved->recipeId!="Core.Caves.PrimitiveField"
||Bound->Resolved->recipeVersion!=1
||!Bound->Resolved->passageCapabilities.hasNativeCaveField
||!std::get_if<FCavesFieldAdapter>(&Bound->Evaluator))bBadPlan=true;
}
else
{bOtherStrateOverlap=true;bIslandOtherStrateOverlap=true;}
{bOtherStrateOverlap=true;bIslandOtherStrateOverlap=true;bLegacyOverlap=true;}
}
bCaveOtherStrateOverlap=bCavesOverlap
&&(bSurfaceOverlap||bIslandOverlap||bLegacyOverlap);
}
const auto Finish=[&](EVoxelTileClass Verdict,bool bUnknown=false)
{
const bool bNative=(bSurfaceOverlap&&!bOtherStrateOverlap)
||(bIslandOverlap&&!bIslandOtherStrateOverlap);
||(bIslandOverlap&&!bIslandOtherStrateOverlap)
||(bCavesOverlap&&!bCaveOtherStrateOverlap);
FBoxClassification Result{Verdict,bNative,bUnknown};
auto& Counts=ThreadClassificationStats();
Counts.Milliseconds+=std::chrono::duration<double,std::milli>(Clock::now()-Start).count();
@@ -5020,7 +5393,8 @@ public:
static_cast<std::int64_t>(BMax[0])*Q,
static_cast<std::int64_t>(BMax[1])*Q,
static_cast<std::int64_t>(BMax[2])*Q};
if(!BuildCavernFeatureTileList(minimum,maximum).empty())
caveCandidates=BuildCavernFeatureTileList(minimum,maximum);
if(!caveCandidates.empty()&&!bCavesOverlap)
return Finish(EVoxelTileClass::Mixed,true);
}
if(ResolvedPassageIntersectsBox(BMin,BMax))
@@ -5050,7 +5424,10 @@ public:
&&BMin[1]>=NativeOnlyMinimum.Y&&BMin[2]>=NativeOnlyMinimum.Z
&&BMax[0]<=NativeOnlyMaximum.X&&BMax[1]<=NativeOnlyMaximum.Y
&&BMax[2]<=NativeOnlyMaximum.Z;
if(!bInsideProofBox||!bSurfaceOverlap||bIslandOverlap||bOtherStrateOverlap
const bool bSupportedNative=(bSurfaceOverlap&&!bOtherStrateOverlap)
||(bIslandOverlap&&!bIslandOtherStrateOverlap)
||(bCavesOverlap&&!bCaveOtherStrateOverlap);
if(!bInsideProofBox||!bSupportedNative
||bBadPlan||!EditBrushes.empty())
{
RecordNativeOnlyViolation();
@@ -5060,13 +5437,15 @@ public:
// A box crossing recipes cannot use one field interval; unknown composition stays Mixed.
// Une boite qui traverse plusieurs recettes reste Mixed, car l'intervalle d'un seul champ ne suffit pas.
if(bBadPlan||(bSurfaceOverlap&&(bIslandOverlap||bOtherStrateOverlap))
||(bIslandOverlap&&bIslandOtherStrateOverlap))
||(bIslandOverlap&&bIslandOtherStrateOverlap)
||(bCavesOverlap&&(bCaveOtherStrateOverlap||bSurfaceOverlap||bIslandOverlap)))
return Finish(EVoxelTileClass::Mixed,true);
// The native interval path below is the exact field selected first by DensityAtLattice
// for SurfaceWorld and FloatingIslands. Legacy analytic primitives/layer bounds cannot
// modify that field; only the explicit edit transaction above can. Keep their guards on
// the legacy classifier path, where those overlays do participate in density.
if(!bNativeOnly&&(!bUseNativeLatticeProof||(!bSurfaceOverlap&&!bIslandOverlap)))
if(!bNativeOnly&&!bCavesOverlap&&(!bUseNativeLatticeProof
||(!bSurfaceOverlap&&!bIslandOverlap)))
{
for(int32 BZ=FloorDiv(BMin[2],ActiveSpatialBinSize);BZ<=FloorDiv(BMax[2],ActiveSpatialBinSize);++BZ)
for(int32 BY=FloorDiv(BMin[1],ActiveSpatialBinSize);BY<=FloorDiv(BMax[1],ActiveSpatialBinSize);++BY)
@@ -5144,6 +5523,17 @@ public:
if(Interval.maximumDensitySubvoxels<0)return Finish(EVoxelTileClass::AllSolid);
return Finish(EVoxelTileClass::Mixed);
}
if(bCavesOverlap)
{
if(!CaveBound||!std::get_if<FCavesFieldAdapter>(&CaveBound->Evaluator))
return Finish(EVoxelTileClass::Mixed,true);
const auto interval=ClassifyNativeCaveFieldBox(BMin,BMax,
CaveBound->StrateIndex,caveCandidates);
if(!interval.known)return Finish(EVoxelTileClass::Mixed,true);
if(interval.minimum>0.0)return Finish(EVoxelTileClass::AllAir);
if(interval.maximum<0.0)return Finish(EVoxelTileClass::AllSolid);
return Finish(EVoxelTileClass::Mixed);
}
const bool bRock=EvaluateFast((BMin[0]+BMax[0])*0.5,
(BMin[1]+BMax[1])*0.5,(BMin[2]+BMax[2])*0.5);
return Finish(bRock?EVoxelTileClass::AllSolid:EVoxelTileClass::AllAir);
@@ -5662,7 +6052,13 @@ private:
if(!(LayoutWidth>0.0f))return false;
const int32 ChunkZ=FMath::FloorToInt(Z/LayoutWidth);
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
if(!Bound||Bound->Kind!=FBoundStratumField::EKind::SurfaceWorld
if(!Bound)return false;
if(Bound->Kind==FBoundStratumField::EKind::Caves)
{
const auto* Field=std::get_if<FCavesFieldAdapter>(&Bound->Evaluator);
return Field&&Field->densityAt(X,Y,Z,OutDensity);
}
if(Bound->Kind!=FBoundStratumField::EKind::SurfaceWorld
||!Bound->SurfacePlan||ChunkZ<Bound->SurfacePlan->bottomChunkZ
||ChunkZ>Bound->SurfacePlan->topChunkZ)return false;
const auto* Field=std::get_if<FSurfaceWorldFieldAdapter>(&Bound->Evaluator);
@@ -6194,6 +6590,11 @@ private:
if(scoped&&!tileQueries->hasCavernCandidates())return density;
const auto* bound=FindBoundStratumForManagerZ(Z);
if(!bound)return density;
if(bound->Kind==FBoundStratumField::EKind::Caves
&&bound->Resolved&&bound->Resolved->recipeId=="Core.Caves.PrimitiveField"
&&bound->Resolved->recipeVersion==1
&&bound->Resolved->passageCapabilities.hasNativeCaveField)
return ApplyResolvedNativeCavesField(density,X,Y,Z,*bound,tileQueries,scoped);
const double micronsPerVoxel=Owner->CoreVoxelSizeMicrons;
const std::array<double,3> worldMicrometres={X*micronsPerVoxel,
Y*micronsPerVoxel,Z*micronsPerVoxel};
@@ -6327,6 +6728,122 @@ private:
return composed;
}
static double SmoothMinMetres(double a,double b,double width)
{
if(!(width>0.0))return std::min(a,b);
const double h=std::max(width-std::abs(a-b),0.0)/width;
return std::min(a,b)-(h*h*h)*(width/6.0);
}
static double SmoothMaxMetres(double a,double b,double width)
{return -SmoothMinMetres(-a,-b,width);}
float ApplyResolvedNativeCavesField(float density,float X,float Y,float Z,
const FBoundStratumField& bound,const FScopedPassageTileQueries* tileQueries,
bool scoped) const
{
(void)density;
const double metresPerVoxel=Owner->CoreVoxelSizeMicrons/1000000.0;
const std::array<double,3> point={X*metresPerVoxel,Y*metresPerVoxel,
Z*metresPerVoxel};
std::vector<std::uint32_t> localCandidates;
const std::vector<std::uint32_t>* candidates=nullptr;
if(scoped)candidates=&tileQueries->cavernFeatureCandidates();
else
{
constexpr double Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
const std::array<std::int64_t,3> coordinate={
static_cast<std::int64_t>(std::llround(X*Q)),
static_cast<std::int64_t>(std::llround(Y*Q)),
static_cast<std::int64_t>(std::llround(Z*Q))};
localCandidates=BuildCavernFeatureTileList(coordinate,coordinate);
candidates=&localCandidates;
}
double caveVoid=std::numeric_limits<double>::infinity();
for(const std::uint32_t index:*candidates)
{
if(index>=CavernRuntime.size())continue;
const auto& feature=CavernRuntime[index];
const std::int32_t strateIndex=feature.room?feature.room->strateIndex
:feature.edge?feature.edge->strateIndex:-1;
if(strateIndex!=bound.StrateIndex)continue;
double featureVoid=std::numeric_limits<double>::infinity();
if(feature.room)
{
double normalizedSquared=0.0,meanRadius=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double radius=feature.halfExtentsMetres[axis];
if(!(radius>0.0)){normalizedSquared=std::numeric_limits<double>::infinity();break;}
const double delta=(point[axis]-feature.centerMetres[axis])/radius;
normalizedSquared+=delta*delta;meanRadius+=radius/3.0;
}
const double ellipsoid=(std::sqrt(normalizedSquared)-1.0)*meanRadius;
const double floor=feature.room->floorMicrometres/1000000.0-point[2];
const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
}
else if(feature.edge&&!feature.segments.empty())
{
double tube=std::numeric_limits<double>::infinity();
double nearestFloor=0.0,nearestGrade=0.0;
for(const auto& segment:feature.segments)
{
double lengthSquared=0.0,dot=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double delta=segment.end[axis]-segment.start[axis];
lengthSquared+=delta*delta;
dot+=(point[axis]-segment.start[axis])*delta;
}
const double t=lengthSquared>0.0
?std::clamp(dot/lengthSquared,0.0,1.0):0.0;
double radialSquared=0.0,vertical=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double delta=point[axis]-(segment.start[axis]
+t*(segment.end[axis]-segment.start[axis]));
if(axis<2)radialSquared+=delta*delta;
else vertical=delta;
}
const double radius=feature.edge->boreRadiusMicrometres/1000000.0;
const double height=feature.edge->boreHeightMicrometres/1000000.0;
if(!(radius>0.0&&height>0.0))continue;
const double normalized=std::sqrt(radialSquared/(radius*radius)
+vertical*vertical/(height*height));
const double distance=(normalized-1.0)*std::min(radius,height);
if(distance<tube)
{
tube=distance;
nearestFloor=segment.start[2]+t*(segment.end[2]-segment.start[2]);
const double horizontal=std::hypot(
segment.end[0]-segment.start[0],segment.end[1]-segment.start[1]);
nearestGrade=horizontal>1.0e-9
?(segment.end[2]-segment.start[2])/horizontal:0.0;
}
}
const double floor=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade);
const double width=feature.edge->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(tube,floor,width);
}
if(!std::isfinite(featureVoid))continue;
if(!std::isfinite(caveVoid))caveVoid=featureVoid;
else caveVoid=SmoothMinMetres(caveVoid,featureVoid,
feature.room?feature.room->smoothWidthMicrometres/1000000.0
:feature.edge->smoothWidthMicrometres/1000000.0);
}
// A versioned native caves recipe owns a solid host-rock field. The
// resolved interval oracle uses the same finite -32 m solid value; using
// the underlying legacy generator density here would leak its unrelated
// terrain detail into the cave floor and invalidate those proofs.
constexpr double HostRockMetres=-32.0;
const double hostDistance=HostRockMetres;
const double composed=std::isfinite(caveVoid)
?SmoothMaxMetres(hostDistance,-caveVoid,0.0):hostDistance;
return static_cast<float>(composed/metresPerVoxel);
}
float ApplyResolvedCavernFloorCaps(float density,float X,float Y,float Z) const
{
if(CavernRuntime.empty()||!Owner||Owner->CoreVoxelSizeMicrons==0)return density;
@@ -9725,6 +10242,8 @@ private:
std::vector<std::uint32_t> OreVeinSpatialOrder;
std::vector<FOreVeinRuntime> OreVeinRuntime;
std::vector<FCavernRuntimeFeature> CavernRuntime;
std::vector<FCavernSpatialNode> CavernSpatialNodes;
std::vector<std::uint32_t> CavernSpatialOrder;
std::vector<VoxelForgeCore::WorldAssemblyFeatureBoundsV1> PassageFeatureBounds;
std::vector<FPassageTileFeature> PassageTileFeatures;
std::vector<std::uint32_t> PassageFeatureOrder;