research(step2f): coherent caves pass every gate, worst 572 ms idle -- look still to be judged in game

Editor-only, default off (voxel.ResearchParameterizedCave 1; the live skip proofs now decline
automatically). Jittered elliptical chambers of varied size and height with rock between them,
narrow tunnels, capped shafts, occasional pillars/ledges/stalactites/stalagmites/hanging masses;
detail bands at 48/24/12 samples (shortest 3 m), bound 3.822.
Gates, step 2e -> step 2f R29 (worst of 8 profiles), each rejecting 2e:
  crumbs < 1 m^3 2,290 -> 0; dihedral >45 deg 40.7% -> 2.2%; lattice detail energy 0.83 -> 0.007
  (RMS floor kept: 0.484 >= 0.40); LOD1 distance p95 0.709 -> 0.377 samples; grid autocorrelation
  0.667 -> 0.348; solid share 2.7% -> 79-82%; roof/floor openings 26,657 -> 0.
Speed, 259^3 ring, six workers, idle machine (20 GB free): 527-572 ms, worst TerracedBasin 572.3 ms
(R27 591 ms, R28 603 ms: run-to-run spread is ~5%). Proof audit 0 violations, undersized control
caught; seams pass; 1 vs 6 worker hash 876069a813c83cee; TraversalSeeds pass.
Captures are UNLIT (lit runs showed only sky; three D3D12 device-removed crashes): they show the
material's tiling, not the cave forms, so the look is not yet judged.

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-10-01 05:50:20 +02:00
1 parent d6555df5fa
commit ca78352e43
16 files changed
+8070 -80

No files matched your search

