The far sheet ring was already gone from the runtime; this removes what was left of it: - UVoxelSettings::bFarSheetRing / FarSheetSpanLevels, the voxel.TestFarSheetRing switch, and their log/trace/dump fields. - The sheet branch of VoxelClipmapDesiredTiles (FParameters sheet fields, OuterShell sheet level, sheet ring emission). Output for the runtime configuration (sheet off) is unchanged: same rings, same alignment. - Tests: VerticalStrateReach now asserts the sheet-free selector (its expected counts were stale since the aligned-ring change85a803b: LOD0 343 -> 512); ClipmapNoOverlap drops the sheet sweep cases and the frozen7486c29oracle's sheet branch. New expected values were computed with a Python port of the selector that reproduces the test's existing 635/594487 and 508/74295 measurements exactly; all 5,145 sweep cases pass (no overlap, full coverage). - Tools/VoxelForgeTest.ps1 no longer passes voxel.TestFarSheetRing or reports far_sheet_ring. The sheet_tile dump field is kept (the parity compare reads it). Also removes settings nothing reads: CeilingViewMultiplier, CeilingBandChunks (pre-clipmap ceiling band) and DensityVolumeMaxTasks (the volume fill runs on its own thread). Saved assets that still carry these values load normally; the values are ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
153 lines
5.8 KiB
C++
153 lines
5.8 KiB
C++
#pragma once
|
|
|
|
#include "VoxelTypes.h"
|
|
|
|
namespace VoxelClipmapDesiredTiles
|
|
{
|
|
struct FParameters
|
|
{
|
|
int32 ClipRadius = 3;
|
|
int32 MaxClipLevel = 4;
|
|
int32 RenderDistanceChunks = 0;
|
|
};
|
|
|
|
FORCEINLINE int32 FloorDiv(int32 Value, int32 Divisor)
|
|
{
|
|
return Value >= 0 ? (Value / Divisor) : -(((-Value) + Divisor - 1) / Divisor);
|
|
}
|
|
|
|
FORCEINLINE FIntVector FloorDiv(const FIntVector& Value, int32 Divisor)
|
|
{
|
|
return FIntVector(FloorDiv(Value.X, Divisor), FloorDiv(Value.Y, Divisor),
|
|
FloorDiv(Value.Z, Divisor));
|
|
}
|
|
|
|
FORCEINLINE int32 CeilDiv(int32 Value, int32 Divisor)
|
|
{
|
|
return -FloorDiv(-Value, Divisor);
|
|
}
|
|
|
|
FORCEINLINE void AlignedRingBounds(const FIntVector& Center, int32 Radius, int32 Alignment,
|
|
FIntVector& OutMin, FIntVector& OutMax)
|
|
{
|
|
const auto AlignAxis = [Radius, Alignment](int32 CenterAxis,
|
|
int32& OutAxisMin, int32& OutAxisMax)
|
|
{
|
|
const int32 BaseMin = CenterAxis - Radius;
|
|
const int32 BaseEnd = CenterAxis + Radius + 1;
|
|
OutAxisMin = FloorDiv(BaseMin, Alignment) * Alignment;
|
|
OutAxisMax = CeilDiv(BaseEnd, Alignment) * Alignment - 1;
|
|
};
|
|
AlignAxis(Center.X, OutMin.X, OutMax.X);
|
|
AlignAxis(Center.Y, OutMin.Y, OutMax.Y);
|
|
AlignAxis(Center.Z, OutMin.Z, OutMax.Z);
|
|
}
|
|
|
|
FORCEINLINE int32 OuterShellRadius(const FParameters& Parameters, int32 Radius, int32 MaxLevel)
|
|
{
|
|
if (Parameters.RenderDistanceChunks <= 0)
|
|
{
|
|
return Radius;
|
|
}
|
|
return FMath::Max(Radius,
|
|
(Parameters.RenderDistanceChunks + (1 << MaxLevel) - 1) >> MaxLevel);
|
|
}
|
|
|
|
FORCEINLINE void OuterShell(const FParameters& Parameters, int32 Radius, int32 MaxLevel,
|
|
int32& OutLevel, int32& OutRadius)
|
|
{
|
|
OutLevel = MaxLevel;
|
|
OutRadius = OuterShellRadius(Parameters, Radius, MaxLevel);
|
|
}
|
|
|
|
FORCEINLINE void ResolveVerticalBounds(
|
|
int32 CenterChunkZ, int32 ViewDistanceUp, int32 ViewDistanceDown,
|
|
int32 StrateViewMarginChunks, bool bClampToStrate, bool bHasStrate,
|
|
int32 StrateTopChunkZ, int32 StrateBottomChunkZ,
|
|
int32& OutMinChunkZ, int32& OutMaxChunkZ)
|
|
{
|
|
OutMinChunkZ = MIN_int32;
|
|
OutMaxChunkZ = MAX_int32;
|
|
if (!bClampToStrate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OutMinChunkZ = CenterChunkZ - ViewDistanceDown;
|
|
OutMaxChunkZ = CenterChunkZ + ViewDistanceUp;
|
|
if (bHasStrate)
|
|
{
|
|
OutMinChunkZ = StrateBottomChunkZ - StrateViewMarginChunks;
|
|
OutMaxChunkZ = StrateTopChunkZ + StrateViewMarginChunks;
|
|
}
|
|
}
|
|
|
|
FORCEINLINE void Build(const FIntVector& Center, int32 MinChunkZ, int32 MaxChunkZ,
|
|
const FParameters& Parameters, TArray<FVoxelTileKey>& OutTiles)
|
|
{
|
|
OutTiles.Reset();
|
|
const int32 Radius = FMath::Max(1, Parameters.ClipRadius);
|
|
const int32 MaxLevel = FMath::Clamp(Parameters.MaxClipLevel, 0, 8);
|
|
const int32 OuterRadius = OuterShellRadius(Parameters, Radius, MaxLevel);
|
|
FIntVector PreviousRingMin = FIntVector::ZeroValue;
|
|
FIntVector PreviousRingMax = FIntVector::ZeroValue;
|
|
|
|
for (int32 Level = 0; Level <= MaxLevel; ++Level)
|
|
{
|
|
const int32 Pow = 1 << Level;
|
|
const FIntVector CenterLevel = FloorDiv(Center, Pow);
|
|
const int32 LevelRadius = (Level == MaxLevel) ? OuterRadius : Radius;
|
|
|
|
// Every finer shell must end on a complete child-pair boundary so a coarser tile is
|
|
// either wholly inside it or wholly outside it. The outermost ring has no coarser
|
|
// neighbour and needs no alignment.
|
|
const int32 RingAlignment = Level < MaxLevel ? 2 : 1;
|
|
FIntVector RingMin, RingMax;
|
|
AlignedRingBounds(CenterLevel, LevelRadius, RingAlignment, RingMin, RingMax);
|
|
|
|
int32 DeltaZMin = RingMin.Z - CenterLevel.Z;
|
|
int32 DeltaZMax = RingMax.Z - CenterLevel.Z;
|
|
if (LevelRadius > Radius && MinChunkZ != MIN_int32)
|
|
{
|
|
DeltaZMin = FMath::Max(DeltaZMin, FloorDiv(MinChunkZ, Pow) - CenterLevel.Z);
|
|
DeltaZMax = FMath::Min(DeltaZMax, FloorDiv(MaxChunkZ, Pow) - CenterLevel.Z);
|
|
}
|
|
|
|
for (int32 DeltaZ = DeltaZMin; DeltaZ <= DeltaZMax; ++DeltaZ)
|
|
for (int32 DeltaY = RingMin.Y - CenterLevel.Y;
|
|
DeltaY <= RingMax.Y - CenterLevel.Y; ++DeltaY)
|
|
for (int32 DeltaX = RingMin.X - CenterLevel.X;
|
|
DeltaX <= RingMax.X - CenterLevel.X; ++DeltaX)
|
|
{
|
|
const FIntVector Tile = CenterLevel + FIntVector(DeltaX, DeltaY, DeltaZ);
|
|
const int32 TileMinZ = Tile.Z << Level;
|
|
const int32 TileMaxZ = ((Tile.Z + 1) << Level) - 1;
|
|
if (TileMaxZ < MinChunkZ || TileMinZ > MaxChunkZ)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Level > 0)
|
|
{
|
|
const bool bCovered =
|
|
(2 * Tile.X >= PreviousRingMin.X)
|
|
&& (2 * Tile.X + 1 <= PreviousRingMax.X)
|
|
&& (2 * Tile.Y >= PreviousRingMin.Y)
|
|
&& (2 * Tile.Y + 1 <= PreviousRingMax.Y)
|
|
&& (2 * Tile.Z >= PreviousRingMin.Z)
|
|
&& (2 * Tile.Z + 1 <= PreviousRingMax.Z);
|
|
if (bCovered)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
OutTiles.Emplace(Tile, Level);
|
|
}
|
|
|
|
PreviousRingMin = RingMin;
|
|
PreviousRingMax = RingMax;
|
|
}
|
|
}
|
|
}
|