skip: passage proof bounds include wall/floor relief (L75 soundness fix); FI -7.6%
L74's passage bound omitted the 0.21 m wall relief and 0.20 m floor relief, so its whole-tile
proof was unsound in principle (no tested view hit it). Passage support is now bounded per route
segment by four parameter-subinterval boxes using Catmull-Rom derivative extrema, local max
radius and relief padding. Stratum slicing only at LOD0-2 in FI/VS. Legacy sub-tile block
certificates are audit-only (runtime version was slower). TN regression (-80,-20,-3218) kept.
Exact full-grid vs skip hashes on every tile, 4 views, Clang/GCC, W1/W8/W12; control caught;
80-camera sweep 0 violations. Whole view vs c67b200: SW -1.3%, FI -7.6%, TN -2.3%, VS +0.2%.
Compiles in the Linux UE host (VFHostEditor, 27/27 actions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
c67b2006aa
commit
9d5455ec68
16 files changed
+1335
-59
No files matched your search
@@ -1570,8 +1570,29 @@ public:
|
||||
*floorAlignment*floorAlignment*floorBelowWeight*mouthWalkLaneWeight;
|
||||
const float fullyCarvedDensity=std::max(composed,
|
||||
static_cast<float>(-roughSignedDistance/Q));
|
||||
static const bool bL75TraceTnPassage=[]
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L75_TN_BLOCK_REGRESSION");
|
||||
return Value&&Value[0]=='1'&&Value[1]=='\0';
|
||||
}();
|
||||
const float passageInput=composed;
|
||||
composed=std::lerp(composed,fullyCarvedDensity,
|
||||
static_cast<float>(floorCarveWeight));
|
||||
if(bL75TraceTnPassage&&X==-80.0f&&Y==-20.0f&&Z==-3218.0f
|
||||
&&composed!=passageInput)
|
||||
{
|
||||
const auto& traceA=passage.controlPoints[static_cast<std::size_t>(bestIndex)];
|
||||
const auto& traceB=passage.controlPoints[static_cast<std::size_t>(bestIndex+1)];
|
||||
std::printf("[L75TNPassageTrace] passage=%zu segment=%td t=%.9g input=%.9g output=%.9g signed=%.9g rough=%.9g carve_weight=%.9g bounds=(%lld,%lld,%lld)..(%lld,%lld,%lld)\n",
|
||||
passageIndex,bestIndex,bestT,passageInput,composed,bestSignedDistance,
|
||||
roughSignedDistance,floorCarveWeight,
|
||||
static_cast<long long>(std::min(traceA.positionSubvoxels[0],traceB.positionSubvoxels[0])),
|
||||
static_cast<long long>(std::min(traceA.positionSubvoxels[1],traceB.positionSubvoxels[1])),
|
||||
static_cast<long long>(std::min(traceA.positionSubvoxels[2],traceB.positionSubvoxels[2])),
|
||||
static_cast<long long>(std::max(traceA.positionSubvoxels[0],traceB.positionSubvoxels[0])),
|
||||
static_cast<long long>(std::max(traceA.positionSubvoxels[1],traceB.positionSubvoxels[1])),
|
||||
static_cast<long long>(std::max(traceA.positionSubvoxels[2],traceB.positionSubvoxels[2])));
|
||||
}
|
||||
const float composedAfterCarve=composed;
|
||||
const auto considerSupportSegment=[&](std::ptrdiff_t index)
|
||||
{
|
||||
@@ -1999,15 +2020,31 @@ public:
|
||||
const float composed=nativeLattice?boundary
|
||||
:ApplyResolvedStratePassageCarves(boundary,X,Y,Z);
|
||||
const float stamped=StampRuntime.empty()?composed:ApplyResolvedSdfStamps(composed,X,Y,Z);
|
||||
static const bool bL75TraceTnComposition=[]
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L75_TN_BLOCK_REGRESSION");
|
||||
return Value&&Value[0]=='1'&&Value[1]=='\0';
|
||||
}();
|
||||
const bool bTraceTnComposition=bL75TraceTnComposition
|
||||
&&X==-80.0f&&Y==-20.0f&&Z==-3218.0f;
|
||||
if(bound&&bound->Kind==FBoundStratumField::EKind::Caves
|
||||
&&bound->Resolved&&VoxelForgeCore::HasNativeGraphField(*bound->Resolved)
|
||||
&&(VoxelForgeCore::IsNativeCaveFieldStratum(*bound->Resolved)
|
||||
?bound->Resolved->passageCapabilities.hasNativeCaveField
|
||||
:bound->Resolved->passageCapabilities.hasNativeLatticeField))
|
||||
{
|
||||
if(bTraceTnComposition)
|
||||
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g native=1\n",
|
||||
density,graphDensity,boundary,composed,stamped);
|
||||
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);
|
||||
const float final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z);
|
||||
if(bTraceTnComposition)
|
||||
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g final=%.9g native=0\n",
|
||||
density,graphDensity,boundary,composed,stamped,final);
|
||||
return final;
|
||||
}
|
||||
|
||||
bool ResolvedPassageIntersectsBox(const int32 minimum[3],const int32 maximum[3]) const
|
||||
@@ -2157,6 +2194,14 @@ public:
|
||||
bool bNativeSurface=false;
|
||||
bool bUnknown=false;
|
||||
bool bSkipSafe=false;
|
||||
bool bLegacyFieldCandidate=false;
|
||||
bool bLegacyBlockProofRelevant=false;
|
||||
std::uint8_t LegacyProofVeto=0;
|
||||
};
|
||||
enum class ELegacyProofVeto : std::uint8_t
|
||||
{
|
||||
None=0,DetailEnvelope=1,ComposerCandidate=2,PassageBounds=3,
|
||||
PrimitiveOverlap=4,Transition=5,StratumCrossing=6,SmoothMinSlack=7,Other=8
|
||||
};
|
||||
struct FClassificationThreadStats
|
||||
{
|
||||
@@ -2497,6 +2542,7 @@ public:
|
||||
WorkerCount=std::clamp(Workers,1,16);DetailVariant=Variant;
|
||||
Scheduler=InScheduler;RoundTunnelsEnabled=bInRoundTunnels;
|
||||
bUseNativeManifestFields=true;
|
||||
UnbinnedSpatialPrimitiveIds.clear();
|
||||
BindResolvedStratumFields();
|
||||
bAnalyticOnly=false;
|
||||
bProfileDetailBand=bInProfileDetailBand;
|
||||
@@ -3323,6 +3369,44 @@ public:
|
||||
return LegacySurfaceDensityAt(static_cast<float>(X),static_cast<float>(Y),
|
||||
static_cast<float>(Z));
|
||||
const bool bPacked=!bAnalyticOnly&&Contains(X,Y,Z);
|
||||
static const bool bL75TraceTnDensity=[]
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L75_TN_BLOCK_REGRESSION");
|
||||
return Value&&Value[0]=='1'&&Value[1]=='\0';
|
||||
}();
|
||||
if(bL75TraceTnDensity&&X==-80&&Y==-20&&Z==-3218)
|
||||
{
|
||||
if(!bPacked)
|
||||
std::printf("[L75TNDensityTrace] packed=0\n");
|
||||
else
|
||||
{
|
||||
const size_t Column=static_cast<size_t>(Y-Min[1])*Size[0]
|
||||
+static_cast<size_t>(X-Min[0]);
|
||||
const size_t Zi=static_cast<size_t>(Z-Min[2]);
|
||||
const size_t Word=Column*WordsPerZ+(Zi>>6);
|
||||
const uint64 Mask=uint64{1}<<(Zi&63u);
|
||||
const bool bRock=(RockWords[Word]&Mask)!=0;
|
||||
const bool bCandidate=(CandidateWords[Word]&Mask)!=0;
|
||||
const float Analytic=AnalyticDensityAt(static_cast<float>(X),
|
||||
static_cast<float>(Y),static_cast<float>(Z),false);
|
||||
std::printf("[L75TNDensityTrace] packed=%d rock=%d candidate=%d proxy=%d analytic=%.9g\n",
|
||||
bPacked?1:0,bRock?1:0,bCandidate?1:0,
|
||||
bCandidate?static_cast<int>(SurfaceDistanceQ[Zi*static_cast<size_t>(Size[1])*Size[0]
|
||||
+Column]):0,Analytic);
|
||||
for(const uint32 Id:GetLineCandidates(2,X,Y))
|
||||
{
|
||||
if(Id>=Primitives.size())continue;
|
||||
const FPrimitive& Primitive=Primitives[Id];
|
||||
if(!Primitive.bCarve||X<Primitive.Min[0]||X>Primitive.Max[0]
|
||||
||Y<Primitive.Min[1]||Y>Primitive.Max[1]
|
||||
||Z<Primitive.Min[2]||Z>Primitive.Max[2])continue;
|
||||
std::printf("[L75TNDensityTrace] primitive=%u carve=1 critical=%d bounds=(%d,%d,%d)..(%d,%d,%d) contains=%d sdf=%.9g\n",
|
||||
Id,Primitive.bCritical?1:0,Primitive.Min[0],Primitive.Min[1],Primitive.Min[2],
|
||||
Primitive.Max[0],Primitive.Max[1],Primitive.Max[2],
|
||||
ShapeContains(Primitive,X,Y,Z)?1:0,ShapeDistance(Primitive,X,Y,Z));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bPacked)
|
||||
return PackedDensityAt(X,Y,Z,IsRock(X,Y,Z),static_cast<float>(X),
|
||||
static_cast<float>(Y),static_cast<float>(Z));
|
||||
@@ -4487,6 +4571,10 @@ private:
|
||||
const double qPerMetre=Q*voxelsPerMetre;
|
||||
const double bridgeHalfWidthVoxels=3.5*voxelsPerMetre;
|
||||
const double bridgeFootDepthVoxels=(5.0-0.13)*voxelsPerMetre;
|
||||
// PassageWallRelief is bounded by 0.21 m along the route. Mouth floor
|
||||
// relief is bounded by 0.20 m and only applies in the upper entry run.
|
||||
const double passageWallReliefVoxels=0.21*voxelsPerMetre;
|
||||
const double passageMouthFloorReliefVoxels=0.20*voxelsPerMetre;
|
||||
PassageColumnSegmentFlags.resize(ResolvedLayout->passages.size());
|
||||
PassageShortSpanSegmentFlags.resize(ResolvedLayout->passages.size());
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
|
||||
@@ -4507,24 +4595,119 @@ private:
|
||||
const auto& a=passage.controlPoints[segment];
|
||||
const auto& b=passage.controlPoints[segment+1];
|
||||
const double radius=static_cast<double>(std::max(a.radiusSubvoxels,b.radiusSubvoxels))/Q;
|
||||
const double horizontalExtent=std::max(radius,bridgeHalfWidthVoxels);
|
||||
const std::ptrdiff_t lastControl=static_cast<std::ptrdiff_t>(
|
||||
passage.controlPoints.size())-1;
|
||||
const std::ptrdiff_t lowerMouthControl=std::clamp<std::ptrdiff_t>(
|
||||
static_cast<std::ptrdiff_t>(passage.lowerMouthControlIndex),1,lastControl);
|
||||
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 bool bMayUseSmoothCarve=segment>2
|
||||
&&static_cast<std::ptrdiff_t>(segment)+2<lowerMouthControl
|
||||
&&DeterministicSqrt(dx*dx+dy*dy+dz*dz)<=8.0*qPerMetre;
|
||||
const double mouthFloorReliefVoxels=segment<static_cast<std::size_t>(
|
||||
std::min<std::ptrdiff_t>(3,lowerMouthControl))
|
||||
?passageMouthFloorReliefVoxels:0.0;
|
||||
constexpr int32 ProofSubdivisions=4;
|
||||
constexpr double OutwardPaddingVoxels=1.0e-6;
|
||||
const double footDepth=std::max(voxelsPerMetre,bridgeFootDepthVoxels);
|
||||
FPassageProofBound proof;
|
||||
proof.minimum={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};
|
||||
proof.maximum={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};
|
||||
PassageProofBounds.push_back(proof);
|
||||
std::array<double,3> featureMinimum={
|
||||
std::numeric_limits<double>::infinity(),
|
||||
std::numeric_limits<double>::infinity(),
|
||||
std::numeric_limits<double>::infinity()};
|
||||
std::array<double,3> featureMaximum={
|
||||
-std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity()};
|
||||
const auto& p0=bMayUseSmoothCarve
|
||||
?passage.controlPoints[segment-1]:a;
|
||||
const auto& p3=bMayUseSmoothCarve
|
||||
?passage.controlPoints[segment+2]:b;
|
||||
for(int32 proofPart=0;proofPart<ProofSubdivisions;++proofPart)
|
||||
{
|
||||
const double t0=static_cast<double>(proofPart)/ProofSubdivisions;
|
||||
const double t1=static_cast<double>(proofPart+1)/ProofSubdivisions;
|
||||
std::array<double,3> centerMinimum{},centerMaximum{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const double v0=static_cast<double>(p0.positionSubvoxels[axis])/Q;
|
||||
const double v1=static_cast<double>(a.positionSubvoxels[axis])/Q;
|
||||
const double v2=static_cast<double>(b.positionSubvoxels[axis])/Q;
|
||||
const double v3=static_cast<double>(p3.positionSubvoxels[axis])/Q;
|
||||
const double c0=v1;
|
||||
const double c1=0.5*(-v0+v2);
|
||||
const double c2=0.5*(2.0*v0-5.0*v1+4.0*v2-v3);
|
||||
const double c3=0.5*(-v0+3.0*v1-3.0*v2+v3);
|
||||
const auto evaluate=[&](double t)
|
||||
{
|
||||
return bMayUseSmoothCarve
|
||||
?((c3*t+c2)*t+c1)*t+c0:v1+t*(v2-v1);
|
||||
};
|
||||
double low=std::min(evaluate(t0),evaluate(t1));
|
||||
double high=std::max(evaluate(t0),evaluate(t1));
|
||||
const double qa=3.0*c3,qb=2.0*c2,qc=c1;
|
||||
const auto includeRoot=[&](double t)
|
||||
{
|
||||
if(t<=t0||t>=t1||!std::isfinite(t))return;
|
||||
const double value=evaluate(t);
|
||||
low=std::min(low,value);high=std::max(high,value);
|
||||
};
|
||||
if(bMayUseSmoothCarve)
|
||||
{
|
||||
if(std::abs(qa)<=1.0e-12)
|
||||
{
|
||||
if(std::abs(qb)>1.0e-12)includeRoot(-qc/qb);
|
||||
}
|
||||
else
|
||||
{
|
||||
const double discriminant=qb*qb-4.0*qa*qc;
|
||||
if(discriminant>=0.0)
|
||||
{
|
||||
const double root=std::sqrt(discriminant);
|
||||
includeRoot((-qb-root)/(2.0*qa));
|
||||
includeRoot((-qb+root)/(2.0*qa));
|
||||
}
|
||||
}
|
||||
}
|
||||
centerMinimum[axis]=low-OutwardPaddingVoxels;
|
||||
centerMaximum[axis]=high+OutwardPaddingVoxels;
|
||||
}
|
||||
const double radiusAt0=(static_cast<double>(a.radiusSubvoxels)
|
||||
+t0*(static_cast<double>(b.radiusSubvoxels)-a.radiusSubvoxels))/Q;
|
||||
const double radiusAt1=(static_cast<double>(a.radiusSubvoxels)
|
||||
+t1*(static_cast<double>(b.radiusSubvoxels)-a.radiusSubvoxels))/Q;
|
||||
const double partRadius=std::max(radiusAt0,radiusAt1);
|
||||
const double relievedRadius=partRadius+passageWallReliefVoxels
|
||||
+mouthFloorReliefVoxels+OutwardPaddingVoxels;
|
||||
const double horizontalExtent=std::max(relievedRadius,
|
||||
bridgeHalfWidthVoxels+OutwardPaddingVoxels);
|
||||
FPassageProofBound proof;
|
||||
proof.minimum={centerMinimum[0]-horizontalExtent,
|
||||
centerMinimum[1]-horizontalExtent,
|
||||
centerMinimum[2]-partRadius-footDepth-passageWallReliefVoxels
|
||||
-mouthFloorReliefVoxels-OutwardPaddingVoxels};
|
||||
proof.maximum={centerMaximum[0]+horizontalExtent,
|
||||
centerMaximum[1]+horizontalExtent,
|
||||
centerMaximum[2]+partRadius+passageWallReliefVoxels
|
||||
+mouthFloorReliefVoxels+OutwardPaddingVoxels};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
featureMinimum[axis]=std::min(featureMinimum[axis],proof.minimum[axis]);
|
||||
featureMaximum[axis]=std::max(featureMaximum[axis],proof.maximum[axis]);
|
||||
}
|
||||
PassageProofBounds.push_back(proof);
|
||||
}
|
||||
const std::array<std::int64_t,3> featureMin={
|
||||
static_cast<std::int64_t>(std::floor(proof.minimum[0]*Q)),
|
||||
static_cast<std::int64_t>(std::floor(proof.minimum[1]*Q)),
|
||||
static_cast<std::int64_t>(std::floor(proof.minimum[2]*Q))};
|
||||
static_cast<std::int64_t>(std::floor(featureMinimum[0]*Q)),
|
||||
static_cast<std::int64_t>(std::floor(featureMinimum[1]*Q)),
|
||||
static_cast<std::int64_t>(std::floor(featureMinimum[2]*Q))};
|
||||
const std::array<std::int64_t,3> featureMax={
|
||||
static_cast<std::int64_t>(std::ceil(proof.maximum[0]*Q)),
|
||||
static_cast<std::int64_t>(std::ceil(proof.maximum[1]*Q)),
|
||||
static_cast<std::int64_t>(std::ceil(proof.maximum[2]*Q))};
|
||||
static_cast<std::int64_t>(std::ceil(featureMaximum[0]*Q)),
|
||||
static_cast<std::int64_t>(std::ceil(featureMaximum[1]*Q)),
|
||||
static_cast<std::int64_t>(std::ceil(featureMaximum[2]*Q))};
|
||||
PassageFeatureBounds.push_back({static_cast<std::uint64_t>(PassageTileFeatures.size()),
|
||||
featureMin,featureMax});
|
||||
PassageTileFeatures.push_back({passageIndex,segment,false});
|
||||
@@ -5965,8 +6148,22 @@ public:
|
||||
{
|
||||
if(SharedDensitySource)
|
||||
return SharedDensitySource->ClassifyBoxDetailed(Origin,Step,Cells,bUseNativeLatticeProof);
|
||||
const int32 Pad=std::max(Step,1);
|
||||
const int32 BMin[3]={Origin.X-Pad,Origin.Y-Pad,Origin.Z-Pad};
|
||||
const int32 BMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,
|
||||
Origin.Z+(Cells+1)*Step};
|
||||
return ClassifyBoundsDetailed(BMin,BMax,std::max(Step,1),bUseNativeLatticeProof);
|
||||
}
|
||||
|
||||
FBoxClassification ClassifyBoundsDetailed(const int32 InputMin[3],
|
||||
const int32 InputMax[3],int32 SampleStep,bool bUseNativeLatticeProof=false) const
|
||||
{
|
||||
if(SharedDensitySource)
|
||||
return SharedDensitySource->ClassifyBoundsDetailed(
|
||||
InputMin,InputMax,SampleStep,bUseNativeLatticeProof);
|
||||
using Clock=std::chrono::steady_clock;
|
||||
const auto Start=Clock::now();
|
||||
std::uint8_t LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::None);
|
||||
static const bool bL74TightNegativeControl=[]
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L74_TIGHT_PROOF_CONTROL");
|
||||
@@ -5975,9 +6172,21 @@ public:
|
||||
std::printf("[L74NegativeControl] enabled=1 bound=base-sign-only-detail-and-primitive-envelope-omitted\n");
|
||||
return Enabled;
|
||||
}();
|
||||
const int32 Pad=std::max(Step,1);
|
||||
int32 BMin[3]={Origin.X-Pad,Origin.Y-Pad,Origin.Z-Pad};
|
||||
int32 BMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,Origin.Z+(Cells+1)*Step};
|
||||
const int32 BMin[3]={InputMin[0],InputMin[1],InputMin[2]};
|
||||
const int32 BMax[3]={InputMax[0],InputMax[1],InputMax[2]};
|
||||
static const bool bL75TraceTnBlock=[]
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L75_TN_BLOCK_REGRESSION");
|
||||
return Value&&Value[0]=='1'&&Value[1]=='\0';
|
||||
}();
|
||||
constexpr int32 ProbeX=-80,ProbeY=-20,ProbeZ=-3218;
|
||||
const bool bTraceTnBlock=bL75TraceTnBlock
|
||||
&&ProbeX>=BMin[0]&&ProbeX<=BMax[0]
|
||||
&&ProbeY>=BMin[1]&&ProbeY<=BMax[1]
|
||||
&&ProbeZ>=BMin[2]&&ProbeZ<=BMax[2];
|
||||
if(bTraceTnBlock)
|
||||
std::printf("[L75TNProofTrace] phase=enter step=%d bounds=(%d,%d,%d)..(%d,%d,%d)\n",
|
||||
SampleStep,BMin[0],BMin[1],BMin[2],BMax[0],BMax[1],BMax[2]);
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1* SurfacePlan=nullptr;
|
||||
const VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1* IslandPlan=nullptr;
|
||||
const FSurfaceWorldFieldAdapter* SurfaceField=nullptr;
|
||||
@@ -5986,6 +6195,11 @@ public:
|
||||
bool bSurfaceOverlap=false,bIslandOverlap=false,bCavesOverlap=false;
|
||||
bool bOtherStrateOverlap=false,bLegacyOverlap=false,bCaveOtherStrateOverlap=false;
|
||||
bool bIslandOtherStrateOverlap=false,bBadPlan=false;
|
||||
bool bMissingStratumBound=false;
|
||||
bool bStratumSlicingExpectedToPay=false;
|
||||
bool bLegacyFieldCandidate=WorldAssembly&&Owner&&Owner->Manager
|
||||
&&ResolvedLayout&&Owner->Generator;
|
||||
bool bLegacyBlockProofRelevant=false;
|
||||
std::vector<std::uint32_t> caveCandidates;
|
||||
if(Owner&&ResolvedLayout)
|
||||
{
|
||||
@@ -5995,8 +6209,13 @@ public:
|
||||
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
|
||||
{
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
|
||||
if(Bound&&Bound->Resolved
|
||||
&&(Bound->Resolved->generatorKind=="FloatingIslands"
|
||||
||Bound->Resolved->generatorKind=="VerticalShafts"))
|
||||
bStratumSlicingExpectedToPay=true;
|
||||
if(Bound&&Bound->Kind==FBoundStratumField::EKind::SurfaceWorld)
|
||||
{
|
||||
bLegacyFieldCandidate=false;
|
||||
bSurfaceOverlap=true;bIslandOtherStrateOverlap=true;
|
||||
const auto* Candidate=Bound->SurfacePlan;
|
||||
if(!Candidate||ChunkZ<Candidate->bottomChunkZ
|
||||
@@ -6016,9 +6235,14 @@ public:
|
||||
IslandPlan=Candidate;
|
||||
IslandField=std::get_if<FFloatingIslandFieldAdapter>(
|
||||
&Bound->Evaluator);
|
||||
if(Bound->IslandPlan)bLegacyFieldCandidate=false;
|
||||
else if(Bound->Resolved
|
||||
&&Bound->Resolved->generatorKind=="TunnelNetwork")
|
||||
bLegacyBlockProofRelevant=true;
|
||||
}
|
||||
else if(Bound&&Bound->Kind==FBoundStratumField::EKind::Caves)
|
||||
{
|
||||
bLegacyFieldCandidate=false;
|
||||
bCavesOverlap=true;bOtherStrateOverlap=true;
|
||||
bIslandOtherStrateOverlap=true;
|
||||
if(CaveBound&&CaveBound!=Bound)bBadPlan=true;
|
||||
@@ -6030,11 +6254,21 @@ public:
|
||||
||!std::get_if<FCavesFieldAdapter>(&Bound->Evaluator))bBadPlan=true;
|
||||
}
|
||||
else
|
||||
{bOtherStrateOverlap=true;bIslandOtherStrateOverlap=true;bLegacyOverlap=true;}
|
||||
{
|
||||
if(!Bound){bLegacyFieldCandidate=false;bMissingStratumBound=true;}
|
||||
else if(Bound->Resolved
|
||||
&&Bound->Resolved->generatorKind=="TunnelNetwork")
|
||||
bLegacyBlockProofRelevant=true;
|
||||
bOtherStrateOverlap=true;bIslandOtherStrateOverlap=true;bLegacyOverlap=true;
|
||||
}
|
||||
}
|
||||
bCaveOtherStrateOverlap=bCavesOverlap
|
||||
&&(bSurfaceOverlap||bIslandOverlap||bLegacyOverlap);
|
||||
}
|
||||
if(bTraceTnBlock)
|
||||
std::printf("[L75TNProofTrace] phase=field_bounds legacy=%d island=%d surface=%d caves=%d missing=%d bad_plan=%d\n",
|
||||
bLegacyFieldCandidate?1:0,bIslandOverlap?1:0,bSurfaceOverlap?1:0,
|
||||
bCavesOverlap?1:0,bMissingStratumBound?1:0,bBadPlan?1:0);
|
||||
const auto Finish=[&](EVoxelTileClass Verdict,bool bUnknown=false,
|
||||
bool bLegacyAnalyticProof=false)
|
||||
{
|
||||
@@ -6045,6 +6279,10 @@ public:
|
||||
||Verdict==EVoxelTileClass::AllSolid;
|
||||
FBoxClassification Result{Verdict,bNative,bUnknown,
|
||||
bLegacyAnalyticProof||(bNative&&!bUnknown&&bUniform)};
|
||||
Result.bLegacyFieldCandidate=bLegacyFieldCandidate;
|
||||
Result.bLegacyBlockProofRelevant=bLegacyBlockProofRelevant;
|
||||
Result.LegacyProofVeto=Result.bSkipSafe
|
||||
?static_cast<std::uint8_t>(ELegacyProofVeto::None):LegacyProofVeto;
|
||||
auto& Counts=ThreadClassificationStats();
|
||||
Counts.Milliseconds+=std::chrono::duration<double,std::milli>(Clock::now()-Start).count();
|
||||
if(bUnknown)++Counts.UnknownBoxes;
|
||||
@@ -6053,6 +6291,73 @@ public:
|
||||
else ++Counts.MixedBoxes;
|
||||
return Result;
|
||||
};
|
||||
if(SampleStep<=2&&bStratumSlicingExpectedToPay&&Owner&&ResolvedLayout
|
||||
&&SampleStep>0&&BMax[2]>=BMin[2]
|
||||
&&(static_cast<std::int64_t>(BMax[2])-BMin[2])%SampleStep==0)
|
||||
{
|
||||
const double LayoutWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
if(LayoutWidth>0.0&&std::isfinite(LayoutWidth))
|
||||
{
|
||||
std::vector<std::pair<int32,int32>> StratumSlices;
|
||||
int32 SliceStart=BMin[2];
|
||||
auto BoundAt=[&](int32 Z)
|
||||
{
|
||||
const int32 ChunkZ=FMath::FloorToInt(static_cast<float>(Z)/LayoutWidth);
|
||||
return FindBoundStratumForChunk(ChunkZ);
|
||||
};
|
||||
const FBoundStratumField* PreviousBound=BoundAt(SliceStart);
|
||||
for(std::int64_t Z=static_cast<std::int64_t>(BMin[2])+SampleStep;
|
||||
Z<=BMax[2];Z+=SampleStep)
|
||||
{
|
||||
const auto* Bound=BoundAt(static_cast<int32>(Z));
|
||||
if(Bound==PreviousBound)continue;
|
||||
StratumSlices.push_back({SliceStart,static_cast<int32>(Z-SampleStep)});
|
||||
SliceStart=static_cast<int32>(Z);
|
||||
PreviousBound=Bound;
|
||||
}
|
||||
if(!StratumSlices.empty())
|
||||
{
|
||||
StratumSlices.push_back({SliceStart,BMax[2]});
|
||||
EVoxelTileClass CommonVerdict=EVoxelTileClass::Mixed;
|
||||
bool bHaveVerdict=false,bAllNative=true,bAllLegacy=true,bAnyLegacy=false;
|
||||
for(const auto& Slice:StratumSlices)
|
||||
{
|
||||
const int32 SliceMinimum[3]={BMin[0],BMin[1],Slice.first};
|
||||
const int32 SliceMaximum[3]={BMax[0],BMax[1],Slice.second};
|
||||
const auto Part=ClassifyBoundsDetailed(SliceMinimum,SliceMaximum,
|
||||
SampleStep,bUseNativeLatticeProof);
|
||||
if(!Part.bSkipSafe||Part.bUnknown
|
||||
||(Part.Verdict!=EVoxelTileClass::AllSolid
|
||||
&&Part.Verdict!=EVoxelTileClass::AllAir))
|
||||
{
|
||||
LegacyProofVeto=Part.LegacyProofVeto
|
||||
?Part.LegacyProofVeto
|
||||
:static_cast<std::uint8_t>(ELegacyProofVeto::StratumCrossing);
|
||||
return Finish(EVoxelTileClass::Mixed,Part.bUnknown);
|
||||
}
|
||||
if(!bHaveVerdict)
|
||||
{
|
||||
CommonVerdict=Part.Verdict;
|
||||
bHaveVerdict=true;
|
||||
}
|
||||
else if(Part.Verdict!=CommonVerdict)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(
|
||||
ELegacyProofVeto::StratumCrossing);
|
||||
return Finish(EVoxelTileClass::Mixed);
|
||||
}
|
||||
bAllNative=bAllNative&&Part.bNativeSurface;
|
||||
bAllLegacy=bAllLegacy&&Part.bLegacyFieldCandidate;
|
||||
bAnyLegacy=bAnyLegacy||Part.bLegacyBlockProofRelevant;
|
||||
}
|
||||
auto Result=Finish(CommonVerdict,false,true);
|
||||
Result.bNativeSurface=bAllNative;
|
||||
Result.bLegacyFieldCandidate=bAllLegacy;
|
||||
Result.bLegacyBlockProofRelevant=bAnyLegacy;
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<std::uint32_t> stampCandidates;
|
||||
if(!StampSpatialBounds.empty())
|
||||
{
|
||||
@@ -6075,7 +6380,10 @@ public:
|
||||
&&stamp.record->kind
|
||||
!=VoxelForgeCore::ResolvedSdfStampKindV1::fillPlatform)
|
||||
||!SurfacePlan||stamp.record->strateIndex!=SurfacePlan->strateIndex)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::ComposerCandidate);
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!CavernRuntime.empty())
|
||||
@@ -6091,10 +6399,19 @@ public:
|
||||
static_cast<std::int64_t>(BMax[2])*Q};
|
||||
caveCandidates=BuildCavernFeatureTileList(minimum,maximum);
|
||||
if(!caveCandidates.empty()&&!bCavesOverlap&&!bL74TightNegativeControl)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::ComposerCandidate);
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
}
|
||||
}
|
||||
if(ResolvedPassageIntersectsBox(BMin,BMax))
|
||||
const bool bPassageOverlap=ResolvedPassageIntersectsBox(BMin,BMax);
|
||||
if(bTraceTnBlock)
|
||||
std::printf("[L75TNProofTrace] phase=passage intersects=%d\n",bPassageOverlap?1:0);
|
||||
if(bPassageOverlap)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::PassageBounds);
|
||||
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])
|
||||
@@ -6111,6 +6428,7 @@ public:
|
||||
if(ZMinMicrometres>=Band->bottomMicrometres
|
||||
&&ZMaxMicrometres<Band->topMicrometres)
|
||||
return Finish(EVoxelTileClass::AllSolid);
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::StratumCrossing);
|
||||
return Finish(EVoxelTileClass::Mixed);
|
||||
}
|
||||
}
|
||||
@@ -6151,7 +6469,10 @@ public:
|
||||
Lower->ceiling.topMicrometres)+MaximumDisplacement;
|
||||
if(ZMaximumMicrometres>=WindowMinimum
|
||||
&&ZMinimumMicrometres<=WindowMaximum)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::Transition);
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto TryLegacyAnalyticProof=[&]() -> std::optional<EVoxelTileClass>
|
||||
@@ -6161,28 +6482,45 @@ public:
|
||||
// terms. SurfaceWorld op stacks, native adapters, and any active composer
|
||||
// feature fail closed and keep using their existing path.
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager
|
||||
||!ResolvedLayout||!Owner->Generator||bSurfaceOverlap||bCavesOverlap
|
||||
||(!bL74TightNegativeControl
|
||||
&&(!stampCandidates.empty()||!caveCandidates.empty()))
|
||||
||!EditBrushes.empty()||LayerRanges.empty()
|
||||
||ActiveSpatialBinSize<=0)return std::nullopt;
|
||||
const double LayoutWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
if(!(LayoutWidth>0.0)||!std::isfinite(LayoutWidth))return std::nullopt;
|
||||
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin[2])/LayoutWidth);
|
||||
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax[2])/LayoutWidth);
|
||||
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
|
||||
||!ResolvedLayout||!Owner->Generator)
|
||||
{
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
|
||||
if(!Bound||Bound->Kind==FBoundStratumField::EKind::SurfaceWorld)
|
||||
return std::nullopt;
|
||||
if(Bound->Kind==FBoundStratumField::EKind::FloatingIslands)
|
||||
{
|
||||
// A missing FI plan is the owner's compiled-analytic fallback. Any
|
||||
// installed plan selects the native FI field and is outside this proof.
|
||||
if(Bound->IslandPlan)return std::nullopt;
|
||||
}
|
||||
if(Bound->Kind==FBoundStratumField::EKind::Caves)return std::nullopt;
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::Other);
|
||||
return std::nullopt;
|
||||
}
|
||||
if(!stampCandidates.empty()||!caveCandidates.empty())
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::ComposerCandidate);
|
||||
if(!bL74TightNegativeControl)return std::nullopt;
|
||||
}
|
||||
if(bSurfaceOverlap||bCavesOverlap)
|
||||
{
|
||||
const bool bCrossedFieldKinds=(bSurfaceOverlap
|
||||
&&(bIslandOverlap||bCavesOverlap||bLegacyOverlap))
|
||||
||(bIslandOverlap&&(bSurfaceOverlap||bCavesOverlap||bLegacyOverlap))
|
||||
||(bCavesOverlap&&(bSurfaceOverlap||bIslandOverlap||bLegacyOverlap));
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(bCrossedFieldKinds||bBadPlan
|
||||
?ELegacyProofVeto::StratumCrossing:ELegacyProofVeto::Other);
|
||||
return std::nullopt;
|
||||
}
|
||||
if(!bLegacyFieldCandidate)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(bBadPlan||bMissingStratumBound
|
||||
?ELegacyProofVeto::StratumCrossing:ELegacyProofVeto::Other);
|
||||
return std::nullopt;
|
||||
}
|
||||
if(!EditBrushes.empty()||LayerRanges.empty()||ActiveSpatialBinSize<=0)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::Other);
|
||||
return std::nullopt;
|
||||
}
|
||||
const double LayoutWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
if(!(LayoutWidth>0.0)||!std::isfinite(LayoutWidth))
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::Other);
|
||||
return std::nullopt;
|
||||
}
|
||||
// The earlier chunk scan establishes that this padded box selects only the
|
||||
// compiled legacy field. Native fields and missing chunk bounds stay fail-closed.
|
||||
for(int32 BZ=FloorDiv(BMin[2],ActiveSpatialBinSize);
|
||||
BZ<=FloorDiv(BMax[2],ActiveSpatialBinSize);++BZ)
|
||||
for(int32 BY=FloorDiv(BMin[1],ActiveSpatialBinSize);
|
||||
@@ -6200,12 +6538,43 @@ public:
|
||||
Overlap=Overlap
|
||||
&&Primitive.Max[Axis]+DetailBand>=BMin[Axis]
|
||||
&&Primitive.Min[Axis]-DetailBand<=BMax[Axis];
|
||||
if(Overlap&&!bL74TightNegativeControl)return std::nullopt;
|
||||
if(bTraceTnBlock&&Overlap)
|
||||
std::printf("[L75TNProofTrace] phase=primitive_overlap id=%u bounds=(%d,%d,%d)..(%d,%d,%d)\n",
|
||||
Id,Primitive.Min[0],Primitive.Min[1],Primitive.Min[2],
|
||||
Primitive.Max[0],Primitive.Max[1],Primitive.Max[2]);
|
||||
if(Overlap&&!bL74TightNegativeControl)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::PrimitiveOverlap);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(const uint32 Id:UnbinnedSpatialPrimitiveIds)
|
||||
{
|
||||
if(Id>=Primitives.size())continue;
|
||||
const FPrimitive& Primitive=Primitives[Id];
|
||||
bool Overlap=true;
|
||||
for(int32 Axis=0;Axis<3;++Axis)
|
||||
Overlap=Overlap
|
||||
&&Primitive.Max[Axis]+DetailBand>=BMin[Axis]
|
||||
&&Primitive.Min[Axis]-DetailBand<=BMax[Axis];
|
||||
if(bTraceTnBlock&&Overlap)
|
||||
std::printf("[L75TNProofTrace] phase=unbinned_primitive_overlap id=%u bounds=(%d,%d,%d)..(%d,%d,%d)\n",
|
||||
Id,Primitive.Min[0],Primitive.Min[1],Primitive.Min[2],
|
||||
Primitive.Max[0],Primitive.Max[1],Primitive.Max[2]);
|
||||
if(Overlap&&!bL74TightNegativeControl)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::PrimitiveOverlap);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
const FInterval& Layer=LayerRanges.front();
|
||||
if(Layer.Hi<=Layer.Lo)return std::nullopt;
|
||||
if(Layer.Hi<=Layer.Lo)
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::Other);
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto BaseAt=[&](double Z)
|
||||
{return std::min(Z-static_cast<double>(Layer.Lo),
|
||||
static_cast<double>(Layer.Hi)-Z);};
|
||||
@@ -6217,19 +6586,29 @@ public:
|
||||
const double Peak=(static_cast<double>(Layer.Lo)+Layer.Hi)*0.5;
|
||||
if(Peak>=Z0&&Peak<=Z1)BaseMaximum=std::max(BaseMaximum,BaseAt(Peak));
|
||||
// With no active primitive, smooth-min sees only its +100000 sentinel;
|
||||
// its largest possible downward correction is 12/6 = 2 voxels. Keep
|
||||
// the entire resulting field outside the measured detail support (4.125).
|
||||
// outside the width-12 transition it is exactly the base field. Charge its
|
||||
// 12/6 maximum only if the base interval can approach that sentinel.
|
||||
constexpr double DetailSupport=DetailSupremum+0.125;
|
||||
constexpr double SmoothMinSlack=StrataCaveBlendRadius/6.0;
|
||||
constexpr double SmoothMinSentinel=100000.0;
|
||||
const double SmoothMinSlack=BaseMaximum>SmoothMinSentinel
|
||||
-StrataCaveBlendRadius?StrataCaveBlendRadius/6.0:0.0;
|
||||
constexpr double SafetyEpsilon=1.0e-3;
|
||||
const double SolidMargin=bL74TightNegativeControl
|
||||
?SafetyEpsilon:DetailSupport+SmoothMinSlack+SafetyEpsilon;
|
||||
const double AirMargin=bL74TightNegativeControl
|
||||
?SafetyEpsilon:DetailSupport+SafetyEpsilon;
|
||||
if(bTraceTnBlock)
|
||||
std::printf("[L75TNProofTrace] phase=legacy_interval base_min=%.9f base_max=%.9f detail=%.9f smooth_slack=%.9f solid_margin=%.9f air_margin=%.9f\n",
|
||||
BaseMinimum,BaseMaximum,DetailSupport,SmoothMinSlack,SolidMargin,AirMargin);
|
||||
if(BaseMinimum>SolidMargin)
|
||||
return EVoxelTileClass::AllSolid;
|
||||
if(BaseMaximum<-AirMargin)
|
||||
return EVoxelTileClass::AllAir;
|
||||
if(!bL74TightNegativeControl&&SmoothMinSlack>0.0
|
||||
&&BaseMinimum>DetailSupport+SafetyEpsilon)
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::SmoothMinSlack);
|
||||
else
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::DetailEnvelope);
|
||||
return std::nullopt;
|
||||
};
|
||||
if(const auto LegacyVerdict=TryLegacyAnalyticProof())
|
||||
@@ -6239,7 +6618,10 @@ public:
|
||||
if(bBadPlan||(bSurfaceOverlap&&(bIslandOverlap||bOtherStrateOverlap))
|
||||
||(bIslandOverlap&&bIslandOtherStrateOverlap)
|
||||
||(bCavesOverlap&&(bCaveOtherStrateOverlap||bSurfaceOverlap||bIslandOverlap)))
|
||||
{
|
||||
LegacyProofVeto=static_cast<std::uint8_t>(ELegacyProofVeto::StratumCrossing);
|
||||
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
|
||||
@@ -6317,7 +6699,7 @@ public:
|
||||
static_cast<int64>(BMax[0])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMax[1])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMax[2])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
std::max(Step,1));
|
||||
std::max(SampleStep,1));
|
||||
switch(Verdict)
|
||||
{
|
||||
case VoxelForgeCore::HeightfieldBoxClass::allAir:
|
||||
@@ -9217,7 +9599,14 @@ private:
|
||||
// primitive from analytic queries outside the packed near region.
|
||||
// Les longues galeries diagonales ont une grande AABB mais un tube fin.
|
||||
// Echantillonner l'axe avec une marge prudente garde les requetes analytiques.
|
||||
if(!P.bCapsule)continue;
|
||||
if(!P.bCapsule)
|
||||
{
|
||||
// Large non-capsule bounds are deliberately omitted from point-query bins,
|
||||
// but interval proofs still need a complete candidate set. Keep these IDs
|
||||
// separate so block and tile proofs cannot mistake an omitted carve for air-free rock.
|
||||
UnbinnedSpatialPrimitiveIds.push_back(Id);
|
||||
continue;
|
||||
}
|
||||
const double BinWidth=static_cast<double>(BinResolution);
|
||||
const double AxisStep=std::max(1.0,BinWidth*0.5);
|
||||
const int32 AxisSamples=std::max(1,static_cast<int32>(std::ceil(
|
||||
@@ -9269,6 +9658,9 @@ private:
|
||||
if(bCriticalAir)CriticalAirBins[Key].push_back(Id);
|
||||
}
|
||||
}
|
||||
std::sort(UnbinnedSpatialPrimitiveIds.begin(),UnbinnedSpatialPrimitiveIds.end());
|
||||
UnbinnedSpatialPrimitiveIds.erase(std::unique(UnbinnedSpatialPrimitiveIds.begin(),
|
||||
UnbinnedSpatialPrimitiveIds.end()),UnbinnedSpatialPrimitiveIds.end());
|
||||
}
|
||||
|
||||
void BuildFarParentShapes()
|
||||
@@ -11460,6 +11852,7 @@ private:
|
||||
std::vector<FStandalonePassageVoidCrossing> PassageVoidCrossings;
|
||||
std::array<FAxisBins,3> AxisBins;
|
||||
std::unordered_map<FBinKey,FBinItems,FBinHash> SpatialBins;
|
||||
std::vector<uint32> UnbinnedSpatialPrimitiveIds;
|
||||
std::unordered_map<FBinKey,FBinItems,FBinHash> CriticalAirBins;
|
||||
mutable std::mutex FarFieldGpuInputMutex;
|
||||
mutable std::shared_ptr<const VoxelForgeCore::FarFieldWorldInput> CachedFarFieldGpuInput;
|
||||
|
||||
@@ -2364,6 +2364,10 @@ namespace VoxelForgeCore
|
||||
const double columnMillisecondsBeforeClassification=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto Classification=tileShape.ClassifyBoxDetailed(origin,step,cells);
|
||||
out.diagnostics.tileProofVetoReason=Classification.LegacyProofVeto;
|
||||
out.diagnostics.tileProofSafe=Classification.bSkipSafe;
|
||||
out.diagnostics.tileLegacyBlockProofRelevant=
|
||||
Classification.bLegacyBlockProofRelevant;
|
||||
classificationColumnComputeMilliseconds=std::max(0.0,
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds
|
||||
-columnMillisecondsBeforeClassification);
|
||||
@@ -2443,11 +2447,10 @@ namespace VoxelForgeCore
|
||||
return out;
|
||||
}
|
||||
}
|
||||
// Keep sub-tile early-outs on native fields whose block interval is complete.
|
||||
// The legacy compiled field has sparse tunnel/composer terms that can change a
|
||||
// small block without making the current whole-tile bound fail; leave those blocks
|
||||
// to the exact grid and use only the separately falsified whole-tile proof above.
|
||||
if(bHasWorldAssembly&&step<=2&&bNativeSurfaceTile)
|
||||
// Preserve the committed native-field block path. Legacy block certificates
|
||||
// remain audit-only until their cost is repaid against the full-grid path.
|
||||
if(bHasWorldAssembly&&step<=2
|
||||
&&(bNativeSurfaceTile||options.auditUniformBlockProofs))
|
||||
mesher.bUseBlockEarlyOut=true;
|
||||
}
|
||||
const auto MeshingStart=std::chrono::steady_clock::now();
|
||||
@@ -3077,13 +3080,15 @@ namespace VoxelForgeCore
|
||||
/10000.0;
|
||||
const int32 stratum=world.Manager
|
||||
?world.Manager->GetStrateIndex(static_cast<float>(centerZ)):-1;
|
||||
std::printf("[UniformGridCeiling] strate=%d lod=%d key=(%d,%d,%d) samples=%zu uniform=%d air=%d blocks8=%llu samples8=%llu blocks4=%llu samples4=%llu\n",
|
||||
std::printf("[UniformGridCeiling] strate=%d lod=%d key=(%d,%d,%d) samples=%zu uniform=%d air=%d blocks8=%llu samples8=%llu blocks4=%llu samples4=%llu proof_safe=%d proof_veto=%u\n",
|
||||
stratum,key.lod,key.coordinate.x,key.coordinate.y,key.coordinate.z,values.size(),
|
||||
tileUniform&&tileHasSign?1:0,tileUniform&&tileHasSign&&tileAir?1:0,
|
||||
static_cast<unsigned long long>(uniformBlocks8),
|
||||
static_cast<unsigned long long>(uniformSamples8),
|
||||
static_cast<unsigned long long>(uniformBlocks4),
|
||||
static_cast<unsigned long long>(uniformSamples4));
|
||||
static_cast<unsigned long long>(uniformSamples4),
|
||||
out.diagnostics.tileProofSafe?1:0,
|
||||
static_cast<unsigned>(out.diagnostics.tileProofVetoReason));
|
||||
out.densityHash=densityHash;out.meshHash=HashViewerMesh(mesh);
|
||||
out.payloadHash=HashPayload(mesh);out.topologyHash=HashTopology(mesh);
|
||||
out.materialHash=HashMaterialSamples(out.vertices);
|
||||
|
||||
Reference in new issue
Block a user