@@ -19,7 +19,8 @@ namespace VoxelForgeRedefinedDensityField
namespace
{
constexpr float LegacyDetailAmplitude = 1.4f + 0.9f + 0.45f + 2.5f;
constexpr float CoherentDetailAmplitude = 2.0f + 1.2f + 0.6f + 0.4f;
constexpr float CoherentDetailAmplitude = 1.82f + 1.092f + 0.546f + 0.364f;
constexpr float CoherentRockDensityBias = 0.10f;
constexpr float DetailBoundNumericalMargin = 1.0e-3f;
constexpr float BaseFieldLipschitzBound = 1.25f;
constexpr float ParameterizedFieldLipschitzBound = 1.60f;
@@ -271,14 +272,25 @@ namespace
return Broad * 1.4f + Medium * 0.9f + Fine * 0.45f + Micro * 2.5f;
}
// The shortest wavelength is eight lattice samples (two meters). Three
// interpolated scales and the smooth ridge response keep broad folds
// The shortest wavelength is twelve lattice samples (three meters). Three
// independently rotated, interpolated scales and the smooth ridge response keep broad folds
// while excluding the lattice-scale noise used by the frozen 2e field.
const float Broad = Noise3D(X, Y, Z, 32.0f, Seed ^ 0x68BC21EBu);
const float Medium = Noise3D(X, Y, Z, 16.0f, Seed ^ 0x02E5BE93u);
const float Fine = Noise3D(X, Y, Z, 8.0f, Seed ^ 0x967A889Bu);
// Independent orthonormal frames keep the value-noise bands smooth and bounded without
// exposing their axis-aligned interpolation cells as repeated square relief on the rock.
const float BroadX = 0.838671f * X - 0.493587f * Y + 0.230129f * Z;
const float BroadY = 0.544639f * X + 0.760095f * Y - 0.354003f * Z;
const float BroadZ = 0.422618f * Y + 0.906308f * Z;
const float MediumX = 0.956305f * X + 0.220661f * Y + 0.191825f * Z;
const float MediumY = -0.292372f * X + 0.721484f * Y + 0.627548f * Z;
const float MediumZ = 0.656059f * Y + 0.754710f * Z;
const float FineX = 0.325568f * X - 0.834725f * Y - 0.443914f * Z;
const float FineY = 0.945519f * X + 0.287381f * Y + 0.152824f * Z;
const float FineZ = -0.469472f * Y + 0.882948f * Z;
const float Broad = Noise3D(BroadX, BroadY, BroadZ, 48.0f, Seed ^ 0x68BC21EBu);
const float Medium = Noise3D(MediumX, MediumY, MediumZ, 24.0f, Seed ^ 0x02E5BE93u);
const float Fine = Noise3D(FineX, FineY, FineZ, 12.0f, Seed ^ 0x967A889Bu);
const float SmoothRidge = FMath::Sqrt(Fine * Fine + 0.0625f) - 0.25f;
return Broad * 2.0f + Medium * 1.2f + Fine * 0.6f + SmoothRidge * 0.4f;
return Broad * 1.82f + Medium * 1.092f + Fine * 0.546f + SmoothRidge * 0.364f;
}
FORCEINLINE float DistanceToPeriodicLine(float Position, float Spacing, float Offset)
@@ -869,6 +881,10 @@ namespace
{
FGeneratedCaveCell GeneratedCells[9];
const FGeneratedCaveCell* Cells[9] = {};
const FGeneratedCaveCell* TunnelCells[25] = {};
uint8 TunnelEdgeCellA[24] = {};
uint8 TunnelEdgeCellB[24] = {};
int32 TunnelEdgeCount = 0;
float RoomDistanceSquared[9] = {};
float RoomRadii[9] = {};
float RoomHorizontals[9] = {};
@@ -879,8 +895,8 @@ namespace
float SpireTipZ[9] = {};
float SpireBaseTopZ[9] = {};
float SpireTipBottomZ[9] = {};
float TunnelT[4] = {};
float TunnelHorizontal[4] = {};
float TunnelT[24] = {};
float TunnelHorizontal[24] = {};
float LandingDistanceSquared[8] = {};
float LandingDistance[8] = {};
float LandingBridgeT[8] = {};
@@ -1401,42 +1417,133 @@ namespace
}
if ((Cell.FeatureFlags & HasSpireFlag) != 0)
{
OutColumn.SpireBaseZ[Index] = Cell.FloorZ + 2.0f;
OutColumn.SpireBaseZ[Index] = Cell.FloorZ - 2.0f;
OutColumn.SpireTipZ[Index] = FMath::Min(
Cell.CeilingZ - 2.0f,
OutColumn.SpireBaseZ[Index] + Cell.SpireLength);
OutColumn.SpireBaseTopZ[Index] = Cell.CeilingZ - 2.0f;
OutColumn.SpireBaseTopZ[Index] = Cell.CeilingZ + 2.0f;
OutColumn.SpireTipBottomZ[Index] = FMath::Max(
Cell.FloorZ + 2.0f,
Cell.FloorZ - 2.0f,
OutColumn.SpireBaseTopZ[Index] - Cell.SpireLength);
}
}
const FGeneratedCaveCell& Centre = *OutColumn.Cells[4];
const int32 EdgeIndices[] = {1, 7, 3, 5};
const uint8 EdgeFlags[] = {
TunnelToNegativeY, TunnelToPositiveY,
TunnelToNegativeX, TunnelToPositiveX};
for (int32 EdgeIndex = 0; EdgeIndex < UE_ARRAY_COUNT(EdgeIndices); ++EdgeIndex)
for (int32 CellY = -1; CellY <= 1; ++CellY)
for (int32 CellX = -1; CellX <= 1; ++CellX)
{
OutColumn.TunnelHorizontal[EdgeIndex] = -FLT_MAX;
if ((Centre.OpenTunnelEdges & EdgeFlags[EdgeIndex]) == 0)
const int32 TunnelIndex = (CellY + 2) * 5 + CellX + 2;
const int32 RoomIndex = (CellY + 1) * 3 + CellX + 1;
OutColumn.TunnelCells[TunnelIndex] = OutColumn.Cells[RoomIndex];
}
if (IsLegacyStep2eAuditMode())
{
// Keep the frozen 2e baseline byte-for-byte in its original four-edge evaluation.
const FGeneratedCaveCell& Centre = *OutColumn.Cells[4];
const int32 EdgeIndices[] = {1, 7, 3, 5};
const int32 TunnelCellIndices[] = {7, 17, 11, 13};
const uint8 EdgeFlags[] = {
TunnelToNegativeY, TunnelToPositiveY,
TunnelToNegativeX, TunnelToPositiveX};
for (int32 EdgeIndex = 0; EdgeIndex < UE_ARRAY_COUNT(EdgeIndices); ++EdgeIndex)
{
continue;
if ((Centre.OpenTunnelEdges & EdgeFlags[EdgeIndex]) == 0)
{
continue;
}
const FGeneratedCaveCell& Next = *OutColumn.Cells[EdgeIndices[EdgeIndex]];
const float ABX = Next.CenterX - Centre.CenterX;
const float ABY = Next.CenterY - Centre.CenterY;
const float LengthSquared = ABX * ABX + ABY * ABY;
const float T = LengthSquared > 1.0e-8f
? FMath::Clamp(((X - Centre.CenterX) * ABX
+ (Y - Centre.CenterY) * ABY) / LengthSquared, 0.0f, 1.0f)
: 0.0f;
const float DX = X - (Centre.CenterX + T * ABX);
const float DY = Y - (Centre.CenterY + T * ABY);
OutColumn.TunnelEdgeCellA[OutColumn.TunnelEdgeCount] = 12;
OutColumn.TunnelEdgeCellB[OutColumn.TunnelEdgeCount] =
static_cast<uint8>(TunnelCellIndices[EdgeIndex]);
OutColumn.TunnelT[OutColumn.TunnelEdgeCount] = T;
OutColumn.TunnelHorizontal[OutColumn.TunnelEdgeCount] =
Plan.Generation.TunnelRadiusVoxels - FMath::Sqrt(DX * DX + DY * DY);
++OutColumn.TunnelEdgeCount;
}
}
else
{
const auto CacheTunnelEdge = [&](int32 AX, int32 AY, int32 BX, int32 BY)
{
const uint8 CellAIndex = static_cast<uint8>((AY + 2) * 5 + AX + 2);
const uint8 CellBIndex = static_cast<uint8>((BY + 2) * 5 + BX + 2);
const FGeneratedCaveCell& A = *OutColumn.TunnelCells[CellAIndex];
const FGeneratedCaveCell& B = *OutColumn.TunnelCells[CellBIndex];
const uint8 OpenEdgeFlag = AX != BX
? TunnelToPositiveX : TunnelToPositiveY;
if ((A.OpenTunnelEdges & OpenEdgeFlag) == 0)
{
return;
}
// Beyond this horizontal reach, even the largest supported detail and tunnel
// smooth-union rise cannot turn rock into air. Reject before calculating the
// point-to-segment distance.
constexpr float MaximumTunnelSignReach =
CoherentDetailAmplitude * 4.0f + 1.25f + 2.0f;
const float Reach = Plan.Generation.TunnelRadiusVoxels
+ MaximumTunnelSignReach;
const float MinX = FMath::Min(A.CenterX, B.CenterX);
const float MaxX = FMath::Max(A.CenterX, B.CenterX);
const float MinY = FMath::Min(A.CenterY, B.CenterY);
const float MaxY = FMath::Max(A.CenterY, B.CenterY);
const float GapX = X < MinX ? MinX - X : (X > MaxX ? X - MaxX : 0.0f);
const float GapY = Y < MinY ? MinY - Y : (Y > MaxY ? Y - MaxY : 0.0f);
if (GapX * GapX + GapY * GapY >= Reach * Reach)
{
return;
}
check(OutColumn.TunnelEdgeCount < UE_ARRAY_COUNT(OutColumn.TunnelT));
const float ABX = B.CenterX - A.CenterX;
const float ABY = B.CenterY - A.CenterY;
const float LengthSquared = ABX * ABX + ABY * ABY;
const float T = LengthSquared > 1.0e-8f
? FMath::Clamp(((X - A.CenterX) * ABX
+ (Y - A.CenterY) * ABY) / LengthSquared, 0.0f, 1.0f)
: 0.0f;
const float DX = X - (A.CenterX + T * ABX);
const float DY = Y - (A.CenterY + T * ABY);
const float Horizontal = Plan.Generation.TunnelRadiusVoxels
- FMath::Sqrt(DX * DX + DY * DY);
int32 InsertAt = OutColumn.TunnelEdgeCount;
while (InsertAt > 0
&& OutColumn.TunnelHorizontal[InsertAt - 1] < Horizontal)
{
OutColumn.TunnelEdgeCellA[InsertAt] =
OutColumn.TunnelEdgeCellA[InsertAt - 1];
OutColumn.TunnelEdgeCellB[InsertAt] =
OutColumn.TunnelEdgeCellB[InsertAt - 1];
OutColumn.TunnelT[InsertAt] = OutColumn.TunnelT[InsertAt - 1];
OutColumn.TunnelHorizontal[InsertAt] =
OutColumn.TunnelHorizontal[InsertAt - 1];
--InsertAt;
}
OutColumn.TunnelEdgeCellA[InsertAt] = CellAIndex;
OutColumn.TunnelEdgeCellB[InsertAt] = CellBIndex;
OutColumn.TunnelT[InsertAt] = T;
OutColumn.TunnelHorizontal[InsertAt] = Horizontal;
++OutColumn.TunnelEdgeCount;
};
// Every undirected edge whose endpoints lie in the room-query 3x3 neighborhood is
// considered once. This retains the old base cell's edges when the query base shifts.
for (int32 CellY = -1; CellY <= 1; ++CellY)
for (int32 CellX = -1; CellX < 1; ++CellX)
{
CacheTunnelEdge(CellX, CellY, CellX + 1, CellY);
}
for (int32 CellX = -1; CellX <= 1; ++CellX)
for (int32 CellY = -1; CellY < 1; ++CellY)
{
CacheTunnelEdge(CellX, CellY, CellX, CellY + 1);
}
const FGeneratedCaveCell& Next = *OutColumn.Cells[EdgeIndices[EdgeIndex]];
const float ABX = Next.CenterX - Centre.CenterX;
const float ABY = Next.CenterY - Centre.CenterY;
const float LengthSquared = ABX * ABX + ABY * ABY;
const float T = LengthSquared > 1.0e-8f
? FMath::Clamp(((X - Centre.CenterX) * ABX
+ (Y - Centre.CenterY) * ABY) / LengthSquared, 0.0f, 1.0f)
: 0.0f;
const float DX = X - (Centre.CenterX + T * ABX);
const float DY = Y - (Centre.CenterY + T * ABY);
OutColumn.TunnelT[EdgeIndex] = T;
OutColumn.TunnelHorizontal[EdgeIndex] = Plan.Generation.TunnelRadiusVoxels
- FMath::Sqrt(DX * DX + DY * DY);
}
OutColumn.bHasLandingXYCache = Plan.Landings.Num() <= UE_ARRAY_COUNT(
@@ -1511,21 +1618,66 @@ namespace
ColumnContext->RoomHorizontals[Index],
*ColumnContext, Index), 6.0f);
}
const FGeneratedCaveCell& Centre = *ColumnContext->Cells[4];
const int32 EdgeIndices[] = {1, 7, 3, 5};
const uint8 EdgeFlags[] = {
TunnelToNegativeY, TunnelToPositiveY,
TunnelToNegativeX, TunnelToPositiveX};
for (int32 EdgeIndex = 0; EdgeIndex < UE_ARRAY_COUNT(EdgeIndices); ++EdgeIndex)
const bool bLegacyStep2eAuditMode = IsLegacyStep2eAuditMode();
if (bLegacyStep2eAuditMode)
{
if ((Centre.OpenTunnelEdges & EdgeFlags[EdgeIndex]) != 0)
for (int32 EdgeIndex = 0; EdgeIndex < ColumnContext->TunnelEdgeCount; ++EdgeIndex)
{
const FGeneratedCaveCell& A = *ColumnContext->TunnelCells[
ColumnContext->TunnelEdgeCellA[EdgeIndex]];
const FGeneratedCaveCell& B = *ColumnContext->TunnelCells[
ColumnContext->TunnelEdgeCellB[EdgeIndex]];
Macro = SmoothMaximum(Macro, GeneratedTunnelEdge(
Plan, Centre, *ColumnContext->Cells[EdgeIndices[EdgeIndex]], Z,
ColumnContext->TunnelT[EdgeIndex],
Plan, A, B, Z, ColumnContext->TunnelT[EdgeIndex],
ColumnContext->TunnelHorizontal[EdgeIndex]), 5.0f);
}
}
else
{
// Far inside solid rock, density magnitude is only a proof margin. Capping it at
// -64 preserves every sign even at the supported 4x detail scale, and makes tunnels
// at least 5 units below that floor mathematically irrelevant to the smooth union.
Macro = FMath::Max(Macro, -64.0f);
const float MinimumTunnelFloor =
Plan.GenerationFloorZ - Plan.Generation.FloorReliefVoxels;
const float MaximumTunnelFloor =
Plan.GenerationFloorZ + Plan.Generation.FloorReliefVoxels;
const float VerticalTunnelUpperBound = FMath::Min(
Z - MinimumTunnelFloor,
MaximumTunnelFloor + 2.0f * Plan.Generation.TunnelHalfHeightVoxels - Z);
constexpr float MaximumDetailAtSupportedScale = CoherentDetailAmplitude * 4.0f;
constexpr float MaximumSmoothUnionRise = 1.25f;
constexpr float SolidTunnelMargin =
MaximumDetailAtSupportedScale + MaximumSmoothUnionRise + 2.0f;
const bool bNoTunnelCanChangeMacro = VerticalTunnelUpperBound <= Macro - 5.0f;
const bool bNoTunnelCanChangeSign = Macro <= -SolidTunnelMargin
&& VerticalTunnelUpperBound <= -SolidTunnelMargin;
float TunnelMacro = -FLT_MAX;
if (!bNoTunnelCanChangeMacro && !bNoTunnelCanChangeSign)
{
for (int32 EdgeIndex = 0;
EdgeIndex < ColumnContext->TunnelEdgeCount; ++EdgeIndex)
{
const float Horizontal = ColumnContext->TunnelHorizontal[EdgeIndex];
if (Horizontal <= Macro - 5.0f || Horizontal <= TunnelMacro)
{
// Remaining tunnel SDFs cannot change either the selected maximum or the
// room/tunnel smooth union; edges are ordered by descending horizontal bound.
break;
}
const FGeneratedCaveCell& A = *ColumnContext->TunnelCells[
ColumnContext->TunnelEdgeCellA[EdgeIndex]];
const FGeneratedCaveCell& B = *ColumnContext->TunnelCells[
ColumnContext->TunnelEdgeCellB[EdgeIndex]];
TunnelMacro = FMath::Max(TunnelMacro, GeneratedTunnelEdge(
Plan, A, B, Z, ColumnContext->TunnelT[EdgeIndex], Horizontal));
}
}
if (TunnelMacro > -FLT_MAX * 0.5f)
{
Macro = SmoothMaximum(Macro, TunnelMacro, 5.0f);
}
}
for (int32 Index = 0; Index < UE_ARRAY_COUNT(ColumnContext->Cells); ++Index)
{
@@ -1631,6 +1783,9 @@ namespace
Z - StrateBottom - StrateSealThickness,
StrateTop - StrateSealThickness - Z);
Macro = FMath::Min(Macro, BoundaryLimit);
// A 2.5 cm solidward offset closes isolated near-zero air samples and helps
// sub-cubic-meter rock contacts meet the surrounding wall without changing detail.
Macro -= CoherentRockDensityBias;
}
if (OutDetailScale != nullptr)
@@ -2117,6 +2272,60 @@ float EvaluateParameterized(const UVoxelGenerator& Generator,
OutPreEditDensity, DetailScale, nullptr, true, nullptr);
}
float EvaluateParameterizedMacroOnly(
const UVoxelGenerator& Generator, float WorldX, float WorldY, float WorldZ)
{
EnsurePlan(Generator);
const FIntVector Chunk(
FMath::FloorToInt(WorldX / CHUNK_SIZE),
FMath::FloorToInt(WorldY / CHUNK_SIZE),
FMath::FloorToInt(WorldZ / CHUNK_SIZE));
const FStratePlan* Plan = FindStrate(Chunk);
return Plan != nullptr
? EvaluateParameterizedMacro(*Plan, WorldX, WorldY, WorldZ, nullptr)
: 0.0f;
}
FString DescribeParameterizedMacroAt(
const UVoxelGenerator& Generator, float WorldX, float WorldY, float WorldZ)
{
EnsurePlan(Generator);
const FIntVector Chunk(
FMath::FloorToInt(WorldX / CHUNK_SIZE),
FMath::FloorToInt(WorldY / CHUNK_SIZE),
FMath::FloorToInt(WorldZ / CHUNK_SIZE));
const FStratePlan* Plan = FindStrate(Chunk);
if (Plan == nullptr)
{
return TEXT("no-plan");
}
FParameterizedColumnContext Column;
BuildParameterizedColumnContext(*Plan, WorldX, WorldY, nullptr, Column);
const FIntPoint BaseCell = GenerationCellAt(*Plan, WorldX, WorldY);
FString Description = FString::Printf(
TEXT("base=(%d,%d); cells="), BaseCell.X, BaseCell.Y);
for (int32 Index = 0; Index < UE_ARRAY_COUNT(Column.Cells); ++Index)
{
const FGeneratedCaveCell& Cell = *Column.Cells[Index];
const float Room = GeneratedRoomCell(Cell, WorldX, WorldY, WorldZ,
Column.RoomRadii[Index], Column.RoomHorizontals[Index], Column, Index);
Description += FString::Printf(
TEXT("(%d,%d,r=%.2f,h=%.2f,v=%.2f,room=%.2f,flags=%u,edges=%u)"),
Cell.CellX, Cell.CellY, Cell.RadiusX, Column.RoomHorizontals[Index],
FMath::Min(WorldZ - Column.RoomFloors[Index], Cell.CeilingZ - WorldZ),
Room, static_cast<uint32>(Cell.FeatureFlags),
static_cast<uint32>(Cell.OpenTunnelEdges));
if (Index + 1 < UE_ARRAY_COUNT(Column.Cells))
{
Description += TEXT(",");
}
}
Description += FString::Printf(TEXT(";macro=%.3f"),
EvaluateParameterizedMacro(*Plan, WorldX, WorldY, WorldZ, nullptr));
return Description;
}
float EvaluateParameterizedDetail(
const UVoxelGenerator& Generator, float WorldX, float WorldY, float WorldZ)
{