research(step2d): a simpler field is fast (81 ns/sample) -- but it is not the owner's world; NOT a pass
Editor-only, default off (voxel.ResearchRedefinedDensityField). The redefined field is a seeded, periodic cross-corridor pattern per strate plus the authored passages, landings and a shaft tree, with four value-noise detail bands at every near-surface 25 cm sample and sound 8^3 brick proofs. 259^3 ring, six workers: 308 ms median (467 ms without skips) vs 8.9 s for the live field. Detail, boundary-artefact, determinism (1 vs 6 workers, two runs) and bound-audit checks pass. Why it is not a pass: the slices show a mostly-solid field with a few straight corridors, where the owner's world is mostly open space; rooms, the tunnel network, composer recipes and archetypes are gone. Much of the speed comes from 60% of bricks being proven solid in that simpler world. What it does show: detail at full resolution plus brick skipping costs ~80-130 ns/sample; the cost of the live field is how rooms/tunnels/passages are represented, not the detail or the lattice. The two walkability tests were redirected to the research field in the working tree; that change is NOT committed (saved as scratchpad step2d-test-redirect.patch) so they keep testing the live world. 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
d6e021a80b
commit
78641da0bb
15 files changed
+3077
-6
No files matched your search
@@ -0,0 +1,146 @@
|
||||
#if WITH_DEV_AUTOMATION_TESTS
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
|
||||
#include "VoxelForgeTestFixture.h"
|
||||
#include "VoxelRedefinedDensityField.h"
|
||||
#include "VoxelStrateMeasure.h"
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FVoxelForgeRedefinedDensityFieldTraversalSeedsTest,
|
||||
"VoxelForge.Research.RedefinedFieldTraversalSeeds",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
||||
|
||||
bool FVoxelForgeRedefinedDensityFieldTraversalSeedsTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
(void)Parameters;
|
||||
struct FRestoreResearchField
|
||||
{
|
||||
bool bWasEnabled = VoxelForgeRedefinedDensityField::IsEnabled();
|
||||
float PreviousScale = VoxelForgeRedefinedDensityField::GetDetailScale();
|
||||
~FRestoreResearchField()
|
||||
{
|
||||
VoxelForgeRedefinedDensityField::SetDetailScale(PreviousScale);
|
||||
VoxelForgeRedefinedDensityField::SetEnabled(bWasEnabled);
|
||||
}
|
||||
} Restore;
|
||||
VoxelForgeRedefinedDensityField::SetEnabled(true);
|
||||
VoxelForgeRedefinedDensityField::SetDetailScale(1.0f);
|
||||
|
||||
constexpr int32 Seeds[] = {1337, 12345, 20260930};
|
||||
int32 CheckedLandings = 0;
|
||||
int32 CheckedRoutes = 0;
|
||||
int32 RoutesWithResolvedEndpoints = 0;
|
||||
for (const int32 Seed : Seeds)
|
||||
{
|
||||
VoxelForgeTest::FTestWorld World;
|
||||
World.Build(Seed, /*InGapChunks=*/2, /*InStrateHeightInChunks=*/4);
|
||||
if (!World.IsValid())
|
||||
{
|
||||
AddError(FString::Printf(
|
||||
TEXT("Seed %d: test world did not build: %s"), Seed, *World.WhyInvalid()));
|
||||
return false;
|
||||
}
|
||||
|
||||
const TArray<FStrateSlot>& Layout = World.StrateManager->GetLayout();
|
||||
const TArray<FVoxelPassage>& Passages = World.StrateManager->GetPassages();
|
||||
for (const FVoxelPassage& Passage : Passages)
|
||||
{
|
||||
if (Passage.UpperStrateIndex == Passage.LowerStrateIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const FVector UpperAir = Passage.UpperLanding.StandingPoint;
|
||||
const FVector LowerAir = Passage.LowerLanding.StandingPoint;
|
||||
const float UpperDensity = World.Generator->GetDensityAt(
|
||||
UpperAir.X, UpperAir.Y, UpperAir.Z);
|
||||
const float UpperFooting = World.Generator->GetDensityAt(
|
||||
UpperAir.X, UpperAir.Y, Passage.UpperLanding.FloorZ - 0.75f);
|
||||
const float LowerDensity = World.Generator->GetDensityAt(
|
||||
LowerAir.X, LowerAir.Y, LowerAir.Z);
|
||||
const float LowerFooting = World.Generator->GetDensityAt(
|
||||
LowerAir.X, LowerAir.Y, Passage.LowerLanding.FloorZ - 0.75f);
|
||||
++CheckedLandings;
|
||||
if (!FMath::IsFinite(UpperDensity) || !FMath::IsFinite(UpperFooting)
|
||||
|| !FMath::IsFinite(LowerDensity) || !FMath::IsFinite(LowerFooting)
|
||||
|| UpperDensity <= 0.0f || LowerDensity <= 0.0f
|
||||
|| UpperFooting >= 0.0f || LowerFooting >= 0.0f)
|
||||
{
|
||||
AddError(FString::Printf(
|
||||
TEXT("Seed %d: passage %d landing failed the final-field air/floor bracket "
|
||||
"(upper %.4f/%.4f, lower %.4f/%.4f)."),
|
||||
Seed, CheckedLandings - 1, UpperDensity, UpperFooting,
|
||||
LowerDensity, LowerFooting));
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 StrateIndex = 0; StrateIndex < Layout.Num(); ++StrateIndex)
|
||||
{
|
||||
FVector Arrival = FVector::ZeroVector;
|
||||
FVector Departure = FVector::ZeroVector;
|
||||
bool bHasArrival = false;
|
||||
bool bHasDeparture = false;
|
||||
for (const FVoxelPassage& Passage : Passages)
|
||||
{
|
||||
if (Passage.UpperStrateIndex == Passage.LowerStrateIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Passage.LowerStrateIndex == StrateIndex && !bHasArrival)
|
||||
{
|
||||
Arrival = Passage.LowerLanding.StandingPoint;
|
||||
bHasArrival = true;
|
||||
}
|
||||
if (Passage.UpperStrateIndex == StrateIndex && !bHasDeparture)
|
||||
{
|
||||
Departure = Passage.UpperLanding.StandingPoint;
|
||||
bHasDeparture = true;
|
||||
}
|
||||
}
|
||||
if (!bHasArrival || !bHasDeparture)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FVoxelStrateMeasureSettings Settings;
|
||||
Settings.SampleStep = 4;
|
||||
Settings.RadiusInVoxels = 192;
|
||||
Settings.CenterXY = FVector2D(
|
||||
(Arrival.X + Departure.X) * 0.5f,
|
||||
(Arrival.Y + Departure.Y) * 0.5f);
|
||||
Settings.MaxCells = 4000000;
|
||||
Settings.HeadroomCells = 2;
|
||||
const FVoxelConnectivityDiagnostics Route = VF_DiagnoseConnectivity(
|
||||
*World.Generator, *World.StrateManager, StrateIndex,
|
||||
Arrival, Departure, Settings);
|
||||
++CheckedRoutes;
|
||||
RoutesWithResolvedEndpoints += Route.bStartSnapped || Route.bGoalSnapped;
|
||||
if (!Route.bValid || Route.Result != EVoxelConnectivityResult::Connected)
|
||||
{
|
||||
AddError(FString::Printf(
|
||||
TEXT("Seed %d strate %d (%s): arrival-to-exit walking route was %s "
|
||||
"(result=%d, valid=%s, start resolved=%s, goal resolved=%s)."),
|
||||
Seed, StrateIndex,
|
||||
Layout[StrateIndex].Definition
|
||||
? *Layout[StrateIndex].Definition->GetName() : TEXT("no definition"),
|
||||
Route.Result == EVoxelConnectivityResult::Connected
|
||||
? TEXT("connected") : TEXT("not connected"),
|
||||
static_cast<int32>(Route.Result),
|
||||
Route.bValid ? TEXT("yes") : TEXT("no"),
|
||||
Route.bStartSnapped ? TEXT("yes") : TEXT("no"),
|
||||
Route.bGoalSnapped ? TEXT("yes") : TEXT("no")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestTrue(TEXT("Several seeds exercised passage landings"), CheckedLandings >= 6);
|
||||
TestTrue(TEXT("Several seeds exercised arrival-to-exit walking"), CheckedRoutes >= 6);
|
||||
AddInfo(FString::Printf(
|
||||
TEXT("Redefined field functional sweep: %d authored passage landings bracket open air "
|
||||
"over %d seeds; %d arrival-to-exit walking routes checked; %d used a resolved "
|
||||
"coarse endpoint after the full-density segment check."),
|
||||
CheckedLandings, UE_ARRAY_COUNT(Seeds), CheckedRoutes, RoutesWithResolvedEndpoints));
|
||||
return CheckedLandings >= 6 && CheckedRoutes >= 6;
|
||||
}
|
||||
|
||||
#endif // WITH_DEV_AUTOMATION_TESTS
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "VoxelDensityAblation.h" // fingerprinted development-only stage measurements
|
||||
#include "VoxelDensityPrimitives.h" // spine / seals / passage — shared with the operator stack
|
||||
#include "VoxelDensityOpStack.h" // the per-strate operator stack
|
||||
#include "VoxelRedefinedDensityField.h" // default-off editor research evaluator
|
||||
#include "VoxelHeightOp.h" // IVoxelBiomeField — the adapter below implements it
|
||||
#include "VoxelStats.h"
|
||||
#include "VoxelDensityProfile.h"
|
||||
@@ -2815,6 +2816,19 @@ bool UVoxelGenerator::EvaluateDensityChunk(
|
||||
float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ,
|
||||
float* OutPreEditDensity) const
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
// Step 2d is an editor-only, default-off side path. It does not alter the live evaluator's
|
||||
// state or caches; the field is selected only for an explicit research/test run.
|
||||
if (VoxelForgeRedefinedDensityField::IsEnabled())
|
||||
{
|
||||
VoxelPassageGeometry::ResetVerticalShaftConnectorAirMarker();
|
||||
const float ResearchDensity = VoxelForgeRedefinedDensityField::Evaluate(
|
||||
*this, WorldX, WorldY, WorldZ, OutPreEditDensity,
|
||||
VoxelForgeRedefinedDensityField::GetDetailScale());
|
||||
VoxelPassageGeometry::ResetVerticalShaftConnectorAirMarker();
|
||||
return ResearchDensity;
|
||||
}
|
||||
#endif
|
||||
// Soundness mode: for a sample in a block whose core or passage-carving reach bit is clear,
|
||||
// evaluate the complete canonical field once with the bypass bit set. The normal call then
|
||||
// takes the fast path below and compares the final MC field after the boundary seal and diff
|
||||
@@ -6298,6 +6312,16 @@ EVoxelTileClass UVoxelGenerator::ClassifyTile(const FIntVector& OriginVoxels, in
|
||||
VoxelDensityProfile::AddCounter(
|
||||
VoxelDensityProfile::ECounter::TileClassifyCalls);
|
||||
|
||||
#if WITH_EDITOR
|
||||
// The ordinary tile classifier proves the live field's interval bounds. While the separate
|
||||
// research field is selected, decline those proofs until its own complete final-field interval
|
||||
// contract is wired into streaming; a conservative Mixed verdict keeps the test mesh complete.
|
||||
if (VoxelForgeRedefinedDensityField::IsEnabled())
|
||||
{
|
||||
return EVoxelTileClass::Mixed;
|
||||
}
|
||||
#endif
|
||||
|
||||
const EVoxelTileClass Verdict = ClassifyTileUncached(
|
||||
OriginVoxels, NormalizedStep, NormalizedCellsPerAxis, OutStats);
|
||||
if (Verdict == EVoxelTileClass::AllSolid)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
class UVoxelGenerator;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Step 2d editor research field. It is intentionally separate from the gameplay evaluator and
|
||||
* is selected only by the default-off voxel.ResearchRedefinedDensityField console variable.
|
||||
*
|
||||
* The field is a connected, archetype-sized cave lattice with an authored walkable floor at each
|
||||
* passage landing. Its macro shape is evaluated directly in world space; bounded, continuous
|
||||
* hash-value noise is added at every full-resolution sample. The brick classifier is permitted to
|
||||
* skip only when a Lipschitz bound on the macro field plus the exact maximum detail amplitude
|
||||
* proves that the final sign is uniform throughout the brick and its marching-cubes halo.
|
||||
*/
|
||||
namespace VoxelForgeRedefinedDensityField
|
||||
{
|
||||
/** Per-worker, per-brick state. The opaque plan pointer is valid until the layout changes. */
|
||||
struct FBrickEvaluationContext
|
||||
{
|
||||
const void* PlanToken = nullptr;
|
||||
bool bPlanResolved = false;
|
||||
bool bMayContainPassage = true;
|
||||
bool bChunkResolved = false;
|
||||
bool bIsGapChunk = false;
|
||||
bool bHasDiffMods = false;
|
||||
bool bMacroXYPrecomputed = false;
|
||||
FIntVector BrickOrigin = FIntVector::ZeroValue;
|
||||
FIntVector BrickSize = FIntVector::ZeroValue;
|
||||
FIntVector Chunk = FIntVector::ZeroValue;
|
||||
TArray<float> FloorByXY;
|
||||
TArray<float> GridAirByXY;
|
||||
TArray<float> DetailFadeByXY;
|
||||
TArray<float> LandingHorizontalByXY;
|
||||
TArray<float> LandingApproachByXY;
|
||||
TArray<float> LandingBridgeHorizontalByXY;
|
||||
TArray<float> LandingBridgeFloorByXY;
|
||||
TArray<int32> PassageCandidates;
|
||||
};
|
||||
|
||||
enum class EBrickFallbackReason : uint8
|
||||
{
|
||||
None,
|
||||
UnsupportedState,
|
||||
ChunkOrStrateBoundary,
|
||||
PassageBounds,
|
||||
MixedMacroInterval
|
||||
};
|
||||
|
||||
struct FBrickCounters
|
||||
{
|
||||
uint64 Tested = 0;
|
||||
uint64 Skipped = 0;
|
||||
uint64 EvaluatedSamples = 0;
|
||||
};
|
||||
|
||||
VOXELFORGE_API bool IsEnabled();
|
||||
VOXELFORGE_API void SetEnabled(bool bEnabled);
|
||||
VOXELFORGE_API float GetDetailScale();
|
||||
VOXELFORGE_API void SetDetailScale(float Scale);
|
||||
|
||||
/** Evaluate the new field, including existing passage carving and player diffs. */
|
||||
VOXELFORGE_API float Evaluate(const UVoxelGenerator& Generator, float WorldX, float WorldY,
|
||||
float WorldZ, float* OutPreEditDensity = nullptr,
|
||||
float DetailScale = 1.0f);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Evaluate with a detail value produced by BuildDetailBrick. */
|
||||
VOXELFORGE_API float EvaluateWithDetailValue(const UVoxelGenerator& Generator,
|
||||
float WorldX, float WorldY, float WorldZ, float DetailValue,
|
||||
float* OutPreEditDensity = nullptr, float DetailScale = 1.0f,
|
||||
const FBrickEvaluationContext* Context = nullptr);
|
||||
|
||||
/** Prove a world-aligned box has one final density sign; false means evaluate every point. */
|
||||
VOXELFORGE_API bool TryClassifyBrick(
|
||||
const UVoxelGenerator& Generator, FIntVector OriginVoxels, FIntVector Size,
|
||||
bool& bOutAir, float DetailScale = 1.0f,
|
||||
bool* bOutEvaluatedMacroCentre = nullptr,
|
||||
EBrickFallbackReason* OutFallbackReason = nullptr);
|
||||
|
||||
/** Reset this thread's immutable plan cache; used by deterministic editor experiments. */
|
||||
VOXELFORGE_API void ResetThreadCache();
|
||||
}
|
||||
#endif
|
||||
Reference in new issue
Block a user