wip(step2f): coherent field -- flakes, speckle, grid, roof fixed; 3 crumbs + 636 ms left; last edits UNCOMPILED
Stopped by low memory (another app at 14 GB; processes failed with 0xC0000142). r6 (compiled and measured, owner playing during the run), step 2e -> step 2f r6: crumbs < 1 m^3 (max per profile) 2,898 -> 3 FAIL (0,0,0,0,2,1,3,2) flakes (shard share / p95 angle) 40.7% / 84.2 deg -> 2.1% / 33.1 deg pass lattice-scale detail energy 0.832 -> 0.016 pass grid signature (autocorrelation) 0.666 -> 0.349 pass min solid share / roof openings 2.7%, 26,657 -> 79.2%, 0 pass worst profile ring time 642.8 -> 636.3 ms FAIL (6 workers, 259^3) Step 2e measured 642.8 ms here vs 570 ms idle earlier: the game was costing ~12% during this run. Brick proofs + audit, seams + injected seam, 1 vs 6 worker determinism pass. After r6, feature probabilities were lowered to remove the last crumbs: NOT compiled, NOT measured. TraversalSeeds, in-game captures and the LOD-rejection figure for 2e are still to do. Reject screenshots included. 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
0a7492ffab
commit
d6555df5fa
9 files changed
+1665
-65
No files matched your search
@@ -12,12 +12,14 @@
|
||||
#include "VoxelTypes.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
namespace VoxelForgeRedefinedDensityField
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr float DetailAmplitude = 1.4f + 0.9f + 0.45f + 2.5f;
|
||||
constexpr float LegacyDetailAmplitude = 1.4f + 0.9f + 0.45f + 2.5f;
|
||||
constexpr float CoherentDetailAmplitude = 2.0f + 1.2f + 0.6f + 0.4f;
|
||||
constexpr float DetailBoundNumericalMargin = 1.0e-3f;
|
||||
constexpr float BaseFieldLipschitzBound = 1.25f;
|
||||
constexpr float ParameterizedFieldLipschitzBound = 1.60f;
|
||||
@@ -54,7 +56,8 @@ namespace
|
||||
FGenerationParameters Parameters;
|
||||
};
|
||||
|
||||
const FParameterProfile GParameterProfiles[] = {
|
||||
// Frozen step 2e profiles are retained only for the step 2f before/after audit.
|
||||
const FParameterProfile GLegacyStep2eProfiles[] = {
|
||||
{ TEXT("OpenVaults"), { 176.0f, 0.62f, 0.76f, 340.0f, 12.0f, 40.0f, 36.0f,
|
||||
0.35f, 0.14f, 22.0f, 0.04f, 0.12f, 0.08f, 0.15f, 0.00f, 0.0f, 0.50f } },
|
||||
{ TEXT("TightNetwork"), { 104.0f, 0.52f, 0.64f, 256.0f, 8.0f, 20.0f, 22.0f,
|
||||
@@ -73,6 +76,38 @@ namespace
|
||||
0.40f, 0.55f, 20.0f, 0.45f, 0.68f, 0.50f, 0.75f, 0.42f, 28.0f, 0.40f } },
|
||||
};
|
||||
|
||||
const FParameterProfile GParameterProfiles[] = {
|
||||
{ TEXT("OpenVaults"), { 176.0f, 0.31f, 0.40f, 132.0f, 8.0f, 22.0f, 18.0f,
|
||||
0.12f, 0.08f, 16.0f, 0.02f, 0.06f, 0.04f, 0.22f, 0.00f, 0.0f, 0.50f } },
|
||||
{ TEXT("TightNetwork"), { 104.0f, 0.27f, 0.35f, 104.0f, 5.0f, 18.0f, 14.0f,
|
||||
0.28f, 0.12f, 15.0f, 0.02f, 0.06f, 0.02f, 0.14f, 0.00f, 0.0f, 0.50f } },
|
||||
{ TEXT("ChamberHalls"), { 144.0f, 0.32f, 0.41f, 136.0f, 10.0f, 21.0f, 17.0f,
|
||||
0.18f, 0.10f, 16.0f, 0.03f, 0.06f, 0.06f, 0.20f, 0.05f, 16.0f, 0.40f } },
|
||||
{ TEXT("ShaftForest"), { 128.0f, 0.28f, 0.36f, 120.0f, 7.0f, 19.0f, 15.0f,
|
||||
0.16f, 0.42f, 20.0f, 0.03f, 0.06f, 0.03f, 0.22f, 0.00f, 0.0f, 0.50f } },
|
||||
{ TEXT("FloatingArchipelago"), { 136.0f, 0.29f, 0.37f, 116.0f, 6.0f, 19.0f, 15.0f,
|
||||
0.12f, 0.08f, 15.0f, 0.02f, 0.06f, 0.08f, 0.20f, 0.00f, 0.0f, 0.50f } },
|
||||
{ TEXT("PillaredCathedral"), { 160.0f, 0.31f, 0.40f, 132.0f, 8.0f, 22.0f, 17.0f,
|
||||
0.20f, 0.08f, 16.0f, 0.16f, 0.06f, 0.04f, 0.22f, 0.00f, 0.0f, 0.50f } },
|
||||
{ TEXT("TerracedBasin"), { 136.0f, 0.30f, 0.38f, 124.0f, 8.0f, 20.0f, 16.0f,
|
||||
0.14f, 0.10f, 16.0f, 0.04f, 0.06f, 0.04f, 0.18f, 0.46f, 24.0f, 0.30f } },
|
||||
{ TEXT("HyperComplex"), { 96.0f, 0.28f, 0.37f, 104.0f, 9.0f, 18.0f, 14.0f,
|
||||
0.26f, 0.32f, 17.0f, 0.08f, 0.06f, 0.05f, 0.18f, 0.12f, 12.0f, 0.40f } },
|
||||
};
|
||||
|
||||
std::atomic<bool> GUseLegacyStep2eAuditMode{false};
|
||||
|
||||
FORCEINLINE bool IsLegacyStep2eAuditMode()
|
||||
{
|
||||
return GUseLegacyStep2eAuditMode.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
FORCEINLINE float CurrentDetailAmplitude(bool bParameterized)
|
||||
{
|
||||
return bParameterized && !IsLegacyStep2eAuditMode()
|
||||
? CoherentDetailAmplitude : LegacyDetailAmplitude;
|
||||
}
|
||||
|
||||
struct FLandingSite
|
||||
{
|
||||
FVector Position = FVector::ZeroVector;
|
||||
@@ -161,6 +196,7 @@ namespace
|
||||
uint32 LayoutVersion = 0xFFFFFFFFu;
|
||||
int32 Seed = INT32_MIN;
|
||||
int32 ParameterSet = INT32_MIN;
|
||||
bool bLegacyStep2eAuditMode = false;
|
||||
TArray<FStratePlan> Strates;
|
||||
TArray<FPassageBound> PassageBounds;
|
||||
};
|
||||
@@ -223,13 +259,26 @@ namespace
|
||||
return FMath::Lerp(Z0Y0, Z0Y1, AZ);
|
||||
}
|
||||
|
||||
FORCEINLINE float DetailAt(float X, float Y, float Z, uint32 Seed)
|
||||
FORCEINLINE float DetailAt(
|
||||
float X, float Y, float Z, uint32 Seed, bool bCoherentStep2f)
|
||||
{
|
||||
const float Broad = Noise3D(X, Y, Z, 8.0f, Seed ^ 0x68BC21EBu);
|
||||
const float Medium = Noise3D(X, Y, Z, 4.0f, Seed ^ 0x02E5BE93u);
|
||||
const float Fine = Noise3D(X, Y, Z, 2.0f, Seed ^ 0x967A889Bu);
|
||||
const float Micro = Noise3D(X, Y, Z, 1.0f, Seed ^ 0xA511E9B3u);
|
||||
return Broad * 1.4f + Medium * 0.9f + Fine * 0.45f + Micro * 2.5f;
|
||||
if (!bCoherentStep2f || IsLegacyStep2eAuditMode())
|
||||
{
|
||||
const float Broad = Noise3D(X, Y, Z, 8.0f, Seed ^ 0x68BC21EBu);
|
||||
const float Medium = Noise3D(X, Y, Z, 4.0f, Seed ^ 0x02E5BE93u);
|
||||
const float Fine = Noise3D(X, Y, Z, 2.0f, Seed ^ 0x967A889Bu);
|
||||
const float Micro = Noise3D(X, Y, Z, 1.0f, Seed ^ 0xA511E9B3u);
|
||||
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
|
||||
// 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);
|
||||
const float SmoothRidge = FMath::Sqrt(Fine * Fine + 0.0625f) - 0.25f;
|
||||
return Broad * 2.0f + Medium * 1.2f + Fine * 0.6f + SmoothRidge * 0.4f;
|
||||
}
|
||||
|
||||
FORCEINLINE float DistanceToPeriodicLine(float Position, float Spacing, float Offset)
|
||||
@@ -398,7 +447,7 @@ namespace
|
||||
// A 36-voxel bore (9 m at 25 cm) keeps the deterministic route tree visible to the
|
||||
// 2-voxel navigation/refinement probes while still allowing full-strength wall noise.
|
||||
const float ConnectorRadius = FMath::Max3(
|
||||
Params.ConnectorRadius, DetailAmplitude + 1.0f, 18.0f);
|
||||
Params.ConnectorRadius, LegacyDetailAmplitude + 1.0f, 18.0f);
|
||||
const float CellMinX = CellX * Spacing;
|
||||
const float CellMaxX = CellMinX + Spacing;
|
||||
const float CellMinY = CellY * Spacing;
|
||||
@@ -447,7 +496,7 @@ namespace
|
||||
const float Spacing = FMath::Max(Params.ShaftSpacing, 1.0f);
|
||||
const int32 BaseCellX = FMath::FloorToInt(X / Spacing);
|
||||
const int32 BaseCellY = FMath::FloorToInt(Y / Spacing);
|
||||
const float VerticalPad = DetailAmplitude + 1.0f;
|
||||
const float VerticalPad = LegacyDetailAmplitude + 1.0f;
|
||||
const float BottomZ = Params.StrateBottomWorldZ + Params.BoundarySealThickness;
|
||||
const float TopZ = Params.StrateTopWorldZ - Params.BoundarySealThickness;
|
||||
const float ShaftVertical = FMath::Min(Z - BottomZ, TopZ - Z);
|
||||
@@ -491,11 +540,13 @@ namespace
|
||||
const uint64 Lifetime = Manager->GetCacheLifetimeId();
|
||||
const uint32 Version = Manager->GetLayoutVersion();
|
||||
const int32 ParameterSet = CVarResearchParameterizedCaveSet.GetValueOnAnyThread();
|
||||
const bool bLegacyStep2eAuditMode = IsLegacyStep2eAuditMode();
|
||||
if (GPlanCache.Manager == Manager
|
||||
&& GPlanCache.ManagerLifetimeId == Lifetime
|
||||
&& GPlanCache.LayoutVersion == Version
|
||||
&& GPlanCache.Seed == Generator.Seed
|
||||
&& GPlanCache.ParameterSet == ParameterSet)
|
||||
&& GPlanCache.ParameterSet == ParameterSet
|
||||
&& GPlanCache.bLegacyStep2eAuditMode == bLegacyStep2eAuditMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -507,6 +558,7 @@ namespace
|
||||
GPlanCache.LayoutVersion = Version;
|
||||
GPlanCache.Seed = Generator.Seed;
|
||||
GPlanCache.ParameterSet = ParameterSet;
|
||||
GPlanCache.bLegacyStep2eAuditMode = bLegacyStep2eAuditMode;
|
||||
const TArray<FStrateSlot>& Layout = Manager->GetLayout();
|
||||
GPlanCache.Strates.Reserve(Layout.Num());
|
||||
for (const FStrateSlot& Slot : Layout)
|
||||
@@ -568,9 +620,12 @@ namespace
|
||||
: static_cast<int32>(Hash3(
|
||||
Plan.StrateIndex, Plan.MinChunkZ, 0x2E, StrateSeed)
|
||||
% UE_ARRAY_COUNT(GParameterProfiles));
|
||||
Plan.Generation = GParameterProfiles[Plan.GenerationProfile].Parameters;
|
||||
const float StrateHeight = FMath::Max(
|
||||
48.0f, Top - Bottom - 8.0f);
|
||||
Plan.Generation = (bLegacyStep2eAuditMode
|
||||
? GLegacyStep2eProfiles[Plan.GenerationProfile]
|
||||
: GParameterProfiles[Plan.GenerationProfile]).Parameters;
|
||||
const float StrateHeight = bLegacyStep2eAuditMode
|
||||
? FMath::Max(48.0f, Top - Bottom - 8.0f)
|
||||
: FMath::Max(48.0f, Top - Bottom - 64.0f);
|
||||
Plan.Generation.RoomHeightVoxels = FMath::Clamp(
|
||||
Plan.Generation.RoomHeightVoxels, 48.0f, StrateHeight);
|
||||
Plan.Generation.RoomPitchVoxels = FMath::Max(
|
||||
@@ -792,6 +847,8 @@ namespace
|
||||
float CeilingZ = 0.0f;
|
||||
float RadiusX = 0.0f;
|
||||
float RadiusY = 0.0f;
|
||||
float RoomCos = 1.0f;
|
||||
float RoomSin = 0.0f;
|
||||
float PillarX = 0.0f;
|
||||
float PillarY = 0.0f;
|
||||
float PillarRadius = 0.0f;
|
||||
@@ -800,6 +857,8 @@ namespace
|
||||
float FloatingY[2] = {};
|
||||
float FloatingZ[2] = {};
|
||||
float FloatingRadius[2] = {};
|
||||
float FloatingRadiusY[2] = {};
|
||||
float FloatingRadiusZ[2] = {};
|
||||
float SpireLength = 0.0f;
|
||||
float SpireRadius = 0.0f;
|
||||
uint8 FeatureFlags = 0;
|
||||
@@ -861,6 +920,19 @@ namespace
|
||||
OutY = -Plan.GridSin * X + Plan.GridCos * Y;
|
||||
}
|
||||
|
||||
FORCEINLINE void GeneratedCellGridCenter(
|
||||
const FStratePlan& Plan, int32 CellX, int32 CellY,
|
||||
float& OutX, float& OutY, bool bLegacyStep2eAuditMode)
|
||||
{
|
||||
const uint32 Hash = Hash3(CellX, CellY, Plan.StrateIndex, Plan.GenerationSeed);
|
||||
const float JitterScale = bLegacyStep2eAuditMode ? 0.08f : 1.00f;
|
||||
const float JitterX = (ParameterRoll(Hash, 0x91A2u) - 0.5f) * JitterScale;
|
||||
const float JitterY = (ParameterRoll(Hash, 0xB4C3u) - 0.5f) * JitterScale;
|
||||
const float Pitch = Plan.Generation.RoomPitchVoxels;
|
||||
OutX = Plan.GridOffsetX + (CellX + 0.5f + JitterX) * Pitch;
|
||||
OutY = Plan.GridOffsetY + (CellY + 0.5f + JitterY) * Pitch;
|
||||
}
|
||||
|
||||
FGeneratedCaveCell BuildGeneratedCell(
|
||||
const FStratePlan& Plan, int32 CellX, int32 CellY)
|
||||
{
|
||||
@@ -870,18 +942,48 @@ namespace
|
||||
const FGenerationParameters& P = Plan.Generation;
|
||||
const float Pitch = P.RoomPitchVoxels;
|
||||
const uint32 Hash = Hash3(CellX, CellY, Plan.StrateIndex, Plan.GenerationSeed);
|
||||
const float JitterX = (ParameterRoll(Hash, 0x91A2u) - 0.5f) * 0.08f;
|
||||
const float JitterY = (ParameterRoll(Hash, 0xB4C3u) - 0.5f) * 0.08f;
|
||||
const float LocalX = Plan.GridOffsetX + (CellX + 0.5f + JitterX) * Pitch;
|
||||
const float LocalY = Plan.GridOffsetY + (CellY + 0.5f + JitterY) * Pitch;
|
||||
const bool bLegacyStep2eAuditMode = IsLegacyStep2eAuditMode();
|
||||
float LocalX = 0.0f;
|
||||
float LocalY = 0.0f;
|
||||
GeneratedCellGridCenter(
|
||||
Plan, CellX, CellY, LocalX, LocalY, bLegacyStep2eAuditMode);
|
||||
Cell.CenterX = Plan.GridCos * LocalX - Plan.GridSin * LocalY;
|
||||
Cell.CenterY = Plan.GridSin * LocalX + Plan.GridCos * LocalY;
|
||||
const float RadiusFraction = FMath::Lerp(
|
||||
P.MinimumRoomRadiusFraction, P.MaximumRoomRadiusFraction,
|
||||
ParameterRoll(Hash, 0xA61Bu));
|
||||
Cell.RadiusX = Pitch * RadiusFraction;
|
||||
Cell.RadiusY = Cell.RadiusX * FMath::Lerp(
|
||||
0.82f, 0.98f, ParameterRoll(Hash, 0x27C5u));
|
||||
if (!bLegacyStep2eAuditMode)
|
||||
{
|
||||
float NearestCenterSpacing = FLT_MAX;
|
||||
for (int32 NeighborY = -1; NeighborY <= 1; ++NeighborY)
|
||||
for (int32 NeighborX = -1; NeighborX <= 1; ++NeighborX)
|
||||
{
|
||||
if (NeighborX == 0 && NeighborY == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float NeighborLocalX = 0.0f;
|
||||
float NeighborLocalY = 0.0f;
|
||||
GeneratedCellGridCenter(Plan, CellX + NeighborX, CellY + NeighborY,
|
||||
NeighborLocalX, NeighborLocalY, false);
|
||||
NearestCenterSpacing = FMath::Min(NearestCenterSpacing,
|
||||
FMath::Sqrt(FMath::Square(NeighborLocalX - LocalX)
|
||||
+ FMath::Square(NeighborLocalY - LocalY)));
|
||||
}
|
||||
// Keep a measurable rock web between chambers. Their graph edges open only narrow
|
||||
// connecting tunnels, so the room lattice no longer merges into a forest of pillars.
|
||||
Cell.RadiusX = FMath::Min(Cell.RadiusX, NearestCenterSpacing * 0.43f);
|
||||
const float Angle = ParameterRoll(Hash, 0xE17Du) * 6.283185307179586f;
|
||||
VoxelMath::DetSinCos(Cell.RoomSin, Cell.RoomCos, Angle);
|
||||
Cell.RadiusY = Cell.RadiusX * FMath::Lerp(
|
||||
0.64f, 0.94f, ParameterRoll(Hash, 0x27C5u));
|
||||
}
|
||||
else
|
||||
{
|
||||
Cell.RadiusY = Cell.RadiusX * FMath::Lerp(
|
||||
0.82f, 0.98f, ParameterRoll(Hash, 0x27C5u));
|
||||
}
|
||||
if (VoxelHash::ToFloat01(Hash3(CellX, CellY, 0x53, Plan.GenerationSeed))
|
||||
< P.VerticalShaftProbability)
|
||||
{
|
||||
@@ -905,7 +1007,11 @@ namespace
|
||||
}
|
||||
Cell.FloorZ = Plan.GenerationFloorZ
|
||||
+ (ParameterRoll(Hash, 0xF103u) * 2.0f - 1.0f) * P.FloorReliefVoxels;
|
||||
Cell.CeilingZ = Cell.FloorZ + P.RoomHeightVoxels;
|
||||
const float RoomHeight = bLegacyStep2eAuditMode
|
||||
? P.RoomHeightVoxels
|
||||
: P.RoomHeightVoxels * FMath::Lerp(
|
||||
0.82f, 1.12f, ParameterRoll(Hash, 0x55A9u));
|
||||
Cell.CeilingZ = Cell.FloorZ + RoomHeight;
|
||||
|
||||
if (ParameterRoll(Hash, 0x8811u) < P.PillarProbability)
|
||||
{
|
||||
@@ -926,7 +1032,8 @@ namespace
|
||||
if (ParameterRoll(Hash, 0xE25Fu) < P.FloatingMassProbability)
|
||||
{
|
||||
Cell.FeatureFlags |= HasFloatingFlag;
|
||||
for (int32 Blob = 0; Blob < 2; ++Blob)
|
||||
const int32 BlobCount = bLegacyStep2eAuditMode ? 2 : 1;
|
||||
for (int32 Blob = 0; Blob < BlobCount; ++Blob)
|
||||
{
|
||||
const uint32 BlobSalt = 0x1567u + static_cast<uint32>(Blob) * 0x101u;
|
||||
const float OffsetX = (ParameterRoll(Hash, BlobSalt) - 0.5f) * Cell.RadiusX * 0.60f;
|
||||
@@ -937,8 +1044,19 @@ namespace
|
||||
Cell.FloorZ + P.RoomHeightVoxels * 0.28f,
|
||||
Cell.CeilingZ - P.RoomHeightVoxels * 0.28f,
|
||||
ParameterRoll(Hash, BlobSalt + 2u));
|
||||
Cell.FloatingRadius[Blob] = Cell.RadiusX * FMath::Lerp(
|
||||
0.07f, 0.15f, ParameterRoll(Hash, BlobSalt + 3u));
|
||||
Cell.FloatingRadius[Blob] = bLegacyStep2eAuditMode
|
||||
? Cell.RadiusX * FMath::Lerp(
|
||||
0.07f, 0.15f, ParameterRoll(Hash, BlobSalt + 3u))
|
||||
: FMath::Clamp(Cell.RadiusX * FMath::Lerp(
|
||||
0.16f, 0.28f, ParameterRoll(Hash, BlobSalt + 3u)), 8.0f, 18.0f);
|
||||
Cell.FloatingRadiusY[Blob] = bLegacyStep2eAuditMode
|
||||
? Cell.FloatingRadius[Blob]
|
||||
: Cell.FloatingRadius[Blob] * FMath::Lerp(
|
||||
0.62f, 1.16f, ParameterRoll(Hash, BlobSalt + 4u));
|
||||
Cell.FloatingRadiusZ[Blob] = bLegacyStep2eAuditMode
|
||||
? Cell.FloatingRadius[Blob]
|
||||
: Cell.FloatingRadius[Blob] * FMath::Lerp(
|
||||
0.58f, 1.62f, ParameterRoll(Hash, BlobSalt + 5u));
|
||||
}
|
||||
}
|
||||
if (ParameterRoll(Hash, 0xC6A9u) < P.SpireProbability)
|
||||
@@ -1014,7 +1132,7 @@ namespace
|
||||
|
||||
FORCEINLINE float GeneratedRoomCell(
|
||||
const FGeneratedCaveCell& Cell,
|
||||
float Z, float Radius, float Horizontal,
|
||||
float X, float Y, float Z, float Radius, float Horizontal,
|
||||
const FParameterizedColumnContext& Column, int32 CellIndex)
|
||||
{
|
||||
const float FloorZ = Column.RoomFloors[CellIndex];
|
||||
@@ -1043,16 +1161,44 @@ namespace
|
||||
}
|
||||
if ((Cell.FeatureFlags & HasFloatingFlag) != 0)
|
||||
{
|
||||
for (int32 Blob = 0; Blob < 2; ++Blob)
|
||||
const int32 BlobCount = IsLegacyStep2eAuditMode() ? 2 : 1;
|
||||
for (int32 Blob = 0; Blob < BlobCount; ++Blob)
|
||||
{
|
||||
if (Room <= -Cell.FloatingRadius[Blob])
|
||||
const float FloatingSupportRadius = IsLegacyStep2eAuditMode()
|
||||
? Cell.FloatingRadius[Blob]
|
||||
: FMath::Max3(Cell.FloatingRadius[Blob],
|
||||
Cell.FloatingRadiusY[Blob], Cell.FloatingRadiusZ[Blob]);
|
||||
if (Room <= -FloatingSupportRadius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const float BZ = Z - Cell.FloatingZ[Blob];
|
||||
const float BlobSolid = FMath::Sqrt(
|
||||
Column.FloatingXYDistanceSquared[CellIndex][Blob] + BZ * BZ)
|
||||
- Cell.FloatingRadius[Blob];
|
||||
float BlobSolid = 0.0f;
|
||||
if (IsLegacyStep2eAuditMode())
|
||||
{
|
||||
BlobSolid = FMath::Sqrt(
|
||||
Column.FloatingXYDistanceSquared[CellIndex][Blob] + BZ * BZ)
|
||||
- Cell.FloatingRadius[Blob];
|
||||
}
|
||||
else
|
||||
{
|
||||
const float BX = X - Cell.FloatingX[Blob];
|
||||
const float BY = Y - Cell.FloatingY[Blob];
|
||||
const float RadiusX = FMath::Max(Cell.FloatingRadius[Blob], 1.0f);
|
||||
const float RadiusY = FMath::Max(Cell.FloatingRadiusY[Blob], 1.0f);
|
||||
const float RadiusZ = FMath::Max(Cell.FloatingRadiusZ[Blob], 1.0f);
|
||||
const float ScaledLength = FMath::Sqrt(
|
||||
FMath::Square(BX / RadiusX)
|
||||
+ FMath::Square(BY / RadiusY)
|
||||
+ FMath::Square(BZ / RadiusZ));
|
||||
const float ScaledGradient = FMath::Sqrt(
|
||||
FMath::Square(BX / FMath::Square(RadiusX))
|
||||
+ FMath::Square(BY / FMath::Square(RadiusY))
|
||||
+ FMath::Square(BZ / FMath::Square(RadiusZ)));
|
||||
BlobSolid = ScaledGradient > 1.0e-3f
|
||||
? ScaledLength * (ScaledLength - 1.0f) / ScaledGradient
|
||||
: -FMath::Min3(RadiusX, RadiusY, RadiusZ);
|
||||
}
|
||||
if (BlobSolid < Room)
|
||||
{
|
||||
Room = BlobSolid;
|
||||
@@ -1212,7 +1358,20 @@ namespace
|
||||
const float DY = Y - OutColumn.Cells[Index]->CenterY;
|
||||
const float DistanceSquared = DX * DX + DY * DY;
|
||||
OutColumn.RoomDistanceSquared[Index] = DistanceSquared;
|
||||
OutColumn.RoomRadii[Index] = FMath::Sqrt(DistanceSquared);
|
||||
if (IsLegacyStep2eAuditMode())
|
||||
{
|
||||
OutColumn.RoomRadii[Index] = FMath::Sqrt(DistanceSquared);
|
||||
}
|
||||
else
|
||||
{
|
||||
const FGeneratedCaveCell& Cell = *OutColumn.Cells[Index];
|
||||
const float LocalX = Cell.RoomCos * DX + Cell.RoomSin * DY;
|
||||
const float LocalY = -Cell.RoomSin * DX + Cell.RoomCos * DY;
|
||||
const float ScaledY = LocalY * Cell.RadiusX
|
||||
/ FMath::Max(Cell.RadiusY, 1.0f);
|
||||
OutColumn.RoomRadii[Index] = FMath::Sqrt(
|
||||
LocalX * LocalX + ScaledY * ScaledY);
|
||||
}
|
||||
OutColumn.RoomHorizontals[Index] = OutColumn.Cells[Index]->RadiusX
|
||||
- OutColumn.RoomRadii[Index];
|
||||
|
||||
@@ -1347,7 +1506,7 @@ namespace
|
||||
}
|
||||
}
|
||||
Macro = SmoothMaximum(Macro,
|
||||
GeneratedRoomCell(Cell, Z,
|
||||
GeneratedRoomCell(Cell, X, Y, Z,
|
||||
ColumnContext->RoomRadii[Index],
|
||||
ColumnContext->RoomHorizontals[Index],
|
||||
*ColumnContext, Index), 6.0f);
|
||||
@@ -1384,7 +1543,10 @@ namespace
|
||||
}
|
||||
const float Horizontal = Plan.Generation.ShaftRadiusVoxels
|
||||
- ColumnContext->RoomRadii[Index];
|
||||
const float Vertical = FMath::Min(Z - Bottom, Top - Z);
|
||||
const float ShaftInset = IsLegacyStep2eAuditMode()
|
||||
? 0.0f : FMath::Min(48.0f, (Top - Bottom) * 0.20f);
|
||||
const float Vertical = FMath::Min(
|
||||
Z - (Bottom + ShaftInset), (Top - ShaftInset) - Z);
|
||||
Macro = SmoothMaximum(Macro,
|
||||
SmoothMinimum(Horizontal, Vertical, 5.0f), 5.0f);
|
||||
}
|
||||
@@ -1458,6 +1620,19 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsLegacyStep2eAuditMode())
|
||||
{
|
||||
// Keep a four-meter roof and floor through rooms, shafts, and authored passage
|
||||
// modifiers. The same cap is applied again after passage edits in EvaluateInternal.
|
||||
constexpr float StrateSealThickness = 16.0f;
|
||||
const float StrateBottom = static_cast<float>(Plan.MinChunkZ * CHUNK_SIZE);
|
||||
const float StrateTop = static_cast<float>(Plan.MaxChunkZExclusive * CHUNK_SIZE);
|
||||
const float BoundaryLimit = FMath::Min(
|
||||
Z - StrateBottom - StrateSealThickness,
|
||||
StrateTop - StrateSealThickness - Z);
|
||||
Macro = FMath::Min(Macro, BoundaryLimit);
|
||||
}
|
||||
|
||||
if (OutDetailScale != nullptr)
|
||||
{
|
||||
*OutDetailScale = DetailFade;
|
||||
@@ -1524,9 +1699,6 @@ namespace
|
||||
FMath::Min(VerticalAir, GridAir), LandingMacro);
|
||||
if (Plan.Archetype == ECaveGeneratorType::VerticalShafts)
|
||||
{
|
||||
// Keep one continuous, walkable cavern horizon between the full-height shaft wells.
|
||||
// The wells and their tree links add vertical routes; this band makes every authored
|
||||
// passage landing part of the same deterministic navigation level.
|
||||
Macro = FMath::Max(Macro, VerticalAir);
|
||||
Macro = FMath::Max(Macro, EvaluateVerticalShaftMacro(Plan, X, Y, Z));
|
||||
}
|
||||
@@ -1717,6 +1889,11 @@ void SetParameterizedEnabled(bool bEnabled)
|
||||
CVarResearchParameterizedCave->Set(bEnabled ? 1 : 0, ECVF_SetByCode);
|
||||
}
|
||||
|
||||
void SetLegacyStep2eAuditMode(bool bEnabled)
|
||||
{
|
||||
GUseLegacyStep2eAuditMode.store(bEnabled, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void SetParameterSet(int32 ParameterSet)
|
||||
{
|
||||
CVarResearchParameterizedCaveSet->Set(
|
||||
@@ -1741,8 +1918,10 @@ bool GetParameterSetInfo(int32 ParameterSet, const TCHAR*& OutName,
|
||||
{
|
||||
return false;
|
||||
}
|
||||
OutName = GParameterProfiles[ParameterSet].Name;
|
||||
OutParameters = GParameterProfiles[ParameterSet].Parameters;
|
||||
const FParameterProfile& Profile = IsLegacyStep2eAuditMode()
|
||||
? GLegacyStep2eProfiles[ParameterSet] : GParameterProfiles[ParameterSet];
|
||||
OutName = Profile.Name;
|
||||
OutParameters = Profile.Parameters;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1817,7 +1996,7 @@ static float EvaluateInternal(const UVoxelGenerator& Generator,
|
||||
const uint32 DetailSeed = static_cast<uint32>(Generator.Seed)
|
||||
^ static_cast<uint32>(Plan->StrateIndex * 7919 + 104729);
|
||||
const float FinalDetail = bHasDetailValue
|
||||
? DetailValue : DetailAt(WorldX, WorldY, WorldZ, DetailSeed);
|
||||
? DetailValue : DetailAt(WorldX, WorldY, WorldZ, DetailSeed, bParameterized);
|
||||
Result = Macro + FinalDetail * DetailFade * DetailScale;
|
||||
|
||||
const bool bMayContainPassage = bParameterized
|
||||
@@ -1888,6 +2067,17 @@ static float EvaluateInternal(const UVoxelGenerator& Generator,
|
||||
}
|
||||
}
|
||||
|
||||
if (bParameterized && Plan != nullptr && !IsLegacyStep2eAuditMode())
|
||||
{
|
||||
constexpr float StrateSealThickness = 16.0f;
|
||||
const float StrateBottom = static_cast<float>(Plan->MinChunkZ * CHUNK_SIZE);
|
||||
const float StrateTop = static_cast<float>(Plan->MaxChunkZExclusive * CHUNK_SIZE);
|
||||
const float BoundaryLimit = FMath::Min(
|
||||
WorldZ - StrateBottom - StrateSealThickness,
|
||||
StrateTop - StrateSealThickness - WorldZ);
|
||||
Result = FMath::Min(Result, BoundaryLimit);
|
||||
}
|
||||
|
||||
if (OutPreEditDensity != nullptr)
|
||||
{
|
||||
*OutPreEditDensity = Result;
|
||||
@@ -1927,6 +2117,24 @@ float EvaluateParameterized(const UVoxelGenerator& Generator,
|
||||
OutPreEditDensity, DetailScale, nullptr, true, nullptr);
|
||||
}
|
||||
|
||||
float EvaluateParameterizedDetail(
|
||||
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 0.0f;
|
||||
}
|
||||
const uint32 DetailSeed = static_cast<uint32>(Generator.Seed)
|
||||
^ static_cast<uint32>(Plan->StrateIndex * 7919 + 104729);
|
||||
return DetailAt(WorldX, WorldY, WorldZ, DetailSeed, true);
|
||||
}
|
||||
|
||||
float EvaluateParameterizedWithDetailValue(const UVoxelGenerator& Generator,
|
||||
float WorldX, float WorldY, float WorldZ, float DetailValue,
|
||||
float* OutPreEditDensity, float DetailScale,
|
||||
@@ -2076,7 +2284,7 @@ void BuildParameterizedBrickContext(
|
||||
|
||||
void BuildDetailBrick(const UVoxelGenerator& Generator, FIntVector OriginVoxels,
|
||||
FIntVector Size, float DetailScale, TArray<float>& OutDetail,
|
||||
FBrickEvaluationContext* OutContext)
|
||||
FBrickEvaluationContext* OutContext, bool bParameterized)
|
||||
{
|
||||
(void)DetailScale;
|
||||
const int32 SampleCount = Size.X * Size.Y * Size.Z;
|
||||
@@ -2160,7 +2368,7 @@ void BuildDetailBrick(const UVoxelGenerator& Generator, FIntVector OriginVoxels,
|
||||
OutDetail[(Z * Size.Y + Y) * Size.X + X] = DetailAt(
|
||||
static_cast<float>(OriginVoxels.X + X),
|
||||
static_cast<float>(OriginVoxels.Y + Y),
|
||||
static_cast<float>(WorldZ), SampleSeed);
|
||||
static_cast<float>(WorldZ), SampleSeed, bParameterized);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -2250,8 +2458,13 @@ void BuildDetailBrick(const UVoxelGenerator& Generator, FIntVector OriginVoxels,
|
||||
{
|
||||
return (Z * CountY + Y) * CountX + X;
|
||||
};
|
||||
const auto AddNoiseBand = [&](int32 CellSize, uint32 BandSeed, float Amplitude)
|
||||
const auto AddNoiseBand = [&](int32 CellSize, uint32 BandSeed, float Amplitude,
|
||||
TArray<float>* OutBand)
|
||||
{
|
||||
if (OutBand != nullptr)
|
||||
{
|
||||
OutBand->SetNumUninitialized(SampleCount);
|
||||
}
|
||||
const int32 MinCellX = FMath::FloorToInt(
|
||||
static_cast<float>(OriginVoxels.X) / static_cast<float>(CellSize));
|
||||
const int32 MinCellY = FMath::FloorToInt(
|
||||
@@ -2330,14 +2543,39 @@ void BuildDetailBrick(const UVoxelGenerator& Generator, FIntVector OriginVoxels,
|
||||
Grid[Index + GridX * GridY + 1], AX),
|
||||
FMath::Lerp(Grid[Index + GridX * GridY + GridX],
|
||||
Grid[Index + GridX * GridY + GridX + 1], AX), AY), AZ);
|
||||
OutDetail[SampleIndex(X, Y, Z)] += Value * Amplitude;
|
||||
const int32 OutputIndex = SampleIndex(X, Y, Z);
|
||||
if (OutBand != nullptr)
|
||||
{
|
||||
(*OutBand)[OutputIndex] = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
OutDetail[OutputIndex] += Value * Amplitude;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddNoiseBand(8, Seed ^ 0x68BC21EBu, 1.4f);
|
||||
AddNoiseBand(4, Seed ^ 0x02E5BE93u, 0.9f);
|
||||
AddNoiseBand(2, Seed ^ 0x967A889Bu, 0.45f);
|
||||
AddNoiseBand(1, Seed ^ 0xA511E9B3u, 2.5f);
|
||||
if (bParameterized && !IsLegacyStep2eAuditMode())
|
||||
{
|
||||
TArray<float> Broad;
|
||||
TArray<float> Medium;
|
||||
TArray<float> Fine;
|
||||
AddNoiseBand(32, Seed ^ 0x68BC21EBu, 1.0f, &Broad);
|
||||
AddNoiseBand(16, Seed ^ 0x02E5BE93u, 1.0f, &Medium);
|
||||
AddNoiseBand(8, Seed ^ 0x967A889Bu, 1.0f, &Fine);
|
||||
for (int32 Index = 0; Index < SampleCount; ++Index)
|
||||
{
|
||||
const float Ridge = FMath::Sqrt(Fine[Index] * Fine[Index] + 0.0625f) - 0.25f;
|
||||
OutDetail[Index] = Broad[Index] * 2.0f + Medium[Index] * 1.2f
|
||||
+ Fine[Index] * 0.6f + Ridge * 0.4f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AddNoiseBand(8, Seed ^ 0x68BC21EBu, 1.4f, nullptr);
|
||||
AddNoiseBand(4, Seed ^ 0x02E5BE93u, 0.9f, nullptr);
|
||||
AddNoiseBand(2, Seed ^ 0x967A889Bu, 0.45f, nullptr);
|
||||
AddNoiseBand(1, Seed ^ 0xA511E9B3u, 2.5f, nullptr);
|
||||
}
|
||||
|
||||
static bool TryClassifyBrickInternal(
|
||||
@@ -2511,7 +2749,7 @@ static bool TryClassifyBrickInternal(
|
||||
? EvaluateParameterizedMacro(*Plan, Centre.X, Centre.Y, Centre.Z, nullptr)
|
||||
: EvaluateMacro(*Plan, Centre.X, Centre.Y, Centre.Z, nullptr);
|
||||
const float FinalBound = MacroLipschitzBound * Radius
|
||||
+ DetailAmplitude * FMath::Abs(DetailScale)
|
||||
+ CurrentDetailAmplitude(bParameterized) * FMath::Abs(DetailScale)
|
||||
+ DetailBoundNumericalMargin * FMath::Max(1.0f, FMath::Abs(DetailScale));
|
||||
if (Macro > FinalBound)
|
||||
{
|
||||
|
||||
@@ -74,10 +74,20 @@ static bool VF_TileFootprintsOverlap(const FVoxelTileKey& A, const FVoxelTileKey
|
||||
#if WITH_EDITOR
|
||||
#include "VoxelStrateComposer.h"
|
||||
#include "VoxelStrateMeasure.h"
|
||||
#include "VoxelRedefinedDensityField.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsParameterizedResearchFieldActive()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
return VoxelForgeRedefinedDensityField::IsParameterizedEnabled();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 GVoxelForgeCollisionGate = 1;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeCollisionGate(
|
||||
TEXT("voxel.CollisionGate"),
|
||||
@@ -2197,7 +2207,8 @@ void AVoxelWorld::BeginPlay()
|
||||
Mesher->bGenerateSkirts = Settings->bGenerateSkirts;
|
||||
Mesher->SkirtCells = Settings->SkirtCells;
|
||||
Mesher->LODOctaveDrop = Settings->LODOctaveDrop; // T2.b — 0 = off
|
||||
Mesher->bUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut != 0;
|
||||
Mesher->bUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut != 0
|
||||
&& !IsParameterizedResearchFieldActive();
|
||||
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeClassifierConfig] outer_mode=%d sealed_solid_proof=%d nested_block_early_out=%d"),
|
||||
@@ -10368,7 +10379,8 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
|
||||
// même pour les cellules uniformes, et ces tuiles sont rares (fenêtre d'ombre).
|
||||
// (Gate IsoLevel == 0 : les verdicts du classifieur supposent l'iso MC à zéro exactement.)
|
||||
bool bTrivialEmpty = false;
|
||||
if (GVoxelForgeSealedSolidProof != 0
|
||||
if (!IsParameterizedResearchFieldActive()
|
||||
&& GVoxelForgeSealedSolidProof != 0
|
||||
&& !bWantCapture && SharedDensityOverride == nullptr
|
||||
&& Generator && Mesher
|
||||
&& Mesher->IsoLevel == 0.0f)
|
||||
@@ -10411,7 +10423,8 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
|
||||
return;
|
||||
}
|
||||
const int32 OuterClassifierMode = FMath::Clamp(GVoxelForgeOuterClassifierMode, 0, 1);
|
||||
if (!bTrivialEmpty && OuterClassifierMode != 0
|
||||
if (!IsParameterizedResearchFieldActive()
|
||||
&& !bTrivialEmpty && OuterClassifierMode != 0
|
||||
&& !bWantCapture && SharedDensityOverride == nullptr
|
||||
&& Generator && Mesher && Mesher->IsoLevel == 0.0f)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace VoxelForgeRedefinedDensityField
|
||||
TArray<int32> PassageCandidates;
|
||||
};
|
||||
|
||||
/** Cell-level controls for the separate step 2e procedural room-network field. */
|
||||
/** Cell-level controls for the separate editor-only parameterized cave field. */
|
||||
struct FGenerationParameters
|
||||
{
|
||||
float RoomPitchVoxels = 144.0f;
|
||||
@@ -101,9 +101,11 @@ namespace VoxelForgeRedefinedDensityField
|
||||
VOXELFORGE_API float GetDetailScale();
|
||||
VOXELFORGE_API void SetDetailScale(float Scale);
|
||||
|
||||
/** Step 2e is editor-only and default-off. -1 rolls one of eight profiles per strate. */
|
||||
/** The parameterized cave field is editor-only and default-off. -1 rolls per strate. */
|
||||
VOXELFORGE_API bool IsParameterizedEnabled();
|
||||
VOXELFORGE_API void SetParameterizedEnabled(bool bEnabled);
|
||||
/** Select the frozen step 2e implementation while the step 2f audit measures its gates. */
|
||||
VOXELFORGE_API void SetLegacyStep2eAuditMode(bool bEnabled);
|
||||
VOXELFORGE_API void SetParameterSet(int32 ParameterSet);
|
||||
VOXELFORGE_API int32 GetParameterSet();
|
||||
VOXELFORGE_API int32 GetParameterSetCount();
|
||||
@@ -115,6 +117,10 @@ namespace VoxelForgeRedefinedDensityField
|
||||
const UVoxelGenerator& Generator, float WorldX, float WorldY, float WorldZ,
|
||||
float* OutPreEditDensity = nullptr, float DetailScale = 1.0f);
|
||||
|
||||
/** Return the active parameterized detail signal for the band-limit research gate. */
|
||||
VOXELFORGE_API float EvaluateParameterizedDetail(
|
||||
const UVoxelGenerator& Generator, float WorldX, float WorldY, float WorldZ);
|
||||
|
||||
VOXELFORGE_API float EvaluateParameterizedWithDetailValue(
|
||||
const UVoxelGenerator& Generator, float WorldX, float WorldY, float WorldZ,
|
||||
float DetailValue, float* OutPreEditDensity = nullptr, float DetailScale = 1.0f,
|
||||
@@ -144,7 +150,8 @@ namespace VoxelForgeRedefinedDensityField
|
||||
/** Build mathematically identical multi-scale noise for one integer-lattice brick. */
|
||||
VOXELFORGE_API void BuildDetailBrick(const UVoxelGenerator& Generator,
|
||||
FIntVector OriginVoxels, FIntVector Size, float DetailScale,
|
||||
TArray<float>& OutDetail, FBrickEvaluationContext* OutContext = nullptr);
|
||||
TArray<float>& OutDetail, FBrickEvaluationContext* OutContext = nullptr,
|
||||
bool bParameterized = false);
|
||||
|
||||
/** Evaluate with a detail value produced by BuildDetailBrick. */
|
||||
VOXELFORGE_API float EvaluateWithDetailValue(const UVoxelGenerator& Generator,
|
||||
|
||||
Reference in new issue
Block a user