passages: smoothed open-air shell joins (L110, v257) - partial, not merged
Bands = end profiles of per-control-span shell capsules (~9.35 m pitch = span spacing). Shell now smooth-unions adjacent spans over 6 m and reuses the interior chord distance; a 0.60 m clearance fades in 5-20 m from each mouth. Close-ups: bands reduced but still visible on part of the exposed shell. Cost vs L109: near-ready +8..+12% (FI 0.88 -> 1.00, TN 0.92 -> 1.01 s @W12). Audits, Clang/GCC determinism, fast tier and Linux UE build pass. Kept on its own branch. 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
5ea01988c4
commit
bf39712792
397 files changed
+7754
-128
No files matched your search
@@ -261,14 +261,17 @@ public:
|
||||
struct FPassageSweepSample
|
||||
{
|
||||
double signedDistance=std::numeric_limits<double>::infinity();
|
||||
double shellSignedDistance=std::numeric_limits<double>::infinity();
|
||||
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;
|
||||
std::uint32_t segmentIndex=std::numeric_limits<std::uint32_t>::max();
|
||||
double shellSignedDistance=std::numeric_limits<double>::infinity();
|
||||
std::uint32_t shellSegmentIndex=std::numeric_limits<std::uint32_t>::max();
|
||||
double previousShellDistance=std::numeric_limits<double>::infinity();
|
||||
double previousShellBlendWeight=0.0;
|
||||
float wallRelief=0.0f;
|
||||
bool valid=false,hasWallRelief=false;
|
||||
bool valid=false,shellValid=false,hasWallRelief=false;
|
||||
};
|
||||
struct FPassageTileSweepData
|
||||
{
|
||||
@@ -1195,13 +1198,16 @@ public:
|
||||
||!std::isfinite(Y)||!std::isfinite(Z))return density;
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr double FloorShellThicknessMetres=0.75;
|
||||
constexpr double WallShellThicknessMetres=0.75;
|
||||
constexpr double ShellClearanceMetres=0.60;
|
||||
constexpr double WallReliefMetres=0.21;
|
||||
constexpr double MouthShellBlendMetres=5.0;
|
||||
const double qPerMetre=Q*1000000.0/
|
||||
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const double qx=static_cast<double>(X)*Q,qy=static_cast<double>(Y)*Q,
|
||||
qz=static_cast<double>(Z)*Q;
|
||||
const double shellThickness=FloorShellThicknessMetres*qPerMetre;
|
||||
const double floorShellThicknessBase=FloorShellThicknessMetres*qPerMetre;
|
||||
const double wallShellThickness=WallShellThicknessMetres*qPerMetre;
|
||||
const auto* passageTileQueries=ActivePassageTileQueries;
|
||||
const bool passageTileScopeMatches=passageTileQueries
|
||||
&&passageTileQueries->matches(*this);
|
||||
@@ -1212,7 +1218,6 @@ public:
|
||||
struct FNearestTubeSample
|
||||
{
|
||||
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;
|
||||
@@ -1232,7 +1237,7 @@ public:
|
||||
continue;
|
||||
const auto& constants=PassageComposerConstants[passageIndex];
|
||||
|
||||
FNearestTubeSample nearest;
|
||||
FNearestTubeSample nearest,nearestShell;
|
||||
const bool usePreparedSweep=passageTileScopeMatches
|
||||
&&passageTileQueries->hasSweptTubeBase();
|
||||
const auto* swept=usePreparedSweep
|
||||
@@ -1240,7 +1245,6 @@ public:
|
||||
if(swept)
|
||||
{
|
||||
nearest.signedDistance=swept->signedDistance;
|
||||
nearest.shellSignedDistance=swept->shellSignedDistance;
|
||||
nearest.roundSignedDistance=swept->roundSignedDistance;
|
||||
nearest.segmentIndex=swept->segmentIndex;
|
||||
nearest.t=swept->t;
|
||||
@@ -1250,6 +1254,10 @@ public:
|
||||
nearest.floorDepth=swept->floorDepthSubvoxels;
|
||||
nearest.valid=true;
|
||||
nearest.swept=swept;
|
||||
nearestShell.signedDistance=swept->shellSignedDistance;
|
||||
nearestShell.segmentIndex=swept->shellSegmentIndex;
|
||||
nearestShell.valid=swept->shellValid;
|
||||
nearestShell.swept=swept;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1265,7 +1273,17 @@ public:
|
||||
|
||||
const std::ptrdiff_t lastIndex=static_cast<std::ptrdiff_t>(
|
||||
passage.controlPoints.size())-1;
|
||||
constexpr double ShellJoinBlendMetres=6.00;
|
||||
double bestDistance=std::numeric_limits<double>::infinity();
|
||||
double bestShellSearchBound=std::numeric_limits<double>::infinity();
|
||||
struct FShellCandidate
|
||||
{
|
||||
double distance=std::numeric_limits<double>::infinity();
|
||||
std::size_t segmentIndex=0;
|
||||
double blendWeight=1.0;
|
||||
};
|
||||
std::array<FShellCandidate,8> shellCandidates{};
|
||||
std::size_t shellCandidateCount=0;
|
||||
const auto visitSegment=[&](std::size_t segmentIndex,int)
|
||||
{
|
||||
if(passageProfile)++passageProfile->passageCarveSegmentVisits;
|
||||
@@ -1275,13 +1293,12 @@ public:
|
||||
+(qy-segment.a[1])*segment.delta[1]
|
||||
+(qz-segment.a[2])*segment.delta[2];
|
||||
const double t=std::clamp(projection/segment.lengthSq,0.0,1.0);
|
||||
double cx=segment.a[0]+t*segment.delta[0];
|
||||
double cy=segment.a[1]+t*segment.delta[1];
|
||||
double cz=segment.a[2]+t*segment.delta[2];
|
||||
const double cx=segment.a[0]+t*segment.delta[0];
|
||||
const double cy=segment.a[1]+t*segment.delta[1];
|
||||
const double cz=segment.a[2]+t*segment.delta[2];
|
||||
const double radius=segment.radiusA+t*segment.radiusDelta;
|
||||
const double ex=qx-cx,ey=qy-cy,ez=qz-cz;
|
||||
const double roundDistance=DeterministicSqrt(
|
||||
ex*ex+ey*ey+ez*ez)-radius;
|
||||
const double roundDistance=DeterministicSqrt(ex*ex+ey*ey+ez*ez)-radius;
|
||||
const double floorT=segment.horizontalLengthSq>1.0e-12
|
||||
?std::clamp(((qx-segment.a[0])*segment.delta[0]
|
||||
+(qy-segment.a[1])*segment.delta[1])
|
||||
@@ -1308,15 +1325,11 @@ public:
|
||||
nearest.floorDepth=floorDepth;
|
||||
nearest.valid=true;
|
||||
}
|
||||
|
||||
};
|
||||
VisitNearestPassageSegmentRange(passageIndex,0,lastIndex-1,
|
||||
qx,qy,qz,0,bestDistance,visitSegment);
|
||||
|
||||
// Shells form a union over nearby route segments, even when another
|
||||
// segment supplies the nearest interior distance. The hierarchy bound
|
||||
// is deliberately wider than the shell so its radius and corner blend
|
||||
// cannot prune a segment that contributes a boundary shell.
|
||||
double shellSearchBound=2.0*qPerMetre;
|
||||
const auto visitShellSegment=[&](std::size_t segmentIndex,int)
|
||||
{
|
||||
const auto& segment=PassageSegmentData[passageIndex][segmentIndex];
|
||||
@@ -1329,8 +1342,9 @@ public:
|
||||
const double cy=segment.a[1]+t*segment.delta[1];
|
||||
const double cz=segment.a[2]+t*segment.delta[2];
|
||||
const double radius=segment.radiusA+t*segment.radiusDelta;
|
||||
const double ex=qx-cx,ey=qy-cy,ez=qz-cz;
|
||||
const double roundDistance=DeterministicSqrt(ex*ex+ey*ey+ez*ez)-radius;
|
||||
const double dx=qx-cx,dy=qy-cy,dz=qz-cz;
|
||||
const double roundDistance=DeterministicSqrt(dx*dx+dy*dy+dz*dz)
|
||||
-radius;
|
||||
const double floorT=segment.horizontalLengthSq>1.0e-12
|
||||
?std::clamp(((qx-segment.a[0])*segment.delta[0]
|
||||
+(qy-segment.a[1])*segment.delta[1])
|
||||
@@ -1341,13 +1355,14 @@ public:
|
||||
const double floorCenterZ=PassageFlatFloorCenterZSubvoxels(
|
||||
constants,segmentIndex,floorT,segment,floorDepth,qPerMetre);
|
||||
const double flatDistance=PassageFlatBottomDistance(
|
||||
static_cast<float>(roundDistance),floorCenterZ,qz,floorDepth,qPerMetre);
|
||||
static_cast<float>(roundDistance),floorCenterZ,qz,floorDepth,
|
||||
qPerMetre);
|
||||
const double alongMetres=segment.authoredAlongMetres
|
||||
+t*segment.authoredLengthMetres;
|
||||
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
|
||||
constants.totalPathLengthMetres-alongMetres));
|
||||
const double shellFade=std::clamp(mouthDistanceMetres/
|
||||
MouthShellBlendMetres,0.0,1.0);
|
||||
const double shellFade=std::clamp(mouthDistanceMetres
|
||||
/MouthShellBlendMetres,0.0,1.0);
|
||||
const double smoothShellFade=shellFade*shellFade
|
||||
*(3.0-2.0*shellFade);
|
||||
const double floorDistanceMetres=(floorCenterZ-floorDepth-qz)
|
||||
@@ -1355,32 +1370,91 @@ public:
|
||||
const double floorBlend=std::clamp(floorDistanceMetres/0.25,0.0,1.0);
|
||||
const double smoothFloorBlend=floorBlend*floorBlend
|
||||
*(3.0-2.0*floorBlend);
|
||||
const double floorAlongMetres=segment.authoredAlongMetres
|
||||
+floorT*segment.authoredLengthMetres;
|
||||
const double floorMouthDistanceMetres=std::max(0.0,
|
||||
std::min(floorAlongMetres,constants.totalPathLengthMetres
|
||||
-floorAlongMetres));
|
||||
const double floorShellFade=std::clamp(floorMouthDistanceMetres
|
||||
/MouthShellBlendMetres,0.0,1.0);
|
||||
const double smoothFloorShellFade=floorShellFade*floorShellFade
|
||||
*(3.0-2.0*floorShellFade);
|
||||
const double mouthWallThickness=segmentIndex
|
||||
<constants.routeEndControlIndex
|
||||
?smoothShellFade*shellThickness:0.0;
|
||||
?smoothShellFade*wallShellThickness:0.0;
|
||||
double floorExtensionFade=1.0;
|
||||
if(segmentIndex>=constants.routeEndControlIndex)
|
||||
{
|
||||
const double pastMouthMetres=std::max(0.0,alongMetres
|
||||
const double pastMouthMetres=std::max(0.0,floorAlongMetres
|
||||
-constants.totalPathLengthMetres);
|
||||
const double extensionFade=std::clamp(1.0
|
||||
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
|
||||
floorExtensionFade=extensionFade*extensionFade
|
||||
*(3.0-2.0*extensionFade);
|
||||
}
|
||||
const double floorShellThickness=smoothFloorBlend*shellThickness
|
||||
*floorExtensionFade*smoothShellFade;
|
||||
const double floorShellThickness=smoothFloorBlend
|
||||
*floorShellThicknessBase*floorExtensionFade
|
||||
*smoothFloorShellFade;
|
||||
const double shellClearanceT=std::clamp(
|
||||
(mouthDistanceMetres-5.0)/15.0,0.0,1.0);
|
||||
const double shellClearanceFade=shellClearanceT*shellClearanceT
|
||||
*(3.0-2.0*shellClearanceT);
|
||||
const double localThickness=std::max(mouthWallThickness,
|
||||
floorShellThickness);
|
||||
const double localShellDistance=std::max(-static_cast<double>(flatDistance),
|
||||
static_cast<double>(flatDistance)-localThickness);
|
||||
nearest.shellSignedDistance=std::min(nearest.shellSignedDistance,
|
||||
localShellDistance);
|
||||
floorShellThickness)+ShellClearanceMetres*qPerMetre
|
||||
*shellClearanceFade;
|
||||
const double joinBlendT=std::clamp(mouthDistanceMetres/15.0,
|
||||
0.0,1.0);
|
||||
const double joinBlendWeight=joinBlendT*joinBlendT
|
||||
*(3.0-2.0*joinBlendT);
|
||||
const double localOuterDistance=flatDistance-localThickness;
|
||||
FShellCandidate candidate{localOuterDistance,segmentIndex,
|
||||
joinBlendWeight};
|
||||
std::size_t insertAt=0;
|
||||
while(insertAt<shellCandidateCount
|
||||
&&(shellCandidates[insertAt].distance<candidate.distance
|
||||
||(shellCandidates[insertAt].distance==candidate.distance
|
||||
&&shellCandidates[insertAt].segmentIndex
|
||||
<candidate.segmentIndex)))++insertAt;
|
||||
if(insertAt<shellCandidates.size())
|
||||
{
|
||||
const std::size_t newCount=std::min(shellCandidateCount+1,
|
||||
shellCandidates.size());
|
||||
for(std::size_t move=newCount-1;move>insertAt;--move)
|
||||
shellCandidates[move]=shellCandidates[move-1];
|
||||
shellCandidates[insertAt]=candidate;
|
||||
shellCandidateCount=newCount;
|
||||
}
|
||||
bestShellSearchBound=std::min(bestShellSearchBound,
|
||||
localOuterDistance+wallShellThickness
|
||||
+ShellClearanceMetres*qPerMetre
|
||||
+(0.25+ShellJoinBlendMetres)*qPerMetre);
|
||||
};
|
||||
VisitNearestPassageSegmentRange(passageIndex,0,lastIndex-1,
|
||||
qx,qy,qz,0,shellSearchBound,visitShellSegment);
|
||||
qx,qy,qz,0,bestShellSearchBound,visitShellSegment);
|
||||
if(shellCandidateCount>0)
|
||||
{
|
||||
nearestShell.signedDistance=shellCandidates[0].distance;
|
||||
nearestShell.segmentIndex=shellCandidates[0].segmentIndex;
|
||||
for(std::size_t left=0;left<shellCandidateCount;++left)
|
||||
for(std::size_t right=left+1;right<shellCandidateCount;++right)
|
||||
{
|
||||
if(shellCandidates[right].segmentIndex
|
||||
!=shellCandidates[left].segmentIndex+1)continue;
|
||||
nearestShell.signedDistance=std::min(
|
||||
nearestShell.signedDistance,PassageSmoothMinDistance(
|
||||
shellCandidates[left].distance,
|
||||
shellCandidates[right].distance,
|
||||
ShellJoinBlendMetres*qPerMetre*std::min(
|
||||
shellCandidates[left].blendWeight,
|
||||
shellCandidates[right].blendWeight)));
|
||||
}
|
||||
nearestShell.valid=true;
|
||||
}
|
||||
|
||||
}
|
||||
if(!nearest.valid)continue;
|
||||
if(!nearest.valid||!nearestShell.valid)continue;
|
||||
nearestShell.signedDistance=std::max(
|
||||
-nearest.signedDistance,nearestShell.signedDistance);
|
||||
double distanceToCenter=std::max(1.0e-9,
|
||||
nearest.roundSignedDistance+nearest.radius);
|
||||
const double verticalWallAlignment=distanceToCenter>1.0e-9
|
||||
@@ -1402,43 +1476,9 @@ public:
|
||||
const double shapedDistance=PassageFlatBottomDistance(
|
||||
static_cast<float>(roughRoundDistance),nearest.floorCenterZ,qz,
|
||||
nearest.floorDepth,qPerMetre);
|
||||
const auto& segment=PassageSegmentData[passageIndex][nearest.segmentIndex];
|
||||
const double alongMetres=segment.authoredAlongMetres
|
||||
+nearest.t*segment.authoredLengthMetres;
|
||||
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
|
||||
constants.totalPathLengthMetres-alongMetres));
|
||||
const double shellFade=std::clamp(mouthDistanceMetres/
|
||||
MouthShellBlendMetres,0.0,1.0);
|
||||
const double smoothShellFade=shellFade*shellFade*(3.0-2.0*shellFade);
|
||||
// Ease both wall and floor shell into the host ground over five metres.
|
||||
// The floor shell remains below the floor plane throughout that blend.
|
||||
const double floorDistanceMetres=(nearest.floorCenterZ
|
||||
-nearest.floorDepth-qz)/qPerMetre;
|
||||
const double floorBlend=std::clamp(floorDistanceMetres/0.25,0.0,1.0);
|
||||
const double smoothFloorBlend=floorBlend*floorBlend
|
||||
*(3.0-2.0*floorBlend);
|
||||
const double mouthWallThickness=nearest.segmentIndex
|
||||
<constants.routeEndControlIndex
|
||||
?smoothShellFade*shellThickness:0.0;
|
||||
double floorExtensionFade=1.0;
|
||||
if(nearest.segmentIndex>=constants.routeEndControlIndex)
|
||||
{
|
||||
const double pastMouthMetres=std::max(0.0,alongMetres
|
||||
-constants.totalPathLengthMetres);
|
||||
const double extensionFade=std::clamp(1.0
|
||||
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
|
||||
floorExtensionFade=extensionFade*extensionFade
|
||||
*(3.0-2.0*extensionFade);
|
||||
}
|
||||
const double floorShellThickness=smoothFloorBlend*shellThickness
|
||||
*floorExtensionFade*smoothShellFade;
|
||||
const double localShellThickness=std::max(mouthWallThickness,
|
||||
floorShellThickness);
|
||||
unionDistance=std::min(unionDistance,static_cast<double>(shapedDistance));
|
||||
const double localShellDistance=std::max(-static_cast<double>(shapedDistance),
|
||||
static_cast<double>(shapedDistance)-localShellThickness);
|
||||
unionShellDistance=std::min(unionShellDistance,
|
||||
std::min(localShellDistance,nearest.shellSignedDistance));
|
||||
nearestShell.signedDistance);
|
||||
}
|
||||
|
||||
if(!std::isfinite(unionDistance))return density;
|
||||
@@ -1592,7 +1632,7 @@ 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+2.36;
|
||||
double low[3]{},high[3]{};
|
||||
for(int axis=0;axis<3;++axis)
|
||||
{
|
||||
@@ -3371,6 +3411,8 @@ private:
|
||||
struct FPassageSegmentData
|
||||
{
|
||||
std::array<double,3> a{},b{},delta{};
|
||||
std::array<std::array<double,4>,3> shellCurveCoefficients{};
|
||||
std::array<double,3> shellCurveMinimum{},shellCurveMaximum{};
|
||||
std::array<std::int64_t,3> sweepMinimumSubvoxels{},sweepMaximumSubvoxels{};
|
||||
double authoredCenterZA=0.0,authoredCenterZDelta=0.0;
|
||||
double authoredAlongMetres=0.0,authoredLengthMetres=0.0;
|
||||
@@ -3432,6 +3474,128 @@ private:
|
||||
return SmoothMaxDistance(roundDistanceSubvoxels,floorDistance,
|
||||
static_cast<float>(CornerBlendMetres*qPerMetre));
|
||||
}
|
||||
static double PassageSmoothMinDistance(double a,double b,double width)
|
||||
{
|
||||
if(!(width>0.0))return std::min(a,b);
|
||||
const double h=std::max(width-std::abs(a-b),0.0)/width;
|
||||
return std::min(a,b)-(h*h*h)*(width/6.0);
|
||||
}
|
||||
static double PassageCurveCoordinate(
|
||||
const std::array<double,4>& coefficients,double t)
|
||||
{
|
||||
return ((coefficients[3]*t+coefficients[2])*t+coefficients[1])*t
|
||||
+coefficients[0];
|
||||
}
|
||||
static double PassageCurveCoordinateExtended(
|
||||
const std::array<double,4>& coefficients,double t)
|
||||
{
|
||||
if(t<0.0)return coefficients[0]+coefficients[1]*t;
|
||||
if(t>1.0)
|
||||
{
|
||||
const double end=PassageCurveCoordinate(coefficients,1.0);
|
||||
const double tangent=3.0*coefficients[3]+2.0*coefficients[2]
|
||||
+coefficients[1];
|
||||
return end+(t-1.0)*tangent;
|
||||
}
|
||||
return PassageCurveCoordinate(coefficients,t);
|
||||
}
|
||||
static void PassageCurveEvaluateExtended(
|
||||
const std::array<double,4>& coefficients,double t,
|
||||
double& coordinate,double& tangent,double& curvature)
|
||||
{
|
||||
if(t<0.0)
|
||||
{
|
||||
coordinate=coefficients[0]+coefficients[1]*t;
|
||||
tangent=coefficients[1];
|
||||
curvature=0.0;
|
||||
}
|
||||
else if(t>1.0)
|
||||
{
|
||||
const double end=PassageCurveCoordinate(coefficients,1.0);
|
||||
tangent=3.0*coefficients[3]+2.0*coefficients[2]+coefficients[1];
|
||||
coordinate=end+(t-1.0)*tangent;
|
||||
curvature=0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
coordinate=PassageCurveCoordinate(coefficients,t);
|
||||
tangent=(3.0*coefficients[3]*t+2.0*coefficients[2])*t
|
||||
+coefficients[1];
|
||||
curvature=6.0*coefficients[3]*t+2.0*coefficients[2];
|
||||
}
|
||||
}
|
||||
static void PassageCurveAxisBounds(const std::array<double,4>& coefficients,
|
||||
double& minimum,double& maximum,double tMinimum=0.0,double tMaximum=1.0)
|
||||
{
|
||||
minimum=std::min(PassageCurveCoordinate(coefficients,tMinimum),
|
||||
PassageCurveCoordinate(coefficients,tMaximum));
|
||||
maximum=std::max(PassageCurveCoordinate(coefficients,tMinimum),
|
||||
PassageCurveCoordinate(coefficients,tMaximum));
|
||||
const double a=3.0*coefficients[3],b=2.0*coefficients[2],c=coefficients[1];
|
||||
const auto include=[&](double t)
|
||||
{
|
||||
if(t<=tMinimum||t>=tMaximum)return;
|
||||
const double value=PassageCurveCoordinate(coefficients,t);
|
||||
minimum=std::min(minimum,value);
|
||||
maximum=std::max(maximum,value);
|
||||
};
|
||||
if(std::abs(a)<=1.0e-18)
|
||||
{
|
||||
if(std::abs(b)>1.0e-18)include(-c/b);
|
||||
return;
|
||||
}
|
||||
const double discriminant=b*b-4.0*a*c;
|
||||
if(discriminant<0.0)return;
|
||||
const double root=DeterministicSqrt(discriminant);
|
||||
include((-b-root)/(2.0*a));
|
||||
include((-b+root)/(2.0*a));
|
||||
}
|
||||
static double PassageCatmullRoundDistanceSubvoxels(
|
||||
const std::vector<FPassageSegmentData>& segments,std::size_t segmentIndex,
|
||||
double qx,double qy,double qz,double extensionSubvoxels=0.0,
|
||||
double* outT=nullptr)
|
||||
{
|
||||
if(segmentIndex>=segments.size())return std::numeric_limits<double>::infinity();
|
||||
const auto& segment=segments[segmentIndex];
|
||||
if(segment.lengthSq<=0.0)return std::numeric_limits<double>::infinity();
|
||||
const double projection=(qx-segment.a[0])*segment.delta[0]
|
||||
+(qy-segment.a[1])*segment.delta[1]
|
||||
+(qz-segment.a[2])*segment.delta[2];
|
||||
const double tExtension=std::max(0.0,extensionSubvoxels)
|
||||
/std::max(1.0,segment.lengthSubvoxels);
|
||||
const double tMinimum=-tExtension,tMaximum=1.0+tExtension;
|
||||
double t=std::clamp(projection/segment.lengthSq,tMinimum,tMaximum);
|
||||
for(int iteration=0;iteration<4;++iteration)
|
||||
{
|
||||
double firstDerivative=0.0,secondDerivative=0.0;
|
||||
const double query[3]={qx,qy,qz};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const auto& c=segment.shellCurveCoefficients[axis];
|
||||
double center=0.0,tangent=0.0,curvature=0.0;
|
||||
PassageCurveEvaluateExtended(c,t,center,tangent,curvature);
|
||||
const double difference=center-query[axis];
|
||||
firstDerivative+=difference*tangent;
|
||||
secondDerivative+=tangent*tangent+difference*curvature;
|
||||
}
|
||||
if(std::abs(secondDerivative)<=1.0e-12)break;
|
||||
const double next=std::clamp(t-firstDerivative/secondDerivative,
|
||||
tMinimum,tMaximum);
|
||||
if(std::abs(next-t)<=1.0e-9){t=next;break;}
|
||||
t=next;
|
||||
}
|
||||
double distanceSq=0.0;
|
||||
const double query[3]={qx,qy,qz};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const auto& c=segment.shellCurveCoefficients[axis];
|
||||
const double center=PassageCurveCoordinateExtended(c,t);
|
||||
const double difference=query[axis]-center;
|
||||
distanceSq+=difference*difference;
|
||||
}
|
||||
if(outT)*outT=t;
|
||||
return DeterministicSqrt(distanceSq)-(segment.radiusA+t*segment.radiusDelta);
|
||||
}
|
||||
struct FPassageNearestNode
|
||||
{
|
||||
std::array<double,3> minimum{},maximum{};
|
||||
@@ -4232,10 +4396,10 @@ private:
|
||||
const auto& prepared=PassageSegmentData[passageIndex][segment];
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
node.minimum[axis]=std::min({node.minimum[axis],
|
||||
prepared.a[axis],prepared.b[axis]});
|
||||
node.maximum[axis]=std::max({node.maximum[axis],
|
||||
prepared.a[axis],prepared.b[axis]});
|
||||
node.minimum[axis]=std::min(node.minimum[axis],
|
||||
prepared.shellCurveMinimum[axis]);
|
||||
node.maximum[axis]=std::max(node.maximum[axis],
|
||||
prepared.shellCurveMaximum[axis]);
|
||||
}
|
||||
node.maximumRadius=std::max({node.maximumRadius,
|
||||
static_cast<double>(a.radiusSubvoxels),static_cast<double>(b.radiusSubvoxels)});
|
||||
@@ -4617,6 +4781,78 @@ private:
|
||||
static_cast<double>(passage.controlPoints[pointIndex].positionSubvoxels[2])
|
||||
+(-routeDepth+localTubeDepth)*qPerMetre;
|
||||
}
|
||||
std::vector<std::array<double,3>> shellControlPoints(
|
||||
segmentData.size()+1);
|
||||
for(std::size_t pointIndex=0;pointIndex<shellControlPoints.size();++pointIndex)
|
||||
{
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
shellControlPoints[pointIndex][axis]=axis==2
|
||||
?effectiveCenterZSubvoxels[pointIndex]
|
||||
:static_cast<double>(passage.controlPoints[pointIndex]
|
||||
.positionSubvoxels[axis]);
|
||||
}
|
||||
const auto authoredShellControlPoints=shellControlPoints;
|
||||
for(std::size_t pointIndex=1;pointIndex+1<shellControlPoints.size();
|
||||
++pointIndex)
|
||||
{
|
||||
const double previousSpan=authoredPathDistanceMetres[pointIndex]
|
||||
-authoredPathDistanceMetres[pointIndex-1];
|
||||
const double nextSpan=authoredPathDistanceMetres[pointIndex+1]
|
||||
-authoredPathDistanceMetres[pointIndex];
|
||||
if(previousSpan>20.0||nextSpan>20.0)continue;
|
||||
const double alongMetres=authoredPathDistanceMetres[pointIndex];
|
||||
const double mouthDistance=std::max(0.0,std::min(alongMetres,
|
||||
authoredTotalPathLengthMetres-alongMetres));
|
||||
const double fadeT=std::clamp((mouthDistance-80.0)/40.0,0.0,1.0);
|
||||
const double fade=fadeT*fadeT*(3.0-2.0*fadeT);
|
||||
const double neighborWeight=0.10*fade;
|
||||
for(std::size_t axis=0;axis<2;++axis)
|
||||
shellControlPoints[pointIndex][axis]=
|
||||
(1.0-2.0*neighborWeight)
|
||||
*authoredShellControlPoints[pointIndex][axis]
|
||||
+neighborWeight*authoredShellControlPoints[pointIndex-1][axis]
|
||||
+neighborWeight*authoredShellControlPoints[pointIndex+1][axis];
|
||||
}
|
||||
std::vector<std::array<double,3>> shellSecondDerivatives(
|
||||
shellControlPoints.size());
|
||||
const std::size_t interiorPointCount=shellControlPoints.size()-2;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
std::vector<double> diagonal(interiorPointCount);
|
||||
std::vector<double> upper(interiorPointCount);
|
||||
std::vector<double> lower(interiorPointCount);
|
||||
std::vector<double> rhs(interiorPointCount);
|
||||
for(std::size_t row=0;row<interiorPointCount;++row)
|
||||
{
|
||||
const std::size_t pointIndex=row+1;
|
||||
const double previousSpan=authoredPathDistanceMetres[pointIndex]
|
||||
-authoredPathDistanceMetres[pointIndex-1];
|
||||
const double nextSpan=authoredPathDistanceMetres[pointIndex+1]
|
||||
-authoredPathDistanceMetres[pointIndex];
|
||||
lower[row]=previousSpan;
|
||||
diagonal[row]=2.0*(previousSpan+nextSpan);
|
||||
upper[row]=nextSpan;
|
||||
rhs[row]=6.0*((shellControlPoints[pointIndex+1][axis]
|
||||
-shellControlPoints[pointIndex][axis])/nextSpan
|
||||
-(shellControlPoints[pointIndex][axis]
|
||||
-shellControlPoints[pointIndex-1][axis])/previousSpan);
|
||||
}
|
||||
for(std::size_t row=1;row<interiorPointCount;++row)
|
||||
{
|
||||
const double factor=lower[row]/diagonal[row-1];
|
||||
diagonal[row]-=factor*upper[row-1];
|
||||
rhs[row]-=factor*rhs[row-1];
|
||||
}
|
||||
if(interiorPointCount>0)
|
||||
{
|
||||
shellSecondDerivatives[interiorPointCount][axis]=
|
||||
rhs[interiorPointCount-1]/diagonal[interiorPointCount-1];
|
||||
for(std::size_t row=interiorPointCount-1;row>0;--row)
|
||||
shellSecondDerivatives[row][axis]=(rhs[row-1]
|
||||
-upper[row-1]*shellSecondDerivatives[row+1][axis])
|
||||
/diagonal[row-1];
|
||||
}
|
||||
}
|
||||
for(std::size_t segmentIndex=0;segmentIndex<segmentData.size();++segmentIndex)
|
||||
{
|
||||
const auto& a=passage.controlPoints[segmentIndex];
|
||||
@@ -4649,6 +4885,44 @@ private:
|
||||
data.horizontalLength=DeterministicSqrt(data.horizontalLengthSq);
|
||||
++preparedSegments;
|
||||
}
|
||||
for(std::size_t segmentIndex=0;segmentIndex<segmentData.size();++segmentIndex)
|
||||
{
|
||||
auto& data=segmentData[segmentIndex];
|
||||
const bool hasLongNeighbor=(segmentIndex>0
|
||||
&&segmentData[segmentIndex-1].authoredLengthMetres>20.0)
|
||||
||(segmentIndex+1<segmentData.size()
|
||||
&&segmentData[segmentIndex+1].authoredLengthMetres>20.0);
|
||||
const bool useCurvedSpan=data.authoredLengthMetres<=20.0
|
||||
&&!hasLongNeighbor;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
if(!useCurvedSpan)
|
||||
{
|
||||
data.shellCurveCoefficients[axis]={data.a[axis],
|
||||
data.delta[axis],0.0,0.0};
|
||||
}
|
||||
else
|
||||
{
|
||||
const double span=data.authoredLengthMetres;
|
||||
const double p0=shellControlPoints[segmentIndex][axis];
|
||||
const double p1=shellControlPoints[segmentIndex+1][axis];
|
||||
const double m0=shellSecondDerivatives[segmentIndex][axis];
|
||||
const double m1=shellSecondDerivatives[segmentIndex+1][axis];
|
||||
data.shellCurveCoefficients[axis]={
|
||||
p0,p1-p0-span*span*(2.0*m0+m1)/6.0,
|
||||
span*span*m0/2.0,span*span*(m1-m0)/6.0};
|
||||
}
|
||||
PassageCurveAxisBounds(data.shellCurveCoefficients[axis],
|
||||
data.shellCurveMinimum[axis],data.shellCurveMaximum[axis]);
|
||||
// This tree also culls chord-based interior queries. Keep
|
||||
// the authored chord inside broad-phase bounds after the
|
||||
// shell-only centerline is smoothed.
|
||||
data.shellCurveMinimum[axis]=std::min(data.shellCurveMinimum[axis],
|
||||
std::min(data.a[axis],data.b[axis]));
|
||||
data.shellCurveMaximum[axis]=std::max(data.shellCurveMaximum[axis],
|
||||
std::max(data.a[axis],data.b[axis]));
|
||||
}
|
||||
}
|
||||
constants.totalPathLengthMetres=authoredTotalPathLengthMetres;
|
||||
PassageSegmentOffsets[passageIndex+1]=preparedSegments;
|
||||
}
|
||||
@@ -4675,7 +4949,7 @@ 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 PassageShellMetres=2.35;
|
||||
constexpr double ReliefMetres=0.21;
|
||||
constexpr double SweepReachMetres=4.0;
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
|
||||
@@ -4698,8 +4972,8 @@ private:
|
||||
std::array<double,3> routeMinimum{},routeMaximum{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
routeMinimum[axis]=std::min(prepared.a[axis],prepared.b[axis])/Q;
|
||||
routeMaximum[axis]=std::max(prepared.a[axis],prepared.b[axis])/Q;
|
||||
routeMinimum[axis]=prepared.shellCurveMinimum[axis]/Q;
|
||||
routeMaximum[axis]=prepared.shellCurveMaximum[axis]/Q;
|
||||
}
|
||||
const double routeExtent=maximumRadius+PassageShellMetres
|
||||
+ReliefMetres+0.01;
|
||||
@@ -11286,6 +11560,11 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
{
|
||||
constexpr std::int64_t Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
constexpr double InfluenceMetres=4.0;
|
||||
constexpr double FloorShellThicknessMetres=0.75;
|
||||
constexpr double WallShellThicknessMetres=0.75;
|
||||
constexpr double ShellClearanceMetres=0.60;
|
||||
constexpr double MouthShellBlendMetres=5.0;
|
||||
constexpr double ShellJoinBlendMetres=6.00;
|
||||
if(!shape_||step<=0||cells<=0||!std::isfinite(passageCarveReachMetres_)
|
||||
||std::abs(passageCarveReachMetres_-InfluenceMetres)>1.0e-9)return;
|
||||
|
||||
@@ -11306,8 +11585,6 @@ 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;
|
||||
constexpr double MouthShellBlendMetres=5.0;
|
||||
const double wallReliefShell=0.41*qPerMetre;
|
||||
const double floorDetailShell=1.0*qPerMetre;
|
||||
const bool profile=profileStats_!=nullptr;
|
||||
@@ -11363,17 +11640,16 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
+static_cast<std::int64_t>(gz)*sweep->stepSubvoxels);
|
||||
const double projectionXY=(qx-segment.a[0])*segment.delta[0]
|
||||
+(qy-segment.a[1])*segment.delta[1];
|
||||
const double t=std::clamp((projectionXY
|
||||
const double chordT=std::clamp((projectionXY
|
||||
+(qz-segment.a[2])*segment.delta[2])/segment.lengthSq,0.0,1.0);
|
||||
double cx=segment.a[0]+t*segment.delta[0];
|
||||
double cy=segment.a[1]+t*segment.delta[1];
|
||||
double cz=segment.a[2]+t*segment.delta[2];
|
||||
const double radius=segment.radiusA+t*segment.radiusDelta;
|
||||
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 cx=segment.a[0]+chordT*segment.delta[0];
|
||||
const double cy=segment.a[1]+chordT*segment.delta[1];
|
||||
const double cz=segment.a[2]+chordT*segment.delta[2];
|
||||
const double radius=segment.radiusA+chordT*segment.radiusDelta;
|
||||
const double dx=qx-cx,dy=qy-cy,dz=qz-cz;
|
||||
const double signedDistance=DeterministicSqrt(dx*dx+dy*dy+dz*dz)-radius;
|
||||
const double floorT=segment.horizontalLengthSq>1.0e-12
|
||||
?std::clamp(projectionXY/segment.horizontalLengthSq,0.0,1.0):t;
|
||||
?std::clamp(projectionXY/segment.horizontalLengthSq,0.0,1.0):chordT;
|
||||
const double floorRadius=segment.radiusA+floorT*segment.radiusDelta;
|
||||
const double floorDepth=FStandaloneShapeWorld::PassageFlatFloorDepthSubvoxels(
|
||||
constants,candidate.segmentIndex,floorT,floorRadius,qPerMetre);
|
||||
@@ -11382,6 +11658,54 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
constants,candidate.segmentIndex,floorT,segment,floorDepth,qPerMetre);
|
||||
const double flatDistance=FStandaloneShapeWorld::PassageFlatBottomDistance(
|
||||
static_cast<float>(signedDistance),floorCenterZ,qz,floorDepth,qPerMetre);
|
||||
const double shellAlongMetres=segment.authoredAlongMetres
|
||||
+chordT*segment.authoredLengthMetres;
|
||||
const double mouthDistanceMetres=std::max(0.0,std::min(shellAlongMetres,
|
||||
constants.totalPathLengthMetres-shellAlongMetres));
|
||||
const double shellFade=std::clamp(mouthDistanceMetres/
|
||||
MouthShellBlendMetres,0.0,1.0);
|
||||
const double smoothShellFade=shellFade*shellFade*(3.0-2.0*shellFade);
|
||||
const double floorDistanceMetres=(floorCenterZ-floorDepth-qz)
|
||||
/qPerMetre;
|
||||
const double floorBlend=std::clamp(floorDistanceMetres/0.25,0.0,1.0);
|
||||
const double smoothFloorBlend=floorBlend*floorBlend
|
||||
*(3.0-2.0*floorBlend);
|
||||
const double floorAlongMetres=segment.authoredAlongMetres
|
||||
+floorT*segment.authoredLengthMetres;
|
||||
const double floorMouthDistanceMetres=std::max(0.0,
|
||||
std::min(floorAlongMetres,constants.totalPathLengthMetres
|
||||
-floorAlongMetres));
|
||||
const double floorShellFade=std::clamp(floorMouthDistanceMetres
|
||||
/MouthShellBlendMetres,0.0,1.0);
|
||||
const double smoothFloorShellFade=floorShellFade*floorShellFade
|
||||
*(3.0-2.0*floorShellFade);
|
||||
const double mouthWallThickness=candidate.segmentIndex
|
||||
<constants.routeEndControlIndex
|
||||
?smoothShellFade*WallShellThicknessMetres*qPerMetre:0.0;
|
||||
double floorExtensionFade=1.0;
|
||||
if(candidate.segmentIndex>=constants.routeEndControlIndex)
|
||||
{
|
||||
const double pastMouthMetres=std::max(0.0,floorAlongMetres
|
||||
-constants.totalPathLengthMetres);
|
||||
const double extensionFade=std::clamp(1.0
|
||||
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
|
||||
floorExtensionFade=extensionFade*extensionFade
|
||||
*(3.0-2.0*extensionFade);
|
||||
}
|
||||
const double floorShellThickness=smoothFloorBlend
|
||||
*FloorShellThicknessMetres*qPerMetre*floorExtensionFade
|
||||
*smoothFloorShellFade;
|
||||
const double shellClearanceT=std::clamp(
|
||||
(mouthDistanceMetres-5.0)/15.0,0.0,1.0);
|
||||
const double shellClearanceFade=shellClearanceT*shellClearanceT
|
||||
*(3.0-2.0*shellClearanceT);
|
||||
const double shellThickness=std::max(mouthWallThickness,
|
||||
floorShellThickness)+(ShellClearanceMetres*qPerMetre)
|
||||
*shellClearanceFade;
|
||||
const double joinBlendT=std::clamp(mouthDistanceMetres/15.0,0.0,1.0);
|
||||
const double joinBlendWeight=joinBlendT*joinBlendT
|
||||
*(3.0-2.0*joinBlendT);
|
||||
const double localShellDistance=flatDistance-shellThickness;
|
||||
if(profile)++profileStats_->passageSweepSegmentSamplePairs;
|
||||
if(signedDistance>sweep->influenceSubvoxels)
|
||||
{
|
||||
@@ -11391,39 +11715,6 @@ 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];
|
||||
const double alongMetres=segment.authoredAlongMetres
|
||||
+t*segment.authoredLengthMetres;
|
||||
const double mouthDistanceMetres=std::max(0.0,std::min(alongMetres,
|
||||
constants.totalPathLengthMetres-alongMetres));
|
||||
const double shellFade=std::clamp(mouthDistanceMetres/
|
||||
MouthShellBlendMetres,0.0,1.0);
|
||||
const double smoothShellFade=shellFade*shellFade
|
||||
*(3.0-2.0*shellFade);
|
||||
const double floorDistanceMetres=(floorCenterZ-floorDepth-qz)/qPerMetre;
|
||||
const double floorBlend=std::clamp(floorDistanceMetres/0.25,0.0,1.0);
|
||||
const double smoothFloorBlend=floorBlend*floorBlend
|
||||
*(3.0-2.0*floorBlend);
|
||||
const double mouthWallThickness=candidate.segmentIndex
|
||||
<constants.routeEndControlIndex
|
||||
?smoothShellFade*shellThickness:0.0;
|
||||
double floorExtensionFade=1.0;
|
||||
if(candidate.segmentIndex>=constants.routeEndControlIndex)
|
||||
{
|
||||
const double pastMouthMetres=std::max(0.0,alongMetres
|
||||
-constants.totalPathLengthMetres);
|
||||
const double extensionFade=std::clamp(1.0
|
||||
-pastMouthMetres/MouthShellBlendMetres,0.0,1.0);
|
||||
floorExtensionFade=extensionFade*extensionFade
|
||||
*(3.0-2.0*extensionFade);
|
||||
}
|
||||
const double floorShellThickness=smoothFloorBlend*shellThickness
|
||||
*floorExtensionFade*smoothShellFade;
|
||||
const double localThickness=std::max(mouthWallThickness,
|
||||
floorShellThickness);
|
||||
const double localShellDistance=std::max(-static_cast<double>(flatDistance),
|
||||
static_cast<double>(flatDistance)-localThickness);
|
||||
sample.shellSignedDistance=std::min(sample.shellSignedDistance,
|
||||
localShellDistance);
|
||||
if(!sample.valid||flatDistance<sample.signedDistance
|
||||
||(flatDistance==sample.signedDistance
|
||||
&&candidate.segmentIndex<sample.segmentIndex))
|
||||
@@ -11434,12 +11725,38 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
sample.floorCenterZSubvoxels=floorCenterZ;
|
||||
sample.radiusSubvoxels=radius;
|
||||
sample.floorDepthSubvoxels=floorDepth;
|
||||
sample.t=t;
|
||||
sample.t=chordT;
|
||||
sample.segmentIndex=static_cast<std::uint32_t>(candidate.segmentIndex);
|
||||
sample.valid=true;
|
||||
sample.hasWallRelief=false;
|
||||
if(profile)++profileStats_->passageSweepMinimumUpdates;
|
||||
}
|
||||
if(!sample.shellValid)
|
||||
{
|
||||
sample.shellSignedDistance=localShellDistance;
|
||||
sample.shellSegmentIndex=static_cast<std::uint32_t>(
|
||||
candidate.segmentIndex);
|
||||
sample.previousShellDistance=localShellDistance;
|
||||
sample.previousShellBlendWeight=joinBlendWeight;
|
||||
sample.shellValid=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(candidate.segmentIndex==sample.shellSegmentIndex+1)
|
||||
sample.shellSignedDistance=std::min(
|
||||
sample.shellSignedDistance,
|
||||
FStandaloneShapeWorld::PassageSmoothMinDistance(
|
||||
sample.previousShellDistance,localShellDistance,
|
||||
ShellJoinBlendMetres*qPerMetre*std::min(
|
||||
sample.previousShellBlendWeight,
|
||||
joinBlendWeight)));
|
||||
sample.shellSignedDistance=std::min(sample.shellSignedDistance,
|
||||
localShellDistance);
|
||||
sample.shellSegmentIndex=static_cast<std::uint32_t>(
|
||||
candidate.segmentIndex);
|
||||
sample.previousShellDistance=localShellDistance;
|
||||
sample.previousShellBlendWeight=joinBlendWeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11448,8 +11765,6 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
|
||||
for(auto& passageSamples:sweep->passages)
|
||||
{
|
||||
const std::size_t passageIndex=passageSamples.passageIndex;
|
||||
const auto& constants=shape_->PassageComposerConstants[passageIndex];
|
||||
const auto& segments=shape_->PassageSegmentData[passageIndex];
|
||||
for(std::size_t sampleIndex=0;sampleIndex<passageSamples.samples.size();++sampleIndex)
|
||||
{
|
||||
auto& sample=passageSamples.samples[sampleIndex];
|
||||
@@ -11468,8 +11783,12 @@ 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);
|
||||
const double wallReliefShell=0.41*qPerMetre;
|
||||
const double radius=sample.radiusSubvoxels;
|
||||
const double centerZ=sample.centerZSubvoxels;
|
||||
if(sample.shellValid)
|
||||
sample.shellSignedDistance=std::max(-sample.signedDistance,
|
||||
sample.shellSignedDistance);
|
||||
if(sample.signedDistance<=sweep->influenceSubvoxels)
|
||||
{
|
||||
anyPassageInfluence[sampleIndex]=1;
|
||||
|
||||
Reference in new issue
Block a user