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:
1 parent
63c7e563d0
commit
103d8b471e
15 files changed
+809
-8
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user