Windows MSVC catch-up after abyss L15-L61 (no Unreal there): 64-bit clipmap shift (C4334), qualified VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel, UHT metadata and duplicate-signature fixes. Optional CoreGeometry section on UVoxelStrateDefinition (native caves, lattice, profiled heightfields, named stamps, rooms/tunnels; off/empty by default), resolved after passage planning. VoxelForgeAssetVersion 2 converts strate height/transition/gap to metres on load (exact, 0 m change on the owner's 5 assets; packages never saved). Gates pass (SurfaceWorld, FloatingIslands, Core-off, M2, W1/W8/W16). KNOWN REGRESSION vs L14 (2026-10-20, same command, same PC): full set 3.9 -> 5.37 s, worker-seconds 15.8 -> 30, LOD0 1.11 -> 1.69 s, ring 0.50 -> 0.80 s; not yet attributed (Core L15-L61 vs Unreal side). Report: Standalone/Research/WorldAssemblerM3/Unreal1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
186 lines
7.4 KiB
C++
186 lines
7.4 KiB
C++
#pragma once
|
|
|
|
#include "VoxelTypes.h"
|
|
|
|
namespace VoxelClipmapDesiredTiles
|
|
{
|
|
FORCEINLINE int64 FloorDivWide(int64 Value, int64 Divisor)
|
|
{
|
|
if (Divisor <= 0) { return 0; }
|
|
const int64 Quotient = Value / Divisor;
|
|
const int64 Remainder = Value % Divisor;
|
|
return Remainder < 0 ? Quotient - 1 : Quotient;
|
|
}
|
|
|
|
FORCEINLINE int64 CeilDivWide(int64 Value, int64 Divisor)
|
|
{
|
|
if (Divisor <= 0) { return 0; }
|
|
const int64 Quotient = Value / Divisor;
|
|
const int64 Remainder = Value % Divisor;
|
|
return Remainder > 0 ? Quotient + 1 : Quotient;
|
|
}
|
|
|
|
FORCEINLINE int32 ClampInt32(int64 Value)
|
|
{
|
|
return static_cast<int32>(FMath::Clamp<int64>(Value, MIN_int32, MAX_int32));
|
|
}
|
|
|
|
struct FParameters
|
|
{
|
|
int32 ClipRadius = 3;
|
|
int32 MaxClipLevel = 4;
|
|
int32 RenderDistanceChunks = 0;
|
|
};
|
|
|
|
FORCEINLINE int32 FloorDiv(int32 Value, int32 Divisor)
|
|
{
|
|
if (Divisor <= 0) { return 0; }
|
|
const int32 Quotient = Value / Divisor;
|
|
const int32 Remainder = Value % Divisor;
|
|
return Remainder < 0 ? Quotient - 1 : Quotient;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (Divisor <= 0) { return 0; }
|
|
const int32 Quotient = Value / Divisor;
|
|
const int32 Remainder = Value % Divisor;
|
|
return Remainder > 0 ? Quotient + 1 : Quotient;
|
|
}
|
|
|
|
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 int64 SafeRadius = FMath::Max<int64>(0, Radius);
|
|
const int64 SafeAlignment = FMath::Max<int64>(1, Alignment);
|
|
const int64 BaseMin = static_cast<int64>(CenterAxis) - SafeRadius;
|
|
const int64 BaseEnd = static_cast<int64>(CenterAxis) + SafeRadius + 1;
|
|
OutAxisMin = ClampInt32(FloorDivWide(BaseMin, SafeAlignment) * SafeAlignment);
|
|
OutAxisMax = ClampInt32(CeilDivWide(BaseEnd, SafeAlignment) * SafeAlignment - 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;
|
|
}
|
|
const int32 SafeLevel = FMath::Clamp(MaxLevel, 0, 8);
|
|
const int64 Scale = int64{1} << static_cast<uint32>(SafeLevel);
|
|
const int64 Numerator = static_cast<int64>(Parameters.RenderDistanceChunks)
|
|
+ Scale - 1;
|
|
const int64 DesiredRadius = Numerator / Scale;
|
|
return static_cast<int32>(FMath::Clamp<int64>(
|
|
FMath::Max<int64>(Radius, DesiredRadius), 0, MAX_int32));
|
|
}
|
|
|
|
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 = ClampInt32(static_cast<int64>(CenterChunkZ) - ViewDistanceDown);
|
|
OutMaxChunkZ = ClampInt32(static_cast<int64>(CenterChunkZ) + ViewDistanceUp);
|
|
if (bHasStrate)
|
|
{
|
|
OutMinChunkZ = ClampInt32(static_cast<int64>(StrateBottomChunkZ) - StrateViewMarginChunks);
|
|
OutMaxChunkZ = ClampInt32(static_cast<int64>(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 = static_cast<int32>(uint32{1} << static_cast<uint32>(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);
|
|
|
|
int64 TileMinZ = RingMin.Z;
|
|
int64 TileMaxZ = RingMax.Z;
|
|
if (LevelRadius > Radius && MinChunkZ != MIN_int32)
|
|
{
|
|
TileMinZ = FMath::Max<int64>(TileMinZ, FloorDiv(MinChunkZ, Pow));
|
|
TileMaxZ = FMath::Min<int64>(TileMaxZ, FloorDiv(MaxChunkZ, Pow));
|
|
}
|
|
|
|
for (int64 Z = TileMinZ; Z <= TileMaxZ; ++Z)
|
|
for (int64 Y = RingMin.Y; Y <= RingMax.Y; ++Y)
|
|
for (int64 X = RingMin.X; X <= RingMax.X; ++X)
|
|
{
|
|
const FIntVector Tile(static_cast<int32>(X), static_cast<int32>(Y),
|
|
static_cast<int32>(Z));
|
|
const int64 TileBottomChunkZ = static_cast<int64>(Tile.Z) * Pow;
|
|
const int64 TileTopChunkZ = (static_cast<int64>(Tile.Z) + 1) * Pow - 1;
|
|
if (TileTopChunkZ < MinChunkZ || TileBottomChunkZ > MaxChunkZ)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Level > 0)
|
|
{
|
|
const bool bCovered =
|
|
(2 * static_cast<int64>(Tile.X) >= PreviousRingMin.X)
|
|
&& (2 * static_cast<int64>(Tile.X) + 1 <= PreviousRingMax.X)
|
|
&& (2 * static_cast<int64>(Tile.Y) >= PreviousRingMin.Y)
|
|
&& (2 * static_cast<int64>(Tile.Y) + 1 <= PreviousRingMax.Y)
|
|
&& (2 * static_cast<int64>(Tile.Z) >= PreviousRingMin.Z)
|
|
&& (2 * static_cast<int64>(Tile.Z) + 1 <= PreviousRingMax.Z);
|
|
if (bCovered)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
OutTiles.Emplace(Tile, Level);
|
|
}
|
|
|
|
PreviousRingMin = RingMin;
|
|
PreviousRingMax = RingMax;
|
|
}
|
|
}
|
|
}
|