wip(assembler): round L30 -- FI floor ring defect fixed; transition cost recovered

The Gradient interface left native solid density in the air-side gap and snapped the face to
voxel levels (concentric ring steps on the FI floor). The owner Gradient path now writes signed
distance both sides; match error RMS 0.36 -> 0.11 m. Band and fade-tile culling cut transition
cost: whole view vs L24 SW +2.7%, FI +0.2%. Evaluator 16. Deterministic across compilers and
workers; interface and passage audits pass. Found: on the real FloatingIslands recipe, the
rock-column connected-ends audit fails (all styles); earlier matrices ran SurfaceWorld twice.

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 11:40:27 +02:00
1 parent 63c7e563d0
commit 103d8b471e
15 files changed
+809 -8

No files matched your search

@@ -249,8 +249,8 @@ namespace VoxelForgeCore
// L'assembleur v1 encode coordonnees et scalaires en 1/256 de voxel, independamment des tuiles.
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 2;
// Version 14 adds shared passage columns for long native open-air crossings.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 15;
// Version 14 adds shared passage columns; version 16 smooths owner Gradient faces.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 16;
enum class FieldChannel : std::uint32_t
{
+286 -6
View File
@@ -53,6 +53,10 @@ public:
:shape_(&shape),minimumSubvoxels_(minimumSubvoxels),
maximumSubvoxels_(maximumSubvoxels),previous_(ActivePassageTileQueries)
{
mayContainResolvedBoundaryBand_=shape.ResolvedBoundaryBandsIntersectSubvoxelRange(
minimumSubvoxels_[2],maximumSubvoxels_[2]);
mayContainTransitionPassageFade_=shape.ResolvedTransitionPassageFadeIntersectsTileXY(
minimumSubvoxels_,maximumSubvoxels_);
featureIndices_=shape.BuildPassageFeatureTileList(
minimumSubvoxels_,maximumSubvoxels_);
passageRoutes_.assign(shape.ResolvedLayout?shape.ResolvedLayout->passages.size():0,0);
@@ -92,6 +96,13 @@ public:
{return passageIndex<passageMouthSupports_.size()&&passageMouthSupports_[passageIndex]!=0;}
bool hasRouteFeatureOverlap() const{return routeFeatureOverlap_;}
bool hasColumnZOverlap() const{return columnZOverlap_;}
bool mayContainResolvedBoundaryBand() const{return mayContainResolvedBoundaryBand_;}
bool mayContainTransitionPassageFade() const{return mayContainTransitionPassageFade_;}
bool mayContainResolvedBoundaryBandAtZ(double zMicrometres) const
{
return mayContainResolvedBoundaryBand_
&&shape_->ResolvedBoundaryBandMayAffectAtZMicrometres(zMicrometres);
}
std::size_t featureCount() const{return featureIndices_.size();}
private:
@@ -100,6 +111,8 @@ public:
std::vector<std::uint32_t> featureIndices_;
std::vector<std::uint8_t> passageRoutes_,passageColumns_,passageMouthSupports_;
bool routeFeatureOverlap_=false,columnZOverlap_=false;
bool mayContainResolvedBoundaryBand_=true;
bool mayContainTransitionPassageFade_=true;
const FScopedPassageTileQueries* previous_;
};
@@ -174,6 +187,83 @@ public:
&&Upper.transitionHonouredInCore;
}
bool ResolvedBoundaryBandsIntersectSubvoxelRange(std::int64_t minimumZSubvoxels,
std::int64_t maximumZSubvoxels) const
{
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0
||minimumZSubvoxels>maximumZSubvoxels)return false;
constexpr double Subvoxels=static_cast<double>(
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double queryMinimumMicrometres=static_cast<double>(minimumZSubvoxels)
*Owner->CoreVoxelSizeMicrons/Subvoxels;
const double queryMaximumMicrometres=static_cast<double>(maximumZSubvoxels)
*Owner->CoreVoxelSizeMicrons/Subvoxels;
const double sampleMarginMicrometres=static_cast<double>(Owner->CoreVoxelSizeMicrons);
for(const auto& interval:ResolvedBoundaryZIntervalsMicrometres)
{
if(queryMaximumMicrometres+sampleMarginMicrometres>=interval[0]-1.0
&&queryMinimumMicrometres-sampleMarginMicrometres<interval[1]+1.0)
return true;
}
return false;
}
bool ResolvedBoundaryBandMayAffectAtZMicrometres(double zMicrometres) const
{
if(!std::isfinite(zMicrometres)||ResolvedBoundaryZIntervalsMicrometres.empty())return false;
const auto it=std::upper_bound(ResolvedBoundaryZIntervalsMicrometres.begin(),
ResolvedBoundaryZIntervalsMicrometres.end(),zMicrometres,
[](double z,const auto& interval){return z<interval[0];});
return it!=ResolvedBoundaryZIntervalsMicrometres.begin()
&&zMicrometres<std::prev(it)->at(1);
}
void BuildResolvedBoundaryZIntervals()
{
ResolvedBoundaryZIntervalsMicrometres.clear();
if(!ResolvedLayout)return;
for(const auto& stratum:ResolvedLayout->strates)
{
if(stratum.ceiling.presentInCurrentField
&&stratum.ceiling.bottomMicrometres<stratum.ceiling.topMicrometres)
ResolvedBoundaryZIntervalsMicrometres.push_back({
static_cast<double>(stratum.ceiling.bottomMicrometres),
static_cast<double>(stratum.ceiling.topMicrometres)});
if(stratum.bedrock.presentInCurrentField
&&stratum.bedrock.bottomMicrometres<stratum.bedrock.topMicrometres)
ResolvedBoundaryZIntervalsMicrometres.push_back({
static_cast<double>(stratum.bedrock.bottomMicrometres),
static_cast<double>(stratum.bedrock.topMicrometres)});
}
for(std::int32_t order=0;order+1<static_cast<std::int32_t>(ResolvedLayout->strates.size());
++order)
{
const auto* upper=FindResolvedLayoutStratumByOrder(order);
const auto* lower=FindResolvedLayoutStratumByOrder(order+1);
if(!upper||!lower||!IsResolvedLayoutTransitionActive(*upper))continue;
const auto displacement=ResolvedTransitionMaximumFaceDisplacementMicrometres(
*upper,*lower);
ResolvedBoundaryZIntervalsMicrometres.push_back({
static_cast<double>(std::min(upper->bedrock.bottomMicrometres,
lower->ceiling.bottomMicrometres)-displacement),
static_cast<double>(std::max(upper->bedrock.topMicrometres,
lower->ceiling.topMicrometres)+displacement)});
}
std::sort(ResolvedBoundaryZIntervalsMicrometres.begin(),
ResolvedBoundaryZIntervalsMicrometres.end(),
[](const auto& a,const auto& b){return a[0]<b[0];});
std::size_t write=0;
for(const auto& interval:ResolvedBoundaryZIntervalsMicrometres)
{
if(write>0&&interval[0]<=ResolvedBoundaryZIntervalsMicrometres[write-1][1])
ResolvedBoundaryZIntervalsMicrometres[write-1][1]=std::max(
ResolvedBoundaryZIntervalsMicrometres[write-1][1],interval[1]);
else
ResolvedBoundaryZIntervalsMicrometres[write++]=interval;
}
ResolvedBoundaryZIntervalsMicrometres.resize(write);
}
std::int64_t ResolvedTransitionMaximumFaceDisplacementMicrometres(
const VoxelForgeCore::ResolvedLayoutStratumV2& Upper,
const VoxelForgeCore::ResolvedLayoutStratumV2& Lower) const
@@ -189,11 +279,68 @@ public:
return (maximumCrossing+1)/2;
}
bool ResolvedTransitionPassageFadeIntersectsTileXY(
const std::array<std::int64_t,3>& minimumSubvoxels,
const std::array<std::int64_t,3>& maximumSubvoxels) const
{
if(!Owner||!ResolvedLayout||Owner->CoreVoxelSizeMicrons==0)return true;
for(std::size_t axis=0;axis<2;++axis)
if(minimumSubvoxels[axis]>maximumSubvoxels[axis])return true;
constexpr std::int64_t Subvoxels=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t SupportMarginMicrometres=1000000;
constexpr std::int64_t FadeWidthMicrometres=2000000;
const double voxelMicrometres=static_cast<double>(Owner->CoreVoxelSizeMicrons);
const double minimumX=static_cast<double>(minimumSubvoxels[0])*voxelMicrometres
/Subvoxels-voxelMicrometres;
const double maximumX=static_cast<double>(maximumSubvoxels[0])*voxelMicrometres
/Subvoxels+voxelMicrometres;
const double minimumY=static_cast<double>(minimumSubvoxels[1])*voxelMicrometres
/Subvoxels-voxelMicrometres;
const double maximumY=static_cast<double>(maximumSubvoxels[1])*voxelMicrometres
/Subvoxels+voxelMicrometres;
for(std::int32_t order=0;order+1<static_cast<std::int32_t>(ResolvedLayout->strates.size());
++order)
{
const auto* upper=FindResolvedLayoutStratumByOrder(order);
const auto* lower=FindResolvedLayoutStratumByOrder(order+1);
if(!upper||!lower||!IsResolvedLayoutTransitionActive(*upper))continue;
const std::int64_t seam=upper->bedrock.bottomMicrometres;
for(const auto& passage:ResolvedLayout->passages)
{
if(passage.upperStrateIndex!=upper->strateIndex
||passage.lowerStrateIndex!=lower->strateIndex)continue;
const std::int64_t mouthRadius=std::max<std::int64_t>(0,
passage.mouthRadiusSubvoxels*Owner->CoreVoxelSizeMicrons/Subvoxels);
const std::int64_t protectedRadius=mouthRadius+SupportMarginMicrometres;
const double fadeEnd=static_cast<double>(protectedRadius+FadeWidthMicrometres);
for(const auto* mouth:{&passage.upperMouthSubvoxels,&passage.lowerMouthSubvoxels})
{
const std::int64_t mouthZ=mouth->at(2)*Owner->CoreVoxelSizeMicrons/Subvoxels;
if(std::abs(mouthZ-seam)
>upper->transitionWidthMicrometres/2+mouthRadius)continue;
const double mouthX=static_cast<double>(mouth->at(0)
*Owner->CoreVoxelSizeMicrons/Subvoxels);
const double mouthY=static_cast<double>(mouth->at(1)
*Owner->CoreVoxelSizeMicrons/Subvoxels);
const double dx=mouthX<minimumX?minimumX-mouthX
:mouthX>maximumX?mouthX-maximumX:0.0;
const double dy=mouthY<minimumY?minimumY-mouthY
:mouthY>maximumY?mouthY-maximumY:0.0;
if(dx*dx+dy*dy<fadeEnd*fadeEnd)return true;
}
}
}
return false;
}
std::int64_t ResolvedTransitionPassageWeightQ16(
const VoxelForgeCore::ResolvedLayoutStratumV2& Upper,
const VoxelForgeCore::ResolvedLayoutStratumV2& Lower,
std::int64_t XMicrometres,std::int64_t YMicrometres) const
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().TransitionPassageFadeCalls;
#endif
constexpr std::int64_t Q16=1ll<<16;
constexpr std::int64_t SupportMarginMicrometres=1000000;
constexpr std::int64_t FadeWidthMicrometres=2000000;
@@ -212,6 +359,9 @@ public:
+SupportMarginMicrometres;
for(const auto* mouth:{&passage.upperMouthSubvoxels,&passage.lowerMouthSubvoxels})
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().TransitionPassageMouthTests;
#endif
const std::int64_t mouthZMicrometres=mouth->at(2)*coreMicrometres
/subvoxelsPerVoxel;
if(std::abs(mouthZMicrometres-seam)
@@ -236,6 +386,31 @@ public:
bool IsInsideResolvedStrateBoundaryBand(float Z) const
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().BoundaryBandCalls;
#endif
const auto* tileQueries=ActivePassageTileQueries;
if(tileQueries&&tileQueries->matches(*this))
{
if(!tileQueries->mayContainResolvedBoundaryBand())
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().BoundaryTileCullRejects;
#endif
return false;
}
if(Owner&&!tileQueries->mayContainResolvedBoundaryBandAtZ(
static_cast<double>(Z)*Owner->CoreVoxelSizeMicrons))
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().BoundaryPointCullRejects;
#endif
return false;
}
// This predicate only selects the scalar path for boundary-capable Z
// samples. The merged intervals are conservative, so a positive is safe.
return true;
}
if(!ResolvedLayout||!Owner||!Owner->Manager||!std::isfinite(Z))return false;
const double worldZCentimetres=static_cast<double>(Z)*GetCoreVoxelSizeCm();
const int32 strateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(worldZCentimetres));
@@ -248,6 +423,9 @@ public:
const auto mayTouchTransition=[&](const VoxelForgeCore::ResolvedLayoutStratumV2* Upper,
const VoxelForgeCore::ResolvedLayoutStratumV2* Lower)
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().TransitionWindowChecks;
#endif
if(!Upper||!Lower||!IsResolvedLayoutTransitionActive(*Upper))return false;
const auto maxDisplacement=ResolvedTransitionMaximumFaceDisplacementMicrometres(
*Upper,*Lower);
@@ -255,7 +433,11 @@ public:
Lower->ceiling.bottomMicrometres)-maxDisplacement;
const double maximum=std::max(Upper->bedrock.topMicrometres,
Lower->ceiling.topMicrometres)+maxDisplacement;
return zMicrometres>=minimum&&zMicrometres<maximum;
const bool touches=zMicrometres>=minimum&&zMicrometres<maximum;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
if(touches)++ThreadClassificationStats().TransitionWindowHits;
#endif
return touches;
};
const auto* lower=FindResolvedLayoutStratumByOrder(stratum->order+1);
const auto* upper=stratum->order>0?FindResolvedLayoutStratumByOrder(stratum->order-1):nullptr;
@@ -265,7 +447,29 @@ public:
float ApplyResolvedStrateBoundaryBand(float density,float X,float Y,float Z) const
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().BoundaryBandCalls;
#endif
(void)X;(void)Y;
const auto* tileQueries=ActivePassageTileQueries;
if(tileQueries&&tileQueries->matches(*this))
{
if(!tileQueries->mayContainResolvedBoundaryBand())
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().BoundaryTileCullRejects;
#endif
return density;
}
if(Owner&&!tileQueries->mayContainResolvedBoundaryBandAtZ(
static_cast<double>(Z)*Owner->CoreVoxelSizeMicrons))
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().BoundaryPointCullRejects;
#endif
return density;
}
}
if(!ResolvedLayout||!Owner||!Owner->Manager||!std::isfinite(Z))return density;
const double worldZCentimetres=static_cast<double>(Z)*GetCoreVoxelSizeCm();
const int32 strateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(worldZCentimetres));
@@ -280,6 +484,9 @@ public:
const auto applyInterface=[&](const VoxelForgeCore::ResolvedLayoutStratumV2* upper,
const VoxelForgeCore::ResolvedLayoutStratumV2* lower)
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().TransitionWindowChecks;
#endif
if(!upper||!lower||!IsResolvedLayoutTransitionActive(*upper))return false;
const auto maxDisplacement=ResolvedTransitionMaximumFaceDisplacementMicrometres(
*upper,*lower);
@@ -288,6 +495,10 @@ public:
const double maximum=std::max(upper->bedrock.topMicrometres,
lower->ceiling.topMicrometres)+maxDisplacement;
if(zMicrometres<minimum||zMicrometres>=maximum)return false;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().TransitionWindowHits;
++ThreadClassificationStats().TransitionEvaluatorCalls;
#endif
if(upper==stratum)bBedrockHandledByTransition=true;
if(lower==stratum)bCeilingHandledByTransition=true;
const std::int64_t xMicrometres=static_cast<std::int64_t>(std::llround(
@@ -298,9 +509,15 @@ public:
Owner->Seed,ResolvedLayout->strateLayoutChunkSizeMicrometres,
xMicrometres,yMicrometres);
auto resolvedBands=bands;
const std::int64_t transitionWeight=ResolvedTransitionPassageWeightQ16(
*upper,*lower,xMicrometres,yMicrometres);
constexpr std::int64_t TransitionQ16=1ll<<16;
const auto* tileQueries=ActivePassageTileQueries;
const bool skipPassageFade=tileQueries&&tileQueries->matches(*this)
&&!tileQueries->mayContainTransitionPassageFade();
const std::int64_t transitionWeight=skipPassageFade?TransitionQ16
:ResolvedTransitionPassageWeightQ16(*upper,*lower,xMicrometres,yMicrometres);
#if defined(VF_L30_ENABLE_COST_COUNTERS)
if(skipPassageFade)++ThreadClassificationStats().TransitionPassageTileSkips;
#endif
if(transitionWeight<TransitionQ16)
{
const std::int64_t seam=upper->bedrock.bottomMicrometres;
@@ -325,9 +542,36 @@ public:
&&zMicrometres<resolvedBands.upperBedrockTopMicrometres;
const bool lowerCeiling=zMicrometres>=resolvedBands.lowerCeilingBottomMicrometres
&&zMicrometres<resolvedBands.lowerCeilingTopMicrometres;
if(!upperBedrock&&!lowerCeiling)return false;
if(!upperBedrock&&!lowerCeiling)
{
// FloatingIslands can retain native solid density above a negatively
// displaced Gradient face, creating a second surface instead of the
// resolved transition top. Keep that air-side gap open in the owner field.
const std::int64_t seam=upper->bedrock.bottomMicrometres;
if(upper->generatorKind=="FloatingIslands"
&&upper->transitionMode=="Gradient"
&&resolvedBands.upperBedrockTopMicrometres<seam
&&zMicrometres>=resolvedBands.upperBedrockTopMicrometres
&&zMicrometres<seam)
{
const double distanceVoxels=static_cast<double>(zMicrometres
-resolvedBands.upperBedrockTopMicrometres)
/std::max<std::uint32_t>(1,Owner->CoreVoxelSizeMicrons);
density=std::max(0.05f,static_cast<float>(distanceVoxels));
return true;
}
return false;
}
const auto& band=upperBedrock?upper->bedrock:lower->ceiling;
density=-std::max(0.05f,static_cast<float>(band.solidDensityQ16)/65536.0f);
if(upperBedrock&&upper->generatorKind=="FloatingIslands"
&&upper->transitionMode=="Gradient")
{
const double distanceVoxels=static_cast<double>(
resolvedBands.upperBedrockTopMicrometres-zMicrometres)
/std::max<std::uint32_t>(1,Owner->CoreVoxelSizeMicrons);
density=-std::max(0.05f,static_cast<float>(distanceVoxels));
}
else density=-std::max(0.05f,static_cast<float>(band.solidDensityQ16)/65536.0f);
return true;
};
@@ -435,23 +679,49 @@ public:
{
if(!crossing.needsColumn||crossing.passageIndex!=passageIndex
||qz>crossing.topZSubvoxels||qz<crossing.bottomZSubvoxels)continue;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().ColumnZOverlapCalls;
#endif
double columnDistance;
const bool tileProvesColumnExterior=passageTileScopeMatches
&&!ActivePassageTileQueries->hasFeature(passageIndex,true);
if(tileProvesColumnExterior)
{
if(composed<0.0f)continue;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().ColumnGuardCalls;
#endif
if(composed<0.0f)
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().ColumnGuardNonpositiveSkips;
#endif
continue;
}
const double dx=qx-crossing.centerXSubvoxels;
const double dy=qy-crossing.centerYSubvoxels;
const double horizontalSquared=dx*dx+dy*dy;
const double maximumRadius=static_cast<double>(crossing.maximumRadiusSubvoxels);
const double exteriorLimit=maximumRadius
+static_cast<double>(composed)*Q+1.0e-4;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
if(horizontalSquared>exteriorLimit*exteriorLimit)
{
++ThreadClassificationStats().ColumnGuardRadialRejects;
continue;
}
++ThreadClassificationStats().ColumnSignedDistanceCalls;
#else
if(horizontalSquared>exteriorLimit*exteriorLimit)continue;
#endif
columnDistance=PassageColumnSignedDistance(crossing,X,Y,Z);
}
else
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
++ThreadClassificationStats().ColumnSignedDistanceCalls;
#endif
columnDistance=PassageColumnSignedDistance(crossing,X,Y,Z);
}
composed=std::min(composed,static_cast<float>(columnDistance/Q));
}
const bool tilePassageFree=passageTileScopeMatches
@@ -867,6 +1137,14 @@ public:
uint64 SolidBoxes=0,AirBoxes=0,MixedBoxes=0,UnknownBoxes=0;
uint64 NativeFloatFieldCalls=0,NativeLatticeFieldCalls=0,NativeUnclampedFieldCalls=0;
double Milliseconds=0.0;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
uint64 BoundaryBandCalls=0,TransitionWindowChecks=0,TransitionWindowHits=0;
uint64 TransitionEvaluatorCalls=0,TransitionPassageFadeCalls=0,TransitionPassageMouthTests=0;
uint64 BoundaryTileCullRejects=0,BoundaryPointCullRejects=0;
uint64 ColumnZOverlapCalls=0,ColumnGuardCalls=0,ColumnGuardNonpositiveSkips=0;
uint64 ColumnGuardRadialRejects=0,ColumnSignedDistanceCalls=0;
uint64 TransitionPassageTileSkips=0;
#endif
};
static void ResetThreadClassificationStats() { ThreadClassificationStats()={}; }
static FClassificationThreadStats GetThreadClassificationStats()
@@ -2793,6 +3071,7 @@ private:
void BuildPassageVoidCrossingPlan()
{
BuildResolvedBoundaryZIntervals();
PassageVoidCrossings.clear();
if(!Owner||!ResolvedLayout||ResolvedLayout->passages.empty())return;
if(!Owner->PassageVoidCrossingCache)
@@ -7231,6 +7510,7 @@ private:
FIntVector NativeOnlyMaximum=FIntVector::ZeroValue;
mutable std::atomic<uint32> NativeOnlyViolations{0};
bool FarBuilt=false;
std::vector<std::array<double,2>> ResolvedBoundaryZIntervalsMicrometres;
std::vector<FInterval> LayerRanges;
std::vector<FLayerField> LayerFields;
std::vector<FPrimitive> Primitives;
+34
View File
@@ -40,6 +40,34 @@ namespace VoxelForgeCore
namespace
{
#if defined(VF_L30_ENABLE_COST_COUNTERS)
void PrintL30CostCounters(const FStandaloneShapeWorld::FClassificationThreadStats& stats)
{
if(stats.BoundaryBandCalls==0&&stats.ColumnZOverlapCalls==0)return;
std::printf("[L30CostTile] boundary_band_calls=%llu transition_window_checks=%llu "
"transition_window_hits=%llu transition_evaluations=%llu passage_fade_calls=%llu "
"passage_fade_mouth_tests=%llu column_z_overlap_calls=%llu column_guard_calls=%llu "
"column_guard_nonpositive_skips=%llu column_guard_radial_rejects=%llu "
"boundary_tile_cull_rejects=%llu boundary_point_cull_rejects=%llu "
"transition_passage_tile_skips=%llu "
"column_sdf_calls=%llu\n",
static_cast<unsigned long long>(stats.BoundaryBandCalls),
static_cast<unsigned long long>(stats.TransitionWindowChecks),
static_cast<unsigned long long>(stats.TransitionWindowHits),
static_cast<unsigned long long>(stats.TransitionEvaluatorCalls),
static_cast<unsigned long long>(stats.TransitionPassageFadeCalls),
static_cast<unsigned long long>(stats.TransitionPassageMouthTests),
static_cast<unsigned long long>(stats.ColumnZOverlapCalls),
static_cast<unsigned long long>(stats.ColumnGuardCalls),
static_cast<unsigned long long>(stats.ColumnGuardNonpositiveSkips),
static_cast<unsigned long long>(stats.ColumnGuardRadialRejects),
static_cast<unsigned long long>(stats.BoundaryTileCullRejects),
static_cast<unsigned long long>(stats.BoundaryPointCullRejects),
static_cast<unsigned long long>(stats.TransitionPassageTileSkips),
static_cast<unsigned long long>(stats.ColumnSignedDistanceCalls));
}
#endif
FIntVector ToNative(Vec3i value) { return {value.x,value.y,value.z}; }
FVector ToNative(Vec3d value) { return {value.x,value.y,value.z}; }
Vec3d FromNative(const FVector& value) { return {value.X,value.Y,value.Z}; }
@@ -1995,6 +2023,9 @@ namespace VoxelForgeCore
out.diagnostics.classificationExclusiveMilliseconds=std::max(0.0,
Counts.Milliseconds-classificationColumnComputeMilliseconds);
out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
PrintL30CostCounters(Counts);
#endif
if(options.captureColumnCoordinates)
{
out.diagnostics.columnEvaluationWorkerHash=static_cast<std::uint64_t>(
@@ -2356,6 +2387,9 @@ namespace VoxelForgeCore
if(bHasWorldAssembly)
{
const auto Counts=FStandaloneShapeWorld::GetThreadClassificationStats();
#if defined(VF_L30_ENABLE_COST_COUNTERS)
PrintL30CostCounters(Counts);
#endif
const auto& Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
out.diagnostics.fieldCalls=Columns.pointCalls;
out.diagnostics.floatFieldCalls=Counts.NativeFloatFieldCalls;