diag: passage source-floor clamp rejected (L91); field has steep non-distance tails
Clamping the passage-composition source density at -c (c=2..6 voxels, diagnostic, default off) changes the surface far beyond the bar: c=6 still 65 sign flips, up to 165 cm displacement, 120 deg normal change (downstream composition consumes deep values). Source gradient at crossings is distance-like at the median but has huge tails (FI p90 |grad| 512, max 894 vox/vox), so the density-dependent passage reach is inflated by non-distance values. No world change. 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
a7a03d42b4
commit
8805291d3e
9 files changed
+937
-11
No files matched your search
@@ -70,6 +70,20 @@ static bool VF_IsL90PassageCandidateCensusEnabled()
|
||||
return bEnabled;
|
||||
}
|
||||
|
||||
static double VF_L91PassageSourceFloorVoxels()
|
||||
{
|
||||
static const double Value=[]
|
||||
{
|
||||
const char* Raw=std::getenv("VOXELFORGE_L91_PASSAGE_SOURCE_FLOOR_VOXELS");
|
||||
if(!Raw||!*Raw)return 0.0;
|
||||
char* End=nullptr;
|
||||
const double Parsed=std::strtod(Raw,&End);
|
||||
return End!=Raw&&*End=='\0'&&std::isfinite(Parsed)&&Parsed>0.0
|
||||
?Parsed:0.0;
|
||||
}();
|
||||
return Value;
|
||||
}
|
||||
|
||||
// Standalone-only world compiler. It compiles the runtime room/tunnel/passages into
|
||||
// axis-line intervals, composes rock/cave signs into packed Z-column words, then displaces
|
||||
// only the conservative surface band. The copied Unreal generator remains the A/B reference.
|
||||
@@ -226,12 +240,18 @@ public:
|
||||
FScopedPassageTileQueries(const FStandaloneShapeWorld& shape,
|
||||
const std::array<std::int64_t,3>& minimumSubvoxels,
|
||||
const std::array<std::int64_t,3>& maximumSubvoxels,
|
||||
VoxelForgeCore::TileGenerationStats* profileStats=nullptr,int32 profileLod=0)
|
||||
VoxelForgeCore::TileGenerationStats* profileStats=nullptr,int32 profileLod=0,
|
||||
double passageSourceFloorVoxels=-1.0)
|
||||
:shape_(&shape),minimumSubvoxels_(minimumSubvoxels),
|
||||
maximumSubvoxels_(maximumSubvoxels),previous_(ActivePassageTileQueries),
|
||||
profileStats_(profileStats),profileLod_(profileLod),
|
||||
candidateProfiled_(profileStats!=nullptr&&shape.L90PassageCandidateProgramEnabled)
|
||||
candidateProfiled_(profileStats!=nullptr&&shape.L90PassageCandidateProgramEnabled),
|
||||
passageSourceFloorVoxels_(std::isfinite(passageSourceFloorVoxels)
|
||||
&&passageSourceFloorVoxels>=0.0?passageSourceFloorVoxels
|
||||
:VF_L91PassageSourceFloorVoxels())
|
||||
{
|
||||
if(passageSourceFloorVoxels_>0.0)
|
||||
clampCounters_=std::make_shared<FPassageSourceClampCounters>();
|
||||
mayContainResolvedBoundaryBand_=shape.ResolvedBoundaryBandsIntersectSubvoxelRange(
|
||||
minimumSubvoxels_[2],maximumSubvoxels_[2]);
|
||||
const bool disablePassageComposition=VF_IsL88PassageCompositionDisabled();
|
||||
@@ -338,11 +358,18 @@ public:
|
||||
mayContainResolvedBoundaryBand_(source.mayContainResolvedBoundaryBand_),
|
||||
mayContainTransitionPassageFade_(source.mayContainTransitionPassageFade_),
|
||||
previous_(ActivePassageTileQueries),profileLod_(source.profileLod_),
|
||||
candidateProfiled_(source.candidateProfiled_)
|
||||
candidateProfiled_(source.candidateProfiled_),
|
||||
passageSourceFloorVoxels_(source.passageSourceFloorVoxels_),
|
||||
clampCounters_(source.clampCounters_),ownsClampCounters_(false)
|
||||
{ ActivePassageTileQueries=this; }
|
||||
~FScopedPassageTileQueries()
|
||||
{
|
||||
ActivePassageTileQueries=previous_;
|
||||
if(ownsClampCounters_&&clampCounters_&&passageSourceFloorVoxels_>0.0)
|
||||
std::printf("[L91PassageSourceClampTile] lod=%d floor_vox=%.3f source_clamped=%llu source_delta_sum=%.9g deepest_source_vox=%.9g\n",
|
||||
profileLod_,passageSourceFloorVoxels_,
|
||||
static_cast<unsigned long long>(clampCounters_->samples.load()),
|
||||
clampCounters_->deltaSum.load(),clampCounters_->deepestSource.load());
|
||||
if(!candidateProfiled_||l90Queries_==0)return;
|
||||
std::array<std::array<char,360>,4> HistText{};
|
||||
constexpr std::array<const char*,4> Names={"carve","tread","clearance","mouth_column"};
|
||||
@@ -451,6 +478,20 @@ public:
|
||||
VoxelForgeCore::TileGenerationStats* profileStats() const noexcept
|
||||
{return profileStats_;}
|
||||
bool candidateCensusProfiled() const noexcept{return candidateProfiled_;}
|
||||
double passageSourceFloorVoxels() const noexcept{return passageSourceFloorVoxels_;}
|
||||
void recordPassageSourceClamp(float sourceDensity) const noexcept
|
||||
{
|
||||
if(passageSourceFloorVoxels_<=0.0)return;
|
||||
const double delta=-passageSourceFloorVoxels_-static_cast<double>(sourceDensity);
|
||||
if(!(delta>0.0)||!std::isfinite(delta))return;
|
||||
if(!clampCounters_)clampCounters_=std::make_shared<FPassageSourceClampCounters>();
|
||||
clampCounters_->samples.fetch_add(1,std::memory_order_relaxed);
|
||||
clampCounters_->deltaSum.fetch_add(delta,std::memory_order_relaxed);
|
||||
double prior=clampCounters_->deepestSource.load(std::memory_order_relaxed);
|
||||
while(static_cast<double>(sourceDensity)<prior
|
||||
&&!clampCounters_->deepestSource.compare_exchange_weak(prior,sourceDensity,
|
||||
std::memory_order_relaxed,std::memory_order_relaxed)){}
|
||||
}
|
||||
void recordL90CandidateHit(const std::array<std::size_t,4>& counts,bool haveBrick) const
|
||||
{
|
||||
if(!candidateProfiled_)return;
|
||||
@@ -491,6 +532,12 @@ public:
|
||||
{return cavernFeatureIndices_;}
|
||||
|
||||
private:
|
||||
struct FPassageSourceClampCounters
|
||||
{
|
||||
std::atomic<std::uint64_t> samples{0};
|
||||
std::atomic<double> deltaSum{0.0};
|
||||
std::atomic<double> deepestSource{0.0};
|
||||
};
|
||||
const FStandaloneShapeWorld* shape_;
|
||||
std::array<std::int64_t,3> minimumSubvoxels_,maximumSubvoxels_;
|
||||
std::vector<std::uint32_t> featureIndices_;
|
||||
@@ -508,6 +555,9 @@ public:
|
||||
VoxelForgeCore::TileGenerationStats* profileStats_=nullptr;
|
||||
int32 profileLod_=0;
|
||||
bool candidateProfiled_=false;
|
||||
double passageSourceFloorVoxels_=0.0;
|
||||
mutable std::shared_ptr<FPassageSourceClampCounters> clampCounters_;
|
||||
bool ownsClampCounters_=true;
|
||||
mutable std::uint64_t l90Queries_=0,l90NoBrickQueries_=0;
|
||||
mutable std::uint64_t l90FallbackIterations_=0,l90FallbackNanoseconds_=0,
|
||||
l90QueryNanoseconds_=0,l90FallbackCarveVisits_=0,l90FallbackTreadVisits_=0,
|
||||
@@ -1269,6 +1319,16 @@ public:
|
||||
~FL90CandidateBrickScope()
|
||||
{FStandaloneShapeWorld::ActivePassageCandidateBrick=previous;}
|
||||
} l90CandidateBrickScope(l90CandidateBrick);
|
||||
const double passageSourceFloorVoxels=passageTileScopeMatches
|
||||
?passageTileQueries->passageSourceFloorVoxels()
|
||||
:VF_L91PassageSourceFloorVoxels();
|
||||
if(passageSourceFloorVoxels>0.0
|
||||
&&static_cast<double>(density)<-passageSourceFloorVoxels)
|
||||
{
|
||||
if(passageTileScopeMatches)
|
||||
passageTileQueries->recordPassageSourceClamp(density);
|
||||
density=static_cast<float>(-passageSourceFloorVoxels);
|
||||
}
|
||||
if(passageTileScopeMatches&&density>=0.0f
|
||||
&&!passageTileQueries->hasRouteFeatureOverlap()
|
||||
&&!passageTileQueries->hasColumnZOverlap()
|
||||
@@ -4002,6 +4062,9 @@ public:
|
||||
return AnalyticDensityAt(X,Y,Z);
|
||||
}
|
||||
|
||||
float PassageSourceDensityAt(float X,float Y,float Z) const
|
||||
{return NativePassageDensityAt(X,Y,Z);}
|
||||
|
||||
std::vector<std::uint32_t> OreVeinTileCandidates(
|
||||
const std::array<std::int64_t,3>& minimum,
|
||||
const std::array<std::int64_t,3>& maximum) const
|
||||
@@ -5900,12 +5963,24 @@ private:
|
||||
L90PassageCandidateBricks.clear();
|
||||
L90PassageCandidateProgramValid=false;
|
||||
L90PassageCandidateBuildFailures=0;
|
||||
L90PassageCandidateReachMetres=10.0;
|
||||
const auto ReadPositiveEnv=[](const char* Name,double Default)
|
||||
{
|
||||
const char* Raw=std::getenv(Name);
|
||||
if(!Raw||!*Raw)return Default;
|
||||
char* End=nullptr;
|
||||
const double Parsed=std::strtod(Raw,&End);
|
||||
return End!=Raw&&*End=='\0'&&std::isfinite(Parsed)&&Parsed>0.0
|
||||
?Parsed:Default;
|
||||
};
|
||||
L90PassageCandidateReachMetres=ReadPositiveEnv(
|
||||
"VOXELFORGE_L90_PASSAGE_REACH_METRES",10.0);
|
||||
const double brickMetres=ReadPositiveEnv(
|
||||
"VOXELFORGE_L90_PASSAGE_BRICK_METRES",4.0);
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double qPerMetre=Q*1000000.0/std::max<std::uint32_t>(1,
|
||||
ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const double voxelsPerMetre=qPerMetre/Q;
|
||||
L90PassageCandidateBrickEdgeSubvoxels=4.0*qPerMetre;
|
||||
L90PassageCandidateBrickEdgeSubvoxels=brickMetres*qPerMetre;
|
||||
const double reachSubvoxels=L90PassageCandidateReachMetres*qPerMetre;
|
||||
const double cubicPadding=1.0;
|
||||
std::array<std::uint64_t,4> emittedReferences{};
|
||||
@@ -6241,8 +6316,9 @@ private:
|
||||
L90PassageCandidateProgramValid=L90PassageCandidateBuildFailures==0;
|
||||
const double buildMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
FClock::now()-buildStart).count();
|
||||
std::printf("[L90PassageCandidateBuild] brick_m=4 reach_m=%.1f bricks=%zu emitted=(%llu,%llu,%llu,%llu) unique=(%llu,%llu,%llu,%llu) bytes_estimate=%zu build_ms=%.3f failures=%llu valid=%d\n",
|
||||
L90PassageCandidateReachMetres,L90PassageCandidateBricks.size(),
|
||||
std::printf("[L90PassageCandidateBuild] brick_m=%.3f reach_m=%.3f source_floor_vox=%.3f bricks=%zu emitted=(%llu,%llu,%llu,%llu) unique=(%llu,%llu,%llu,%llu) bytes_estimate=%zu build_ms=%.3f failures=%llu valid=%d\n",
|
||||
brickMetres,L90PassageCandidateReachMetres,VF_L91PassageSourceFloorVoxels(),
|
||||
L90PassageCandidateBricks.size(),
|
||||
static_cast<unsigned long long>(emittedReferences[0]),
|
||||
static_cast<unsigned long long>(emittedReferences[1]),
|
||||
static_cast<unsigned long long>(emittedReferences[2]),
|
||||
|
||||
Reference in new issue
Block a user