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:
1 parent
3b4d6855fe
commit
41a9265ebd
8 files changed
+1693
-31
No files matched your search
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user