wip(assembler): round L29 -- strate transitions honoured at the bedrock/ceiling interface
EVoxelStrateTransition from the upper strate shapes the bedrock(N)/ceiling(N+1) interface in the shared composition layer: Hard flat; Gradient = smooth 2D hash surface (16 m owner width, about 4 m measured undulation); Interleaved = 3D domain-warped fingered overlap within the width. Each band keeps >= 2 Core voxels solid; faces return to the seam near passage mouths. Interface audit over 512 m: 0 hole columns for all modes; wider-than-declared and too-thin controls rejected. All passage, route, proof and validator audits pass; deterministic Clang = GCC. Evaluator 15. Cost +4% SW, +1.3% FI vs L28. Open: ring-like artifact on the FI floor capture; transition look unverified. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
f23ab7b9fa
commit
63c7e563d0
16 files changed
+794
-42
No files matched your search
+27
-2
@@ -1883,8 +1883,9 @@ Each record separates the nominal ceiling, content, and bedrock bands. A strict
|
||||
sequence for every strate and requires bedrock(N) to touch the next strate's ceiling(N+1). The two
|
||||
bands remain separately owned. Gradient and Interleaved interfaces may declare interpenetration only
|
||||
inside their explicit transition zone; Hard has zero effective width. The manager's `TransitionBlendChunks`
|
||||
is expressed in owner-chunk metres. Interleaved's existing two-chunk domain warp extends its possible
|
||||
fingers beyond the blend width and is recorded separately. Compatibility validation reports current
|
||||
is expressed in owner-chunk metres. L21 recorded the older Interleaved allowance as blend width plus
|
||||
warp amplitude; L29 now treats the declared blend width as the complete physical crossing allowance.
|
||||
Domain warp changes finger placement, but cannot extend overlap beyond that width. Compatibility validation reports current
|
||||
gaps, overlaps, ambiguities, inactive/missing bands, and unhonoured transitions while accepting them;
|
||||
strict validation rejects them. Core dispatches per-strate recipe fields without applying that
|
||||
parameter blend, even though the legacy manager helper implements it. Both band types default diggable,
|
||||
@@ -1943,6 +1944,7 @@ opens a band; the support strip stays below the player capsule. The lower mouth
|
||||
monotone descent from the short route-bound landing tail, whose height may vary with the route.
|
||||
Both route and passage audits sample the 0.34 m by 1.76 m capsule, support probes, and grade; the
|
||||
negative control compiles the same world without passage carving and must detect a blocked capsule.
|
||||
|
||||
The L23 shared-field change is `WorldAssemblyEvaluatorVersion=9`. It does not change the L22 bands
|
||||
or any generator's field away from a passage. The L23 report includes exact-query/hash comparisons,
|
||||
the blocked control, captures, and the Windows adapter checklist. §8.10's cache, proof, streaming,
|
||||
@@ -2051,6 +2053,29 @@ proof bounds preserve the L27 output.
|
||||
[Linux28 report](Standalone/Research/WorldAssemblerM3/Linux28/REPORT.md)
|
||||
records counts, parity, costs and audit caveats.
|
||||
|
||||
### 8.46 Honored bedrock/ceiling transitions — World Assembler L29
|
||||
|
||||
The upper strate's `EVoxelStrateTransition` controls the shared interface between its bedrock band
|
||||
and the next strate's ceiling band in the Core composition path. `TransitionBlendChunks` is converted
|
||||
using the resolved layout chunk width (8 m by default). Hard preserves the flat interface. Gradient
|
||||
moves the shared surface with a smooth 2D hash field whose peak-to-peak range is at most one quarter
|
||||
of the declared width. Interleaved uses a seed/interface/coordinate-keyed, domain-warped 3D hash field
|
||||
to set actual overlap between the facing bands from zero to the declared width. The two faces move
|
||||
inward by half that overlap; the warp changes finger placement without increasing the crossing
|
||||
allowance. Both bands keep authored thickness, and their solid intervals touch or overlap in every
|
||||
XY column. Core does not blend unrelated generator fields.
|
||||
|
||||
Each boundary band keeps at least **two Core voxels** of solid thickness (0.5 m at the default 25 cm
|
||||
voxel size). Strict validation rejects thinner bands and declared interpenetration beyond the
|
||||
transition width. The world audit samples a 512 m square XY area, measures actual crossing depth and
|
||||
minimum band thickness, and sweeps every non-passage column through both bands at half-voxel spacing.
|
||||
Passage columns are excluded from the hole sweep. Near an interface mouth, the shared face returns to
|
||||
its nominal height inside the mouth radius plus a 1 m support margin, then fades back to the selected
|
||||
transition over 2 m so the authored landing floor remains supported. Negative controls reject a
|
||||
transition one micrometre wider than declared and a band one micrometre below the minimum. See
|
||||
[`Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md)
|
||||
for audit measurements, hashes, timings, captures and run history.
|
||||
|
||||
## 9. Multiplayer model (listen-server first, dedicated-friendly)
|
||||
|
||||
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a
|
||||
|
||||
+14
-2
@@ -167,7 +167,7 @@ remains `0`, and lateral regions remain gated off.
|
||||
| `../../Standalone/Core/src/CoreNamespacePreamble.h` | First include of every Core TU. Forces FP contraction off per compiler (Clang/MSVC/GCC pragmas; unknown compiler = `#error`) and defines `vfcore::fp_contract::selfTest`, which `World::compile` runs before building: a fused multiply-add (e.g. Clang `-ffp-contract=fast`) refuses world generation. The world depends on contraction (Linux2/Linux3 reports), so never remove either. |
|
||||
| `../../VoxelForgeCore/Private/ModuleEntry/` | Unreal module entry and an isolated `CoreMinimal.h` shim for the actual engine header. |
|
||||
| `../../Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h` | Standard-library-only public Core API. `VOXELFORGECORE_API` exports the API from the Unreal module and is empty for standalone builds. |
|
||||
| `../../Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h` / `../../Standalone/Core/src/ResolvedLayoutInternal.h` | `ResolvedLayoutSnapshotV3`: physical layout-chunk width, versioned micrometre intervals, owned ceiling/content/bedrock bands, transition and dispatch provenance, recipe anchors, director-planned passages, canonical JSON/FNV identity, compatibility/strict validators, and negative-control audit. Built once from the director-resolved plan and consumed by bounds planning, Core dispatch, composition, and validation. §8.40–8.41. |
|
||||
| `../../Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h` / `../../Standalone/Core/src/ResolvedLayoutInternal.h` | `ResolvedLayoutSnapshotV3`: physical layout-chunk width, versioned micrometre intervals, owned ceiling/content/bedrock bands, honored transition settings, deterministic per-column transition bands, recipe anchors, director-planned passages, canonical JSON/FNV identity, compatibility/strict validators, and negative-control audit. Built once from the director-resolved plan and consumed by bounds planning, Core dispatch, composition, and validation. §8.40–8.42, §8.46. |
|
||||
| `../../Standalone/Core/src/FloatingIslandsInternal.h` | Canonical standalone/Core floating-island planner, geometry-versioned field, manifest serializer, influence bounds, capsule-route checks, and thickness audit. |
|
||||
| `Tools/VoxelForgeTest.ps1` | The single guarded test entry point. `-Scenario canonical|owner|probe|perf|tests|surface-fall|crossing|gate-stress|visual-edit|visual-lod|visual-gap-walk|visual-gap-inject|dig-motion|parity|horizon|horizon-render|determinism|region-eval|step2d|step2e|step2f|step2f-capture|step3-scale|step3-game`, optional `-Build`, `-Assets owner|default`, `-Cvars`, absolute `-Out`, `-Label`, `-HorizonLevel`, `-HorizonRenderDistanceChunks`, `-Step2fProfile=0..7`, `-Step2fCaptureView=inside|wall-closeup`, `-ParityExportSize=32|64|128`, `-ParityExportTile=x,y,z`, `-SharedDDC`, `-SampleDensityStacks`, `-StackSampleIntervalUs`, and `-RegionHashProbe -RegionWorkers N -RegionReferenceHash HASH`; stages the host/assets, isolates user/DDC/config state, serializes one Unreal launch at a time, enforces the 8 GiB/30-minute guards, waits for 6 GiB free RAM before launches, and emits compact `result.json`/`summary.txt` plus full logs. Parity can render its game leg with `-EnableRender` and compares the selected export region against the exact streamed tile hashes. `visual-gap-walk` records camera-frustum surface-point coverage at 6000 cm/s; `visual-gap-inject` proves an early component hide is detected; `dig-motion` logs per-frame vertical velocity, gate, support readiness, and movement mode. `-Cvars @{ 'voxel.TestGapAuditLegacyHandoff'=1 }` replays the old visibility handoff for A/B audit baselines. Horizon traces report desired/loaded tiles, resident stream geometry, worker seconds, steady wall time, and peak private memory; `horizon-render` captures the owner view; `step2f-capture` selects one of eight parameter sets, finds a positive-density camera point and aims at a sampled rock wall for an inside or wall-closeup view, then saves it under `Saved/Research/step2f/captures` (pass the persistent render DDC with `-SharedDDC`); `determinism` compares per-tile hashes across the default CRT path and `-voxel.CrtFma3=0`; `region-eval` compares the live path with the default-off editor chunk program on the 259³ lattice and five-seed probe set; `step2d` retains the earlier independent field; `step2e` retains its earlier benchmark; `step2f` compares frozen step 2e and the coherent field for all eight profiles on the same 259³ ring, applies geometry gates, and checks sound bounds, seams, and 1-vs-6-worker hashes. `-RegionHashProbe -RegionWorkers N -RegionReferenceHash HASH` checks a full-ring hash at another partition count. `-SampleDensityStacks` captures profiles, and `-StackSampleIntervalUs` sets the sampling interval.
|
||||
| Step-3 scale and game validation | `step3-scale` measures the same 519³ and 259³ six-worker lattices at 12.5 cm. The cached medians are 7.347496 s and 1.169567 s respectively; determinism and brick sound-proof audits pass. `step3-game` routes in-game generation through the brick evaluator and records tile timings, spawn fill, moving frames, and profile-switch rebuilds. The visible D3D12 run measured tile generation p50/p95/max 3.326/31.045/169.867 ms, full-detail L0 fill 12.369 s for 2,765 tiles, and moving-frame p50/p95/p99/max 4.856/8.922/13.090/80.575 ms. Switching profiles rebuilt 5,815 loaded tiles. Both high-resolution screenshot requests were logged, but the harness found only one fresh PNG and it is a blank pale viewport; no lit comparison capture is accepted. Raw output: `Saved/TestHarness/step3_game_visible_final`; scale output: `Saved/Research/step3/scale/step3_scale_cacheperf_v1`. |
|
||||
@@ -1160,7 +1160,7 @@ and [`Standalone/Research/WorldAssemblerM3/Linux18/REPORT.md`](Standalone/Resear
|
||||
| Symbol | Canonical file | Role |
|
||||
|---|---|---|
|
||||
| `PlanResolvedLayoutPassagesV1`, `ResolvedLayoutPassageV1::lowerMouthControlIndex` | `Standalone/Core/src/ResolvedLayoutInternal.h`, public record in `VoxelForgeCore.h` | Plans deterministic fixed-metre passages from adjacent director-resolved strata, binds route departure/arrival mouths, and records the derived split between the monotone descent and route-bound landing tail. |
|
||||
| `FStandaloneShapeWorld::ApplyResolvedStrateComposition`, `ApplyResolvedStratePassageCarves` | `Standalone/Core/src/ShapeWorldInternal.h` | Applies boundary bands first, then opens the passage capsule and supported floor along the passage for every native generator. |
|
||||
| `FStandaloneShapeWorld::ApplyResolvedStrateComposition`, `ApplyResolvedStratePassageCarves` | `Standalone/Core/src/ShapeWorldInternal.h` | Applies resolved boundary bands first, then opens the passage capsule and supported floor along the passage for every native generator. |
|
||||
| `--audit-resolved-layout-passages` | `Standalone/Source/StreamBenchmarkPipeline.h` | Samples passage capsules, floor support, mouth ownership/radius, in-strate route bindings, and a blocked-passage negative control. |
|
||||
|
||||
The evaluator version is 9. See ARCHITECTURE §8.41 and
|
||||
@@ -1217,6 +1217,18 @@ schema V3 is unchanged. See ARCHITECTURE §8.44 and
|
||||
See ARCHITECTURE §8.45 and
|
||||
[Linux28 report](Standalone/Research/WorldAssemblerM3/Linux28/REPORT.md).
|
||||
|
||||
### 3.29 Honored bedrock/ceiling transitions — World Assembler L29
|
||||
|
||||
| Symbol | Canonical file | Role |
|
||||
|---|---|---|
|
||||
| `EvaluateResolvedLayoutTransitionBandsV1`, `AuditResolvedStrateTransitions` | Public Core header / `Standalone/Core/src/ResolvedLayoutInternal.h`; `Standalone/Source/StreamBenchmarkPipeline.h` | Resolve the deterministic mode field from seed, interface and coordinates; audit actual crossing depth, minimum band thickness, passage-excluded holes, and strict negative controls. |
|
||||
| `FStandaloneShapeWorld::ApplyResolvedStrateBoundaryBand` | `Standalone/Core/src/ShapeWorldInternal.h` | Applies Hard, Gradient or Interleaved to both facing bands in the shared Core composition path; tapers locally at passage mouths to preserve landing support. |
|
||||
|
||||
The shared evaluator is version 15. Recipe and resolved-layout schema versions are unchanged.
|
||||
The Unreal adapter now forwards the authored transition type and chunk width; those Unreal edits
|
||||
were not compiled here. See ARCHITECTURE §8.46 and
|
||||
[`Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md).
|
||||
|
||||
## 4. The density pipeline (most-edited hot path)
|
||||
|
||||
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749
|
||||
|
||||
@@ -1106,13 +1106,21 @@ bool AttachValidatedWorldAssembly(const UVoxelSettings* Settings,
|
||||
Item.transitionWidthMicrometres = static_cast<std::int64_t>(Item.transitionBlendChunks)
|
||||
* Item.managerChunkWidthMicrometres;
|
||||
Item.transitionWarpAmplitudeMicrometres = Item.transitionMode == "Interleaved"
|
||||
? 2 * Item.managerChunkWidthMicrometres : 0;
|
||||
Item.transitionInterpenetrationMicrometres = 0;
|
||||
Item.transitionHonouredInCore = Slot.Definition->TransitionType == EVoxelStrateTransition::Hard;
|
||||
Item.transitionFallsBackToHard = Slot.Definition->TransitionType != EVoxelStrateTransition::Hard;
|
||||
? std::min(Item.transitionWidthMicrometres / 2, Item.managerChunkWidthMicrometres) : 0;
|
||||
Item.transitionInterpenetrationMicrometres = Item.transitionMode == "Interleaved"
|
||||
? Item.transitionWidthMicrometres
|
||||
: Item.transitionMode == "Gradient" ? Item.transitionWidthMicrometres / 4 : 0;
|
||||
Item.transitionFallsBackToHard = Item.transitionMode != "Hard"
|
||||
&& Item.transitionWidthMicrometres <= 0;
|
||||
Item.transitionHonouredInCore = Item.transitionMode == "Hard"
|
||||
|| !Item.transitionFallsBackToHard;
|
||||
Item.transitionDispatchPath = Item.transitionFallsBackToHard
|
||||
? "declared Gradient/Interleaved recorded; Core uses an explicit Hard transition fallback with zero interpenetration"
|
||||
: "declared Hard transition is applied at this interface";
|
||||
? "declared zero-width transition uses an explicit Hard fallback"
|
||||
: Item.transitionMode == "Hard"
|
||||
? "declared Hard transition preserves the flat shared bedrock/ceiling interface"
|
||||
: Item.transitionMode == "Interleaved"
|
||||
? "shared composition overlaps both facing bands with deterministic domain-warped fingers inside the declared width"
|
||||
: "shared composition moves both band faces together with the smooth declared-width transition field";
|
||||
Item.managerLookup = "UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate";
|
||||
if (Type == ECaveGeneratorType::SurfaceWorld)
|
||||
{
|
||||
|
||||
@@ -201,15 +201,19 @@ enum class ECaveGeneratorType : uint8
|
||||
};
|
||||
|
||||
/**
|
||||
* EVoxelStrateTransition — How two adjacent strates blend at their shared boundary.
|
||||
* EVoxelStrateTransition — How two adjacent strates meet at their shared boundary.
|
||||
*
|
||||
* Each strate boundary can use a different transition style. The transition type
|
||||
* is defined on the UPPER strate's definition and controls the boundary between
|
||||
* itself and the strate below it.
|
||||
*
|
||||
* Gradient: Smooth linear interpolation of generation params across TransitionBlendChunks.
|
||||
* This is the classic approach — no seam visible, but also no dramatic
|
||||
* geological feature at the boundary. Best for similar-looking strates.
|
||||
* In the Core composition path, TransitionBlendChunks defines the full permitted
|
||||
* crossing depth between the upper bedrock and lower ceiling bands. Each band keeps
|
||||
* its authored thickness and the solid bands always overlap or touch. The legacy manager
|
||||
* may also blend generation parameters for Gradient; Core does not blend unrelated fields.
|
||||
*
|
||||
* Gradient: A low-amplitude smooth height field moves the shared interface and both
|
||||
* content-facing band surfaces. Best for gentle geological variation.
|
||||
*
|
||||
* Hard: No blending at all. The strate's params apply right up to the boundary,
|
||||
* then instantly switch to the neighbor's params. The abrupt change in
|
||||
@@ -217,26 +221,21 @@ enum class ECaveGeneratorType : uint8
|
||||
* discontinuity at the boundary. Best for strates that should feel like
|
||||
* distinct geological layers with a visible dividing line.
|
||||
*
|
||||
* Interleaved: The boundary position is warped by a 3D noise field, so "fingers"
|
||||
* of one strate reach into the other. Some XY columns transition early,
|
||||
* others late, creating an irregular, interlocking boundary. The warp
|
||||
* amplitude is ~2 chunks, and Z frequency is lower than XY to produce
|
||||
* horizontal finger-like intrusions. Best for strates that should feel
|
||||
* like they grew into each other over geological time.
|
||||
* Interleaved: A deterministic 3D domain-warped hash field sets the overlap depth per XY
|
||||
* column from zero to the declared width, creating irregular fingers.
|
||||
* Best for strates that should feel grown together.
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EVoxelStrateTransition : uint8
|
||||
{
|
||||
// Smooth linear interpolation of all generation params across the blend zone.
|
||||
// No visible boundary — params change gradually over TransitionBlendChunks distance.
|
||||
// Smoothly undulates the shared bedrock/ceiling interface in the Core composition path.
|
||||
Gradient UMETA(DisplayName = "Gradient (smooth blend)"),
|
||||
|
||||
// No blending — params switch instantly at the boundary.
|
||||
// Creates natural cliffs, ledges, or material discontinuities.
|
||||
Hard UMETA(DisplayName = "Hard (sharp boundary with cliff)"),
|
||||
|
||||
// 3D noise warps the boundary position per XY column.
|
||||
// Creates finger-like intrusions of one strate into the other.
|
||||
// A 3D domain-warped hash field creates finger-like intrusions within the declared width.
|
||||
Interleaved UMETA(DisplayName = "Interleaved (domain-warped fingers)")
|
||||
};
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace VoxelForgeCore
|
||||
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
|
||||
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 2;
|
||||
// Version 14 adds shared passage columns for long native open-air crossings.
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 14;
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 15;
|
||||
|
||||
enum class FieldChannel : std::uint32_t
|
||||
{
|
||||
@@ -639,6 +639,18 @@ namespace VoxelForgeCore
|
||||
std::vector<ResolvedLayoutAnchorV2> anchors;
|
||||
};
|
||||
|
||||
/** Per-column resolved bands for one adjacent bedrock/ceiling interface.
|
||||
* crossingDepthMicrometres is the actual overlap depth between the two facing bands.
|
||||
*/
|
||||
struct ResolvedLayoutTransitionBandsV1
|
||||
{
|
||||
std::int64_t crossingDepthMicrometres = 0;
|
||||
std::int64_t upperBedrockBottomMicrometres = 0;
|
||||
std::int64_t upperBedrockTopMicrometres = 0;
|
||||
std::int64_t lowerCeilingBottomMicrometres = 0;
|
||||
std::int64_t lowerCeilingTopMicrometres = 0;
|
||||
};
|
||||
|
||||
struct ResolvedLayoutIssueV2
|
||||
{
|
||||
std::string code;
|
||||
@@ -730,6 +742,17 @@ namespace VoxelForgeCore
|
||||
std::string_view json, std::string& error);
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve the deterministic band faces at one XY column. Gradient shares one smooth
|
||||
* displaced face; Interleaved moves the faces inward by a total crossing no larger than
|
||||
* the declared width. Both bands retain authored thickness and touch or overlap, so no
|
||||
* air gap can form.
|
||||
*/
|
||||
VFCORE_API ResolvedLayoutTransitionBandsV1 EvaluateResolvedLayoutTransitionBandsV1(
|
||||
const ResolvedLayoutStratumV2& upper, const ResolvedLayoutStratumV2& lower,
|
||||
std::int32_t worldSeed, std::int64_t layoutChunkSizeMicrometres,
|
||||
std::int64_t xMicrometres, std::int64_t yMicrometres);
|
||||
|
||||
struct ResolvedLayoutValidatorAuditV3
|
||||
{
|
||||
std::uint32_t controlsRun = 0;
|
||||
|
||||
@@ -13,6 +13,97 @@ namespace VoxelForgeCore
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr std::int64_t TransitionQ16 = 1ll << 16;
|
||||
|
||||
void TransitionHashWord(std::uint64_t& hash, std::uint64_t word)
|
||||
{
|
||||
for (int byte = 0; byte < 8; ++byte)
|
||||
{
|
||||
hash ^= static_cast<std::uint8_t>(word >> (byte * 8));
|
||||
hash *= 1099511628211ull;
|
||||
}
|
||||
}
|
||||
|
||||
std::uint64_t TransitionHash(std::int32_t seed, std::int32_t interfaceIndex,
|
||||
std::int64_t x, std::int64_t y, std::int64_t z, std::uint32_t salt)
|
||||
{
|
||||
std::uint64_t hash = 14695981039346656037ull;
|
||||
TransitionHashWord(hash, static_cast<std::uint32_t>(seed));
|
||||
TransitionHashWord(hash, static_cast<std::uint32_t>(interfaceIndex));
|
||||
TransitionHashWord(hash, static_cast<std::uint64_t>(x));
|
||||
TransitionHashWord(hash, static_cast<std::uint64_t>(y));
|
||||
TransitionHashWord(hash, static_cast<std::uint64_t>(z));
|
||||
TransitionHashWord(hash, salt);
|
||||
hash ^= hash >> 30; hash *= 0xbf58476d1ce4e5b9ull;
|
||||
hash ^= hash >> 27; hash *= 0x94d049bb133111ebull;
|
||||
hash ^= hash >> 31;
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::int64_t TransitionNoiseValue(std::int32_t seed, std::int32_t interfaceIndex,
|
||||
std::int64_t x, std::int64_t y, std::int64_t z, std::uint32_t salt)
|
||||
{
|
||||
const std::uint32_t bits = static_cast<std::uint32_t>(
|
||||
TransitionHash(seed, interfaceIndex, x, y, z, salt) >> 32);
|
||||
return static_cast<std::int64_t>(bits >> 15) - TransitionQ16;
|
||||
}
|
||||
|
||||
std::int64_t TransitionFloorDiv(std::int64_t value, std::int64_t divisor)
|
||||
{
|
||||
const std::int64_t quotient = value / divisor;
|
||||
const std::int64_t remainder = value % divisor;
|
||||
return quotient - (remainder < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
std::int64_t TransitionFade(std::int64_t valueQ16)
|
||||
{
|
||||
valueQ16 = std::clamp(valueQ16, std::int64_t{0}, TransitionQ16);
|
||||
const std::int64_t square = (valueQ16 * valueQ16) / TransitionQ16;
|
||||
return (square * (3 * TransitionQ16 - 2 * valueQ16)) / TransitionQ16;
|
||||
}
|
||||
|
||||
std::int64_t TransitionLerp(std::int64_t a, std::int64_t b, std::int64_t tQ16)
|
||||
{ return a + ((b - a) * tQ16) / TransitionQ16; }
|
||||
|
||||
std::int64_t TransitionValueNoise2D(std::int32_t seed, std::int32_t interfaceIndex,
|
||||
std::int64_t x, std::int64_t y, std::int64_t cell, std::uint32_t salt)
|
||||
{
|
||||
const std::int64_t ix = TransitionFloorDiv(x, cell), iy = TransitionFloorDiv(y, cell);
|
||||
const std::int64_t tx = TransitionFade(((x - ix * cell) * TransitionQ16) / cell);
|
||||
const std::int64_t ty = TransitionFade(((y - iy * cell) * TransitionQ16) / cell);
|
||||
const auto at = [&](std::int64_t dx, std::int64_t dy)
|
||||
{ return TransitionNoiseValue(seed, interfaceIndex, ix + dx, iy + dy, 0, salt); };
|
||||
return TransitionLerp(TransitionLerp(at(0, 0), at(1, 0), tx),
|
||||
TransitionLerp(at(0, 1), at(1, 1), tx), ty);
|
||||
}
|
||||
|
||||
std::int64_t TransitionValueNoise3D(std::int32_t seed, std::int32_t interfaceIndex,
|
||||
std::int64_t x, std::int64_t y, std::int64_t z, std::int64_t cell,
|
||||
std::uint32_t salt)
|
||||
{
|
||||
const std::int64_t ix = TransitionFloorDiv(x, cell), iy = TransitionFloorDiv(y, cell);
|
||||
const std::int64_t iz = TransitionFloorDiv(z, cell);
|
||||
const std::int64_t tx = TransitionFade(((x - ix * cell) * TransitionQ16) / cell);
|
||||
const std::int64_t ty = TransitionFade(((y - iy * cell) * TransitionQ16) / cell);
|
||||
const std::int64_t tz = TransitionFade(((z - iz * cell) * TransitionQ16) / cell);
|
||||
const auto at = [&](std::int64_t dx, std::int64_t dy, std::int64_t dz)
|
||||
{ return TransitionNoiseValue(seed, interfaceIndex, ix + dx, iy + dy, iz + dz, salt); };
|
||||
const std::int64_t z0 = TransitionLerp(
|
||||
TransitionLerp(at(0, 0, 0), at(1, 0, 0), tx),
|
||||
TransitionLerp(at(0, 1, 0), at(1, 1, 0), tx), ty);
|
||||
const std::int64_t z1 = TransitionLerp(
|
||||
TransitionLerp(at(0, 0, 1), at(1, 0, 1), tx),
|
||||
TransitionLerp(at(0, 1, 1), at(1, 1, 1), tx), ty);
|
||||
return TransitionLerp(z0, z1, tz);
|
||||
}
|
||||
|
||||
std::int64_t LayoutTransitionLimit(const ResolvedLayoutStratumV2& stratum)
|
||||
{
|
||||
if (stratum.transitionMode == "Hard" || stratum.transitionFallsBackToHard)
|
||||
return 0;
|
||||
return std::max<std::int64_t>(0, stratum.transitionWidthMicrometres);
|
||||
}
|
||||
|
||||
std::string LayoutEscape(std::string_view value)
|
||||
{
|
||||
std::string out="\"";
|
||||
@@ -41,12 +132,6 @@ namespace
|
||||
}
|
||||
std::string LayoutHex(std::uint64_t value)
|
||||
{std::ostringstream out;out<<std::hex<<std::setfill('0')<<std::setw(16)<<value;return out.str();}
|
||||
std::int64_t LayoutTransitionLimit(const ResolvedLayoutStratumV2& stratum)
|
||||
{
|
||||
if(stratum.transitionMode=="Hard"||stratum.transitionFallsBackToHard)return 0;
|
||||
return stratum.transitionWidthMicrometres
|
||||
+(stratum.transitionMode=="Interleaved"?stratum.transitionWarpAmplitudeMicrometres:0);
|
||||
}
|
||||
std::string LayoutBandJson(const ResolvedLayoutBandV2& band)
|
||||
{
|
||||
std::ostringstream out;out<<"{\"kind\":"<<LayoutEscape(band.kind)
|
||||
@@ -205,6 +290,84 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
ResolvedLayoutTransitionBandsV1 EvaluateResolvedLayoutTransitionBandsV1(
|
||||
const ResolvedLayoutStratumV2& upper, const ResolvedLayoutStratumV2& lower,
|
||||
std::int32_t worldSeed, std::int64_t layoutChunkSizeMicrometres,
|
||||
std::int64_t xMicrometres, std::int64_t yMicrometres)
|
||||
{
|
||||
ResolvedLayoutTransitionBandsV1 result;
|
||||
const std::int64_t seam = upper.bedrock.bottomMicrometres;
|
||||
const std::int64_t upperThickness = upper.bedrock.topMicrometres - seam;
|
||||
const std::int64_t lowerThickness = lower.ceiling.topMicrometres
|
||||
- lower.ceiling.bottomMicrometres;
|
||||
result.upperBedrockBottomMicrometres = seam;
|
||||
result.upperBedrockTopMicrometres = seam + std::max<std::int64_t>(0, upperThickness);
|
||||
result.lowerCeilingBottomMicrometres = seam - std::max<std::int64_t>(0, lowerThickness);
|
||||
result.lowerCeilingTopMicrometres = seam;
|
||||
|
||||
const std::int64_t width = upper.transitionFallsBackToHard
|
||||
? 0 : std::max<std::int64_t>(0, upper.transitionWidthMicrometres);
|
||||
if (upper.transitionMode == "Hard" || width == 0
|
||||
|| (upper.transitionMode != "Gradient" && upper.transitionMode != "Interleaved")
|
||||
|| !upper.bedrock.presentInCurrentField || !lower.ceiling.presentInCurrentField
|
||||
|| upperThickness <= 0 || lowerThickness <= 0)
|
||||
return result;
|
||||
|
||||
if (width <= 0) return result;
|
||||
|
||||
std::int64_t noise = 0;
|
||||
if (upper.transitionMode == "Gradient")
|
||||
{
|
||||
const std::int64_t wavelength = std::max<std::int64_t>(
|
||||
1000000, std::max<std::int64_t>(1, layoutChunkSizeMicrometres) * 4);
|
||||
noise = TransitionValueNoise2D(worldSeed, upper.strateIndex,
|
||||
xMicrometres, yMicrometres, wavelength, 0x47524144u);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A face crossing wider than the sum of the authored bands would separate
|
||||
// their far edges and leave an air slab. Limit the physical crossing to the
|
||||
// smaller of the declared transition width and the combined band thickness.
|
||||
const std::int64_t maximumContinuousCrossing=upperThickness+lowerThickness;
|
||||
const std::int64_t crossingLimit=std::min(width,maximumContinuousCrossing);
|
||||
const std::int64_t cell = std::max<std::int64_t>(1000000,
|
||||
std::max<std::int64_t>(1, layoutChunkSizeMicrometres) * 2);
|
||||
const std::int64_t warp = std::min(crossingLimit / 2, cell / 2);
|
||||
const std::int64_t warpX = (TransitionValueNoise3D(worldSeed, upper.strateIndex,
|
||||
xMicrometres, yMicrometres, seam, cell, 0x49574158u) * warp) / TransitionQ16;
|
||||
const std::int64_t warpY = (TransitionValueNoise3D(worldSeed, upper.strateIndex,
|
||||
xMicrometres, yMicrometres, seam, cell, 0x49574159u) * warp) / TransitionQ16;
|
||||
const std::int64_t warpZ = (TransitionValueNoise3D(worldSeed, upper.strateIndex,
|
||||
xMicrometres, yMicrometres, seam, cell, 0x4957415au) * warp) / TransitionQ16;
|
||||
noise = TransitionValueNoise3D(worldSeed, upper.strateIndex,
|
||||
xMicrometres + warpX, yMicrometres + warpY, seam + warpZ,
|
||||
cell, 0x494e544cu);
|
||||
|
||||
// Interleaved means physical crossing: the two faces enter each other's
|
||||
// bands by a total depth in [0, declared width]. Split that overlap across
|
||||
// both faces so neither stratum owns the entire displacement. Since both
|
||||
// resulting intervals are solid and overlap, the transition cannot open a gap.
|
||||
const std::int64_t overlap = ((noise + TransitionQ16) * crossingLimit)
|
||||
/ (2 * TransitionQ16);
|
||||
const std::int64_t upperInset = overlap / 2;
|
||||
const std::int64_t lowerInset = overlap - upperInset;
|
||||
result.crossingDepthMicrometres = overlap;
|
||||
result.upperBedrockBottomMicrometres = seam - upperInset;
|
||||
result.upperBedrockTopMicrometres = result.upperBedrockBottomMicrometres + upperThickness;
|
||||
result.lowerCeilingTopMicrometres = seam + lowerInset;
|
||||
result.lowerCeilingBottomMicrometres = result.lowerCeilingTopMicrometres - lowerThickness;
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::int64_t displacement = (noise * (width / 8)) / TransitionQ16;
|
||||
const std::int64_t interfaceZ = seam + displacement;
|
||||
result.upperBedrockBottomMicrometres = interfaceZ;
|
||||
result.upperBedrockTopMicrometres = interfaceZ + upperThickness;
|
||||
result.lowerCeilingBottomMicrometres = interfaceZ - lowerThickness;
|
||||
result.lowerCeilingTopMicrometres = interfaceZ;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ResolvedLayoutSnapshotV3::ToJson() const
|
||||
{
|
||||
const std::string body=LayoutBodyJson(*this);
|
||||
@@ -216,7 +379,8 @@ bool ResolvedLayoutSnapshotV3::finalize(std::string& error)
|
||||
{
|
||||
error.clear();
|
||||
if(schemaVersion!=ResolvedLayoutSchemaVersionV3||worldIdentity.empty()||strates.empty()
|
||||
||strateLayoutChunkSizeMicrometres==0||strateLayoutChunkWidthSubvoxels<=0)
|
||||
||coreVoxelSizeMicrometres==0||strateLayoutChunkSizeMicrometres==0
|
||||
||strateLayoutChunkWidthSubvoxels<=0)
|
||||
{error="resolved layout has an unsupported version, empty world identity, or no strates";return false;}
|
||||
std::stable_sort(strates.begin(),strates.end(),[](const auto& a,const auto& b)
|
||||
{return a.interiorTopMicrometres!=b.interiorTopMicrometres
|
||||
@@ -257,7 +421,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
{
|
||||
issues.clear();error.clear();
|
||||
if(schemaVersion!=ResolvedLayoutSchemaVersionV3||worldIdentity.empty()||strates.empty()
|
||||
||strateLayoutChunkSizeMicrometres==0||strateLayoutChunkWidthSubvoxels<=0)
|
||||
||coreVoxelSizeMicrometres==0||strateLayoutChunkSizeMicrometres==0
|
||||
||strateLayoutChunkWidthSubvoxels<=0)
|
||||
{error="resolved layout schema/world/strata header is invalid";return false;}
|
||||
const auto add=[&](std::string code,std::string detail)
|
||||
{issues.push_back({std::move(code),std::move(detail)});};
|
||||
@@ -271,6 +436,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
if(s.transitionInterpenetrationMicrometres>effectiveTransitionWidth
|
||||
||s.transitionInterpenetrationMicrometres<0)
|
||||
add("transition_overlap_exceeds_width",s.stableId+" transition interpenetration exceeds its declared width");
|
||||
if(s.transitionWidthMicrometres<0||s.transitionBlendChunks<0)
|
||||
add("invalid_transition_width",s.stableId+" has a negative transition width");
|
||||
if(s.managerChunkWidthMicrometres>0&&s.dispatchChunkWidthMicrometres>0
|
||||
&&s.managerChunkWidthMicrometres!=s.dispatchChunkWidthMicrometres)
|
||||
add("ambiguous_dispatch_units",s.stableId+" manager chunk width differs from Core dispatch chunk width");
|
||||
@@ -290,6 +457,10 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
{
|
||||
if(b.kind!=expected||b.topMicrometres<=b.bottomMicrometres)
|
||||
add("wrong_order",s.stableId+" has an invalid "+std::string(expected)+" band");
|
||||
const std::int64_t minimumThickness=2ll*coreVoxelSizeMicrometres;
|
||||
if(b.topMicrometres-b.bottomMicrometres<minimumThickness)
|
||||
add("band_thinner_than_minimum",s.stableId+" "+std::string(expected)
|
||||
+" is thinner than the two-Core-voxel minimum");
|
||||
if(b.protectedBand&&b.diggable)
|
||||
add("invalid_protection",s.stableId+" band is both protected and diggable");
|
||||
if(b.solidDensityQ16<=0)
|
||||
@@ -1055,6 +1226,11 @@ ResolvedLayoutValidatorAuditV3 AuditResolvedLayoutValidatorV3()
|
||||
auto missingCeiling=base;missingCeiling.strates[0].ceiling.presentInCurrentField=false;expectReject("missing ceiling",missingCeiling);
|
||||
auto wrongOrder=base;wrongOrder.strates[0].content.kind="bedrock";expectReject("wrong band order",wrongOrder);
|
||||
auto largeOverlap=base;largeOverlap.strates[0].transitionInterpenetrationMicrometres=4000001;expectReject("interpenetration exceeds transition width",largeOverlap);
|
||||
auto thinBand=base;
|
||||
thinBand.strates[0].bedrock.topMicrometres=thinBand.strates[0].bedrock.bottomMicrometres
|
||||
+2ll*base.coreVoxelSizeMicrometres-1;
|
||||
thinBand.strates[0].content.bottomMicrometres=thinBand.strates[0].bedrock.topMicrometres;
|
||||
expectReject("bedrock band below minimum thickness",thinBand);
|
||||
auto genericOverlap=base;genericOverlap.strates[0].bedrock.bottomMicrometres-=1;expectReject("nominal band overlap",genericOverlap);
|
||||
auto genericGap=base;genericGap.strates[0].bedrock.bottomMicrometres+=1;expectReject("nominal band gap",genericGap);
|
||||
result.passed=result.controlsRun==result.controlsRejectedAsExpected&&result.failures.empty();return result;
|
||||
|
||||
@@ -144,11 +144,96 @@ public:
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* FindResolvedLayoutStratum(int32 StrateIndex) const
|
||||
{
|
||||
if(!ResolvedLayout)return nullptr;
|
||||
if(StrateIndex>=0&&static_cast<std::size_t>(StrateIndex)<ResolvedLayout->strates.size())
|
||||
{
|
||||
const auto& direct=ResolvedLayout->strates[static_cast<std::size_t>(StrateIndex)];
|
||||
if(direct.strateIndex==StrateIndex)return &direct;
|
||||
}
|
||||
const auto it=std::find_if(ResolvedLayout->strates.begin(),ResolvedLayout->strates.end(),
|
||||
[StrateIndex](const auto& item){return item.strateIndex==StrateIndex;});
|
||||
return it==ResolvedLayout->strates.end()?nullptr:&*it;
|
||||
}
|
||||
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* FindResolvedLayoutStratumByOrder(int32 Order) const
|
||||
{
|
||||
if(!ResolvedLayout||Order<0)return nullptr;
|
||||
if(static_cast<std::size_t>(Order)<ResolvedLayout->strates.size())
|
||||
{
|
||||
const auto& direct=ResolvedLayout->strates[static_cast<std::size_t>(Order)];
|
||||
if(direct.order==Order)return &direct;
|
||||
}
|
||||
const auto it=std::find_if(ResolvedLayout->strates.begin(),ResolvedLayout->strates.end(),
|
||||
[Order](const auto& item){return item.order==Order;});
|
||||
return it==ResolvedLayout->strates.end()?nullptr:&*it;
|
||||
}
|
||||
|
||||
bool IsResolvedLayoutTransitionActive(const VoxelForgeCore::ResolvedLayoutStratumV2& Upper) const
|
||||
{
|
||||
return Upper.transitionMode!="Hard"&&!Upper.transitionFallsBackToHard
|
||||
&&Upper.transitionWidthMicrometres>0
|
||||
&&Upper.transitionHonouredInCore;
|
||||
}
|
||||
|
||||
std::int64_t ResolvedTransitionMaximumFaceDisplacementMicrometres(
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2& Upper,
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2& Lower) const
|
||||
{
|
||||
const std::int64_t width=std::max<std::int64_t>(0,Upper.transitionWidthMicrometres);
|
||||
if(Upper.transitionMode=="Gradient")return width/8;
|
||||
if(Upper.transitionMode!="Interleaved")return 0;
|
||||
const std::int64_t upperThickness=std::max<std::int64_t>(0,
|
||||
Upper.bedrock.topMicrometres-Upper.bedrock.bottomMicrometres);
|
||||
const std::int64_t lowerThickness=std::max<std::int64_t>(0,
|
||||
Lower.ceiling.topMicrometres-Lower.ceiling.bottomMicrometres);
|
||||
const std::int64_t maximumCrossing=std::min(width,upperThickness+lowerThickness);
|
||||
return (maximumCrossing+1)/2;
|
||||
}
|
||||
|
||||
std::int64_t ResolvedTransitionPassageWeightQ16(
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2& Upper,
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2& Lower,
|
||||
std::int64_t XMicrometres,std::int64_t YMicrometres) const
|
||||
{
|
||||
constexpr std::int64_t Q16=1ll<<16;
|
||||
constexpr std::int64_t SupportMarginMicrometres=1000000;
|
||||
constexpr std::int64_t FadeWidthMicrometres=2000000;
|
||||
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0)return Q16;
|
||||
const std::int64_t seam=Upper.bedrock.bottomMicrometres;
|
||||
const std::int64_t coreMicrometres=Owner->CoreVoxelSizeMicrons;
|
||||
const std::int64_t subvoxelsPerVoxel=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
std::int64_t weight=Q16;
|
||||
for(const auto& passage:ResolvedLayout->passages)
|
||||
{
|
||||
if(passage.upperStrateIndex!=Upper.strateIndex
|
||||
||passage.lowerStrateIndex!=Lower.strateIndex)continue;
|
||||
const std::int64_t mouthRadiusMicrometres=std::max<std::int64_t>(0,
|
||||
passage.mouthRadiusSubvoxels*coreMicrometres/subvoxelsPerVoxel);
|
||||
const std::int64_t protectedRadius=mouthRadiusMicrometres
|
||||
+SupportMarginMicrometres;
|
||||
for(const auto* mouth:{&passage.upperMouthSubvoxels,&passage.lowerMouthSubvoxels})
|
||||
{
|
||||
const std::int64_t mouthZMicrometres=mouth->at(2)*coreMicrometres
|
||||
/subvoxelsPerVoxel;
|
||||
if(std::abs(mouthZMicrometres-seam)
|
||||
>Upper.transitionWidthMicrometres/2+mouthRadiusMicrometres)continue;
|
||||
const double dx=static_cast<double>(XMicrometres-mouth->at(0)*coreMicrometres
|
||||
/subvoxelsPerVoxel);
|
||||
const double dy=static_cast<double>(YMicrometres-mouth->at(1)*coreMicrometres
|
||||
/subvoxelsPerVoxel);
|
||||
const std::int64_t fadeEnd=protectedRadius+FadeWidthMicrometres;
|
||||
const double distanceSquared=dx*dx+dy*dy;
|
||||
if(distanceSquared>=static_cast<double>(fadeEnd)*fadeEnd)continue;
|
||||
if(distanceSquared<=static_cast<double>(protectedRadius)*protectedRadius)return 0;
|
||||
const double distance=std::sqrt(distanceSquared);
|
||||
const double t=static_cast<double>(static_cast<std::int64_t>(distance)
|
||||
-protectedRadius)/FadeWidthMicrometres;
|
||||
const double smooth=t*t*(3.0-2.0*t);
|
||||
weight=std::min(weight,static_cast<std::int64_t>(std::llround(smooth*Q16)));
|
||||
}
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
bool IsInsideResolvedStrateBoundaryBand(float Z) const
|
||||
{
|
||||
if(!ResolvedLayout||!Owner||!Owner->Manager||!std::isfinite(Z))return false;
|
||||
@@ -160,6 +245,21 @@ public:
|
||||
const auto solidAt=[&](const VoxelForgeCore::ResolvedLayoutBandV2& band)
|
||||
{return band.presentInCurrentField&&zMicrometres>=band.bottomMicrometres
|
||||
&&zMicrometres<band.topMicrometres;};
|
||||
const auto mayTouchTransition=[&](const VoxelForgeCore::ResolvedLayoutStratumV2* Upper,
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* Lower)
|
||||
{
|
||||
if(!Upper||!Lower||!IsResolvedLayoutTransitionActive(*Upper))return false;
|
||||
const auto maxDisplacement=ResolvedTransitionMaximumFaceDisplacementMicrometres(
|
||||
*Upper,*Lower);
|
||||
const double minimum=std::min(Upper->bedrock.bottomMicrometres,
|
||||
Lower->ceiling.bottomMicrometres)-maxDisplacement;
|
||||
const double maximum=std::max(Upper->bedrock.topMicrometres,
|
||||
Lower->ceiling.topMicrometres)+maxDisplacement;
|
||||
return zMicrometres>=minimum&&zMicrometres<maximum;
|
||||
};
|
||||
const auto* lower=FindResolvedLayoutStratumByOrder(stratum->order+1);
|
||||
const auto* upper=stratum->order>0?FindResolvedLayoutStratumByOrder(stratum->order-1):nullptr;
|
||||
if(mayTouchTransition(stratum,lower)||mayTouchTransition(upper,stratum))return true;
|
||||
return solidAt(stratum->ceiling)||solidAt(stratum->bedrock);
|
||||
}
|
||||
|
||||
@@ -175,8 +275,68 @@ public:
|
||||
const auto solidAt=[&](const VoxelForgeCore::ResolvedLayoutBandV2& band)
|
||||
{return band.presentInCurrentField&&zMicrometres>=band.bottomMicrometres
|
||||
&&zMicrometres<band.topMicrometres;};
|
||||
const auto* band=solidAt(stratum->ceiling)?&stratum->ceiling
|
||||
:solidAt(stratum->bedrock)?&stratum->bedrock:nullptr;
|
||||
|
||||
bool bCeilingHandledByTransition=false,bBedrockHandledByTransition=false;
|
||||
const auto applyInterface=[&](const VoxelForgeCore::ResolvedLayoutStratumV2* upper,
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* lower)
|
||||
{
|
||||
if(!upper||!lower||!IsResolvedLayoutTransitionActive(*upper))return false;
|
||||
const auto maxDisplacement=ResolvedTransitionMaximumFaceDisplacementMicrometres(
|
||||
*upper,*lower);
|
||||
const double minimum=std::min(upper->bedrock.bottomMicrometres,
|
||||
lower->ceiling.bottomMicrometres)-maxDisplacement;
|
||||
const double maximum=std::max(upper->bedrock.topMicrometres,
|
||||
lower->ceiling.topMicrometres)+maxDisplacement;
|
||||
if(zMicrometres<minimum||zMicrometres>=maximum)return false;
|
||||
if(upper==stratum)bBedrockHandledByTransition=true;
|
||||
if(lower==stratum)bCeilingHandledByTransition=true;
|
||||
const std::int64_t xMicrometres=static_cast<std::int64_t>(std::llround(
|
||||
static_cast<double>(X)*Owner->CoreVoxelSizeMicrons));
|
||||
const std::int64_t yMicrometres=static_cast<std::int64_t>(std::llround(
|
||||
static_cast<double>(Y)*Owner->CoreVoxelSizeMicrons));
|
||||
const auto bands=VoxelForgeCore::EvaluateResolvedLayoutTransitionBandsV1(*upper,*lower,
|
||||
Owner->Seed,ResolvedLayout->strateLayoutChunkSizeMicrometres,
|
||||
xMicrometres,yMicrometres);
|
||||
auto resolvedBands=bands;
|
||||
const std::int64_t transitionWeight=ResolvedTransitionPassageWeightQ16(
|
||||
*upper,*lower,xMicrometres,yMicrometres);
|
||||
constexpr std::int64_t TransitionQ16=1ll<<16;
|
||||
if(transitionWeight<TransitionQ16)
|
||||
{
|
||||
const std::int64_t seam=upper->bedrock.bottomMicrometres;
|
||||
const std::int64_t upperThickness=upper->bedrock.topMicrometres-seam;
|
||||
const std::int64_t lowerThickness=lower->ceiling.topMicrometres
|
||||
-lower->ceiling.bottomMicrometres;
|
||||
const auto scaled=[&](std::int64_t value)
|
||||
{return (value*transitionWeight)/TransitionQ16;};
|
||||
resolvedBands.upperBedrockBottomMicrometres=seam+scaled(
|
||||
bands.upperBedrockBottomMicrometres-seam);
|
||||
resolvedBands.upperBedrockTopMicrometres=
|
||||
resolvedBands.upperBedrockBottomMicrometres+upperThickness;
|
||||
resolvedBands.lowerCeilingTopMicrometres=seam+scaled(
|
||||
bands.lowerCeilingTopMicrometres-seam);
|
||||
resolvedBands.lowerCeilingBottomMicrometres=
|
||||
resolvedBands.lowerCeilingTopMicrometres-lowerThickness;
|
||||
resolvedBands.crossingDepthMicrometres=std::max<std::int64_t>(0,
|
||||
resolvedBands.lowerCeilingTopMicrometres
|
||||
-resolvedBands.upperBedrockBottomMicrometres);
|
||||
}
|
||||
const bool upperBedrock=zMicrometres>=resolvedBands.upperBedrockBottomMicrometres
|
||||
&&zMicrometres<resolvedBands.upperBedrockTopMicrometres;
|
||||
const bool lowerCeiling=zMicrometres>=resolvedBands.lowerCeilingBottomMicrometres
|
||||
&&zMicrometres<resolvedBands.lowerCeilingTopMicrometres;
|
||||
if(!upperBedrock&&!lowerCeiling)return false;
|
||||
const auto& band=upperBedrock?upper->bedrock:lower->ceiling;
|
||||
density=-std::max(0.05f,static_cast<float>(band.solidDensityQ16)/65536.0f);
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto* lower=FindResolvedLayoutStratumByOrder(stratum->order+1);
|
||||
const auto* upper=stratum->order>0?FindResolvedLayoutStratumByOrder(stratum->order-1):nullptr;
|
||||
if(applyInterface(stratum,lower)||applyInterface(upper,stratum))return density;
|
||||
const auto* band=(!bCeilingHandledByTransition&&solidAt(stratum->ceiling))
|
||||
?&stratum->ceiling:(!bBedrockHandledByTransition&&solidAt(stratum->bedrock))
|
||||
?&stratum->bedrock:nullptr;
|
||||
if(!band)return density;
|
||||
return -std::max(0.05f,static_cast<float>(band->solidDensityQ16)/65536.0f);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# World Assembler L29 — bedrock/ceiling transitions
|
||||
|
||||
**Core now honors the upper strate's `EVoxelStrateTransition` at the shared interface.** The owner
|
||||
world's upper two-chunk Gradient resolves to 16 m at the 8 m layout chunk size. SurfaceWorld's
|
||||
one-chunk setting remains in the resolved strate record. Core does not blend unrelated generator
|
||||
parameters. `WorldAssemblyEvaluatorVersion` is 15; recipe versions and resolved-layout schema V3
|
||||
are unchanged.
|
||||
|
||||
## Appearance and safety
|
||||
|
||||
| Mode | Result |
|
||||
|---|---|
|
||||
| Hard | The original flat, touching boundary. No displacement or overlap. |
|
||||
| Gradient | Both faces follow one smooth 2D hash surface. On the 16 m interface, the measured peak-to-peak undulation is 3.988 m. |
|
||||
| Interleaved | A deterministic 3D domain-warped hash field moves both faces inward, producing fingered overlap. Measured peak crossing was 6.487 m in FloatingIslands and 6.952 m in SurfaceWorld, within the declared 16 m. |
|
||||
|
||||
Each boundary band keeps at least **two Core voxels** of solid thickness: 0.5 m at the default
|
||||
25 cm voxel size. The owner bands measured 0.5–1.0 m on the upper side and 6.25 m on the lower
|
||||
side. Interleaved is capped by both the declared width and the bands' combined thickness, so the
|
||||
solid intervals still touch or overlap.
|
||||
|
||||
The strict interface audit covers a 512 m square: 4,221 columns per interface, with four passage
|
||||
columns excluded from the hole sweep. Hard, Gradient and Interleaved each report zero hole columns
|
||||
and zero air samples. Both negative controls reject as expected: crossing one micrometre wider than
|
||||
declared, and either band one micrometre thinner than the minimum. All six recipe/mode runs pass.
|
||||
|
||||
Transition faces return to the nominal seam near passage mouths: the protected radius is the mouth
|
||||
radius plus 1 m, followed by a 2 m smooth fade. This kept the style-2 FI landing supported; its
|
||||
final capsule/support audit passes.
|
||||
|
||||
## Passage and parity audits
|
||||
|
||||
The final matrix has 4 styles × 2 recipes × 2 compilers (16 runs). Capsule and walkability,
|
||||
open-air, naturalness, no-long-bridge, route bindings, strict-validator controls, L13 route parity,
|
||||
and L9 proof audits pass. Open-air reports zero violations; validator controls reject 10/10;
|
||||
route-parity and proof audits report zero mismatches/violations. The L23/L24/L26 negative controls
|
||||
are detected.
|
||||
|
||||
The L28 `FloorProfileBlend` diagnostic still exceeds its 2-voxel threshold in styles 1 and 3:
|
||||
2.544 and 3.645 voxels. L28 records the same values on both compilers, so this is inherited and
|
||||
unchanged.
|
||||
|
||||
## Hashes
|
||||
|
||||
The L25 W1/W8/W12 hash matrix is identical across Clang and GCC for both recipes: full-detail,
|
||||
whole-view, all LOD rows and exact-query hashes match. Hash order below is density / mesh / payload /
|
||||
topology.
|
||||
|
||||
| Recipe | Full detail (960 tiles) | Whole view (2,370 tiles) | Exact-query density / detail / collision |
|
||||
|---|---|---|---|
|
||||
| SurfaceWorld | `ace56c8f205238ad / a9c603a1248de6b8 / ea55f1f4fcc6a894 / 5ea7bc78a1b678ae` | `0e8c5af01df96b14 / 70099e89b77811ed / dbce0fdcf6764126 / ddaf81a1228f9b4b` | `c949c4df2ce6f9d9 / 114c501e2d830d94 / e6011d3a248d4849` |
|
||||
| FloatingIslands | `ed550e0c8b182695 / 0e410a8e80a3e7f7 / 0751926f0c67d8a4 / f01208bc2f7841ec` | `a492eefa4f534975 / 141f00f3e411a0df / 93f50c569526c5cb / d83b53c8e53df81c` | `7b7c50ac24ccadcc / efcba20c0c70c415 / e604823a249029bf` |
|
||||
|
||||
The 24 m moving crossing processed 314 entering tiles per recipe; Clang/GCC crossing hashes match.
|
||||
SurfaceWorld's away-from-interface probe at z = −400 m also matches L28: all 960 full-detail
|
||||
tiles and the exact-query density/detail/collision hashes are identical. The broader whole-view
|
||||
hash differs because its far-LOD coverage also includes the changed interface.
|
||||
|
||||
## Cost
|
||||
|
||||
Five clean, alternating W12 pairs against
|
||||
`Saved/Linux/bin/VoxelForgeCoreHeadless-linux28-clang-r28`, hashing off:
|
||||
|
||||
| Recipe | L28 whole-view ready | L29 whole-view ready | Change |
|
||||
|---|---:|---:|---:|
|
||||
| SurfaceWorld | 1.581 s | 1.644 s | +3.97% |
|
||||
| FloatingIslands | 16.605 s | 16.814 s | +1.26% |
|
||||
|
||||
FloatingIslands is within the ~3% target. SurfaceWorld is about one percentage point above it.
|
||||
|
||||
## Captures
|
||||
|
||||
All are 1280×720 JPEGs, each under 21 KB (well below 300 KB):
|
||||
|
||||
- `captures/fi-floor-gradient-above.jpg` — FI floor from above, inside FI.
|
||||
- `captures/sw-ceiling-gradient-below.jpg` — SW ceiling from below, inside SW.
|
||||
- `captures/interface-hard-side-cutaway.jpg`
|
||||
- `captures/interface-gradient-side-cutaway.jpg`
|
||||
- `captures/interface-interleaved-side-cutaway.jpg`
|
||||
|
||||
## Changes and run history
|
||||
|
||||
Core transition evaluation/validation and the shared composition path changed, along with the
|
||||
headless transition audit, architecture/codemap, and the two Unreal files
|
||||
`Source/VoxelForge/Public/VoxelStrateTypes.h` and
|
||||
`Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp`. Unreal was unavailable here, so those edits
|
||||
were not compiled. No commit, push or stash was made.
|
||||
|
||||
Failed or retried runs:
|
||||
|
||||
- An early Clang build used a nonexistent dispatch helper; corrected to the current query method and rebuilt with Clang and GCC.
|
||||
- The first Interleaved overlap used the full sum of band thicknesses and opened gaps (1,085 columns / 20,313 air samples). Capping overlap to `min(declared width, combined thickness)` removed the gaps.
|
||||
- An initial hole sweep sampled a half-open band edge and reported 752 boundary-rounding holes. Sampling half a voxel inside each band removed those edge artifacts; final sweeps report zero holes.
|
||||
- The first FI style-2 passage audit had 11 unsupported probes after Gradient moved the mouth face. The local mouth taper fixed it; all 16 final passage runs pass.
|
||||
- A resolved-layout-only command initially omitted required pipeline flags and exited 2; rerun with the required flags passed. Early cutaway camera/clip settings produced blank framing; the final orientation and clip plane produced the captures above.
|
||||
- One cost folder contained a duplicated timing sequence and was discarded. The five-pair clean run above was sequential. A cache experiment did not improve cost and was removed. A cost-extraction `sed` typo was rerun with Python; benchmark logs were unaffected.
|
||||
|
||||
No build or audit exceeded 30 minutes, and no step remained stuck for 15 minutes. All builds and runs
|
||||
used CPUs 0–11, at most 12 workers, normal priority, and the 6 GiB address-space cap.
|
||||
|
||||
## Orchestrator review (Claude)
|
||||
|
||||
- Merged: transitions honoured safely (0 holes over 4,221 columns per interface, negative controls reject, all
|
||||
passage audits pass, deterministic).
|
||||
- **The look is not verified by the captures:** `interface-interleaved-side-cutaway.jpg` shows flat faces
|
||||
(black = uncapped solid between faces), with no visible fingers; `sw-ceiling-gradient-below.jpg` is a flat
|
||||
plane; `fi-floor-gradient-above.jpg` shows **concentric dashed rings** on the FI floor, which looks like an
|
||||
artifact (possibly the passage-mouth protected-radius fade, or quantised undulation), not a smooth Gradient.
|
||||
- Cumulative SW cost vs L24 is now ~+8% (L28 +3.7% plus L29 +4%). L30 covers the ring artifact, readable
|
||||
transition captures, and cost recovery.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -69,6 +69,9 @@ namespace
|
||||
bool AuditResolvedLayoutBoundaries=false;
|
||||
bool AuditResolvedLayoutPassages=false;
|
||||
bool AuditSurfaceClearance=false;
|
||||
bool AuditStrateTransitions=false;
|
||||
int TransitionModeOverride=-1;
|
||||
int TransitionWidthChunksOverride=-1;
|
||||
};
|
||||
|
||||
bool ParseInteger(std::string_view text,int& output)
|
||||
@@ -103,6 +106,38 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OverrideTransitions(VFJson::Value& root,int mode,int widthChunks,std::string& error)
|
||||
{
|
||||
auto* object=std::get_if<VFJson::Value::Object>(&root.Data);
|
||||
if(!object){error="transition fixture override requires an owner JSON object";return false;}
|
||||
const auto stratesIt=object->find("strates");
|
||||
if(stratesIt==object->end())
|
||||
{error="transition fixture override cannot find the resolved strate array";return false;}
|
||||
auto* strates=std::get_if<VFJson::Value::Array>(&stratesIt->second.Data);
|
||||
if(!strates||strates->empty())
|
||||
{error="transition fixture override found an empty or invalid strate array";return false;}
|
||||
for(auto& strate:*strates)
|
||||
{
|
||||
auto* strateObject=std::get_if<VFJson::Value::Object>(&strate.Data);
|
||||
if(!strateObject){error="transition fixture contains a malformed strate";return false;}
|
||||
const auto assetIt=strateObject->find("asset");
|
||||
if(assetIt==strateObject->end())continue;
|
||||
auto* asset=std::get_if<VFJson::Value::Object>(&assetIt->second.Data);
|
||||
if(!asset)continue;
|
||||
if(mode>=0)
|
||||
{
|
||||
VFJson::Value value;value.Data=static_cast<double>(mode);
|
||||
(*asset)["TransitionType"]=std::move(value);
|
||||
}
|
||||
if(widthChunks>=0)
|
||||
{
|
||||
VFJson::Value value;value.Data=static_cast<double>(widthChunks);
|
||||
(*asset)["TransitionBlendChunks"]=std::move(value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseOptions(int argc,char** argv,FOptions& options)
|
||||
{
|
||||
const std::filesystem::path executable=std::filesystem::absolute(argv[0]);
|
||||
@@ -126,6 +161,8 @@ namespace
|
||||
" --resolved-layout-output=FILE (canonical plan-time layout snapshot)\n"
|
||||
" --resolved-layout-only (exit after plan and snapshot validation)\n"
|
||||
" --audit-resolved-layout-boundaries (query Core at each recorded endpoint)\n"
|
||||
" --audit-strate-transitions (measure interface crossing, band thickness and holes)\n"
|
||||
" --transition-mode=hard|gradient|interleaved --transition-width-chunks=N (fixture override)\n"
|
||||
" --audit-resolved-layout-passages (capsule, support, route-binding and blocked-control audit)\n"
|
||||
" --audit-surface-clearance (sample biome terrain/caps against the resolved ceiling)\n"
|
||||
" --auto-surface-camera (place the 27-tile cube on this seed's surface)\n"
|
||||
@@ -149,6 +186,21 @@ namespace
|
||||
else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true;
|
||||
else if(arg=="--audit-resolved-layout-passages")options.AuditResolvedLayoutPassages=true;
|
||||
else if(arg=="--audit-surface-clearance")options.AuditSurfaceClearance=true;
|
||||
else if(arg=="--audit-strate-transitions")options.AuditStrateTransitions=true;
|
||||
else if(arg.starts_with("--transition-mode="))
|
||||
{
|
||||
const std::string_view mode=arg.substr(std::string_view("--transition-mode=").size());
|
||||
if(mode=="gradient")options.TransitionModeOverride=0;
|
||||
else if(mode=="hard")options.TransitionModeOverride=1;
|
||||
else if(mode=="interleaved")options.TransitionModeOverride=2;
|
||||
else {std::fprintf(stderr,"Invalid transition mode: %.*s\n",static_cast<int>(mode.size()),mode.data());return false;}
|
||||
}
|
||||
else if(arg.starts_with("--transition-width-chunks="))
|
||||
{
|
||||
if(!readInt("--transition-width-chunks=",options.TransitionWidthChunksOverride)
|
||||
||options.TransitionWidthChunksOverride<0||options.TransitionWidthChunksOverride>64)
|
||||
{std::fprintf(stderr,"transition width chunks must be in 0..64\n");return false;}
|
||||
}
|
||||
else if(arg=="--stream-benchmark")options.StreamBenchmark=true;
|
||||
else if(arg=="--stream-pipeline")options.StreamPipeline=true;
|
||||
else if(arg=="--stream-hash")options.StreamHash=true;
|
||||
@@ -303,6 +355,10 @@ int main(int argc,char** argv)
|
||||
if(options.FloatingIslandsRecipe
|
||||
&&!StandaloneWorldAssemblyPrototype::SelectTopStrateAsFloatingIslands(ownerJsonRoot,error))
|
||||
{std::fprintf(stderr,"Floating-island recipe selection failed: %s\n",error.c_str());return 2;}
|
||||
if((options.TransitionModeOverride>=0||options.TransitionWidthChunksOverride>=0)
|
||||
&&!OverrideTransitions(ownerJsonRoot,options.TransitionModeOverride,
|
||||
options.TransitionWidthChunksOverride,error))
|
||||
{std::fprintf(stderr,"Transition fixture override failed: %s\n",error.c_str());return 2;}
|
||||
if(!StandaloneOwnerLoader::Resolve(ownerJsonRoot,*ownerWorld,error))
|
||||
{std::fprintf(stderr,"Owner data load failed: %s\n",error.c_str());return 2;}
|
||||
std::printf("[ColdTimeline] stage=owner_data_load end_ms=%.3f\n",
|
||||
@@ -551,6 +607,6 @@ int main(int argc,char** argv)
|
||||
options.RegionCrossing,options.AuditTileShapeReadiness,
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence,
|
||||
options.AuditRouteParity,options.AuditResolvedLayoutBoundaries,
|
||||
options.AuditResolvedLayoutPassages,
|
||||
options.AuditResolvedLayoutPassages,options.AuditStrateTransitions,
|
||||
&resolvedLayout);
|
||||
}
|
||||
@@ -240,6 +240,172 @@ namespace StandaloneStreamBenchmark
|
||||
return Hash;
|
||||
}
|
||||
|
||||
inline bool AuditResolvedStrateTransitions(const VoxelForgeCore::World& Field,
|
||||
const FStandaloneOwnerWorld& Owner,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& Layout)
|
||||
{
|
||||
constexpr std::int64_t XYExtentMicrometres=256000000;
|
||||
constexpr std::int64_t XYStepMicrometres=8000000;
|
||||
const std::int64_t coreMicrometres=std::max<std::uint32_t>(1,Owner.CoreVoxelSizeMicrons);
|
||||
const std::int64_t zStepMicrometres=std::max<std::int64_t>(1,coreMicrometres/2);
|
||||
const std::int64_t minimumBandMicrometres=2*coreMicrometres;
|
||||
std::uint64_t interfacesPassed=0,totalColumns=0,passageColumnsSkipped=0;
|
||||
std::uint64_t holeColumns=0,airSamples=0,solidSamples=0;
|
||||
bool negativeWidthRejected=false,negativeThinBandRejected=false;
|
||||
|
||||
for(std::size_t interfaceIndex=0;interfaceIndex+1<Layout.strates.size();++interfaceIndex)
|
||||
{
|
||||
const auto& upper=Layout.strates[interfaceIndex];
|
||||
const auto& lower=Layout.strates[interfaceIndex+1];
|
||||
std::int64_t minimumOffset=std::numeric_limits<std::int64_t>::max();
|
||||
std::int64_t maximumOffset=std::numeric_limits<std::int64_t>::min();
|
||||
std::int64_t maximumCrossingDepth=0;
|
||||
std::int64_t minimumUpperThickness=std::numeric_limits<std::int64_t>::max();
|
||||
std::int64_t minimumLowerThickness=std::numeric_limits<std::int64_t>::max();
|
||||
std::uint64_t columns=0,skipped=0,holes=0,air=0,solid=0;
|
||||
bool firstHoleReported=false;
|
||||
const std::int64_t seam=upper.bedrock.bottomMicrometres;
|
||||
const auto passageCoversColumn=[&](std::int64_t xUm,std::int64_t yUm,
|
||||
std::int64_t zMinimumUm,std::int64_t zMaximumUm)
|
||||
{
|
||||
constexpr double Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
for(const auto& passage:Layout.passages)
|
||||
{
|
||||
if(passage.upperStrateIndex!=upper.strateIndex
|
||||
||passage.lowerStrateIndex!=lower.strateIndex)continue;
|
||||
for(std::size_t pointIndex=1;pointIndex<passage.controlPoints.size();++pointIndex)
|
||||
{
|
||||
const auto& a=passage.controlPoints[pointIndex-1];
|
||||
const auto& b=passage.controlPoints[pointIndex];
|
||||
const double ax=a.positionSubvoxels[0]*coreMicrometres/Q;
|
||||
const double ay=a.positionSubvoxels[1]*coreMicrometres/Q;
|
||||
const double az=a.positionSubvoxels[2]*coreMicrometres/Q;
|
||||
const double bx=b.positionSubvoxels[0]*coreMicrometres/Q;
|
||||
const double by=b.positionSubvoxels[1]*coreMicrometres/Q;
|
||||
const double bz=b.positionSubvoxels[2]*coreMicrometres/Q;
|
||||
const double radius=std::max(a.radiusSubvoxels,b.radiusSubvoxels)
|
||||
*coreMicrometres/Q+750000.0;
|
||||
if(std::max(az,bz)+radius<zMinimumUm
|
||||
||std::min(az,bz)-radius>zMaximumUm)continue;
|
||||
const double dx=bx-ax,dy=by-ay;
|
||||
const double lengthSquared=dx*dx+dy*dy;
|
||||
const double t=lengthSquared>0.0
|
||||
?std::clamp(((xUm-ax)*dx+(yUm-ay)*dy)/lengthSquared,0.0,1.0):0.0;
|
||||
const double ex=xUm-(ax+t*dx),ey=yUm-(ay+t*dy);
|
||||
if(ex*ex+ey*ey<=radius*radius)return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
for(std::int64_t iy=-XYExtentMicrometres/XYStepMicrometres;
|
||||
iy<=XYExtentMicrometres/XYStepMicrometres;++iy)
|
||||
for(std::int64_t ix=-XYExtentMicrometres/XYStepMicrometres;
|
||||
ix<=XYExtentMicrometres/XYStepMicrometres;++ix)
|
||||
{
|
||||
const std::int64_t xUm=ix*XYStepMicrometres;
|
||||
const std::int64_t yUm=iy*XYStepMicrometres;
|
||||
const auto bands=VoxelForgeCore::EvaluateResolvedLayoutTransitionBandsV1(
|
||||
upper,lower,Owner.Seed,Layout.strateLayoutChunkSizeMicrometres,xUm,yUm);
|
||||
const std::int64_t offset=bands.upperBedrockBottomMicrometres-seam;
|
||||
minimumOffset=std::min(minimumOffset,offset);
|
||||
maximumOffset=std::max(maximumOffset,offset);
|
||||
maximumCrossingDepth=std::max(maximumCrossingDepth,
|
||||
bands.crossingDepthMicrometres);
|
||||
minimumUpperThickness=std::min(minimumUpperThickness,
|
||||
bands.upperBedrockTopMicrometres-bands.upperBedrockBottomMicrometres);
|
||||
minimumLowerThickness=std::min(minimumLowerThickness,
|
||||
bands.lowerCeilingTopMicrometres-bands.lowerCeilingBottomMicrometres);
|
||||
const std::int64_t zMinimum=std::min(bands.upperBedrockBottomMicrometres,
|
||||
bands.lowerCeilingBottomMicrometres);
|
||||
const std::int64_t zMaximum=std::max(bands.upperBedrockTopMicrometres,
|
||||
bands.lowerCeilingTopMicrometres);
|
||||
if(passageCoversColumn(xUm,yUm,zMinimum,zMaximum)){++skipped;continue;}
|
||||
|
||||
++columns;
|
||||
bool hasHole=false;
|
||||
for(std::int64_t zUm=zMinimum+zStepMicrometres/2;
|
||||
zUm<zMaximum-zStepMicrometres/2;zUm+=zStepMicrometres)
|
||||
{
|
||||
const VoxelForgeCore::Vec3d point{
|
||||
static_cast<double>(xUm)/coreMicrometres,
|
||||
static_cast<double>(yUm)/coreMicrometres,
|
||||
static_cast<double>(zUm)/coreMicrometres};
|
||||
const float density=Field.densityAt(point);
|
||||
if(!std::isfinite(density)||density>=0.0f)
|
||||
{
|
||||
if(!firstHoleReported)
|
||||
{
|
||||
std::printf("[StrateTransitionFirstHole] x_um=%lld y_um=%lld z_um=%lld z_min_um=%lld z_max_um=%lld density=%.9g dispatch=%s\n",
|
||||
static_cast<long long>(xUm),static_cast<long long>(yUm),
|
||||
static_cast<long long>(zUm),static_cast<long long>(zMinimum),
|
||||
static_cast<long long>(zMaximum),density,
|
||||
Field.densityDispatchPathAt(point).c_str());
|
||||
firstHoleReported=true;
|
||||
}
|
||||
hasHole=true;++air;
|
||||
}
|
||||
else ++solid;
|
||||
}
|
||||
if(hasHole)++holes;
|
||||
}
|
||||
|
||||
const std::int64_t boundaryPeakToPeak=maximumOffset-minimumOffset;
|
||||
const std::int64_t allowedWidth=upper.transitionMode=="Hard"
|
||||
||upper.transitionFallsBackToHard?0:upper.transitionWidthMicrometres;
|
||||
if(interfaceIndex==0)
|
||||
{
|
||||
auto wide=Layout;
|
||||
wide.strates[0].transitionMode="Interleaved";
|
||||
wide.strates[0].transitionFallsBackToHard=false;
|
||||
wide.strates[0].transitionWidthMicrometres=8000000;
|
||||
wide.strates[0].transitionInterpenetrationMicrometres=8000001;
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> issues;
|
||||
std::string error;
|
||||
negativeWidthRejected=!wide.validate(
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV3::strict,issues,error);
|
||||
|
||||
auto thin=Layout;
|
||||
auto& bedrock=thin.strates[0].bedrock;
|
||||
bedrock.topMicrometres=bedrock.bottomMicrometres+minimumBandMicrometres-1;
|
||||
thin.strates[0].content.bottomMicrometres=bedrock.topMicrometres;
|
||||
negativeThinBandRejected=!thin.validate(
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV3::strict,issues,error);
|
||||
}
|
||||
const bool passed=columns>0&&holes==0&&maximumCrossingDepth<=allowedWidth
|
||||
&&minimumUpperThickness>=minimumBandMicrometres
|
||||
&&minimumLowerThickness>=minimumBandMicrometres;
|
||||
interfacesPassed+=passed?1u:0u;
|
||||
totalColumns+=columns;passageColumnsSkipped+=skipped;
|
||||
holeColumns+=holes;airSamples+=air;solidSamples+=solid;
|
||||
std::printf("[StrateTransitionInterface] upper=%d lower=%d mode=%s width_um=%lld max_crossing_depth_um=%lld boundary_peak_to_peak_um=%lld allowed_um=%lld upper_min_band_um=%lld lower_min_band_um=%lld columns=%llu passage_columns=%llu hole_columns=%llu air_samples=%llu status=%s\n",
|
||||
upper.strateIndex,lower.strateIndex,upper.transitionMode.c_str(),
|
||||
static_cast<long long>(upper.transitionWidthMicrometres),
|
||||
static_cast<long long>(maximumCrossingDepth),
|
||||
static_cast<long long>(boundaryPeakToPeak),static_cast<long long>(allowedWidth),
|
||||
static_cast<long long>(minimumUpperThickness),static_cast<long long>(minimumLowerThickness),
|
||||
static_cast<unsigned long long>(columns),static_cast<unsigned long long>(skipped),
|
||||
static_cast<unsigned long long>(holes),static_cast<unsigned long long>(air),
|
||||
passed?"PASS":"FAIL");
|
||||
}
|
||||
const bool controlsPassed=negativeWidthRejected&&negativeThinBandRejected;
|
||||
std::printf("[StrateTransitionNegativeControls] wider_than_declared=%s thinner_than_minimum=%s minimum_band_um=%lld status=%s\n",
|
||||
negativeWidthRejected?"REJECTED":"ACCEPTED",
|
||||
negativeThinBandRejected?"REJECTED":"ACCEPTED",
|
||||
static_cast<long long>(minimumBandMicrometres),controlsPassed?"PASS":"FAIL");
|
||||
const bool passed=!Layout.strates.empty()&&interfacesPassed+1==Layout.strates.size()
|
||||
&&holeColumns==0&&airSamples==0&&controlsPassed;
|
||||
std::printf("[StrateTransitionAuditSummary] interfaces=%llu passed=%llu columns=%llu skipped_passage_columns=%llu hole_columns=%llu air_samples=%llu solid_samples=%llu status=%s\n",
|
||||
static_cast<unsigned long long>(Layout.strates.size()-1),
|
||||
static_cast<unsigned long long>(interfacesPassed),
|
||||
static_cast<unsigned long long>(totalColumns),
|
||||
static_cast<unsigned long long>(passageColumnsSkipped),
|
||||
static_cast<unsigned long long>(holeColumns),
|
||||
static_cast<unsigned long long>(airSamples),
|
||||
static_cast<unsigned long long>(solidSamples),passed?"PASS":"FAIL");
|
||||
return passed;
|
||||
}
|
||||
|
||||
inline int RunPipelinedStreamingBenchmark(std::shared_ptr<FStandaloneOwnerWorld> OwnerWorld,
|
||||
const VoxelForgeCore::ResolvedWorldSnapshot& CoreSnapshot,
|
||||
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
|
||||
@@ -256,6 +422,7 @@ namespace StandaloneStreamBenchmark
|
||||
bool bAuditBasePageEquivalence=false,bool bAuditRouteParity=false,
|
||||
bool bAuditResolvedLayoutBoundaries=false,
|
||||
bool bAuditResolvedLayoutPassages=false,
|
||||
bool bAuditStrateTransitions=false,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3* ResolvedLayout=nullptr)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
@@ -441,6 +608,14 @@ namespace StandaloneStreamBenchmark
|
||||
Streamer.Stop();
|
||||
std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;
|
||||
}
|
||||
if(bAuditStrateTransitions)
|
||||
{
|
||||
if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout())
|
||||
{Streamer.Stop();std::fprintf(stderr,"[StrateTransitionAudit] missing resolved-layout snapshot\n");return 2;}
|
||||
const bool passed=AuditResolvedStrateTransitions(Shape,World,*ResolvedLayout);
|
||||
Streamer.Stop();
|
||||
return passed?0:11;
|
||||
}
|
||||
if(bAuditResolvedLayoutPassages)
|
||||
{
|
||||
if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout())
|
||||
|
||||
@@ -801,13 +801,21 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
s.transitionBlendChunks=slot.Definition->TransitionBlendChunks;
|
||||
s.transitionWidthMicrometres=static_cast<std::int64_t>(s.transitionBlendChunks)*owner.StrateLayoutChunkSizeMicrometres;
|
||||
s.transitionWarpAmplitudeMicrometres=s.transitionMode=="Interleaved"
|
||||
?2*static_cast<std::int64_t>(owner.StrateLayoutChunkSizeMicrometres):0;
|
||||
s.transitionInterpenetrationMicrometres=0;
|
||||
s.transitionHonouredInCore=s.transitionMode=="Hard";
|
||||
s.transitionFallsBackToHard=s.transitionMode!="Hard";
|
||||
?std::min(s.transitionWidthMicrometres/2,
|
||||
static_cast<std::int64_t>(owner.StrateLayoutChunkSizeMicrometres)):0;
|
||||
s.transitionInterpenetrationMicrometres=s.transitionMode=="Interleaved"
|
||||
?s.transitionWidthMicrometres
|
||||
:s.transitionMode=="Gradient"?s.transitionWidthMicrometres/4:0;
|
||||
s.transitionFallsBackToHard=s.transitionMode!="Hard"
|
||||
&&s.transitionWidthMicrometres<=0;
|
||||
s.transitionHonouredInCore=s.transitionMode=="Hard"||!s.transitionFallsBackToHard;
|
||||
s.transitionDispatchPath=s.transitionFallsBackToHard
|
||||
?"declared Gradient/Interleaved recorded; Core uses explicit Hard fallback with zero interpenetration"
|
||||
:"declared Hard transition applied at the strate interface";
|
||||
?"declared zero-width transition uses explicit Hard fallback"
|
||||
:s.transitionMode=="Hard"
|
||||
?"declared Hard transition preserves the flat shared bedrock/ceiling interface"
|
||||
:s.transitionMode=="Interleaved"
|
||||
?"shared composition overlaps both facing bands with deterministic domain-warped fingers inside the declared width"
|
||||
:"shared composition moves both band faces together with the smooth declared-width transition field";
|
||||
s.managerLookup="UVoxelStrateManager selects the slot from the director-resolved layout chunk width";
|
||||
s.managerChunkWidthMicrometres=owner.StrateLayoutChunkSizeMicrometres;
|
||||
s.dispatchChunkWidthMicrometres=owner.StrateLayoutChunkSizeMicrometres;
|
||||
|
||||
Reference in new issue
Block a user