perf(generation): gate the tile cache window by LOD; LOD0 back to switch-off speed

2de603c's tile-sized room cache cut worker CPU, but LOD0 generation was ~9% slower. The per-tile
split shows the loss was evaluation (more candidates per sample in a bigger window), not cache
building. Policy, measured per path and LOD:
- fused path: tile window at LOD >= voxel.TileCacheWindowMinLOD (default 3)
- op-stack path: tile window at LOD >= voxel.TileCacheWindowOpStackMinLOD (default 4)
- lower LODs keep the 4-chunk worker window
- voxel.SpatialIndex: -1 auto (off at fused LOD0, on elsewhere), 0 or 1

Proof caveat closed: VF_ComputeTerrainOpEnvelope adds authored terrain-op extents (pits, chimneys,
detail overrides, ...) to the invariance envelope. Ops beyond the proven 256-voxel extent, or
malformed/unloaded ones, force the legacy window, so window invariance holds for every asset.

Sampler fix: the leaf symbol kept pointers into a TMap that FindOrAdd can rehash while later
frames resolve; the leaf is now re-found by program counter after insertion.

Game path, two runs each, sampler off (Codex; checked against its report):
                       LOD0 request p50/p95    generation p50/p95    worker s
  static  optimized    106.3 / 175.9 ms        76.6 / 124.3 ms       39.2
  static  switch-off   106.3 / 173.0           75.9 / 124.1          58.5   (-33%)
  moving  optimized     79.4 / 158.5           72.4 / 123.7         113.7
  moving  switch-off    82.8 / 160.4           73.2 / 125.4         141.9   (-20%)
LOD0 is flat within noise static and slightly better moving; worker CPU keeps most of the gain.
Field exact: 841/841 per-tile triangle identity, export OBJ SHA-256 unchanged (b3e5f4c3..9b377),
capability 20,830 / 10,909.

Caution: compare these numbers only within this round. The machine is in daytime use, so
absolute figures drift between rounds.

Current LOD0 map (sampler, moving, inclusive): tunnel SDF 12.5% + tunnel core 12.3% (the same
tunnel in two coordinate spaces); passage carving 11.1%, structural posts 11.4%, native floor
4.1%, with floor projection at 8.9% inside them; cave warp ~8% exclusive. Development-build-only
overheads: TArray RangeCheck ~6.5%, per-sample FScopedTimer ~2.6%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-13 18:17:28 +02:00
1 parent 2de603cd77
commit cfad3d1c18
8 files changed
+729 -146

No files matched your search

