core: one passage influence reach in metres, converted once (L130, evaluator v227)
Reach = 0.75 shell + 0.21 relief + 0.50/6 smooth-max support + 0.01 = 1.0533 m, used by proof, feature, fallback, off-grid bounds and the sweep cutoff. Removes the dead 4 m term (v225 added metres to core-voxel coordinates). Captures and mouth close-ups byte-identical; proof audit 0 misses over 25,728 shell/crest samples; floor 7.884 mm; seams, walkability, fast tier, Clang/GCC W1/W8/W12 green; timing equal within noise. 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
b96dd739b8
commit
044cd6d9c5
50 files changed
+2389
-29
No files matched your search
@@ -72,11 +72,28 @@ static bool VF_IsL98PassagePrepareTraceEnabled()
|
||||
return bEnabled;
|
||||
}
|
||||
|
||||
static double VF_L93PassageCarveReachMetres()
|
||||
static bool VF_IsL130PassageReachProofAuditEnabled()
|
||||
{
|
||||
// The generated world uses a fixed bounded tube influence. An explicit diagnostic
|
||||
// option can still select the legacy point-evaluation path for comparisons.
|
||||
return 4.0;
|
||||
static const bool bEnabled=VF_IsL88DiagnosticSwitchEnabled(
|
||||
"VOXELFORGE_L130_PASSAGE_REACH_PROOF_AUDIT");
|
||||
return bEnabled;
|
||||
}
|
||||
|
||||
static constexpr double VF_PassageShellThicknessMetres=0.75;
|
||||
static constexpr double VF_PassageWallReliefMetres=0.21;
|
||||
static constexpr double VF_PassageCornerBlendMetres=0.50;
|
||||
static constexpr double VF_PassageReachMarginMetres=0.01;
|
||||
// Radial support in metres: shell + maximum relief + the smooth-max support
|
||||
// (corner-blend width / 6) + a 1 cm guard for float and bound rounding.
|
||||
static constexpr double VF_PassageInfluenceReachMetres=
|
||||
VF_PassageShellThicknessMetres+VF_PassageWallReliefMetres
|
||||
+VF_PassageCornerBlendMetres/6.0+VF_PassageReachMarginMetres;
|
||||
|
||||
static double VF_PassageInfluenceReachCoreVoxels(
|
||||
std::uint32_t coreVoxelSizeMicrometres)
|
||||
{
|
||||
return VF_PassageInfluenceReachMetres*1000000.0
|
||||
/std::max<std::uint32_t>(1,coreVoxelSizeMicrometres);
|
||||
}
|
||||
|
||||
enum class EPassageDensityQueryCaller : std::size_t
|
||||
@@ -306,7 +323,7 @@ public:
|
||||
profileStats_(profileStats),profileLod_(profileLod),
|
||||
passageCarveReachMetres_(std::isfinite(passageCarveReachMetres)
|
||||
&&passageCarveReachMetres>=0.0?passageCarveReachMetres
|
||||
:VF_L93PassageCarveReachMetres())
|
||||
:VF_PassageInfluenceReachMetres)
|
||||
{
|
||||
mayContainResolvedBoundaryBand_=shape.ResolvedBoundaryBandsIntersectSubvoxelRange(
|
||||
minimumSubvoxels_[2],maximumSubvoxels_[2]);
|
||||
@@ -1204,8 +1221,8 @@ public:
|
||||
if(!ResolvedLayout||ResolvedLayout->passages.empty()||!std::isfinite(X)
|
||||
||!std::isfinite(Y)||!std::isfinite(Z))return density;
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr double FloorShellThicknessMetres=0.75;
|
||||
constexpr double WallReliefMetres=0.21;
|
||||
constexpr double FloorShellThicknessMetres=VF_PassageShellThicknessMetres;
|
||||
constexpr double WallReliefMetres=VF_PassageWallReliefMetres;
|
||||
constexpr double OpenAirSideWallHeightMetres=1.0;
|
||||
constexpr double MouthShellBlendMetres=5.0;
|
||||
const double qPerMetre=Q*1000000.0/
|
||||
@@ -1630,8 +1647,8 @@ public:
|
||||
{
|
||||
constexpr double Q=static_cast<double>(
|
||||
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double qPerMetre=Q*1000000.0/
|
||||
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const double reachCoreVoxels=VF_PassageInfluenceReachCoreVoxels(
|
||||
ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const auto overlaps=[&](const double low[3],const double high[3])
|
||||
{
|
||||
for(int axis=0;axis<3;++axis)
|
||||
@@ -1648,7 +1665,8 @@ public:
|
||||
const auto& p3=passage.controlPoints[
|
||||
std::min(segment+2,passage.controlPoints.size()-1)];
|
||||
const double radius=std::max(static_cast<double>(a.radiusSubvoxels),
|
||||
static_cast<double>(b.radiusSubvoxels))/Q+1.0;
|
||||
static_cast<double>(b.radiusSubvoxels))/Q
|
||||
+reachCoreVoxels+0.01;
|
||||
double low[3]{},high[3]{};
|
||||
for(int axis=0;axis<3;++axis)
|
||||
{
|
||||
@@ -3499,7 +3517,7 @@ private:
|
||||
double centerZSubvoxels,double queryZSubvoxels,double floorDepthSubvoxels,
|
||||
double qPerMetre)
|
||||
{
|
||||
constexpr double CornerBlendMetres=0.50;
|
||||
constexpr double CornerBlendMetres=VF_PassageCornerBlendMetres;
|
||||
const float floorDistance=static_cast<float>(centerZSubvoxels
|
||||
-floorDepthSubvoxels-queryZSubvoxels);
|
||||
return SmoothMaxDistance(roundDistanceSubvoxels,floorDistance,
|
||||
@@ -4811,13 +4829,9 @@ private:
|
||||
PassageNearestNodes.resize(ResolvedLayout?ResolvedLayout->passages.size():0);
|
||||
if(!ResolvedLayout||ResolvedLayout->passages.empty())return;
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr double PassageShellMetres=0.75;
|
||||
constexpr double ReliefMetres=0.21;
|
||||
constexpr double SweepReachMetres=4.0;
|
||||
constexpr double CornerBlendMetres=0.50;
|
||||
constexpr std::size_t CandidateFineSamplesPerSpan=5;
|
||||
const double requiredReach=std::max(SweepReachMetres+ReliefMetres
|
||||
+CornerBlendMetres*0.25,PassageShellMetres+ReliefMetres);
|
||||
const double requiredReachCoreVoxels=VF_PassageInfluenceReachCoreVoxels(
|
||||
ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
|
||||
++passageIndex)
|
||||
{
|
||||
@@ -4854,7 +4868,7 @@ private:
|
||||
routeMinimum[axis]=std::min(routeMinimum[axis],localMinimum[axis]);
|
||||
routeMaximum[axis]=std::max(routeMaximum[axis],localMaximum[axis]);
|
||||
}
|
||||
const double localExtent=requiredReach
|
||||
const double localExtent=requiredReachCoreVoxels
|
||||
+std::max(prepared.radiusA,prepared.radiusB)/Q+0.01;
|
||||
FPassageProofBound proof;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
@@ -4872,7 +4886,8 @@ private:
|
||||
std::ceil((localMaximum[axis]+localExtent)*Q));
|
||||
}
|
||||
}
|
||||
const double routeExtent=maximumRadiusSubvoxels/Q+requiredReach+0.01;
|
||||
const double routeExtent=maximumRadiusSubvoxels/Q
|
||||
+requiredReachCoreVoxels+0.01;
|
||||
const double tileExtent=routeExtent;
|
||||
std::array<std::int64_t,3> featureMinimum{},featureMaximum{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
@@ -4899,6 +4914,113 @@ private:
|
||||
BuildPassageFeatureNode(0,PassageFeatureOrder.size());
|
||||
if(!PassageProofBounds.empty())
|
||||
BuildPassageProofNode(0,PassageProofBounds.size());
|
||||
|
||||
if(VF_IsL130PassageReachProofAuditEnabled())
|
||||
{
|
||||
constexpr double Q=static_cast<double>(
|
||||
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr std::array<std::array<double,2>,8> RadialDirections={{
|
||||
{1.0,0.0},{0.7071067811865475,0.7071067811865475},
|
||||
{0.0,1.0},{-0.7071067811865475,0.7071067811865475},
|
||||
{-1.0,0.0},{-0.7071067811865475,-0.7071067811865475},
|
||||
{0.0,-1.0},{0.7071067811865475,-0.7071067811865475}}};
|
||||
constexpr std::array<double,2> CrestHeightsMetres={0.625,0.875};
|
||||
const double qPerMetre=Q*1000000.0/
|
||||
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const std::array<double,6> shellOffsetsMetres={0.70,0.78,0.86,0.94,
|
||||
VF_PassageInfluenceReachMetres-0.005,
|
||||
VF_PassageInfluenceReachMetres+0.005};
|
||||
std::uint64_t sampled=0,signRelevant=0,proofMisses=0,featureMisses=0;
|
||||
for(std::size_t passageIndex=0;
|
||||
passageIndex<PassageSegmentData.size();++passageIndex)
|
||||
{
|
||||
const auto& segments=PassageSegmentData[passageIndex];
|
||||
if(segments.empty())continue;
|
||||
const auto& constants=PassageComposerConstants[passageIndex];
|
||||
const std::size_t stride=std::max<std::size_t>(1,
|
||||
(segments.size()+127)/128);
|
||||
for(std::size_t segmentIndex=0;segmentIndex<segments.size();
|
||||
segmentIndex+=stride)
|
||||
{
|
||||
const auto& segment=segments[segmentIndex];
|
||||
if(segment.lengthSq<=1.0e-12)continue;
|
||||
constexpr double T=0.5;
|
||||
const double centerX=segment.a[0]+T*segment.delta[0];
|
||||
const double centerY=segment.a[1]+T*segment.delta[1];
|
||||
const double centerZ=segment.a[2]+T*segment.delta[2];
|
||||
const double radius=segment.radiusA+T*segment.radiusDelta;
|
||||
const double floorDepth=PassageFlatFloorDepthSubvoxels(
|
||||
constants,segmentIndex,T,radius,qPerMetre);
|
||||
const double floorCenterZ=PassageFlatFloorCenterZSubvoxels(
|
||||
constants,segments,segmentIndex,T,segment,centerX,centerY,
|
||||
floorDepth,qPerMetre);
|
||||
for(const double crestHeightMetres:CrestHeightsMetres)
|
||||
{
|
||||
const double sampleZ=floorCenterZ-floorDepth
|
||||
+crestHeightMetres*qPerMetre;
|
||||
const double verticalDelta=sampleZ-centerZ;
|
||||
for(const double shellOffsetMetres:shellOffsetsMetres)
|
||||
{
|
||||
const double radialDistance=radius
|
||||
+shellOffsetMetres*qPerMetre;
|
||||
if(radialDistance*radialDistance
|
||||
<=verticalDelta*verticalDelta)continue;
|
||||
const double horizontalDistance=std::sqrt(
|
||||
radialDistance*radialDistance
|
||||
-verticalDelta*verticalDelta);
|
||||
for(const auto& direction:RadialDirections)
|
||||
{
|
||||
const double sampleQ[3]={
|
||||
centerX+direction[0]*horizontalDistance,
|
||||
centerY+direction[1]*horizontalDistance,sampleZ};
|
||||
const double sampleVoxel[3]={sampleQ[0]/Q,
|
||||
sampleQ[1]/Q,sampleQ[2]/Q};
|
||||
const float contribution=ApplyResolvedStratePassageCarves(
|
||||
0.0f,static_cast<float>(sampleVoxel[0]),
|
||||
static_cast<float>(sampleVoxel[1]),
|
||||
static_cast<float>(sampleVoxel[2]));
|
||||
++sampled;
|
||||
if(std::abs(contribution)<=1.0e-6f)continue;
|
||||
++signRelevant;
|
||||
if(PassageProofNodes.empty()
|
||||
||!PassageProofBoundsOverlapNode(0,sampleVoxel,
|
||||
sampleVoxel))
|
||||
++proofMisses;
|
||||
std::array<std::int64_t,3> sampleMinimum{},sampleMaximum{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
sampleMinimum[axis]=static_cast<std::int64_t>(
|
||||
std::floor(sampleQ[axis]));
|
||||
sampleMaximum[axis]=static_cast<std::int64_t>(
|
||||
std::ceil(sampleQ[axis]));
|
||||
}
|
||||
const auto candidates=BuildPassageFeatureTileList(
|
||||
sampleMinimum,sampleMaximum);
|
||||
const bool featureContainsSample=std::any_of(
|
||||
candidates.begin(),candidates.end(),
|
||||
[&](std::uint32_t featureIndex)
|
||||
{
|
||||
return featureIndex<PassageTileFeatures.size()
|
||||
&&PassageTileFeatures[featureIndex].passageIndex
|
||||
==passageIndex;
|
||||
});
|
||||
if(!featureContainsSample)++featureMisses;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool auditPassed=sampled>0&&signRelevant>0
|
||||
&&proofMisses==0&&featureMisses==0;
|
||||
std::printf("[L130PassageReachProofAudit] reach_m=%.3f sampled=%llu sign_relevant=%llu proof_misses=%llu feature_misses=%llu status=%s\n",
|
||||
VF_PassageInfluenceReachMetres,
|
||||
static_cast<unsigned long long>(sampled),
|
||||
static_cast<unsigned long long>(signRelevant),
|
||||
static_cast<unsigned long long>(proofMisses),
|
||||
static_cast<unsigned long long>(featureMisses),
|
||||
auditPassed?"PASS":"FAIL");
|
||||
if(!auditPassed)std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool PassageProofBoundsOverlapNode(int nodeIndex,const double minimum[3],
|
||||
@@ -11442,10 +11564,9 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
const FIntVector& origin,std::int32_t step,std::int32_t cells,std::uint32_t seed)
|
||||
{
|
||||
constexpr std::int64_t Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
constexpr double InfluenceMetres=4.0;
|
||||
constexpr double OpenAirSideWallHeightMetres=1.0;
|
||||
if(!shape_||step<=0||cells<=0||!std::isfinite(passageCarveReachMetres_)
|
||||
||std::abs(passageCarveReachMetres_-InfluenceMetres)>1.0e-9)return;
|
||||
||std::abs(passageCarveReachMetres_-VF_PassageInfluenceReachMetres)>1.0e-9)return;
|
||||
const bool profile=profileStats_!=nullptr;
|
||||
const auto prepareStart=profile?std::chrono::steady_clock::now()
|
||||
:std::chrono::steady_clock::time_point{};
|
||||
@@ -11459,7 +11580,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
sweep->dimension=cells+3;
|
||||
const std::size_t gridSampleCount=static_cast<std::size_t>(sweep->dimension)
|
||||
*static_cast<std::size_t>(sweep->dimension)*static_cast<std::size_t>(sweep->dimension);
|
||||
sweep->influenceSubvoxels=InfluenceMetres*static_cast<double>(Q)*1000000.0
|
||||
sweep->influenceSubvoxels=passageCarveReachMetres_*static_cast<double>(Q)*1000000.0
|
||||
/std::max<std::uint32_t>(1,shape_->ResolvedLayout
|
||||
?shape_->ResolvedLayout->coreVoxelSizeMicrometres:250000);
|
||||
sweep->passageToSweep.assign(passageRouteSegments_.size(),-1);
|
||||
@@ -11473,7 +11594,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
const double qPerMetre=static_cast<double>(Q)*1000000.0/
|
||||
std::max<std::uint32_t>(1,shape_->ResolvedLayout
|
||||
?shape_->ResolvedLayout->coreVoxelSizeMicrometres:250000);
|
||||
const double shellThickness=0.75*qPerMetre;
|
||||
const double shellThickness=VF_PassageShellThicknessMetres*qPerMetre;
|
||||
const double stampedOutsideDistance=sweep->influenceSubvoxels+1.0;
|
||||
constexpr double MouthShellBlendMetres=5.0;
|
||||
const double wallReliefShell=0.41*qPerMetre;
|
||||
@@ -11633,7 +11754,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
// more than cornerWidth/6. Skip its work for pairs outside this exact
|
||||
// support, with a small subvoxel guard for float rounding. The final
|
||||
// shell union only consumes negative distances.
|
||||
constexpr double CornerBlendMetres=0.50;
|
||||
constexpr double CornerBlendMetres=VF_PassageCornerBlendMetres;
|
||||
const float roundDistanceFloat=static_cast<float>(signedDistance);
|
||||
const float floorDistanceFloat=static_cast<float>(floorCenterZ-floorDepth-qz);
|
||||
const double flatDistanceLowerBound=std::max(
|
||||
|
||||
Reference in new issue
Block a user