measure(step2a): candidate bins alone are slower -- the per-sample GetDensityAt call must go

Editor-only ablation (explore commandlet, -regioneval, default off): 8^3 bins for rooms,
room-floor joins and tunnel chains, but every point still goes through GetDensityAt.
259^3 ring, seed 1337, six workers:
  live  : 6.776 s wall, 29.16 s worker CPU, 1.679 us/sample, 21.2 candidate visits/sample
  binned: 19.54 s wall, 50.02 s worker CPU, 2.879 us/sample, 16.4 candidate visits/sample
Correctness: zero Q16 (x16, ties away from zero) disagreements on the full ring and ~284k probes
on 4 more seeds (composer recipes, warped rooms, passages, landings, strate edges, edits);
shrinking room reach to 0.5 -> 18,107 Q16 / 735 sign differences (the check bites).
Not the planned region program: the step-2 question is still open. Baseline for the next attempt:
1.68 us CPU per sample on the live path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-09-30 01:19:11 +02:00
1 parent 3b4d6855fe
commit 41a9265ebd
8 files changed
+1693 -31

No files matched your search

+433 -16
View File
@@ -46,6 +46,11 @@
namespace
{
#if WITH_EDITOR
thread_local bool GVoxelResearchBrickBinningEnabled = false;
thread_local float GVoxelResearchBrickRoomReachScale = 1.0f;
#endif
using FVFPlayerDensitySampler = TFunctionRef<float(float, float, float)>;
// Density coordinates and cached cull radii are float-valued. FVector is double-valued in
@@ -1282,6 +1287,68 @@ bool FChunkSDFSpatialIndex::GetRange(
return OutBegin >= 0 && OutEnd >= OutBegin && OutEnd <= ItemIndices.Num();
}
void FChunkSDFBrickIndex::Reset()
{
MinBrickX = MinBrickY = MinBrickZ = 0;
NumBricksX = NumBricksY = NumBricksZ = 0;
bValid = false;
CellOffsets.Reset();
ItemIndices.Reset();
}
void FChunkSDFBrickIndex::Release()
{
MinBrickX = MinBrickY = MinBrickZ = 0;
NumBricksX = NumBricksY = NumBricksZ = 0;
bValid = false;
CellOffsets.Empty();
ItemIndices.Empty();
}
SIZE_T FChunkSDFBrickIndex::GetAllocatedSize() const
{
return static_cast<SIZE_T>(CellOffsets.GetAllocatedSize())
+ static_cast<SIZE_T>(ItemIndices.GetAllocatedSize());
}
bool FChunkSDFBrickIndex::GetRange(
float WorldX, float WorldY, float WorldZ,
int32& OutBegin, int32& OutEnd) const
{
OutBegin = OutEnd = 0;
constexpr double BrickSize = 8.0;
if (!bValid || NumBricksX <= 0 || NumBricksY <= 0 || NumBricksZ <= 0
|| !VoxelMath::IsFinite(WorldX) || !VoxelMath::IsFinite(WorldY)
|| !VoxelMath::IsFinite(WorldZ))
{
return false;
}
const double BX = FMath::FloorToDouble(static_cast<double>(WorldX) / BrickSize);
const double BY = FMath::FloorToDouble(static_cast<double>(WorldY) / BrickSize);
const double BZ = FMath::FloorToDouble(static_cast<double>(WorldZ) / BrickSize);
if (BX < MIN_int32 || BX > MAX_int32 || BY < MIN_int32 || BY > MAX_int32
|| BZ < MIN_int32 || BZ > MAX_int32)
{
return false;
}
const int64 X = static_cast<int64>(BX) - MinBrickX;
const int64 Y = static_cast<int64>(BY) - MinBrickY;
const int64 Z = static_cast<int64>(BZ) - MinBrickZ;
if (X < 0 || X >= NumBricksX || Y < 0 || Y >= NumBricksY
|| Z < 0 || Z >= NumBricksZ)
{
return false;
}
const int64 Slot = (Z * NumBricksY + Y) * NumBricksX + X;
if (Slot < 0 || Slot + 1 >= CellOffsets.Num())
{
return false;
}
OutBegin = CellOffsets[static_cast<int32>(Slot)];
OutEnd = CellOffsets[static_cast<int32>(Slot + 1)];
return OutBegin >= 0 && OutEnd >= OutBegin && OutEnd <= ItemIndices.Num();
}
namespace
{
struct FVFSpatialBounds
@@ -1513,6 +1580,42 @@ namespace
return CandidateCount;
}
template <typename FVisit>
static FORCEINLINE int32 VF_ForEachSDFCandidate(
const FChunkSDFSpatialIndex& SpatialIndex,
#if WITH_EDITOR
const FChunkSDFBrickIndex& BrickIndex,
#endif
int32 ItemCount, float WorldX, float WorldY, float WorldZ,
FVisit&& Visit, bool bUseSpatialIndex = true)
{
#if WITH_EDITOR
if (VoxelCaveMorphology::IsResearchBrickBinningEnabled())
{
int32 Begin = 0;
int32 End = 0;
if (BrickIndex.GetRange(WorldX, WorldY, WorldZ, Begin, End))
{
int32 CandidateCount = 0;
const int32* Indices = BrickIndex.ItemIndices.GetData();
for (int32 Cursor = Begin; Cursor < End; ++Cursor)
{
const int32 ItemIndex = Indices[Cursor];
if (ItemIndex >= 0 && ItemIndex < ItemCount)
{
++CandidateCount;
Visit(ItemIndex);
}
}
return CandidateCount;
}
}
#endif
return VF_ForEachSpatialCandidate(
SpatialIndex, ItemCount, WorldX, WorldY,
Forward<FVisit>(Visit), bUseSpatialIndex);
}
static bool VF_GetRoomSpatialBounds(int32, const FCachedRoom& Room, FVFSpatialBounds& OutBounds)
{
return VF_SetSpatialBounds(
@@ -1559,9 +1662,13 @@ namespace
}
bInside = VF_CachedRoomShapeSDF(Room, Position) < 0.0f;
};
VF_ForEachSpatialCandidate(
Cache.RoomSpatialIndex, Cache.Rooms.Num(),
static_cast<float>(Position.X), static_cast<float>(Position.Y),
VF_ForEachSDFCandidate(
Cache.RoomSpatialIndex,
#if WITH_EDITOR
Cache.RoomBrickIndex,
#endif
Cache.Rooms.Num(), static_cast<float>(Position.X),
static_cast<float>(Position.Y), static_cast<float>(Position.Z),
VisitRoom, bUseSpatialIndex);
return bInside;
}
@@ -1643,6 +1750,261 @@ namespace
Column.CenterX, Column.CenterY,
FMath::Sqrt(FMath::Max(Column.BoundXYRadiusSq, 0.0f)), OutBounds);
}
#if WITH_EDITOR
struct FVFBrickBounds
{
FVector Min = FVector::ZeroVector;
FVector Max = FVector::ZeroVector;
};
static bool VF_IncludeBrickBounds(
FVFBrickBounds& InOut, const FVector& Min, const FVector& Max, bool& bHasBounds)
{
if (!VoxelMath::IsFinite(Min.X) || !VoxelMath::IsFinite(Min.Y)
|| !VoxelMath::IsFinite(Min.Z) || !VoxelMath::IsFinite(Max.X)
|| !VoxelMath::IsFinite(Max.Y) || !VoxelMath::IsFinite(Max.Z)
|| Min.X > Max.X || Min.Y > Max.Y || Min.Z > Max.Z)
{
return false;
}
if (!bHasBounds)
{
InOut.Min = Min;
InOut.Max = Max;
bHasBounds = true;
}
else
{
InOut.Min.X = FMath::Min(InOut.Min.X, Min.X);
InOut.Min.Y = FMath::Min(InOut.Min.Y, Min.Y);
InOut.Min.Z = FMath::Min(InOut.Min.Z, Min.Z);
InOut.Max.X = FMath::Max(InOut.Max.X, Max.X);
InOut.Max.Y = FMath::Max(InOut.Max.Y, Max.Y);
InOut.Max.Z = FMath::Max(InOut.Max.Z, Max.Z);
}
return true;
}
static bool VF_IncludeBrickSphere(
FVFBrickBounds& InOut, const FVector& Center, float Radius, bool& bHasBounds)
{
if (!VoxelMath::IsFinite(Radius) || Radius < 0.0f)
{
return false;
}
const FVector FloatCenter(
static_cast<float>(Center.X), static_cast<float>(Center.Y), static_cast<float>(Center.Z));
const float CoordinateScale = FMath::Max3(
1.0f, FMath::Abs(static_cast<float>(Center.X)),
FMath::Max3(FMath::Abs(static_cast<float>(Center.Y)),
FMath::Abs(static_cast<float>(Center.Z)), FMath::Abs(Radius)));
// Leave a float-operation margin around the exact live cull. It covers rounding in the
// float center conversion, distance square/sum, and radius-square comparison; it is a
// numerical bound, not an authored feature-radius guess.
const float ConservativeRadius = Radius + CoordinateScale * (32.0f * FLT_EPSILON);
const FVector R(ConservativeRadius, ConservativeRadius, ConservativeRadius);
return VF_IncludeBrickBounds(InOut, FloatCenter - R, FloatCenter + R, bHasBounds);
}
template <typename FGetBounds>
static void VF_BuildBrickIndex(
FChunkSDFBrickIndex& OutIndex, int32 ItemCount, FGetBounds&& GetBounds)
{
OutIndex.Reset();
if (ItemCount <= 0)
{
OutIndex.bValid = true;
return;
}
constexpr double BrickSize = 8.0;
constexpr int64 MaxBuckets = 1024 * 1024;
constexpr int64 MaxReferences = 4 * 1024 * 1024;
TArray<FIntVector> ItemMin;
TArray<FIntVector> ItemMax;
ItemMin.SetNumUninitialized(ItemCount);
ItemMax.SetNumUninitialized(ItemCount);
int32 GlobalMinX = MAX_int32, GlobalMinY = MAX_int32, GlobalMinZ = MAX_int32;
int32 GlobalMaxX = MIN_int32, GlobalMaxY = MIN_int32, GlobalMaxZ = MIN_int32;
int64 TotalReferences = 0;
for (int32 Item = 0; Item < ItemCount; ++Item)
{
FVFBrickBounds Bounds;
if (!GetBounds(Item, Bounds)
|| !VoxelMath::IsFinite(Bounds.Min.X) || !VoxelMath::IsFinite(Bounds.Min.Y)
|| !VoxelMath::IsFinite(Bounds.Min.Z) || !VoxelMath::IsFinite(Bounds.Max.X)
|| !VoxelMath::IsFinite(Bounds.Max.Y) || !VoxelMath::IsFinite(Bounds.Max.Z))
{
return;
}
const double MinX = FMath::FloorToDouble(Bounds.Min.X / BrickSize);
const double MinY = FMath::FloorToDouble(Bounds.Min.Y / BrickSize);
const double MinZ = FMath::FloorToDouble(Bounds.Min.Z / BrickSize);
const double MaxX = FMath::FloorToDouble(Bounds.Max.X / BrickSize);
const double MaxY = FMath::FloorToDouble(Bounds.Max.Y / BrickSize);
const double MaxZ = FMath::FloorToDouble(Bounds.Max.Z / BrickSize);
if (MinX < MIN_int32 || MinX > MAX_int32 || MinY < MIN_int32 || MinY > MAX_int32
|| MinZ < MIN_int32 || MinZ > MAX_int32 || MaxX < MIN_int32 || MaxX > MAX_int32
|| MaxY < MIN_int32 || MaxY > MAX_int32 || MaxZ < MIN_int32 || MaxZ > MAX_int32)
{
return;
}
const FIntVector Lower(
static_cast<int32>(MinX), static_cast<int32>(MinY), static_cast<int32>(MinZ));
const FIntVector Upper(
static_cast<int32>(MaxX), static_cast<int32>(MaxY), static_cast<int32>(MaxZ));
if (Lower.X > Upper.X || Lower.Y > Upper.Y || Lower.Z > Upper.Z)
{
return;
}
ItemMin[Item] = Lower;
ItemMax[Item] = Upper;
GlobalMinX = FMath::Min(GlobalMinX, Lower.X);
GlobalMinY = FMath::Min(GlobalMinY, Lower.Y);
GlobalMinZ = FMath::Min(GlobalMinZ, Lower.Z);
GlobalMaxX = FMath::Max(GlobalMaxX, Upper.X);
GlobalMaxY = FMath::Max(GlobalMaxY, Upper.Y);
GlobalMaxZ = FMath::Max(GlobalMaxZ, Upper.Z);
const int64 NX = static_cast<int64>(Upper.X) - Lower.X + 1;
const int64 NY = static_cast<int64>(Upper.Y) - Lower.Y + 1;
const int64 NZ = static_cast<int64>(Upper.Z) - Lower.Z + 1;
if (NX <= 0 || NY <= 0 || NZ <= 0 || NX > MaxReferences / NY
|| NX * NY > MaxReferences / NZ)
{
return;
}
TotalReferences += NX * NY * NZ;
if (TotalReferences > MaxReferences)
{
return;
}
}
const int64 NumX = static_cast<int64>(GlobalMaxX) - GlobalMinX + 1;
const int64 NumY = static_cast<int64>(GlobalMaxY) - GlobalMinY + 1;
const int64 NumZ = static_cast<int64>(GlobalMaxZ) - GlobalMinZ + 1;
if (NumX <= 0 || NumY <= 0 || NumZ <= 0 || NumX > MaxBuckets / NumY
|| NumX * NumY > MaxBuckets / NumZ)
{
return;
}
const int64 BucketCount = NumX * NumY * NumZ;
OutIndex.MinBrickX = GlobalMinX;
OutIndex.MinBrickY = GlobalMinY;
OutIndex.MinBrickZ = GlobalMinZ;
OutIndex.NumBricksX = static_cast<int32>(NumX);
OutIndex.NumBricksY = static_cast<int32>(NumY);
OutIndex.NumBricksZ = static_cast<int32>(NumZ);
OutIndex.CellOffsets.SetNumZeroed(static_cast<int32>(BucketCount + 1));
for (int32 Item = 0; Item < ItemCount; ++Item)
{
for (int32 Z = ItemMin[Item].Z; Z <= ItemMax[Item].Z; ++Z)
{
for (int32 Y = ItemMin[Item].Y; Y <= ItemMax[Item].Y; ++Y)
{
const int64 Row = (static_cast<int64>(Z - OutIndex.MinBrickZ)
* OutIndex.NumBricksY + (Y - OutIndex.MinBrickY))
* OutIndex.NumBricksX;
for (int32 X = ItemMin[Item].X; X <= ItemMax[Item].X; ++X)
{
const int64 Slot = Row + (X - OutIndex.MinBrickX);
++OutIndex.CellOffsets[static_cast<int32>(Slot + 1)];
}
}
}
}
for (int32 Slot = 0; Slot < static_cast<int32>(BucketCount); ++Slot)
{
OutIndex.CellOffsets[Slot + 1] += OutIndex.CellOffsets[Slot];
}
OutIndex.ItemIndices.SetNumUninitialized(static_cast<int32>(TotalReferences));
TArray<int32> WriteOffsets = OutIndex.CellOffsets;
for (int32 Item = 0; Item < ItemCount; ++Item)
{
for (int32 Z = ItemMin[Item].Z; Z <= ItemMax[Item].Z; ++Z)
{
for (int32 Y = ItemMin[Item].Y; Y <= ItemMax[Item].Y; ++Y)
{
const int64 Row = (static_cast<int64>(Z - OutIndex.MinBrickZ)
* OutIndex.NumBricksY + (Y - OutIndex.MinBrickY))
* OutIndex.NumBricksX;
for (int32 X = ItemMin[Item].X; X <= ItemMax[Item].X; ++X)
{
const int64 Slot = Row + (X - OutIndex.MinBrickX);
OutIndex.ItemIndices[WriteOffsets[static_cast<int32>(Slot)]++] = Item;
}
}
}
}
OutIndex.bValid = true;
}
static bool VF_GetRoomBrickBoundsSafe(
int32, const FCachedRoom& Room, FVFBrickBounds& OutBounds)
{
// EvaluateSDFCached receives the already-warped room query coordinate. Bin in that same
// space, so we preserve the live query's exact warp without estimating an inverse-warp
// displacement. CullRadiusSq is the baked superset of room shape, relief, blend and op reach.
const float Radius = FMath::Sqrt(FMath::Max(Room.CullRadiusSq, 0.0f))
* FMath::Clamp(GVoxelResearchBrickRoomReachScale, 0.0f, 1.0f);
const FVector R(Radius, Radius, Radius);
bool bHasBounds = false;
return VF_IncludeBrickBounds(OutBounds, Room.Center - R, Room.Center + R, bHasBounds);
}
static bool VF_GetJoinBrickBounds(
int32, const FCachedRoomFloorJoin& Join, FVFBrickBounds& OutBounds)
{
const float Radius = FMath::Sqrt(FMath::Max(Join.BoundRadiusSq, 0.0f));
const FVector R(Radius, Radius, Radius);
bool bHasBounds = false;
return VF_IncludeBrickBounds(OutBounds, Join.BoundCenter - R, Join.BoundCenter + R, bHasBounds);
}
static bool VF_GetTunnelBrickBounds(
int32, const FCachedTunnel& Tunnel, FVFBrickBounds& OutBounds)
{
// The final field evaluates both the warped SDF chain and the authored world chain. Keep
// both bound sets, including their finalized blend/detail/floor influence, in every bin.
bool bHasBounds = false;
const auto IncludeSphere = [&](const FVector& Center, float Radius)
{
return VF_IncludeBrickSphere(OutBounds, Center, Radius, bHasBounds);
};
const auto IncludeAabb = [&](const FVector& Min, const FVector& Max, float Radius)
{
const FVector FloatMin(
static_cast<float>(Min.X), static_cast<float>(Min.Y), static_cast<float>(Min.Z));
const FVector FloatMax(
static_cast<float>(Max.X), static_cast<float>(Max.Y), static_cast<float>(Max.Z));
const float CoordinateScale = FMath::Max3(
1.0f,
FMath::Max3(FMath::Abs(static_cast<float>(Min.X)), FMath::Abs(static_cast<float>(Max.X)), FMath::Abs(Radius)),
FMath::Max3(FMath::Abs(static_cast<float>(Min.Y)), FMath::Abs(static_cast<float>(Max.Y)),
FMath::Max(FMath::Abs(static_cast<float>(Min.Z)), FMath::Abs(static_cast<float>(Max.Z)))));
const float ConservativeRadius = FMath::Max(Radius, 0.0f)
+ CoordinateScale * (32.0f * FLT_EPSILON);
const FVector R(ConservativeRadius, ConservativeRadius, ConservativeRadius);
return VF_IncludeBrickBounds(OutBounds, FloatMin - R, FloatMax + R, bHasBounds);
};
return IncludeSphere(Tunnel.BoundCenter, FMath::Sqrt(FMath::Max(Tunnel.BoundRadiusSq, 0.0f)))
&& IncludeSphere(Tunnel.WorldBoundCenter, FMath::Sqrt(FMath::Max(Tunnel.WorldBoundRadiusSq, 0.0f)))
&& IncludeAabb(Tunnel.SDFCenterlineMin, Tunnel.SDFCenterlineMax, Tunnel.SDFInfluenceRadius)
&& IncludeAabb(Tunnel.WorldCenterlineMin, Tunnel.WorldCenterlineMax, Tunnel.WorldInfluenceRadius)
&& bHasBounds;
}
static void VF_BuildResearchBrickIndices(FChunkSDFCache& Cache)
{
VF_BuildBrickIndex(Cache.RoomBrickIndex, Cache.Rooms.Num(),
[&Cache](int32 I, FVFBrickBounds& B) { return VF_GetRoomBrickBoundsSafe(I, Cache.Rooms[I], B); });
VF_BuildBrickIndex(Cache.RoomFloorJoinBrickIndex, Cache.RoomFloorJoins.Num(),
[&Cache](int32 I, FVFBrickBounds& B) { return VF_GetJoinBrickBounds(I, Cache.RoomFloorJoins[I], B); });
VF_BuildBrickIndex(Cache.TunnelBrickIndex, Cache.Tunnels.Num(),
[&Cache](int32 I, FVFBrickBounds& B) { return VF_GetTunnelBrickBounds(I, Cache.Tunnels[I], B); });
}
#endif
}
void FChunkSDFCache::Reset()
@@ -1667,6 +2029,11 @@ void FChunkSDFCache::Reset()
PitSpatialIndex.Reset();
ChimneySpatialIndex.Reset();
ColumnSpatialIndex.Reset();
#if WITH_EDITOR
RoomBrickIndex.Reset();
RoomFloorJoinBrickIndex.Reset();
TunnelBrickIndex.Reset();
#endif
SDFBlendRadius = 0.0f;
}
@@ -1684,6 +2051,11 @@ void FChunkSDFCache::Release()
PitSpatialIndex.Release();
ChimneySpatialIndex.Release();
ColumnSpatialIndex.Release();
#if WITH_EDITOR
RoomBrickIndex.Release();
RoomFloorJoinBrickIndex.Release();
TunnelBrickIndex.Release();
#endif
}
SIZE_T FChunkSDFCache::GetAllocatedSize() const
@@ -1712,6 +2084,12 @@ VoxelDensityProfile::FCacheMemoryBreakdown FChunkSDFCache::GetAllocatedSizeBreak
+ PitSpatialIndex.GetAllocatedSize()
+ ChimneySpatialIndex.GetAllocatedSize()
+ ColumnSpatialIndex.GetAllocatedSize());
#if WITH_EDITOR
Breakdown.SpatialIndexBytes += static_cast<uint64>(
RoomBrickIndex.GetAllocatedSize()
+ RoomFloorJoinBrickIndex.GetAllocatedSize()
+ TunnelBrickIndex.GetAllocatedSize());
#endif
for (const FCachedTunnel& Tunnel : Tunnels)
{
Breakdown.TunnelsBytes += ArrayBytes(Tunnel.ControlPoints);
@@ -2753,6 +3131,12 @@ static void BakeRoomFeature(
{
return VF_GetColumnSpatialBounds(Index, Cache.Columns[Index], Bounds);
});
#if WITH_EDITOR
if (VoxelCaveMorphology::IsResearchBrickBinningEnabled())
{
VF_BuildResearchBrickIndices(Cache);
}
#endif
}
namespace
{
@@ -6278,6 +6662,24 @@ namespace
VoxelPassageGeometry::SealSafetyMarginVoxels;
}
#if WITH_EDITOR
void VoxelCaveMorphology::SetResearchBrickBinningEnabled(bool bEnabled)
{
GVoxelResearchBrickBinningEnabled = bEnabled;
}
bool VoxelCaveMorphology::IsResearchBrickBinningEnabled()
{
return GVoxelResearchBrickBinningEnabled;
}
void VoxelCaveMorphology::SetResearchBrickRoomReachScale(float Scale)
{
GVoxelResearchBrickRoomReachScale = VoxelMath::IsFinite(Scale)
? FMath::Clamp(Scale, 0.0f, 1.0f) : 1.0f;
}
#endif
FVector VoxelCaveMorphology::ApplyCaveWarp(
const FVector& WorldPoint,
const FStrateGenerationParams& Params,
@@ -8702,9 +9104,12 @@ float VoxelCaveMorphology::EvaluateSDFCached(
int32 RoomCandidateCount = 0;
if (!VoxelDensityAblation::IsRoomSDFOff())
{
RoomCandidateCount = VF_ForEachSpatialCandidate(
Cache.RoomSpatialIndex, Cache.Rooms.Num(), WorldX, WorldY, EvaluateRoom,
bUseSpatialIndex);
RoomCandidateCount = VF_ForEachSDFCandidate(
Cache.RoomSpatialIndex,
#if WITH_EDITOR
Cache.RoomBrickIndex,
#endif
Cache.Rooms.Num(), WorldX, WorldY, WorldZ, EvaluateRoom, bUseSpatialIndex);
}
if (VoxelDensityProfile::AreCountersEnabled())
{
@@ -8742,9 +9147,13 @@ float VoxelCaveMorphology::EvaluateSDFCached(
};
if (!VoxelDensityAblation::IsRoomSDFOff())
{
VF_ForEachSpatialCandidate(
Cache.RoomFloorJoinSpatialIndex, Cache.RoomFloorJoins.Num(),
WorldX, WorldY, EvaluateJoin, bUseSpatialIndex);
VF_ForEachSDFCandidate(
Cache.RoomFloorJoinSpatialIndex,
#if WITH_EDITOR
Cache.RoomFloorJoinBrickIndex,
#endif
Cache.RoomFloorJoins.Num(), WorldX, WorldY, WorldZ,
EvaluateJoin, bUseSpatialIndex);
}
//=========================================================================
@@ -8839,10 +9248,14 @@ float VoxelCaveMorphology::EvaluateSDFCached(
int32 TunnelCandidateCount = 0;
if (!VoxelDensityAblation::IsTunnelSDFOff())
{
TunnelCandidateCount = VF_ForEachSpatialCandidate(
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), TunnelPosition.X, TunnelPosition.Y,
EvaluateTunnel,
bUseSpatialIndex);
TunnelCandidateCount = VF_ForEachSDFCandidate(
Cache.TunnelSpatialIndex,
#if WITH_EDITOR
Cache.TunnelBrickIndex,
#endif
Cache.Tunnels.Num(), static_cast<float>(TunnelPosition.X),
static_cast<float>(TunnelPosition.Y), static_cast<float>(TunnelPosition.Z),
EvaluateTunnel, bUseSpatialIndex);
}
if (VoxelDensityProfile::AreCountersEnabled())
{
@@ -9002,9 +9415,13 @@ FTunnelCoreWorldEvaluation VoxelCaveMorphology::EvaluateTunnelCoreWorld(
}
};
const int32 TunnelCandidateCount = VF_ForEachSpatialCandidate(
Cache.TunnelSpatialIndex, Cache.Tunnels.Num(), WorldX, WorldY, EvaluateTunnel,
bUseSpatialIndex);
const int32 TunnelCandidateCount = VF_ForEachSDFCandidate(
Cache.TunnelSpatialIndex,
#if WITH_EDITOR
Cache.TunnelBrickIndex,
#endif
Cache.Tunnels.Num(), WorldX, WorldY, WorldZ,
EvaluateTunnel, bUseSpatialIndex);
if (VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(