profile: per-term legacy FI/TN sample profiler (L76); passage/support is ~75% of field time
Opt-in at build time (VOXELFORGE_PROFILE_LEGACY_TERMS=1; compiled out by default, ~2% when on). Per legacy sample: passage/support composer ~1.05 us vs primitive scan ~0.19 us, boundary band ~0.06 us, noise ~0.01 us. Resolved-passage constants are cached at field binding (arithmetic order unchanged); whole-view effect within noise. Per-tile hashes identical to L75 in all 24 compiler x worker x view comparisons; fast tier PASS; Linux UE host builds. R40 researcher notes attached. 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
9d5455ec68
commit
90378a585f
13 files changed
+1070
-86
No files matched your search
@@ -345,7 +345,8 @@ public:
|
||||
BoundStratumFields.clear();
|
||||
BoundManagerLayoutChunkWidthVoxels=Owner&&Owner->Manager
|
||||
?Owner->Manager->GetStrateLayoutChunkWidthVoxels():0.0f;
|
||||
if(!ResolvedLayout||ResolvedLayout->strateLayoutChunkSizeMicrometres<=0)return;
|
||||
if(!ResolvedLayout||ResolvedLayout->strateLayoutChunkSizeMicrometres<=0)
|
||||
{BuildPassageComposerConstants();return;}
|
||||
const int64 ChunkWidthMicrometres=ResolvedLayout->strateLayoutChunkSizeMicrometres;
|
||||
const auto FloorDiv64=[](int64 Value,int64 Divisor)
|
||||
{
|
||||
@@ -430,6 +431,7 @@ public:
|
||||
std::sort(BoundStratumFields.begin(),BoundStratumFields.end(),
|
||||
[](const auto& A,const auto& B){return A.BottomChunkZ<B.BottomChunkZ;});
|
||||
BuildOreVeinSpatialIndex();
|
||||
BuildPassageComposerConstants();
|
||||
}
|
||||
|
||||
const FBoundStratumField* FindBoundStratumForChunk(int32 ChunkZ) const
|
||||
@@ -456,6 +458,14 @@ public:
|
||||
return Bound&&Bound->Kind==FBoundStratumField::EKind::FloatingIslands;
|
||||
}
|
||||
|
||||
bool IsLegacyFloatingIslandFieldAtZ(float Z) const
|
||||
{
|
||||
const FBoundStratumField* Bound=FindBoundStratumForZ(Z);
|
||||
return Bound&&Bound->Resolved
|
||||
&&Bound->Resolved->generatorKind=="FloatingIslands"
|
||||
&&Bound->IslandPlan==nullptr;
|
||||
}
|
||||
|
||||
const FBoundStratumField* FindBoundStratumForManagerZ(float Z) const
|
||||
{
|
||||
if(!std::isfinite(Z)||!Owner||!(BoundManagerLayoutChunkWidthVoxels>0.0f))
|
||||
@@ -992,7 +1002,7 @@ public:
|
||||
?passageTileQueries->profileStats():nullptr;
|
||||
if(passageProfile)++passageProfile->passageComposerQueries;
|
||||
bool queryNearUpperMouthSupport=false;
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
const auto checkUpperMouth=[&](const auto& passage)
|
||||
{
|
||||
if(passageProfile)++passageProfile->passageUpperMouthFilterChecks;
|
||||
const double dx=qx-static_cast<double>(passage.upperMouthSubvoxels[0]);
|
||||
@@ -1000,7 +1010,12 @@ public:
|
||||
const double dz=qz-static_cast<double>(passage.upperMouthSubvoxels[2]);
|
||||
const double reach=15.0*qPerMetre;
|
||||
if(dx*dx+dy*dy<=reach*reach&&std::abs(dz)<=8.0*qPerMetre)
|
||||
{queryNearUpperMouthSupport=true;break;}
|
||||
queryNearUpperMouthSupport=true;
|
||||
};
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
{
|
||||
checkUpperMouth(passage);
|
||||
if(queryNearUpperMouthSupport)break;
|
||||
}
|
||||
if(passageTileScopeMatches&&density>=0.0f
|
||||
&&!passageTileQueries->hasRouteFeatureOverlap()
|
||||
@@ -1088,40 +1103,19 @@ public:
|
||||
static_cast<float>(mouthBlendWidth/Q));
|
||||
};
|
||||
// The connected passage tread continues the surface route into the tunnel.
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
|
||||
const std::size_t passageIterationCount=ResolvedLayout->passages.size();
|
||||
for(std::size_t passageIteration=0;passageIteration<passageIterationCount;
|
||||
++passageIteration)
|
||||
{
|
||||
const std::size_t passageIndex=passageIteration;
|
||||
if(passageProfile)++passageProfile->passageIterations;
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
if(passage.controlPoints.size()<2)continue;
|
||||
const auto supportStratum=[&](std::int32_t strateIndex)
|
||||
->const VoxelForgeCore::ResolvedLayoutStratumV2*
|
||||
{
|
||||
const auto it=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[strateIndex](const auto& stratum)
|
||||
{return stratum.strateIndex==strateIndex;});
|
||||
return it==ResolvedLayout->strates.end()?nullptr:&*it;
|
||||
};
|
||||
const auto* supportUpper=supportStratum(passage.upperStrateIndex);
|
||||
const auto* supportLower=supportStratum(passage.lowerStrateIndex);
|
||||
const auto mouthInsideContent=[&](
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* stratum,
|
||||
const std::array<std::int64_t,3>& mouth)
|
||||
{
|
||||
if(!stratum||!stratum->content.presentInCurrentField)return false;
|
||||
const auto zMicrometres=static_cast<std::int64_t>(std::llround(
|
||||
static_cast<double>(mouth[2])
|
||||
*ResolvedLayout->coreVoxelSizeMicrometres/Q));
|
||||
return zMicrometres>=stratum->content.bottomMicrometres
|
||||
&&zMicrometres<stratum->content.topMicrometres;
|
||||
};
|
||||
const auto& passageConstants=PassageComposerConstants[passageIndex];
|
||||
// A passage is internal when both mouths are bounded by their strate
|
||||
// content and neither endpoint is a SurfaceWorld landing. Route order
|
||||
// and the total number of strata are irrelevant.
|
||||
const bool internalChainPassage=supportUpper&&supportLower
|
||||
&&!supportUpper->passageCapabilities.hasSurfaceLanding
|
||||
&&!supportLower->passageCapabilities.hasSurfaceLanding
|
||||
&&mouthInsideContent(supportUpper,passage.upperMouthSubvoxels)
|
||||
&&mouthInsideContent(supportLower,passage.lowerMouthSubvoxels);
|
||||
const bool internalChainPassage=passageConstants.internalChainPassage;
|
||||
const auto laterPassageMayCarve=[&](std::ptrdiff_t supportSegment,
|
||||
double sampleX,double sampleY,double sampleZ)
|
||||
{
|
||||
@@ -1129,14 +1123,10 @@ public:
|
||||
return PassageSupportMayBeCarvedByLaterBore(passageIndex,
|
||||
supportSegment,sampleX,sampleY,sampleZ,0.35*qPerMetre);
|
||||
};
|
||||
const bool floatingIslandPassage=supportUpper
|
||||
&&supportUpper->passageCapabilities.hasFloatingIslandLanding;
|
||||
const bool surfaceToTunnelSupport=supportUpper&&supportLower
|
||||
&&supportUpper->lowerInterfaceCapabilities.surfaceToTunnelLandingSupport;
|
||||
const double firstFloorQ=passage.controlPoints.front().positionSubvoxels[2]
|
||||
-passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
const double lastFloorQ=passage.controlPoints.back().positionSubvoxels[2]
|
||||
-passage.controlPoints.back().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
const bool floatingIslandPassage=passageConstants.floatingIslandPassage;
|
||||
const bool surfaceToTunnelSupport=passageConstants.surfaceToTunnelSupport;
|
||||
const double firstFloorQ=passageConstants.firstFloorQ;
|
||||
const double lastFloorQ=passageConstants.lastFloorQ;
|
||||
for(const auto& crossing:PassageVoidCrossings)
|
||||
{
|
||||
if(!crossing.needsColumn||crossing.passageIndex!=passageIndex
|
||||
@@ -1202,15 +1192,13 @@ public:
|
||||
}
|
||||
const auto floorProfile=[&](const auto& point)
|
||||
{return point.positionSubvoxels[2]-point.radiusSubvoxels+playerCenterAboveFloorQ;};
|
||||
const std::ptrdiff_t lastIndex=static_cast<std::ptrdiff_t>(passage.controlPoints.size())-1;
|
||||
const std::ptrdiff_t lowerMouthIndex=std::clamp<std::ptrdiff_t>(
|
||||
static_cast<std::ptrdiff_t>(passage.lowerMouthControlIndex),1,lastIndex);
|
||||
const std::ptrdiff_t lastIndex=passageConstants.lastIndex;
|
||||
const std::ptrdiff_t lowerMouthIndex=passageConstants.lowerMouthIndex;
|
||||
const auto mainEnd=passage.controlPoints.begin()+lowerMouthIndex+1;
|
||||
// The tube radius is measured from its centerline, while floorProfile is
|
||||
// one player-center offset away. Search the full vertical radius of the SDF;
|
||||
// a narrow point window misses earlier turns of a low-pitch helix.
|
||||
const double carveSearchMargin=std::max(passage.mouthRadiusSubvoxels,
|
||||
passage.midRadiusSubvoxels)+0.25*qPerMetre;
|
||||
const double carveSearchMargin=passageConstants.carveSearchMargin;
|
||||
const auto carveHigh=std::lower_bound(passage.controlPoints.begin(),mainEnd,
|
||||
qz+carveSearchMargin,
|
||||
[&](const auto& point,double value){return floorProfile(point)>value;});
|
||||
@@ -1221,9 +1209,8 @@ public:
|
||||
(carveHigh-passage.controlPoints.begin())-1);
|
||||
const std::ptrdiff_t carveEnd=std::min<std::ptrdiff_t>(lowerMouthIndex-1,
|
||||
(carveLow-passage.controlPoints.begin())+1);
|
||||
const std::ptrdiff_t mouthSearchBegin=std::max<std::ptrdiff_t>(0,lowerMouthIndex-8);
|
||||
const std::ptrdiff_t mouthSearchEnd=std::min<std::ptrdiff_t>(lastIndex,
|
||||
lowerMouthIndex+8);
|
||||
const std::ptrdiff_t mouthSearchBegin=passageConstants.mouthSearchBegin;
|
||||
const std::ptrdiff_t mouthSearchEnd=passageConstants.mouthSearchEnd;
|
||||
if(density<0.0f&&tilePassageFree)
|
||||
{
|
||||
if(passageProfile)++passageProfile->passageNegativeBoundChecks;
|
||||
@@ -1984,8 +1971,11 @@ public:
|
||||
}
|
||||
// Restore the surface route where the passage cut crossed otherwise solid
|
||||
// native ground. The support stays native-solid-only, so it cannot float.
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
|
||||
const std::size_t mouthSupportIterationCount=ResolvedLayout->passages.size();
|
||||
for(std::size_t passageIteration=0;
|
||||
passageIteration<mouthSupportIterationCount;++passageIteration)
|
||||
{
|
||||
const std::size_t passageIndex=passageIteration;
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
if(passage.controlPoints.size()<2)continue;
|
||||
const bool tileHasSupport=passageTileScopeMatches;
|
||||
@@ -2002,7 +1992,11 @@ public:
|
||||
|
||||
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
|
||||
{
|
||||
const float graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z);
|
||||
float graphDensity=0.0f;
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerCavern);
|
||||
graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z);
|
||||
}
|
||||
const auto* bound=FindBoundStratumForManagerZ(Z);
|
||||
const bool nativeLattice=bound&&bound->Resolved
|
||||
&&VoxelForgeCore::IsNativeLatticeFieldStratum(*bound->Resolved)
|
||||
@@ -2012,14 +2006,28 @@ public:
|
||||
// the generic stratum transition distance creates a discontinuity at a
|
||||
// mouth that crosses a wide transition band. Keep that opt-in field whole;
|
||||
// its host-rock value already supplies the ceiling and bedrock.
|
||||
const float boundary=nativeLattice?graphDensity
|
||||
:ApplyResolvedStrateBoundaryBand(graphDensity,X,Y,Z);
|
||||
float boundary=graphDensity;
|
||||
if(!nativeLattice)
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerBoundary);
|
||||
boundary=ApplyResolvedStrateBoundaryBand(graphDensity,X,Y,Z);
|
||||
}
|
||||
// The native lattice owns its resolved arrival/departure portals and their
|
||||
// supported spine. Reapplying legacy passage carves here can erase the
|
||||
// tread immediately above a broad legacy mouth or support column.
|
||||
const float composed=nativeLattice?boundary
|
||||
:ApplyResolvedStratePassageCarves(boundary,X,Y,Z);
|
||||
const float stamped=StampRuntime.empty()?composed:ApplyResolvedSdfStamps(composed,X,Y,Z);
|
||||
float composed=boundary;
|
||||
if(!nativeLattice)
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(
|
||||
ELegacyFieldTerm::ComposerPassageAndSupport);
|
||||
composed=ApplyResolvedStratePassageCarves(boundary,X,Y,Z);
|
||||
}
|
||||
float stamped=composed;
|
||||
if(!StampRuntime.empty())
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerStamps);
|
||||
stamped=ApplyResolvedSdfStamps(composed,X,Y,Z);
|
||||
}
|
||||
static const bool bL75TraceTnComposition=[]
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L75_TN_BLOCK_REGRESSION");
|
||||
@@ -2040,7 +2048,12 @@ public:
|
||||
}
|
||||
// The legacy landmark demo retains its compatibility floor cap. Native caves
|
||||
// already combine each floor with its wall before the single void carve above.
|
||||
const float final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z);
|
||||
float final=0.0f;
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(
|
||||
ELegacyFieldTerm::ComposerPassageAndSupport);
|
||||
final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z);
|
||||
}
|
||||
if(bTraceTnComposition)
|
||||
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g final=%.9g native=0\n",
|
||||
density,graphDensity,boundary,composed,stamped,final);
|
||||
@@ -2212,6 +2225,14 @@ public:
|
||||
DensityUnclampedPointCalls=0,DensityX8PointCalls=0;
|
||||
uint64 DensityFieldEvalNanoseconds=0,DensityFieldEvalSamplePoints=0;
|
||||
std::array<uint64,4> DensityFieldEvalNanosecondsByKind{},DensityFieldEvalSamplePointsByKind{};
|
||||
static constexpr std::size_t LegacyFieldTermCount=9;
|
||||
uint64 LegacyFieldEvalCalls=0;
|
||||
std::array<uint64,LegacyFieldTermCount> LegacyFieldTermCalls{};
|
||||
std::array<uint64,LegacyFieldTermCount> LegacyFieldTermNanoseconds{};
|
||||
std::array<uint64,LegacyFieldTermCount> LegacyFieldTermTimedCalls{};
|
||||
uint64 LegacyPrimitiveCandidateVisits=0,LegacyPrimitiveDistanceEvaluations=0;
|
||||
uint64 LegacySmoothMinCalls=0,LegacySmoothMinNanoseconds=0,
|
||||
LegacySmoothMinTimedCalls=0;
|
||||
double Milliseconds=0.0;
|
||||
#if defined(VF_L30_ENABLE_COST_COUNTERS)
|
||||
uint64 BoundaryBandCalls=0,TransitionWindowChecks=0,TransitionWindowHits=0;
|
||||
@@ -2232,6 +2253,105 @@ public:
|
||||
{static thread_local uint32 Depth=0;return Depth;}
|
||||
static uint64& DensityQuerySampleSequence()
|
||||
{static thread_local uint64 Sequence=0;return Sequence;}
|
||||
static bool& DensityQueryTermTimingActive()
|
||||
{static thread_local bool Active=false;return Active;}
|
||||
static bool& LegacyFieldTermScopeActive()
|
||||
{static thread_local bool Active=false;return Active;}
|
||||
|
||||
enum class ELegacyFieldTerm : std::size_t
|
||||
{
|
||||
PrimitiveScan,
|
||||
Detail,
|
||||
SurfaceReliefNoise,
|
||||
OwnerNoiseAndWarp,
|
||||
ComposerCavern,
|
||||
ComposerBoundary,
|
||||
ComposerPassageAndSupport,
|
||||
ComposerStamps,
|
||||
EditBrushes,
|
||||
Count
|
||||
};
|
||||
|
||||
struct FScopedLegacyFieldTermTimer
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
explicit FScopedLegacyFieldTermTimer(ELegacyFieldTerm InTerm,
|
||||
uint64 InMultiplicity=1)
|
||||
:Term(static_cast<std::size_t>(InTerm)),Multiplicity(InMultiplicity)
|
||||
{
|
||||
if(!LegacyFieldTermScopeActive())return;
|
||||
FClassificationThreadStats& Stats=ThreadClassificationStats();
|
||||
Stats.LegacyFieldTermCalls[Term]+=Multiplicity;
|
||||
if(!DensityQueryTermTimingActive())return;
|
||||
Start=std::chrono::steady_clock::now();
|
||||
bTimed=true;
|
||||
}
|
||||
~FScopedLegacyFieldTermTimer()
|
||||
{
|
||||
if(!bTimed)return;
|
||||
const uint64 Nanoseconds=static_cast<uint64>(std::max<std::int64_t>(0,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now()-Start).count()));
|
||||
FClassificationThreadStats& Stats=ThreadClassificationStats();
|
||||
Stats.LegacyFieldTermNanoseconds[Term]+=Nanoseconds;
|
||||
Stats.LegacyFieldTermTimedCalls[Term]+=Multiplicity;
|
||||
}
|
||||
std::size_t Term=0;
|
||||
uint64 Multiplicity=1;
|
||||
std::chrono::steady_clock::time_point Start{};
|
||||
bool bTimed=false;
|
||||
#else
|
||||
explicit FScopedLegacyFieldTermTimer(ELegacyFieldTerm,uint64=1) noexcept {}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct FScopedLegacyFieldTermScope
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
explicit FScopedLegacyFieldTermScope(bool bEligible,uint64 InMultiplicity=1)
|
||||
:Previous(LegacyFieldTermScopeActive()),Multiplicity(InMultiplicity)
|
||||
{
|
||||
const bool bEnabled=ThreadClassificationStats().CaptureDensityQueryProfile
|
||||
&&bEligible;
|
||||
LegacyFieldTermScopeActive()=bEnabled;
|
||||
if(bEnabled)ThreadClassificationStats().LegacyFieldEvalCalls+=Multiplicity;
|
||||
}
|
||||
~FScopedLegacyFieldTermScope()
|
||||
{LegacyFieldTermScopeActive()=Previous;}
|
||||
bool Previous=false;
|
||||
uint64 Multiplicity=1;
|
||||
#else
|
||||
explicit FScopedLegacyFieldTermScope(bool,uint64=1) noexcept {}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct FScopedLegacySmoothMinTimer
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
FScopedLegacySmoothMinTimer()
|
||||
{
|
||||
if(!LegacyFieldTermScopeActive())return;
|
||||
++ThreadClassificationStats().LegacySmoothMinCalls;
|
||||
if(!DensityQueryTermTimingActive())return;
|
||||
Start=std::chrono::steady_clock::now();
|
||||
bTimed=true;
|
||||
}
|
||||
~FScopedLegacySmoothMinTimer()
|
||||
{
|
||||
if(!bTimed)return;
|
||||
auto& Stats=ThreadClassificationStats();
|
||||
Stats.LegacySmoothMinNanoseconds+=static_cast<uint64>(std::max<std::int64_t>(0,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now()-Start).count()));
|
||||
++Stats.LegacySmoothMinTimedCalls;
|
||||
}
|
||||
std::chrono::steady_clock::time_point Start{};
|
||||
bool bTimed=false;
|
||||
#else
|
||||
FScopedLegacySmoothMinTimer() noexcept {}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct FScopedDensityQueryProfile
|
||||
{
|
||||
FScopedDensityQueryProfile(EDensityQueryKind Kind,uint64 PointCount=1)
|
||||
@@ -2240,6 +2360,10 @@ public:
|
||||
{
|
||||
if(!stats->CaptureDensityQueryProfile||DensityQuerySuppressionDepth()!=0)
|
||||
{stats=nullptr;return;}
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
PreviousTermTiming=DensityQueryTermTimingActive();
|
||||
DensityQueryTermTimingActive()=false;
|
||||
#endif
|
||||
stats->DensityPointCalls+=pointCount;
|
||||
switch(Kind)
|
||||
{
|
||||
@@ -2248,20 +2372,30 @@ public:
|
||||
case EDensityQueryKind::Unclamped:stats->DensityUnclampedPointCalls+=pointCount;break;
|
||||
case EDensityQueryKind::X8:stats->DensityX8PointCalls+=pointCount;break;
|
||||
}
|
||||
const uint64 Sequence=++DensityQuerySampleSequence();
|
||||
if((Sequence&255u)!=0)return;
|
||||
const uint64 Sequence=DensityQuerySampleSequence();
|
||||
DensityQuerySampleSequence()+=pointCount;
|
||||
if(((Sequence+pointCount)>>8)==(Sequence>>8))return;
|
||||
sampled=true;start=std::chrono::steady_clock::now();
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
DensityQueryTermTimingActive()=true;
|
||||
#endif
|
||||
}
|
||||
~FScopedDensityQueryProfile()
|
||||
{
|
||||
if(!sampled)return;
|
||||
const uint64 nanoseconds=static_cast<uint64>(std::max<std::int64_t>(0,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now()-start).count()));
|
||||
stats->DensityFieldEvalNanoseconds+=nanoseconds;
|
||||
stats->DensityFieldEvalSamplePoints+=pointCount;
|
||||
stats->DensityFieldEvalNanosecondsByKind[kindIndex]+=nanoseconds;
|
||||
stats->DensityFieldEvalSamplePointsByKind[kindIndex]+=pointCount;
|
||||
if(stats==nullptr)return;
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
DensityQueryTermTimingActive()=PreviousTermTiming;
|
||||
#endif
|
||||
if(sampled)
|
||||
{
|
||||
const uint64 nanoseconds=static_cast<uint64>(std::max<std::int64_t>(0,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now()-start).count()));
|
||||
stats->DensityFieldEvalNanoseconds+=nanoseconds;
|
||||
stats->DensityFieldEvalSamplePoints+=pointCount;
|
||||
stats->DensityFieldEvalNanosecondsByKind[kindIndex]+=nanoseconds;
|
||||
stats->DensityFieldEvalSamplePointsByKind[kindIndex]+=pointCount;
|
||||
}
|
||||
}
|
||||
bool IsActive()const noexcept{return stats!=nullptr;}
|
||||
FClassificationThreadStats* stats=nullptr;
|
||||
@@ -2269,6 +2403,9 @@ public:
|
||||
uint64 pointCount=0;
|
||||
std::chrono::steady_clock::time_point start{};
|
||||
bool sampled=false;
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
bool PreviousTermTiming=false;
|
||||
#endif
|
||||
};
|
||||
struct FScopedDensityQueryBatch
|
||||
{
|
||||
@@ -3577,6 +3714,15 @@ private:
|
||||
std::size_t passageIndex=0,segmentIndex=0;
|
||||
bool bColumn=false,bMouthSupport=false;
|
||||
};
|
||||
struct FPassageComposerConstants
|
||||
{
|
||||
bool internalChainPassage=false;
|
||||
bool floatingIslandPassage=false;
|
||||
bool surfaceToTunnelSupport=false;
|
||||
double firstFloorQ=0.0,lastFloorQ=0.0,carveSearchMargin=0.0;
|
||||
std::ptrdiff_t lowerMouthIndex=1,lastIndex=1;
|
||||
std::ptrdiff_t mouthSearchBegin=0,mouthSearchEnd=0;
|
||||
};
|
||||
struct FPassageMouthSupportSegment
|
||||
{
|
||||
std::array<std::int64_t,4> a{},b{};
|
||||
@@ -4549,6 +4695,68 @@ private:
|
||||
return result;
|
||||
}
|
||||
|
||||
void BuildPassageComposerConstants()
|
||||
{
|
||||
PassageComposerConstants.clear();
|
||||
if(!ResolvedLayout)return;
|
||||
PassageComposerConstants.resize(ResolvedLayout->passages.size());
|
||||
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double qPerMetre=Q*1000000.0/
|
||||
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const double playerCenterAboveFloorQ=1.13*qPerMetre;
|
||||
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
|
||||
++passageIndex)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
if(passage.controlPoints.size()<2)continue;
|
||||
const auto supportStratum=[&](std::int32_t strateIndex)
|
||||
->const VoxelForgeCore::ResolvedLayoutStratumV2*
|
||||
{
|
||||
const auto it=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[strateIndex](const auto& stratum)
|
||||
{return stratum.strateIndex==strateIndex;});
|
||||
return it==ResolvedLayout->strates.end()?nullptr:&*it;
|
||||
};
|
||||
const auto* supportUpper=supportStratum(passage.upperStrateIndex);
|
||||
const auto* supportLower=supportStratum(passage.lowerStrateIndex);
|
||||
const auto mouthInsideContent=[&](
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* stratum,
|
||||
const std::array<std::int64_t,3>& mouth)
|
||||
{
|
||||
if(!stratum||!stratum->content.presentInCurrentField)return false;
|
||||
const auto zMicrometres=static_cast<std::int64_t>(std::llround(
|
||||
static_cast<double>(mouth[2])
|
||||
*ResolvedLayout->coreVoxelSizeMicrometres/Q));
|
||||
return zMicrometres>=stratum->content.bottomMicrometres
|
||||
&&zMicrometres<stratum->content.topMicrometres;
|
||||
};
|
||||
auto& constants=PassageComposerConstants[passageIndex];
|
||||
constants.internalChainPassage=supportUpper&&supportLower
|
||||
&&!supportUpper->passageCapabilities.hasSurfaceLanding
|
||||
&&!supportLower->passageCapabilities.hasSurfaceLanding
|
||||
&&mouthInsideContent(supportUpper,passage.upperMouthSubvoxels)
|
||||
&&mouthInsideContent(supportLower,passage.lowerMouthSubvoxels);
|
||||
constants.floatingIslandPassage=supportUpper
|
||||
&&supportUpper->passageCapabilities.hasFloatingIslandLanding;
|
||||
constants.surfaceToTunnelSupport=supportUpper&&supportLower
|
||||
&&supportUpper->lowerInterfaceCapabilities.surfaceToTunnelLandingSupport;
|
||||
constants.firstFloorQ=passage.controlPoints.front().positionSubvoxels[2]
|
||||
-passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
constants.lastFloorQ=passage.controlPoints.back().positionSubvoxels[2]
|
||||
-passage.controlPoints.back().radiusSubvoxels+playerCenterAboveFloorQ;
|
||||
constants.lastIndex=static_cast<std::ptrdiff_t>(passage.controlPoints.size())-1;
|
||||
constants.lowerMouthIndex=std::clamp<std::ptrdiff_t>(
|
||||
static_cast<std::ptrdiff_t>(passage.lowerMouthControlIndex),1,
|
||||
constants.lastIndex);
|
||||
constants.mouthSearchBegin=std::max<std::ptrdiff_t>(0,
|
||||
constants.lowerMouthIndex-8);
|
||||
constants.mouthSearchEnd=std::min<std::ptrdiff_t>(constants.lastIndex,
|
||||
constants.lowerMouthIndex+8);
|
||||
constants.carveSearchMargin=std::max(passage.mouthRadiusSubvoxels,
|
||||
passage.midRadiusSubvoxels)+0.25*qPerMetre;
|
||||
}
|
||||
}
|
||||
|
||||
void BuildPassageSpatialIndices()
|
||||
{
|
||||
BuildStampSpatialIndex();
|
||||
@@ -5756,12 +5964,24 @@ private:
|
||||
?ApplyResolvedStrateComposition(Density,X,Y,Z):Density;
|
||||
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
|
||||
}
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
const FBoundStratumField* LegacyTermBound=FindBoundStratumForZ(Z);
|
||||
const bool bLegacyFiOrTnField=LegacyTermBound&&LegacyTermBound->Resolved
|
||||
&&(LegacyTermBound->Resolved->generatorKind=="TunnelNetwork"
|
||||
||(LegacyTermBound->Resolved->generatorKind=="FloatingIslands"
|
||||
&&LegacyTermBound->IslandPlan==nullptr));
|
||||
FScopedLegacyFieldTermScope LegacyFieldTermScope(bLegacyFiOrTnField);
|
||||
#endif
|
||||
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
|
||||
&&Z>=Min[2]&&Z<=Max[2];
|
||||
if(!bInsideFine)
|
||||
{
|
||||
bool bCriticalAir=false;
|
||||
float RockDistance=CoarseRockDistance(X,Y,Z,&bCriticalAir);
|
||||
float RockDistance=0.0f;
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::PrimitiveScan);
|
||||
RockDistance=CoarseRockDistance(X,Y,Z,&bCriticalAir);
|
||||
}
|
||||
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
|
||||
const bool bRock=RockDistance>=0.0f;
|
||||
float Density=-RockDistance;
|
||||
@@ -5770,14 +5990,23 @@ private:
|
||||
bClampNearZero?0.05f:1.0e-7f);
|
||||
const float composed=bApplyComposition
|
||||
?ApplyResolvedStrateComposition(Density,X,Y,Z):Density;
|
||||
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
|
||||
if(bApplyEdits)
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::EditBrushes);
|
||||
return ApplyEditBrushes(composed,X,Y,Z);
|
||||
}
|
||||
return composed;
|
||||
}
|
||||
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
|
||||
bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
|
||||
float PassageInfluence=0.0f;
|
||||
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
|
||||
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir,nullptr,false,
|
||||
&PassageInfluence);
|
||||
float RockDistance=0.0f;
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::PrimitiveScan);
|
||||
RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
|
||||
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir,nullptr,false,
|
||||
&PassageInfluence);
|
||||
}
|
||||
if(OutCaveInfluence)*OutCaveInfluence=CaveInfluence;
|
||||
if(OutFloorInfluence)*OutFloorInfluence=FloorInfluence;
|
||||
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
|
||||
@@ -5787,8 +6016,13 @@ private:
|
||||
CaveInfluence,FloorInfluence);
|
||||
if(Fade>0.0f)
|
||||
{
|
||||
const float Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,
|
||||
FloorInfluence,PassageInfluence)*Fade;
|
||||
float DetailNoiseValue=0.0f;
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::Detail);
|
||||
DetailNoiseValue=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,
|
||||
FloorInfluence,PassageInfluence);
|
||||
}
|
||||
const float Detail=DetailNoiseValue*Fade;
|
||||
RockDistance+=Detail;
|
||||
if(OutDetailDisplacement)*OutDetailDisplacement=Detail;
|
||||
}
|
||||
@@ -5800,7 +6034,12 @@ private:
|
||||
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),
|
||||
bClampNearZero?0.05f:1.0e-7f);
|
||||
if(bApplyComposition)Density=ApplyResolvedStrateComposition(Density,X,Y,Z);
|
||||
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
|
||||
if(bApplyEdits)
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::EditBrushes);
|
||||
return ApplyEditBrushes(Density,X,Y,Z);
|
||||
}
|
||||
return Density;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -7649,7 +7888,11 @@ private:
|
||||
{return N>=0?(N+D/2)/D:-((-N+D/2)/D);}
|
||||
static float SmoothMinDistance(float A,float B,float Width)
|
||||
{
|
||||
if(Width<=0.0f)return std::min(A,B);
|
||||
FScopedLegacySmoothMinTimer SmoothMinTimer;
|
||||
if(Width<=0.0f)
|
||||
{
|
||||
return std::min(A,B);
|
||||
}
|
||||
const float H=std::max(Width-std::abs(A-B),0.0f)/Width;
|
||||
return std::min(A,B)-(H*H*H)*(Width/6.0f);
|
||||
}
|
||||
@@ -10911,6 +11154,10 @@ private:
|
||||
{
|
||||
for(uint32 Id:*BinItems)
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
++ThreadClassificationStats().LegacyPrimitiveCandidateVisits;
|
||||
#endif
|
||||
const FPrimitive& P=Primitives[Id];
|
||||
if(P.bCarve)
|
||||
{
|
||||
@@ -10921,6 +11168,10 @@ private:
|
||||
if(X<P.Min[0]-DetailBand||X>P.Max[0]+DetailBand
|
||||
||Y<P.Min[1]-DetailBand||Y>P.Max[1]+DetailBand
|
||||
||Z<P.Min[2]-DetailBand||Z>P.Max[2]+DetailBand)continue;
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
|
||||
#endif
|
||||
float Distance=ShapeDistance(P,X,Y,Z);
|
||||
float PrimitiveFloorInfluence=0.0f;
|
||||
if(P.bHasFloorClip)
|
||||
@@ -10944,7 +11195,13 @@ private:
|
||||
}
|
||||
else if(X>=P.Min[0]&&X<=P.Max[0]&&Y>=P.Min[1]&&Y<=P.Max[1]
|
||||
&&Z>=P.Min[2]&&Z<=P.Max[2])
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
|
||||
#endif
|
||||
Overlay=std::max(Overlay,-ShapeDistance(P,X,Y,Z));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(OutCriticalAir)*OutCriticalAir=bCriticalAir;
|
||||
@@ -11093,6 +11350,11 @@ private:
|
||||
{
|
||||
for(uint32 Id:*BinItems)
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
ThreadClassificationStats().LegacyPrimitiveCandidateVisits+=
|
||||
static_cast<uint64>(Count);
|
||||
#endif
|
||||
const FPrimitive& P=Primitives[Id];
|
||||
bool Eligible[8]{};
|
||||
bool AnyEligible=false;
|
||||
@@ -11127,6 +11389,11 @@ private:
|
||||
}
|
||||
if(!AnyEligible)continue;
|
||||
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations+=
|
||||
static_cast<uint64>(Count);
|
||||
#endif
|
||||
float Distances[8];
|
||||
ShapeDistanceZ8(P,X,Y,Z,Count,Distances);
|
||||
if(P.bCarve)
|
||||
@@ -11201,6 +11468,9 @@ public:
|
||||
return;
|
||||
}
|
||||
if(Count<=0||OutDensity==nullptr)return;
|
||||
FScopedDensityQueryProfile queryProfile(EDensityQueryKind::Lattice,
|
||||
static_cast<uint64>(Count));
|
||||
FScopedDensityQueryBatch queryBatch(queryProfile.IsActive());
|
||||
if(bNativeOnly)
|
||||
{
|
||||
for(int32 Lane=0;Lane<Count;++Lane)
|
||||
@@ -11260,7 +11530,27 @@ public:
|
||||
&&FloorDiv(Z[BatchCount],ActiveSpatialBinSize)==BinZ)++BatchCount;
|
||||
|
||||
FEffectiveRockSample Samples[8]{};
|
||||
EffectiveRockDistanceZ8(X,Y,Z,BatchCount,BinItems,Samples);
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
bool bLegacyTermBatch=true;
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
const FBoundStratumField* Bound=FindBoundStratumForZ(
|
||||
static_cast<float>(Z[Lane]));
|
||||
bLegacyTermBatch=bLegacyTermBatch&&Bound&&Bound->Resolved
|
||||
&&(Bound->Resolved->generatorKind=="TunnelNetwork"
|
||||
||(Bound->Resolved->generatorKind=="FloatingIslands"
|
||||
&&Bound->IslandPlan==nullptr));
|
||||
}
|
||||
#else
|
||||
const bool bLegacyTermBatch=false;
|
||||
#endif
|
||||
FScopedLegacyFieldTermScope LegacyTermScope(bLegacyTermBatch,
|
||||
static_cast<uint64>(BatchCount));
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(
|
||||
ELegacyFieldTerm::PrimitiveScan,static_cast<uint64>(BatchCount));
|
||||
EffectiveRockDistanceZ8(X,Y,Z,BatchCount,BinItems,Samples);
|
||||
}
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
const int32 SampleZ=Z[Lane];
|
||||
@@ -11274,11 +11564,15 @@ public:
|
||||
static_cast<float>(Y),static_cast<float>(SampleZ),RockDistance,
|
||||
Samples[Lane].SurfaceSDF,Samples[Lane].CaveInfluence,
|
||||
Samples[Lane].FloorInfluence);
|
||||
if(Fade>0.0f)RockDistance+=DetailNoiseOnLine(static_cast<float>(X),
|
||||
static_cast<float>(Y),static_cast<float>(SampleZ),
|
||||
Samples[Lane].SurfaceSDF,Samples[Lane].CaveInfluence,
|
||||
Samples[Lane].FloorInfluence,Samples[Lane].PassageInfluence,
|
||||
DetailNoiseLine)*Fade;
|
||||
if(Fade>0.0f)
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::Detail);
|
||||
RockDistance+=DetailNoiseOnLine(static_cast<float>(X),
|
||||
static_cast<float>(Y),static_cast<float>(SampleZ),
|
||||
Samples[Lane].SurfaceSDF,Samples[Lane].CaveInfluence,
|
||||
Samples[Lane].FloorInfluence,Samples[Lane].PassageInfluence,
|
||||
DetailNoiseLine)*Fade;
|
||||
}
|
||||
}
|
||||
if(Samples[Lane].bCriticalAir)
|
||||
RockDistance=std::min(RockDistance,-1.0f/16.0f);
|
||||
@@ -11286,7 +11580,10 @@ public:
|
||||
float Density=-RockDistance;
|
||||
if((Density<0.0f)!=bRock||std::abs(Density)<0.05f)
|
||||
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),0.05f);
|
||||
OutDensity[Offset+Lane]=ApplyEditBrushes(Density,X,Y,SampleZ);
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::EditBrushes);
|
||||
OutDensity[Offset+Lane]=ApplyEditBrushes(Density,X,Y,SampleZ);
|
||||
}
|
||||
}
|
||||
Offset+=BatchCount;
|
||||
}
|
||||
@@ -11312,6 +11609,10 @@ private:
|
||||
{
|
||||
for(uint32 Id:*QueryItems)
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
++ThreadClassificationStats().LegacyPrimitiveCandidateVisits;
|
||||
#endif
|
||||
const FPrimitive& P=Primitives[Id];
|
||||
const VF_FieldPrimitive& Shared=SharedFieldPrimitives[Id];
|
||||
if(P.bCarve)
|
||||
@@ -11323,6 +11624,10 @@ private:
|
||||
if(X<P.Min[0]-DetailBand||X>P.Max[0]+DetailBand
|
||||
||Y<P.Min[1]-DetailBand||Y>P.Max[1]+DetailBand
|
||||
||Z<P.Min[2]-DetailBand||Z>P.Max[2]+DetailBand)continue;
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
|
||||
#endif
|
||||
float Distance=VF_FieldPrimitiveDistance(Shared,X,Y,Z);
|
||||
if(P.bHasFloorClip)
|
||||
{
|
||||
@@ -11330,15 +11635,33 @@ private:
|
||||
Distance=VF_FieldSmoothMax(Distance,static_cast<float>(Floor-Z),
|
||||
static_cast<float>(P.FloorBlendRadius));
|
||||
}
|
||||
Cave=VF_FieldSmoothMin(Cave,Distance,static_cast<float>(P.BlendRadius));
|
||||
float Combined=0.0f;
|
||||
{
|
||||
FScopedLegacySmoothMinTimer SmoothMinTimer;
|
||||
Combined=VF_FieldSmoothMin(Cave,Distance,
|
||||
static_cast<float>(P.BlendRadius));
|
||||
}
|
||||
Cave=Combined;
|
||||
}
|
||||
else if(X>=P.Min[0]&&X<=P.Max[0]&&Y>=P.Min[1]&&Y<=P.Max[1]
|
||||
&&Z>=P.Min[2]&&Z<=P.Max[2])
|
||||
{
|
||||
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
|
||||
if(LegacyFieldTermScopeActive())
|
||||
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
|
||||
#endif
|
||||
Overlay=std::max(Overlay,-VF_FieldPrimitiveDistance(Shared,X,Y,Z));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(OutCriticalAir)*OutCriticalAir=bCriticalAir;
|
||||
return VF_FieldSmoothMin(Base,std::max(Cave,Overlay),StrataCaveBlendRadius);
|
||||
float RockDistance=0.0f;
|
||||
{
|
||||
FScopedLegacySmoothMinTimer SmoothMinTimer;
|
||||
RockDistance=VF_FieldSmoothMin(Base,std::max(Cave,Overlay),
|
||||
StrataCaveBlendRadius);
|
||||
}
|
||||
return RockDistance;
|
||||
}
|
||||
|
||||
struct FOwnerNoiseLineContext
|
||||
@@ -11543,15 +11866,22 @@ private:
|
||||
*DetailSupremum;
|
||||
}
|
||||
float PassageBandRelief=0.0f;
|
||||
const float Relief=SurfaceRelief(static_cast<float>(X),static_cast<float>(Y),
|
||||
static_cast<float>(Z),static_cast<uint32>(Owner->Seed),&PassageBandRelief);
|
||||
float Relief=0.0f;
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::SurfaceReliefNoise);
|
||||
Relief=SurfaceRelief(static_cast<float>(X),static_cast<float>(Y),
|
||||
static_cast<float>(Z),static_cast<uint32>(Owner->Seed),&PassageBandRelief);
|
||||
}
|
||||
const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
|
||||
const FLayerField* Field=FindLayerField(Z);
|
||||
FOwnerNoiseLineContext OwnerNoise;
|
||||
float OwnerRelief=0.0f;
|
||||
if(Field)
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::OwnerNoiseAndWarp);
|
||||
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
|
||||
CaveInfluence,PassageWeight,OwnerNoise);
|
||||
}
|
||||
const float Detail=std::clamp(BlendDetailFamilies(Relief,PassageBandRelief,
|
||||
OwnerRelief,CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
|
||||
return Detail;
|
||||
@@ -11570,8 +11900,12 @@ private:
|
||||
using Clock=std::chrono::steady_clock;
|
||||
const auto ReliefStart=Profile?Clock::now():Clock::time_point{};
|
||||
float PassageBandRelief=0.0f;
|
||||
const float Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),
|
||||
bMacroKnown,KnownMacro,&PassageBandRelief);
|
||||
float Relief=0.0f;
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::SurfaceReliefNoise);
|
||||
Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),
|
||||
bMacroKnown,KnownMacro,&PassageBandRelief);
|
||||
}
|
||||
if(Profile)
|
||||
Profile->ReliefSampleNanoseconds+=static_cast<uint64>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-ReliefStart).count());
|
||||
@@ -11585,8 +11919,11 @@ private:
|
||||
const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
|
||||
float OwnerRelief=0.0f;
|
||||
if(Field)
|
||||
{
|
||||
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::OwnerNoiseAndWarp);
|
||||
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
|
||||
CaveInfluence,PassageWeight,NoiseLine.OwnerNoise,Profile);
|
||||
}
|
||||
if(Profile)
|
||||
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
|
||||
@@ -11885,6 +12222,7 @@ private:
|
||||
bool PassageFeatureBoundsFailOpen=false;
|
||||
std::vector<std::vector<FPassageNearestNode>> PassageNearestNodes;
|
||||
std::vector<std::vector<FPassageMouthSupportSegment>> PassageMouthSupportRoutes;
|
||||
std::vector<FPassageComposerConstants> PassageComposerConstants;
|
||||
std::vector<std::vector<FPassageLaterBoreSource>> PassageLaterBoreSources;
|
||||
std::vector<FPassageProofBound> PassageProofBounds;
|
||||
std::vector<FPassageProofNode> PassageProofNodes;
|
||||
|
||||
Reference in new issue
Block a user