+376 -24
View File
@@ -1538,12 +1538,13 @@ namespace
template <typename FVisit>
static int32 VF_ForEachSpatialCandidate(
const FChunkSDFSpatialIndex& Index, int32 ItemCount,
float WorldX, float WorldY, FVisit&& Visit)
float WorldX, float WorldY, FVisit&& Visit,
bool bUseSpatialIndex = true)
{
int32 Begin = 0;
int32 End = 0;
int32 CandidateCount = 0;
if (Index.GetRange(WorldX, WorldY, Begin, End))
if (bUseSpatialIndex && Index.GetRange(WorldX, WorldY, Begin, End))
{
for (int32 Cursor = Begin; Cursor < End; ++Cursor)
{
@@ -1786,6 +1787,10 @@ struct FBuildRoomMouthRise
struct FVFRoomGraphReach
{
float DirectRoomReach = 0.0f;
// Complete XY reach of a room body plus any authored terrain operation that can be selected
// for that room. This is also used by the STORE cull; leaving it out would drop a room whose
// pit/chimney/detail override extends beyond the base SDF body.
float TerrainOpReach = 0.0f;
float FloorReliefEnvelope = 0.0f;
float BlendEnvelope = 0.0f;
float MaxTunnelLength = 0.0f;
@@ -1808,8 +1813,324 @@ static float VF_RoomShapeReachUpperBound(float RadiusXY, float RadiusZ)
return FMath::Max3(Ellipsoid, RoundedBox, Capsule);
}
// Terrain operations are authored outside FStrateGenerationParams and are selected per room at
// cache-build time. The tile-window proof must therefore include both the transported params and
// every operation that the pool can select. The limit is deliberately finite: an authored feature
// beyond it uses the legacy worker window, while the fixed-margin proof remains valid for all
// assets that stay inside this envelope.
static constexpr float VF_MaxProvenTerrainOpExtent = 256.0f;
static bool VF_ComputeTerrainOpEnvelope(
const FStrateGenerationParams& Params,
const TArray<FStrateTerrainOpEntry>* TerrainOps,
float BaseRoomReach,
float AllRoomRadiusEnvelope,
float BlendEnvelope,
float& OutReach)
{
float MaxReach = BaseRoomReach;
float MaxAuthoredExtent = 0.0f;
bool bHasActiveTerrainOperation = false;
auto RecordAuthoredExtent = [&](float Extent) -> bool
{
if (!FMath::IsFinite(Extent) || Extent < 0.0f)
{
return false;
}
bHasActiveTerrainOperation = true;
MaxAuthoredExtent = FMath::Max(MaxAuthoredExtent, Extent);
return MaxAuthoredExtent <= VF_MaxProvenTerrainOpExtent;
};
auto RecordReach = [&](float Reach) -> bool
{
if (!FMath::IsFinite(Reach) || Reach < 0.0f)
{
return false;
}
MaxReach = FMath::Max(MaxReach, Reach);
return FMath::IsFinite(MaxReach);
};
auto RecordDetail = [&](float EffectRange) -> bool
{
return RecordAuthoredExtent(EffectRange)
&& RecordReach(BaseRoomReach + EffectRange);
};
auto RecordStructural = [&](float EffectExtent, float FeatureReach) -> bool
{
return RecordAuthoredExtent(EffectExtent)
&& RecordReach(FeatureReach);
};
auto AccumulateParams = [&](const FStrateGenerationParams& OpParams) -> bool
{
const float OperationValues[] = {
OpParams.TerraceStepHeight,
OpParams.TerraceHardness,
OpParams.TerraceNoiseDisplacement,
OpParams.LayerLineSpacing,
OpParams.LayerLineDepth,
OpParams.OverhangStrength,
OpParams.OverhangDepth,
OpParams.OverhangFrequency,
OpParams.RibbingSpacing,
OpParams.RibbingDepth,
OpParams.CliffStrength,
OpParams.ScallopStrength,
OpParams.ScallopFrequency,
OpParams.ArchDensity,
OpParams.ArchMinRadius,
OpParams.ArchMaxRadius,
OpParams.ColumnDensity,
OpParams.ColumnMinRadius,
OpParams.ColumnMaxRadius,
OpParams.PitDensity,
OpParams.PitMinRadius,
OpParams.PitMaxRadius,
OpParams.PitDepth,
OpParams.ChimneyDensity,
OpParams.ChimneyMinRadius,
OpParams.ChimneyMaxRadius,
OpParams.ChimneyHeight,
OpParams.DomeDensity,
OpParams.DomeMinRadius,
OpParams.DomeMaxRadius,
OpParams.DomeHeightRatio,
OpParams.PinchDensity,
OpParams.PinchStrength,
OpParams.PinchLength};
for (const float Value : OperationValues)
{
if (!FMath::IsFinite(Value))
{
return false;
}
}
if (OpParams.TerraceStepHeight > 0.0f)
{
if (OpParams.TerraceHardness < 0.0f || OpParams.TerraceHardness > 1.0f
|| OpParams.TerraceNoiseDisplacement < 0.0f)
{
return false;
}
if (!RecordDetail(
OpParams.TerraceStepHeight * 3.0f
+ OpParams.TerraceNoiseDisplacement + 4.0f))
{
return false;
}
}
if (OpParams.LayerLineSpacing > 0.0f)
{
if (OpParams.LayerLineDepth < 0.0f
|| !RecordDetail(OpParams.LayerLineSpacing
+ OpParams.LayerLineDepth + 4.0f))
{
return false;
}
}
if (OpParams.RibbingSpacing > 0.0f)
{
if (OpParams.RibbingDepth < 0.0f
|| !RecordDetail(OpParams.RibbingSpacing
+ OpParams.RibbingDepth + 4.0f))
{
return false;
}
}
if (OpParams.CliffStrength > 0.0f)
{
if (!RecordDetail(OpParams.CliffStrength + 8.0f))
{
return false;
}
}
if (OpParams.ScallopStrength > 0.0f)
{
if (OpParams.ScallopFrequency <= 0.0f
|| !RecordDetail(
4.0f / OpParams.ScallopFrequency
+ OpParams.ScallopStrength + 4.0f))
{
return false;
}
}
if (OpParams.OverhangStrength > 0.0f)
{
if (OpParams.OverhangDepth < 0.0f
|| OpParams.OverhangFrequency <= 0.0f
|| !RecordDetail(OpParams.OverhangDepth * 2.0f + 4.0f))
{
return false;
}
}
if (OpParams.ArchDensity > 0.0f)
{
if (OpParams.ArchMinRadius < 0.0f || OpParams.ArchMaxRadius < 0.0f)
{
return false;
}
const float Radius = FMath::Max(OpParams.ArchMinRadius, OpParams.ArchMaxRadius);
if (!RecordStructural(
Radius + 4.0f,
AllRoomRadiusEnvelope * 1.2f + Radius + BlendEnvelope * 3.0f + 4.0f))
{
return false;
}
}
if (OpParams.ColumnDensity > 0.0f)
{
if (OpParams.ColumnMinRadius < 0.0f || OpParams.ColumnMaxRadius < 0.0f)
{
return false;
}
const float Radius = FMath::Max(OpParams.ColumnMinRadius, OpParams.ColumnMaxRadius);
if (!RecordStructural(
Radius + 6.0f,
AllRoomRadiusEnvelope * 0.75f + Radius + 6.0f))
{
return false;
}
}
if (OpParams.PitDensity > 0.0f)
{
if (OpParams.PitMinRadius < 0.0f || OpParams.PitMaxRadius < 0.0f
|| OpParams.PitDepth < 0.0f)
{
return false;
}
const float Radius = FMath::Max(OpParams.PitMinRadius, OpParams.PitMaxRadius);
if (!RecordStructural(
2.0f * Radius + OpParams.PitDepth + BlendEnvelope + 4.0f,
AllRoomRadiusEnvelope * 0.6f + 2.0f * Radius
+ OpParams.PitDepth + BlendEnvelope + 4.0f))
{
return false;
}
}
if (OpParams.ChimneyDensity > 0.0f)
{
if (OpParams.ChimneyMinRadius < 0.0f || OpParams.ChimneyMaxRadius < 0.0f
|| OpParams.ChimneyHeight < 0.0f)
{
return false;
}
const float Radius = FMath::Max(
OpParams.ChimneyMinRadius, OpParams.ChimneyMaxRadius);
if (!RecordStructural(
2.0f * Radius + OpParams.ChimneyHeight + BlendEnvelope + 4.0f,
AllRoomRadiusEnvelope * 0.6f + 2.0f * Radius
+ OpParams.ChimneyHeight + BlendEnvelope + 4.0f))
{
return false;
}
}
if (OpParams.DomeDensity > 0.0f)
{
if (OpParams.DomeMinRadius < 0.0f || OpParams.DomeMaxRadius < 0.0f
|| OpParams.DomeHeightRatio < 0.0f)
{
return false;
}
const float Radius = FMath::Max(OpParams.DomeMinRadius, OpParams.DomeMaxRadius);
if (!RecordStructural(
Radius * (1.0f + OpParams.DomeHeightRatio) + 4.0f,
AllRoomRadiusEnvelope * 0.4f + Radius
* (1.0f + OpParams.DomeHeightRatio) + 4.0f))
{
return false;
}
}
if (OpParams.PinchDensity > 0.0f)
{
if (OpParams.PinchStrength < 0.0f || OpParams.PinchLength < 0.0f)
{
return false;
}
const float Extent = FMath::Max(OpParams.PinchStrength, OpParams.PinchLength);
if (!RecordStructural(
Extent + 5.0f,
AllRoomRadiusEnvelope * 0.85f + Extent + 5.0f))
{
return false;
}
}
return true;
};
if (!AccumulateParams(Params))
{
return false;
}
if (TerrainOps != nullptr)
{
for (const FStrateTerrainOpEntry& Entry : *TerrainOps)
{
if (!FMath::IsFinite(Entry.Probability)
|| Entry.Probability < 0.0f || Entry.Probability > 1.0f
|| !FMath::IsFinite(Entry.Weight)
|| Entry.Weight < 0.0f || Entry.Weight > 3.0f)
{
return false;
}
if (Entry.Probability <= 0.0f || Entry.Weight <= 0.0f)
{
continue;
}
if (Entry.Operation.IsNull())
{
continue;
}
const UVoxelTerrainOpDefinition* Operation = Entry.Operation.Get();
if (Operation == nullptr)
{
// A referenced but unloaded asset is not a proof of absence. Keep the legacy
// window until the manager has a concrete definition to inspect.
return false;
}
switch (Operation->Type)
{
case EVoxelTerrainOpType::Terrace:
case EVoxelTerrainOpType::LayerLines:
case EVoxelTerrainOpType::Ribbing:
case EVoxelTerrainOpType::Cliff:
case EVoxelTerrainOpType::Scallop:
case EVoxelTerrainOpType::Overhang:
case EVoxelTerrainOpType::Arch:
case EVoxelTerrainOpType::Column:
case EVoxelTerrainOpType::Pit:
case EVoxelTerrainOpType::Chimney:
case EVoxelTerrainOpType::Dome:
case EVoxelTerrainOpType::Pinch:
break;
default:
return false;
}
FStrateGenerationParams OperationParams{};
Operation->ApplyTo(OperationParams, Entry.Weight);
if (!AccumulateParams(OperationParams))
{
return false;
}
}
}
// Keep the field at zero when the strate has no active terrain operation. The caller already
// has the base room envelope; using that same value as a second global room radius would make
// the legacy/no-op path store extra rooms and could change the canonical field. Active pools
// publish the wider envelope so the tile proof and STORE cull see pits/chimneys/detail reach.
OutReach = bHasActiveTerrainOperation ? MaxReach : 0.0f;
return FMath::IsFinite(OutReach)
&& FMath::IsFinite(MaxAuthoredExtent)
&& MaxAuthoredExtent <= VF_MaxProvenTerrainOpExtent;
}
static bool VF_ComputeRoomGraphReach(
const FStrateGenerationParams& Params,
const TArray<FStrateTerrainOpEntry>* TerrainOps,
FVFRoomGraphReach& OutReach)
{
const float RelevantParams[] = {
@@ -1892,9 +2213,17 @@ static bool VF_ComputeRoomGraphReach(
FMath::Abs(Params.TunnelMaxRadius)) * 1.18f);
const float MaxTunnelLength = FMath::Max(
Params.MaxTunnelLength, 0.0f);
const float DirectRoomReach = VF_RoomShapeReachUpperBound(
const float BaseDirectRoomReach = VF_RoomShapeReachUpperBound(
AllRoomRadiusEnvelope, AllRoomHeightEnvelope)
+ FloorReliefEnvelope + BlendEnvelope * 3.0f;
float TerrainOpReach = BaseDirectRoomReach;
if (!VF_ComputeTerrainOpEnvelope(
Params, TerrainOps, BaseDirectRoomReach,
AllRoomRadiusEnvelope, BlendEnvelope, TerrainOpReach))
{
return false;
}
const float DirectRoomReach = FMath::Max(BaseDirectRoomReach, TerrainOpReach);
const float TunnelInfluence = FMath::Abs(Params.TunnelWarpStrength)
+ TunnelRadiusEnvelope + BlendEnvelope * 3.0f;
const float MaxInfluence = FMath::Max(DirectRoomReach, TunnelInfluence);
@@ -1958,6 +2287,7 @@ static bool VF_ComputeRoomGraphReach(
}
OutReach.DirectRoomReach = DirectRoomReach;
OutReach.TerrainOpReach = TerrainOpReach;
OutReach.FloorReliefEnvelope = FloorReliefEnvelope;
OutReach.BlendEnvelope = BlendEnvelope;
OutReach.MaxTunnelLength = MaxTunnelLength;
@@ -2000,9 +2330,10 @@ static bool VF_MayHaveRoomGraphFeature(
{
return true;
}
const float RoomReach = VF_RoomShapeReachUpperBound(
const float BaseRoomReach = VF_RoomShapeReachUpperBound(
Room.RadiusXY, Room.RadiusZ)
+ Reach.FloorReliefEnvelope + Reach.BlendEnvelope * 3.0f;
const float RoomReach = FMath::Max(BaseRoomReach, Reach.TerrainOpReach);
if (!FMath::IsFinite(RoomReach) || RoomReach < 0.0f)
{
return true;
@@ -5474,7 +5805,8 @@ bool VoxelCaveMorphology::MayHaveFeatureInSearchBox(
float SearchMaxX, float SearchMaxY,
const FStrateGenerationParams& Params,
uint32 Seed, int32 StrateIndex,
bool bUseZ, float SearchMinZ, float SearchMaxZ)
bool bUseZ, float SearchMinZ, float SearchMaxZ,
const TArray<FStrateTerrainOpEntry>* TerrainOps)
{
if (!FMath::IsFinite(SearchMinX) || !FMath::IsFinite(SearchMinY)
|| !FMath::IsFinite(SearchMaxX) || !FMath::IsFinite(SearchMaxY)
@@ -5496,7 +5828,7 @@ bool VoxelCaveMorphology::MayHaveFeatureInSearchBox(
}
FVFRoomGraphReach Reach;
if (!VF_ComputeRoomGraphReach(Params, Reach))
if (!VF_ComputeRoomGraphReach(Params, TerrainOps, Reach))
{
return true;
}
@@ -5639,10 +5971,11 @@ bool VoxelCaveMorphology::MayHaveFeatureInSearchBox(
}
bool VoxelCaveMorphology::IsRoomGraphWindowInvariant(
const FStrateGenerationParams& Params)
const FStrateGenerationParams& Params,
const TArray<FStrateTerrainOpEntry>* TerrainOps)
{
FVFRoomGraphReach Reach;
return VF_ComputeRoomGraphReach(Params, Reach);
return VF_ComputeRoomGraphReach(Params, TerrainOps, Reach);
}
//=============================================================================
@@ -5707,7 +6040,8 @@ void VoxelCaveMorphology::BuildChunkCache(
// behavior below. The tile-window optimization is only a field-preserving optimization for
// the finite parameter envelope accepted by VF_ComputeRoomGraphReach.
FVFRoomGraphReach FeatureReach;
const bool bHasFeatureReach = VF_ComputeRoomGraphReach(Params, FeatureReach);
const bool bHasFeatureReach = VF_ComputeRoomGraphReach(
Params, TerrainOps, FeatureReach);
//=========================================================================
// INFLUENCE RADII
@@ -5801,7 +6135,9 @@ void VoxelCaveMorphology::BuildChunkCache(
auto RoomReachesSearchBox = [&](const FVector& C, float RadiusXY, float RadiusZ) -> bool
{
const float ShapeReach = VF_RoomShapeReachUpperBound(RadiusXY, RadiusZ);
const float Reach = ShapeReach + FloorReliefEnvelope + BlendEnvelope * 3.0f;
const float Reach = FMath::Max(
ShapeReach + FloorReliefEnvelope + BlendEnvelope * 3.0f,
bHasFeatureReach ? FeatureReach.TerrainOpReach : 0.0f);
const float dx = FMath::Max3((float)(SearchMinX - C.X), 0.0f, (float)(C.X - SearchMaxX));
const float dy = FMath::Max3((float)(SearchMinY - C.Y), 0.0f, (float)(C.Y - SearchMaxY));
return (dx * dx + dy * dy) <= Reach * Reach;
@@ -6766,6 +7102,10 @@ void VoxelCaveMorphology::BuildChunkCache(
FMath::Abs(CR.ShapeA.Z));
}
MaxExtent = ShapeReach + FloorReliefEnvelope + BlendEnvelope * 3.0f;
if (bHasFeatureReach)
{
MaxExtent = FMath::Max(MaxExtent, FeatureReach.TerrainOpReach);
}
CR.CullRadiusSq = MaxExtent * MaxExtent;
}
@@ -7004,7 +7344,8 @@ float VoxelCaveMorphology::EvaluateSDFCached(
float WorldX, float WorldY, float WorldZ,
const FChunkSDFCache& Cache,
float SDFBlendRadius,
int32* OutNearestRoomIdx)
int32* OutNearestRoomIdx,
bool bUseSpatialIndex)
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::RoomGraphSdf);
@@ -7117,7 +7458,8 @@ float VoxelCaveMorphology::EvaluateSDFCached(
};
const int32 RoomCandidateCount = VF_ForEachSpatialCandidate(
Cache.RoomSpatialIndex, Cache.Rooms.Num(), WorldX, WorldY, EvaluateRoom);
Cache.RoomSpatialIndex, Cache.Rooms.Num(), WorldX, WorldY, EvaluateRoom,
bUseSpatialIndex);
if (VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(
@@ -7148,7 +7490,7 @@ float VoxelCaveMorphology::EvaluateSDFCached(
};
VF_ForEachSpatialCandidate(
Cache.RoomFloorJoinSpatialIndex, Cache.RoomFloorJoins.Num(),
WorldX, WorldY, EvaluateJoin);
WorldX, WorldY, EvaluateJoin, bUseSpatialIndex);
//=========================================================================
// Tunnel SDFs
@@ -7188,7 +7530,8 @@ float VoxelCaveMorphology::EvaluateSDFCached(
};
const int32 TunnelCandidateCount = VF_ForEachSpatialCandidate(
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel);
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel,
bUseSpatialIndex);
if (VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(
@@ -7207,7 +7550,8 @@ float VoxelCaveMorphology::EvaluateSDFCached(
float VoxelCaveMorphology::EvaluateTunnelCoreSDF(
float WorldX, float WorldY, float WorldZ,
const FChunkSDFCache& Cache)
const FChunkSDFCache& Cache,
bool bUseSpatialIndex)
{
const FVector Pos(WorldX, WorldY, WorldZ);
float MinSDF = FLT_MAX;
@@ -7237,7 +7581,8 @@ float VoxelCaveMorphology::EvaluateTunnelCoreSDF(
};
VF_ForEachSpatialCandidate(
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel);
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel,
bUseSpatialIndex);
return MinSDF;
}
@@ -7245,7 +7590,8 @@ float VoxelCaveMorphology::EvaluateTunnelCoreSDF(
FTunnelCoreWorldEvaluation VoxelCaveMorphology::EvaluateTunnelCoreWorld(
float WorldX, float WorldY, float WorldZ,
const FChunkSDFCache& Cache,
const FTunnelSupportFloorColumn* SupportColumn)
const FTunnelSupportFloorColumn* SupportColumn,
bool bUseSpatialIndex)
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::TunnelCoreWorld);
@@ -7358,7 +7704,8 @@ FTunnelCoreWorldEvaluation VoxelCaveMorphology::EvaluateTunnelCoreWorld(
};
const int32 TunnelCandidateCount = VF_ForEachSpatialCandidate(
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel);
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel,
bUseSpatialIndex);
if (VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(
@@ -7371,14 +7718,17 @@ FTunnelCoreWorldEvaluation VoxelCaveMorphology::EvaluateTunnelCoreWorld(
float VoxelCaveMorphology::EvaluateTunnelCoreWorldSDF(
float WorldX, float WorldY, float WorldZ,
const FChunkSDFCache& Cache)
const FChunkSDFCache& Cache,
bool bUseSpatialIndex)
{
return EvaluateTunnelCoreWorld(WorldX, WorldY, WorldZ, Cache).SDF;
return EvaluateTunnelCoreWorld(
WorldX, WorldY, WorldZ, Cache, nullptr, bUseSpatialIndex).SDF;
}
bool VoxelCaveMorphology::IsTunnelSupportFloorWorldPoint(
float WorldX, float WorldY, float WorldZ,
const FChunkSDFCache& Cache)
const FChunkSDFCache& Cache,
bool bUseSpatialIndex)
{
const FVector Pos(WorldX, WorldY, WorldZ);
@@ -7462,7 +7812,7 @@ bool VoxelCaveMorphology::IsTunnelSupportFloorWorldPoint(
int32 Begin = 0;
int32 End = 0;
if (Cache.TunnelSpatialIndex.GetRange(WorldX, WorldY, Begin, End))
if (bUseSpatialIndex && Cache.TunnelSpatialIndex.GetRange(WorldX, WorldY, Begin, End))
{
TestRange(Begin, End);
}
@@ -7494,7 +7844,8 @@ bool VoxelCaveMorphology::IsTunnelSupportFloorWorldPoint(
void VoxelCaveMorphology::BuildTunnelSupportFloorColumn(
float WorldX, float WorldY,
const FChunkSDFCache& Cache,
FTunnelSupportFloorColumn& OutColumn)
FTunnelSupportFloorColumn& OutColumn,
bool bUseSpatialIndex)
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::TunnelCoreSupport);
@@ -7552,7 +7903,8 @@ void VoxelCaveMorphology::BuildTunnelSupportFloorColumn(
}
};
const int32 CandidateCount = VF_ForEachSpatialCandidate(
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel);
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel,
bUseSpatialIndex);
if (VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(