passages: floor profile by nearest centreline + metric distance outside voids (L114, v222) - WIP
Floor ribs are present in L109/L112/L113 rock tunnels too: the floor height comes from the nearest segment, so it jumps (up to 5.9-7.3 cm) where the nearest segment switches. v222 reduces them; faint ribs remain. Quiet-machine timing (load 0.64): near-ready 9-17% slower than L109 (FI W12 0.95 -> 1.10 s); candidate totals ~5x L109. Fast tier FAIL: 2 SW wall-shell probes of 31,381 read host air. Walk/grade audits pass; Clang/GCC deterministic; UE builds. 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
36d954cf2f
commit
5e875ae504
45 files changed
+3081
-53
No files matched your search
@@ -266,8 +266,10 @@ public:
|
||||
double roundSignedDistance=std::numeric_limits<double>::infinity();
|
||||
double centerZSubvoxels=0.0,floorCenterZSubvoxels=0.0;
|
||||
double radiusSubvoxels=0.0,floorDepthSubvoxels=0.0;
|
||||
double t=0.0;
|
||||
double floorCenterDistanceSq=std::numeric_limits<double>::infinity();
|
||||
double t=0.0,floorT=0.0;
|
||||
std::uint32_t segmentIndex=std::numeric_limits<std::uint32_t>::max();
|
||||
std::uint32_t floorSegmentIndex=std::numeric_limits<std::uint32_t>::max();
|
||||
float wallRelief=0.0f;
|
||||
bool valid=false,hasWallRelief=false;
|
||||
};
|
||||
@@ -1216,8 +1218,9 @@ public:
|
||||
double signedDistance=std::numeric_limits<double>::infinity();
|
||||
double shellSignedDistance=std::numeric_limits<double>::infinity();
|
||||
double roundSignedDistance=std::numeric_limits<double>::infinity();
|
||||
double centerZ=0.0,floorCenterZ=0.0,radius=0.0,floorDepth=0.0,t=0.0;
|
||||
std::size_t segmentIndex=0;
|
||||
double centerZ=0.0,floorCenterZ=0.0,radius=0.0,floorDepth=0.0;
|
||||
double t=0.0,floorT=0.0;
|
||||
std::size_t segmentIndex=0,floorSegmentIndex=0;
|
||||
bool valid=false;
|
||||
const FScopedPassageTileQueries::FPassageSweepSample* swept=nullptr;
|
||||
};
|
||||
@@ -1255,6 +1258,8 @@ public:
|
||||
nearest.floorCenterZ=swept->floorCenterZSubvoxels;
|
||||
nearest.radius=swept->radiusSubvoxels;
|
||||
nearest.floorDepth=swept->floorDepthSubvoxels;
|
||||
nearest.floorSegmentIndex=swept->floorSegmentIndex;
|
||||
nearest.floorT=swept->floorT;
|
||||
nearest.valid=true;
|
||||
nearest.swept=swept;
|
||||
}
|
||||
@@ -1273,6 +1278,7 @@ public:
|
||||
const std::ptrdiff_t lastIndex=static_cast<std::ptrdiff_t>(
|
||||
PassageSegmentData[passageIndex].size())-1;
|
||||
double bestDistance=std::numeric_limits<double>::infinity();
|
||||
double bestFloorCenterDistanceSq=std::numeric_limits<double>::infinity();
|
||||
const auto visitSegment=[&](std::size_t segmentIndex,int)
|
||||
{
|
||||
if(passageProfile)++passageProfile->passageCarveSegmentVisits;
|
||||
@@ -1289,6 +1295,7 @@ public:
|
||||
const double ex=qx-cx,ey=qy-cy,ez=qz-cz;
|
||||
const double roundDistance=DeterministicSqrt(
|
||||
ex*ex+ey*ey+ez*ez)-radius;
|
||||
const double floorCenterDistanceSq=ex*ex+ey*ey+ez*ez;
|
||||
const double floorT=segment.horizontalLengthSq>1.0e-12
|
||||
?std::clamp(((qx-segment.a[0])*segment.delta[0]
|
||||
+(qy-segment.a[1])*segment.delta[1])
|
||||
@@ -1300,24 +1307,43 @@ public:
|
||||
constants,segmentIndex,floorT,segment,qx,qy,floorDepth,qPerMetre);
|
||||
const double flatDistance=PassageFlatBottomDistance(
|
||||
static_cast<float>(roundDistance),floorCenterZ,qz,floorDepth,qPerMetre);
|
||||
if(flatDistance<bestDistance
|
||||
||(flatDistance==bestDistance
|
||||
// Radius-based tube selection can favor a wider downhill segment
|
||||
// and carry its lower floor plane backward. Select the floor profile
|
||||
// by the centerline position, independently of tube radius.
|
||||
if(floorCenterDistanceSq<bestFloorCenterDistanceSq
|
||||
||(floorCenterDistanceSq==bestFloorCenterDistanceSq
|
||||
&&segmentIndex<nearest.floorSegmentIndex))
|
||||
{
|
||||
bestFloorCenterDistanceSq=floorCenterDistanceSq;
|
||||
nearest.floorCenterZ=floorCenterZ;
|
||||
nearest.floorDepth=floorDepth;
|
||||
nearest.floorSegmentIndex=segmentIndex;
|
||||
nearest.floorT=floorT;
|
||||
}
|
||||
// Select the tube segment before applying its floor plane. Taking the
|
||||
// minimum of each segment's flattened distance lets a lower, downhill
|
||||
// floor plane extend backward through the overlapping tube and makes
|
||||
// the floor jump at the prepared-segment spacing.
|
||||
if(roundDistance<bestDistance
|
||||
||(roundDistance==bestDistance
|
||||
&&(!nearest.valid||segmentIndex<nearest.segmentIndex)))
|
||||
{
|
||||
bestDistance=flatDistance;
|
||||
bestDistance=roundDistance;
|
||||
nearest.signedDistance=flatDistance;
|
||||
nearest.roundSignedDistance=roundDistance;
|
||||
nearest.segmentIndex=segmentIndex;
|
||||
nearest.t=t;
|
||||
nearest.centerZ=cz;
|
||||
nearest.floorCenterZ=floorCenterZ;
|
||||
nearest.radius=radius;
|
||||
nearest.floorDepth=floorDepth;
|
||||
nearest.valid=true;
|
||||
}
|
||||
};
|
||||
VisitNearestPassageSegmentRange(passageIndex,0,lastIndex-1,
|
||||
qx,qy,qz,0,bestDistance,visitSegment);
|
||||
qx,qy,qz,0,bestDistance,bestFloorCenterDistanceSq,visitSegment);
|
||||
if(nearest.valid)
|
||||
nearest.signedDistance=PassageFlatBottomDistance(
|
||||
static_cast<float>(nearest.roundSignedDistance),
|
||||
nearest.floorCenterZ,qz,nearest.floorDepth,qPerMetre);
|
||||
|
||||
// Shells form a union over nearby route segments, even when another
|
||||
// segment supplies the nearest interior distance. The hierarchy bound
|
||||
@@ -1388,8 +1414,10 @@ public:
|
||||
nearest.shellSignedDistance=std::min(nearest.shellSignedDistance,
|
||||
localShellDistance);
|
||||
};
|
||||
double shellFloorSearchBoundSq=-1.0;
|
||||
VisitNearestPassageSegmentRange(passageIndex,0,lastIndex-1,
|
||||
qx,qy,qz,0,shellSearchBound,visitShellSegment);
|
||||
qx,qy,qz,0,shellSearchBound,shellFloorSearchBoundSq,
|
||||
visitShellSegment);
|
||||
}
|
||||
if(!nearest.valid)continue;
|
||||
const double wallElevationMetres=(qz-(nearest.floorCenterZ
|
||||
@@ -1420,6 +1448,7 @@ public:
|
||||
static_cast<float>(roughRoundDistance),nearest.floorCenterZ,qz,
|
||||
nearest.floorDepth,qPerMetre);
|
||||
const auto& segment=PassageSegmentData[passageIndex][nearest.segmentIndex];
|
||||
const auto& floorSegment=PassageSegmentData[passageIndex][nearest.floorSegmentIndex];
|
||||
const double alongMetres=segment.authoredAlongMetres
|
||||
+nearest.t*segment.authoredLengthMetres;
|
||||
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
|
||||
@@ -1437,9 +1466,11 @@ public:
|
||||
const double mouthWallThickness=segment.routeSegment
|
||||
?smoothShellFade*shellThickness:0.0;
|
||||
double floorExtensionFade=1.0;
|
||||
if(!segment.routeSegment)
|
||||
if(!floorSegment.routeSegment)
|
||||
{
|
||||
const double pastMouthMetres=std::max(0.0,alongMetres
|
||||
const double floorAlongMetres=floorSegment.authoredAlongMetres
|
||||
+nearest.floorT*floorSegment.authoredLengthMetres;
|
||||
const double pastMouthMetres=std::max(0.0,floorAlongMetres
|
||||
-constants.totalPathLengthMetres);
|
||||
const double extensionFade=std::clamp(1.0
|
||||
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
|
||||
@@ -1461,31 +1492,22 @@ public:
|
||||
}
|
||||
|
||||
if(!std::isfinite(unionDistance))return density;
|
||||
// The union's interior is unconditional air. Its one exterior shell is
|
||||
// solid only in air, so it leaves host rock unchanged. Taking the union
|
||||
// before writing either polarity makes every tunnel interior beat every
|
||||
// overlapping tunnel shell.
|
||||
// Preserve both native graph air and passage interior before any shell
|
||||
// can change their sign. If they overlap, their positive air fields combine.
|
||||
const float passageAirDistance=static_cast<float>(-unionDistance/Q);
|
||||
// Air is the union of the native graph and passage voids. Keeping the
|
||||
// signed passage distance outside the void as well gives marching cubes
|
||||
// a real negative distance through the floor instead of the terrain's
|
||||
// saturated deep-rock value.
|
||||
if(nativeTunnelFieldDensity>=0.0f)
|
||||
{
|
||||
const float passageAir=unionDistance<=0.0
|
||||
?static_cast<float>(-unionDistance/Q)
|
||||
:std::numeric_limits<float>::lowest();
|
||||
return std::max(nativeTunnelFieldDensity,passageAir);
|
||||
}
|
||||
return std::max(nativeTunnelFieldDensity,passageAirDistance);
|
||||
if(nativeFloorSupport!=std::numeric_limits<float>::lowest()
|
||||
&&nativeFloorSupport<0.0f)
|
||||
return std::min(nativeFloorSupport,unionDistance<=0.0
|
||||
?static_cast<float>(-unionDistance/Q):density);
|
||||
?passageAirDistance:density);
|
||||
if(nativeTunnelFieldDensity!=std::numeric_limits<float>::lowest())
|
||||
return unionDistance<=0.0
|
||||
?static_cast<float>(-unionDistance/Q):density;
|
||||
if(unionDistance<=0.0)
|
||||
return static_cast<float>(-unionDistance/Q);
|
||||
if(unionShellDistance<0.0&&density>=0.0f)
|
||||
return std::max(density,passageAirDistance);
|
||||
if(unionDistance>0.0&&unionShellDistance<0.0&&density>=0.0f)
|
||||
return static_cast<float>(unionShellDistance/Q);
|
||||
return density;
|
||||
return std::max(density,passageAirDistance);
|
||||
}
|
||||
|
||||
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
|
||||
@@ -4303,7 +4325,8 @@ private:
|
||||
template<class FVisitor>
|
||||
void VisitNearestPassageSegmentRange(std::size_t passageIndex,
|
||||
std::ptrdiff_t first,std::ptrdiff_t last,double qx,double qy,double qz,
|
||||
int priority,double& bestDistance,FVisitor&& visitor) const
|
||||
int priority,double& bestDistance,double& bestCenterDistanceSq,
|
||||
FVisitor&& visitor) const
|
||||
{
|
||||
(void)priority;
|
||||
if(first>last||passageIndex>=PassageNearestNodes.size()
|
||||
@@ -4322,13 +4345,22 @@ private:
|
||||
return DeterministicSqrt(dx*dx+dy*dy+dz*dz)-node.maximumRadius
|
||||
-SmoothDistanceSlackMetres*qPerMetre;
|
||||
};
|
||||
const auto centerDistanceLowerBoundSq=[&](const FPassageNearestNode& node)
|
||||
{
|
||||
const double dx=std::max({node.minimum[0]-qx,0.0,qx-node.maximum[0]});
|
||||
const double dy=std::max({node.minimum[1]-qy,0.0,qy-node.maximum[1]});
|
||||
const double dz=std::max({node.minimum[2]-qz,0.0,qz-node.maximum[2]});
|
||||
return dx*dx+dy*dy+dz*dz;
|
||||
};
|
||||
const auto visit=[&](auto&& self,int nodeIndex)->void
|
||||
{
|
||||
if(nodeIndex<0)return;
|
||||
const auto& node=nodes[static_cast<std::size_t>(nodeIndex)];
|
||||
if(static_cast<std::ptrdiff_t>(node.maximumSegment)<first
|
||||
||static_cast<std::ptrdiff_t>(node.minimumSegment)>last
|
||||
||lowerBound(node)>bestDistance)return;
|
||||
||(lowerBound(node)>bestDistance
|
||||
&&(bestCenterDistanceSq<0.0
|
||||
||centerDistanceLowerBoundSq(node)>bestCenterDistanceSq)))return;
|
||||
if(node.left<0)
|
||||
{
|
||||
for(const std::size_t segment:node.segments)
|
||||
@@ -11430,6 +11462,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
const double ex=qx-cx,ey=qy-cy,ez=qz-cz;
|
||||
const double signedDistance=FStandaloneShapeWorld::DeterministicSqrt(
|
||||
ex*ex+ey*ey+ez*ez)-radius;
|
||||
const double floorCenterDistanceSq=ex*ex+ey*ey+ez*ez;
|
||||
const double floorT=segment.horizontalLengthSq>1.0e-12
|
||||
?std::clamp(projectionXY/segment.horizontalLengthSq,0.0,1.0):t;
|
||||
const double floorRadius=segment.radiusA+floorT*segment.radiusDelta;
|
||||
@@ -11450,6 +11483,17 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
*static_cast<std::size_t>(sweep->dimension)+static_cast<std::size_t>(gy))
|
||||
*static_cast<std::size_t>(sweep->dimension)+static_cast<std::size_t>(gx);
|
||||
auto& sample=passageSamples.samples[sampleIndex];
|
||||
if(floorCenterDistanceSq<sample.floorCenterDistanceSq
|
||||
||(floorCenterDistanceSq==sample.floorCenterDistanceSq
|
||||
&&candidate.segmentIndex<sample.floorSegmentIndex))
|
||||
{
|
||||
sample.floorCenterDistanceSq=floorCenterDistanceSq;
|
||||
sample.floorCenterZSubvoxels=floorCenterZ;
|
||||
sample.floorDepthSubvoxels=floorDepth;
|
||||
sample.floorT=floorT;
|
||||
sample.floorSegmentIndex=static_cast<std::uint32_t>(
|
||||
candidate.segmentIndex);
|
||||
}
|
||||
const double alongMetres=segment.authoredAlongMetres
|
||||
+t*segment.authoredLengthMetres;
|
||||
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
|
||||
@@ -11488,16 +11532,16 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
floorShellDistance);
|
||||
sample.shellSignedDistance=std::min(sample.shellSignedDistance,
|
||||
localShellDistance);
|
||||
if(!sample.valid||flatDistance<sample.signedDistance
|
||||
||(flatDistance==sample.signedDistance
|
||||
// Rank the tube body by round SDF. Its floor plane is selected
|
||||
// independently above from the nearest centerline position.
|
||||
if(!sample.valid||signedDistance<sample.roundSignedDistance
|
||||
||(signedDistance==sample.roundSignedDistance
|
||||
&&candidate.segmentIndex<sample.segmentIndex))
|
||||
{
|
||||
sample.signedDistance=flatDistance;
|
||||
sample.roundSignedDistance=signedDistance;
|
||||
sample.centerZSubvoxels=cz;
|
||||
sample.floorCenterZSubvoxels=floorCenterZ;
|
||||
sample.radiusSubvoxels=radius;
|
||||
sample.floorDepthSubvoxels=floorDepth;
|
||||
sample.t=t;
|
||||
sample.segmentIndex=static_cast<std::uint32_t>(candidate.segmentIndex);
|
||||
sample.valid=true;
|
||||
@@ -11532,6 +11576,9 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
+static_cast<std::int64_t>(gy)*sweep->stepSubvoxels);
|
||||
const double qz=static_cast<double>(sweep->gridMinimumSubvoxels[2]
|
||||
+static_cast<std::int64_t>(gz)*sweep->stepSubvoxels);
|
||||
sample.signedDistance=FStandaloneShapeWorld::PassageFlatBottomDistance(
|
||||
static_cast<float>(sample.roundSignedDistance),
|
||||
sample.floorCenterZSubvoxels,qz,sample.floorDepthSubvoxels,qPerMetre);
|
||||
const double radius=sample.radiusSubvoxels;
|
||||
const double centerZ=sample.centerZSubvoxels;
|
||||
if(sample.signedDistance<=sweep->influenceSubvoxels)
|
||||
|
||||
Reference in new issue
Block a user