Compare commits

..
Author SHA1 Message Date
Fr0zkaandClaude Opus 5.5 4a97023b24 wip: continuous backbone floor profile for the cave planner (L134 design 4, untested)
Written by Luna before the Codex usage limit cut the round: one shared distance/floor profile across backbone rooms
and edges, edge cubic floors derived from it, sloped route-room floors, lower-floor backing cap at overlaps, and
validator checks for joint height/slope continuity. Not yet built or audited.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 12:03:48 +01:00
Fr0zkaandClaude Opus 5.5 379093401e wip: cave planner route support fixed (L134 design 3); clearance at edge joints open
Arrival edge floor aligned to the passage mouth, 0.75 m backbone floor-support budget, anchors on the planned floor:
0 unsupported, 0 grade failures. Open: 196/50,967 capsule samples blocked where a higher descending edge's floor
backing meets the next edge (route-edge-6/7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 11:58:33 +01:00
Fr0zkaandClaude Opus 5.5 75c6ad251d wip: Tunnel Network cave planner on Core caves (L134, route support still failing)
Seed + 21/47 legacy fields -> 30 compound rooms (72 primitives), 3 branches, destination chamber; mouths fixed;
8.67 deg max grade. Passage composer no longer skips native cave layers (incoming passage 21,425/21,425 open).
Open: route-support audit has 380 unsupported samples near the arrival mouth. Not for merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 11:42:04 +01:00
Fr0zkaandClaude Opus 5.5 0065ee68e6 tools: per-strate density dispatch report in the fast tier (L132)
--density-dispatch-report prints each strate's bound path and tile shares. Owner world: SW native heightfield;
FI, TN, VS on compiled-analytic legacy fallback (FI lacks an imported FloatingIslandPlan; TN/VS have legacy recipes).
A native FI candidate broke the composed owner walk/shell gates, so the owner stays on its path. Output unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 02:40:58 +01:00
Fr0zkaandClaude Opus 5.5 ebae71b142 research: R46 generator capability matrix (read-only)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 02:11:29 +01:00
Fr0zkaandClaude Opus 5.5 81071969fc docs+cleanup: CURRENT-STATE.md and dead L86/L88 ablation switches removed (L131)
One current-state page (shipping behaviour, references with sources, gaps, verification, closed lines), linked from
CLAUDE.md. Removes the L86/L88 passage/boundary ablation switches and the unused legacy route-grade audit; inventory
of every kept switch in the report. Output identical: 24/24 + 18/18 hashes, fast tier, walk/seam audits, UE host
build; timing unchanged (-0.2%, CI spans 0). No Source/ changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 02:11:10 +01:00
Fr0zkaandClaude Opus 5.5 cbbfd46bd3 research: Astra low open project review — what to focus on now (read-only)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 01:19:41 +01:00
Fr0zkaandClaude Opus 5.5 044cd6d9c5 core: one passage influence reach in metres, converted once (L130, evaluator v227)
Reach = 0.75 shell + 0.21 relief + 0.50/6 smooth-max support + 0.01 = 1.0533 m, used by proof, feature, fallback,
off-grid bounds and the sweep cutoff. Removes the dead 4 m term (v225 added metres to core-voxel coordinates).
Captures and mouth close-ups byte-identical; proof audit 0 misses over 25,728 shell/crest samples; floor 7.884 mm;
seams, walkability, fast tier, Clang/GCC W1/W8/W12 green; timing equal within noise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-31 00:43:44 +01:00
Fr0zkaandClaude Opus 5.5 b96dd739b8 research: Astra low review of per-block bit-flag masks (read-only)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-30 21:30:51 +01:00
97 changed files with 3390 additions and 2800 deletions

No files matched your search

+8 -7
View File
@@ -1522,11 +1522,12 @@ collision ring became walkable at 0.632 s. `voxel.StreamingCoarseFirst=1` with
residency by about one third on the staged plan, but null-RHI residency does not prove rendered residency by about one third on the staged plan, but null-RHI residency does not prove rendered
enclosure. Keep coarse-first opt-in until that view is checked in a rendered owner-plan run. enclosure. Keep coarse-first opt-in until that view is checked in a rendered owner-plan run.
The separate `voxel.StreamingStartupApplyBoost` switch stays default-off. At `1`, it temporarily allows The separate `voxel.StreamingStartupApplyBoost` switch is default-on in the current code. At `1`, it
12 visible mesh submissions / 14 ms while the initial desired set and walkable ring are incomplete, temporarily allows up to 24 visible mesh submissions / 14 ms while the initial desired set and
with a 30 second ceiling; at `0`, the authored budget is unchanged. On the matched legacy-order test, walkable ring are incomplete, with a 30 second ceiling; at `0`, the authored budget is unchanged.
the boost changed full-set readiness from 17.412 s to 17.346 s and removed count-cap frames, but did The historical matched S6 run changed full-set readiness from 17.412 s to 17.346 s and removed
not improve end-to-end residency. Keep the normal 4 / 2 ms guard for play. count-cap frames, but did not improve end-to-end residency. These current defaults are defined in
`Source/VoxelForge/Private/VoxelWorld.cpp`; the S6 measurement remains historical context.
The S6 trace found the shared pool was not saturated: its 10-slot idle estimate was about 132 The S6 trace found the shared pool was not saturated: its 10-slot idle estimate was about 132
slot-seconds on legacy order and 73 on gated coarse-first, while game-thread tile applies totaled only slot-seconds on legacy order and 73 on gated coarse-first, while game-thread tile applies totaled only
@@ -1556,8 +1557,8 @@ by its RAII guard when the worker lambda exits. This still counts queued and run
results do not consume an admission slot. `voxel.StreamingLegacyPendingResultCap=1` restores the old results do not consume an admission slot. `voxel.StreamingLegacyPendingResultCap=1` restores the old
`PendingTiles` accounting; the test harness's `NearReserveMode=Legacy` also sets `PendingTiles` accounting; the test harness's `NearReserveMode=Legacy` also sets
`voxel.StreamingDynamicNearReserve=0`, reproducing the S6 path. `StreamingDynamicNearReserve` now `voxel.StreamingDynamicNearReserve=0`, reproducing the S6 path. `StreamingDynamicNearReserve` now
defaults to `1`; `StreamingCoarseFirst` remains `0`, and the apply boost remains `0` in the matched defaults to `1`; `StreamingCoarseFirst` remains `0`. That historical S6 matched measurement disabled
measurement. the startup apply boost (`voxel.StreamingStartupApplyBoost=0`); the current default is on (§8.27).
On the 2,578-key owner SurfaceWorld trace, the old-path control reached the full set in 17.527 s, On the 2,578-key owner SurfaceWorld trace, the old-path control reached the full set in 17.527 s,
walkable ring in 0.650 s, and full LOD0 in 2.281 s. Dynamic admission reached them in 7.780 s, walkable ring in 0.650 s, and full LOD0 in 2.281 s. Dynamic admission reached them in 7.780 s,
+1
View File
@@ -10,6 +10,7 @@ then wait for his results / pasted errors. Same for in-editor checks — ask for
try to run anything. try to run anything.
## Read first (in order, load only what the task needs) ## Read first (in order, load only what the task needs)
- **[CURRENT-STATE.md](CURRENT-STATE.md) — current shipping snapshot and verification entry point.**
- **[CODEMAP.md](CODEMAP.md) — ALWAYS.** Navigation: what it is, data-flow (§2), symbol→`file:line` - **[CODEMAP.md](CODEMAP.md) — ALWAYS.** Navigation: what it is, data-flow (§2), symbol→`file:line`
index (§3), "I want to change X → go here" (§5), conventions & gotchas (§6). Trust **symbol names index (§3), "I want to change X → go here" (§5), conventions & gotchas (§6). Trust **symbol names
over line numbers** (lines drift). over line numbers** (lines drift).
+2 -2
View File
@@ -388,7 +388,7 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error**
| `AdvanceHeadlessDigMotionTest` / `RecordHeadlessDigMotionFrame` | 3060 / 3217 | Explicit `-voxel.TestDigMotion=1` repro: optional near digs, then repeats underfoot digs and samples `Generator->GetDensityAt` immediately after each edit, on its first subsequent simulated frame, and on every following observation frame. `-voxel.TestDigMotionFieldOverride=1` is a measurement-only prototype that clears `CurrentFloor` and switches CMC to falling when the sampled post-edit density is air; it is inactive by default. The test counts false support, false air and downward velocity beyond one gravity step. `-voxel.TestDigMotionUnderfootDigCount` is clamped to 1–100; `-voxel.TestDigMotionUnderfootIntervalSeconds=0` performs per-frame rapid edits. The accompanying gap audit is scoped to the dig session. | | `AdvanceHeadlessDigMotionTest` / `RecordHeadlessDigMotionFrame` | 3060 / 3217 | Explicit `-voxel.TestDigMotion=1` repro: optional near digs, then repeats underfoot digs and samples `Generator->GetDensityAt` immediately after each edit, on its first subsequent simulated frame, and on every following observation frame. `-voxel.TestDigMotionFieldOverride=1` is a measurement-only prototype that clears `CurrentFloor` and switches CMC to falling when the sampled post-edit density is air; it is inactive by default. The test counts false support, false air and downward velocity beyond one gravity step. `-voxel.TestDigMotionUnderfootDigCount` is clamped to 1–100; `-voxel.TestDigMotionUnderfootIntervalSeconds=0` performs per-frame rapid edits. The accompanying gap audit is scoped to the dig session. |
| `MaybeFinishHeadlessStreamingTest` | — | The existing `voxel.TestExitSeconds` diagnostic emits `VoxelForgeStreamingExitDiag` at most every 30 elapsed seconds, including threshold and early-return test flags; no output when the timer is unconfigured. | | `MaybeFinishHeadlessStreamingTest` | — | The existing `voxel.TestExitSeconds` diagnostic emits `VoxelForgeStreamingExitDiag` at most every 30 elapsed seconds, including threshold and early-return test flags; no output when the timer is unconfigured. |
| `UpdateVisualStalenessAudit` | 5034 | Test-only camera-frustum coverage audit: tracks up to 24 real pre-skirt isosurface points per visible component, then counts frames where no visible tile component covers a previously visible, still-desired point (excluding edited bounds). `voxel.TestVisualGapInjectEarlyHide=1` hides a valid component for three frames and must be caught. `voxel.TestGapAuditLegacyHandoff=1` restores old handoff culling for A/B baselines. | | `UpdateVisualStalenessAudit` | 5034 | Test-only camera-frustum coverage audit: tracks up to 24 real pre-skirt isosurface points per visible component, then counts frames where no visible tile component covers a previously visible, still-desired point (excluding edited bounds). `voxel.TestVisualGapInjectEarlyHide=1` hides a valid component for three frames and must be caught. `voxel.TestGapAuditLegacyHandoff=1` restores old handoff culling for A/B baselines. |
| `ProcessPendingChunks` | 7435 | Drains `CriticalProcessQueue`, `EditedProcessQueue`, `NearProcessQueue`, then `ProcessQueue`; applies under the authored 4-mesh/2 ms budget during normal play and the bounded shared startup budget (12 meshes/14 ms) until the initial desired set and walkable ring are ready. `voxel.StreamingStartupApplyBoost=0` restores the authored budget. `startup_apply_config` traces the actor-read CVar and both budgets; `startup_apply_budget` records activation/end. Reads per-request cancellation before removing its map entry, traces stale/obsolete/aborted discards with epoch and ordinal, then applies via `ApplyTileResult`. Step 1 prequeue still releases here and emits `VFStep1UploadCorpusReleased`. | | `ProcessPendingChunks` | 7435 | Drains `CriticalProcessQueue`, `EditedProcessQueue`, `NearProcessQueue`, then `ProcessQueue`; applies under the authored 4-mesh/2 ms budget during normal play and the default-on bounded startup budget (24 meshes/14 ms, at most 30 s) until the initial desired set and walkable ring are ready. `voxel.StreamingStartupApplyBoost=0` restores the authored budget. `startup_apply_config` traces the actor-read CVar and both budgets; `startup_apply_budget` records activation/end. Reads per-request cancellation before removing its map entry, traces stale/obsolete/aborted discards with epoch and ordinal, then applies via `ApplyTileResult`. Step 1 prequeue still releases here and emits `VFStep1UploadCorpusReleased`. |
| `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). | | `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). |
| `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic raw stack sampler, off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It writes bounded raw PCs to CSV plus a symbolization-free summary under `Saved/`; symbol resolution is deferred until after process exit so the measured process never initializes/calls DbgHelp for this diagnostic. Lifecycle teardown is serialized and registration TLS is session-tagged. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=<us>`, commandlet `-samplestacks[=us]`. | | `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic raw stack sampler, off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It writes bounded raw PCs to CSV plus a symbolization-free summary under `Saved/`; symbol resolution is deferred until after process exit so the measured process never initializes/calls DbgHelp for this diagnostic. Lifecycle teardown is serialized and registration TLS is session-tagged. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=<us>`, commandlet `-samplestacks[=us]`. |
| Room-cache performance (2026-09-13): `VF_FindPlayerFitPointForRoomMemoized` / `GPlayerFitMemo` (`VoxelCaveMorphology.cpp`), `voxel.TileCacheWindow` + `VoxelGenLOD::TileOriginVoxels/TileCellsPerAxis` (`VoxelGenerator.h`), `FChunkSDFSpatialIndex` | — | **Player-fit memo:** exact 97-word key, process-wide, `voxel.PlayerFitMemo`. **Tile cache window:** the room cache covers the requesting tile's footprint plus a fixed collect margin. It is set via `TGuardValue` in `GenerateMesh`/classify, and is window-invariant by construction (the margin does not grow with the tile). **LOD-gated:** fused path at LOD ≥ `voxel.TileCacheWindowMinLOD` (3), op-stack at LOD ≥ `voxel.TileCacheWindowOpStackMinLOD` (4); lower LODs keep the 4-chunk worker window, because the tile window slowed LOD0 evaluation ~6%. Strates whose authored terrain ops exceed the proven 256-voxel extent (or are malformed/unloaded) fall back to the legacy window (`VF_ComputeTerrainOpEnvelope`). `voxel.SpatialIndex` is -1 auto (off at fused LOD0, on elsewhere), 0 or 1. **Spatial index:** ordered XY buckets over cache primitives, exact rejection. The `static_assert` in `VoxelStrateTypes.h` ties `VF_STRATE_PARAM_FIELDS` to `FStrateGenerationParams` (83 fields + 8 B padding). | | Room-cache performance (2026-09-13): `VF_FindPlayerFitPointForRoomMemoized` / `GPlayerFitMemo` (`VoxelCaveMorphology.cpp`), `voxel.TileCacheWindow` + `VoxelGenLOD::TileOriginVoxels/TileCellsPerAxis` (`VoxelGenerator.h`), `FChunkSDFSpatialIndex` | — | **Player-fit memo:** exact 97-word key, process-wide, `voxel.PlayerFitMemo`. **Tile cache window:** the room cache covers the requesting tile's footprint plus a fixed collect margin. It is set via `TGuardValue` in `GenerateMesh`/classify, and is window-invariant by construction (the margin does not grow with the tile). **LOD-gated:** fused path at LOD ≥ `voxel.TileCacheWindowMinLOD` (3), op-stack at LOD ≥ `voxel.TileCacheWindowOpStackMinLOD` (4); lower LODs keep the 4-chunk worker window, because the tile window slowed LOD0 evaluation ~6%. Strates whose authored terrain ops exceed the proven 256-voxel extent (or are malformed/unloaded) fall back to the legacy window (`VF_ComputeTerrainOpEnvelope`). `voxel.SpatialIndex` is -1 auto (off at fused LOD0, on elsewhere), 0 or 1. **Spatial index:** ordered XY buckets over cache primitives, exact rejection. The `static_assert` in `VoxelStrateTypes.h` ties `VF_STRATE_PARAM_FIELDS` to `FStrateGenerationParams` (83 fields + 8 B padding). |
@@ -396,7 +396,7 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error**
| `RecordTileHash` / `WriteTileHashDump` | — | Headless `-voxel.TileHashDump=<absolute path>` parity instrument. Records the exact `FVoxelMeshData` used by the streaming result, including empty/classified tiles, then writes sorted absolute tile keys, LOD step/cells, content-band limits, settings digest context, and the shared `VoxelGeometryHash` at `EndPlay`. | | `RecordTileHash` / `WriteTileHashDump` | — | Headless `-voxel.TileHashDump=<absolute path>` parity instrument. Records the exact `FVoxelMeshData` used by the streaming result, including empty/classified tiles, then writes sorted absolute tile keys, LOD step/cells, content-band limits, settings digest context, and the shared `VoxelGeometryHash` at `EndPlay`. |
| `SyncRemeshTile` | — | **INSTANT DIG**: level-0 same-frame re-mesh on the game thread. Uses `PreEditDensityCache` when the tile is resident in the bounded local cache, replays the current DiffLayer over that exact procedural lattice, then meshes/applies inline so a carve is visible THIS frame. A cache miss uses normal generation; it captures the unedited lattice if the tile needs meshing, while uniform-tile proofs remain enabled. | | `SyncRemeshTile` | — | **INSTANT DIG**: level-0 same-frame re-mesh on the game thread. Uses `PreEditDensityCache` when the tile is resident in the bounded local cache, replays the current DiffLayer over that exact procedural lattice, then meshes/applies inline so a carve is visible THIS frame. A cache miss uses normal generation; it captures the unedited lattice if the tile needs meshing, while uniform-tile proofs remain enabled. |
| `PreEditDensityCache` / `VoxelForgePreEditDensityCache::BuildEditedGrid` | — | Per-world cache of the pawn's current L0 tile: exact 35³ pre-edit samples, max **171,500 bytes**. It is pruned when the pawn crosses chunk boundaries and cleared on `RegenerateAllChunks`. `BuildEditedGrid` snapshots touched diff chunks once and reproduces `GetDensityAt`'s final `pre-edit - EvaluateMods` operation before the per-call shared-grid mesher path. | | `PreEditDensityCache` / `VoxelForgePreEditDensityCache::BuildEditedGrid` | — | Per-world cache of the pawn's current L0 tile: exact 35³ pre-edit samples, max **171,500 bytes**. It is pruned when the pawn crosses chunk boundaries and cleared on `RegenerateAllChunks`. `BuildEditedGrid` snapshots touched diff chunks once and reproduces `GetDensityAt`'s final `pre-edit - EvaluateMods` operation before the per-call shared-grid mesher path. |
| `UpdateChunksAroundPosition` / `GetGenerationAdmissionCount` | 10990 / 10971 | Shared Core/legacy scheduler. Builds the desired set and delta-culls leavers; admits the occupied/support tile plus collision-required LOD0 tiles in the pawn's 3×3 XY ring as `CollisionCritical`. S5 coarse-first admission stays opt-in (`voxel.StreamingCoarseFirst=1`). `voxel.StreamingDynamicNearReserve=1` is now the default: it keeps the authored near reserve while moving or until the stationary walkable ring is ready, then makes those slots available to coarse work. Admission counts submitted queued/running tasks, not completed results waiting in the MPSC result queues; `voxel.StreamingLegacyPendingResultCap=1` restores the old `PendingTiles`-based cap for the negative control. `PendingTiles` still lasts through game-thread apply for duplicate suppression and result bookkeeping. The clipmap stays non-overlapping and load-before-unload / `TransitionHold` reconciliation remains authoritative. Standalone has no mirror of this actor scheduler. §8.10 + §8.26–8.28. | | `UpdateChunksAroundPosition` / `GetGenerationAdmissionCount` | 10990 / 10971 | Shared Core/legacy scheduler. Builds the desired set and delta-culls leavers; admits the occupied/support tile plus collision-required LOD0 tiles in the pawn's 3×3 XY ring as `CollisionCritical`. S5 coarse-first admission stays opt-in (`voxel.StreamingCoarseFirst=1`). `voxel.StreamingDynamicNearReserve=1` is the default: it keeps the authored near reserve while moving or until the stationary walkable ring is ready, then makes those slots available to coarse work. Admission counts submitted queued/running tasks, not completed results waiting in the MPSC result queues; `voxel.StreamingLegacyPendingResultCap=1` restores the old `PendingTiles`-based cap for the negative control. `voxel.StreamingFarApplySupplement=1` is the default L121 lane: after the visible-apply cap, at most one far result is admitted when under half the apply-time budget and startup boost is inactive. `PendingTiles` still lasts through game-thread apply for duplicate suppression and result bookkeeping. The clipmap stays non-overlapping and load-before-unload / `TransitionHold` reconciliation remains authoritative. Standalone has no mirror of this actor scheduler. §8.10 + §8.26–8.28. |
| `AdvanceHeadlessCollisionGateStressTest` / `ObserveHeadlessCollisionGateStressTest` / `MaybeFinishHeadlessCollisionGateStressTest` / `MeasureHeadlessCollisionGateStressTerrain` | — | Explicit `-voxel.TestCollisionGateStress=1` harness only. The normal spawn-height cvar offsets from the measured density surface; `voxel.TestCollisionGateStressDeepDrop=1` carves a process-local box shaft from above the top to just above the floor tile, extending along the post-landing route so the pawn does not run into the shaft sidewall, temporarily raises only the transient test DiffLayer brush-radius allowance for that fixture, removes only the local pawn's redundant streaming anchor for this isolated repro, proves every sample in two complete outside-ring level-0 tiles is air before seeding their known-empty status, prewarms the known-empty in-ring column, and asserts that the support walk reaches the in-layout floor outside the aligned level-0 render ring before the pawn drops. The log reports whether the fixed gate retains that otherwise out-of-ring target. Other streaming anchors remain active. The fixture rejects a target or floor outside the layout, a non-air empty-tile proof, or a floor inside the aligned level-0 ring. The `GetDensityAt` column at capsule feet defines the local air/solid surface; the deep-drop column crossing is cached after its first exact scan. Each frame records actor position, velocity, direct foot tile and gate support readiness, gate state, signed feet clearance, and a ≤0.5-voxel sweep between positions. PASS requires no measured clearance below −5 cm; deep-drop gate-on also requires horizontal travel during a hold. Result logs include the terminal density column, first crossing below spawn height, hold count/time, and gated horizontal progress. | | `AdvanceHeadlessCollisionGateStressTest` / `ObserveHeadlessCollisionGateStressTest` / `MaybeFinishHeadlessCollisionGateStressTest` / `MeasureHeadlessCollisionGateStressTerrain` | — | Explicit `-voxel.TestCollisionGateStress=1` harness only. The normal spawn-height cvar offsets from the measured density surface; `voxel.TestCollisionGateStressDeepDrop=1` carves a process-local box shaft from above the top to just above the floor tile, extending along the post-landing route so the pawn does not run into the shaft sidewall, temporarily raises only the transient test DiffLayer brush-radius allowance for that fixture, removes only the local pawn's redundant streaming anchor for this isolated repro, proves every sample in two complete outside-ring level-0 tiles is air before seeding their known-empty status, prewarms the known-empty in-ring column, and asserts that the support walk reaches the in-layout floor outside the aligned level-0 render ring before the pawn drops. The log reports whether the fixed gate retains that otherwise out-of-ring target. Other streaming anchors remain active. The fixture rejects a target or floor outside the layout, a non-air empty-tile proof, or a floor inside the aligned level-0 ring. The `GetDensityAt` column at capsule feet defines the local air/solid surface; the deep-drop column crossing is cached after its first exact scan. Each frame records actor position, velocity, direct foot tile and gate support readiness, gate state, signed feet clearance, and a ≤0.5-voxel sweep between positions. PASS requires no measured clearance below −5 cm; deep-drop gate-on also requires horizontal travel during a hold. Result logs include the terminal density column, first crossing below spawn height, hold count/time, and gated horizontal progress. |
| `BuildDesiredTiles` | 6482 | Builds `DesiredSorted`+`DesiredStamped` (player clipmap shells), then adds streaming anchors and the pending player's collision-only support column before the leaver sweep. It orders from the true local pawn position and builds `CriticalDesiredTiles` for the unresolved support target, occupied/support and heading tiles. Also rebuilds `DesiredTransitionDescendants` (ancestor → desired descendants, including face/edge-touching replacements) for the reconcile pass. | | `BuildDesiredTiles` | 6482 | Builds `DesiredSorted`+`DesiredStamped` (player clipmap shells), then adds streaming anchors and the pending player's collision-only support column before the leaver sweep. It orders from the true local pawn position and builds `CriticalDesiredTiles` for the unresolved support target, occupied/support and heading tiles. Also rebuilds `DesiredTransitionDescendants` (ancestor → desired descendants, including face/edge-touching replacements) for the reconcile pass. |
| `CollisionGateDesiredTiles` / `RequestCollisionGateSupportTile` | 5062 | Retains an unresolved support target and up to three level-0 tiles below it across desired-set rebuilds. The additional keys are hidden collision-only tiles, queued ahead of ordinary streaming, and pruned once resolved or outside the active column. | | `CollisionGateDesiredTiles` / `RequestCollisionGateSupportTile` | 5062 | Retains an unresolved support target and up to three level-0 tiles below it across desired-set rebuilds. The additional keys are hidden collision-only tiles, queued ahead of ordinary streaming, and pruned once resolved or outside the active column. |
+57
View File
@@ -0,0 +1,57 @@
# VoxelForge current state
As of 2026-10-31. L131 is on `assembler/world` as `8107196`; this L132 worktree is on `wip/l132-dispatch-truth`, starting at `ebae71b`. Current-state claims below point to their code or measured report.
## What ships today
- **Core:** evaluator version **227**. Unreal's `AVoxelWorld::bUseVoxelForgeCore` is opt-in and defaults to `false`; the legacy generator remains the default runtime path. [Core version](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h), [runtime switch](Source/VoxelForge/Public/VoxelWorld.h), [L130 report](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md).
- **Verified owner4 generator dispatch (L132):** SW binds `SurfaceWorld-native` because the owner export contains a matching `SurfaceWorld.Heightfield2D` v2 plan. FI binds `compiled-analytic/FloatingIslands` because its `FloatingIslands.ClusterField` v0 layout has no matching native island plan. TN and VS bind `compiled-analytic/TunnelNetwork` and `compiled-analytic/VerticalShafts`; their legacy recipe IDs provide neither a `NativeCaves`/`NativeLattice` graph recipe nor native field capability. The L132 fast-tier report prints the binding and generated tile-center share at each owner strate. [Dispatch](Standalone/Core/src/ShapeWorldInternal.h), [adapter recipe checks](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h), [L132 report](Standalone/Research/WorldAssemblerM3/Linux132/REPORT.md).
- **Passages:** continuous smooth swept tubes with a continuous floor profile. L125 stamps conservative route slabs and outside sentinels into the tile cache; exact route geometry is still evaluated. All 21,425 route samples passed the open-air walk check; the nine-station floor probe's maximum root offset was 7.884 mm. L130 sets the shared influence reach to **1.0533 m** (0.75 m shell + 0.21 m relief + 0.50 m smooth-max support / 6 + 0.01 m margin), converted once from metres to Core voxels for the proof, feature, fallback, and off-grid bounds. [L125](Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md), [L130](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md).
- **Streaming:** arrival uses a bounded startup apply burst: `voxel.StreamingStartupApplyBoost=1` by default, up to 24 visible meshes or 14 ms per frame for at most 30 seconds, until the initial desired set and walkable ring are ready. `voxel.StreamingFarApplySupplement=1` is also default-on: after the normal visible apply cap, at most one far result applies when under half the apply-time budget; it is inactive during the startup burst. Dynamic near reserve defaults on; coarse-first remains off; the legacy pending-result cap defaults off. The far-apply lane is Unreal-only. [CVar source](Source/VoxelForge/Private/VoxelWorld.cpp), [L121 report](Standalone/Research/WorldAssemblerM3/Linux121/REPORT.md), [architecture](ARCHITECTURE.md).
- **Determinism:** proven on Linux with Clang and GCC at W1/W8/W12. L131's unchanged-output comparison covers owner4's SW/FI/TN/VS views in 24 compiler/worker cases; the canonical owner4/owner2 recipe matrix also passes. Windows determinism is unverified. [L131 checks](Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md), [v227 canonical hashes](Standalone/Tools/verify/baselines/owner-hash/canonical/).
## Current performance references
Headless medians in seconds, five interleaved L80-style measurements per version in each cell. Near-ready means the complete LOD0–2 set; whole-view is the full requested view. Values below are the edited L131 binary; the paired pre-cleanup comparison is in the [L131 report](Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md) and [timing summary](Saved/Linux/l131/timing/summary.json).
| Stratum | Near-ready W8 / W12 | Whole-view W8 / W12 |
|---|---:|---:|
| S0 | 0.804 / 0.848 | 1.811 / 1.832 |
| S1 | 1.014 / 1.002 | 1.993 / 1.886 |
| S2 | 1.060 / 1.012 | 1.960 / 1.865 |
| S3 | 0.917 / 0.884 | 1.887 / 1.782 |
Across 40 paired runs per metric, the mean relative change from pre-cleanup L130 was −0.22% near-ready (95% CI −1.02% to +0.57%) and −0.29% whole-view (−1.04% to +0.46%); neither indicates a measurable slowdown. The machine gate was load 0.81 and 99.2% idle. [L80 protocol](Standalone/Research/WorldAssemblerM3/Linux80/REPORT.md), [runner and raw runs](Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py).
The latest recorded Linux Unreal start-to-LOD0–2 values are L127's one run per stratum: S0 2.456 s, S1 2.231 s, S2 2.514 s, S3 2.221 s. L130 did not rerun UE startup or movement. Five L127 runs at 30 m/s, 12 s moving plus 5 s dwell, had median moving near coverage **40.22%**, far holes **3.46%**, tick p99 **10.80 ms**, and worst tick **13.27 ms**. After stopping, near coverage reached 100% and far holes reached 0%. These are the latest UE references, not v227 UE measurements. [L127 report](Standalone/Research/WorldAssemblerM3/Linux127/REPORT.md), [L127 movement summary](Saved/Linux/l127/ue/movement-pairs-summary.json), [L127 start summaries](Saved/Linux/l127/ue/merge/start-summary.json).
## Open gaps
- Multiplayer world lifecycle is unfinished: seed/layout replication, authoritative modification RPCs, and serialized diff replay for late join are still open. See [ARCHITECTURE §9.5 and §9.7](ARCHITECTURE.md).
- Determinism has not been verified on Windows. Linux Clang/GCC agreement does not establish MSVC agreement.
- NullRHI measures residency and apply timing, not the first presented pixel or rendered enclosure. L127 also found four low-contrast views among 23 otherwise verified captures. [L121](Standalone/Research/WorldAssemblerM3/Linux121/REPORT.md), [L127](Standalone/Research/WorldAssemblerM3/Linux127/REPORT.md).
- UE startup/movement numbers above predate L130's v227 reach change; there is no L130/L131 UE movement comparison.
## How to verify
- Build standalone Clang or GCC with [`Standalone/BuildLinux.sh`](Standalone/BuildLinux.sh), for example `bash Standalone/BuildLinux.sh --compiler=clang++-19 --variant=l132-clang --fp-contract=off --jobs=4`. The script pins CPUs 0–11 and applies `prlimit --as=6442450944` itself.
- Fast tier: `python3 Standalone/Tools/verify/verify.py fast --out Saved/Linux/l132 --overall-timeout 1740`. It includes the L132 four-pose owner density-dispatch report; per-run logs and the summary stay under `Saved/Linux/l132`.
- Canonical output baselines are the v227 files in `Standalone/Tools/verify/baselines/owner-hash/canonical/`. L131 also provides reproducible pre-cleanup identity matrices for owner4 tile hashes and owner4/owner2 canonical hashes under `Standalone/Research/WorldAssemblerM3/Linux131/`.
- Timing follows L80: quiet machine, no overlapping build, one warm-up per version, five interleaved runs per stratum and worker count, W8/W12. Each headless run uses `taskset -c 0-11`, `prlimit --as=6442450944`, and a 1,740 s timeout. See [`RunL131Timing.py`](Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py).
- Linux host build: `/home/jahni/UnrealEngine/Engine/Build/BatchFiles/Linux/Build.sh VFHostEditor Linux Development -Project=/home/jahni/VFHost/VFHost.uproject -NoUBA -MaxParallelActions=4 -Log=/home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l131/ubt.log`. Run Unreal without `prlimit`.
## Closed experiments
These rejected speed/streaming proposals stay closed unless new evidence changes their measured failure. Reports and surviving patches are linked in the [L131 inventory](Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md).
| Line | Result |
|---|---|
| L116 tunnel chord bound and z cache | Pair count fell 20.8%, but stayed 1.96× L109 and readiness was 15–30% slower. Patch: [`chord-bound-zcolumn-not-merged.patch`](Standalone/Research/WorldAssemblerM3/Linux116/patches/chord-bound-zcolumn-not-merged.patch), commit `89504ec`; see [report](Standalone/Research/WorldAssemblerM3/Linux116/REPORT.md). |
| L118 pair pruning / deferred floor work | Pruned 17–18% of candidate pairs but remained 17.2–25.7% behind L109 near-ready. [Report](Standalone/Research/WorldAssemblerM3/Linux118/REPORT.md); patch: [`l118-designs.patch`](Standalone/Research/WorldAssemblerM3/Linux118/l118-designs.patch). |
| L126 floor-call filters | Cut floor calls 20–23% without reducing pair counts; all eight cells missed L109. Candidate source was restored and no standalone patch was kept; see [report](Standalone/Research/WorldAssemblerM3/Linux126/REPORT.md) and commit `01a5094`. |
| L128 half-open slab ownership | Ownership audit passed, but readiness was 14.8–28.0% behind L109 and moving coverage dropped. Report and patch `l128-slab-ownership.patch` are in archived commit `7022d02`. |
| L129 2 m physical reach | v225's metre/Core-voxel bounds mismatch made the correctly bounded candidate do more work; it was slower and failed UE movement gates. Report and patch `l129-reach-2m.patch` are in archived commit `0d1140c`. |
| L120 priority split / extra worker slots | Neither met coverage and tick gates; 10 slots reached a 16.67 ms worst tick against 15.43 ms baseline. [Report](Standalone/Research/WorldAssemblerM3/Linux120/REPORT.md); no production patch retained. |
| L122 admission relaxation | Early admission missed p99 or far-hole gates; no behavior retained. [Report](Standalone/Research/WorldAssemblerM3/Linux122/REPORT.md); patch: [`l122-trace.patch`](Standalone/Research/WorldAssemblerM3/Linux122/l122-trace.patch). |
| L123 lookahead / completion gate | Lookahead failed p99 and worsened near/far coverage; queue-only also failed p99. [Report](Standalone/Research/WorldAssemblerM3/Linux123/REPORT.md); patch: [`l123-queue-lookahead.patch`](Standalone/Research/WorldAssemblerM3/Linux123/l123-queue-lookahead.patch). |
| L124 early LOD0 pages | Optimistic gain stayed below 100 ms except negative cases; no publication/scheduling implementation was made. [Report](Standalone/Research/WorldAssemblerM3/Linux124/REPORT.md). |
@@ -494,7 +494,12 @@ namespace VoxelForgeCore
// Version 223 projects the floor onto one continuous arc-length profile and groups // Version 223 projects the floor onto one continuous arc-length profile and groups
// dense passage samples into coarse tile candidates. Version 224 keeps the same // dense passage samples into coarse tile candidates. Version 224 keeps the same
// continuous floor while relaxing the spline chord tolerance and bounding shell work. // continuous floor while relaxing the spline chord tolerance and bounding shell work.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 225; // Version 226 unifies the passage influence reach in metres and converts it to core
// voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max
// support width/6 when deriving that reach. Version 228 composes deterministic cave
// graphs from fixed passage mouths and the owner Tunnel Network settings. Version 229
// budgets cave floor support; 230 records a shared backbone floor profile and joint cap.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 230;
enum class FieldChannel : std::uint32_t enum class FieldChannel : std::uint32_t
{ {
@@ -889,6 +894,7 @@ namespace VoxelForgeCore
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV2 = 2; inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV2 = 2;
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV3 = 3; inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV3 = 3;
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV1 = 1; inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV1 = 1;
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV2 = 2;
inline constexpr std::uint32_t ResolvedLayoutOreVeinVocabularyVersionV1 = 1; inline constexpr std::uint32_t ResolvedLayoutOreVeinVocabularyVersionV1 = 1;
enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict }; enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict };
@@ -953,6 +959,11 @@ namespace VoxelForgeCore
std::array<std::int64_t, 3> centerMicrometres{}; std::array<std::int64_t, 3> centerMicrometres{};
std::array<std::int64_t, 3> halfExtentsMicrometres{}; std::array<std::int64_t, 3> halfExtentsMicrometres{};
std::int64_t floorMicrometres = 0; std::int64_t floorMicrometres = 0;
// Optional shared backbone profile reference. The room floor plane is
// floorMicrometres plus this Q16 XY gradient from centerMicrometres.
std::string floorProfileStableId;
std::int64_t floorProfileDistanceMicrometres = -1;
std::array<std::int32_t, 2> floorGradientQ16{};
std::int64_t smoothWidthMicrometres = 0; std::int64_t smoothWidthMicrometres = 0;
// Optional feature-to-feature union width. Zero preserves the historical // Optional feature-to-feature union width. Zero preserves the historical
// behavior where smoothWidthMicrometres also controls the graph union. // behavior where smoothWidthMicrometres also controls the graph union.
@@ -972,6 +983,11 @@ namespace VoxelForgeCore
std::int32_t strateIndex = -1; std::int32_t strateIndex = -1;
// A single cubic Bezier in XY. Its four Z values are the cubic floor profile. // A single cubic Bezier in XY. Its four Z values are the cubic floor profile.
std::array<std::array<std::int64_t, 3>, 4> controlPointsMicrometres{}; std::array<std::array<std::int64_t, 3>, 4> controlPointsMicrometres{};
// Edges with the same profile ID are adjacent pieces of one shared h(s).
// Distances are signed along that profile, so a terminal edge may be reversed.
std::string floorProfileStableId;
std::int64_t floorProfileStartDistanceMicrometres = -1;
std::int64_t floorProfileEndDistanceMicrometres = -1;
// Resolve-time, error-bounded approximation of the cubic centerline. Each // Resolve-time, error-bounded approximation of the cubic centerline. Each
// consecutive pair is a physical segment used by the native cave field. // consecutive pair is a physical segment used by the native cave field.
std::vector<std::array<std::int64_t, 3>> polylineMicrometres; std::vector<std::array<std::int64_t, 3>> polylineMicrometres;
@@ -988,6 +1004,37 @@ namespace VoxelForgeCore
std::array<std::int64_t, 3> boundsMaximumMicrometres{}; std::array<std::int64_t, 3> boundsMaximumMicrometres{};
}; };
// Inputs to the reusable, plan-time cave graph composer. All distances are
// integer micrometres and unit-interval values are Q16 so the resolved graph
// does not depend on worker count or floating-point random distributions.
struct ResolvedCavernPlannerSettingsV1
{
std::int64_t roomSpacingMicrometres = 16000000;
std::int64_t minimumRoomRadiusMicrometres = 2000000;
std::int64_t maximumRoomRadiusMicrometres = 5000000;
std::int64_t destinationRadiusMicrometres = 6000000;
// Solid floor backing below the planned edge floor, preserved after graph
// unions and passage composition. This is separate from bore height.
std::int64_t floorSupportDepthMicrometres = 750000;
std::int64_t minimumTunnelRadiusMicrometres = 750000;
std::int64_t maximumTunnelRadiusMicrometres = 1000000;
std::int64_t maximumTunnelLengthMicrometres = 45000000;
std::int64_t tunnelWarpMicrometres = 0;
std::int64_t caveWarpMicrometres = 0;
std::int64_t smoothWidthMicrometres = 500000;
std::int64_t irregularityAmplitudeMicrometres = 0;
std::int64_t irregularityWavelengthMicrometres = 0;
std::int32_t roomDensityQ16 = 22938;
std::int32_t tunnelDensityQ16 = 26214;
std::int32_t roomShapeVarietyQ16 = 32768;
std::int32_t roomHeightRatioQ16 = 55706;
std::int32_t roomFloorCutMinimumQ16 = 45875;
std::int32_t roomFloorCutMaximumQ16 = 58982;
std::int32_t tunnelHorizontalBiasQ16 = 32768;
std::int32_t originRoomMaximumConnections = 4;
bool tunnelsFlowTowardOrigin = true;
};
// Immutable material-only deposits. The capsule and its conservative bounds are in // Immutable material-only deposits. The capsule and its conservative bounds are in
// micrometres; bounds include irregularity and the blend envelope. // micrometres; bounds include irregularity and the blend envelope.
struct ResolvedLayoutOreVeinRequestV1 struct ResolvedLayoutOreVeinRequestV1
@@ -1092,7 +1139,7 @@ namespace VoxelForgeCore
inline bool IsNativeCaveFieldStratum(const ResolvedLayoutStratumV2& stratum) noexcept inline bool IsNativeCaveFieldStratum(const ResolvedLayoutStratumV2& stratum) noexcept
{ {
return stratum.generatorKind=="Caves" return (stratum.generatorKind=="Caves"||stratum.generatorKind=="TunnelNetwork")
&&stratum.recipeId=="Core.Caves.PrimitiveField" &&stratum.recipeId=="Core.Caves.PrimitiveField"
&&stratum.recipeVersion==1; &&stratum.recipeVersion==1;
} }
@@ -1253,6 +1300,17 @@ namespace VoxelForgeCore
std::span<const ResolvedCavernRoomV1> rooms, std::span<const ResolvedCavernRoomV1> rooms,
std::span<const ResolvedCavernEdgeV1> edges, std::span<const ResolvedCavernEdgeV1> edges,
std::string& error); std::string& error);
// Build a deterministic, walkable room-and-tunnel graph between the two
// passage mouths that terminate in strateIndex. Compound rooms are emitted
// as overlapping room primitives; the route is planned below a 15 degree
// walking grade and every corridor carries a 4.5 m vertical clearance.
VFCORE_API bool PlanResolvedCavernNetworkV1(
const ResolvedLayoutSnapshotV3& layout, std::int32_t seed,
std::int32_t strateIndex,
const ResolvedCavernPlannerSettingsV1& settings,
std::vector<ResolvedCavernRoomV1>& rooms,
std::vector<ResolvedCavernEdgeV1>& edges,
std::string& error);
VFCORE_API bool ResolveResolvedLayoutOreVeinsV1( VFCORE_API bool ResolveResolvedLayoutOreVeinsV1(
ResolvedLayoutSnapshotV3& layout, std::int32_t seed, ResolvedLayoutSnapshotV3& layout, std::int32_t seed,
std::span<const ResolvedLayoutOreVeinRequestV1> requests, std::span<const ResolvedLayoutOreVeinRequestV1> requests,
+683 -2
View File
@@ -621,6 +621,10 @@ namespace
<<"],\"half_extents_um\":["<<room.halfExtentsMicrometres[0]<<',' <<"],\"half_extents_um\":["<<room.halfExtentsMicrometres[0]<<','
<<room.halfExtentsMicrometres[1]<<','<<room.halfExtentsMicrometres[2] <<room.halfExtentsMicrometres[1]<<','<<room.halfExtentsMicrometres[2]
<<"],\"floor_um\":"<<room.floorMicrometres <<"],\"floor_um\":"<<room.floorMicrometres
<<",\"floor_profile_id\":"<<LayoutEscape(room.floorProfileStableId)
<<",\"floor_profile_distance_um\":"<<room.floorProfileDistanceMicrometres
<<",\"floor_gradient_q16\":["<<room.floorGradientQ16[0]<<','
<<room.floorGradientQ16[1]<<']'
<<",\"smooth_width_um\":"<<room.smoothWidthMicrometres <<",\"smooth_width_um\":"<<room.smoothWidthMicrometres
; ;
if(room.unionWidthMicrometres>0) if(room.unionWidthMicrometres>0)
@@ -647,6 +651,11 @@ namespace
out<<'['<<p[0]<<','<<p[1]<<','<<p[2]<<']'; out<<'['<<p[0]<<','<<p[1]<<','<<p[2]<<']';
} }
out<<']'; out<<']';
out<<",\"floor_profile_id\":"<<LayoutEscape(edge.floorProfileStableId)
<<",\"floor_profile_start_distance_um\":"
<<edge.floorProfileStartDistanceMicrometres
<<",\"floor_profile_end_distance_um\":"
<<edge.floorProfileEndDistanceMicrometres;
if(!edge.polylineMicrometres.empty()) if(!edge.polylineMicrometres.empty())
{ {
out<<",\"polyline_um\":["; out<<",\"polyline_um\":[";
@@ -1235,7 +1244,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
add("cavern_graph_vocabulary_version", add("cavern_graph_vocabulary_version",
"cavern graph vocabulary version is set without graph records"); "cavern graph vocabulary version is set without graph records");
} }
else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1) else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1
&&cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV2)
add("cavern_graph_vocabulary_version", add("cavern_graph_vocabulary_version",
"cavern graph records require vocabulary version 1"); "cavern graph records require vocabulary version 1");
if(cavernGraphVocabularyVersion!=0) if(cavernGraphVocabularyVersion!=0)
@@ -1268,6 +1278,16 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
||(room.irregularityAmplitudeMicrometres>0 ||(room.irregularityAmplitudeMicrometres>0
&&room.irregularityWavelengthMicrometres<=0)) &&room.irregularityWavelengthMicrometres<=0))
add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters"); add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters");
const std::int64_t gradientSquared=static_cast<std::int64_t>(room.floorGradientQ16[0])
*room.floorGradientQ16[0]+static_cast<std::int64_t>(room.floorGradientQ16[1])
*room.floorGradientQ16[1];
if((room.floorProfileStableId.empty()
&&room.floorProfileDistanceMicrometres!=-1)
||(!room.floorProfileStableId.empty()
&&room.floorProfileDistanceMicrometres<0)
||gradientSquared>static_cast<std::int64_t>(13108)*13108)
add("cavern_room_floor_profile",room.stableId+
" has an invalid shared profile reference or floor grade");
const std::int64_t pad=room.smoothWidthMicrometres const std::int64_t pad=room.smoothWidthMicrometres
+room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres; +room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres;
for(std::size_t axis=0;axis<3;++axis) for(std::size_t axis=0;axis<3;++axis)
@@ -1308,6 +1328,15 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
||(edge.irregularityAmplitudeMicrometres>0 ||(edge.irregularityAmplitudeMicrometres>0
&&edge.irregularityWavelengthMicrometres<=0)) &&edge.irregularityWavelengthMicrometres<=0))
add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters"); add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters");
const bool hasFloorProfile=!edge.floorProfileStableId.empty();
if((hasFloorProfile&&(edge.floorProfileStartDistanceMicrometres<0
||edge.floorProfileEndDistanceMicrometres<0
||edge.floorProfileStartDistanceMicrometres
==edge.floorProfileEndDistanceMicrometres))
||(!hasFloorProfile&&(edge.floorProfileStartDistanceMicrometres!=-1
||edge.floorProfileEndDistanceMicrometres!=-1)))
add("cavern_edge_floor_profile",edge.stableId+
" has an invalid shared profile distance interval");
std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0]; std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0];
std::array<std::int64_t,3> high=low; std::array<std::int64_t,3> high=low;
if(nativeLattice&&!edge.polylineMicrometres.empty()) if(nativeLattice&&!edge.polylineMicrometres.empty())
@@ -1363,6 +1392,114 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
add("cavern_edge_polyline",edge.stableId+ add("cavern_edge_polyline",edge.stableId+
" has no bounded resolve-time polyline with matching endpoints"); " has no bounded resolve-time polyline with matching endpoints");
} }
std::vector<std::string> floorProfileIds;
for(const auto& edge:cavernEdges)
if(!edge.floorProfileStableId.empty()
&&std::find(floorProfileIds.begin(),floorProfileIds.end(),
edge.floorProfileStableId)==floorProfileIds.end())
floorProfileIds.push_back(edge.floorProfileStableId);
for(const std::string& profileId:floorProfileIds)
{
std::vector<const ResolvedCavernEdgeV1*> profileEdges;
for(const auto& edge:cavernEdges)
if(edge.floorProfileStableId==profileId)profileEdges.push_back(&edge);
std::sort(profileEdges.begin(),profileEdges.end(),[](const auto* a,const auto* b)
{
return std::min(a->floorProfileStartDistanceMicrometres,
a->floorProfileEndDistanceMicrometres)
<std::min(b->floorProfileStartDistanceMicrometres,
b->floorProfileEndDistanceMicrometres);
});
const auto endpointZ=[](const ResolvedCavernEdgeV1& edge,bool atMinimum)
{
const bool forward=edge.floorProfileStartDistanceMicrometres
<edge.floorProfileEndDistanceMicrometres;
const std::size_t point=(atMinimum==forward)?0:3;
return edge.controlPointsMicrometres[point][2];
};
const auto endpointSlope=[](const ResolvedCavernEdgeV1& edge,bool atMinimum)
{
const double span=static_cast<double>(edge.floorProfileEndDistanceMicrometres
-edge.floorProfileStartDistanceMicrometres);
if(span==0.0)return std::numeric_limits<double>::infinity();
const bool forward=edge.floorProfileStartDistanceMicrometres
<edge.floorProfileEndDistanceMicrometres;
const std::size_t control=atMinimum==forward?0:2;
return 3.0*(edge.controlPointsMicrometres[control+1][2]
-edge.controlPointsMicrometres[control][2])/span;
};
const ResolvedCavernEdgeV1* previous=nullptr;
for(const auto* edge:profileEdges)
{
const std::int64_t low=std::min(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
const std::int64_t high=std::max(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
const double slope0=endpointSlope(*edge,true);
const double slope1=endpointSlope(*edge,false);
if(std::max(std::abs(slope0),std::abs(slope1))>0.200001)
add("cavern_floor_profile_grade",edge->stableId+
" exceeds the shared backbone 1:5 grade limit");
if(previous)
{
const std::int64_t previousHigh=std::max(
previous->floorProfileStartDistanceMicrometres,
previous->floorProfileEndDistanceMicrometres);
if(low!=previousHigh
||std::abs(endpointZ(*previous,false)-endpointZ(*edge,true))>1000
||std::abs(endpointSlope(*previous,false)-slope0)>0.000001)
add("cavern_floor_profile_joint",profileId+
" has a gap, height step, or slope step between adjacent edges");
}
previous=edge;
(void)high;
}
for(const auto& room:cavernRooms)
{
if(room.floorProfileStableId!=profileId)continue;
const ResolvedCavernEdgeV1* containing=nullptr;
for(const auto* edge:profileEdges)
{
const auto low=std::min(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
const auto high=std::max(edge->floorProfileStartDistanceMicrometres,
edge->floorProfileEndDistanceMicrometres);
if(room.floorProfileDistanceMicrometres>=low
&&room.floorProfileDistanceMicrometres<=high)
{containing=edge;break;}
}
if(!containing)
{
add("cavern_room_floor_profile",room.stableId+
" does not lie on its referenced continuous floor profile");
continue;
}
const auto& edge=*containing;
const double t=static_cast<double>(room.floorProfileDistanceMicrometres
-edge.floorProfileStartDistanceMicrometres)
/(edge.floorProfileEndDistanceMicrometres
-edge.floorProfileStartDistanceMicrometres);
const double one=1.0-t;
const double expected=one*one*one*edge.controlPointsMicrometres[0][2]
+3.0*one*one*t*edge.controlPointsMicrometres[1][2]
+3.0*one*t*t*edge.controlPointsMicrometres[2][2]
+t*t*t*edge.controlPointsMicrometres[3][2];
std::array<double,2> routeXY{};
for(std::size_t axis=0;axis<2;++axis)
routeXY[axis]=one*one*one*edge.controlPointsMicrometres[0][axis]
+3.0*one*one*t*edge.controlPointsMicrometres[1][axis]
+3.0*one*t*t*edge.controlPointsMicrometres[2][axis]
+t*t*t*edge.controlPointsMicrometres[3][axis];
const double roomFloorAtProfile=room.floorMicrometres
+room.floorGradientQ16[0]/65536.0
*(routeXY[0]-room.centerMicrometres[0])
+room.floorGradientQ16[1]/65536.0
*(routeXY[1]-room.centerMicrometres[1]);
if(std::abs(expected-roomFloorAtProfile)>1000.0)
add("cavern_room_floor_profile",room.stableId+
" floor does not match its referenced h(s)");
}
}
} }
if(oreVeins.empty()) if(oreVeins.empty())
{ {
@@ -1526,6 +1663,12 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
readTriplet("bounds_min_um",room.boundsMinimumMicrometres); readTriplet("bounds_min_um",room.boundsMinimumMicrometres);
readTriplet("bounds_max_um",room.boundsMaximumMicrometres); readTriplet("bounds_max_um",room.boundsMaximumMicrometres);
room.floorMicrometres=LayoutInt(v,"floor_um"); room.floorMicrometres=LayoutInt(v,"floor_um");
room.floorProfileStableId=LayoutString(v,"floor_profile_id");
if(v.Get("floor_profile_distance_um").IsNumber())
room.floorProfileDistanceMicrometres=LayoutInt(v,"floor_profile_distance_um");
const auto& floorGradient=v.Get("floor_gradient_q16").AsArray();
for(std::size_t axis=0;axis<2&&axis<floorGradient.size();++axis)
room.floorGradientQ16[axis]=static_cast<std::int32_t>(floorGradient[axis].Integer());
room.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um"); room.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um");
room.unionWidthMicrometres=LayoutInt(v,"union_width_um"); room.unionWidthMicrometres=LayoutInt(v,"union_width_um");
room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um"); room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
@@ -1539,6 +1682,13 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
edge.startAnchorStableId=LayoutString(v,"start_anchor_stable_id"); edge.startAnchorStableId=LayoutString(v,"start_anchor_stable_id");
edge.endRoomStableId=LayoutString(v,"end_room_stable_id"); edge.endRoomStableId=LayoutString(v,"end_room_stable_id");
edge.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index")); edge.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index"));
edge.floorProfileStableId=LayoutString(v,"floor_profile_id");
if(v.Get("floor_profile_start_distance_um").IsNumber())
edge.floorProfileStartDistanceMicrometres=
LayoutInt(v,"floor_profile_start_distance_um");
if(v.Get("floor_profile_end_distance_um").IsNumber())
edge.floorProfileEndDistanceMicrometres=
LayoutInt(v,"floor_profile_end_distance_um");
const auto& points=v.Get("control_points_um").AsArray(); const auto& points=v.Get("control_points_um").AsArray();
for(std::size_t i=0;i<edge.controlPointsMicrometres.size()&&i<points.size();++i) for(std::size_t i=0;i<edge.controlPointsMicrometres.size()&&i<points.size();++i)
{ {
@@ -1879,7 +2029,7 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
if(!candidate.finalize(error))return false; if(!candidate.finalize(error))return false;
layout=std::move(candidate);return true; layout=std::move(candidate);return true;
} }
candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV1; candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV2;
candidate.cavernRooms.assign(rooms.begin(),rooms.end()); candidate.cavernRooms.assign(rooms.begin(),rooms.end());
candidate.cavernEdges.assign(edges.begin(),edges.end()); candidate.cavernEdges.assign(edges.begin(),edges.end());
const auto isNativeCavesStrate=[&candidate](std::int32_t strateIndex) const auto isNativeCavesStrate=[&candidate](std::int32_t strateIndex)
@@ -2031,6 +2181,537 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
layout=std::move(candidate);return true; layout=std::move(candidate);return true;
} }
bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
std::int32_t seed,std::int32_t strateIndex,
const ResolvedCavernPlannerSettingsV1& settings,
std::vector<ResolvedCavernRoomV1>& rooms,
std::vector<ResolvedCavernEdgeV1>& edges,std::string& error)
{
rooms.clear();edges.clear();error.clear();
const auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[strateIndex](const auto& value){return value.strateIndex==strateIndex;});
if(stratum==layout.strates.end()||!IsNativeCaveFieldStratum(*stratum))
{error="cavern planner target is not a resolved native cave stratum";return false;}
if(settings.roomSpacingMicrometres<=0
||settings.minimumRoomRadiusMicrometres<500000
||settings.maximumRoomRadiusMicrometres<settings.minimumRoomRadiusMicrometres
||settings.maximumRoomRadiusMicrometres>100000000
||settings.destinationRadiusMicrometres<settings.maximumRoomRadiusMicrometres
||settings.destinationRadiusMicrometres>100000000
||settings.floorSupportDepthMicrometres<250000
||settings.floorSupportDepthMicrometres>2000000
||settings.maximumTunnelLengthMicrometres<=0
||settings.minimumTunnelRadiusMicrometres<0
||settings.maximumTunnelRadiusMicrometres<settings.minimumTunnelRadiusMicrometres
||settings.smoothWidthMicrometres<0
||settings.smoothWidthMicrometres>10000000
||settings.irregularityAmplitudeMicrometres<0
||settings.irregularityAmplitudeMicrometres>10000000
||(settings.irregularityAmplitudeMicrometres>0
&&settings.irregularityWavelengthMicrometres<=0))
{error="cavern planner settings are outside the bounded room, tunnel or relief ranges";return false;}
const ResolvedLayoutPassageV1* arrivalPassage=nullptr;
const ResolvedLayoutPassageV1* departurePassage=nullptr;
for(const auto& passage:layout.passages)
{
if(passage.lowerStrateIndex==strateIndex&&passage.upperStrateIndex<strateIndex
&&(!arrivalPassage||passage.stableId<arrivalPassage->stableId))
arrivalPassage=&passage;
if(passage.upperStrateIndex==strateIndex&&passage.lowerStrateIndex>strateIndex
&&(!departurePassage||passage.stableId<departurePassage->stableId))
departurePassage=&passage;
}
if(!arrivalPassage||!departurePassage)
{error="cavern planner needs an incoming lower mouth and an outgoing upper mouth in the target strate";return false;}
if(layout.coreVoxelSizeMicrometres==0)
{error="cavern planner requires a nonzero Core voxel size";return false;}
using FPoint=std::array<std::int64_t,3>;
constexpr std::int64_t MicrometresPerMetre=1000000;
constexpr std::int64_t MinimumBoreRadius=1750000;
constexpr std::int64_t MinimumBoreHeight=2200000;
constexpr std::int64_t PlannedBoreHeight=4500000;
constexpr std::int64_t MouthOverlap=3000000;
constexpr std::int64_t MaximumBoreRadius=10000000;
constexpr std::int64_t MaximumWalkGradeDenominator=5; // 1:5 = 11.31 degrees.
const auto integerSqrt=[](std::uint64_t value)
{
std::uint64_t result=0;
std::uint64_t bit=std::uint64_t{1}<<62;
while(bit>value)bit>>=2;
while(bit!=0)
{
if(value>=result+bit)
{value-=result+bit;result=(result>>1)+bit;}
else result>>=1;
bit>>=2;
}
return result;
};
const auto distance2d=[&](const FPoint& a,const FPoint& b)
{
const std::int64_t dx=b[0]-a[0],dy=b[1]-a[1];
const auto squared=static_cast<std::uint64_t>(dx*dx+dy*dy);
return static_cast<std::int64_t>(integerSqrt(squared));
};
const auto qToMicrometres=[&](std::int64_t q)
{return q*static_cast<std::int64_t>(layout.coreVoxelSizeMicrometres)
/WorldAssemblySubvoxelsPerVoxel;};
const auto mouthInnerPoint=[&](const ResolvedLayoutPassageV1& passage,
bool lowerSide,FPoint& point)
{
for(std::size_t axis=0;axis<3;++axis)
point[axis]=qToMicrometres(lowerSide?passage.lowerMouthSubvoxels[axis]
:passage.upperMouthSubvoxels[axis]);
if(passage.controlPoints.size()<2)return false;
const std::size_t mouthIndex=lowerSide
?std::min(passage.lowerMouthControlIndex,passage.controlPoints.size()-1):0;
const std::size_t tangentIndex=lowerSide
?(mouthIndex>0?mouthIndex-1:1):1;
const std::int64_t tx=passage.controlPoints[mouthIndex].positionSubvoxels[0]
-passage.controlPoints[tangentIndex].positionSubvoxels[0];
const std::int64_t ty=passage.controlPoints[mouthIndex].positionSubvoxels[1]
-passage.controlPoints[tangentIndex].positionSubvoxels[1];
const auto tangentLength=static_cast<std::int64_t>(integerSqrt(
static_cast<std::uint64_t>(tx*tx+ty*ty)));
if(tangentLength<=0)return false;
point[0]+=tx*MouthOverlap/tangentLength;
point[1]+=ty*MouthOverlap/tangentLength;
return true;
};
FPoint routeStart{},routeEnd{};
if(!mouthInnerPoint(*arrivalPassage,true,routeStart)
||!mouthInnerPoint(*departurePassage,false,routeEnd))
{error="cavern passage mouth is missing a usable horizontal route tangent";return false;}
const std::int64_t dx=routeEnd[0]-routeStart[0];
const std::int64_t dy=routeEnd[1]-routeStart[1];
const std::int64_t dz=routeEnd[2]-routeStart[2];
const std::int64_t straightLength=distance2d(routeStart,routeEnd);
const std::int64_t horizontalBias=std::clamp<std::int64_t>(
settings.tunnelHorizontalBiasQ16,0,65536);
const std::int64_t planGradeDenominator=MaximumWalkGradeDenominator
+horizontalBias/32768;
const std::int64_t requiredLength=std::abs(dz)*planGradeDenominator+1000;
std::int64_t lateral=std::max<std::int64_t>(1000000,
std::max(settings.tunnelWarpMicrometres,settings.caveWarpMicrometres)/2);
const std::uint64_t routeHash=LayoutStampSeed(seed,strateIndex,
"tunnel-network/main-route");
if((routeHash&1u)!=0)lateral=-lateral;
std::int64_t pathAxisX=dx,pathAxisY=dy;
const auto makeRoute=[&](std::int64_t lateralOffset)
{
std::array<FPoint,4> points{};
points[0]=routeStart;points[3]=routeEnd;
std::int64_t axisX=dx,axisY=dy;
std::int64_t axisLength=straightLength;
if(axisLength<=0)
{
axisX=1;axisY=0;axisLength=1;
}
pathAxisX=axisX;pathAxisY=axisY;
const std::int64_t perpX=-axisY*lateralOffset/axisLength;
const std::int64_t perpY= axisX*lateralOffset/axisLength;
for(std::size_t axis=0;axis<3;++axis)
{
const std::int64_t delta=routeEnd[axis]-routeStart[axis];
points[1][axis]=routeStart[axis]+delta/3;
points[2][axis]=routeStart[axis]+(delta*2)/3;
}
points[1][0]+=perpX;points[1][1]+=perpY;
points[2][0]-=perpX;points[2][1]-=perpY;
return points;
};
const auto routeLength=[&](const std::array<FPoint,4>& points)
{
std::int64_t length=0;
for(std::size_t i=1;i<points.size();++i)length+=distance2d(points[i-1],points[i]);
return length;
};
auto routePoints=makeRoute(lateral);
for(int attempt=0;routeLength(routePoints)<requiredLength&&attempt<32;++attempt)
{
if(lateral>0)lateral=std::min<std::int64_t>(lateral*2,1000000000ll);
else lateral=std::max<std::int64_t>(lateral*2,-1000000000ll);
routePoints=makeRoute(lateral);
}
const std::int64_t totalRouteLength=routeLength(routePoints);
if(totalRouteLength<=0||totalRouteLength<requiredLength)
{error="cavern planner cannot fit the fixed mouth floor difference under the 1:5 grade budget";return false;}
std::array<std::int64_t,4> cumulative{};
for(std::size_t i=1;i<routePoints.size();++i)
cumulative[i]=cumulative[i-1]+distance2d(routePoints[i-1],routePoints[i]);
if(cumulative.back()!=totalRouteLength)
{error="cavern planner route length accounting failed";return false;}
const std::string backboneFloorProfileId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/backbone-floor";
const auto routeFloorAt=[&](std::int64_t distance)
{
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
return routeStart[2]+dz*distance/totalRouteLength;
};
const auto routeFloorGradientAt=[&](std::int64_t distance)
{
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
std::size_t segment=1;
while(segment<cumulative.size()-1&&distance>cumulative[segment])++segment;
const auto unitTangent=[&](std::size_t index)
{
const double tx=static_cast<double>(routePoints[index][0]
-routePoints[index-1][0]);
const double ty=static_cast<double>(routePoints[index][1]
-routePoints[index-1][1]);
const double length=std::hypot(tx,ty);
return length>0.0?std::array<double,2>{tx/length,ty/length}
:std::array<double,2>{0.0,0.0};
};
auto tangent=unitTangent(segment);
if(segment+1<cumulative.size()&&distance==cumulative[segment])
{
const auto next=unitTangent(segment+1);
tangent[0]+=next[0];tangent[1]+=next[1];
const double length=std::hypot(tangent[0],tangent[1]);
if(length>1.0e-12){tangent[0]/=length;tangent[1]/=length;}
}
constexpr double Q16=65536.0;
const double profileSlope=static_cast<double>(dz)/totalRouteLength;
return std::array<std::int32_t,2>{
static_cast<std::int32_t>(std::llround(tangent[0]*profileSlope*Q16)),
static_cast<std::int32_t>(std::llround(tangent[1]*profileSlope*Q16))};
};
const auto pointAt=[&](std::int64_t distance)
{
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
std::size_t segment=1;
while(segment<cumulative.size()-1&&distance>cumulative[segment])++segment;
const std::int64_t startDistance=cumulative[segment-1];
const std::int64_t segmentLength=std::max<std::int64_t>(1,
cumulative[segment]-startDistance);
const std::int64_t local=distance-startDistance;
FPoint point{};
for(std::size_t axis=0;axis<2;++axis)
{
const std::int64_t delta=routePoints[segment][axis]
-routePoints[segment-1][axis];
point[axis]=routePoints[segment-1][axis]+delta*local/segmentLength;
}
point[2]=routeFloorAt(distance);
return point;
};
std::int64_t spacing=std::min(std::max<std::int64_t>(settings.roomSpacingMicrometres,
4000000),settings.maximumTunnelLengthMicrometres);
spacing=std::max<std::int64_t>(4000000,spacing);
if(spacing>settings.maximumTunnelLengthMicrometres)
spacing=settings.maximumTunnelLengthMicrometres;
if(spacing<=0)
{error="cavern planner has no positive route spacing after applying tunnel length limits";return false;}
std::vector<std::int64_t> nodeDistances;
for(std::size_t leg=1;leg<routePoints.size();++leg)
{
const std::int64_t legStart=cumulative[leg-1];
const std::int64_t legLength=cumulative[leg]-legStart;
const std::int64_t segments=std::max<std::int64_t>(1,
(legLength+spacing-1)/spacing);
if(segments>256)
{error="cavern planner would exceed the 256-node bounded backbone";return false;}
for(std::int64_t part=1;part<=segments;++part)
{
const std::int64_t distance=legStart+legLength*part/segments;
if(distance>0&&distance<totalRouteLength)nodeDistances.push_back(distance);
}
}
nodeDistances.push_back(totalRouteLength/2);
std::sort(nodeDistances.begin(),nodeDistances.end());
nodeDistances.erase(std::unique(nodeDistances.begin(),nodeDistances.end()),nodeDistances.end());
if(nodeDistances.empty())nodeDistances.push_back(totalRouteLength/2);
if(nodeDistances.size()>256)
{error="cavern planner would exceed the 256-node bounded backbone";return false;}
const auto clampQ16=[](std::int32_t value)
{return std::clamp<std::int64_t>(value,0,65536);};
const std::int64_t smooth=std::clamp<std::int64_t>(
std::max<std::int64_t>(1500000,settings.smoothWidthMicrometres),
50000,10000000);
const std::int64_t irregularity=std::clamp<std::int64_t>(
settings.irregularityAmplitudeMicrometres,0,500000);
const std::int64_t routeBoreRadius=std::clamp<std::int64_t>(
std::max(MinimumBoreRadius,settings.minimumTunnelRadiusMicrometres),
MinimumBoreRadius,MaximumBoreRadius);
const std::int64_t maximumRouteBoreRadius=std::clamp<std::int64_t>(
std::max(routeBoreRadius,settings.maximumTunnelRadiusMicrometres),
routeBoreRadius,MaximumBoreRadius);
const auto selectedBoreRadius=[&](const std::string& stableId)
{
const std::uint64_t span=static_cast<std::uint64_t>(
maximumRouteBoreRadius-routeBoreRadius);
return routeBoreRadius+(span==0?0:static_cast<std::int64_t>(
LayoutStampSeed(seed,strateIndex,stableId)%(span+1)));
};
const auto passageBoreRadius=[&](const ResolvedLayoutPassageV1& passage,bool lowerSide)
{
std::int64_t radius=qToMicrometres(passage.mouthRadiusSubvoxels);
if(!passage.controlPoints.empty())
{
const auto control=lowerSide?std::min(passage.lowerMouthControlIndex,
passage.controlPoints.size()-1):std::size_t{0};
radius=std::max(radius,qToMicrometres(passage.controlPoints[control].radiusSubvoxels));
}
return std::clamp<std::int64_t>(std::max(routeBoreRadius,radius),
routeBoreRadius,MaximumBoreRadius);
};
const auto fractional=[](std::int64_t value,std::int64_t q16)
{return value*std::clamp<std::int64_t>(q16,0,65536)/65536;};
struct FRoomNode
{
std::string id;
FPoint point{};
std::string floorProfileStableId;
std::int64_t floorProfileDistanceMicrometres=-1;
std::array<std::int32_t,2> floorGradientQ16{};
std::int64_t radiusMicrometres=0;
bool destination=false;
};
std::vector<FRoomNode> nodes;nodes.reserve(nodeDistances.size());
const std::int64_t floorCutMinimum=clampQ16(settings.roomFloorCutMinimumQ16);
const std::int64_t floorCutMaximum=std::max(floorCutMinimum,
clampQ16(settings.roomFloorCutMaximumQ16));
const std::int64_t floorRange=floorCutMaximum-floorCutMinimum;
const std::int64_t heightRatio=std::clamp<std::int64_t>(
settings.roomHeightRatioQ16,6554,65536);
const std::int64_t baseRoomSpan=settings.maximumRoomRadiusMicrometres
-settings.minimumRoomRadiusMicrometres;
const std::int64_t mainRouteMidpoint=totalRouteLength/2;
for(std::size_t nodeIndex=0;nodeIndex<nodeDistances.size();++nodeIndex)
{
const std::int64_t distance=nodeDistances[nodeIndex];
FRoomNode node;node.point=pointAt(distance);
node.floorProfileStableId=backboneFloorProfileId;
node.floorProfileDistanceMicrometres=distance;
node.floorGradientQ16=routeFloorGradientAt(distance);
node.destination=distance==mainRouteMidpoint;
node.id="tunnel-network/strate-"+std::to_string(strateIndex)+"/room-"
+std::to_string(nodeIndex);
const std::uint64_t hash=LayoutStampSeed(seed,strateIndex,node.id);
node.radiusMicrometres=node.destination?settings.destinationRadiusMicrometres
:settings.minimumRoomRadiusMicrometres
+static_cast<std::int64_t>(hash%static_cast<std::uint64_t>(baseRoomSpan+1));
nodes.push_back(std::move(node));
}
const auto makeCompound=[&](const FRoomNode& node,bool destination,
std::vector<ResolvedCavernRoomV1>& outRooms)
{
const std::uint64_t hash=LayoutStampSeed(seed,strateIndex,node.id+"/compound");
const std::int64_t variety=clampQ16(settings.roomShapeVarietyQ16);
const std::size_t additionalPrimitiveCount=static_cast<std::size_t>(
variety*2/65536);
const std::size_t primitiveCount=2+static_cast<std::size_t>(
hash%(additionalPrimitiveCount+1));
std::uint64_t state=hash;
const std::int64_t radius=node.radiusMicrometres;
const std::int64_t floorCutBase=clampQ16(settings.roomFloorCutMinimumQ16);
const std::int64_t componentCount=static_cast<std::int64_t>(primitiveCount);
for(std::int64_t component=0;component<componentCount;++component)
{
const std::string stableId=node.id+"/primitive-"+std::to_string(component);
const std::uint64_t componentHash=LayoutStampNext(state);
const std::int64_t componentScaleQ16=component==0?65536
:42000+static_cast<std::int64_t>(componentHash%22001);
const std::int64_t componentRadius=std::max<std::int64_t>(500000,
fractional(radius,componentScaleQ16));
const std::int64_t axisScaleQ16=component==0?65536
:47000+static_cast<std::int64_t>((componentHash>>8)%19001);
const bool swapAxes=((componentHash>>24)&1u)!=0;
const std::int64_t halfX=swapAxes?fractional(componentRadius,axisScaleQ16)
:componentRadius;
const std::int64_t halfY=swapAxes?componentRadius
:fractional(componentRadius,axisScaleQ16);
const std::int64_t halfZ=std::max<std::int64_t>(500000,
fractional(componentRadius,heightRatio));
const std::int64_t floorCut=floorCutBase
+(floorRange==0?0:static_cast<std::int64_t>(componentHash%(
static_cast<std::uint64_t>(floorRange+1))));
const std::int64_t offsetMagnitude=component==0?0
:fractional(radius,12000+static_cast<std::int64_t>(componentHash%11001));
const std::int64_t routeLengthXY=std::max<std::int64_t>(1,
static_cast<std::int64_t>(integerSqrt(static_cast<std::uint64_t>(
pathAxisX*pathAxisX+pathAxisY*pathAxisY))));
const std::int64_t centerOffset=(component%2==0?1:-1)*offsetMagnitude;
ResolvedCavernRoomV1 room;room.stableId=stableId;
room.strateIndex=strateIndex;
room.floorProfileStableId=node.floorProfileStableId;
room.floorProfileDistanceMicrometres=node.floorProfileDistanceMicrometres;
room.floorGradientQ16=node.floorGradientQ16;
room.halfExtentsMicrometres={halfX,halfY,halfZ};
room.centerMicrometres={node.point[0]+(-pathAxisY)*centerOffset/routeLengthXY,
node.point[1]+pathAxisX*centerOffset/routeLengthXY,
node.point[2]+fractional(halfZ,floorCut)};
if(component>0)
{
const std::int64_t headroom=std::max<std::int64_t>(0,
halfZ-fractional(halfZ,floorCut));
const std::int64_t jitterRange=std::min(headroom,halfZ/5);
const std::int64_t ceilingJitter=jitterRange>0
?static_cast<std::int64_t>((componentHash>>32)
%static_cast<std::uint64_t>(jitterRange+1)):0;
room.centerMicrometres[2]+=ceilingJitter;
}
room.floorMicrometres=node.point[2]
-(static_cast<std::int64_t>(room.floorGradientQ16[0])
*(room.centerMicrometres[0]-node.point[0])
+static_cast<std::int64_t>(room.floorGradientQ16[1])
*(room.centerMicrometres[1]-node.point[1]))/65536;
room.smoothWidthMicrometres=smooth;
room.irregularityAmplitudeMicrometres=irregularity;
room.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
outRooms.push_back(std::move(room));
}
(void)destination;
};
for(const auto& node:nodes)makeCompound(node,node.destination,rooms);
const auto componentRoomId=[](const FRoomNode& node)
{return node.id+"/primitive-0";};
const auto makeEdge=[&](std::string id,std::string startAnchor,
const FPoint& start,const FRoomNode& endNode,std::int64_t boreRadius,
std::int64_t profileStartDistance,std::int64_t profileEndDistance)
{
ResolvedCavernEdgeV1 edge;edge.stableId=std::move(id);
edge.startAnchorStableId=std::move(startAnchor);
edge.endRoomStableId=componentRoomId(endNode);edge.strateIndex=strateIndex;
edge.boreRadiusMicrometres=boreRadius;
edge.boreHeightMicrometres=std::max(PlannedBoreHeight,MinimumBoreHeight);
edge.floorSupportDepthMicrometres=settings.floorSupportDepthMicrometres;
if(profileStartDistance>=0&&profileEndDistance>=0)
{
edge.floorProfileStableId=backboneFloorProfileId;
edge.floorProfileStartDistanceMicrometres=profileStartDistance;
edge.floorProfileEndDistanceMicrometres=profileEndDistance;
}
edge.smoothWidthMicrometres=smooth;
edge.irregularityAmplitudeMicrometres=irregularity;
edge.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
const FPoint end={endNode.point[0],endNode.point[1],endNode.point[2]};
for(std::size_t axis=0;axis<3;++axis)
{
const std::int64_t delta=end[axis]-start[axis];
edge.controlPointsMicrometres[0][axis]=start[axis];
edge.controlPointsMicrometres[1][axis]=start[axis]+delta/3;
edge.controlPointsMicrometres[2][axis]=start[axis]+(delta*2)/3;
edge.controlPointsMicrometres[3][axis]=end[axis];
}
if(profileStartDistance>=0&&profileEndDistance>=0)
{
const std::int64_t floorStart=routeFloorAt(profileStartDistance);
const std::int64_t floorEnd=routeFloorAt(profileEndDistance);
const std::int64_t floorDelta=floorEnd-floorStart;
edge.controlPointsMicrometres[0][2]=floorStart;
edge.controlPointsMicrometres[1][2]=floorStart+floorDelta/3;
edge.controlPointsMicrometres[2][2]=floorStart+(floorDelta*2)/3;
edge.controlPointsMicrometres[3][2]=floorEnd;
}
return edge;
};
const auto arrivalPoint=routeStart;
FPoint previousPoint=arrivalPoint;
std::int64_t previousProfileDistance=0;
std::string previousAnchor=arrivalPassage->stableId+"/lower-mouth";
for(std::size_t nodeIndex=0;nodeIndex<nodes.size();++nodeIndex)
{
const auto& node=nodes[nodeIndex];
const bool portal=nodeIndex==0;
const std::string edgeId="tunnel-network/strate-"+std::to_string(strateIndex)
+"/route-edge-"+std::to_string(nodeIndex);
edges.push_back(makeEdge(edgeId,previousAnchor,previousPoint,node,
portal?passageBoreRadius(*arrivalPassage,true):selectedBoreRadius(edgeId),
previousProfileDistance,node.floorProfileDistanceMicrometres));
previousPoint={node.point[0],node.point[1],node.point[2]};
previousProfileDistance=node.floorProfileDistanceMicrometres;
previousAnchor=componentRoomId(node);
}
if(!nodes.empty())
{
const std::string departureEdgeId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/departure-edge";
edges.push_back(makeEdge(departureEdgeId,departurePassage->stableId+"/upper-mouth",
routeEnd,nodes.back(),passageBoreRadius(*departurePassage,false),
totalRouteLength,nodes.back().floorProfileDistanceMicrometres));
}
const std::int64_t roomDensity=clampQ16(settings.roomDensityQ16);
const std::int64_t tunnelDensity=clampQ16(settings.tunnelDensityQ16);
const std::int64_t branchThreshold=roomDensity*tunnelDensity/65536;
const std::size_t branchLimit=settings.originRoomMaximumConnections<=0
?32u:static_cast<std::size_t>(std::clamp(settings.originRoomMaximumConnections,0,32));
std::size_t branchCount=0;
for(std::size_t nodeIndex=1;nodeIndex+1<nodes.size()
&&branchCount<branchLimit&&rooms.size()<2048;++nodeIndex)
{
const auto& parent=nodes[nodeIndex];
const std::uint64_t branchHash=LayoutStampSeed(seed,strateIndex,
parent.id+"/side-branch");
if(static_cast<std::int64_t>(branchHash&0xffffu)>=branchThreshold)continue;
const std::int64_t branchLength=std::max<std::int64_t>(
routeBoreRadius*3,std::min(spacing/2,settings.maximumTunnelLengthMicrometres/2));
const std::int64_t direction=(branchHash&0x10000u)?1:-1;
FRoomNode branch;branch.id=parent.id+"/side-"+std::to_string(branchCount);
branch.radiusMicrometres=settings.minimumRoomRadiusMicrometres
+static_cast<std::int64_t>((branchHash>>17)
%static_cast<std::uint64_t>(baseRoomSpan+1));
const std::int64_t pathAxisLength=std::max<std::int64_t>(1,
static_cast<std::int64_t>(integerSqrt(static_cast<std::uint64_t>(
pathAxisX*pathAxisX+pathAxisY*pathAxisY))));
branch.point={parent.point[0]+(-pathAxisY)*branchLength/pathAxisLength*direction,
parent.point[1]+pathAxisX*branchLength/pathAxisLength*direction,
parent.point[2]};
branch.floorProfileStableId=parent.floorProfileStableId;
branch.floorProfileDistanceMicrometres=parent.floorProfileDistanceMicrometres;
branch.floorGradientQ16=parent.floorGradientQ16;
makeCompound(branch,false,rooms);
const std::string branchEdgeId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/side-edge-"+std::to_string(branchCount);
ResolvedCavernEdgeV1 branchEdge;
if(settings.tunnelsFlowTowardOrigin)
branchEdge=makeEdge(branchEdgeId,componentRoomId(branch),
branch.point,parent,selectedBoreRadius(branchEdgeId),-1,-1);
else
branchEdge=makeEdge(branchEdgeId,componentRoomId(parent),
parent.point,branch,selectedBoreRadius(branchEdgeId),-1,-1);
// Side branches retain their cave floor SDF, but they do not cap the
// descending backbone when their tubes overlap it near a junction.
branchEdge.floorSupportDepthMicrometres=0;
edges.push_back(std::move(branchEdge));
++branchCount;
}
if(nodes.empty()||rooms.empty()||edges.empty())
{error="cavern planner emitted an empty arrival-to-departure graph";return false;}
double maximumGradeDegrees=0.0;
for(const auto& edge:edges)
{
const auto& a=edge.controlPointsMicrometres.front();
const auto& b=edge.controlPointsMicrometres.back();
const std::int64_t run=static_cast<std::int64_t>(integerSqrt(
static_cast<std::uint64_t>((b[0]-a[0])*(b[0]-a[0])
+(b[1]-a[1])*(b[1]-a[1]))));
const std::int64_t rise=std::abs(b[2]-a[2]);
if(run<=0&&rise>0)
{error="cavern planner produced a vertical route edge with no walking run";return false;}
if(run>0&&rise*MaximumWalkGradeDenominator>run)
{error="cavern planner emitted an edge beyond its 1:5 route grade budget";return false;}
if(run>0)maximumGradeDegrees=std::max(maximumGradeDegrees,
std::atan2(static_cast<double>(rise),static_cast<double>(run))
*180.0/3.14159265358979323846);
}
std::printf("[CavernPlannerV1] strate=%d seed=%d mouths=%s->%s route_nodes=%zu room_primitives=%zu edges=%zu branches=%zu route_horizontal_m=%.3f floor_delta_m=%.3f max_grade_deg=%.3f grade_limit_deg=11.310 clearance_radius_m=%.3f clearance_height_m=%.3f destination_radius_m=%.3f status=PASS\n",
strateIndex,seed,arrivalPassage->stableId.c_str(),departurePassage->stableId.c_str(),
nodes.size(),rooms.size(),edges.size(),branchCount,totalRouteLength/1.0e6,
std::abs(dz)/1.0e6,maximumGradeDegrees,routeBoreRadius/1.0e6,PlannedBoreHeight/1.0e6,
settings.destinationRadiusMicrometres/1.0e6);
return true;
}
bool ResolveResolvedLayoutOreVeinsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed, bool ResolveResolvedLayoutOreVeinsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed,
std::span<const ResolvedLayoutOreVeinRequestV1> requests,std::string& error) std::span<const ResolvedLayoutOreVeinRequestV1> requests,std::string& error)
{ {
+272 -126
View File
@@ -33,50 +33,41 @@
#include <variant> #include <variant>
#include <vector> #include <vector>
// L88-only composition split. These switches let the timing harness remove passage work and static bool VF_IsDiagnosticEnvSwitchEnabled(const char* Name)
// resolved boundary-band work independently. They are diagnostic only, never world settings.
static bool VF_IsL88DiagnosticSwitchEnabled(const char* Name)
{ {
const char* Value=std::getenv(Name); const char* Value=std::getenv(Name);
return Value&&Value[0]=='1'&&Value[1]=='\0'; return Value&&Value[0]=='1'&&Value[1]=='\0';
} }
static bool VF_IsL88PassageCompositionDisabled()
{
static const bool bDisabled=VF_IsL88DiagnosticSwitchEnabled(
"VOXELFORGE_L88_DISABLE_PASSAGES")
||VF_IsL88DiagnosticSwitchEnabled("VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION");
return bDisabled;
}
static bool VF_IsL88BoundaryBandsDisabled()
{
static const bool bDisabled=VF_IsL88DiagnosticSwitchEnabled(
"VOXELFORGE_L88_DISABLE_BOUNDARY_BANDS")
||VF_IsL88DiagnosticSwitchEnabled("VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION");
return bDisabled;
}
static bool VF_IsL88AllowPassageAblationWalkabilityFailure()
{
static const bool bEnabled=VF_IsL88DiagnosticSwitchEnabled(
"VOXELFORGE_L88_ALLOW_PASSAGE_ABLATION_WALKABILITY_FAILURE");
return bEnabled;
}
static bool VF_IsL98PassagePrepareTraceEnabled() static bool VF_IsL98PassagePrepareTraceEnabled()
{ {
static const bool bEnabled=VF_IsL88DiagnosticSwitchEnabled( static const bool bEnabled=VF_IsDiagnosticEnvSwitchEnabled(
"VOXELFORGE_L98_PASSAGE_PREP_TRACE"); "VOXELFORGE_L98_PASSAGE_PREP_TRACE");
return bEnabled; return bEnabled;
} }
static double VF_L93PassageCarveReachMetres() static bool VF_IsL130PassageReachProofAuditEnabled()
{ {
// The generated world uses a fixed bounded tube influence. An explicit diagnostic static const bool bEnabled=VF_IsDiagnosticEnvSwitchEnabled(
// option can still select the legacy point-evaluation path for comparisons. "VOXELFORGE_L130_PASSAGE_REACH_PROOF_AUDIT");
return 4.0; return bEnabled;
}
static constexpr double VF_PassageShellThicknessMetres=0.75;
static constexpr double VF_PassageWallReliefMetres=0.21;
static constexpr double VF_PassageCornerBlendMetres=0.50;
static constexpr double VF_PassageReachMarginMetres=0.01;
// Radial support in metres: shell + maximum relief + the smooth-max support
// (corner-blend width / 6) + a 1 cm guard for float and bound rounding.
static constexpr double VF_PassageInfluenceReachMetres=
VF_PassageShellThicknessMetres+VF_PassageWallReliefMetres
+VF_PassageCornerBlendMetres/6.0+VF_PassageReachMarginMetres;
static double VF_PassageInfluenceReachCoreVoxels(
std::uint32_t coreVoxelSizeMicrometres)
{
return VF_PassageInfluenceReachMetres*1000000.0
/std::max<std::uint32_t>(1,coreVoxelSizeMicrometres);
} }
enum class EPassageDensityQueryCaller : std::size_t enum class EPassageDensityQueryCaller : std::size_t
@@ -306,22 +297,18 @@ public:
profileStats_(profileStats),profileLod_(profileLod), profileStats_(profileStats),profileLod_(profileLod),
passageCarveReachMetres_(std::isfinite(passageCarveReachMetres) passageCarveReachMetres_(std::isfinite(passageCarveReachMetres)
&&passageCarveReachMetres>=0.0?passageCarveReachMetres &&passageCarveReachMetres>=0.0?passageCarveReachMetres
:VF_L93PassageCarveReachMetres()) :VF_PassageInfluenceReachMetres)
{ {
mayContainResolvedBoundaryBand_=shape.ResolvedBoundaryBandsIntersectSubvoxelRange( mayContainResolvedBoundaryBand_=shape.ResolvedBoundaryBandsIntersectSubvoxelRange(
minimumSubvoxels_[2],maximumSubvoxels_[2]); minimumSubvoxels_[2],maximumSubvoxels_[2]);
const bool disablePassageComposition=VF_IsL88PassageCompositionDisabled(); mayContainTransitionPassageFade_=shape.ResolvedTransitionPassageFadeIntersectsTileXY(
if(VF_IsL88BoundaryBandsDisabled())mayContainResolvedBoundaryBand_=false; minimumSubvoxels_,maximumSubvoxels_);
mayContainTransitionPassageFade_=!disablePassageComposition featureIndices_=shape.BuildPassageFeatureTileList(
&&shape.ResolvedTransitionPassageFadeIntersectsTileXY( minimumSubvoxels_,maximumSubvoxels_);
minimumSubvoxels_,maximumSubvoxels_);
if(!disablePassageComposition)
featureIndices_=shape.BuildPassageFeatureTileList(
minimumSubvoxels_,maximumSubvoxels_);
stampIndices_=shape.BuildStampTileList(minimumSubvoxels_,maximumSubvoxels_); stampIndices_=shape.BuildStampTileList(minimumSubvoxels_,maximumSubvoxels_);
cavernFeatureIndices_=shape.BuildCavernFeatureTileList( cavernFeatureIndices_=shape.BuildCavernFeatureTileList(
minimumSubvoxels_,maximumSubvoxels_); minimumSubvoxels_,maximumSubvoxels_);
passageRoutes_.assign(shape.ResolvedLayout&&!disablePassageComposition passageRoutes_.assign(shape.ResolvedLayout
?shape.ResolvedLayout->passages.size():0,0); ?shape.ResolvedLayout->passages.size():0,0);
passageRouteSegments_.resize(passageRoutes_.size()); passageRouteSegments_.resize(passageRoutes_.size());
for(const std::uint32_t featureIndex:featureIndices_) for(const std::uint32_t featureIndex:featureIndices_)
@@ -1204,8 +1191,8 @@ public:
if(!ResolvedLayout||ResolvedLayout->passages.empty()||!std::isfinite(X) if(!ResolvedLayout||ResolvedLayout->passages.empty()||!std::isfinite(X)
||!std::isfinite(Y)||!std::isfinite(Z))return density; ||!std::isfinite(Y)||!std::isfinite(Z))return density;
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel); constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
constexpr double FloorShellThicknessMetres=0.75; constexpr double FloorShellThicknessMetres=VF_PassageShellThicknessMetres;
constexpr double WallReliefMetres=0.21; constexpr double WallReliefMetres=VF_PassageWallReliefMetres;
constexpr double OpenAirSideWallHeightMetres=1.0; constexpr double OpenAirSideWallHeightMetres=1.0;
constexpr double MouthShellBlendMetres=5.0; constexpr double MouthShellBlendMetres=5.0;
const double qPerMetre=Q*1000000.0/ const double qPerMetre=Q*1000000.0/
@@ -1234,16 +1221,15 @@ public:
double unionDistance=std::numeric_limits<double>::infinity(); double unionDistance=std::numeric_limits<double>::infinity();
double unionShellDistance=std::numeric_limits<double>::infinity(); double unionShellDistance=std::numeric_limits<double>::infinity();
const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0); const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
const auto* queryStratum=FindBoundStratumForManagerZ(Z);
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size(); for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
++passageIndex) ++passageIndex)
{ {
const auto& passage=ResolvedLayout->passages[passageIndex]; const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue; if(passage.controlPoints.size()<2)continue;
if(queryStratum&&queryStratum->Resolved // Native graph fields own their local rooms and tunnels, not the
&&queryStratum->Resolved->passageCapabilities.hasNativeCaveField // inter-strate connection above their mouth. Union the shared passage
&&passage.lowerStrateIndex==queryStratum->StrateIndex) // field here as well so the connector remains open through the boundary
continue; // band for both native caves and future native lattice layers.
if(passageProfile)++passageProfile->passageIterations; if(passageProfile)++passageProfile->passageIterations;
if(passageTileScopeMatches&&!passageTileQueries->hasRouteFeature(passageIndex)) if(passageTileScopeMatches&&!passageTileQueries->hasRouteFeature(passageIndex))
continue; continue;
@@ -1527,13 +1513,13 @@ public:
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
{ {
float graphDensity=0.0f; float graphDensity=0.0f;
float nativeLatticeFloorSupport=std::numeric_limits<float>::lowest(); float nativeGraphFloorSupport=std::numeric_limits<float>::lowest();
{ {
VoxelForgeCore::Linux77Profiler::FScopedPhase l77GraphPhase( VoxelForgeCore::Linux77Profiler::FScopedPhase l77GraphPhase(
VoxelForgeCore::Linux77Profiler::EPhase::CompositionGraph); VoxelForgeCore::Linux77Profiler::EPhase::CompositionGraph);
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerCavern); FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerCavern);
graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z, graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z,
&nativeLatticeFloorSupport); &nativeGraphFloorSupport);
} }
const auto* bound=FindBoundStratumForManagerZ(Z); const auto* bound=FindBoundStratumForManagerZ(Z);
const bool nativeLattice=bound&&bound->Resolved const bool nativeLattice=bound&&bound->Resolved
@@ -1544,12 +1530,8 @@ public:
// the generic stratum transition distance creates a discontinuity at a // the generic stratum transition distance creates a discontinuity at a
// mouth that crosses a wide transition band. Keep that opt-in field whole; // mouth that crosses a wide transition band. Keep that opt-in field whole;
// its host-rock value already supplies the ceiling and bedrock. // its host-rock value already supplies the ceiling and bedrock.
const bool disablePassageComposition=!nativeLattice
&&VF_IsL88PassageCompositionDisabled();
const bool disableBoundaryBands=!nativeLattice
&&VF_IsL88BoundaryBandsDisabled();
float boundary=graphDensity; float boundary=graphDensity;
if(!nativeLattice&&!disableBoundaryBands) if(!nativeLattice)
{ {
VoxelForgeCore::Linux77Profiler::FScopedPhase l77BandsPhase( VoxelForgeCore::Linux77Profiler::FScopedPhase l77BandsPhase(
VoxelForgeCore::Linux77Profiler::EPhase::Bands); VoxelForgeCore::Linux77Profiler::EPhase::Bands);
@@ -1578,7 +1560,6 @@ public:
?bound->Resolved->passageCapabilities.hasNativeCaveField ?bound->Resolved->passageCapabilities.hasNativeCaveField
:bound->Resolved->passageCapabilities.hasNativeLatticeField)) :bound->Resolved->passageCapabilities.hasNativeLatticeField))
{ {
if(!disablePassageComposition)
{ {
VoxelForgeCore::Linux77Profiler::FScopedPhase l77PassagePhase( VoxelForgeCore::Linux77Profiler::FScopedPhase l77PassagePhase(
VoxelForgeCore::Linux77Profiler::EPhase::Passage); VoxelForgeCore::Linux77Profiler::EPhase::Passage);
@@ -1592,7 +1573,7 @@ public:
const float nativeTunnelFieldDensity=nativeTunnelField const float nativeTunnelFieldDensity=nativeTunnelField
?graphDensity:std::numeric_limits<float>::lowest(); ?graphDensity:std::numeric_limits<float>::lowest();
stamped=ApplyResolvedStratePassageCarves(stamped,X,Y,Z, stamped=ApplyResolvedStratePassageCarves(stamped,X,Y,Z,
nativeTunnelFieldDensity,nativeLatticeFloorSupport); nativeTunnelFieldDensity,nativeGraphFloorSupport);
} }
if(bTraceTnComposition) if(bTraceTnComposition)
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g native=1\n", std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g native=1\n",
@@ -1609,7 +1590,6 @@ public:
ELegacyFieldTerm::ComposerPassage); ELegacyFieldTerm::ComposerPassage);
final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z); final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z);
} }
if(!disablePassageComposition)
{ {
VoxelForgeCore::Linux77Profiler::FScopedPhase l77PassagePhase( VoxelForgeCore::Linux77Profiler::FScopedPhase l77PassagePhase(
VoxelForgeCore::Linux77Profiler::EPhase::Passage); VoxelForgeCore::Linux77Profiler::EPhase::Passage);
@@ -1630,8 +1610,8 @@ public:
{ {
constexpr double Q=static_cast<double>( constexpr double Q=static_cast<double>(
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel); VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double qPerMetre=Q*1000000.0/ const double reachCoreVoxels=VF_PassageInfluenceReachCoreVoxels(
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres); ResolvedLayout->coreVoxelSizeMicrometres);
const auto overlaps=[&](const double low[3],const double high[3]) const auto overlaps=[&](const double low[3],const double high[3])
{ {
for(int axis=0;axis<3;++axis) for(int axis=0;axis<3;++axis)
@@ -1648,7 +1628,8 @@ public:
const auto& p3=passage.controlPoints[ const auto& p3=passage.controlPoints[
std::min(segment+2,passage.controlPoints.size()-1)]; std::min(segment+2,passage.controlPoints.size()-1)];
const double radius=std::max(static_cast<double>(a.radiusSubvoxels), const double radius=std::max(static_cast<double>(a.radiusSubvoxels),
static_cast<double>(b.radiusSubvoxels))/Q+1.0; static_cast<double>(b.radiusSubvoxels))/Q
+reachCoreVoxels+0.01;
double low[3]{},high[3]{}; double low[3]{},high[3]{};
for(int axis=0;axis<3;++axis) for(int axis=0;axis<3;++axis)
{ {
@@ -2314,18 +2295,8 @@ public:
const bool bPassageRoutesValid=!bValidatePassageRoutes||ValidatePassageRoutes(); const bool bPassageRoutesValid=!bValidatePassageRoutes||ValidatePassageRoutes();
if(!bPassageRoutesValid) if(!bPassageRoutesValid)
{ {
if(VF_IsL88PassageCompositionDisabled() Error="compiled shape world contains a solid passage centreline sample";
&&!VF_IsL88BoundaryBandsDisabled() return false;
&&VF_IsL88AllowPassageAblationWalkabilityFailure())
{
std::printf("[L88PassageAblation] route walkability failed as expected; "
"continuing only to measure passage-off/bands-on readiness\n");
}
else
{
Error="compiled shape world contains a solid passage centreline sample";
return false;
}
} }
Stats.PassageValidationSeconds=std::chrono::duration<double>(Clock::now()-Substage).count(); Stats.PassageValidationSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
Built=true; Built=true;
@@ -3499,7 +3470,7 @@ private:
double centerZSubvoxels,double queryZSubvoxels,double floorDepthSubvoxels, double centerZSubvoxels,double queryZSubvoxels,double floorDepthSubvoxels,
double qPerMetre) double qPerMetre)
{ {
constexpr double CornerBlendMetres=0.50; constexpr double CornerBlendMetres=VF_PassageCornerBlendMetres;
const float floorDistance=static_cast<float>(centerZSubvoxels const float floorDistance=static_cast<float>(centerZSubvoxels
-floorDepthSubvoxels-queryZSubvoxels); -floorDepthSubvoxels-queryZSubvoxels);
return SmoothMaxDistance(roundDistanceSubvoxels,floorDistance, return SmoothMaxDistance(roundDistanceSubvoxels,floorDistance,
@@ -3612,6 +3583,7 @@ private:
double minimumFloorMetres=std::numeric_limits<double>::infinity(); double minimumFloorMetres=std::numeric_limits<double>::infinity();
double maximumFloorMetres=-std::numeric_limits<double>::infinity(); double maximumFloorMetres=-std::numeric_limits<double>::infinity();
double maximumSegmentGrade=0.0; double maximumSegmentGrade=0.0;
std::array<double,2> floorGradient{};
std::array<float,3> centerVoxels{},halfExtentsVoxels{}; std::array<float,3> centerVoxels{},halfExtentsVoxels{};
std::array<std::array<float,3>,4> controlPointsVoxels{}; std::array<std::array<float,3>,4> controlPointsVoxels{};
float floorVoxels=0.0f; float floorVoxels=0.0f;
@@ -4040,8 +4012,10 @@ private:
{ {
CavernRuntime.clear();CavernSpatialNodes.clear();CavernSpatialOrder.clear(); CavernRuntime.clear();CavernSpatialNodes.clear();CavernSpatialOrder.clear();
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0 if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0
||ResolvedLayout->cavernGraphVocabularyVersion ||(ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1)return; !=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1
&&ResolvedLayout->cavernGraphVocabularyVersion
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV2))return;
const auto isNativeGraphStrate=[this](std::int32_t strateIndex) const auto isNativeGraphStrate=[this](std::int32_t strateIndex)
{ {
const auto found=std::find_if(ResolvedLayout->strates.begin(), const auto found=std::find_if(ResolvedLayout->strates.begin(),
@@ -4071,6 +4045,8 @@ private:
} }
runtime.floorVoxels=static_cast<float>( runtime.floorVoxels=static_cast<float>(
static_cast<double>(room.floorMicrometres)/micronsPerVoxel); static_cast<double>(room.floorMicrometres)/micronsPerVoxel);
runtime.floorGradient={room.floorGradientQ16[0]/65536.0,
room.floorGradientQ16[1]/65536.0};
runtime.smoothWidthVoxels=static_cast<float>( runtime.smoothWidthVoxels=static_cast<float>(
static_cast<double>(room.smoothWidthMicrometres)/micronsPerVoxel); static_cast<double>(room.smoothWidthMicrometres)/micronsPerVoxel);
runtime.irregularityAmplitudeVoxels=static_cast<float>( runtime.irregularityAmplitudeVoxels=static_cast<float>(
@@ -4811,13 +4787,9 @@ private:
PassageNearestNodes.resize(ResolvedLayout?ResolvedLayout->passages.size():0); PassageNearestNodes.resize(ResolvedLayout?ResolvedLayout->passages.size():0);
if(!ResolvedLayout||ResolvedLayout->passages.empty())return; if(!ResolvedLayout||ResolvedLayout->passages.empty())return;
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel); constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
constexpr double PassageShellMetres=0.75;
constexpr double ReliefMetres=0.21;
constexpr double SweepReachMetres=4.0;
constexpr double CornerBlendMetres=0.50;
constexpr std::size_t CandidateFineSamplesPerSpan=5; constexpr std::size_t CandidateFineSamplesPerSpan=5;
const double requiredReach=std::max(SweepReachMetres+ReliefMetres const double requiredReachCoreVoxels=VF_PassageInfluenceReachCoreVoxels(
+CornerBlendMetres*0.25,PassageShellMetres+ReliefMetres); ResolvedLayout->coreVoxelSizeMicrometres);
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size(); for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
++passageIndex) ++passageIndex)
{ {
@@ -4854,7 +4826,7 @@ private:
routeMinimum[axis]=std::min(routeMinimum[axis],localMinimum[axis]); routeMinimum[axis]=std::min(routeMinimum[axis],localMinimum[axis]);
routeMaximum[axis]=std::max(routeMaximum[axis],localMaximum[axis]); routeMaximum[axis]=std::max(routeMaximum[axis],localMaximum[axis]);
} }
const double localExtent=requiredReach const double localExtent=requiredReachCoreVoxels
+std::max(prepared.radiusA,prepared.radiusB)/Q+0.01; +std::max(prepared.radiusA,prepared.radiusB)/Q+0.01;
FPassageProofBound proof; FPassageProofBound proof;
for(std::size_t axis=0;axis<3;++axis) for(std::size_t axis=0;axis<3;++axis)
@@ -4872,7 +4844,8 @@ private:
std::ceil((localMaximum[axis]+localExtent)*Q)); std::ceil((localMaximum[axis]+localExtent)*Q));
} }
} }
const double routeExtent=maximumRadiusSubvoxels/Q+requiredReach+0.01; const double routeExtent=maximumRadiusSubvoxels/Q
+requiredReachCoreVoxels+0.01;
const double tileExtent=routeExtent; const double tileExtent=routeExtent;
std::array<std::int64_t,3> featureMinimum{},featureMaximum{}; std::array<std::int64_t,3> featureMinimum{},featureMaximum{};
for(std::size_t axis=0;axis<3;++axis) for(std::size_t axis=0;axis<3;++axis)
@@ -4899,6 +4872,113 @@ private:
BuildPassageFeatureNode(0,PassageFeatureOrder.size()); BuildPassageFeatureNode(0,PassageFeatureOrder.size());
if(!PassageProofBounds.empty()) if(!PassageProofBounds.empty())
BuildPassageProofNode(0,PassageProofBounds.size()); BuildPassageProofNode(0,PassageProofBounds.size());
if(VF_IsL130PassageReachProofAuditEnabled())
{
constexpr double Q=static_cast<double>(
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
constexpr std::array<std::array<double,2>,8> RadialDirections={{
{1.0,0.0},{0.7071067811865475,0.7071067811865475},
{0.0,1.0},{-0.7071067811865475,0.7071067811865475},
{-1.0,0.0},{-0.7071067811865475,-0.7071067811865475},
{0.0,-1.0},{0.7071067811865475,-0.7071067811865475}}};
constexpr std::array<double,2> CrestHeightsMetres={0.625,0.875};
const double qPerMetre=Q*1000000.0/
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
const std::array<double,6> shellOffsetsMetres={0.70,0.78,0.86,0.94,
VF_PassageInfluenceReachMetres-0.005,
VF_PassageInfluenceReachMetres+0.005};
std::uint64_t sampled=0,signRelevant=0,proofMisses=0,featureMisses=0;
for(std::size_t passageIndex=0;
passageIndex<PassageSegmentData.size();++passageIndex)
{
const auto& segments=PassageSegmentData[passageIndex];
if(segments.empty())continue;
const auto& constants=PassageComposerConstants[passageIndex];
const std::size_t stride=std::max<std::size_t>(1,
(segments.size()+127)/128);
for(std::size_t segmentIndex=0;segmentIndex<segments.size();
segmentIndex+=stride)
{
const auto& segment=segments[segmentIndex];
if(segment.lengthSq<=1.0e-12)continue;
constexpr double T=0.5;
const double centerX=segment.a[0]+T*segment.delta[0];
const double centerY=segment.a[1]+T*segment.delta[1];
const double centerZ=segment.a[2]+T*segment.delta[2];
const double radius=segment.radiusA+T*segment.radiusDelta;
const double floorDepth=PassageFlatFloorDepthSubvoxels(
constants,segmentIndex,T,radius,qPerMetre);
const double floorCenterZ=PassageFlatFloorCenterZSubvoxels(
constants,segments,segmentIndex,T,segment,centerX,centerY,
floorDepth,qPerMetre);
for(const double crestHeightMetres:CrestHeightsMetres)
{
const double sampleZ=floorCenterZ-floorDepth
+crestHeightMetres*qPerMetre;
const double verticalDelta=sampleZ-centerZ;
for(const double shellOffsetMetres:shellOffsetsMetres)
{
const double radialDistance=radius
+shellOffsetMetres*qPerMetre;
if(radialDistance*radialDistance
<=verticalDelta*verticalDelta)continue;
const double horizontalDistance=std::sqrt(
radialDistance*radialDistance
-verticalDelta*verticalDelta);
for(const auto& direction:RadialDirections)
{
const double sampleQ[3]={
centerX+direction[0]*horizontalDistance,
centerY+direction[1]*horizontalDistance,sampleZ};
const double sampleVoxel[3]={sampleQ[0]/Q,
sampleQ[1]/Q,sampleQ[2]/Q};
const float contribution=ApplyResolvedStratePassageCarves(
0.0f,static_cast<float>(sampleVoxel[0]),
static_cast<float>(sampleVoxel[1]),
static_cast<float>(sampleVoxel[2]));
++sampled;
if(std::abs(contribution)<=1.0e-6f)continue;
++signRelevant;
if(PassageProofNodes.empty()
||!PassageProofBoundsOverlapNode(0,sampleVoxel,
sampleVoxel))
++proofMisses;
std::array<std::int64_t,3> sampleMinimum{},sampleMaximum{};
for(std::size_t axis=0;axis<3;++axis)
{
sampleMinimum[axis]=static_cast<std::int64_t>(
std::floor(sampleQ[axis]));
sampleMaximum[axis]=static_cast<std::int64_t>(
std::ceil(sampleQ[axis]));
}
const auto candidates=BuildPassageFeatureTileList(
sampleMinimum,sampleMaximum);
const bool featureContainsSample=std::any_of(
candidates.begin(),candidates.end(),
[&](std::uint32_t featureIndex)
{
return featureIndex<PassageTileFeatures.size()
&&PassageTileFeatures[featureIndex].passageIndex
==passageIndex;
});
if(!featureContainsSample)++featureMisses;
}
}
}
}
}
const bool auditPassed=sampled>0&&signRelevant>0
&&proofMisses==0&&featureMisses==0;
std::printf("[L130PassageReachProofAudit] reach_m=%.3f sampled=%llu sign_relevant=%llu proof_misses=%llu feature_misses=%llu status=%s\n",
VF_PassageInfluenceReachMetres,
static_cast<unsigned long long>(sampled),
static_cast<unsigned long long>(signRelevant),
static_cast<unsigned long long>(proofMisses),
static_cast<unsigned long long>(featureMisses),
auditPassed?"PASS":"FAIL");
if(!auditPassed)std::abort();
}
} }
bool PassageProofBoundsOverlapNode(int nodeIndex,const double minimum[3], bool PassageProofBoundsOverlapNode(int nodeIndex,const double minimum[3],
@@ -5209,11 +5289,7 @@ public:
FMetricInterval caveVoid{std::numeric_limits<double>::infinity(), FMetricInterval caveVoid{std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity(),true}; std::numeric_limits<double>::infinity(),true};
bool haveFeature=false; bool haveFeature=false;
double latticeFloorSupportLowerBound=std::numeric_limits<double>::infinity(); double nativeFloorSupportLowerBound=std::numeric_limits<double>::infinity();
const auto* resolvedStratum=FindResolvedLayoutStratum(strateIndex);
const bool nativeLattice=resolvedStratum
&&VoxelForgeCore::IsNativeLatticeFieldStratum(*resolvedStratum)
&&resolvedStratum->passageCapabilities.hasNativeLatticeField;
for(const std::uint32_t index:candidates) for(const std::uint32_t index:candidates)
{ {
if(index>=CavernRuntime.size())continue; if(index>=CavernRuntime.size())continue;
@@ -5243,7 +5319,18 @@ public:
(std::sqrt(normalizedMinimumSquared)-1.0)*meanRadius, (std::sqrt(normalizedMinimumSquared)-1.0)*meanRadius,
(std::sqrt(normalizedMaximumSquared)-1.0)*meanRadius,true}; (std::sqrt(normalizedMaximumSquared)-1.0)*meanRadius,true};
const double floorZ=feature.room->floorMicrometres/1000000.0; const double floorZ=feature.room->floorMicrometres/1000000.0;
const FMetricInterval floor={floorZ-hi[2],floorZ-lo[2],true}; const double centerX=feature.room->centerMicrometres[0]/1000000.0;
const double centerY=feature.room->centerMicrometres[1]/1000000.0;
const double gx=feature.room->floorGradientQ16[0]/65536.0;
const double gy=feature.room->floorGradientQ16[1]/65536.0;
const double x0=lo[0]-centerX,x1=hi[0]-centerX;
const double y0=lo[1]-centerY,y1=hi[1]-centerY;
const double gradientMinimum=(gx>=0.0?gx*x0:gx*x1)
+(gy>=0.0?gy*y0:gy*y1);
const double gradientMaximum=(gx>=0.0?gx*x1:gx*x0)
+(gy>=0.0?gy*y1:gy*y0);
const FMetricInterval floor={floorZ-hi[2]+gradientMinimum,
floorZ-lo[2]+gradientMaximum,true};
const double width=feature.room->smoothWidthMicrometres/1000000.0; const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width), featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width),
SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true}; SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true};
@@ -5365,21 +5452,19 @@ public:
SmoothMaxMetres(tube.maximum,floor.maximum,width),true}; SmoothMaxMetres(tube.maximum,floor.maximum,width),true};
} }
else return {0.0,0.0,false}; else return {0.0,0.0,false};
if(nativeLattice) if(feature.edge)
{ {
const double supportDepth=feature.edge const double supportDepth=
?feature.edge->floorSupportDepthMicrometres/1000000.0:0.0; feature.edge->floorSupportDepthMicrometres/1000000.0;
const double floorMinimum=feature.edge const double floorMinimum=feature.minimumFloorMetres;
?feature.minimumFloorMetres:std::numeric_limits<double>::infinity(); const double floorMaximum=feature.maximumFloorMetres;
const double floorMaximum=feature.edge
?feature.maximumFloorMetres:-std::numeric_limits<double>::infinity();
const bool overlapsXY=hi[0]>=feature.boundsMinimumMicrometres[0]/1.0e6 const bool overlapsXY=hi[0]>=feature.boundsMinimumMicrometres[0]/1.0e6
&&lo[0]<=feature.boundsMaximumMicrometres[0]/1.0e6 &&lo[0]<=feature.boundsMaximumMicrometres[0]/1.0e6
&&hi[1]>=feature.boundsMinimumMicrometres[1]/1.0e6 &&hi[1]>=feature.boundsMinimumMicrometres[1]/1.0e6
&&lo[1]<=feature.boundsMaximumMicrometres[1]/1.0e6; &&lo[1]<=feature.boundsMaximumMicrometres[1]/1.0e6;
if(supportDepth>0.0&&overlapsXY if(supportDepth>0.0&&overlapsXY
&&hi[2]>=floorMinimum-supportDepth&&lo[2]<=floorMaximum) &&hi[2]>=floorMinimum-supportDepth&&lo[2]<=floorMaximum)
latticeFloorSupportLowerBound=std::min(latticeFloorSupportLowerBound, nativeFloorSupportLowerBound=std::min(nativeFloorSupportLowerBound,
std::max(-supportDepth,lo[2]-floorMaximum)); std::max(-supportDepth,lo[2]-floorMaximum));
} }
if(!std::isfinite(featureVoid.minimum)||!std::isfinite(featureVoid.maximum)) if(!std::isfinite(featureVoid.minimum)||!std::isfinite(featureVoid.maximum))
@@ -5408,11 +5493,11 @@ public:
result.minimum=std::max(HostRockMetres,-caveVoid.maximum); result.minimum=std::max(HostRockMetres,-caveVoid.maximum);
result.maximum=std::max(HostRockMetres,-caveVoid.minimum); result.maximum=std::max(HostRockMetres,-caveVoid.minimum);
} }
// The final lattice field owns an extra support cap after feature union. // Native graph edges may own a support cap after feature union.
// Lower only the interval minimum when a box could touch that cap; the // Lower only the interval minimum when a box could touch that cap; the
// untouched maximum remains a sound bound when only part of the box does. // untouched maximum remains a sound bound when only part of the box does.
if(std::isfinite(latticeFloorSupportLowerBound)) if(std::isfinite(nativeFloorSupportLowerBound))
result.minimum=std::min(result.minimum,latticeFloorSupportLowerBound); result.minimum=std::min(result.minimum,nativeFloorSupportLowerBound);
// Cover floating-point rounding in interval endpoints and the field's fixed-point // Cover floating-point rounding in interval endpoints and the field's fixed-point
// coordinate conversion while keeping the interval tight compared with one micron. // coordinate conversion while keeping the interval tight compared with one micron.
constexpr double OutwardPaddingMetres=1.0e-6; constexpr double OutwardPaddingMetres=1.0e-6;
@@ -7390,10 +7475,11 @@ private:
candidates=&localCandidates; candidates=&localCandidates;
} }
double caveVoid=std::numeric_limits<double>::infinity(); double caveVoid=std::numeric_limits<double>::infinity();
double latticeFloorSupportMinimum=std::numeric_limits<double>::infinity(); double nativeFloorSupportMinimum=std::numeric_limits<double>::infinity();
const bool nativeLattice=bound.Resolved double floorSupportScore=std::numeric_limits<double>::infinity();
&&VoxelForgeCore::IsNativeLatticeFieldStratum(*bound.Resolved) double floorSupportDepth=0.0,floorSupportFloor=0.0,floorSupportGrade=0.0;
&&bound.Resolved->passageCapabilities.hasNativeLatticeField; const std::string* floorSupportEdgeId=nullptr;
const std::string* floorSupportProfileId=nullptr;
for(const std::uint32_t index:*candidates) for(const std::uint32_t index:*candidates)
{ {
if(index>=CavernRuntime.size())continue; if(index>=CavernRuntime.size())continue;
@@ -7412,8 +7498,19 @@ private:
const double delta=(point[axis]-feature.centerMetres[axis])/radius; const double delta=(point[axis]-feature.centerMetres[axis])/radius;
normalizedSquared+=delta*delta;meanRadius+=radius/3.0; normalizedSquared+=delta*delta;meanRadius+=radius/3.0;
} }
const double ellipsoid=(std::sqrt(normalizedSquared)-1.0)*meanRadius; double ellipsoid=(std::sqrt(normalizedSquared)-1.0)*meanRadius;
const double floor=feature.room->floorMicrometres/1000000.0-point[2]; if(feature.irregularityAmplitudeVoxels>0.0f)
{
const float fade=SmoothUnit((Z-feature.floorVoxels)/0.8f);
ellipsoid+=StampValueNoise(
X-feature.centerVoxels[0],Y-feature.centerVoxels[1],
Z-feature.centerVoxels[2],
feature.irregularityWavelengthVoxels,feature.irregularitySeed)
*feature.irregularityAmplitudeVoxels*metresPerVoxel*fade;
}
const double floor=feature.room->floorMicrometres/1000000.0-point[2]
+feature.floorGradient[0]*(point[0]-feature.centerMetres[0])
+feature.floorGradient[1]*(point[1]-feature.centerMetres[1]);
const double width=feature.room->smoothWidthMicrometres/1000000.0; const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(ellipsoid,floor,width); featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
} }
@@ -7450,7 +7547,17 @@ private:
} }
const double normalized=std::sqrt(radialSquared/(radius*radius) const double normalized=std::sqrt(radialSquared/(radius*radius)
+vertical*vertical/(height*height)); +vertical*vertical/(height*height));
const double distance=(normalized-1.0)*std::min(radius,height); double distance=(normalized-1.0)*std::min(radius,height);
if(feature.irregularityAmplitudeVoxels>0.0f)
{
const float floorVoxels=static_cast<float>(nearestFloor/metresPerVoxel);
const float fade=SmoothUnit((Z-floorVoxels)/0.8f);
distance+=StampValueNoise(
X-feature.controlPointsVoxels[0][0],
Y-feature.controlPointsVoxels[0][1],Z-floorVoxels,
feature.irregularityWavelengthVoxels,feature.irregularitySeed)
*feature.irregularityAmplitudeVoxels*metresPerVoxel*fade;
}
const bool clearlyCloser=distance<tube-SegmentDistanceTieToleranceMetres; const bool clearlyCloser=distance<tube-SegmentDistanceTieToleranceMetres;
const bool stableTie=std::abs(distance-tube) const bool stableTie=std::abs(distance-tube)
<=SegmentDistanceTieToleranceMetres&&segmentIndex<nearestSegment; <=SegmentDistanceTieToleranceMetres&&segmentIndex<nearestSegment;
@@ -7504,11 +7611,47 @@ private:
} }
else for(std::uint32_t segment=0;segment<feature.segments.size();++segment) else for(std::uint32_t segment=0;segment<feature.segments.size();++segment)
evaluateSegment(segment); evaluateSegment(segment);
const double supportDepth=feature.edge->floorSupportDepthMicrometres/1000000.0; const double candidateSupportDepth=
if(nativeLattice&&supportDepth>0.0&&nearestRadial<radius-0.12 feature.edge->floorSupportDepthMicrometres/1000000.0;
&&point[2]<nearestFloor&&nearestFloor-point[2]<=supportDepth) if(candidateSupportDepth>0.0&&nearestRadial<radius-0.12
latticeFloorSupportMinimum=std::min(latticeFloorSupportMinimum, &&std::abs(point[2]-nearestFloor)<=height)
(point[2]-nearestFloor)/std::sqrt(1.0+nearestGrade*nearestGrade)); {
const double deltaZ=point[2]-nearestFloor;
const double candidateScore=nearestRadial*nearestRadial+deltaZ*deltaZ;
if(!feature.edge->floorProfileStableId.empty())
{
const bool sameProfile=floorSupportProfileId
&&*floorSupportProfileId==feature.edge->floorProfileStableId;
if(!floorSupportProfileId||sameProfile)
{
// Backbone edges share h(s). At a geometric joint their
// cave volumes overlap, so the backing top is the lower
// of the local floors, never the closer higher segment.
if(!floorSupportProfileId||nearestFloor<floorSupportFloor
||(nearestFloor==floorSupportFloor
&&feature.edge->stableId<*floorSupportEdgeId))
{
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
}
floorSupportProfileId=&feature.edge->floorProfileStableId;
}
}
else if(!floorSupportProfileId
&&(candidateScore<floorSupportScore
||(candidateScore==floorSupportScore
&&(!floorSupportEdgeId
||feature.edge->stableId<*floorSupportEdgeId))))
{
floorSupportScore=candidateScore;
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
}
}
const double floor=(nearestFloor-point[2]) const double floor=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade); /std::sqrt(1.0+nearestGrade*nearestGrade);
const double width=feature.edge->smoothWidthMicrometres/1000000.0; const double width=feature.edge->smoothWidthMicrometres/1000000.0;
@@ -7525,6 +7668,10 @@ private:
?feature.edge->unionWidthMicrometres ?feature.edge->unionWidthMicrometres
:feature.edge->smoothWidthMicrometres)/1000000.0); :feature.edge->smoothWidthMicrometres)/1000000.0);
} }
if(std::isfinite(floorSupportScore)&&point[2]<floorSupportFloor
&&floorSupportFloor-point[2]<=floorSupportDepth)
nativeFloorSupportMinimum=(point[2]-floorSupportFloor)
/std::sqrt(1.0+floorSupportGrade*floorSupportGrade);
// A versioned native caves recipe owns a solid host-rock field. The // A versioned native caves recipe owns a solid host-rock field. The
// resolved interval oracle uses the same finite -32 m solid value; using // resolved interval oracle uses the same finite -32 m solid value; using
// the underlying legacy generator density here would leak its unrelated // the underlying legacy generator density here would leak its unrelated
@@ -7533,13 +7680,13 @@ private:
const double hostDistance=HostRockMetres; const double hostDistance=HostRockMetres;
double composed=std::isfinite(caveVoid) double composed=std::isfinite(caveVoid)
?SmoothMaxMetres(hostDistance,-caveVoid,0.0):hostDistance; ?SmoothMaxMetres(hostDistance,-caveVoid,0.0):hostDistance;
// Keep the floor support owned by the lattice route after smooth-unioning // Preserve each native graph edge's planned floor backing after smooth
// neighboring room and corridor voids. This cap is limited to each record's // unions. The record's finite depth keeps independent routes from sealing
// explicit support depth so separated helix turns remain independent. // one another when they pass at different elevations.
if(std::isfinite(latticeFloorSupportMinimum)) if(std::isfinite(nativeFloorSupportMinimum))
composed=std::min(composed,latticeFloorSupportMinimum); composed=std::min(composed,nativeFloorSupportMinimum);
if(nativeFloorSupport&&std::isfinite(latticeFloorSupportMinimum)) if(nativeFloorSupport&&std::isfinite(nativeFloorSupportMinimum))
*nativeFloorSupport=static_cast<float>(latticeFloorSupportMinimum/metresPerVoxel); *nativeFloorSupport=static_cast<float>(nativeFloorSupportMinimum/metresPerVoxel);
return static_cast<float>(composed/metresPerVoxel); return static_cast<float>(composed/metresPerVoxel);
} }
@@ -11442,10 +11589,9 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
const FIntVector& origin,std::int32_t step,std::int32_t cells,std::uint32_t seed) const FIntVector& origin,std::int32_t step,std::int32_t cells,std::uint32_t seed)
{ {
constexpr std::int64_t Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel; constexpr std::int64_t Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
constexpr double InfluenceMetres=4.0;
constexpr double OpenAirSideWallHeightMetres=1.0; constexpr double OpenAirSideWallHeightMetres=1.0;
if(!shape_||step<=0||cells<=0||!std::isfinite(passageCarveReachMetres_) if(!shape_||step<=0||cells<=0||!std::isfinite(passageCarveReachMetres_)
||std::abs(passageCarveReachMetres_-InfluenceMetres)>1.0e-9)return; ||std::abs(passageCarveReachMetres_-VF_PassageInfluenceReachMetres)>1.0e-9)return;
const bool profile=profileStats_!=nullptr; const bool profile=profileStats_!=nullptr;
const auto prepareStart=profile?std::chrono::steady_clock::now() const auto prepareStart=profile?std::chrono::steady_clock::now()
:std::chrono::steady_clock::time_point{}; :std::chrono::steady_clock::time_point{};
@@ -11459,7 +11605,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
sweep->dimension=cells+3; sweep->dimension=cells+3;
const std::size_t gridSampleCount=static_cast<std::size_t>(sweep->dimension) const std::size_t gridSampleCount=static_cast<std::size_t>(sweep->dimension)
*static_cast<std::size_t>(sweep->dimension)*static_cast<std::size_t>(sweep->dimension); *static_cast<std::size_t>(sweep->dimension)*static_cast<std::size_t>(sweep->dimension);
sweep->influenceSubvoxels=InfluenceMetres*static_cast<double>(Q)*1000000.0 sweep->influenceSubvoxels=passageCarveReachMetres_*static_cast<double>(Q)*1000000.0
/std::max<std::uint32_t>(1,shape_->ResolvedLayout /std::max<std::uint32_t>(1,shape_->ResolvedLayout
?shape_->ResolvedLayout->coreVoxelSizeMicrometres:250000); ?shape_->ResolvedLayout->coreVoxelSizeMicrometres:250000);
sweep->passageToSweep.assign(passageRouteSegments_.size(),-1); sweep->passageToSweep.assign(passageRouteSegments_.size(),-1);
@@ -11473,7 +11619,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
const double qPerMetre=static_cast<double>(Q)*1000000.0/ const double qPerMetre=static_cast<double>(Q)*1000000.0/
std::max<std::uint32_t>(1,shape_->ResolvedLayout std::max<std::uint32_t>(1,shape_->ResolvedLayout
?shape_->ResolvedLayout->coreVoxelSizeMicrometres:250000); ?shape_->ResolvedLayout->coreVoxelSizeMicrometres:250000);
const double shellThickness=0.75*qPerMetre; const double shellThickness=VF_PassageShellThicknessMetres*qPerMetre;
const double stampedOutsideDistance=sweep->influenceSubvoxels+1.0; const double stampedOutsideDistance=sweep->influenceSubvoxels+1.0;
constexpr double MouthShellBlendMetres=5.0; constexpr double MouthShellBlendMetres=5.0;
const double wallReliefShell=0.41*qPerMetre; const double wallReliefShell=0.41*qPerMetre;
@@ -11633,7 +11779,7 @@ inline void FStandaloneShapeWorld::FScopedPassageTileQueries::prepareSweptTubeBa
// more than cornerWidth/6. Skip its work for pairs outside this exact // more than cornerWidth/6. Skip its work for pairs outside this exact
// support, with a small subvoxel guard for float rounding. The final // support, with a small subvoxel guard for float rounding. The final
// shell union only consumes negative distances. // shell union only consumes negative distances.
constexpr double CornerBlendMetres=0.50; constexpr double CornerBlendMetres=VF_PassageCornerBlendMetres;
const float roundDistanceFloat=static_cast<float>(signedDistance); const float roundDistanceFloat=static_cast<float>(signedDistance);
const float floorDistanceFloat=static_cast<float>(floorCenterZ-floorDepth-qz); const float floorDistanceFloat=static_cast<float>(floorCenterZ-floorDepth-qz);
const double flatDistanceLowerBound=std::max( const double flatDistanceLowerBound=std::max(
@@ -0,0 +1,53 @@
# VoxelForge — read-only design review: per-block bit flags (masks) to skip work
You are reviewing an idea against real code. **Do not edit, create or delete any file, and do not build or run
anything.** Read code and reason. Be thorough; mark each claim as [CODE] (with file:line), [MEASURED] (from a report),
or [INFERENCE].
## Context
VoxelForge Core (`Standalone/Core`) generates a density field: negative = solid, positive = air, 25 cm voxels, 32³ tiles,
LOD0–8 clipmap, marching cubes. The production mesher for the standalone path is in `Standalone/Source/...`, and Core is
also compiled into Unreal. Same seed must give the same world on every machine and worker count; byte-identity with
older versions is NOT required.
Branch `assembler/world` (HEAD `7022d02`'s parent line, currently `4041d4b`) has the smooth inter-strate tunnels.
Readiness target: LOD0–2 near terrain in ~1 s on arrival. Measured today: SW ~0.7–0.8 s, VS ~0.9 s, FI/TN ~1.0 s
headless. In Linux UE, start→LOD0–2 is ~2.2–2.5 s. At 30 m/s in UE, near coverage is ~40%.
Reports to consult: `Standalone/Research/WorldAssemblerM3/Linux1*/REPORT.md`, especially:
- L77/L87/L117, cost census;
- L124, shape gate and per-strate timings;
- L125/L126/L128, tunnel prepare: per-segment work, 67–73% pair overlap, support up to 7.9 m;
- L128, where an exact one-owner slab scheme halved pairs but got slower because of per-sample ownership tests.
Tile skipping already exists at tile level: box verdicts / classify, where all-solid or all-air tiles are skipped.
## The idea (from the owner)
Keep bit flags per block (1 bit per voxel, or per small brick of e.g. 4³ or 8³ voxels): for example "certainly deep
solid", "certainly deep air", "near a surface", "touched by a tunnel", "already evaluated". Use them to skip work in
generation and meshing, and possibly elsewhere (collision, streaming, edits, AI/nav, lighting).
## Questions
1. Where does time actually go per tile today, and which of that work a mask could skip? Cover density fill,
classification, mesher (marching cubes, normals/gradients, skirts, seam refinement), passage prepare, and composition.
Use measured census numbers where they exist.
2. Which flags are sound? A flag must be a proof, never a guess. A mask "certainly solid" must be derived from a
conservative bound (the bound must be the supremum of what the evaluator produces), or the mesher will delete
surfaces. Which bounds already exist in the code (box verdicts, interval bounds) that a brick-level mask could reuse?
Name the existing hazards. For example, `Identity` means `Sdf >= T`, and `ClassifyTile` proving below-TerrainZ solid
would delete 3D caves.
3. Granularity: per-voxel bits vs per-brick (4³/8³) vs per-column. What does each cost to build, versus what it
saves? Consider SIMD and cache behaviour. Marching cubes needs only cells whose corner signs differ, and gradients
need a halo.
4. "Already evaluated" or "checked" flags: where would they be correct (memoisation of identical queries), and where
would they be wrong (nearest-of-many queries like the tunnel segments)? Is there repeated identical evaluation today,
for example halo samples shared between neighbouring tiles, or skirt/seam re-queries?
5. Other uses: player edits (diff layer), collision-only server tiles, streaming priority, nav. Which would a mask
genuinely help, given the multiplayer rule (replicate seed + layout + diff, never geometry)?
6. Recommendation: the one or two mask designs most likely to pay off, with the expected gain (a range, marked
inference), the measurement that would prove or kill each, and the risks. Keep it short.
Write plain Markdown, at most ~1,200 words.
@@ -0,0 +1,83 @@
[INFERENCE] **Improve the existing brick-proof system; don’t add a general “checked voxel” mask.** The promising savings are avoided field/composition work and passage preparation, rather than marching-cubes classification.
References below abbreviate these files: [M = production mesher](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp), [S = ShapeWorldInternal.h](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h), [C = VoxelForgeCore.cpp](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp), [O = VoxelDensityOpStack.cpp](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelDensityOpStack.cpp). L-numbers identify `Standalone/Research/WorldAssemblerM3/LinuxN/REPORT.md`.
[INFERENCE] Evidence limitation: Linux128 is absent from this checkout. Its supplied 7.9 m support and slower one-owner result remain unverified here. The reports below span different revisions; their percentages must not be presented as today’s census.
**1. Where time goes**
[MEASURED] L77’s exclusive FI/TN LOD0–2 worker account:
| Work | FI | TN |
|---|---:|---:|
| Total worker CPU/view | 23.02 s | 21.50 s |
| Passage composition | 65.9% | 65.4% |
| Support | 14.2% | 13.5% |
| Base field | 3.2% | 3.3% |
| Bands / dispatch | 2.3% / 3.7% | 2.6% / 3.6% |
| MC / skirts / refinement, excluding field work | 1.6% / 0.1% / 7.5% | 1.2% / 0.2% / 8.6% |
[MEASURED] Grid fill occupied 19.058/17.377 worker-seconds, **overlapping** those phase categories. L117’s later census found support zero; its smooth-tunnel regression concentrated about 95% of added tile CPU in passage-feature tiles. Cell classification stayed flat; gradients became cheaper. Thus L77 identifies historical leverage, not current percentages.
[MEASURED] L125 preparation averaged 514/130/4.1 ms worker work at LOD0/1/2. LOD0 evaluated 3.11–8.49 million segment/sample pairs with 67–72% overlap, although the exact band occupied only 5–7% of grid samples. L126 reduced floor-profile calls 20–23% without useful readiness improvement.
[CODE] Brick skipping already exists: configurable block verdicts, required-sample masks, and active-block gradient halos (M:646,1053). MC already rejects cells without sign crossings before interpolation/gradients (M:2186). Gradient memoisation exists (M:1956). Skirts issue separate density queries (M:2491); refinement has its own stage (C:2975).
[INFERENCE] A pre-evaluation proof can save density, composition, and subsequent MC work. A sign mask constructed after density evaluation saves only downstream scanning. Classification itself costs work; no isolated current classification percentage is established by these reports.
[MEASURED] L124’s shape gate alone took 179–226 ms, with no near-tile admissions before completion. L87 additionally measured a 107–153 ms far-parent gate and no headless collision wait. Tile masks cannot directly remove these gates or explain the supplied UE/headless gap.
**2. Sound flags and hazards**
[INFERENCE] For final density \(D\), solid requires an upper bound covering the evaluator’s supremum, \(U<0\); air requires a valid lower bound \(L\ge0\). “Deep” requires an explicit margin. Unknown must stay unknown. A crossing proves an MC-active cell; uniform sampled signs do not prove an entire continuous volume surface-free.
[CODE] Reusable proofs include:
- S:5428: halo-expanded box classification.
- S:5974: exact surface-column extrema over the MC lattice.
- S:6019: floating-island density intervals.
- S:5197,6032: native cave intervals.
- S:5949: stamp interval composition with outward numerical padding.
- O:6998: operation-specific inactivity from SDF lower bounds.
[CODE] These proofs have guards: passage overlap returns unknown (S:5687); unsupported stamp composition returns unknown (S:5655); stratum crossings are rejected (S:5900). The mesher distinguishes uniform candidates from `bSkipSafe` certificates (M:715,756), and otherwise validates candidates against actual samples (M:1838).
[CODE] For gated modifiers, `Identity` can mean `Sdf >= T`, not `abs(Sdf) >= T` (O:1496,6998). Noise bounds cannot assume nominal `[-1,1]`: FBM and unclamped cellular bounds are explicitly discussed at O:848,301.
[INFERENCE] Additional hazards: confusing internal SDF with final density; certifying base terrain before caves, tunnels, shells, stamps or edits; using lattice proofs for off-grid queries; ignoring rounding or gradient halos. A below-`TerrainZ` solid rule applied to 3D caves deletes caves. “Touched by tunnel” should mean conservative candidate overlap; only **proven untouched** authorizes omission.
**3. Granularity**
[INFERENCE] Storage per flag for 32³ cells:
| Granularity | Storage | Construction/tradeoff |
|---|---:|---|
| Per cell | 4 KiB | 32,768 decisions; expensive unless derived during necessary work |
| 4³ bricks | 64 B | 512 proofs; tighter boundaries, more proof/halo overhead |
| 8³ bricks | 8 B | 64 proofs; cheaper, more mixed bricks |
| XY columns | 128 B | 1,024 decisions; useful mainly for heightfield-specific proofs |
[INFERENCE] These exclude corner/halo samples, metadata and additional flags. A column bit cannot represent multiple cave intervals. Brick runs preserve contiguous X sampling and SIMD better than scattered per-sample branches; bit packing alone does not establish a speedup.
[CODE] The existing implementation uses byte arrays and preserves exact samples around active bricks (M:1053–1083). Replacing densities with arbitrary same-sign constants near active cells would corrupt interpolated positions or normals.
**4. “Already evaluated”**
[CODE] Passage preparation precedes meshing (C:2747). Its valid sentinel is initialization, not completion: subsequent segments update floor selection, shell minima and round-distance winners, with deterministic tie-breaking (S:11520,11615,11688).
[INFERENCE] Therefore first-visitor wins is invalid. Memoisation is valid only for identical queries against identical seed/layout/evaluator/edit revision and query semantics; cache the value, not just a bit.
[MEASURED] L77 counted 8.6–8.9% halo points, without measuring duplicate coordinates. L125 verified 4,900 shared samples across four neighbour pairs and still recorded substantial off-grid skirt/midpoint fallbacks.
[CODE] Shared-grid reuse and gradient validity caches already exist (M:1031,1956). [INFERENCE] Skirt/seam queries are reuse candidates only when coordinates and semantics actually match; “same voxel” is insufficient.
**5. Other uses**
[INFERENCE] Edit-dirty bricks can localize regeneration, with invalidation expanded through normals, seams and collision dependencies. Collision-only servers can skip certified empty regions, but still need boundary geometry. Streaming can prioritize potentially occupied bricks; nav and lighting need clearance/connectivity or visibility data beyond occupancy. Keep masks derived locally from replicated seed + layout + diff, versioned with that state.
**6. Recommendation**
- [INFERENCE] **Tune existing 4³/8³ certificates**, caching proofs and emitting contiguous required-sample runs. Plausible incremental near-ready saving: **0–5%**, with regressions possible. Measure proof time, samples avoided, halo expansion, total worker CPU and UE readiness.
- [INFERENCE] **Pass required-region information into passage preparation**, rejecting whole segment/brick regions conservatively. Plausible near-ready saving: **0–10%**, highly uncertain because passage overlap currently vetoes proofs. Kill it if pair reduction fails to reduce preparation and end-to-end time.
[INFERENCE] Both require seam/off-grid validation, adversarial bound checks, edit invalidation tests, and cross-machine/worker determinism. Preserve canonical tie-breaking; old-version byte identity need not constrain a sound redesign.
@@ -0,0 +1,37 @@
# VoxelForge — open read-only review: what should be done and focused on now?
You are an outside reviewer. **Do not edit, create or delete any file, and do not build or run anything.** Read the
repository and its reports and reason. Be thorough; mark each claim as [CODE] (with file:line), [MEASURED] (from a
report), or [INFERENCE].
## The project, in the owner's words and goals
- VoxelForge is a UE5 density-field voxel plugin for a **multiplayer** game. The world is made of stacked "strates"
(layers) linked by tunnels. The standalone C++ generator is "Core" (`Standalone/Core`), and it is also compiled into
Unreal. Read `CLAUDE.md`, `CODEMAP.md`, `ARCHITECTURE.md`, `GDD.md`, `COMPOSER-NOTES.md` and `fable-idea.md` at the
plugin root for the game and design intent. The research reports are in
`Standalone/Research/WorldAssemblerM3/Linux*/REPORT.md` and the `Astra*/` reviews.
- Owner goals stated over time:
- the near terrain (LOD0–2) ready in ~1 s on arrival, then far terrain keeps filling in at low priority;
- same seed gives the same world on every machine (byte-identity with older versions is NOT required);
- smooth for VR and high-speed travel;
- all generators (surface, floating islands, caves, dreamcore/liminal, etc.) working in Core;
- the system should eventually *invent* strates and worlds from tagged authored content plus a seed;
- keep things simple and avoid over-complication.
- The current main line is branch `assembler/world`.
## Your task
Look at the whole: code, reports, design docs, and the direction of the last few dozen rounds. Tell the owner what
should be done and focused on now, whatever it is. That may be performance, features, generation quality, gameplay
foundations, architecture, technical debt, tooling, process, or stopping something. Don't assume the recent focus is
the right one; question it if the evidence says so.
Answer:
1. **Where the project actually is**: what works, what's fragile, what's missing for the game to exist.
2. **The top 3–5 things to focus on now**, ranked, each with why, a rough size, what "done" looks like, and how to
tell it worked.
3. **What to stop or deprioritise**, and why.
4. **Risks or debts** that will bite later if ignored.
Write plain Markdown, at most ~1,500 words, readable by a non-specialist owner (explain jargon briefly).
@@ -0,0 +1,72 @@
[INFERENCE] **Focus now on a small, persistent multiplayer descent, supported by end-to-end Unreal performance work.** VoxelForge has substantial terrain technology. The larger uncertainty is whether it delivers a reliable, interesting shared expedition. Another sequence of tunnel optimizations will not resolve that.
Read-only review of `assembler/world` at `044cd6d`; no files changed and no builds, tests, or project execution. [CODE] identifies repository evidence; design-document references establish intent, not implementation. [MEASURED] means reported results, not independently reproduced measurements.
**1. Where the project actually is**
[CODE] **Core is real and integrated, but generator implementation remains mixed.** Unreal compiles the canonical standalone sources through validated wrappers. Core dispatches to surface, floating-island and native graph evaluators, with a legacy-generator fallback when appropriate. That is useful coverage, but does not mean every family has a complete native implementation. See [VoxelForgeCore.Build.cs:15](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForgeCore/VoxelForgeCore.Build.cs:15) and [ShapeWorldInternal.h:600](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:600).
[MEASURED] **Passage correctness and speed have improved substantially.** L109 simplified tunnels and removed support-restoration machinery, reducing floating-island and tunnel-network near-generation times by approximately 31–38%. L125 reports 21,425/21,425 open route samples, a maximum sampled floor offset of 7.884 mm, and matching cross-compiler/worker hashes. These are meaningful engineering achievements. [L109](Standalone/Research/WorldAssemblerM3/Linux109/REPORT.md), [L125](Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md).
[MEASURED] **The one-second player experience is not achieved.** L125’s standalone near-ready medians were approximately 0.78–1.03 seconds. L127’s Unreal startup took 2.22–2.51 seconds; at 30 m/s, median near coverage fell from 43.36% to 40.22% after the smooth-tunnel merge. Both versions recovered after stopping. L130 found no measurable timing improvement and skipped Unreal movement. These are different workloads; subtracting their times would not isolate a particular overhead. [L125](Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md), [L127](Standalone/Research/WorldAssemblerM3/Linux127/REPORT.md), [L130](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md).
[CODE] **Multiplayer foundations exist; the multiplayer lifecycle remains unfinished here.** There are season hashes, local terrain-edit entry points, and streaming-anchor infrastructure. The architecture explicitly leaves join replication, authoritative edit messages, late-join snapshots and networked season changes open. The edit entry point directly dispatches to local modification paths. [VoxelWorld.h:574](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelWorld.h:574), [VoxelWorld.cpp:15771](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelWorld.cpp:15771), [ARCHITECTURE.md:2408](/home/jahni/VoxelM/Plugins/VoxelForge/ARCHITECTURE.md:2408).
[CODE] **The composer is more than a proposal, but less than the full content vision.** It rolls candidates, measures them and rejects failures. The intended system additionally combines tagged geometry, materials, decorations, creatures and audio; theme coherence remains an open design item. [VoxelSeasonManifest.cpp:2523](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelSeasonManifest.cpp:2523), [COMPOSER-NOTES.md:1512](/home/jahni/VoxelM/Plugins/VoxelForge/COMPOSER-NOTES.md:1512).
[INFERENCE] **This repository demonstrates a capable terrain platform, not yet a validated game loop.** I cannot establish what gameplay exists outside the plugin. The GDD itself identifies “Is the descent fun?” as its first unresolved question. [GDD.md:441](/home/jahni/VoxelM/Plugins/VoxelForge/GDD.md:441).
**2. Ranked priorities**
All sizes below are [INFERENCE]: rough focused engineering estimates, excluding substantial art production.
**1 — Deliver one multiplayer expedition that survives reconnecting. Size: 3–6 weeks.**
[INFERENCE] Use two contrasting strates and their connecting tunnel. Two players descend, find one destination, uncover or collect something, return, and retain their changes after restart. Include a third player joining after terrain has changed. Avoid expanding immediately into the city economy or community backend.
[INFERENCE] The necessary foundation is a server-approved world identity, ordered authoritative edits, a saved snapshot plus subsequent edits, and collision around separated players. Give edits stable identifiers so retransmission cannot apply them twice. Reuse the existing local edit and anchor systems.
[INFERENCE] **Done:** host/client edits agree; a late joiner reconstructs the same terrain; restart preserves it; incompatible world versions fail clearly; remote players have ground when away from the host.
[INFERENCE] **Success:** a complete shared expedition without terrain disagreement, lost progress or collision failures—and players can explain what made them want to descend again. This tests the project’s central assumptions much earlier than a complete RPG would.
**2 — Close the Unreal arrival and movement gap. Size: 1–3 weeks initially.**
[MEASURED] L122 found idle generation capacity while admission was blocked; L123 measured approximately 19 ms from region request to game-thread observation despite sub-millisecond median compilation. Simple lookahead and admission relaxations failed their combined coverage/frame-time gates. L121’s modest extra far-apply lane did improve far coverage. [L121](Standalone/Research/WorldAssemblerM3/Linux121/REPORT.md), [L122](Standalone/Research/WorldAssemblerM3/Linux122/REPORT.md), [L123](Standalone/Research/WorldAssemblerM3/Linux123/REPORT.md).
[INFERENCE] First establish one current-build, rendered Unreal baseline on intended hardware. Measure arrival through **visible terrain and usable collision**, then walking, 30 m/s travel, turns, teleporting and edits. Record near coverage, far gaps, frame-time spikes and memory separately. NullRHI measurements, which omit rendering, cannot certify VR comfort.
[INFERENCE] Investigate region completion, admission and publication before another broad density rewrite. A game-thread continuation is a candidate experiment, not a proven solution; keep world mutation on its owning thread.
[INFERENCE] **Done:** approximately one-second LOD0–2 readiness on the agreed hardware, safe collision, steadily filling far terrain, and an explicit movement-coverage target without worsening frame spikes. **Success:** repeated rendered runs meet those limits, including with remote-player collision work. Do not redefine “ready” as only the nearest handful of tiles.
**3 — Turn generator breadth into playable variety. Size: 2–4 weeks for the first content slice.**
[INFERENCE] Establish a compact capability matrix for each family: Core dispatch, entrances/exits, interior connectivity, edits, materials/content, far terrain and performance. Include dreamcore/liminal explicitly; motif generation alone does not establish a convincing anomalous place.
[MEASURED] L58 illustrates the distinction: its lattice route passed reachability checks, but the review described a corkscrew tunnel rather than the intended open vertical shaft. Technical validity and identity are separate acceptance criteria. [L58](Standalone/Research/WorldAssemblerM3/Linux58/REPORT.md).
[INFERENCE] Extend the existing offline composer with a small tagged library: a grounded cave, an island environment and one liminal identity, each with coherent materials, sound and a destination. Preserve owner review before publishing a season.
[INFERENCE] **Done:** several seeds produce recognizably different, coherent places with findable progression and usable encounter spaces. **Success:** players navigate them without developer guidance and distinguish their identities without reading labels. Expand families after this works.
**4 — Consolidate the evidence and remove obsolete complexity incrementally. Size: 2–4 days, then ongoing.**
[INFERENCE] Maintain one short current-state page: shipped behavior, open defects, active baseline, hardware and next acceptance test. Keep historical reports as history. Remove rejected experimental paths when they no longer serve a specific comparison; extract stable responsibilities when touching oversized files.
[INFERENCE] **Done:** a contributor can identify the current evaluator, runtime path, multiplayer gaps and performance limits without reconstructing 130 rounds. **Success:** new work no longer repeats rejected experiments or treats old measurements as current.
**3. Stop or deprioritise**
- [INFERENCE] **Pause open-ended tunnel micro-optimization.** L125/L126 missed the older speed bar; L130’s reach correction produced no measurable speedup. Keep correctness fixes, but require a current cost account and a bounded experiment before further optimization. [MEASURED: L126](Standalone/Research/WorldAssemblerM3/Linux126/REPORT.md), [L130](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md).
- [INFERENCE] **Do not implement Astra recommendations mechanically.** Astra1’s support-heavy model predates L109’s simplification. Astra2/3 offer hypotheses, not measured wins for today’s code. Re-profile before adopting them.
- [INFERENCE] **Defer GPU generation, a mesher replacement, elaborate discovery systems and large-scale backend work.** None currently resolves the missing shared expedition.
- [INFERENCE] **Stop requiring old-version byte identity for deliberate generation changes.** Preserve peer agreement within a version and explicit versioned saves; permit reviewed geometry improvements.
**4. Debts that will bite later**
[INFERENCE] **Determinism needs a deployment contract.** Linux Clang/GCC and worker-count agreement is strong evidence, not proof across every intended machine. Include Windows/compiler/CPU combinations actually supported. World identity must cover evaluator, content, layout, scale and edit state—not merely seed. [MEASURED: L130](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md).
[CODE] **Scale and fallback complexity remain active integration risks.** Core is opt-in, and runtime queries bridge different coordinate conventions. [VoxelWorld.h:300](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelWorld.h:300), [VoxelWorld.cpp:15612](/home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelWorld.cpp:15612). [INFERENCE] Test edits, content placement and collision across those boundaries before broadening authoring.
[INFERENCE] **A passing route audit does not prove a whole strate is enjoyable, connected or suitable for six-player combat.** Retain geometric checks, but add actual traversal, destination discovery and encounter-space evaluation. Those are now more valuable than another polished tunnel benchmark.
@@ -1,13 +0,0 @@
# L128 slab ownership
Base `4041d4b`; branch `wip/l128-slab-ownership`; core version 226. Source stamp: `8682a9a088f0f02357b3b9722cda9b18a5ebce9886a13cba2d114bc0655d8c70`.
Each route body uses half-open frame-plane slabs, so a boundary sample belongs to one segment. The required support is `S = r + max(4.0 + 0.21 + 0.125, 0.75 + 0.21) + 0.05 m`: bore radius plus the larger sweep/relief/corner or shell/relief reach, with a 5 cm audit margin. The largest body support is 7.885 m; the 4.5 m mouth profile reaches 8.885 m and is assigned to transition slabs. Local uniqueness requires curvature radius `R > S` (`κS < 1`). For equal adjacent chord lengths `L` and turn `θ`, this is `L / (2 sin(θ/2)) > S`. Nonlocal mutually-normal centerline pairs must be farther apart than the sum of their supports. Intersections between different passages remain a deterministic field union.
All three owner-world routes pass the body curvature and self-separation checks; no route control points changed. Minimum body curvature radii are 11.565 m (0–1), 14.042 m (1–2), and 8.869 m (2–3), giving at least 0.984 m margin over body support. Route 0–1's 8 tight mouth joints and 5 nonlocal mouth pairs use transitions. The prepared/off-grid query paths share ownership. The audit tested 33,813 support samples: zero missing or duplicate owners, and both negative controls were detected.
The slab projection supplies `s` to the continuous floor profile `h(s)`; round-distance does not choose floor height. Across 459 floor roots at nine stations, the largest adjacent root step was 1.765 mm and the largest absolute station offset was 7.884 mm. Seam audit: 4,900 shared samples, zero mismatches. Fast suite passed (264 checks, 5 diagnostic); walkability passed. Clang and GCC W1/W8/W12 hashes matched across all three datasets. The 23 L125 captures are in `captures/L128/` (19 pass; 4 low-contrast with geometry visible).
**Performance bar failed.** Five interleaved runs per group across all four strates and W8/W12 put LOD0–2 readiness 14.8–28.0% behind L109 and 8.0–20.6% behind `4041d4b`. The five-run 30 m/s UE median also failed versus `4041d4b`: near coverage 31.46% vs 40.22%, moving far holes 5.66% vs 3.46%, p99 tick 12.42 ms vs 10.80 ms, and worst tick 14.35 ms vs 13.27 ms. L128 is not ready.
Linux UE host build succeeded (27/27 actions, `-NoUBA -MaxParallelActions=4`); Unreal ran without `prlimit`. No commit made. Detailed logs are under `Saved/Linux/l128/`.
@@ -1,103 +0,0 @@
#!/usr/bin/env python3
"""Render the exact 23 L125 camera views with the L128 slab-ownership candidate."""
from __future__ import annotations
import importlib.util
import json
import math
import os
import sys
from pathlib import Path
sys.dont_write_bytecode = True
ROOT = Path(__file__).resolve().parents[4]
RESEARCH = Path(__file__).resolve().parent
CAPTURES = RESEARCH / "captures"
SAVED = ROOT / "Saved/Linux/l128/captures"
BASELINE_INDEX = (ROOT / "Standalone/Research/WorldAssemblerM3/Linux125"
/ "captures/L125/capture-index.json")
LAYOUT = ROOT / "Saved/Linux/l128/hashes/owner4-surface-world-w12-clang.layout.json"
CLANG = ROOT / os.environ.get("L128_CAPTURE_CLANG",
"Saved/Linux/l128/bin/VoxelForgeCoreHeadless-clang19")
RENDERER = ROOT / os.environ.get("L128_CAPTURE_RENDERER",
"Saved/Linux/l128/bin/VoxelForgeOffscreenCapture-clang19")
HELPER_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux113/RunL113Captures.py"
SPEC = importlib.util.spec_from_file_location("l113_capture_helpers", HELPER_PATH)
if SPEC is None or SPEC.loader is None:
raise SystemExit("cannot load the verified L113 capture helpers")
HELPERS = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(HELPERS)
HELPERS.RESEARCH = RESEARCH
HELPERS.CAPTURES = CAPTURES
HELPERS.SAVED = SAVED
HELPERS.BINARIES = {"L128": CLANG}
HELPERS.RENDERERS = {"L128": RENDERER}
def main() -> int:
if any(not path.is_file() for path in (BASELINE_INDEX, LAYOUT, CLANG, RENDERER)):
raise SystemExit("missing L125 camera set, L128 layout, or capture binary")
baseline = json.loads(BASELINE_INDEX.read_text(encoding="utf-8"))
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
rows = []
CAPTURES.mkdir(parents=True, exist_ok=True)
SAVED.mkdir(parents=True, exist_ok=True)
for view in baseline["captures"]:
row = HELPERS.capture("L128", view, layout)
row["variant"] = "L128-slab-ownership-v226"
if view["kind"] == "rock-floor-close":
scene = ROOT / row["scene"]
anchor = HELPERS.nearest_surface_anchor(scene, view["target"], margin_m=5.0,
preferred_normal=(0.0, 0.0, 1.0), minimum_alignment=0.85)
alignment = HELPERS.dot(anchor["normal"], (0.0, 0.0, 1.0))
camera_distance = math.dist(row["camera_actual_m"], anchor["position_m"])
row.update({"floor_anchor_position_m": anchor["position_m"],
"floor_anchor_normal": anchor["normal"],
"closeup_camera_to_floor_distance_m": camera_distance,
"closeup_floor_normal_alignment": alignment,
"verified_upward_floor_normal": alignment >= 0.85})
if (not row["verified"] or not 2.0 <= camera_distance <= 3.2
or anchor["distance_m"] > 0.25 or alignment < 0.85
or row["drawn_triangles"] <= 0
or row["pixel_quality_status"] not in
("pass", "low_contrast_geometry_visible")):
raise RuntimeError(f"L128 floor close-up failed at {view['name']}")
rows.append(row)
if len(rows) != len(baseline["captures"]) or len(rows) != 23:
raise RuntimeError(f"expected all 23 L125 views, captured {len(rows)}")
index = CAPTURES / "L128" / "capture-index.json"
index.parent.mkdir(parents=True, exist_ok=True)
index.write_text(json.dumps({"captures": rows}, indent=2) + "\n",
encoding="utf-8")
html = ["<!doctype html><meta charset='utf-8'>",
"<title>L125 / L128 tunnel capture comparison</title>",
"<style>body{font:14px system-ui;margin:24px;background:#17191d;color:#eee}"
"section{margin:28px 0}h2{font-size:18px}div{display:grid;"
"grid-template-columns:1fr 1fr;gap:12px}img{width:100%;height:auto}"
"p{color:#bbb}</style>",
"<h1>L125 / L128 slab ownership: identical views</h1>"]
old_by_name = {Path(row["capture"]).name: row for row in baseline["captures"]}
for row in rows:
name = Path(row["capture"]).name
old = old_by_name[name]
old_path = os.path.relpath(ROOT / "Standalone/Research/WorldAssemblerM3/Linux125"
/ old["capture"], CAPTURES)
new_path = os.path.relpath(RESEARCH / row["capture"], CAPTURES)
html.extend(["<section>", f"<h2>{row['passage']} — {row['station']}</h2>",
f"<p>{row['kind']} · L125 {old['pixel_quality_status']} · "
f"L128 {row['pixel_quality_status']}</p>", "<div>",
f"<figure><figcaption>L125</figcaption><img src='{old_path}'></figure>",
f"<figure><figcaption>L128 slab ownership</figcaption><img src='{new_path}'></figure>",
"</div>", "</section>"])
(CAPTURES / "COMPARE.html").write_text("\n".join(html) + "\n",
encoding="utf-8")
print(f"[L128Captures] rendered={len(rows)} views index={index.relative_to(ROOT)}",
flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,88 +0,0 @@
#!/usr/bin/env python3
"""Probe reconstructed tunnel floor roots at the nine L119 stations."""
from __future__ import annotations
import csv
import json
import math
import os
import re
import statistics
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / os.environ.get("L128_PROBE_DIR", "Saved/Linux/l128/floor-probe")
BINARY = ROOT / os.environ.get("L128_PROBE_BINARY",
"Saved/Linux/l128/bin/VoxelForgeCoreHeadless-clang19")
DATA = ROOT / "Standalone/data/owner_world.json"
SLOPE = re.compile(
r"^\[L115DensitySlopeSample\] passage=(\S+) station=(\S+) s_m=([\d.]+) "
r".*?planned_floor_m=(-?[\d.]+) root_floor_m=(-?[\d.]+|nan) "
r"residual_m=(-?[\d.]+|nan)", re.M)
def main() -> int:
OUT.mkdir(parents=True, exist_ok=True)
log, layout = OUT / "owner4-clang.log", OUT / "layout.json"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARY), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=12", "--clip-radius=3",
"--max-clip-level=8", "--max-level=8",
"--camera=8,16,-372.2626953125,0,0", "--audit-resolved-layout-passages",
f"--resolved-layout-output={layout}",
]
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT,
env={**os.environ, "VOXELFORGE_L115_DENSITY_PROFILE": "1"},
stdout=stream, stderr=subprocess.STDOUT, check=False, timeout=1740)
if result.returncode:
raise SystemExit(f"density probe failed: {log}; exit={result.returncode}")
text = log.read_text(encoding="utf-8", errors="replace")
groups: dict[tuple[str, str], list[dict]] = {}
for passage, station, distance, planned, root, residual in SLOPE.findall(text):
root_value = float(root) if root != "nan" else math.nan
residual_value = float(residual) if residual != "nan" else math.nan
groups.setdefault((passage, station), []).append({
"route_s_m": float(distance), "planned_floor_m": float(planned),
"root_floor_m": root_value, "offset_m": residual_value})
summary = []
with (OUT / "zero-crossing-offsets.csv").open("w", newline="", encoding="utf-8") as stream:
writer = csv.DictWriter(stream, fieldnames=("passage", "station", "route_s_m",
"planned_floor_m", "root_floor_m", "offset_m"))
writer.writeheader()
for (passage, station), samples in sorted(groups.items()):
samples.sort(key=lambda sample: sample["route_s_m"])
for sample in samples:
writer.writerow({"passage": passage, "station": station, **sample})
finite = [sample["offset_m"] for sample in samples
if math.isfinite(sample["offset_m"])]
adjacent_steps = [abs(right["offset_m"] - left["offset_m"])
for left, right in zip(samples, samples[1:])
if math.isfinite(left["offset_m"])
and math.isfinite(right["offset_m"])]
summary.append({"passage": passage, "station": station,
"samples": len(samples), "finite_roots": len(finite),
"max_abs_offset_m": max(map(abs, finite)) if finite else None,
"max_adjacent_root_step_m": max(adjacent_steps)
if adjacent_steps else None,
"median_abs_offset_m": statistics.median(map(abs, finite))
if finite else None,
"p95_abs_offset_m": sorted(map(abs, finite))[
min(len(finite)-1, math.ceil(0.95*len(finite))-1)]
if finite else None})
if not summary or any(item["samples"] != 51 for item in summary):
raise RuntimeError("density profile is missing 51-sample route stations")
worst_step = max(item["max_adjacent_root_step_m"] or 0.0 for item in summary)
if worst_step > 0.003:
raise RuntimeError(f"floor root jumps by {worst_step:.6f} m between 0.1 m probes")
(OUT / "zero-crossing-summary.json").write_text(
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
print(f"[L128DensityProbe] exit={result.returncode} stations={len(summary)} "
f"log={log.relative_to(ROOT)}", flush=True)
return result.returncode
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,112 +0,0 @@
#!/usr/bin/env python3
"""Check Clang/GCC W1/W8/W12 hash determinism for L128."""
from __future__ import annotations
import json
import os
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / os.environ.get("L128_HASH_DIR", "Saved/Linux/l128/hashes")
DATASETS = {
"owner4": ROOT / "Standalone/data/owner_world.json",
"owner2": ROOT / "Standalone/data/regression/owner_world_2strate_l61.json",
}
BINARIES = {
"clang": ROOT / os.environ.get("L128_HASH_CLANG",
"Saved/Linux/l128/bin/VoxelForgeCoreHeadless-clang19"),
"gcc": ROOT / os.environ.get("L128_HASH_GCC",
"Saved/Linux/l128/bin/VoxelForgeCoreHeadless-l128-gcc"),
}
HASH = re.compile(r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=(\S+)$", re.M)
def run(dataset: str, recipe: str, workers: int, compiler: str) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
label = f"{dataset}-{recipe}-w{workers}-{compiler}"
log = OUT / f"{label}.log"
layout = OUT / f"{label}.layout.json"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[compiler]), str(DATASETS[dataset]),
f"--workers={workers}", "--world-assembler-prototype", "--stream-benchmark",
"--stream-pipeline", "--force-generate", "--clip-radius=3",
"--max-clip-level=8", "--max-level=8", f"--recipe={recipe}",
"--stream-hash", f"--resolved-layout-output={layout}",
]
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
output = log.read_text(encoding="utf-8", errors="replace")
hashes = HASH.findall(output)
if result.returncode or len(hashes) != 12:
raise SystemExit(f"hash run failed or incomplete: {label}; exit={result.returncode} "
f"hashes={len(hashes)} log={log}")
row = {"dataset": dataset, "recipe": recipe, "workers": workers,
"compiler": compiler, "exit_code": result.returncode, "hashes": hashes,
"source_stamp": (STAMP.search(output).group(1)
if STAMP.search(output) else None),
"elapsed_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT))}
print("[L128HashRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
if any(not path.is_file() for path in (*DATASETS.values(), *BINARIES.values())):
raise SystemExit("missing L128 hash input")
rows = []
recipes = {"owner4": ("surface-world",),
"owner2": ("surface-world", "floating-islands")}
for dataset, dataset_recipes in recipes.items():
for recipe in dataset_recipes:
for workers in (1, 8, 12):
for compiler in ("clang", "gcc"):
rows.append(run(dataset, recipe, workers, compiler))
(OUT / "runs-partial.json").write_text(
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
groups = {}
for dataset, dataset_recipes in recipes.items():
for recipe in dataset_recipes:
key = f"{dataset}/{recipe}"
group = [row for row in rows if row["dataset"] == dataset
and row["recipe"] == recipe]
reference = group[0]["hashes"]
stable = all(row["hashes"] == reference for row in group)
groups[key] = {
"all_worker_compiler_hashes_match": stable,
"source_stamps": sorted({row["source_stamp"] for row in group}),
"runs": [{"workers": row["workers"], "compiler": row["compiler"],
"log": row["log"]} for row in group],
}
if not stable:
raise SystemExit(f"determinism mismatch: {key}")
summary = {"runs": rows, "groups": groups}
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n",
encoding="utf-8")
baseline_dir = ROOT / "Standalone/Tools/verify/baselines/owner-hash/canonical"
for dataset, recipe, style, baseline_name in (
("owner4", "surface-world", 1,
"owner-hash-owner4-surface-world-style1-v226.baseline"),
("owner2", "surface-world", 1,
"owner-hash-surface-world-style1-v226.baseline"),
("owner2", "floating-islands", 2,
"owner-hash-floating-islands-style2-v226.baseline"),
):
canonical = next(row for row in rows if row["dataset"] == dataset
and row["recipe"] == recipe and row["workers"] == 12
and row["compiler"] == "clang")
baseline = baseline_dir / baseline_name
baseline.write_text("\n".join(canonical["hashes"]) + "\n",
encoding="utf-8")
print("[L128HashSummary] " + json.dumps(groups, sort_keys=True), flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,136 +0,0 @@
#!/usr/bin/env python3
"""Five interleaved L80 readiness runs for L109, 4041d4b and L128."""
from __future__ import annotations
import json
import os
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / os.environ.get("L128_TIMING_DIR", "Saved/Linux/l128/timing")
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"L109": ROOT / "Saved/Linux/l109/bin-final/VoxelForgeCoreHeadless-l109-clang",
"4041d4b": ROOT / "Saved/Linux/l127/bin-clang/VoxelForgeCoreHeadless-l127-clang",
"L128": ROOT / os.environ.get("L128_CANDIDATE_BINARY",
"Saved/Linux/l128/bin/VoxelForgeCoreHeadless-clang19"),
}
CAMERAS = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
PIPELINE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
NEAR = re.compile(
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
re.M)
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
log = OUT / f"{label}.log"
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[version]), str(DATA),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
f"--workers={workers}", "--clip-radius=3", "--max-clip-level=8",
"--max-level=8", f"--camera={camera}", "--profile-lod-work",
"--stream-scheduler-policy=3",
]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
result = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
pipeline_match, near_match = PIPELINE.search(content), NEAR.search(content)
if result.returncode or pipeline_match is None or near_match is None:
raise SystemExit(f"readiness run failed or incomplete: {log}; exit={result.returncode}")
pipeline = dict(FIELDS.findall(pipeline_match.group(1)))
row = {"strate": strate, "workers": workers, "version": version,
"phase": phase, "replicate": replicate, "exit_code": result.returncode,
"wall_s": round(time.monotonic() - started, 3),
"lod02_ready_s": float(near_match.group(3)),
"whole_view_ready_s": float(pipeline["whole_view_ready"].removesuffix("s")),
"whole_tiles": int(pipeline["whole_tiles"].split("/")[0]),
"log": str(log.relative_to(ROOT))}
print("[L128TimingRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def cpu_idle_percent(interval_s: float = 2.0) -> float:
def totals() -> tuple[int, int]:
fields = Path("/proc/stat").read_text(encoding="utf-8").splitlines()[0].split()
values = [int(value) for value in fields[1:]]
return sum(values), values[3] + (values[4] if len(values) > 4 else 0)
before_total, before_idle = totals()
time.sleep(interval_s)
after_total, after_idle = totals()
return 100.0 * (after_idle-before_idle) / max(1, after_total-before_total)
def main() -> int:
if not DATA.is_file() or any(not binary.is_file() for binary in BINARIES.values()):
raise SystemExit("missing L109/4041d4b/L128 timing input")
OUT.mkdir(parents=True, exist_ok=True)
load_1m, idle_pct = os.getloadavg()[0], cpu_idle_percent()
uptime = subprocess.run(["uptime"], cwd=ROOT, text=True,
capture_output=True, check=True).stdout.strip()
top = subprocess.run(["ps", "-eo", "pid,pcpu,comm", "--sort=-pcpu"],
cwd=ROOT, text=True, capture_output=True, check=True).stdout
(OUT / "machine-load-before.txt").write_text(
f"uptime: {uptime}\n1-minute load: {load_1m:.2f}\n"
f"2-second CPU idle: {idle_pct:.1f}%\ntop CPU users:\n{top}", encoding="utf-8")
print(f"[L128TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle_pct:.1f}%",
flush=True)
if load_1m >= 0.9 or idle_pct < 97.0:
raise SystemExit("timing refused: machine is above the quiet-load gate")
rows = []
versions = ("L109", "4041d4b", "L128")
for strate in range(4):
for workers in (8, 12):
for version in versions:
run(strate, workers, version, "warmup", 0)
for replicate in range(1, 6):
offset = (replicate - 1) % len(versions)
order = versions[offset:] + versions[:offset]
for version in order:
rows.append(run(strate, workers, version, "measured", replicate))
(OUT / "runs-partial.json").write_text(
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
summary = []
for strate in range(4):
for workers in (8, 12):
item = {"strate": strate, "workers": workers, "replicates": 5}
medians = {}
for version in versions:
group = [row for row in rows if row["strate"] == strate
and row["workers"] == workers and row["version"] == version]
medians[version] = {}
for key in ("lod02_ready_s", "whole_view_ready_s"):
values = [row[key] for row in group]
medians[version][key] = statistics.median(values)
item[f"{version}_{key}_values"] = values
item[f"{version}_{key}_median"] = medians[version][key]
for comparison in ("L109", "4041d4b"):
for key in ("lod02_ready_s", "whole_view_ready_s"):
item[f"L128_vs_{comparison}_{key}_delta_pct"] = round(
100.0 * (medians["L128"][key] / medians[comparison][key]-1.0), 2)
summary.append(item)
(OUT / "summary.json").write_text(
json.dumps({"runs": rows, "summary": summary}, indent=2) + "\n",
encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,106 +0,0 @@
#!/usr/bin/env python3
"""Run five UE 30 m/s movement trials and compare with the L127 4041d4b baseline."""
from __future__ import annotations
import importlib.util
import json
import os
import re
import statistics
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
RESEARCH = ROOT / "Standalone/Research/WorldAssemblerM3"
OUT = ROOT / "Saved/Linux/l128/ue"
HOST = Path("/home/jahni/VFHost")
EDITOR = Path("/home/jahni/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Cmd")
MOVEMENT_PATH = RESEARCH / "Linux121/RunL121Movement.py"
BASELINE_PATH = ROOT / "Saved/Linux/l127/ue/movement-pairs-summary.json"
def load_movement_module():
spec = importlib.util.spec_from_file_location("l128_movement", MOVEMENT_PATH)
if spec is None or spec.loader is None:
raise RuntimeError(f"cannot load UE movement runner: {MOVEMENT_PATH}")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def main() -> int:
if not BASELINE_PATH.is_file():
raise SystemExit(f"missing 4041d4b movement baseline: {BASELINE_PATH}")
OUT.mkdir(parents=True, exist_ok=True)
module = load_movement_module()
module.HOST = HOST
module.EDITOR = EDITOR
module.OUT = OUT / "movement"
module.HOST_SAVED = HOST / "Saved/Linux/l128/host"
module.HOST_OUT = HOST / "Plugins/VoxelForge/Saved/Linux/l128/movement"
module.RUN_TIMEOUT_S = 1740
module.HOST_OUT.mkdir(parents=True, exist_ok=True)
os.environ["XDG_CONFIG_HOME"] = str(OUT / "config")
rows = []
for repetition in range(1, 6):
row = module.run_unreal("L128", repetition)
events = module.parse_events(ROOT / row["trace"])
row["supplemental_far_applies_total"] = sum(
int(event.get("supplemental_far_applies", 0))
for event in events if event.get("type") == "apply_frame")
row["supplemental_far_applies_moving"] = sum(
int(event.get("supplemental_far_applies", 0))
for event in events if event.get("type") == "apply_frame"
and float(event.get("speed_cm_s", 0.0)) >= 50.0)
if row.get("effective_tile_slots") != 8 or row["supplemental_far_applies_moving"] <= 0:
raise SystemExit(f"movement run missed the configured apply lane: {row}")
rows.append(row)
(OUT / "movement-runs-partial.json").write_text(
json.dumps(rows, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print("[L128UEMovement] " + json.dumps({
key: row[key] for key in (
"condition", "repetition", "near_ready_moving_mean_pct",
"far_holes_moving_mean_pct", "game_thread_tick_p99_ms",
"game_thread_tick_max_ms", "supplemental_far_applies_moving",
"wall_s", "trace")}, sort_keys=True), flush=True)
baseline_doc = json.loads(BASELINE_PATH.read_text(encoding="utf-8"))
baseline_rows = [row for row in baseline_doc["runs"]
if row["condition"] == "merge"]
if len(baseline_rows) != 5:
raise SystemExit(f"expected five 4041d4b baseline runs, got {len(baseline_rows)}")
metrics = ("near_ready_moving_mean_pct", "far_holes_moving_mean_pct",
"game_thread_tick_p99_ms", "game_thread_tick_max_ms")
baseline = {metric: statistics.median(row[metric] for row in baseline_rows)
for metric in metrics}
candidate = {metric: statistics.median(row[metric] for row in rows)
for metric in metrics}
build_log = (ROOT / "Saved/Linux/l128/build-clang.log").read_text(
encoding="utf-8", errors="replace")
stamp_match = re.search(r"^Source stamp: (\S+)$", build_log, re.M)
if stamp_match is None:
raise SystemExit("missing L128 Clang source stamp")
summary = {
"baseline": {"commit": "4041d4b", "runs": len(baseline_rows),
"metrics": baseline},
"candidate": {"source_stamp": stamp_match.group(1),
"runs": len(rows), "metrics": candidate},
"candidate_vs_4041d4b_delta": {
metric: candidate[metric] - baseline[metric] for metric in metrics},
"bar_pass": candidate["near_ready_moving_mean_pct"]
> baseline["near_ready_moving_mean_pct"]
and all(candidate[metric] <= baseline[metric] for metric in (
"far_holes_moving_mean_pct", "game_thread_tick_p99_ms",
"game_thread_tick_max_ms")),
}
(OUT / "movement-summary.json").write_text(
json.dumps({"runs": rows, "summary": summary}, indent=2, sort_keys=True) + "\n",
encoding="utf-8")
print("[L128UEMovementSummary] " + json.dumps(summary, sort_keys=True), flush=True)
if not summary["bar_pass"]:
raise SystemExit("L128 UE movement medians missed the 4041d4b coverage/tick bar")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,188 +0,0 @@
<!doctype html><meta charset='utf-8'>
<title>L125 / L128 tunnel capture comparison</title>
<style>body{font:14px system-ui;margin:24px;background:#17191d;color:#eee}section{margin:28px 0}h2{font-size:18px}div{display:grid;grid-template-columns:1fr 1fr;gap:12px}img{width:100%;height:auto}p{color:#bbb}</style>
<h1>L125 / L128 slab ownership: identical views</h1>
<section>
<h2>passage/0-1/0 — rock-floor/401.7m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-rock-floor-station-1.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-rock-floor-station-1.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — rock-floor/792.8m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-rock-floor-station-2.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-rock-floor-station-2.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — rock-floor/1634.5m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-rock-floor-station-3.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-rock-floor-station-3.png'></figure>
</div>
</section>
<section>
<h2>passage/1-2/0 — rock-floor/27.0m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-rock-floor-station-1.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-1-2-0-rock-floor-station-1.png'></figure>
</div>
</section>
<section>
<h2>passage/1-2/0 — rock-floor/52.9m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-rock-floor-station-2.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-1-2-0-rock-floor-station-2.png'></figure>
</div>
</section>
<section>
<h2>passage/1-2/0 — rock-floor/78.9m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-rock-floor-station-3.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-1-2-0-rock-floor-station-3.png'></figure>
</div>
</section>
<section>
<h2>passage/2-3/0 — rock-floor/323.4m</h2>
<p>rock-floor-close · L125 low_contrast_geometry_visible · L128 low_contrast_geometry_visible</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-rock-floor-station-1.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-2-3-0-rock-floor-station-1.png'></figure>
</div>
</section>
<section>
<h2>passage/2-3/0 — rock-floor/333.4m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-rock-floor-station-2.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-2-3-0-rock-floor-station-2.png'></figure>
</div>
</section>
<section>
<h2>passage/2-3/0 — rock-floor/343.4m</h2>
<p>rock-floor-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-rock-floor-station-3.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-2-3-0-rock-floor-station-3.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — section-1/4m-outside</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-1-4m-outside.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-openair-1-4m-outside.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — section-1/4m-interior-wall</h2>
<p>open-air-close · L125 low_contrast_geometry_visible · L128 low_contrast_geometry_visible</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-1-4m-interior-wall.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-openair-1-4m-interior-wall.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — section-2/4m-outside</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-2-4m-outside.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-openair-2-4m-outside.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — section-2/4m-interior-wall</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-2-4m-interior-wall.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-openair-2-4m-interior-wall.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — section-3/4m-outside</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-3-4m-outside.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-openair-3-4m-outside.png'></figure>
</div>
</section>
<section>
<h2>passage/0-1/0 — section-3/4m-interior-wall</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-3-4m-interior-wall.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-0-1-0-openair-3-4m-interior-wall.png'></figure>
</div>
</section>
<section>
<h2>passage/1-2/0 — section-1/4m-outside</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-1-4m-outside.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-1-2-0-openair-1-4m-outside.png'></figure>
</div>
</section>
<section>
<h2>passage/1-2/0 — section-1/4m-interior-wall</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-1-4m-interior-wall.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-1-2-0-openair-1-4m-interior-wall.png'></figure>
</div>
</section>
<section>
<h2>passage/1-2/0 — section-2/4m-outside</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-2-4m-outside.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-1-2-0-openair-2-4m-outside.png'></figure>
</div>
</section>
<section>
<h2>passage/1-2/0 — section-2/4m-interior-wall</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-2-4m-interior-wall.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-1-2-0-openair-2-4m-interior-wall.png'></figure>
</div>
</section>
<section>
<h2>passage/2-3/0 — section-1/4m-outside</h2>
<p>open-air-close · L125 low_contrast_geometry_visible · L128 low_contrast_geometry_visible</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-1-4m-outside.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-2-3-0-openair-1-4m-outside.png'></figure>
</div>
</section>
<section>
<h2>passage/2-3/0 — section-1/4m-interior-wall</h2>
<p>open-air-close · L125 low_contrast_geometry_visible · L128 low_contrast_geometry_visible</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-1-4m-interior-wall.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-2-3-0-openair-1-4m-interior-wall.png'></figure>
</div>
</section>
<section>
<h2>passage/2-3/0 — section-2/4m-outside</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-2-4m-outside.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-2-3-0-openair-2-4m-outside.png'></figure>
</div>
</section>
<section>
<h2>passage/2-3/0 — section-2/4m-interior-wall</h2>
<p>open-air-close · L125 pass · L128 pass</p>
<div>
<figure><figcaption>L125</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-2-4m-interior-wall.png'></figure>
<figure><figcaption>L128 slab ownership</figcaption><img src='L128/passage-2-3-0-openair-2-4m-interior-wall.png'></figure>
</div>
</section>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,9 @@
# Linux130 — passage reach
On `wip/l130-reach-units` from `b96dd73`, passage influence is now **1.0533 m**: 0.75 m shell + 0.21 m relief + 0.50 m smooth-max support (`width/6`) + 0.01 m margin. One metres-to-core-voxels conversion feeds proof, feature, fallback and off-grid bounds; the sweep cutoff uses the same metre value. The dead 4 m cutoff is removed. Evaluator version is 227.
The debug shell/crest audit sampled 25,728 points, including 5 mm inside and outside the reach; 18,993 had sign-relevant passage contribution. Proof and feature bounds missed none. The nine-station floor probe still peaks at **7.884 mm**, matching L125. The 23 L125 views and six L129 mouth close-ups are byte-identical. Fast verification, walkability and shell audits pass; the seam audit found 0 mismatches across 4 pairs / 4,900 shared samples.
Clang/GCC W1/W8/W12 hashes agree across all 18 runs. Versus v225, hashes changed for owner4 full-detail, whole-view and LOD0–3; owner2 surface-world and floating-islands whole-view and LOD2–3. Exact-query hashes stayed unchanged. The v227 canonical baselines are recorded; changed hashes reflect the shorter density influence halo, while sampled sign-relevant bounds and captured surfaces remain covered.
L80 timing used 10 interleaved pairs per stratum/worker count. Across the eight case medians, the weighted deltas were +0.33% for LOD0–2 and +0.31% whole-view. Across 80 paired runs, mean deltas were +0.26% (95% interval −0.44% to +0.96%) and +0.47% (−0.06% to +1.00%); both intervals include zero. No measurable timing change; UE movement was skipped. The Linux UE host build succeeded in 76.30 s with `-NoUBA -MaxParallelActions=4`, without `prlimit`. `ARCHITECTURE.md` and `CODEMAP.md` had no note for this optimized influence reach, so they were unchanged.
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""Run Clang/GCC owner hashes at W1/W8/W12 and compare with b96dd73."""
from __future__ import annotations
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l130/hashes"
BASELINE = json.loads((ROOT / "Saved/Linux/l125/hashes/summary.json").read_text())
DATASETS = {
"owner4": ROOT / "Standalone/data/owner_world.json",
"owner2": ROOT / "Standalone/data/regression/owner_world_2strate_l61.json",
}
RECIPES = {"owner4": ("surface-world",),
"owner2": ("surface-world", "floating-islands")}
BINARIES = {
"clang": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang",
"gcc": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-gcc",
}
HASH_RE = re.compile(r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=(\S+)$", re.M)
def run(dataset: str, recipe: str, workers: int, compiler: str) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
label = f"{dataset}-{recipe}-w{workers}-{compiler}"
log = OUT / f"{label}.log"
layout = OUT / f"{label}.layout.json"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[compiler]), str(DATASETS[dataset]),
f"--workers={workers}", "--world-assembler-prototype", "--stream-benchmark",
"--stream-pipeline", "--force-generate", "--clip-radius=3",
"--max-clip-level=8", "--max-level=8", f"--recipe={recipe}",
"--stream-hash", f"--resolved-layout-output={layout}"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
output = log.read_text(encoding="utf-8", errors="replace")
hashes = HASH_RE.findall(output)
if result.returncode or len(hashes) != 12:
raise SystemExit(f"hash run failed: {label}; exit={result.returncode} "
f"hashes={len(hashes)} log={log}")
stamp = STAMP_RE.search(output)
row = {"dataset": dataset, "recipe": recipe, "workers": workers,
"compiler": compiler, "exit_code": result.returncode, "hashes": hashes,
"source_stamp": stamp.group(1) if stamp else None,
"elapsed_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT))}
print("[L130HashRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
if any(not path.is_file() for path in (*DATASETS.values(), *BINARIES.values())):
raise SystemExit("missing L130 hash input")
old = {(row["dataset"], row["recipe"], row["workers"], row["compiler"]): row
for row in BASELINE["runs"]}
rows = []
for dataset, recipes in RECIPES.items():
for recipe in recipes:
for workers in (1, 8, 12):
for compiler in ("clang", "gcc"):
rows.append(run(dataset, recipe, workers, compiler))
(OUT / "runs-partial.json").write_text(
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
groups = {}
for dataset, recipes in RECIPES.items():
for recipe in recipes:
group = [row for row in rows if row["dataset"] == dataset
and row["recipe"] == recipe]
key = f"{dataset}/{recipe}"
stable = all(row["hashes"] == group[0]["hashes"] for row in group)
baseline = old[(dataset, recipe, 12, "clang")]["hashes"]
changed = [i for i, (before, after) in enumerate(zip(baseline,
group[0]["hashes"])) if before != after]
groups[key] = {"all_workers_and_compilers_match": stable,
"matches_b96dd73_v225": not changed,
"changed_hash_rows_from_v225": changed,
"source_stamps": sorted({row["source_stamp"] for row in group})}
if not stable:
raise SystemExit(f"worker/compiler hash mismatch: {key}")
(OUT / "summary.json").write_text(
json.dumps({"baseline_source": "b96dd73", "runs": rows,
"groups": groups}, indent=2) + "\n", encoding="utf-8")
print("[L130HashSummary] " + json.dumps(groups, sort_keys=True), flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""Render the six L129 b96dd73 mouth views against the L130 meshes."""
from __future__ import annotations
import hashlib
import html
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
SOURCE = ROOT / "Saved/Linux/l129/captures/mouth-closeups/before"
SAVED = ROOT / "Saved/Linux/l130/captures/mouth-closeups"
CAPTURES = ROOT / "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups"
DATA = ROOT / "Standalone/data/owner_world.json"
HEADLESS = ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang"
RENDERER = ROOT / "Saved/Linux/l130/bin/VoxelForgeOffscreenCapture-l130-clang"
HEADLESS_CONFIG = re.compile(
r"^\[HeadlessConfig\].*?camera_m=\(([^)]*)\) camera_rotation_deg=\(([^)]*)\)", re.M)
SCENE = re.compile(r"^\[OffscreenScene\].*?height_center_m=(-?[\d.]+)", re.M)
DRAWN = re.compile(r"drawn_triangles=(\d+)")
def wrapped(command: list[str]) -> list[str]:
return ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", *map(str, command)]
def run(label: str, command: list[str], log: Path) -> str:
started = time.monotonic()
log.parent.mkdir(parents=True, exist_ok=True)
with log.open("w", encoding="utf-8") as output:
result = subprocess.run(wrapped(command), cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
if result.returncode:
raise SystemExit(f"{label} failed exit={result.returncode}: {log}")
print(f"[L130MouthProcess] {label} elapsed_s={time.monotonic()-started:.2f}",
flush=True)
return content
def parse_vector(text: str) -> tuple[float, ...]:
return tuple(float(value) for value in text.split(","))
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def main() -> int:
if any(not path.is_file() for path in (DATA, HEADLESS, RENDERER)):
raise SystemExit("missing L130 mouth capture input")
(CAPTURES / "b96dd73").mkdir(parents=True, exist_ok=True)
(CAPTURES / "L130").mkdir(parents=True, exist_ok=True)
rows = []
for source_log in sorted(SOURCE.glob("*-mouth-mesh.log")):
name = source_log.name.removesuffix("-mesh.log")
source_render = SOURCE / f"{name}-render.log"
baseline_mesh = SOURCE / f"{name}.vfmesh"
if not all(path.is_file() for path in (source_render, baseline_mesh)):
raise SystemExit(f"missing b96dd73 source artifact for {name}")
config = HEADLESS_CONFIG.search(source_log.read_text())
scene = SCENE.search(source_render.read_text())
if config is None or scene is None:
raise SystemExit(f"missing camera metadata for {name}")
camera = parse_vector(config.group(1))
yaw, pitch = parse_vector(config.group(2))
height_center = float(scene.group(1))
camera_arg = ",".join(f"{value:.6f}" for value in (*camera, yaw, pitch))
candidate_mesh = SAVED / "L130" / f"{name}.vfmesh"
candidate_mesh.parent.mkdir(parents=True, exist_ok=True)
run(f"{name}-mesh", [HEADLESS, DATA, "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=12",
"--clip-radius=3", "--max-clip-level=0", "--max-level=0",
"--force-generate", "--recipe=surface-world", f"--camera={camera_arg}",
"--render-distance-chunks=1", f"--mesh-export={candidate_mesh}"],
SAVED / "L130" / f"{name}-mesh.log")
if not candidate_mesh.is_file() or candidate_mesh.stat().st_size == 0:
raise SystemExit(f"empty L130 mesh for {name}")
render_args = ["--size=1920x1080",
f"--camera-m={','.join(f'{value:.6f}' for value in camera)}",
f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}",
"--shade=readable", "--headlamp=on", "--height-tint=on",
f"--height-center-m={height_center:.6f}", "--height-range-m=32"]
images = {}
draw_counts = {}
for variant, mesh in (("b96dd73", baseline_mesh), ("L130", candidate_mesh)):
image = CAPTURES / variant / f"{name}.png"
render_log = SAVED / variant / f"{name}-render.log"
content = run(f"{name}-{variant}-render",
[RENDERER, mesh, image, *render_args], render_log)
drawn = DRAWN.search(content)
if drawn is None or int(drawn.group(1)) <= 0 or not image.is_file():
raise SystemExit(f"render has no visible triangles for {name}/{variant}")
images[variant] = image
draw_counts[variant] = int(drawn.group(1))
equal = images["b96dd73"].read_bytes() == images["L130"].read_bytes()
row = {"name": name, "camera_m": camera, "yaw_deg": yaw,
"pitch_deg": pitch, "height_center_m": height_center,
"source_mesh": str(baseline_mesh.relative_to(ROOT)),
"candidate_mesh": str(candidate_mesh.relative_to(ROOT)),
"baseline_png": str(images["b96dd73"].relative_to(ROOT)),
"candidate_png": str(images["L130"].relative_to(ROOT)),
"baseline_sha256": sha256(images["b96dd73"]),
"candidate_sha256": sha256(images["L130"]),
"byte_identical": equal, "drawn_triangles": draw_counts}
rows.append(row)
print(f"[L130MouthCompare] {name} byte_identical={equal} "
f"sha256={row['candidate_sha256']}", flush=True)
if len(rows) != 6:
raise SystemExit(f"expected six L129 mouth views, found {len(rows)}")
(CAPTURES / "summary.json").write_text(json.dumps(rows, indent=2) + "\n")
sections = []
for row in rows:
name = html.escape(row["name"])
sections.append(f"<section><h2>{name}</h2><p>byte identical: "
f"{str(row['byte_identical']).lower()}</p><div>"
f"<figure><figcaption>b96dd73 source mesh</figcaption><img src='b96dd73/{name}.png'></figure>"
f"<figure><figcaption>L130 reach units</figcaption><img src='L130/{name}.png'></figure>"
"</div></section>")
page = ("<!doctype html><meta charset='utf-8'><title>L130 mouth closeups</title>"
"<style>body{font:14px system-ui;margin:24px;background:#17191d;color:#eee}"
"section{margin:28px 0}div{display:grid;grid-template-columns:1fr 1fr;gap:12px}"
"img{width:100%;height:auto}p{color:#bbb}</style>"
"<h1>L129 mouth closeups: b96dd73 vs L130</h1>" + "\n".join(sections))
(CAPTURES / "COMPARE.html").write_text(page)
return 0 if all(row["byte_identical"] for row in rows) else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""Render the 23 L125 camera poses with the L130 evaluator and compare them."""
from __future__ import annotations
import html
import importlib.util
import json
import shutil
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
RESEARCH = Path(__file__).resolve().parent
CAPTURES = RESEARCH / "captures"
SAVED = ROOT / "Saved/Linux/l130/captures"
BASELINE_INDEX = (ROOT / "Standalone/Research/WorldAssemblerM3/Linux119"
/ "captures/L119/capture-index.json")
REFERENCE = ROOT / "Standalone/Research/WorldAssemblerM3/Linux125/captures/L125"
LAYOUT = ROOT / "Saved/Linux/l130/hashes/owner4-surface-world-w12-clang.layout.json"
HEADLESS = ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang"
RENDERER = ROOT / "Saved/Linux/l130/bin/VoxelForgeOffscreenCapture-l130-clang"
HELPER_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux113/RunL113Captures.py"
def main() -> int:
if any(not path.is_file() for path in
(BASELINE_INDEX, REFERENCE / "passage-0-1-0-rock-floor-station-1.png",
LAYOUT, HEADLESS, RENDERER)):
raise SystemExit("missing pose, layout, or L130 capture input")
spec = importlib.util.spec_from_file_location("l125_capture_runner",
ROOT / "Standalone/Research/WorldAssemblerM3/Linux125/RunL125Captures.py")
if spec is None or spec.loader is None:
raise SystemExit("cannot load the verified L125 pose set")
runner = importlib.util.module_from_spec(spec)
spec.loader.exec_module(runner)
runner.RESEARCH = RESEARCH
runner.CAPTURES = CAPTURES
runner.SAVED = SAVED
runner.LAYOUT = LAYOUT
runner.CLANG = HEADLESS
runner.RENDERER = RENDERER
runner.HELPERS.RESEARCH = RESEARCH
runner.HELPERS.CAPTURES = CAPTURES
runner.HELPERS.SAVED = SAVED
runner.HELPERS.BINARIES = {"L125": HEADLESS}
runner.HELPERS.RENDERERS = {"L125": RENDERER}
if runner.main() != 0:
raise SystemExit("L130 pose generation failed")
new_captures = CAPTURES / "L125"
new_saved = SAVED / "L125"
index_path = new_captures / "capture-index.json"
data = json.loads(index_path.read_text())
if len(data["captures"]) != 23:
raise SystemExit(f"expected 23 poses, found {len(data['captures'])}")
identical = 0
sections = []
for row in data["captures"]:
name = row["name"]
old_image = REFERENCE / f"{name}.png"
new_image = new_captures / f"{name}.png"
if not old_image.is_file() or not new_image.is_file():
raise SystemExit(f"missing comparison image for {name}")
same = old_image.read_bytes() == new_image.read_bytes()
identical += int(same)
row["variant"] = "L130-v227"
row["capture"] = row["capture"].replace("captures/L125/", "captures/L130/")
row["scene"] = row["scene"].replace("captures/L125/", "captures/L130/")
row["logs"] = [path.replace("captures/L125/", "captures/L130/")
for path in row.get("logs", [])]
sections.append(f"<section><h2>{html.escape(row['passage'])} — "
f"{html.escape(row.get('station', ''))}</h2><p>{html.escape(row['kind'])} · "
f"byte identical: {str(same).lower()}</p><div>"
f"<figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/{html.escape(name)}.png'></figure>"
f"<figure><figcaption>L130 v227</figcaption><img src='L130/{html.escape(name)}.png'></figure>"
"</div></section>")
if identical != 23:
raise SystemExit(f"only {identical}/23 L125 pose captures are byte-identical")
index_path.write_text(json.dumps(data, indent=2) + "\n")
old_captures, old_saved = CAPTURES / "L130", SAVED / "L130"
if old_captures.exists():
shutil.rmtree(old_captures)
if old_saved.exists():
shutil.rmtree(old_saved)
new_captures.rename(old_captures)
new_saved.rename(old_saved)
page = ("<!doctype html><meta charset='utf-8'><title>L130 passage captures</title>"
"<style>body{font:14px system-ui;margin:24px;background:#17191d;color:#eee}"
"section{margin:28px 0}div{display:grid;grid-template-columns:1fr 1fr;gap:12px}"
"img{width:100%;height:auto}p{color:#bbb}</style>"
"<h1>L125 v225 vs L130 v227: passage captures</h1>" + "\n".join(sections))
(CAPTURES / "COMPARE.html").write_text(page)
print(f"[L130PoseCapture] poses=23 byte_identical={identical}/23", flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""Five interleaved L80 near-ready runs per stratum/worker vs b96dd73."""
from __future__ import annotations
import json
import os
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l130/timing"
PASS_ID = int(os.environ.get("VOXELFORGE_L130_TIMING_PASS", "1"))
REPLICATE_FIRST = (PASS_ID - 1) * 5 + 1
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"b96dd73": ROOT / "Saved/Linux/l125/bin/VoxelForgeCoreHeadless-l125-clang",
"L130": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang",
}
CAMERAS = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
PIPELINE_RE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
FIELDS_RE = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
NEAR_RE = re.compile(
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
re.M)
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
pass_suffix = "" if PASS_ID == 1 else f"-p{PASS_ID}"
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}-l130-v227{pass_suffix}"
log = OUT / f"{label}.log"
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[version]), str(DATA),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
f"--workers={workers}", "--clip-radius=3", "--max-clip-level=8",
"--max-level=8", f"--camera={camera}", "--profile-lod-work",
"--stream-scheduler-policy=3"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
result = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
pipeline, near = PIPELINE_RE.search(content), NEAR_RE.search(content)
if result.returncode or pipeline is None or near is None:
raise SystemExit(f"timing run failed: {log}; exit={result.returncode}")
fields = dict(FIELDS_RE.findall(pipeline.group(1)))
row = {"strate": strate, "workers": workers, "version": version,
"phase": phase, "replicate": replicate, "exit_code": result.returncode,
"wall_s": round(time.monotonic() - started, 3),
"lod02_ready_s": float(near.group(3)),
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
"log": str(log.relative_to(ROOT))}
print("[L130TimingRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
raise SystemExit("missing L130 timing input")
OUT.mkdir(parents=True, exist_ok=True)
load_1m = __import__("os").getloadavg()[0]
def cpu_idle(interval: float = 2.0) -> float:
def totals() -> tuple[int, int]:
fields = Path("/proc/stat").read_text().splitlines()[0].split()
values = [int(value) for value in fields[1:]]
return sum(values), values[3] + (values[4] if len(values) > 4 else 0)
before = totals()
time.sleep(interval)
after = totals()
return 100.0 * (after[1] - before[1]) / max(1, after[0] - before[0])
idle = cpu_idle()
pass_suffix = "" if PASS_ID == 1 else f"-p{PASS_ID}"
(OUT / f"machine-load-v227{pass_suffix}-before.txt").write_text(
f"1-minute load: {load_1m:.2f}\n2-second CPU idle: {idle:.1f}%\n")
print(f"[L130TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%",
flush=True)
if load_1m >= 0.9 or idle < 97.0:
raise SystemExit("timing refused: machine is above the quiet-load gate")
rows = []
for strate in range(4):
for workers in (8, 12):
for version in BINARIES:
run(strate, workers, version, "warmup", 0)
for replicate in range(REPLICATE_FIRST, REPLICATE_FIRST + 5):
order = tuple(BINARIES) if replicate % 2 else tuple(reversed(BINARIES))
for version in order:
rows.append(run(strate, workers, version, "measured", replicate))
(OUT / f"runs-v227{pass_suffix}-partial.json").write_text(
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
prior_runs = []
if PASS_ID > 1:
previous = OUT / ("v227-summary.json" if PASS_ID == 2
else "v227-combined-summary.json")
if previous.is_file():
prior_runs = json.loads(previous.read_text(encoding="utf-8")).get("runs", [])
all_rows = prior_runs + rows
measured_rows = [row for row in all_rows if row["phase"] == "measured"]
summary = []
for strate in range(4):
for workers in (8, 12):
group_size = sum(row["strate"] == strate and row["workers"] == workers
for row in measured_rows) // 2
item = {"strate": strate, "workers": workers,
"replicates_per_version": group_size}
medians = {}
for version in BINARIES:
group = [row for row in measured_rows if row["strate"] == strate
and row["workers"] == workers and row["version"] == version]
medians[version] = {}
for key in ("lod02_ready_s", "whole_view_ready_s"):
values = [row[key] for row in group]
medians[version][key] = statistics.median(values)
item[f"{version}_{key}_values"] = values
item[f"{version}_{key}_median"] = medians[version][key]
for key in ("lod02_ready_s", "whole_view_ready_s"):
item[f"L130_vs_b96dd73_{key}_delta_pct"] = round(
100.0*(medians["L130"][key]/medians["b96dd73"][key]-1.0), 2)
summary.append(item)
summary_path = OUT / ("v227-summary.json" if PASS_ID == 1
else f"v227-combined-summary.json")
summary_path.write_text(json.dumps(
{"runs": all_rows, "summary": summary}, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,23 @@
<!doctype html><meta charset='utf-8'><title>L130 passage captures</title><style>body{font:14px system-ui;margin:24px;background:#17191d;color:#eee}section{margin:28px 0}div{display:grid;grid-template-columns:1fr 1fr;gap:12px}img{width:100%;height:auto}p{color:#bbb}</style><h1>L125 v225 vs L130 v227: passage captures</h1><section><h2>passage/0-1/0 — rock-floor/401.7m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-rock-floor-station-1.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-rock-floor-station-1.png'></figure></div></section>
<section><h2>passage/0-1/0 — rock-floor/792.8m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-rock-floor-station-2.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-rock-floor-station-2.png'></figure></div></section>
<section><h2>passage/0-1/0 — rock-floor/1634.5m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-rock-floor-station-3.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-rock-floor-station-3.png'></figure></div></section>
<section><h2>passage/1-2/0 — rock-floor/27.0m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-rock-floor-station-1.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-1-2-0-rock-floor-station-1.png'></figure></div></section>
<section><h2>passage/1-2/0 — rock-floor/52.9m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-rock-floor-station-2.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-1-2-0-rock-floor-station-2.png'></figure></div></section>
<section><h2>passage/1-2/0 — rock-floor/78.9m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-rock-floor-station-3.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-1-2-0-rock-floor-station-3.png'></figure></div></section>
<section><h2>passage/2-3/0 — rock-floor/323.4m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-rock-floor-station-1.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-2-3-0-rock-floor-station-1.png'></figure></div></section>
<section><h2>passage/2-3/0 — rock-floor/333.4m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-rock-floor-station-2.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-2-3-0-rock-floor-station-2.png'></figure></div></section>
<section><h2>passage/2-3/0 — rock-floor/343.4m</h2><p>rock-floor-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-rock-floor-station-3.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-2-3-0-rock-floor-station-3.png'></figure></div></section>
<section><h2>passage/0-1/0 — section-1/4m-outside</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-1-4m-outside.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-openair-1-4m-outside.png'></figure></div></section>
<section><h2>passage/0-1/0 — section-1/4m-interior-wall</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-1-4m-interior-wall.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-openair-1-4m-interior-wall.png'></figure></div></section>
<section><h2>passage/0-1/0 — section-2/4m-outside</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-2-4m-outside.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-openair-2-4m-outside.png'></figure></div></section>
<section><h2>passage/0-1/0 — section-2/4m-interior-wall</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-2-4m-interior-wall.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-openair-2-4m-interior-wall.png'></figure></div></section>
<section><h2>passage/0-1/0 — section-3/4m-outside</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-3-4m-outside.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-openair-3-4m-outside.png'></figure></div></section>
<section><h2>passage/0-1/0 — section-3/4m-interior-wall</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-0-1-0-openair-3-4m-interior-wall.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-0-1-0-openair-3-4m-interior-wall.png'></figure></div></section>
<section><h2>passage/1-2/0 — section-1/4m-outside</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-1-4m-outside.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-1-2-0-openair-1-4m-outside.png'></figure></div></section>
<section><h2>passage/1-2/0 — section-1/4m-interior-wall</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-1-4m-interior-wall.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-1-2-0-openair-1-4m-interior-wall.png'></figure></div></section>
<section><h2>passage/1-2/0 — section-2/4m-outside</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-2-4m-outside.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-1-2-0-openair-2-4m-outside.png'></figure></div></section>
<section><h2>passage/1-2/0 — section-2/4m-interior-wall</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-1-2-0-openair-2-4m-interior-wall.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-1-2-0-openair-2-4m-interior-wall.png'></figure></div></section>
<section><h2>passage/2-3/0 — section-1/4m-outside</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-1-4m-outside.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-2-3-0-openair-1-4m-outside.png'></figure></div></section>
<section><h2>passage/2-3/0 — section-1/4m-interior-wall</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-1-4m-interior-wall.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-2-3-0-openair-1-4m-interior-wall.png'></figure></div></section>
<section><h2>passage/2-3/0 — section-2/4m-outside</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-2-4m-outside.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-2-3-0-openair-2-4m-outside.png'></figure></div></section>
<section><h2>passage/2-3/0 — section-2/4m-interior-wall</h2><p>open-air-close · byte identical: true</p><div><figure><figcaption>L125 v225</figcaption><img src='../../Linux125/captures/L125/passage-2-3-0-openair-2-4m-interior-wall.png'></figure><figure><figcaption>L130 v227</figcaption><img src='L130/passage-2-3-0-openair-2-4m-interior-wall.png'></figure></div></section>
@@ -28,11 +28,11 @@
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"planned_floor_m": -445.66373993191075, "planned_floor_m": -445.66373993191075,
"planned_grade_deg": 10.372328677972732, "planned_grade_deg": 10.372328677972732,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-rock-floor-station-1.png", "capture": "captures/L130/passage-0-1-0-rock-floor-station-1.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-1.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-1.vfmesh",
"drawn_triangles": 122264, "drawn_triangles": 122294,
"source_triangles": 146688, "source_triangles": 146798,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 401.6612010038529, "route_distance_actual_m": 401.6612010038529,
@@ -54,9 +54,9 @@
-445.66373993191075 -445.66373993191075
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-1-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-1-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-1-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-1-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-1-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-1-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
-7.026885053959959, -7.026885053959959,
@@ -100,11 +100,11 @@
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"planned_floor_m": -516.0616688299093, "planned_floor_m": -516.0616688299093,
"planned_grade_deg": 10.367725129664787, "planned_grade_deg": 10.367725129664787,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-rock-floor-station-2.png", "capture": "captures/L130/passage-0-1-0-rock-floor-station-2.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-2.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-2.vfmesh",
"drawn_triangles": 586094, "drawn_triangles": 586092,
"source_triangles": 626214, "source_triangles": 626196,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 792.8284020077059, "route_distance_actual_m": 792.8284020077059,
@@ -126,9 +126,9 @@
-516.0616688299093 -516.0616688299093
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-2-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-2-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-2-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-2-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-2-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-2-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
-2.471476828908, -2.471476828908,
@@ -172,11 +172,11 @@
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"planned_floor_m": -667.5348848643227, "planned_floor_m": -667.5348848643227,
"planned_grade_deg": 10.360515186362372, "planned_grade_deg": 10.360515186362372,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-rock-floor-station-3.png", "capture": "captures/L130/passage-0-1-0-rock-floor-station-3.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-3.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-3.vfmesh",
"drawn_triangles": 137258, "drawn_triangles": 137288,
"source_triangles": 166706, "source_triangles": 166736,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 1634.4566030115589, "route_distance_actual_m": 1634.4566030115589,
@@ -198,9 +198,9 @@
-667.5348848643227 -667.5348848643227
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-3-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-3-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-3-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-3-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-rock-floor-station-3-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-rock-floor-station-3-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
17.33622182815858, 17.33622182815858,
@@ -244,11 +244,11 @@
"route_length_m": 114.99063175809309, "route_length_m": 114.99063175809309,
"planned_floor_m": -738.1970266319915, "planned_floor_m": -738.1970266319915,
"planned_grade_deg": 10.657740387934142, "planned_grade_deg": 10.657740387934142,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-1-2-0-rock-floor-station-1.png", "capture": "captures/L130/passage-1-2-0-rock-floor-station-1.png",
"scene": "Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-1.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-1.vfmesh",
"drawn_triangles": 652040, "drawn_triangles": 652056,
"source_triangles": 719644, "source_triangles": 719660,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 26.963657939523273, "route_distance_actual_m": 26.963657939523273,
@@ -270,9 +270,9 @@
-738.1970266319915 -738.1970266319915
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-1-mesh.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-1-mesh.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-1-render.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-1-render.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-1-quality.log" "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-1-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
15.354124807038394, 15.354124807038394,
@@ -316,11 +316,11 @@
"route_length_m": 114.99063175809309, "route_length_m": 114.99063175809309,
"planned_floor_m": -742.995373541633, "planned_floor_m": -742.995373541633,
"planned_grade_deg": 10.645569354114926, "planned_grade_deg": 10.645569354114926,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-1-2-0-rock-floor-station-2.png", "capture": "captures/L130/passage-1-2-0-rock-floor-station-2.png",
"scene": "Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-2.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-2.vfmesh",
"drawn_triangles": 636134, "drawn_triangles": 636162,
"source_triangles": 700530, "source_triangles": 700558,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 52.927315879046546, "route_distance_actual_m": 52.927315879046546,
@@ -342,9 +342,9 @@
-742.995373541633 -742.995373541633
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-2-mesh.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-2-mesh.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-2-render.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-2-render.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-2-quality.log" "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-2-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
-4.766428465537912, -4.766428465537912,
@@ -388,11 +388,11 @@
"route_length_m": 114.99063175809309, "route_length_m": 114.99063175809309,
"planned_floor_m": -747.7963166548619, "planned_floor_m": -747.7963166548619,
"planned_grade_deg": 10.659092990906162, "planned_grade_deg": 10.659092990906162,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-1-2-0-rock-floor-station-3.png", "capture": "captures/L130/passage-1-2-0-rock-floor-station-3.png",
"scene": "Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-3.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-3.vfmesh",
"drawn_triangles": 580464, "drawn_triangles": 580472,
"source_triangles": 639368, "source_triangles": 639376,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 78.89097381856982, "route_distance_actual_m": 78.89097381856982,
@@ -414,9 +414,9 @@
-747.7963166548619 -747.7963166548619
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-3-mesh.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-3-mesh.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-3-render.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-3-render.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-rock-floor-station-3-quality.log" "Saved/Linux/l130/captures/L130/passage-1-2-0-rock-floor-station-3-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
-12.283352972838873, -12.283352972838873,
@@ -460,11 +460,11 @@
"route_length_m": 494.0722798830563, "route_length_m": 494.0722798830563,
"planned_floor_m": -871.0755965556104, "planned_floor_m": -871.0755965556104,
"planned_grade_deg": 10.274530613220444, "planned_grade_deg": 10.274530613220444,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-2-3-0-rock-floor-station-1.png", "capture": "captures/L130/passage-2-3-0-rock-floor-station-1.png",
"scene": "Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-1.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-1.vfmesh",
"drawn_triangles": 474960, "drawn_triangles": 474964,
"source_triangles": 502358, "source_triangles": 502362,
"verified": true, "verified": true,
"pixel_quality_status": "low_contrast_geometry_visible", "pixel_quality_status": "low_contrast_geometry_visible",
"route_distance_actual_m": 323.4481319239866, "route_distance_actual_m": 323.4481319239866,
@@ -486,9 +486,9 @@
-871.0755965556104 -871.0755965556104
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-1-mesh.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-1-mesh.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-1-render.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-1-render.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-1-quality.log" "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-1-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
9.103799793973732, 9.103799793973732,
@@ -532,11 +532,11 @@
"route_length_m": 494.0722798830563, "route_length_m": 494.0722798830563,
"planned_floor_m": -872.8561420785873, "planned_floor_m": -872.8561420785873,
"planned_grade_deg": 10.266334408339132, "planned_grade_deg": 10.266334408339132,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-2-3-0-rock-floor-station-2.png", "capture": "captures/L130/passage-2-3-0-rock-floor-station-2.png",
"scene": "Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-2.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-2.vfmesh",
"drawn_triangles": 494500, "drawn_triangles": 494504,
"source_triangles": 524338, "source_triangles": 524342,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 333.4481319239866, "route_distance_actual_m": 333.4481319239866,
@@ -558,9 +558,9 @@
-872.8561420785873 -872.8561420785873
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-2-mesh.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-2-mesh.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-2-render.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-2-render.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-2-quality.log" "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-2-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
15.204108325185125, 15.204108325185125,
@@ -604,11 +604,11 @@
"route_length_m": 494.0722798830563, "route_length_m": 494.0722798830563,
"planned_floor_m": -874.6379150751349, "planned_floor_m": -874.6379150751349,
"planned_grade_deg": 10.259484647608062, "planned_grade_deg": 10.259484647608062,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-2-3-0-rock-floor-station-3.png", "capture": "captures/L130/passage-2-3-0-rock-floor-station-3.png",
"scene": "Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-3.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-3.vfmesh",
"drawn_triangles": 485352, "drawn_triangles": 485356,
"source_triangles": 512660, "source_triangles": 512664,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 343.4481319239866, "route_distance_actual_m": 343.4481319239866,
@@ -630,9 +630,9 @@
-874.6379150751349 -874.6379150751349
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-3-mesh.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-3-mesh.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-3-render.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-3-render.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-rock-floor-station-3-quality.log" "Saved/Linux/l130/captures/L130/passage-2-3-0-rock-floor-station-3-quality.log"
], ],
"floor_anchor_position_m": [ "floor_anchor_position_m": [
15.464091528009144, 15.464091528009144,
@@ -682,11 +682,11 @@
"route_distance_m": 8.3545, "route_distance_m": 8.3545,
"requested_route_distance_m": 8.3545, "requested_route_distance_m": 8.3545,
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-openair-1-4m-outside.png", "capture": "captures/L130/passage-0-1-0-openair-1-4m-outside.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-outside.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-outside.vfmesh",
"drawn_triangles": 275333, "drawn_triangles": 275333,
"source_triangles": 327761, "source_triangles": 327745,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 8.3545, "route_distance_actual_m": 8.3545,
@@ -708,9 +708,9 @@
-373.75 -373.75
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-outside-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-outside-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-outside-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-outside-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-outside-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-outside-quality.log"
] ]
}, },
{ {
@@ -747,11 +747,11 @@
"route_distance_m": 8.3545, "route_distance_m": 8.3545,
"requested_route_distance_m": 8.3545, "requested_route_distance_m": 8.3545,
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-openair-1-4m-interior-wall.png", "capture": "captures/L130/passage-0-1-0-openair-1-4m-interior-wall.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-interior-wall.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-interior-wall.vfmesh",
"drawn_triangles": 308012, "drawn_triangles": 308012,
"source_triangles": 368140, "source_triangles": 368124,
"verified": true, "verified": true,
"pixel_quality_status": "low_contrast_geometry_visible", "pixel_quality_status": "low_contrast_geometry_visible",
"route_distance_actual_m": 8.3545, "route_distance_actual_m": 8.3545,
@@ -773,9 +773,9 @@
-373.75 -373.75
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-interior-wall-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-interior-wall-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-interior-wall-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-interior-wall-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-1-4m-interior-wall-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-1-4m-interior-wall-quality.log"
] ]
}, },
{ {
@@ -812,11 +812,11 @@
"route_distance_m": 14.4925, "route_distance_m": 14.4925,
"requested_route_distance_m": 14.4925, "requested_route_distance_m": 14.4925,
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-openair-2-4m-outside.png", "capture": "captures/L130/passage-0-1-0-openair-2-4m-outside.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-outside.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-outside.vfmesh",
"drawn_triangles": 275333, "drawn_triangles": 275333,
"source_triangles": 327761, "source_triangles": 327745,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 14.4925, "route_distance_actual_m": 14.4925,
@@ -838,9 +838,9 @@
-376.0 -376.0
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-outside-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-outside-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-outside-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-outside-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-outside-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-outside-quality.log"
] ]
}, },
{ {
@@ -877,11 +877,11 @@
"route_distance_m": 14.4925, "route_distance_m": 14.4925,
"requested_route_distance_m": 14.4925, "requested_route_distance_m": 14.4925,
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-openair-2-4m-interior-wall.png", "capture": "captures/L130/passage-0-1-0-openair-2-4m-interior-wall.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-interior-wall.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-interior-wall.vfmesh",
"drawn_triangles": 345044, "drawn_triangles": 345026,
"source_triangles": 414692, "source_triangles": 414658,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 14.4925, "route_distance_actual_m": 14.4925,
@@ -903,9 +903,9 @@
-376.0 -376.0
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-interior-wall-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-interior-wall-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-interior-wall-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-interior-wall-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-2-4m-interior-wall-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-2-4m-interior-wall-quality.log"
] ]
}, },
{ {
@@ -942,35 +942,35 @@
"route_distance_m": 1265.3425000000002, "route_distance_m": 1265.3425000000002,
"requested_route_distance_m": 1265.3425000000002, "requested_route_distance_m": 1265.3425000000002,
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-openair-3-4m-outside.png", "capture": "captures/L130/passage-0-1-0-openair-3-4m-outside.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-outside.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-outside.vfmesh",
"drawn_triangles": 151372, "drawn_triangles": 149234,
"source_triangles": 183724, "source_triangles": 181128,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 1265.3425000000002, "route_distance_actual_m": 1265.3425000000002,
"route_station_adjustment_m": 0.0, "route_station_adjustment_m": 0.0,
"closeup_camera_target_distance_m": 3.9999999999999947, "closeup_camera_target_distance_m": 3.9999999999999907,
"closeup_target_surface_distance_m": 0.06828066292003203, "closeup_target_surface_distance_m": 0.06836631107301705,
"closeup_predicted_target_offset_m": 0.06828066292003203, "closeup_predicted_target_offset_m": 0.06836631107301705,
"closeup_wall_normal_alignment": 0.9604791479186932, "closeup_wall_normal_alignment": 0.9604329420550252,
"fallback_kind": null, "fallback_kind": null,
"fallback_surface_distance_m": null, "fallback_surface_distance_m": null,
"camera_actual_m": [ "camera_actual_m": [
-28.639699107248397, -28.63982379180018,
-15.583356294879769, -15.582695163623317,
-601.2437793015765 -601.2438490311043
], ],
"target_actual_m": [ "target_actual_m": [
-24.95574413685722, -24.955621627611308,
-14.176529326299274, -14.176566764792845,
-600.5734706737007 -600.5734332352072
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-outside-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-outside-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-outside-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-outside-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-outside-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-outside-quality.log"
] ]
}, },
{ {
@@ -1007,35 +1007,35 @@
"route_distance_m": 1265.3425000000002, "route_distance_m": 1265.3425000000002,
"requested_route_distance_m": 1265.3425000000002, "requested_route_distance_m": 1265.3425000000002,
"route_length_m": 2026.6238040154117, "route_length_m": 2026.6238040154117,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-0-1-0-openair-3-4m-interior-wall.png", "capture": "captures/L130/passage-0-1-0-openair-3-4m-interior-wall.png",
"scene": "Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-interior-wall.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-interior-wall.vfmesh",
"drawn_triangles": 150614, "drawn_triangles": 148100,
"source_triangles": 183232, "source_triangles": 180244,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 1265.3425000000002, "route_distance_actual_m": 1265.3425000000002,
"route_station_adjustment_m": 0.0, "route_station_adjustment_m": 0.0,
"closeup_camera_target_distance_m": 4.000000000000011, "closeup_camera_target_distance_m": 4.000000000000004,
"closeup_target_surface_distance_m": 0.15815699667954516, "closeup_target_surface_distance_m": 0.15667051955483755,
"closeup_predicted_target_offset_m": 0.15815699667954516, "closeup_predicted_target_offset_m": 0.15667051955483755,
"closeup_wall_normal_alignment": 0.9379407978793181, "closeup_wall_normal_alignment": 0.936006465878356,
"fallback_kind": null, "fallback_kind": null,
"fallback_surface_distance_m": null, "fallback_surface_distance_m": null,
"camera_actual_m": [ "camera_actual_m": [
-21.87278031692096, -21.875641690065105,
-11.881551644506661, -11.887416826703374,
-599.3233300443438 -599.3020917133557
], ],
"target_actual_m": [ "target_actual_m": [
-24.868945473985846, -24.866552934428398,
-14.149254069257264, -14.149067333208382,
-600.6946505992728 -600.6947104389109
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-interior-wall-mesh.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-interior-wall-mesh.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-interior-wall-render.log", "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-interior-wall-render.log",
"Saved/Linux/l128/captures/L128/passage-0-1-0-openair-3-4m-interior-wall-quality.log" "Saved/Linux/l130/captures/L130/passage-0-1-0-openair-3-4m-interior-wall-quality.log"
] ]
}, },
{ {
@@ -1072,11 +1072,11 @@
"route_distance_m": 83.504, "route_distance_m": 83.504,
"requested_route_distance_m": 83.504, "requested_route_distance_m": 83.504,
"route_length_m": 114.99063175809309, "route_length_m": 114.99063175809309,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-1-2-0-openair-1-4m-outside.png", "capture": "captures/L130/passage-1-2-0-openair-1-4m-outside.png",
"scene": "Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-outside.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-outside.vfmesh",
"drawn_triangles": 620798, "drawn_triangles": 620814,
"source_triangles": 684760, "source_triangles": 684776,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 83.504, "route_distance_actual_m": 83.504,
@@ -1098,9 +1098,9 @@
-747.5006103515625 -747.5006103515625
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-outside-mesh.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-outside-mesh.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-outside-render.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-outside-render.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-outside-quality.log" "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-outside-quality.log"
] ]
}, },
{ {
@@ -1137,11 +1137,11 @@
"route_distance_m": 83.504, "route_distance_m": 83.504,
"requested_route_distance_m": 83.504, "requested_route_distance_m": 83.504,
"route_length_m": 114.99063175809309, "route_length_m": 114.99063175809309,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-1-2-0-openair-1-4m-interior-wall.png", "capture": "captures/L130/passage-1-2-0-openair-1-4m-interior-wall.png",
"scene": "Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-interior-wall.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-interior-wall.vfmesh",
"drawn_triangles": 609749, "drawn_triangles": 609757,
"source_triangles": 668123, "source_triangles": 668131,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 83.504, "route_distance_actual_m": 83.504,
@@ -1163,9 +1163,9 @@
-748.1744141343152 -748.1744141343152
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-interior-wall-mesh.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-interior-wall-mesh.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-interior-wall-render.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-interior-wall-render.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-1-4m-interior-wall-quality.log" "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-1-4m-interior-wall-quality.log"
] ]
}, },
{ {
@@ -1202,11 +1202,11 @@
"route_distance_m": 104.362, "route_distance_m": 104.362,
"requested_route_distance_m": 104.362, "requested_route_distance_m": 104.362,
"route_length_m": 114.99063175809309, "route_length_m": 114.99063175809309,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-1-2-0-openair-2-4m-outside.png", "capture": "captures/L130/passage-1-2-0-openair-2-4m-outside.png",
"scene": "Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-outside.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-outside.vfmesh",
"drawn_triangles": 584534, "drawn_triangles": 584542,
"source_triangles": 636994, "source_triangles": 637002,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 104.362, "route_distance_actual_m": 104.362,
@@ -1228,9 +1228,9 @@
-752.0293187551489 -752.0293187551489
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-outside-mesh.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-outside-mesh.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-outside-render.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-outside-render.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-outside-quality.log" "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-outside-quality.log"
] ]
}, },
{ {
@@ -1267,11 +1267,11 @@
"route_distance_m": 104.362, "route_distance_m": 104.362,
"requested_route_distance_m": 104.362, "requested_route_distance_m": 104.362,
"route_length_m": 114.99063175809309, "route_length_m": 114.99063175809309,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-1-2-0-openair-2-4m-interior-wall.png", "capture": "captures/L130/passage-1-2-0-openair-2-4m-interior-wall.png",
"scene": "Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-interior-wall.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-interior-wall.vfmesh",
"drawn_triangles": 572250, "drawn_triangles": 572258,
"source_triangles": 618638, "source_triangles": 618646,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 104.362, "route_distance_actual_m": 104.362,
@@ -1293,9 +1293,9 @@
-752.0074179101423 -752.0074179101423
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-interior-wall-mesh.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-interior-wall-mesh.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-interior-wall-render.log", "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-interior-wall-render.log",
"Saved/Linux/l128/captures/L128/passage-1-2-0-openair-2-4m-interior-wall-quality.log" "Saved/Linux/l130/captures/L130/passage-1-2-0-openair-2-4m-interior-wall-quality.log"
] ]
}, },
{ {
@@ -1332,9 +1332,9 @@
"route_distance_m": 16.616500000000002, "route_distance_m": 16.616500000000002,
"requested_route_distance_m": 16.616500000000002, "requested_route_distance_m": 16.616500000000002,
"route_length_m": 494.0722798830563, "route_length_m": 494.0722798830563,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-2-3-0-openair-1-4m-outside.png", "capture": "captures/L130/passage-2-3-0-openair-1-4m-outside.png",
"scene": "Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-outside.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-outside.vfmesh",
"drawn_triangles": 104612, "drawn_triangles": 104612,
"source_triangles": 129326, "source_triangles": 129326,
"verified": true, "verified": true,
@@ -1358,9 +1358,9 @@
-816.1078690565205 -816.1078690565205
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-outside-mesh.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-outside-mesh.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-outside-render.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-outside-render.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-outside-quality.log" "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-outside-quality.log"
] ]
}, },
{ {
@@ -1397,9 +1397,9 @@
"route_distance_m": 16.616500000000002, "route_distance_m": 16.616500000000002,
"requested_route_distance_m": 16.616500000000002, "requested_route_distance_m": 16.616500000000002,
"route_length_m": 494.0722798830563, "route_length_m": 494.0722798830563,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-2-3-0-openair-1-4m-interior-wall.png", "capture": "captures/L130/passage-2-3-0-openair-1-4m-interior-wall.png",
"scene": "Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-interior-wall.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-interior-wall.vfmesh",
"drawn_triangles": 94962, "drawn_triangles": 94962,
"source_triangles": 118028, "source_triangles": 118028,
"verified": true, "verified": true,
@@ -1423,9 +1423,9 @@
-815.6327986602337 -815.6327986602337
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-interior-wall-mesh.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-interior-wall-mesh.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-interior-wall-render.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-interior-wall-render.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-1-4m-interior-wall-quality.log" "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-1-4m-interior-wall-quality.log"
] ]
}, },
{ {
@@ -1462,11 +1462,11 @@
"route_distance_m": 374.929, "route_distance_m": 374.929,
"requested_route_distance_m": 374.929, "requested_route_distance_m": 374.929,
"route_length_m": 494.0722798830563, "route_length_m": 494.0722798830563,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-2-3-0-openair-2-4m-outside.png", "capture": "captures/L130/passage-2-3-0-openair-2-4m-outside.png",
"scene": "Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-outside.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-outside.vfmesh",
"drawn_triangles": 475856, "drawn_triangles": 475860,
"source_triangles": 504240, "source_triangles": 504244,
"verified": true, "verified": true,
"pixel_quality_status": "pass", "pixel_quality_status": "pass",
"route_distance_actual_m": 374.929, "route_distance_actual_m": 374.929,
@@ -1488,9 +1488,9 @@
-879.7662205769708 -879.7662205769708
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-outside-mesh.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-outside-mesh.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-outside-render.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-outside-render.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-outside-quality.log" "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-outside-quality.log"
] ]
}, },
{ {
@@ -1527,9 +1527,9 @@
"route_distance_m": 374.929, "route_distance_m": 374.929,
"requested_route_distance_m": 374.929, "requested_route_distance_m": 374.929,
"route_length_m": 494.0722798830563, "route_length_m": 494.0722798830563,
"variant": "L128-slab-ownership-v226", "variant": "L130-v227",
"capture": "captures/L128/passage-2-3-0-openair-2-4m-interior-wall.png", "capture": "captures/L130/passage-2-3-0-openair-2-4m-interior-wall.png",
"scene": "Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-interior-wall.vfmesh", "scene": "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-interior-wall.vfmesh",
"drawn_triangles": 486480, "drawn_triangles": 486480,
"source_triangles": 513552, "source_triangles": 513552,
"verified": true, "verified": true,
@@ -1553,9 +1553,9 @@
-879.75 -879.75
], ],
"logs": [ "logs": [
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-interior-wall-mesh.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-interior-wall-mesh.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-interior-wall-render.log", "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-interior-wall-render.log",
"Saved/Linux/l128/captures/L128/passage-2-3-0-openair-2-4m-interior-wall-quality.log" "Saved/Linux/l130/captures/L130/passage-2-3-0-openair-2-4m-interior-wall-quality.log"
] ]
} }
] ]
Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

@@ -0,0 +1,6 @@
<!doctype html><meta charset='utf-8'><title>L130 mouth closeups</title><style>body{font:14px system-ui;margin:24px;background:#17191d;color:#eee}section{margin:28px 0}div{display:grid;grid-template-columns:1fr 1fr;gap:12px}img{width:100%;height:auto}p{color:#bbb}</style><h1>L129 mouth closeups: b96dd73 vs L130</h1><section><h2>passage-0-1-0-lower-mouth</h2><p>byte identical: true</p><div><figure><figcaption>b96dd73 source mesh</figcaption><img src='b96dd73/passage-0-1-0-lower-mouth.png'></figure><figure><figcaption>L130 reach units</figcaption><img src='L130/passage-0-1-0-lower-mouth.png'></figure></div></section>
<section><h2>passage-0-1-0-upper-mouth</h2><p>byte identical: true</p><div><figure><figcaption>b96dd73 source mesh</figcaption><img src='b96dd73/passage-0-1-0-upper-mouth.png'></figure><figure><figcaption>L130 reach units</figcaption><img src='L130/passage-0-1-0-upper-mouth.png'></figure></div></section>
<section><h2>passage-1-2-0-lower-mouth</h2><p>byte identical: true</p><div><figure><figcaption>b96dd73 source mesh</figcaption><img src='b96dd73/passage-1-2-0-lower-mouth.png'></figure><figure><figcaption>L130 reach units</figcaption><img src='L130/passage-1-2-0-lower-mouth.png'></figure></div></section>
<section><h2>passage-1-2-0-upper-mouth</h2><p>byte identical: true</p><div><figure><figcaption>b96dd73 source mesh</figcaption><img src='b96dd73/passage-1-2-0-upper-mouth.png'></figure><figure><figcaption>L130 reach units</figcaption><img src='L130/passage-1-2-0-upper-mouth.png'></figure></div></section>
<section><h2>passage-2-3-0-lower-mouth</h2><p>byte identical: true</p><div><figure><figcaption>b96dd73 source mesh</figcaption><img src='b96dd73/passage-2-3-0-lower-mouth.png'></figure><figure><figcaption>L130 reach units</figcaption><img src='L130/passage-2-3-0-lower-mouth.png'></figure></div></section>
<section><h2>passage-2-3-0-upper-mouth</h2><p>byte identical: true</p><div><figure><figcaption>b96dd73 source mesh</figcaption><img src='b96dd73/passage-2-3-0-upper-mouth.png'></figure><figure><figcaption>L130 reach units</figcaption><img src='L130/passage-2-3-0-upper-mouth.png'></figure></div></section>
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

@@ -0,0 +1,134 @@
[
{
"name": "passage-0-1-0-lower-mouth",
"camera_m": [
32.688347,
9.553723,
-735.48053
],
"yaw_deg": -132.845,
"pitch_deg": -6.052,
"height_center_m": -736.267,
"source_mesh": "Saved/Linux/l129/captures/mouth-closeups/before/passage-0-1-0-lower-mouth.vfmesh",
"candidate_mesh": "Saved/Linux/l130/captures/mouth-closeups/L130/passage-0-1-0-lower-mouth.vfmesh",
"baseline_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/b96dd73/passage-0-1-0-lower-mouth.png",
"candidate_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/L130/passage-0-1-0-lower-mouth.png",
"baseline_sha256": "aff7310b94e1b57e9cd1e42d3294e10964aed4a56e74bec8c1cdc0a5b2bffa98",
"candidate_sha256": "aff7310b94e1b57e9cd1e42d3294e10964aed4a56e74bec8c1cdc0a5b2bffa98",
"byte_identical": true,
"drawn_triangles": {
"b96dd73": 549170,
"L130": 549170
}
},
{
"name": "passage-0-1-0-upper-mouth",
"camera_m": [
8.324777,
20.450708,
-371.84259
],
"yaw_deg": -175.826,
"pitch_deg": -0.889,
"height_center_m": -372.122,
"source_mesh": "Saved/Linux/l129/captures/mouth-closeups/before/passage-0-1-0-upper-mouth.vfmesh",
"candidate_mesh": "Saved/Linux/l130/captures/mouth-closeups/L130/passage-0-1-0-upper-mouth.vfmesh",
"baseline_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/b96dd73/passage-0-1-0-upper-mouth.png",
"candidate_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/L130/passage-0-1-0-upper-mouth.png",
"baseline_sha256": "76dbb3263eb4f2018d13496f42b6eff7eb875e2029358776e22337aea8893a6d",
"candidate_sha256": "76dbb3263eb4f2018d13496f42b6eff7eb875e2029358776e22337aea8893a6d",
"byte_identical": true,
"drawn_triangles": {
"b96dd73": 243380,
"L130": 243380
}
},
{
"name": "passage-1-2-0-lower-mouth",
"camera_m": [
27.129938,
-19.07345,
-751.877197
],
"yaw_deg": -58.537,
"pitch_deg": -5.944,
"height_center_m": -751.734,
"source_mesh": "Saved/Linux/l129/captures/mouth-closeups/before/passage-1-2-0-lower-mouth.vfmesh",
"candidate_mesh": "Saved/Linux/l130/captures/mouth-closeups/L130/passage-1-2-0-lower-mouth.vfmesh",
"baseline_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/b96dd73/passage-1-2-0-lower-mouth.png",
"candidate_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/L130/passage-1-2-0-lower-mouth.png",
"baseline_sha256": "1c193b6fcb65b98b1c721704990392dc816486639e047b53bb72b4a17d89ae4c",
"candidate_sha256": "1c193b6fcb65b98b1c721704990392dc816486639e047b53bb72b4a17d89ae4c",
"byte_identical": true,
"drawn_triangles": {
"b96dd73": 544808,
"L130": 544808
}
},
{
"name": "passage-1-2-0-upper-mouth",
"camera_m": [
23.391808,
-15.509525,
-731.781067
],
"yaw_deg": -72.785,
"pitch_deg": -0.897,
"height_center_m": -730.561,
"source_mesh": "Saved/Linux/l129/captures/mouth-closeups/before/passage-1-2-0-upper-mouth.vfmesh",
"candidate_mesh": "Saved/Linux/l130/captures/mouth-closeups/L130/passage-1-2-0-upper-mouth.vfmesh",
"baseline_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/b96dd73/passage-1-2-0-upper-mouth.png",
"candidate_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/L130/passage-1-2-0-upper-mouth.png",
"baseline_sha256": "28576cefb1b59601d2e68981a7b77fc28a1d667c58ab27292deac0acd9deb338",
"candidate_sha256": "28576cefb1b59601d2e68981a7b77fc28a1d667c58ab27292deac0acd9deb338",
"byte_identical": true,
"drawn_triangles": {
"b96dd73": 593026,
"L130": 593026
}
},
{
"name": "passage-2-3-0-lower-mouth",
"camera_m": [
-28.589163,
-1.226313,
-898.811707
],
"yaw_deg": 114.256,
"pitch_deg": -6.159,
"height_center_m": -898.713,
"source_mesh": "Saved/Linux/l129/captures/mouth-closeups/before/passage-2-3-0-lower-mouth.vfmesh",
"candidate_mesh": "Saved/Linux/l130/captures/mouth-closeups/L130/passage-2-3-0-lower-mouth.vfmesh",
"baseline_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/b96dd73/passage-2-3-0-lower-mouth.png",
"candidate_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/L130/passage-2-3-0-lower-mouth.png",
"baseline_sha256": "58cd3ea88c754afab20d1cdcaf5f0edf676f0303d9f392d3632a6f2257205766",
"candidate_sha256": "58cd3ea88c754afab20d1cdcaf5f0edf676f0303d9f392d3632a6f2257205766",
"byte_identical": true,
"drawn_triangles": {
"b96dd73": 293832,
"L130": 293832
}
},
{
"name": "passage-2-3-0-upper-mouth",
"camera_m": [
-24.455027,
-5.631774,
-811.975403
],
"yaw_deg": 82.691,
"pitch_deg": -0.896,
"height_center_m": -810.755,
"source_mesh": "Saved/Linux/l129/captures/mouth-closeups/before/passage-2-3-0-upper-mouth.vfmesh",
"candidate_mesh": "Saved/Linux/l130/captures/mouth-closeups/L130/passage-2-3-0-upper-mouth.vfmesh",
"baseline_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/b96dd73/passage-2-3-0-upper-mouth.png",
"candidate_png": "Standalone/Research/WorldAssemblerM3/Linux130/captures/mouth-closeups/L130/passage-2-3-0-upper-mouth.png",
"baseline_sha256": "6b903c68fac1a20783185afbac8cdad717c6e9374e665e3618e118c5a6aadf51",
"candidate_sha256": "6b903c68fac1a20783185afbac8cdad717c6e9374e665e3618e118c5a6aadf51",
"byte_identical": true,
"drawn_triangles": {
"b96dd73": 59554,
"L130": 59554
}
}
]
@@ -0,0 +1,50 @@
# Linux131 — current state and dead experiment cleanup
Branch `wip/l131-current-state`, based on `cbbfd46`; Core evaluator remains v227. The root [`CURRENT-STATE.md`](../../../../CURRENT-STATE.md) consolidates the shipping behavior, current references, verification commands, open gaps, and closed proposals.
## Removed
- Removed `VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION`, `VOXELFORGE_L88_DISABLE_PASSAGES`, `VOXELFORGE_L88_DISABLE_BOUNDARY_BANDS`, and `VOXELFORGE_L88_ALLOW_PASSAGE_ABLATION_WALKABILITY_FAILURE`. These only supported archived passage/boundary ablations in `Linux86/RunL86Ceiling.py`, `Linux87/RunL87Timeline.py`, and `Linux88/RunL88CeilingSplit.py`; no current `verify.py` gate or live comparison uses them. Passage feature selection, passage composition, and boundary bands now run on the normal path, and passage-route validation cannot be waived by the old ablation flag.
- Removed the `VOXELFORGE_AUDIT_LEGACY_ROUTE_GRADE` block from `Standalone/Source/WorldAssemblyPrototype.h`. The source contained its only reference; no test, current tool, or report invoked it.
- Kept route-candidate grading and all user-visible behavior. This cleanup adds no evaluator version or baseline change.
## Switch and hook inventory
### Environment switches in compiled Core/Source
| Switch | Effect and consumer | Keep decision |
|---|---|---|
| `VOXELFORGE_L74_TIGHT_PROOF_CONTROL` | Proof negative control. `Linux74/FalsifierL74.py` and `Linux75/FalsifierL75.py`. | Keep for falsifier reproducibility; not a current fast-tier input. |
| `VOXELFORGE_L75_TN_BLOCK_REGRESSION` | Fixed TunnelNetwork regression path in Core and mesher. `RunTnBlockRegressionL75.py` and `FalsifierL75.py`. | Keep; the regression tools still consume it. |
| `VOXELFORGE_L98_PASSAGE_PREP_TRACE` | Emits preparation-stage timing/count data. `RunL98Hashes.py` and `RunL98ReuseCounts.py`. | Keep as a focused profiler for future preparation comparisons. |
| `VOXELFORGE_L98_PASSAGE_REUSE_PROBE` | Compares the prepared projection with direct scalar queries. `RunL98ReuseCounts.py`. | Keep for exactness probes. |
| `VOXELFORGE_L104_FAR_HIT_CACHE` | Selects indexed versus linear far-hit candidate lookup. `RunL104ReadinessPairs.py` and `RunL104ProbeStats.py`. | Keep for reproducible A/B comparisons; cache defaults on. |
| `VOXELFORGE_L104_FAR_HIT_STATS` | Emits far-hit candidate/query counters. `RunL104ProbeStats.py`. | Keep for the measurement tool. |
| `VOXELFORGE_L115_DENSITY_PROFILE` | Enables density and floor-query profiling. `RunL115DensityProbe.py`, `RunL119DensityProbe.py`, and `RunL125DensityProbe.py`. | Keep; these remain the focused floor-probe path. |
| `VOXELFORGE_L130_PASSAGE_REACH_PROOF_AUDIT` | Checks passage proof/feature influence bounds. L130's reach audit and report. | Keep for reach changes and audit reproduction. |
Archived runners also set `VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS`, `VOXELFORGE_L86_PASSAGE_RESULT_CACHE`, `VOXELFORGE_L90_PASSAGE_BRICK_METRES`, `VOXELFORGE_L90_PASSAGE_CANDIDATE_CENSUS`, `VOXELFORGE_L90_PASSAGE_REACH_METRES`, `VOXELFORGE_L91_PASSAGE_SOURCE_FLOOR_VOXELS`, `VOXELFORGE_L93_PASSAGE_CARVE_REACH_METRES`, `VOXELFORGE_L98_PASSAGE_REUSE_COUNTS`, `VOXELFORGE_L114_DENSITY_PROFILE`, and `VOXELFORGE_L118_TAG_TIMING`. None has a compiled-source reader at `cbbfd46`; they are inert historical runner strings.
### Other experiments, diagnostics, and legacy paths retained
- **World-changing tunnel ablations:** all 14 `voxel.TunnelAblate*` controls remain (`CaveWarp`, `DetailOps`, `RoomSDF`, `TunnelSDF`, `TunnelCore`, `PassageCarving`, `PassageStructuralPosts`, `NativeFloor`, `Disturbances`, `OriginSpine`, `BoundarySeal`, `LandingPosts`, `XYEdgeSeal`, `PitChimneySDF`). The editor commandlet writes their resolved mask into export and result manifests; the archived `PERF-SAMPLED.md` and `WORK-NEXT.md` record their measurements. No current runner invokes an ablation. Keep for manifest attribution and historical reproduction; removing it needs a metadata migration. Source: [`VoxelDensityAblation.cpp`](../../../../Source/VoxelForge/Private/VoxelDensityAblation.cpp), [`VoxelForgeExploreCommandlet.cpp`](../../../../Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp).
- **Research fields:** `voxel.ResearchRedefinedDensityField`, `voxel.ResearchRedefinedDensityDetailScale`, `voxel.ResearchParameterizedCave`, and `voxel.ResearchParameterizedCaveSet` are default-off and used by `Tools/VoxelForgeTest.ps1` steps 2d/2e/2f. The region-eval commandlet consumes `SetResearchBrickBinningEnabled`, `SetResearchBrickRoomReachScale`, and `SetResearchRegionCacheWindowEnabled`; keep these editor-only experiment paths. Source: [`VoxelForgeRegionEvalExperiment.cpp`](../../../../Source/VoxelForgeEditor/Private/VoxelForgeRegionEvalExperiment.cpp), [`VoxelForgeTest.ps1`](../../../../Tools/VoxelForgeTest.ps1).
- **Runtime profiling and trace:** `voxel.SampleStacks` still starts `FVoxelStackSampler` (the guarded `Tools/VoxelForgeTest.ps1 -SampleDensityStacks` path consumes it); `voxel.ProfileDensity`, `voxel.ProfileDensityFull`, `voxel.ProfileTileGeneration`, `voxel.ProfileCollisionCookStages`, and `voxel.PerfAttribution` remain opt-in. `voxel.StartupTraceFile`, `voxel.StartupTraceMaxTiles`, and `voxel.StartupTraceUntilEndPlay` feed the Unreal startup trace used by the arrival/movement runners. Keep. Build-time `VOXELFORGE_PROFILE_LEGACY_TERMS` (L76) and `VOXELFORGE_PROFILE_L77`/`--linux77-profile` (stack sampler and route census) remain in `Standalone/BuildLinux.sh` and Core.
- **Standalone profiles and timelines:** retained `--profile-column-overlap`, `--profile-lod-work`, `--profile-passage-composer`, `--instrument-tile-drain`, `--cold-worker-timeline`, `--linux77-profile`, `--tls-probe=N`, `--tls-probe-touch=...`, and `--diagnostic-slow-shape-compile-ms=N`. The viewer's `--profile-attribution`, `--profile-detail-band`, and `--profile-far-column-overlap` remain. The L77 profiler, tile drain instrumentation, timelines, and TLS probe are measurement hooks; archived reports and runners still provide reproducible consumers.
- **Standalone audits:** all source-advertised hooks remain: `--audit-ore-vein-yield`, `--audit-resolved-layout-boundaries`, `--audit-strate-transitions`, `--audit-resolved-layout-passages`, `--audit-heightfield-profile`, `--audit-motif-intervals`, `--audit-landmark-reachability`, `--audit-native-caves`, `--audit-native-lattice`, `--audit-surface-clearance`, `--audit-uniform-block-proofs`, `--audit-base-page-equivalence`, `--audit-route-parity`, `--audit-passage-tile-seams`, `--audit-passage-source-clamp`, `--audit-passage-carve-reach`, `--audit-tile-shape-readiness`, and the mesher's `--audit-raw-skirts`. `verify.py fast` consumes the current route, passage, block-proof, transition, native-field and negative-control subset; dedicated L37/L75/L125/L130 runners exercise the specialized audits. Keep the whole audit set.
- **Evaluator A/B controls:** `voxel.FastIsFinite`, `voxel.UseFusedEvaluator`, `voxel.WormBlockSkip`, `voxel.TileCacheWindow` / its two minimum-LOD controls, `voxel.SpatialIndex`, `voxel.ChunkRoutingCache`, `voxel.UseOperatorBlock`, `voxel.CoreColumnCacheDirectMapped`, `voxel.CoreMCGradientMemoization`, `voxel.FloorRound1PassageProjectionCache`, `voxel.PlayerFitMemo`, `voxel.UseBlockEarlyOut`, `voxel.OuterClassifierMode`, and `voxel.SealedSolidProof` remain. They select optimized or comparison paths; the density block equivalence test explicitly forces `UseFusedEvaluator=0`, while most others are production defaults or historical measurement controls. `voxel.WormsForceOff` is a world-changing development measurement override. Kept; no current L131 gate changes them.
- **Standalone A/B and geometry controls:** `--exact-midpoint-density-memo`, `--no-exact-midpoint-density-memo`, `--active-lane-batching`, `--proof-block-cells=N`, `--parallel-sign-edge-repair`, `--serial-sign-edge-repair`, and `--shape-helper-limit=8|12` remain. The L94 report documents serial edge repair; no current `verify.py` consumer was found for the other CLI A/B controls, so they remain pending a separately reviewed removal. `voxel.TilePostReachDebug`, `voxel.TilePostReachScale`, and `voxel.TileReachCostDiagnostics` remain for the tile-post-reach test; `voxel.TunnelMouthTrim` remains a development-only room-mouth geometry control. No L131 gate uses these.
- `FLegacyGeneratorFieldAdapter` remains the Core fallback for unsupported generator/recipe combinations, and Unreal's Core switch remains default-off. `voxel.StreamingLegacyPendingResultCap=1` remains the L120–L123 admission negative control; `voxel.StreamingCoarseFirst` remains opt-in pending a rendered owner-plan check. `bStandaloneResearchRoundTunnels` and `bStandaloneResearchEnhancedWalls` remain consumed by the standalone viewer/Core adapter.
## Checks
- Clang 19 and GCC standalone builds passed with the L131 source stamp `5119cee164406856cbca496d160c0e0a8124ae2529fa7c0ebd51634a7c65a6cb`. `Standalone/BuildLinux.sh` applied the required CPU and address-space limits.
- L80 timing: one warm-up per binary and five interleaved pairs in each of four strata × W8/W12 cells, comparing the pre-cleanup L130 binary with L131. Load was 0.81; CPU idle was 99.2%. Across 40 pairs per metric, mean relative change was −0.22% near-ready (95% CI −1.02% to +0.57%) and −0.29% whole-view (−1.04% to +0.46%); the intervals include zero. [Raw timing summary](../../../../Saved/Linux/l131/timing/summary.json).
- Exact hashes: **24/24** owner4 SW/FI/TN/VS × Clang/GCC × W1/W8/W12 tile and whole-view identities; **18/18** canonical cases across owner4 SurfaceWorld and owner2 SurfaceWorld/FloatingIslands. All match the pre-cleanup L130 binary and agree across compiler/worker combinations. For transparency, forcing the owner4 dataset through `--recipe=floating-islands` exited during setup with “could not find a finite SurfaceWorld strate”, before hashing; it is not a valid canonical case. [Tile matrix](../../../../Saved/Linux/l131/hash-matrix/summary.json), [canonical matrix](../../../../Saved/Linux/l131/canonical-matrix/summary.json).
- Walkability: **21,425/21,425** passage centerline samples open. Seam audit: **4 pairs / 4,900** shared samples, zero mismatches. [Audit log](../../../../Saved/Linux/l131/seam-audit.log).
- Fast tier: **PASS**, 259 gated checks plus 5 diagnostics, 80 runs, 371.0 s. The five diagnostic-only comparisons are historical baselines; all required checks pass. [Summary](../../../../Saved/Linux/l131/fast-summary.json).
- Linux `VFHostEditor`: **PASS**, 27/27 actions, 78.81 s with `-NoUBA -MaxParallelActions=4`, no `prlimit`. UBT's default log rotation first failed before compilation in the read-only user config directory; the successful rerun set `-Log=.../Saved/Linux/l131/ubt.log`. Three existing RealtimeMesh deprecation warnings remain. [Build log](../../../../Saved/Linux/l131/ue-build.log).
- Unreal `Source/` files changed: **none**. No Windows compile is requested by this diff; Windows determinism remains unverified.
- `git diff --check` passes. No commit, push, or stash was made.
Builds, per-run logs, timing records, hash records, and audit output are under `Saved/Linux/l131/`.
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Compare 18 canonical owner4/owner2 hashes with the pre-cleanup L130 binary."""
from __future__ import annotations
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l131/canonical-matrix"
DATASETS = {
"owner4": ROOT / "Standalone/data/owner_world.json",
"owner2": ROOT / "Standalone/data/regression/owner_world_2strate_l61.json",
}
RECIPES = {"owner4": ("surface-world",), "owner2": ("surface-world", "floating-islands")}
BINARIES = {
"base": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-{compiler}",
"l131": ROOT / "Saved/Linux/l131/bin/VoxelForgeCoreHeadless-l131-{compiler}",
}
HASH_RE = re.compile(r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
def run(dataset: str, recipe: str, compiler: str, workers: int, version: str) -> tuple[list[str], float]:
binary = Path(str(BINARIES[version]).format(compiler=compiler))
label = f"{dataset}-{recipe}-{compiler}-w{workers}-{version}"
log = OUT / f"{label}.log"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(binary), str(DATASETS[dataset]),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--force-generate", f"--workers={workers}", "--clip-radius=3",
"--max-clip-level=8", "--max-level=8", f"--recipe={recipe}", "--stream-hash"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
process = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
hashes = HASH_RE.findall(log.read_text(encoding="utf-8", errors="replace"))
if process.returncode or len(hashes) != 12:
raise RuntimeError(f"hash run failed: {log}; exit={process.returncode}; rows={len(hashes)}")
return hashes, time.monotonic() - started
def main() -> int:
OUT.mkdir(parents=True, exist_ok=True)
rows = []
groups = {}
for dataset, recipes in RECIPES.items():
for recipe in recipes:
signatures = []
for workers in (1, 8, 12):
for compiler in ("clang", "gcc"):
before, before_s = run(dataset, recipe, compiler, workers, "base")
after, after_s = run(dataset, recipe, compiler, workers, "l131")
row = {"dataset": dataset, "recipe": recipe, "compiler": compiler,
"workers": workers, "hash_mismatches": sum(a != b for a, b in zip(before, after)),
"rows": len(after), "base_s": round(before_s, 3), "l131_s": round(after_s, 3),
"base_log": f"Saved/Linux/l131/canonical-matrix/{dataset}-{recipe}-{compiler}-w{workers}-base.log",
"l131_log": f"Saved/Linux/l131/canonical-matrix/{dataset}-{recipe}-{compiler}-w{workers}-l131.log"}
rows.append(row)
signatures.append((compiler, workers, after))
print("[L131CanonicalHash] " + json.dumps(row, sort_keys=True), flush=True)
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
first = signatures[0][2]
stable = all(sig == first for _, _, sig in signatures)
groups[f"{dataset}/{recipe}"] = {"base_equals_l131": all(row["hash_mismatches"] == 0 for row in rows if row["dataset"] == dataset and row["recipe"] == recipe),
"all_clang_gcc_workers_agree": stable}
failures = [row for row in rows if row["hash_mismatches"]]
(OUT / "summary.json").write_text(json.dumps({"runs": rows, "groups": groups,
"passed": len(rows)-len(failures), "failed": len(failures)}, indent=2) + "\n")
return 1 if failures or not all(group["all_clang_gcc_workers_agree"] for group in groups.values()) else 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Compare 24 owner4 tile-hash cases with the pre-cleanup L130 binary."""
from __future__ import annotations
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l131/hash-matrix"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"base": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-{compiler}",
"l131": ROOT / "Saved/Linux/l131/bin/VoxelForgeCoreHeadless-l131-{compiler}",
}
CAMERAS = {
"sw": ((8.0, 16.0, -372.2626953125), 2850),
"fi": ((25.8369140625, 3.19921875, -737.0), 2249),
"tn": ((19.166015625, -14.2001953125, -753.3740234375), 2233),
"vs": ((-20.0625, -5.068359375, -900.353515625), 2329),
}
TILE_RE = re.compile(
r"^\[PipelineTileHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) "
r"density=(0x[0-9a-f]+) mesh=(0x[0-9a-f]+) payload=(0x[0-9a-f]+) "
r"topology=(0x[0-9a-f]+)$", re.M)
WHOLE_RE = re.compile(
r"^\[PipelineWholeViewHash\] tiles=(\d+) density=(0x[0-9a-f]+) "
r"mesh=(0x[0-9a-f]+) payload=(0x[0-9a-f]+) topology=(0x[0-9a-f]+)$", re.M)
def capture(camera_name: str, compiler: str, workers: int, version: str) -> tuple[dict, tuple[str, ...], float]:
camera, expected_tiles = CAMERAS[camera_name]
camera_arg = ",".join(f"{value:.12f}" for value in camera) + ",0,0"
binary = Path(str(BINARIES[version]).format(compiler=compiler))
log = OUT / f"owner4-{camera_name}-{compiler}-w{workers}-{version}.log"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(binary), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", f"--workers={workers}",
"--clip-radius=3", "--max-clip-level=8", "--max-level=8",
f"--camera={camera_arg}", "--stream-hash-tiles"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
elapsed = time.monotonic() - started
output = log.read_text(encoding="utf-8", errors="replace")
tiles = {(int(match.group(1)), int(match.group(2)), int(match.group(3)), int(match.group(4))):
match.group(5, 6, 7, 8) for match in TILE_RE.finditer(output)}
whole = WHOLE_RE.search(output)
if result.returncode or whole is None or len(tiles) != expected_tiles or len(tiles) != int(whole.group(1)):
raise RuntimeError(f"incomplete {version} {camera_name}/{compiler}/W{workers}: {log}")
return tiles, whole.groups(), elapsed
def main() -> int:
OUT.mkdir(parents=True, exist_ok=True)
rows = []
for camera_name in CAMERAS:
for compiler in ("clang", "gcc"):
for workers in (1, 8, 12):
old_tiles, old_whole, old_s = capture(camera_name, compiler, workers, "base")
new_tiles, new_whole, new_s = capture(camera_name, compiler, workers, "l131")
keys = set(old_tiles) | set(new_tiles)
tile_mismatches = sum(old_tiles.get(key) != new_tiles.get(key) for key in keys)
row = {"owner": "owner4", "camera": camera_name, "compiler": compiler,
"workers": workers, "tiles": len(new_tiles),
"tile_mismatches": tile_mismatches,
"whole_view_match": old_whole == new_whole,
"base_s": round(old_s, 3), "l131_s": round(new_s, 3),
"base_log": f"Saved/Linux/l131/hash-matrix/owner4-{camera_name}-{compiler}-w{workers}-base.log",
"l131_log": f"Saved/Linux/l131/hash-matrix/owner4-{camera_name}-{compiler}-w{workers}-l131.log"}
rows.append(row)
print("[L131HashIdentity] " + json.dumps(row, sort_keys=True), flush=True)
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
failures = [row for row in rows if row["tile_mismatches"] or not row["whole_view_match"]]
(OUT / "summary.json").write_text(json.dumps({"runs": rows, "passed": len(rows)-len(failures),
"failed": len(failures)}, indent=2) + "\n")
return 1 if failures else 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""Five interleaved L80 readiness measurements per stratum and worker count."""
from __future__ import annotations
import json
import os
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l131/timing"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"base": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang",
"l131": ROOT / "Saved/Linux/l131/bin/VoxelForgeCoreHeadless-l131-clang",
}
CAMERAS = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
PIPELINE_RE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
FIELDS_RE = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
NEAR_RE = re.compile(r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$", re.M)
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}-l131-v227"
log = OUT / f"{label}.log"
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[version]), str(DATA),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
f"--workers={workers}", "--clip-radius=3", "--max-clip-level=8",
"--max-level=8", f"--camera={camera}", "--profile-lod-work",
"--stream-scheduler-policy=3"]
started = time.monotonic()
with log.open("w", encoding="utf-8") as output:
result = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
content = log.read_text(encoding="utf-8", errors="replace")
pipeline, near = PIPELINE_RE.search(content), NEAR_RE.search(content)
if result.returncode or pipeline is None or near is None:
raise RuntimeError(f"timing run failed: {log}; exit={result.returncode}")
fields = dict(FIELDS_RE.findall(pipeline.group(1)))
row = {"strate": strate, "workers": workers, "version": version,
"phase": phase, "replicate": replicate,
"lod02_ready_s": float(near.group(3)),
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
"wall_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT))}
print("[L131TimingRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def quiet_machine() -> tuple[float, float]:
load_1m = os.getloadavg()[0]
def totals() -> tuple[int, int]:
fields = Path("/proc/stat").read_text().splitlines()[0].split()
values = [int(value) for value in fields[1:]]
return sum(values), values[3] + (values[4] if len(values) > 4 else 0)
before = totals()
time.sleep(2.0)
after = totals()
idle = 100.0 * (after[1] - before[1]) / max(1, after[0] - before[0])
return load_1m, idle
def main() -> int:
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
raise SystemExit("missing timing input")
OUT.mkdir(parents=True, exist_ok=True)
load_1m, idle = quiet_machine()
(OUT / "machine-load-before.txt").write_text(
f"1-minute load: {load_1m:.2f}\n2-second CPU idle: {idle:.1f}%\n")
print(f"[L131TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True)
if load_1m >= 0.9 or idle < 97.0:
raise SystemExit("timing refused: machine is above the L80 quiet-load gate")
rows = []
for strate in range(4):
for workers in (8, 12):
for version in BINARIES:
rows.append(run(strate, workers, version, "warmup", 0))
for replicate in range(1, 6):
order = tuple(BINARIES) if replicate % 2 else tuple(reversed(BINARIES))
for version in order:
rows.append(run(strate, workers, version, "measured", replicate))
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
measured = [row for row in rows if row["phase"] == "measured"]
summary = []
for strate in range(4):
for workers in (8, 12):
old = [row for row in measured if row["strate"] == strate and row["workers"] == workers and row["version"] == "base"]
new = [row for row in measured if row["strate"] == strate and row["workers"] == workers and row["version"] == "l131"]
item = {"strate": strate, "workers": workers, "replicates_per_version": 5}
for key in ("lod02_ready_s", "whole_view_ready_s"):
old_med, new_med = statistics.median(row[key] for row in old), statistics.median(row[key] for row in new)
item[f"base_{key}_median"] = old_med
item[f"l131_{key}_median"] = new_med
item[f"delta_pct_{key}"] = round(100.0 * (new_med / old_med - 1.0), 2)
item[f"base_{key}_values"] = [row[key] for row in old]
item[f"l131_{key}_values"] = [row[key] for row in new]
summary.append(item)
(OUT / "summary.json").write_text(json.dumps({"runs": rows, "summary": summary}, indent=2) + "\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Create the isolated L132 owner snapshot with a native plan for existing FI strate 1."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
SOURCE = ROOT / "Standalone/data/owner_world.json"
MANIFEST = ROOT / "Saved/Linux/l132/fixtures/owner-native-fi.world-assembly.json"
LAYOUT = ROOT / "Saved/Linux/l132/fixtures/owner-native-fi.resolved-layout.json"
OUTPUT = ROOT / "Saved/Linux/l132/fixtures/owner_world_native_fi.json"
def main() -> int:
owner = json.loads(SOURCE.read_text(encoding="utf-8"))
manifest_text = MANIFEST.read_text(encoding="utf-8").strip()
layout_text = LAYOUT.read_text(encoding="utf-8").strip()
manifest = json.loads(manifest_text)
layout = json.loads(layout_text)
islands = manifest.get("floating_islands", [])
native = [row for row in islands if row.get("strate_index") == 1]
resolved = [row for row in layout.get("strates", [])
if row.get("strate_index") == 1]
if len(native) != 1 or len(resolved) != 1:
raise SystemExit("native FI snapshot must contain exactly one strate-1 island plan/layout")
if (resolved[0].get("generator_kind") != "FloatingIslands"
or resolved[0].get("recipe_id") != "FloatingIslands.ClusterField"
or resolved[0].get("recipe_version") != 8):
raise SystemExit("resolved strate 1 is not FloatingIslands.ClusterField v8")
owner["world_assembly"] = manifest
owner["core_world_assembly_json"] = manifest_text
owner["resolved_layout_json"] = layout_text
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
OUTPUT.write_text(json.dumps(owner, separators=(",", ":")) + "\n", encoding="utf-8")
print(f"[L132NativeFISnapshot] path={OUTPUT.relative_to(ROOT)} "
f"islands={len(native[0].get('manifest', {}).get('plan', {}).get('islands', []))} "
"strate=1 recipe=FloatingIslands.ClusterField/v8 status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,41 @@
# L132 — owner density dispatch
## Result
With Core enabled in the owner headless pipeline, the four strates bind to four paths. Unreal’s `bUseVoxelForgeCore` remains opt-in and defaults off. The fast-tier diagnostic reports the Core bindings at the four L80 owner poses and attributes hashed tiles by tile-center height within the resolved stratum bounds.
| Strate | Owner recipe | Bound path | Tile centers / share |
|---|---|---|---:|
| SW (0) | `SurfaceWorld.Heightfield2D` v2 | `SurfaceWorld-native` | 2,640 / 100% |
| FI (1) | `FloatingIslands.ClusterField` v0 | `compiled-analytic/FloatingIslands` | 2,128 / 100% |
| TN (2) | `legacy.TunnelNetwork` v0 | `compiled-analytic/TunnelNetwork` | 2,208 / 100% |
| VS (3) | `legacy.VerticalShafts` v0 | `compiled-analytic/VerticalShafts` | 1,392 / 100% |
SW has a matching strate/bounds `SurfaceWorld` plan, and its `Heightfield2D` evaluator is valid. FI has a legacy v0 layout record but no matching `FloatingIslandPlan`, so `FloatingIslandFieldV1` cannot bind. The Core owner manifest has zero island plans. TN and VS have legacy recipe IDs, not `Core.Caves.PrimitiveField` v1 or `Core.Lattice.PrimitiveField` v1. Their resolved layouts therefore lack the matching graph recipe and native-field capability. All three fall through to `FLegacyGeneratorFieldAdapter`.
The dispatch check is in the fast tier and can also be requested from the headless binary with `--density-dispatch-report`. It prints the per-strate bound adapter/path and hashed tile-center path shares. Its tile counts aggregate the four standard owner poses; tiles whose centers fall outside an interior interval are excluded, and a tile spanning a boundary is assigned by its center. Per-view logs are `Saved/Linux/l132/owner-density-dispatch-s*-clang.log`; the printed shares are in `Saved/Linux/l132/logs/verify-fast.log` and `Saved/Linux/l132/fast-summary.json`.
Across the 8,368 classified tile centers, path shares are 31.5% SurfaceWorld-native, 25.4% compiled-analytic/FloatingIslands, 26.4% compiled-analytic/TunnelNetwork, and 16.6% compiled-analytic/VerticalShafts.
## Native FI decision
The native evaluator is present. `BuildFloatingIslandStratumPlan` can convert the owner’s legacy island parameters to a v8 plan, but the owner’s imported manifest does not contain that plan. The existing automatic-plan branch is guarded by `bImportedOverride` inside the generated-assembly branch, so it does not fill this owner’s missing imported plan.
I built a candidate v8 plan for the existing owner FI strate without changing the default owner data. Its raw island-route audit passed: 29 route points, 78,336 capsule samples, zero blocked samples, zero unsupported probes, and zero grade failures. The composed owner audit did not pass:
- Existing compiled-analytic owner: all 3 passage interfaces pass; shell floor violations 0; ripple checks 3/3.
- Native FI candidate: only 2/3 interfaces pass; the added FI route is not walkable (1/2 strate routes walkable); shell floor violations 13; ripple checks 2/3.
- Passage tile seams passed for both bindings: 4 pairs, 4,900 shared samples, zero mismatches.
Native FI is a quality regression on the owner’s current passages, despite its standalone route plan passing. I did not switch the owner. The conditional captures, W8/W12 timing matrix, and Clang/GCC W1/W8/W12 matrix were not run because the candidate failed the composed walk/shell gate. No Core version or output baseline changed; the default owner output remains on its existing bindings. No Linux UE host build was run because there was no qualifying native change or timing matrix.
## TN/VS graph translation size
The active TN `GenerationParams` block has 47 fields; VS `VerticalShaftParams` has 11. Each also has 15 passage settings. Both have `CoreGeometry.GeneratorRecipe=0` and empty `Rooms`, `Tunnels`, and `Stamps` arrays. Native cave/lattice authoring requires at least one explicit room and one edge, with geometry, links, and route data. There is no adapter translating those legacy generator blocks into graph records. This is a substantial procedural migration, not a small recipe mapping, so I did not build it this round.
## Verification
- Fast tier: PASS, 304.0 s, 84 runs and 273 checks; includes four-pose dispatch output.
- Passage tile seams: compiled-analytic and native FI candidate both PASS at the FI owner pose.
- Owner walk/shell comparison: default PASS; native candidate FAIL as detailed above.
- Build stamp: `766b94ca7638475975b0345e5907eebb96158360c384f5d916c78eccbb1aa815`.
@@ -0,0 +1,13 @@
# Linux134 — Tunnel Network cave planner
Tunnel Network dispatches `Caves-native` (`Core.Caves.PrimitiveField` v1). The seed-0 plan is strict-valid: 30 compound rooms, 72 primitives, 31 edges, 3 side branches, and one larger destination chamber. The arrival-to-departure route is 387.067 m long, has a 59.021 m floor delta, and reaches 8.670° maximum grade. The planner maps 21 of the 47 legacy generation fields; the other 26 do not map to resolved room or edge record fields.
The second design fixed the incoming passage at its source. Shared passage composition now applies passages when the lower stratum has a native cave field; the unchanged walkability audit passes 21,425/21,425 samples.
The third design aligned the arrival-edge floor to the passage mouth, added a 0.75 m backbone floor-support budget to the resolved edge records, and raised route anchors to follow the planned edge floor. The route audit now reports 0 unsupported samples and 0 grade failures. It still fails clearance: 196/50,967 capsule samples are solid. The first is `(-14.548,-104.858,-768.115) m`, density `-0.042135`, at the route-edge-6/7 junction. Its closest planned floor is route-edge-6 at approximately `z=-768.104 m`; the probe is about 1.1 cm below that floor. The player center interpolated along the next route segment is only about 0.79 m above this higher neighboring floor, while the capsule samples extend 0.84 m below center. The native field is returning the intended solid floor backing, which intrudes into the capsule near the overlapping descending edges. The remaining issue is clearance planning across neighboring edge floors, not missing floor support, a room floor cut, or the inter-strate passage.
I stopped at this failed route gate, as requested. Shell, floor, ripple, and blocked-route negative controls pass; the route's blocked control also detects the blockage. The rest of the bar was not run: seams, fast tier, Clang/GCC W1/W8/W12 determinism and baselines, captures, L80 timing, and the post-timing UE host build. Clang headless build passed; source stamp `9845de5d3d614db620bf99aa3bd7b4fd66a440d76676e38ccad3d198b851e476`.
Logs: `Saved/Linux/l134/build-clang-support-fix.log` and `Saved/Linux/l134/walk-gate-support-fix.log`. Resolved layout: `layout-seed0-final-gate.json`.
Legacy field mapping: `RoomSpacing`, `RoomDensity`, `MinRoomRadius`, `MaxRoomRadius`, `RoomHeightRatio`, `RoomFloorCutMin`, `RoomFloorCutMax`, `RoomShapeVariety`, `OriginRoomRadius`, `OriginRoomMaxConnections`, `TunnelMinRadius`, `TunnelMaxRadius`, `TunnelDensity`, `MaxTunnelLength`, `TunnelWarpStrength`, `TunnelHorizontalBias`, `bTunnelsFlowTowardOrigin`, `CaveWarpStrength`, `SurfaceRoughness`, `RoughnessFrequency`, and `SDFBlendRadius`. Unmapped: `BaseDensity`, `BoundarySealThickness`, `CaveWarpFrequency`, `DomainWarpFrequency`, `DomainWarpStrength`, `FloorBias`, `FloorReliefFrequency`, `FloorReliefStrength`, `RoomMouthRiseBlendVoxels`, `RoomMouthRiseStrength`, `TunnelEndpointZOffset`, `TunnelFloorGentleSlopeThreshold`, `TunnelFloorLedgeCountPreference`, `TunnelFloorMaxLedgeHeight`, `TunnelFloorMaxLedges`, `TunnelFloorTerraceStepHeight`, `VerticalScale`, `WaterLevelRelative`, `WormFrequency`, `WormHorizontalBias`, `WormNetworkRange`, `WormStrength`, `WormThreshold`, `bTunnelFloorEnabled`, `bTunnelFloorTerracingEnabled`, and `RoughnessNoiseType`.
@@ -0,0 +1 @@
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
@@ -0,0 +1 @@
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
@@ -0,0 +1 @@
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
@@ -0,0 +1,35 @@
# VoxelForge — research R46 (read-only): generator capability matrix
You are a researcher. **Read only: do not edit, create or delete any file, and do not build, run or test anything.**
Read the code, reports and design docs. Be thorough: verify each claim by hand in the code, and mark each one
[CODE] file:line, [MEASURED] report, or [INFERENCE]. Branch `assembler/world` (`cbbfd46`).
## Why
The owner approved the next focus (from `Standalone/Research/WorldAssemblerM3/Astra4/REVIEW.md` #3): turn generator
breadth into playable variety. First we need an honest picture of what each strate/generator family can do today.
## Task
Find every generator family / strate type: surface world, floating islands, caves/cavern, tunnel network, vertical
shafts, dreamcore/liminal, and any others in the code (Core native, legacy fallback, op stack, composer). For each,
fill one row:
- **Core path:** native Core evaluator, legacy fallback, or not in Core at all? Where is it dispatched?
- **Entrances/exits:** arrival and departure mouths, and the primordial law (arrival → departure walkable).
- **Interior connectivity:** walkable, and audited by what?
- **Edits:** does digging (diff layer) work on it in the Core path?
- **Materials/content:** material IDs, decorations, foliage, lights, anything placeable.
- **Far terrain:** does it have far LOD/shells?
- **Performance:** latest measured near-ready, with the report.
- **Identity:** what makes it look and feel distinct today; is it recognisable without a label?
- **Owner world:** used in `Standalone/data/owner_world.json`? Which strate letter (SW/FI/TN/VS)?
- **Gaps:** what's missing for it to be a "place" with its own identity and a destination.
Then:
1. Which three families are closest to being distinct places (the owner's example: a grounded cave, an island
environment, one liminal identity)? What is the smallest work to get each there?
2. What does the composer (`VoxelSeasonManifest.cpp`, `COMPOSER-NOTES.md`) already do for tagged content, and what's
the smallest next step?
3. Risks: places where the Core path silently falls back, or where a family exists in docs but not in code.
Write plain Markdown: the matrix plus ≤ ~800 words of findings.
@@ -0,0 +1,41 @@
# VoxelForge R46 — generator capability matrix
Read-only review of `assembler/world` at `cbbfd46`; no files changed, and no builds, runs, or tests were performed.
## Shared dispatch and contracts
[CODE] The eight `ECaveGeneratorType` values are TunnelNetwork, FlatPlain, CrystalChamber, Maze, SurfaceWorld, VerticalShafts, FloatingIslands, and Underwater. `NativeCaves`, `NativeLattice`, and `ProfiledHeightfield` are optional Core recipes layered onto those types, not additional strate types. [VoxelStrateTypes.h:175](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:175>) [VoxelStrateTypes.h:203](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:203>)
[CODE] Core binds native SurfaceWorld and FloatingIslands evaluators when matching plans exist; it binds native cave/lattice graph fields when their capabilities are present. Otherwise it attaches a legacy generator adapter. The density dispatcher exposes paths such as `SurfaceWorld-native`, `SurfaceWorld-legacy`, `Caves-native`, and `compiled-analytic/<kind>`. [ShapeWorldInternal.h:600](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:600>) [ShapeWorldInternal.h:640](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:640>) [ShapeWorldInternal.h:649](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:649>) [ShapeWorldInternal.h:3101](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:3101>)
[CODE] The adapter plans inter-strate passages and resolves the layout before Core evaluates it. The composer’s primordial law checks a player-fit, walk-only path between the center-column interior endpoints; it is an arrival-to-departure test, not a requirement that every disconnected room or shaft join the largest component. [VoxelForgeCoreAdapter.cpp:1545](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp:1545>) [VoxelSeasonManifest.cpp:2194](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelSeasonManifest.cpp:2194>) [VoxelStrateMeasure.h:315](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateMeasure.h:315>)
[CODE] Core edit transactions become density brushes applied after field composition. The same stage covers native and fallback density paths. [VoxelForgeCore.cpp:3307](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:3307>) [ShapeWorldInternal.h:3036](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:3036>) [ShapeWorldInternal.h:5201](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:5201>)
[CODE] Any strate can author decorations, landmarks, ambient actors, and creatures. Placement supports actors or instanced meshes; Core material samples default to host ID 1 and can carry two resource IDs. These are shared content hooks, not a distinct palette per generator family. [VoxelStrateDefinition.h:352](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateDefinition.h:352>) [VoxelStrateTypes.h:2204](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:2204>) [VoxelForgeCore.h:1273](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h:1273>)
**Performance key:** L125 is the latest absolute owner four-strata LOD0–2 timing table I found; L130’s paired comparison found no measurable change. L125 labels rows S0–S3, so mapping them to SW/FI/TN/VS in the explicit L109 order is an **[INFERENCE]**. Values below are W8/W12 medians in seconds; they measure the owner layout, not isolated native evaluator costs. [L125:24](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md:24>) [L125:28](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md:28>) [L125:35](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md:35>) [L130:9](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md:9>) [L109:21](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux109/REPORT.md:21>)
## Matrix
| Family / owner | Core path | Entrances, connectivity, and primordial law | Edits, materials, and content | Far terrain and performance | Identity and gaps |
|---|---|---|---|---|---|
| **SurfaceWorld — SW** | [CODE] Native `Heightfield2D` when a matching surface plan binds; legacy SurfaceWorld fallback otherwise. [ShapeWorldInternal.h:600](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:600>) [ShapeWorldInternal.h:6685](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:6685>) | [CODE] Owner export enables an open surface entry; SW also supplies the 0–1 departure mouth. [MEASURED] The shared route audit passed 21,425/21,425 centerline samples. [owner_world.json:1](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/data/owner_world.json:1>) [L125:39](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md:39>) | [CODE] Digging works. Surface supports biome terrain/material signals, water, and the shared placement hooks; no family-specific content pack is proven in the owner export. [VoxelStrateTypes.h:1609](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:1609>) | [CODE] Common mesh LOD and far shells; native surface plans also get far ground/cap refinement. [MEASURED] **SW:** 0.780/0.807 near-ready. [VoxelForgeCore.cpp:2996](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:2996>) [VoxelForgeCore.cpp:3073](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:3073>) | [INFERENCE] Outdoor hills, mountains, rivers, and beaches have a legible silhouette. It still needs distinctive foliage/materials, sound, and a destination to read as a complete place. |
| **FloatingIslands — FI** | [CODE] Native island field when a matching island plan binds; otherwise falls through to the legacy analytic path. [ShapeWorldInternal.h:620](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:620>) [ShapeWorldInternal.h:6692](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:6692>) | [CODE] Owner route enters from SW and departs toward TN; native island route audits check capsule clearance and support. [MEASURED] The owner passage checks passed. [MEASURED] L67 reported the owner FI path as `compiled-analytic/FloatingIslands`, not native; the reason was unresolved in R36. [L67:5](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux67/REPORT.md:5>) [Research36:11](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Research36/REPORT.md:11>) | [CODE] Digging works; foliage, landmarks, ambient actors, and lights can use shared placement systems. [INFERENCE] No island-specific owner palette/content is established by the export. | [CODE] Common far mesh and shell paths. [MEASURED] **FI:** 0.981/0.962 near-ready in the owner layout. | [INFERENCE] Floating land silhouettes can be immediately recognizable. Confirm native dispatch, then add an island ecology, a landmark, and a clear route/destination. |
| **TunnelNetwork — TN** | [CODE] The owner’s regular path is Core’s legacy/compiled-analytic fallback. `NativeCaves` is optional for TN/Underwater and evaluates authored room/tunnel graphs. [VoxelForgeCoreAdapter.cpp:145](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp:145>) [VoxelGenerator.cpp:2645](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:2645>) | [CODE] Owner arrival from FI and departure toward VS. [MEASURED] Inter-strate walkability passes; that does not prove every interior room is connected. Native cave fixture flood-fill connected its chosen arrival/departure pair. [L54:21](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux54/REPORT.md:21>) | [CODE] Digging works. Generic cave decorations/landmarks/lighting are available; there is no family-specific Core material palette. | [CODE] Common far LOD/shells. [MEASURED] **TN:** 1.025/0.965 near-ready in the owner layout. Optional native-cave fixture: 0.177 s near ring and 0.706 s cold whole view, a different workload. [L54:37](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux54/REPORT.md:37>) | [INFERENCE] Rooms and tunnels read as caves, but the owner route lacks a content-defined destination. Small cave graph and a landmark/content pack would make it a place; translating the owner’s legacy parameters to native cave records remains separate work. |
| **FlatPlain** | [CODE] Legacy fallback/op stack only; it uses the slab builder. [VoxelGenerator.cpp:2600](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:2600>) [VoxelDensityOpStack.cpp:9373](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelDensityOpStack.cpp:9373>) | [CODE] No intrinsic mouths; shared passage planner supplies endpoints. [INFERENCE] Its open floor/ceiling band may connect the selected endpoints, but no family-specific arrival/departure audit was found. | [CODE] Digging works; shared decorations/landmarks can populate the chamber. | [CODE] Common far LOD/shells. [MEASURED] No separate near-ready timing found. | [INFERENCE] Open underground plain with columns. Needs a distinctive floor/ceiling profile, content, and destination; it has no native Core slab recipe. |
| **CrystalChamber** | [CODE] Same slab op-stack builder as FlatPlain; distinctness comes from parameter values, especially ceiling roughness. [VoxelGenerator.cpp:2600](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:2600>) | [CODE] Same shared-mouth arrangement as FlatPlain; no family-specific walk audit found. | [CODE] Digging works. Crystal decoration is authorable through shared placement, not guaranteed by the geometry type. [VoxelStrateTypes.h:169](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:169>) | [CODE] Common far LOD/shells. [MEASURED] No separate near-ready timing found. | [INFERENCE] Hanging formations could distinguish it, but parameters alone do not supply crystal materials, lighting, or a destination. |
| **Maze** | [CODE] Legacy maze op stack; optional `NativeLattice` graph recipe, which is shared with VerticalShafts rather than a dedicated native maze generator. [VoxelForgeCoreAdapter.cpp:159](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp:159>) [VoxelGenerator.cpp:2592](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:2592>) | [CODE] Mouths are shared-planner endpoints; native lattice fixtures test capsule reachability between chosen endpoints. [MEASURED] L60 native lattice fixture: 0.561 s near ring, 1.701 s whole view—not the owner near-ready workload. [L60:19](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux60/REPORT.md:19>) [L60:36](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux60/REPORT.md:36>) | [CODE] Digging works; common content hooks apply. | [CODE] Common far LOD/shells; L60 streamed the lattice fixture through LOD0–8. [L60:15](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux60/REPORT.md:15>) | [INFERENCE] Tight branching corridors are a recognizable grammar, but the native fixture’s helix/maze graph needs a content destination and stronger per-seed identity. |
| **VerticalShafts — VS** | [CODE] Legacy shaft op stack; optional `NativeLattice` is a generic authored graph, not a shaft-specific Core evaluator. [VoxelGenerator.cpp:2622](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:2622>) | [CODE] Owner arrival comes from TN; the four-strata export has no lower fifth-stratum passage. [MEASURED] The composer found a VerticalShafts arrival/departure violation; L58’s lattice route passed but was a helix. [COMPOSER-NOTES.md:1437](</home/jahni/VoxelM/Plugins/VoxelForge/COMPOSER-NOTES.md:1437>) [Linux58:11](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux58/REPORT.md:11>) | [CODE] Digging works; generic props and lights can be placed. | [CODE] Common far LOD/shells. [MEASURED] **VS:** 0.879/0.838 near-ready in owner layout. | [INFERENCE] Vertical silhouettes could distinguish it, but the native graph example is not an open shaft. Needs a shaft-specific core, safe landings, and a proven route. |
| **Underwater** | [CODE] TunnelNetwork density plus water behavior; optional NativeCaves recipe is accepted. No separate native water generator recipe. [VoxelGenerator.cpp:2638](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelGenerator.cpp:2638>) [Unreal1:131](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Unreal1/REPORT.md:131>) | [CODE] Shared passage mouths; composer’s law is walk-only, so it does not certify swimming or water traversal. [INFERENCE] A flooded route needs dry walkable sections or a separate traversal rule. | [CODE] Digging works; water and shared content are available, but the native assembly recipe does not itself provide an authored water feature. | [CODE] Common far terrain; far water presentation is not established here. [MEASURED] No separate near-ready timing found. | [INFERENCE] “Flooded cave” is a plausible identity, but water, lighting, air pockets, and destination spaces need authored treatment. |
| **Dreamcore / liminal** | [CODE] No `Dreamcore` generator type. Existing Core stamps/motif fields are overlays; motif override currently requires a resolved SurfaceWorld strate. [VoxelStrateTypes.h:175](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:175>) [WorldAssemblyPrototype.h:568](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/WorldAssemblyPrototype.h:568>) | [CODE] Inherits mouths and connectivity from its base family; motif fixtures preserve route keepouts. [MEASURED] L60 motif overlay: 0.493 s near ring, 1.326 s whole view. [Linux56:21](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux56/REPORT.md:21>) [L60:35](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux60/REPORT.md:35>) | [CODE] Digging applies to the composed field; current motif kinds include carve arch/ellipsoid and fill pillar/dais/monolith/platform. [VoxelStrateTypes.h:222](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateTypes.h:222>) | [CODE] Base field uses common far LOD/shells. Motifs have LOD coverage, but no liminal-specific far treatment. | [INFERENCE] Motifs alone do not read as dreamcore. It needs a repeated spatial grammar, coherent palette/light/sound, and an explorable destination. Not in owner world. |
Owner mapping: [MEASURED] the owner export references four fixed strata and opens the surface; the named four-family owner route is SW/FI/TN/VS. [CODE] The L125–L130 timings are for that owner layout. [owner_world.json:1](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/data/owner_world.json:1>) [L109:21](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux109/REPORT.md:21>) [L120:23](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux120/REPORT.md:23>)
## Findings
1. **Closest to distinct places: TN caves, FI, and liminal overlays.** [INFERENCE] TN already has cave geometry and route proof: add one authored destination chamber plus a small landmark/material/light kit. FI already has a distinctive suspended-land silhouette and route support: first confirm its native dispatch, then add foliage, a landmark, and route cues. Liminal is the smallest overlay slice: reuse the flat/profiled base and existing monolith/platform stamps, then define one repeatable anomaly pattern and a matching content kit. These need authored destinations and themes more than new density families.
2. **Composer status and smallest next step.** [CODE] `VoxelSeasonManifest.cpp` rolls candidates from a corpus, measures them, rejects vacuous/fragmented/primordial-law failures, and selects for grounded/outlier mix plus archetype/recipe variety. Promoted records carry an archetype tag and corpus provenance is tagged `project`, `default`, or `promoted`. [VoxelSeasonManifest.cpp:2523](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelSeasonManifest.cpp:2523>) [COMPOSER-NOTES.md:431](</home/jahni/VoxelM/Plugins/VoxelForge/COMPOSER-NOTES.md:431>) [COMPOSER-NOTES.md:1466](</home/jahni/VoxelM/Plugins/VoxelForge/COMPOSER-NOTES.md:1466>) Strate and biome gameplay tags are stored, but theme draws for materials, creatures, decorations, and audio are still open. [VoxelStrateDefinition.h:389](</home/jahni/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelStrateDefinition.h:389>) [COMPOSER-NOTES.md:1484](</home/jahni/VoxelM/Plugins/VoxelForge/COMPOSER-NOTES.md:1484>) [INFERENCE] Smallest step: tag one real content class with multiple theme tags and let one seed-selected family draw a coherent, reviewable mini-pack.
3. **Risks.** [CODE] Missing native plans/capabilities reach the legacy binding; path diagnostics are needed to distinguish them. [ShapeWorldInternal.h:649](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:649>) [MEASURED] The latest explicit owner dispatch note I found says FI/TN/VS used compiled analytic fields; L130 reports timing but not dispatch, so that path should be rechecked before calling those owner generators native. [Linux67:5](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux67/REPORT.md:5>) [Linux130:9](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md:9>) [INFERENCE] Dreamcore exists as a design goal, not a generator; NativeLattice proves routes but not a shaft identity; FlatPlain/CrystalChamber remain legacy slab fields; and water presentation remains outside the native Core recipe.
+102 -11
View File
@@ -70,8 +70,11 @@ namespace
bool HasRenderDistanceOverride=false; bool HasRenderDistanceOverride=false;
int RenderDistanceOverride=0; int RenderDistanceOverride=0;
bool FloatingIslandsRecipe=false; bool FloatingIslandsRecipe=false;
bool NativeOwnerFloatingIslandsRecipe=false;
bool DensityDispatchReport=false;
bool CavesRecipe=false; bool CavesRecipe=false;
bool LatticeRecipe=false; bool LatticeRecipe=false;
bool LegacyTunnelNetwork=false;
int LatticeStrateIndex=-1; int LatticeStrateIndex=-1;
bool AuditHeightfieldProfile=false; bool AuditHeightfieldProfile=false;
bool LandmarkChamberDemo=false; bool LandmarkChamberDemo=false;
@@ -1432,6 +1435,9 @@ namespace
" --core-snapshot=FILE (benchmark the exact resolved Core snapshot from Unreal)\n" " --core-snapshot=FILE (benchmark the exact resolved Core snapshot from Unreal)\n"
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n" " --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
" --seed=N --recipe=surface-world|floating-islands|caves|lattice\n" " --seed=N --recipe=surface-world|floating-islands|caves|lattice\n"
" --legacy-tunnel-network (keep TN on its compiled-analytic comparison path)\n"
" --native-owner-fi (build the existing owner FI plan; leave other strates intact)\n"
" --density-dispatch-report (print bound path per strate and tile hashes)\n"
" --lattice-strate=N (opt one Maze or VerticalShafts strate into Core.Lattice)\n" " --lattice-strate=N (opt one Maze or VerticalShafts strate into Core.Lattice)\n"
" --landmark-chamber-demo (opt-in SW cavern room and swept tunnel)\n" " --landmark-chamber-demo (opt-in SW cavern room and swept tunnel)\n"
" --ore-veins-demo (opt-in material-only SW capsule veins)\n" " --ore-veins-demo (opt-in material-only SW capsule veins)\n"
@@ -1490,6 +1496,10 @@ namespace
else if(arg=="--resolved-layout-only")options.ResolvedLayoutOnly=true; else if(arg=="--resolved-layout-only")options.ResolvedLayoutOnly=true;
else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true; else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true;
else if(arg=="--audit-resolved-layout-passages")options.AuditResolvedLayoutPassages=true; else if(arg=="--audit-resolved-layout-passages")options.AuditResolvedLayoutPassages=true;
else if(arg=="--native-owner-fi")options.NativeOwnerFloatingIslandsRecipe=true;
else if(arg=="--density-dispatch-report")
{options.DensityDispatchReport=true;options.StreamHash=true;options.StreamHashTiles=true;}
else if(arg=="--legacy-tunnel-network")options.LegacyTunnelNetwork=true;
else if(arg=="--audit-landmark-reachability")options.AuditLandmarkReachability=true; else if(arg=="--audit-landmark-reachability")options.AuditLandmarkReachability=true;
else if(arg=="--audit-native-caves")options.AuditNativeCaves=true; else if(arg=="--audit-native-caves")options.AuditNativeCaves=true;
else if(arg=="--audit-native-lattice")options.AuditNativeLattice=true; else if(arg=="--audit-native-lattice")options.AuditNativeLattice=true;
@@ -1730,7 +1740,8 @@ int main(int argc,char** argv)
options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel, options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel,
options.MaxLevel,options.StreamHash?"on":"off", options.MaxLevel,options.StreamHash?"on":"off",
options.StreamHashAllowUniformSkips?"on":"off",options.ProfileColumnOverlap?"on":"off", options.StreamHashAllowUniformSkips?"on":"off",options.ProfileColumnOverlap?"on":"off",
options.ForceGenerate?"on":"off",options.CavesRecipe?"caves" options.ForceGenerate?"on":"off",options.LegacyTunnelNetwork?"legacy-tunnel-network"
:options.CavesRecipe?"caves"
:options.LatticeRecipe?"lattice" :options.LatticeRecipe?"lattice"
:options.FloatingIslandsRecipe?"floating-islands":"surface-world", :options.FloatingIslandsRecipe?"floating-islands":"surface-world",
options.LatticeStrateIndex, options.LatticeStrateIndex,
@@ -1819,7 +1830,9 @@ int main(int argc,char** argv)
else if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype( else if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype(
*ownerWorld,ownerJsonRoot,error,{},&worldAssembly)) *ownerWorld,ownerJsonRoot,error,{},&worldAssembly))
{std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;}
if(!useExportedCorePlan&&options.FloatingIslandsRecipe if(((!useExportedCorePlan&&options.FloatingIslandsRecipe)
||(options.NativeOwnerFloatingIslandsRecipe
&&worldAssembly.floatingIslands.empty()))
&&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly( &&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
*ownerWorld,ownerJsonRoot,worldAssembly,error)) *ownerWorld,ownerJsonRoot,worldAssembly,error))
{std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;}
@@ -1849,18 +1862,27 @@ int main(int argc,char** argv)
} }
VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout; VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout;
{ {
const std::string variant=options.LatticeRecipe const std::string variant=options.LegacyTunnelNetwork
?"legacy-tunnel-network"
:options.LatticeRecipe
?"lattice/"+std::to_string(options.LatticeStrateIndex) ?"lattice/"+std::to_string(options.LatticeStrateIndex)
:options.CavesRecipe?"caves" :options.CavesRecipe?"caves"
:options.FloatingIslandsRecipe?"floating-islands":"surface-world"; :(options.FloatingIslandsRecipe||options.NativeOwnerFloatingIslandsRecipe)
?"floating-islands":"surface-world";
const bool layoutResolved=useExportedCorePlan const bool layoutResolved=useExportedCorePlan
&&!options.NativeOwnerFloatingIslandsRecipe
?StandaloneWorldAssemblyPrototype::ImportExportedResolvedLayout( ?StandaloneWorldAssemblyPrototype::ImportExportedResolvedLayout(
ownerJsonRoot,resolvedLayout,error) ownerJsonRoot,resolvedLayout,error)
:StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot( :StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot(
*ownerWorld,worldAssembly,variant,resolvedLayout,error); *ownerWorld,worldAssembly,variant,resolvedLayout,error);
if(!layoutResolved) if(!layoutResolved)
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;}
if(useExportedCorePlan if(useExportedCorePlan&&!options.NativeOwnerFloatingIslandsRecipe
&&!options.LegacyTunnelNetwork&&!options.CavesRecipe
&&!StandaloneWorldAssemblyPrototype::PlanOwnerTunnelNetworkCaves(
*ownerWorld,resolvedLayout,error))
{std::fprintf(stderr,"Imported owner Tunnel Network cave planning failed: %s\n",error.c_str());return 2;}
if(useExportedCorePlan&&!options.NativeOwnerFloatingIslandsRecipe
&&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner( &&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner(
*ownerWorld,worldAssembly,resolvedLayout,error)) *ownerWorld,worldAssembly,resolvedLayout,error))
{std::fprintf(stderr,"Unreal-resolved Core plan/owner slot validation failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"Unreal-resolved Core plan/owner slot validation failed: %s\n",error.c_str());return 2;}
@@ -1942,8 +1964,11 @@ int main(int argc,char** argv)
{ {
std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms; std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms;
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges; std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges;
if(!MakeNativeCavesSpine(resolvedLayout,rooms,edges,error) if(!MakeNativeCavesSpine(resolvedLayout,rooms,edges,error))
||!VoxelForgeCore::ResolveResolvedCavernGraphV1( {std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;}
rooms.insert(rooms.end(),resolvedLayout.cavernRooms.begin(),resolvedLayout.cavernRooms.end());
edges.insert(edges.end(),resolvedLayout.cavernEdges.begin(),resolvedLayout.cavernEdges.end());
if(!VoxelForgeCore::ResolveResolvedCavernGraphV1(
resolvedLayout,seed,rooms,edges,error)) resolvedLayout,seed,rooms,edges,error))
{std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;}
} }
@@ -1954,8 +1979,11 @@ int main(int argc,char** argv)
std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms; std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms;
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges; std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges;
if(!MakeNativeLatticeSpine(resolvedLayout,seed,options.LatticeStrateIndex, if(!MakeNativeLatticeSpine(resolvedLayout,seed,options.LatticeStrateIndex,
options.LatticeOrphanControl,rooms,edges,error) options.LatticeOrphanControl,rooms,edges,error))
||!VoxelForgeCore::ResolveResolvedCavernGraphV1( {std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;}
rooms.insert(rooms.end(),resolvedLayout.cavernRooms.begin(),resolvedLayout.cavernRooms.end());
edges.insert(edges.end(),resolvedLayout.cavernEdges.begin(),resolvedLayout.cavernEdges.end());
if(!VoxelForgeCore::ResolveResolvedCavernGraphV1(
resolvedLayout,seed,rooms,edges,error)) resolvedLayout,seed,rooms,edges,error))
{std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;} {std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;}
} }
@@ -2076,12 +2104,16 @@ int main(int argc,char** argv)
const bool hasNativeLatticeRecipe=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(), const bool hasNativeLatticeRecipe=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
[](const auto& stratum){return VoxelForgeCore::IsNativeLatticeFieldStratum(stratum) [](const auto& stratum){return VoxelForgeCore::IsNativeLatticeFieldStratum(stratum)
&&stratum.passageCapabilities.hasNativeLatticeField;}); &&stratum.passageCapabilities.hasNativeLatticeField;});
const auto actualRecipe=hasNativeLatticeRecipe?std::string_view("lattice") const auto actualRecipe=requestedRecipe=="lattice"&&hasNativeLatticeRecipe
:hasNativeCavesRecipe?std::string_view("caves") ?std::string_view("lattice")
:requestedRecipe=="caves"&&hasNativeCavesRecipe
?std::string_view("caves")
:requestedRecipe=="surface-world"&&hasSurfaceWorldPlan :requestedRecipe=="surface-world"&&hasSurfaceWorldPlan
?std::string_view("surface-world") ?std::string_view("surface-world")
:requestedRecipe=="floating-islands"&&hasFloatingIslands :requestedRecipe=="floating-islands"&&hasFloatingIslands
?std::string_view("floating-islands") ?std::string_view("floating-islands")
:hasNativeLatticeRecipe?std::string_view("lattice")
:hasNativeCavesRecipe?std::string_view("caves")
:hasFloatingIslands?std::string_view("floating-islands") :hasFloatingIslands?std::string_view("floating-islands")
:hasSurfaceWorldPlan?std::string_view("surface-world") :hasSurfaceWorldPlan?std::string_view("surface-world")
:std::string_view("missing"); :std::string_view("missing");
@@ -2113,6 +2145,65 @@ int main(int argc,char** argv)
controls.controlsRejectedAsExpected,controls.controlsRun,controls.passed?"PASS":"FAIL"); controls.controlsRejectedAsExpected,controls.controlsRun,controls.passed?"PASS":"FAIL");
if(!controls.passed) if(!controls.passed)
{for(const auto& failure:controls.failures)std::printf("[ResolvedLayoutValidatorControlFailure] %s %s\n",failure.code.c_str(),failure.detail.c_str());return 2;} {for(const auto& failure:controls.failures)std::printf("[ResolvedLayoutValidatorControlFailure] %s %s\n",failure.code.c_str(),failure.detail.c_str());return 2;}
if(options.DensityDispatchReport)
{
const auto& slots=ownerWorld->Manager->GetLayout();
for(const auto& stratum:resolvedLayout.strates)
{
const auto slot=std::find_if(slots.begin(),slots.end(),[&](const auto& candidate)
{return candidate.StrateIndex==stratum.strateIndex;});
bool native=false;
std::string path,adapter;
if(slot!=slots.end()&&stratum.generatorKind=="SurfaceWorld")
{
const auto plan=std::find_if(worldAssembly.plan.strates.begin(),
worldAssembly.plan.strates.end(),[&](const auto& candidate)
{return candidate.strateIndex==stratum.strateIndex
&&candidate.bottomChunkZ==slot->BottomChunkZ
&&candidate.topChunkZ==slot->TopChunkZ;});
if(plan!=worldAssembly.plan.strates.end())
{
const VoxelForgeCore::Heightfield2D field(worldAssembly.recipe,*plan);
native=field.isValid();
}
if(native){path="SurfaceWorld-native";adapter="FSurfaceWorldFieldAdapter";}
}
else if(slot!=slots.end()&&stratum.generatorKind=="FloatingIslands")
{
const auto plan=std::find_if(worldAssembly.floatingIslands.begin(),
worldAssembly.floatingIslands.end(),[&](const auto& candidate)
{return candidate.strateIndex==stratum.strateIndex
&&candidate.bottomChunkZ==slot->BottomChunkZ
&&candidate.topChunkZ==slot->TopChunkZ;});
if(plan!=worldAssembly.floatingIslands.end())
{
const VoxelForgeCore::FloatingIslandFieldV1 field(
plan->manifest.recipe,plan->manifest.plan);
native=field.isValid();
}
if(native){path="FloatingIslands-native";adapter="FFloatingIslandFieldAdapter";}
}
else if(VoxelForgeCore::HasNativeGraphField(stratum)
&&(VoxelForgeCore::IsNativeCaveFieldStratum(stratum)
?stratum.passageCapabilities.hasNativeCaveField
:stratum.passageCapabilities.hasNativeLatticeField))
{
native=true;
path=VoxelForgeCore::IsNativeLatticeFieldStratum(stratum)
?"Lattice-native":"Caves-native";
adapter="FCavesFieldAdapter";
}
if(!native)
{
path=stratum.generatorKind=="SurfaceWorld"
?"SurfaceWorld-legacy":"compiled-analytic/"+stratum.generatorKind;
adapter="FLegacyGeneratorFieldAdapter";
}
std::printf("[DensityDispatch] strate=%d generator=%s recipe=%s version=%u bound_path=%s adapter=%s\n",
stratum.strateIndex,stratum.generatorKind.c_str(),stratum.recipeId.c_str(),
stratum.recipeVersion,path.c_str(),adapter.c_str());
}
}
if(!options.ResolvedLayoutOutputPath.empty()) if(!options.ResolvedLayoutOutputPath.empty())
std::printf("[ResolvedLayoutSnapshotOutput] path=%s\n",options.ResolvedLayoutOutputPath.c_str()); std::printf("[ResolvedLayoutSnapshotOutput] path=%s\n",options.ResolvedLayoutOutputPath.c_str());
if(auto* root=std::get_if<VFJson::Value::Object>(&ownerJsonRoot.Data)) if(auto* root=std::get_if<VFJson::Value::Object>(&ownerJsonRoot.Data))
+266 -33
View File
@@ -1595,6 +1595,205 @@ namespace StandaloneStreamBenchmark
return passed; return passed;
} }
inline bool AuditNativeTunnelNetworkField(const VoxelForgeCore::World& shape,
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
std::uint32_t voxelMicrometres)
{
using namespace VoxelForgeCore;
constexpr double Pi=3.14159265358979323846;
const double vm=std::max<std::uint32_t>(1,voxelMicrometres)/1.0e6;
const auto density=[&](double x,double y,double z)
{return static_cast<double>(shape.densityAt({x/vm,y/vm,z/vm}))*vm;};
const auto cave=std::find_if(layout.strates.begin(),layout.strates.end(),
[](const auto& s){return s.generatorKind=="TunnelNetwork"
&&IsNativeCaveFieldStratum(s)&&s.passageCapabilities.hasNativeCaveField;});
if(cave==layout.strates.end()||layout.cavernGraphVocabularyVersion!=1)
{std::printf("[TunnelNetworkCavernAudit] recipe=%s graph=%u status=FAIL\n",
cave==layout.strates.end()?"missing":"present",
layout.cavernGraphVocabularyVersion);return false;}
const ResolvedLayoutPassageV1* arrival=nullptr;
const ResolvedLayoutPassageV1* departure=nullptr;
for(const auto& passage:layout.passages)
{
if(passage.lowerStrateIndex==cave->strateIndex
&&passage.upperStrateIndex<cave->strateIndex)arrival=&passage;
if(passage.upperStrateIndex==cave->strateIndex
&&passage.lowerStrateIndex>cave->strateIndex)departure=&passage;
}
std::vector<const ResolvedCavernRoomV1*> rooms;
std::vector<const ResolvedCavernEdgeV1*> routeEdges;
for(const auto& room:layout.cavernRooms)
if(room.strateIndex==cave->strateIndex)rooms.push_back(&room);
for(const auto& edge:layout.cavernEdges)
if(edge.strateIndex==cave->strateIndex
&&edge.stableId.find("/route-edge-")!=std::string::npos)
routeEdges.push_back(&edge);
const auto routeIndex=[](const ResolvedCavernEdgeV1* edge)
{
const auto marker=edge->stableId.rfind("/route-edge-");
return marker==std::string::npos?std::uint64_t{0}
:static_cast<std::uint64_t>(std::stoull(edge->stableId.substr(marker+12)));
};
std::sort(routeEdges.begin(),routeEdges.end(),[&](const auto* a,const auto* b)
{return routeIndex(a)<routeIndex(b);});
std::unordered_map<std::string,std::size_t> compoundCounts;
std::unordered_map<std::string,double> compoundRadii;
double largestRoomRadius=0.0,largestOtherRoomRadius=0.0;
std::string largestRoomGroup;
for(const auto* room:rooms)
{
const auto marker=room->stableId.rfind("/primitive-");
const std::string group=marker==std::string::npos
?room->stableId:room->stableId.substr(0,marker);
++compoundCounts[group];
const double radius=std::max(room->halfExtentsMicrometres[0],
room->halfExtentsMicrometres[1])/1.0e6;
compoundRadii[group]=std::max(compoundRadii[group],radius);
if(radius>largestRoomRadius)
{largestRoomRadius=radius;largestRoomGroup=group;}
}
for(const auto& [group,radius]:compoundRadii)
if(group!=largestRoomGroup)
largestOtherRoomRadius=std::max(largestOtherRoomRadius,radius);
const bool compoundRooms=!compoundCounts.empty()
&&std::all_of(compoundCounts.begin(),compoundCounts.end(),
[](const auto& row){return row.second>=2&&row.second<=4;});
const bool destination=largestOtherRoomRadius>0.0
&&largestRoomRadius>largestOtherRoomRadius*1.10;
std::vector<const ResolvedLayoutAnchorV2*> routeAnchors;
for(const auto& anchor:cave->anchors)
if(anchor.kind=="route_point"&&anchor.values.size()>=3)
routeAnchors.push_back(&anchor);
double maximumGrade=0.0;
bool gradeOk=routeAnchors.size()>=2;
for(std::size_t i=1;i<routeAnchors.size();++i)
{
const auto& a=routeAnchors[i-1]->values;
const auto& b=routeAnchors[i]->values;
const double run=std::hypot(static_cast<double>(b[0]-a[0]),
static_cast<double>(b[1]-a[1]));
const double rise=std::abs(static_cast<double>(b[2]-a[2]));
if(run<=0.0){gradeOk=false;continue;}
const double grade=std::atan2(rise,run)*180.0/Pi;
maximumGrade=std::max(maximumGrade,grade);
gradeOk=gradeOk&&grade<=15.0+1.0e-6;
}
bool mouthBinding=arrival&&departure&&routeAnchors.size()>=2;
if(mouthBinding)
{
const auto qOffset=static_cast<std::int64_t>(std::llround(
1000000.0*WorldAssemblySubvoxelsPerVoxel/layout.coreVoxelSizeMicrometres));
const auto& start=routeAnchors.front()->values;
const auto& end=routeAnchors.back()->values;
mouthBinding=start[0]==arrival->lowerMouthSubvoxels[0]
&&start[1]==arrival->lowerMouthSubvoxels[1]
&&start[2]==arrival->lowerMouthSubvoxels[2]+qOffset
&&end[0]==departure->upperMouthSubvoxels[0]
&&end[1]==departure->upperMouthSubvoxels[1]
&&end[2]==departure->upperMouthSubvoxels[2]+qOffset;
}
std::uint64_t airSamples=0,floorSamples=0,shellSamples=0;
bool centerAir=true,floorSolid=true,shellSolid=true;
for(const auto* edge:routeEdges)
{
const auto& p=edge->controlPointsMicrometres;
const double x=(p[0][0]+3.0*p[1][0]+3.0*p[2][0]+p[3][0])/8.0e6;
const double y=(p[0][1]+3.0*p[1][1]+3.0*p[2][1]+p[3][1])/8.0e6;
const double floor=(p[0][2]+3.0*p[1][2]+3.0*p[2][2]+p[3][2])/8.0e6;
const double bore=edge->boreRadiusMicrometres/1.0e6;
if(density(x,y,floor+1.0)<=0.0)centerAir=false;
++airSamples;
if(density(x,y,floor-0.10)>=0.0)floorSolid=false;
++floorSamples;
const double dx=(p[3][0]-p[0][0])/1.0e6;
const double dy=(p[3][1]-p[0][1])/1.0e6;
const double length=std::hypot(dx,dy);
if(length<=1.0e-6){shellSolid=false;continue;}
const double nx=-dy/length,ny=dx/length;
if(density(x+nx*(bore+0.90),y+ny*(bore+0.90),floor+2.0)>=0.0
||density(x-nx*(bore+0.90),y-ny*(bore+0.90),floor+2.0)>=0.0)
shellSolid=false;
shellSamples+=2;
}
const ResolvedCavernEdgeV1* rippleEdge=nullptr;
double longest=0.0;
for(const auto* edge:routeEdges)
{
const auto& p=edge->controlPointsMicrometres;
const double length=std::hypot(static_cast<double>(p[3][0]-p[0][0]),
static_cast<double>(p[3][1]-p[0][1]));
if(length>longest){longest=length;rippleEdge=edge;}
}
std::vector<double> rippleRadii;
if(rippleEdge)
{
const auto& p=rippleEdge->controlPointsMicrometres;
const double radius=rippleEdge->boreRadiusMicrometres/1.0e6;
constexpr std::array<double,7> stations{{0.18,0.29,0.40,0.51,0.62,0.73,0.84}};
for(double t:stations)
{
const auto bezier=[&](double u,std::size_t axis)
{
const double one=1.0-u;
return (one*one*one*p[0][axis]+3.0*one*one*u*p[1][axis]
+3.0*one*u*u*p[2][axis]+u*u*u*p[3][axis])/1.0e6;
};
const double x=bezier(t,0),y=bezier(t,1),floor=bezier(t,2);
const double tx=bezier(std::min(1.0,t+0.01),0)
-bezier(std::max(0.0,t-0.01),0);
const double ty=bezier(std::min(1.0,t+0.01),1)
-bezier(std::max(0.0,t-0.01),1);
const double tangent=std::hypot(tx,ty);
if(tangent<=1.0e-6)continue;
for(const double side:{-1.0,1.0})
{
double prior=0.20;
double priorDensity=density(x,y,floor+2.0);
bool found=false;
for(double radial=0.25;radial<=radius+1.8;radial+=0.05)
{
const double sampleX=x-side*ty/tangent*radial;
const double sampleY=y+side*tx/tangent*radial;
const double value=density(sampleX,sampleY,floor+2.0);
if(priorDensity>=0.0&&value<0.0)
{
const double fraction=priorDensity/(priorDensity-value);
rippleRadii.push_back(prior+(radial-prior)*fraction);
found=true;break;
}
prior=radial;priorDensity=value;
}
if(!found)continue;
}
}
}
double ripplePeakToPeak=0.0;
if(!rippleRadii.empty())
{
const auto limits=std::minmax_element(rippleRadii.begin(),rippleRadii.end());
ripplePeakToPeak=*limits.second-*limits.first;
}
const bool ripple=rippleRadii.size()>=8&&ripplePeakToPeak>=0.02
&&ripplePeakToPeak<=1.25;
std::printf("[TunnelNetworkCavernAudit] strate=%d recipe=%s rooms=%zu compound_groups=%zu destination_radius_m=%.3f route_edges=%zu route_points=%zu mouth_binding=%s max_grade_deg=%.3f grade_ok=%s center_air=%llu/%llu floor_solid=%llu/%llu shell_solid=%llu/%llu ripple_samples=%zu ripple_peak_to_peak_m=%.4f status=%s\n",
cave->strateIndex,cave->recipeId.c_str(),rooms.size(),compoundCounts.size(),
largestRoomRadius,routeEdges.size(),routeAnchors.size(),mouthBinding?"PASS":"FAIL",
maximumGrade,gradeOk?"PASS":"FAIL",
static_cast<unsigned long long>(airSamples),static_cast<unsigned long long>(airSamples),
static_cast<unsigned long long>(floorSamples),static_cast<unsigned long long>(floorSamples),
static_cast<unsigned long long>(shellSamples),static_cast<unsigned long long>(shellSamples),
rippleRadii.size(),ripplePeakToPeak,
arrival&&departure&&routeEdges.size()>=2&&!rooms.empty()&&compoundRooms
&&destination&&mouthBinding&&gradeOk&&centerAir&&floorSolid&&shellSolid
&&ripple?"PASS":"FAIL");
return arrival&&departure&&routeEdges.size()>=2&&!rooms.empty()&&compoundRooms
&&destination&&mouthBinding&&gradeOk&&centerAir&&floorSolid&&shellSolid&&ripple;
}
inline bool AuditNativeCavesField(const VoxelForgeCore::World& shape, inline bool AuditNativeCavesField(const VoxelForgeCore::World& shape,
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::uint32_t voxelMicrometres) const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::uint32_t voxelMicrometres)
{ {
@@ -1605,9 +1804,13 @@ namespace StandaloneStreamBenchmark
const auto density=[&](double x,double y,double z) const auto density=[&](double x,double y,double z)
{return static_cast<double>(shape.densityAt({x/vm,y/vm,z/vm}))*vm;}; {return static_cast<double>(shape.densityAt({x/vm,y/vm,z/vm}))*vm;};
const auto cave=std::find_if(layout.strates.begin(),layout.strates.end(), const auto cave=std::find_if(layout.strates.begin(),layout.strates.end(),
[](const auto& s){return s.generatorKind=="Caves" [](const auto& s){return IsNativeCaveFieldStratum(s)
&&s.recipeId=="Core.Caves.PrimitiveField"&&s.recipeVersion==1
&&s.passageCapabilities.hasNativeCaveField;}); &&s.passageCapabilities.hasNativeCaveField;});
if(cave!=layout.strates.end()&&cave->generatorKind=="TunnelNetwork")
return AuditNativeTunnelNetworkField(shape,layout,voxelMicrometres);
if(cave==layout.strates.end()||cave->generatorKind!="Caves")
{std::printf("[CavesAudit] recipe=missing rooms=%zu edges=%zu status=FAIL\n",
layout.cavernRooms.size(),layout.cavernEdges.size());return false;}
if(cave==layout.strates.end()||layout.cavernRooms.size()!=1||layout.cavernEdges.size()!=2) if(cave==layout.strates.end()||layout.cavernRooms.size()!=1||layout.cavernEdges.size()!=2)
{std::printf("[CavesAudit] recipe=%s rooms=%zu edges=%zu status=FAIL\n", {std::printf("[CavesAudit] recipe=%s rooms=%zu edges=%zu status=FAIL\n",
cave==layout.strates.end()?"missing":"present",layout.cavernRooms.size(),layout.cavernEdges.size());return false;} cave==layout.strates.end()?"missing":"present",layout.cavernRooms.size(),layout.cavernEdges.size());return false;}
@@ -2566,15 +2769,9 @@ namespace StandaloneStreamBenchmark
ShapeRequest.detail=DetailVariant;ShapeRequest.includeFarWorld=bIncludeFarWorld&&!bDeferFarWorld; ShapeRequest.detail=DetailVariant;ShapeRequest.includeFarWorld=bIncludeFarWorld&&!bDeferFarWorld;
ShapeRequest.roundTunnels=bRoundTunnels; ShapeRequest.roundTunnels=bRoundTunnels;
ShapeRequest.enhancedTunnelWalls=bEnhancedTunnelWalls; ShapeRequest.enhancedTunnelWalls=bEnhancedTunnelWalls;
const char* L86DisablePassages=std::getenv( ShapeRequest.validatePassageRoutes=!bCaptureSkipWalkability;
"VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION");
const bool bL86DisablePassages=L86DisablePassages
&&L86DisablePassages[0]=='1'&&L86DisablePassages[1]=='\0';
ShapeRequest.validatePassageRoutes=!bCaptureSkipWalkability&&!bL86DisablePassages;
if(bCaptureSkipWalkability) if(bCaptureSkipWalkability)
std::printf("[CaptureWalkabilityGate] validation=deferred geometry_input=unchanged mode=mesh-export\n"); std::printf("[CaptureWalkabilityGate] validation=deferred geometry_input=unchanged mode=mesh-export\n");
if(bL86DisablePassages)
std::printf("[L86PassageCeiling] validation=skipped geometry=passage_composition_disabled diagnostic_only=true\n");
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel, FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap, bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap,
FarHorizonLOD8InteriorStepVoxels,bUseSharedColumnCache, FarHorizonLOD8InteriorStepVoxels,bUseSharedColumnCache,
@@ -4802,6 +4999,7 @@ namespace StandaloneStreamBenchmark
} }
} }
std::uint64_t routePoints=0,routePasses=0,routeWalkPasses=0,routeBindings=0,routeBindingFailures=0; std::uint64_t routePoints=0,routePasses=0,routeWalkPasses=0,routeBindings=0,routeBindingFailures=0;
std::uint64_t tunnelNetworkRoutes=0,tunnelNetworkBlockedControls=0;
for(const auto& stratum:ResolvedLayout->strates) for(const auto& stratum:ResolvedLayout->strates)
{ {
std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> anchors; std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> anchors;
@@ -4865,36 +5063,56 @@ namespace StandaloneStreamBenchmark
supportStepMetres,supportMaximumMetres,minimumSupportProbes, supportStepMetres,supportMaximumMetres,minimumSupportProbes,
supportProbeRadiusMetres); supportProbeRadiusMetres);
bool binding=true; bool binding=true;
const auto equalPosition=[](const auto& a,const auto& b)
{return a[0]==b[0]&&a[1]==b[1]&&a[2]==b[2];};
const auto boundMouthMatches=[&](const auto& authored,const auto& mouth)
{
if(stratum.generatorKind!="TunnelNetwork")
return equalPosition(authored,mouth);
auto expected=mouth;
expected[2]+=static_cast<std::int64_t>(std::llround(
1000000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel
/World.CoreVoxelSizeMicrons));
return equalPosition(authored,expected);
};
for(const auto& passage:ResolvedLayout->passages) for(const auto& passage:ResolvedLayout->passages)
{ {
const auto equalPosition=[](const auto& a,const auto& b)
{return a[0]==b[0]&&a[1]==b[1]&&a[2]==b[2];};
if(passage.upperStrateIndex==stratum.strateIndex) if(passage.upperStrateIndex==stratum.strateIndex)
{ {
const auto rootedMouth=std::find_if(stratum.anchors.begin(), if(stratum.generatorKind=="TunnelNetwork")
stratum.anchors.end(),[](const auto& anchor) binding&=boundMouthMatches(anchors.back()->values,
{return anchor.kind=="passage_anchor"&&anchor.values.size()>=10;}); passage.upperMouthSubvoxels);
const std::array<std::int64_t,3> departure= else
rootedMouth!=stratum.anchors.end() {
?std::array<std::int64_t,3>{{rootedMouth->values[7], const auto rootedMouth=std::find_if(stratum.anchors.begin(),
rootedMouth->values[8],rootedMouth->values[9]}} stratum.anchors.end(),[](const auto& anchor)
:std::array<std::int64_t,3>{{anchors.back()->values[0], {return anchor.kind=="passage_anchor"&&anchor.values.size()>=10;});
anchors.back()->values[1],anchors.back()->values[2]}}; const std::array<std::int64_t,3> departure=
binding&=equalPosition(passage.upperMouthSubvoxels, rootedMouth!=stratum.anchors.end()
departure); ?std::array<std::int64_t,3>{{rootedMouth->values[7],
rootedMouth->values[8],rootedMouth->values[9]}}
:std::array<std::int64_t,3>{{anchors.back()->values[0],
anchors.back()->values[1],anchors.back()->values[2]}};
binding&=equalPosition(passage.upperMouthSubvoxels,departure);
}
} }
if(passage.lowerStrateIndex==stratum.strateIndex) if(passage.lowerStrateIndex==stratum.strateIndex)
{ {
const auto passageArrival=std::find_if(stratum.anchors.begin(), if(stratum.generatorKind=="TunnelNetwork")
stratum.anchors.end(),[](const auto& anchor) binding&=boundMouthMatches(anchors.front()->values,
{return anchor.kind=="passage_arrival"&&anchor.values.size()>=3;}); passage.lowerMouthSubvoxels);
const std::array<std::int64_t,3> arrival=passageArrival!=stratum.anchors.end() else
?std::array<std::int64_t,3>{{passageArrival->values[0], {
passageArrival->values[1],passageArrival->values[2]}} const auto passageArrival=std::find_if(stratum.anchors.begin(),
:std::array<std::int64_t,3>{{anchors.front()->values[0], stratum.anchors.end(),[](const auto& anchor)
anchors.front()->values[1],anchors.front()->values[2]}}; {return anchor.kind=="passage_arrival"&&anchor.values.size()>=3;});
binding&=equalPosition(passage.lowerMouthSubvoxels, const std::array<std::int64_t,3> arrival=passageArrival!=stratum.anchors.end()
arrival); ?std::array<std::int64_t,3>{{passageArrival->values[0],
passageArrival->values[1],passageArrival->values[2]}}
:std::array<std::int64_t,3>{{anchors.front()->values[0],
anchors.front()->values[1],anchors.front()->values[2]}};
binding&=equalPosition(passage.lowerMouthSubvoxels,arrival);
}
} }
} }
++routePasses;routeWalkPasses+=routeAudit.passed; ++routePasses;routeWalkPasses+=routeAudit.passed;
@@ -4924,6 +5142,18 @@ namespace StandaloneStreamBenchmark
stratum.strateIndex,routeAudit.firstUnsupported[i][0], stratum.strateIndex,routeAudit.firstUnsupported[i][0],
routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2], routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2],
routeAudit.firstUnsupported[i][3]); routeAudit.firstUnsupported[i][3]);
if(stratum.generatorKind=="TunnelNetwork")
{
++tunnelNetworkRoutes;
const FWalkAudit control=auditPlayerPath(blockedWorld,route,
supportStartMetres,supportStepMetres,supportMaximumMetres,
minimumSupportProbes,supportProbeRadiusMetres);
const bool detected=control.blocked>0&&!control.passed;
tunnelNetworkBlockedControls+=detected;
std::printf("[TunnelNetworkBlockedRouteControl] strate=%d arrival_to_departure=true blocked_capsule_samples=%llu result=%s expected=FAIL status=%s\n",
stratum.strateIndex,static_cast<unsigned long long>(control.blocked),
control.passed?"PASS":"FAIL",detected?"PASS":"FAIL");
}
} }
for(std::size_t passageIndex=0; for(std::size_t passageIndex=0;
passageIndex<ResolvedLayout->passages.size();++passageIndex) passageIndex<ResolvedLayout->passages.size();++passageIndex)
@@ -4990,13 +5220,16 @@ namespace StandaloneStreamBenchmark
&&shellAuditPassed &&shellAuditPassed
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings &&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
&&routeBindingFailures==0 &&routeBindingFailures==0
&&tunnelNetworkRoutes==tunnelNetworkBlockedControls
&&blockedCompiled&&blockedControls==passageCount; &&blockedCompiled&&blockedControls==passageCount;
std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu natural=%llu/%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu blocked_controls=%llu/%llu status=%s\n", std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu natural=%llu/%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu tunnel_network_block_controls=%llu/%llu blocked_controls=%llu/%llu status=%s\n",
static_cast<unsigned long long>(passageCount),static_cast<unsigned long long>(passagePasses), static_cast<unsigned long long>(passageCount),static_cast<unsigned long long>(passagePasses),
static_cast<unsigned long long>(naturalPasses),static_cast<unsigned long long>(passageCount), static_cast<unsigned long long>(naturalPasses),static_cast<unsigned long long>(passageCount),
static_cast<unsigned long long>(routePasses),static_cast<unsigned long long>(routeWalkPasses), static_cast<unsigned long long>(routePasses),static_cast<unsigned long long>(routeWalkPasses),
static_cast<unsigned long long>(routePoints), static_cast<unsigned long long>(routePoints),
static_cast<unsigned long long>(routeBindings),static_cast<unsigned long long>(routeBindingFailures), static_cast<unsigned long long>(routeBindings),static_cast<unsigned long long>(routeBindingFailures),
static_cast<unsigned long long>(tunnelNetworkBlockedControls),
static_cast<unsigned long long>(tunnelNetworkRoutes),
static_cast<unsigned long long>(blockedControls),static_cast<unsigned long long>(passageCount), static_cast<unsigned long long>(blockedControls),static_cast<unsigned long long>(passageCount),
auditPassed?"PASS":"FAIL"); auditPassed?"PASS":"FAIL");
if(!auditPassed) if(!auditPassed)
+191 -76
View File
@@ -153,80 +153,6 @@ namespace StandaloneWorldAssemblyPrototype
candidateCenterSpacing=32; candidateCenterSpacing=32;
} }
const double coreVoxelMetres=static_cast<double>(coreVoxelSizeMicrons)/1000000.0; const double coreVoxelMetres=static_cast<double>(coreVoxelSizeMicrons)/1000000.0;
if(std::getenv("VOXELFORGE_AUDIT_LEGACY_ROUTE_GRADE"))
{
constexpr std::int32_t legacyCenterSpacing=64;
constexpr std::int32_t legacyWaypointSpacing=8;
std::array<GradeSegmentAudit,32> legacySegments{};
std::uint64_t legacyGradeFailCandidates=0;
double legacyMinimumGround=std::numeric_limits<double>::infinity();
double legacyMaximumGround=-std::numeric_limits<double>::infinity();
for(std::int32_t centerY=-256;centerY<=256;centerY+=legacyCenterSpacing)
for(std::int32_t centerX=-256;centerX<=256;centerX+=legacyCenterSpacing)
for(const Direction& direction:directions)
{
bool candidateGradeFail=false;
std::int64_t previousGround=0,previousX=0,previousY=0;
for(std::int32_t index=0;index<=32;++index)
{
const std::int64_t along=(index-16)*legacyWaypointSpacing;
const std::int64_t x=centerX+std::llround(
static_cast<double>(along)*direction.x/65536.0);
const std::int64_t y=centerY+std::llround(
static_cast<double>(along)*direction.y/65536.0);
const auto column=finalField.point(WorldAssemblyFixed(static_cast<double>(x)),
WorldAssemblyFixed(static_cast<double>(y)),0);
const double groundMetres=static_cast<double>(column.groundHeightSubvoxels)
*coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel;
legacyMinimumGround=std::min(legacyMinimumGround,groundMetres);
legacyMaximumGround=std::max(legacyMaximumGround,groundMetres);
if(index>0)
{
const std::int64_t horizontalQ=static_cast<std::int64_t>(std::llround(
std::hypot(static_cast<double>(x-previousX),
static_cast<double>(y-previousY))*WorldAssemblySubvoxelsPerVoxel));
const std::int64_t riseQ=std::abs(
column.groundHeightSubvoxels-previousGround);
if(horizontalQ==0||riseQ*65536>horizontalQ*GradeQ16)
{
candidateGradeFail=true;
GradeSegmentAudit& segment=legacySegments[
static_cast<std::size_t>(index-1)];
++segment.failures;
const double grade=horizontalQ>0
?static_cast<double>(riseQ)/static_cast<double>(horizontalQ)
:std::numeric_limits<double>::infinity();
if(grade>segment.maximumGrade)
{
segment.maximumGrade=grade;
segment.fromGround=previousGround;
segment.toGround=column.groundHeightSubvoxels;
segment.fromX=previousX;segment.fromY=previousY;
segment.toX=x;segment.toY=y;
}
}
}
previousGround=column.groundHeightSubvoxels;previousX=x;previousY=y;
}
if(candidateGradeFail)++legacyGradeFailCandidates;
}
std::printf("[WorldAssemblerLegacyRouteGradeProfile] candidates=648 grade_fail_candidates=%llu ground_range_m=%.3f..%.3f grade_limit_deg=15.000 chord_m=%.3f waypoint_step_m=%.3f center_step_m=%.3f\n",
static_cast<unsigned long long>(legacyGradeFailCandidates),
legacyMinimumGround,legacyMaximumGround,256.0*coreVoxelMetres,
legacyWaypointSpacing*coreVoxelMetres,legacyCenterSpacing*coreVoxelMetres);
const double metresPerSubvoxel=coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel;
for(std::size_t segmentIndex=0;segmentIndex<legacySegments.size();++segmentIndex)
{
const GradeSegmentAudit& segment=legacySegments[segmentIndex];
if(segment.failures==0)continue;
std::printf("[WorldAssemblerLegacyRouteGradeSegment] index=%zu rejected_candidates=%llu max_grade_deg=%.3f ground_m=%.3f->%.3f xy_m=(%.3f,%.3f)->(%.3f,%.3f)\n",
segmentIndex,static_cast<unsigned long long>(segment.failures),
std::atan(segment.maximumGrade)*57.29577951308232,
segment.fromGround*metresPerSubvoxel,segment.toGround*metresPerSubvoxel,
segment.fromX*coreVoxelMetres,segment.fromY*coreVoxelMetres,
segment.toX*coreVoxelMetres,segment.toY*coreVoxelMetres);
}
}
std::printf("[WorldAssemblerRouteCandidateEnvelope] max_chord_m=%.3f length_variants=%zu center_step_m=%.3f arrival_radius_m=%.3f departure_radius_m=%.3f\n", std::printf("[WorldAssemblerRouteCandidateEnvelope] max_chord_m=%.3f length_variants=%zu center_step_m=%.3f arrival_radius_m=%.3f departure_radius_m=%.3f\n",
2.0*candidateHalfLengths.front()*coreVoxelMetres,candidateHalfLengths.size(), 2.0*candidateHalfLengths.front()*coreVoxelMetres,candidateHalfLengths.size(),
candidateCenterSpacing*coreVoxelMetres, candidateCenterSpacing*coreVoxelMetres,
@@ -1143,6 +1069,190 @@ namespace StandaloneWorldAssemblyPrototype
return "Unknown"; return "Unknown";
} }
inline bool PlanOwnerTunnelNetworkCaves(const FStandaloneOwnerWorld& owner,
VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,std::string& error)
{
using namespace VoxelForgeCore;
if(!owner.Manager)
{error="Tunnel Network cave planner requires the initialized owner director";return false;}
std::vector<const FStrateSlot*> tunnelSlots;
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition
||owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ))
!=ECaveGeneratorType::TunnelNetwork)continue;
auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[&](const auto& item){return item.strateIndex==slot.StrateIndex;});
if(stratum==layout.strates.end())
{error="Tunnel Network strate is missing from the resolved layout";return false;}
stratum->generatorKind="TunnelNetwork";
stratum->recipeId="Core.Caves.PrimitiveField";
stratum->recipeVersion=1;
stratum->stableId=layout.worldIdentity+"/strate/"
+std::to_string(slot.StrateIndex)+"/generator/TunnelNetwork";
const bool alreadyPlanned=std::any_of(layout.cavernRooms.begin(),
layout.cavernRooms.end(),[&](const auto& room)
{return room.strateIndex==slot.StrateIndex;})
&&std::any_of(layout.cavernEdges.begin(),layout.cavernEdges.end(),
[&](const auto& edge){return edge.strateIndex==slot.StrateIndex;});
if(!alreadyPlanned)tunnelSlots.push_back(&slot);
}
if(!layout.finalize(error))return false;
const auto toMicrometres=[&](double ownerVoxels)
{
return static_cast<std::int64_t>(std::llround(ownerVoxels
*static_cast<double>(owner.OwnerVoxelSizeMicrons)));
};
const auto toQ16=[](float value)
{
return static_cast<std::int32_t>(std::llround(std::clamp(
static_cast<double>(value),0.0,1.0)*65536.0));
};
if(!tunnelSlots.empty())
{
std::vector<ResolvedCavernRoomV1> plannedRooms=layout.cavernRooms;
std::vector<ResolvedCavernEdgeV1> plannedEdges=layout.cavernEdges;
for(const FStrateSlot* slot:tunnelSlots)
{
const FStrateGenerationParams& params=slot->Definition->GenerationParams;
ResolvedCavernPlannerSettingsV1 settings;
settings.roomSpacingMicrometres=std::max<std::int64_t>(4000000,
toMicrometres(params.RoomSpacing));
settings.minimumRoomRadiusMicrometres=std::max<std::int64_t>(500000,
toMicrometres(params.MinRoomRadius));
settings.maximumRoomRadiusMicrometres=std::max(
settings.minimumRoomRadiusMicrometres,toMicrometres(params.MaxRoomRadius));
settings.destinationRadiusMicrometres=std::max(
toMicrometres(params.OriginRoomRadius),
settings.maximumRoomRadiusMicrometres*135/100);
settings.minimumTunnelRadiusMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.TunnelMinRadius));
settings.maximumTunnelRadiusMicrometres=std::max(
settings.minimumTunnelRadiusMicrometres,toMicrometres(params.TunnelMaxRadius));
settings.maximumTunnelLengthMicrometres=std::max<std::int64_t>(4000000,
toMicrometres(params.MaxTunnelLength));
settings.tunnelWarpMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.TunnelWarpStrength));
settings.caveWarpMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.CaveWarpStrength));
settings.smoothWidthMicrometres=std::max<std::int64_t>(1500000,
toMicrometres(params.SDFBlendRadius));
settings.floorSupportDepthMicrometres=std::clamp<std::int64_t>(
settings.smoothWidthMicrometres/2,750000,2000000);
settings.irregularityAmplitudeMicrometres=std::max<std::int64_t>(0,
toMicrometres(params.SurfaceRoughness));
settings.irregularityWavelengthMicrometres=params.RoughnessFrequency>0.0f
?static_cast<std::int64_t>(std::llround(owner.OwnerVoxelSizeMicrons
/static_cast<double>(params.RoughnessFrequency))):0;
settings.roomDensityQ16=toQ16(params.RoomDensity);
settings.tunnelDensityQ16=toQ16(params.TunnelDensity);
settings.roomShapeVarietyQ16=toQ16(params.RoomShapeVariety);
settings.roomHeightRatioQ16=toQ16(params.RoomHeightRatio);
settings.roomFloorCutMinimumQ16=toQ16(params.RoomFloorCutMin);
settings.roomFloorCutMaximumQ16=toQ16(params.RoomFloorCutMax);
settings.tunnelHorizontalBiasQ16=toQ16(params.TunnelHorizontalBias);
settings.originRoomMaximumConnections=params.OriginRoomMaxConnections;
settings.tunnelsFlowTowardOrigin=params.bTunnelsFlowTowardOrigin;
std::vector<ResolvedCavernRoomV1> rooms;
std::vector<ResolvedCavernEdgeV1> edges;
if(!PlanResolvedCavernNetworkV1(layout,owner.Seed,slot->StrateIndex,
settings,rooms,edges,error))return false;
plannedRooms.insert(plannedRooms.end(),
std::make_move_iterator(rooms.begin()),std::make_move_iterator(rooms.end()));
plannedEdges.insert(plannedEdges.end(),
std::make_move_iterator(edges.begin()),std::make_move_iterator(edges.end()));
}
if(!ResolveResolvedCavernGraphV1(layout,owner.Seed,
plannedRooms,plannedEdges,error))return false;
}
const auto toCoreSubvoxels=[&](std::int64_t micrometres)
{
return static_cast<std::int64_t>(std::llround(
static_cast<double>(micrometres)*WorldAssemblySubvoxelsPerVoxel
/layout.coreVoxelSizeMicrometres));
};
for(const FStrateSlot& slot:owner.Manager->GetLayout())
{
if(!slot.Definition
||owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ))
!=ECaveGeneratorType::TunnelNetwork)continue;
auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(),
[&](const auto& item){return item.strateIndex==slot.StrateIndex;});
if(stratum==layout.strates.end())continue;
const bool hasRoutePoints=std::count_if(stratum->anchors.begin(),
stratum->anchors.end(),[](const auto& anchor)
{return anchor.kind=="route_point"&&anchor.values.size()>=3;})>=2;
if(hasRoutePoints)continue;
const ResolvedLayoutPassageV1* arrival=nullptr;
const ResolvedLayoutPassageV1* departure=nullptr;
for(const auto& passage:layout.passages)
{
if(passage.lowerStrateIndex==slot.StrateIndex
&&passage.upperStrateIndex<slot.StrateIndex)arrival=&passage;
if(passage.upperStrateIndex==slot.StrateIndex
&&passage.lowerStrateIndex>slot.StrateIndex)departure=&passage;
}
if(!arrival||!departure)
{error="Tunnel Network route anchors cannot bind both passage mouths";return false;}
const auto appendRoutePoint=[&](std::string id,
const std::array<std::int64_t,3>& point)
{
ResolvedLayoutAnchorV2 anchor;anchor.kind="route_point";
anchor.stableId="tunnel-network/route/"+std::move(id);
anchor.units="1/256 Core voxel";
anchor.values={point[0],point[1],point[2]};
stratum->anchors.push_back(std::move(anchor));
};
constexpr std::int64_t PlayerCenterAboveFloorMicrometres=1000000;
auto start=arrival->lowerMouthSubvoxels;
start[2]+=toCoreSubvoxels(PlayerCenterAboveFloorMicrometres);
appendRoutePoint("000-arrival",start);
for(std::size_t edgeIndex=0;edgeIndex<256;++edgeIndex)
{
const std::string stableId="tunnel-network/strate-"
+std::to_string(slot.StrateIndex)+"/route-edge-"
+std::to_string(edgeIndex);
const auto edge=std::find_if(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& value)
{return value.strateIndex==slot.StrateIndex
&&value.stableId==stableId;});
if(edge==layout.cavernEdges.end())break;
if(edgeIndex==0)
{
auto inner=edge->controlPointsMicrometres.front();
inner[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint("000-edge-start",
{toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
toCoreSubvoxels(inner[2])});
}
auto point=edge->controlPointsMicrometres.back();
point[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint(std::to_string(edgeIndex+1),
{toCoreSubvoxels(point[0]),toCoreSubvoxels(point[1]),
toCoreSubvoxels(point[2])});
}
const std::string departureEdgeId="tunnel-network/strate-"
+std::to_string(slot.StrateIndex)+"/departure-edge";
const auto departureEdge=std::find_if(layout.cavernEdges.begin(),
layout.cavernEdges.end(),[&](const auto& value)
{return value.strateIndex==slot.StrateIndex
&&value.stableId==departureEdgeId;});
if(departureEdge!=layout.cavernEdges.end())
{
auto inner=departureEdge->controlPointsMicrometres.front();
inner[2]+=PlayerCenterAboveFloorMicrometres;
appendRoutePoint("998-departure-inner",
{toCoreSubvoxels(inner[0]),toCoreSubvoxels(inner[1]),
toCoreSubvoxels(inner[2])});
}
auto end=departure->upperMouthSubvoxels;
end[2]+=toCoreSubvoxels(PlayerCenterAboveFloorMicrometres);
appendRoutePoint("999-departure",end);
}
return layout.finalize(error);
}
inline bool ImportExportedCoreWorldAssembly(const VFJson::Value& root, inline bool ImportExportedCoreWorldAssembly(const VFJson::Value& root,
VoxelForgeCore::WorldAssemblyManifest& out,std::string& error) VoxelForgeCore::WorldAssemblyManifest& out,std::string& error)
{ {
@@ -1258,8 +1368,11 @@ namespace StandaloneWorldAssemblyPrototype
if(!slot.Definition)continue; if(!slot.Definition)continue;
const auto type=owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ)); const auto type=owner.Manager->GetGeneratorTypeForChunk(FIntVector(0,0,slot.TopChunkZ));
const auto authored=sealAndDensity(*slot.Definition,type); const auto authored=sealAndDensity(*slot.Definition,type);
const bool nativeCaves=recipeVariant=="caves" const bool nativeSurfaceCaves=recipeVariant=="caves"
&&type==ECaveGeneratorType::SurfaceWorld; &&type==ECaveGeneratorType::SurfaceWorld;
const bool nativeTunnelCaves=type==ECaveGeneratorType::TunnelNetwork
&&recipeVariant!="legacy-tunnel-network"&&recipeVariant!="caves";
const bool nativeCaves=nativeSurfaceCaves||nativeTunnelCaves;
const bool nativeLattice=recipeVariant==("lattice/"+std::to_string(slot.StrateIndex)) const bool nativeLattice=recipeVariant==("lattice/"+std::to_string(slot.StrateIndex))
&&(type==ECaveGeneratorType::Maze||type==ECaveGeneratorType::VerticalShafts); &&(type==ECaveGeneratorType::Maze||type==ECaveGeneratorType::VerticalShafts);
const std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*owner.StrateLayoutChunkSizeMicrometres; const std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*owner.StrateLayoutChunkSizeMicrometres;
@@ -1269,7 +1382,7 @@ namespace StandaloneWorldAssemblyPrototype
if(seal<=0||2*seal>=top-bottom) if(seal<=0||2*seal>=top-bottom)
{error="strate "+std::to_string(slot.StrateIndex)+" has an invalid BoundarySealThickness for two boundary bands";return false;} {error="strate "+std::to_string(slot.StrateIndex)+" has an invalid BoundarySealThickness for two boundary bands";return false;}
ResolvedLayoutStratumV2 s;s.strateIndex=slot.StrateIndex; ResolvedLayoutStratumV2 s;s.strateIndex=slot.StrateIndex;
s.generatorKind=nativeCaves?"Caves":GeneratorKind(type); s.generatorKind=nativeSurfaceCaves?"Caves":GeneratorKind(type);
s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex) s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex)
+"/generator/"+s.generatorKind; +"/generator/"+s.generatorKind;
s.recipeId=nativeLattice?"Core.Lattice.PrimitiveField" s.recipeId=nativeLattice?"Core.Lattice.PrimitiveField"
@@ -1481,6 +1594,8 @@ namespace StandaloneWorldAssemblyPrototype
passageConfigs.push_back(config); passageConfigs.push_back(config);
} }
if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false; if(!PlanResolvedLayoutPassagesV1(out,owner.Seed,passageConfigs,error))return false;
if(recipeVariant!="legacy-tunnel-network"&&recipeVariant!="caves")
return PlanOwnerTunnelNetworkCaves(owner,out,error);
return out.finalize(error); return out.finalize(error);
} }
@@ -0,0 +1,12 @@
[PipelineFullDetailHash] tiles=960 density=0x91931a57a067fd92 mesh=0xa818654be89790ac payload=0x370ff80451105a39 topology=0x06e507557aa70173
[PipelineWholeViewHash] tiles=2370 density=0xf4d3d09d3f31d9bc mesh=0x520f545165440f40 payload=0x6a45502e6bb7ae01 topology=0x8f7d03ba1e2000f3
[PipelineLODHash] lod=0 tiles=512 density=0x2fd4c78685a43cfe mesh=0x50478cff1c1f8ca0 payload=0x54e48e57e0af26de topology=0x860c011cfdb9833a
[PipelineLODHash] lod=1 tiles=448 density=0x4b8d9a6dcd2942ad mesh=0x17e89b7087bfe711 payload=0x2389c109eb780db6 topology=0x7eccd4cf560d0630
[PipelineLODHash] lod=2 tiles=320 density=0x7640a0646ee5ac17 mesh=0x9b63c5aa29160969 payload=0x91cd7ca9a828149b topology=0xcb558843c345a327
[PipelineLODHash] lod=3 tiles=208 density=0xdd07fe8f31ede73a mesh=0xfaa2f90438226625 payload=0x86904d144f5db47c topology=0xcfd10c90d38f3fca
[PipelineLODHash] lod=4 tiles=240 density=0xfb11591772a3d18e mesh=0xebf2baee8764591b payload=0xe491d7ac9c0061ad topology=0xd618ac33b95ce494
[PipelineLODHash] lod=5 tiles=144 density=0x2f8b7b7fb1e67258 mesh=0x4cd508cbe97344fd payload=0x2b6e96a6050458c9 topology=0x4ff6d0eda8988573
[PipelineLODHash] lod=6 tiles=96 density=0x0114a34c8b8d6113 mesh=0xb1fec4d01ae1fa44 payload=0x3e50cfd429b8e950 topology=0x08090c96d15c1618
[PipelineLODHash] lod=7 tiles=96 density=0x3836ed8cfce73418 mesh=0x40bc6589c5fc1aac payload=0x677349bcae16790f topology=0x36e3aa53192e6a6b
[PipelineLODHash] lod=8 tiles=306 density=0x144f3c1fd722708b mesh=0xef77b28bf7fd9453 payload=0xe3f47b37fcd61489 topology=0x5a3c73e295494e04
[PipelineExactQueryHash] probes=9 density=0x7b7c50ac24ccadcc unclamped=0x7b7c50ac24ccadcc detail=0xefcba20c0c70c415 collision=0xe604823a249029bf path=direct-Core-queries
@@ -0,0 +1,12 @@
[PipelineFullDetailHash] tiles=960 density=0x11802e529414a62a mesh=0xee7dab071f572c46 payload=0x824a7c07f3cd3f8f topology=0x14a372c4f0003a9b
[PipelineWholeViewHash] tiles=2578 density=0x077534532d13b549 mesh=0x0932314a7475fed4 payload=0x6c2bfa3e4a72f8dc topology=0x7e6b3b76ed31bd0f
[PipelineLODHash] lod=0 tiles=512 density=0xf8cbb499fca6f62b mesh=0xf3b8e097407eb9e1 payload=0xeef6bfe266279de5 topology=0x79c5ed3811360d1e
[PipelineLODHash] lod=1 tiles=448 density=0x7d95c955a8433cac mesh=0xa803bb427deeba9a payload=0xf4718f1ce85d4f4f topology=0x7b770585aafd8174
[PipelineLODHash] lod=2 tiles=320 density=0x1c9445d15cce0de3 mesh=0x4da3bcf40eb6d137 payload=0x561116e3bbf34b8e topology=0x5e0e5a961bea641e
[PipelineLODHash] lod=3 tiles=272 density=0xe505981dea225509 mesh=0x1b13027b94137ad0 payload=0x3f93cd2fda3fef1c topology=0x8e160cd2f5ab0805
[PipelineLODHash] lod=4 tiles=272 density=0xb743eb64967bddcd mesh=0xf6b5bd36b0ebfead payload=0xd1284c1540f50636 topology=0x4a6e197e2a1a9500
[PipelineLODHash] lod=5 tiles=208 density=0x38706b628a74dd17 mesh=0x445b1c7b51489cbb payload=0x713efc66290768f7 topology=0x56486b919146bb1e
[PipelineLODHash] lod=6 tiles=144 density=0xd53849b2002a1faa mesh=0xaa210abc8498c415 payload=0xd109aef8639706d1 topology=0x61a4d75fbbc35268
[PipelineLODHash] lod=7 tiles=96 density=0xb0ea9a140275c9cc mesh=0x3ffd8365c8b0ec29 payload=0x3577afb0a2e4a420 topology=0x97530aafde15b7e0
[PipelineLODHash] lod=8 tiles=306 density=0x8b3d1816799d67a0 mesh=0x80bf554f17700e1e payload=0xccbe22c3333a9c34 topology=0xcb11b28f6b6bcc50
[PipelineExactQueryHash] probes=9 density=0xf2df67b38b7802df unclamped=0x48b347986345b557 detail=0xefcba20c0c70c415 collision=0x137e8f6c0b03a624 path=direct-Core-queries
@@ -0,0 +1,12 @@
[PipelineFullDetailHash] tiles=960 density=0xace56c8f205238ad mesh=0xa9c603a1248de6b8 payload=0xea55f1f4fcc6a894 topology=0x5ea7bc78a1b678ae
[PipelineWholeViewHash] tiles=2370 density=0x03331b1721d89092 mesh=0x1fa61520dc6c6d7b payload=0xe5c306e087381056 topology=0xef499f11d87ca569
[PipelineLODHash] lod=0 tiles=512 density=0xcd6ab28cfa504aca mesh=0xf03dc80a669fca31 payload=0x397aded1c3d2d297 topology=0xb4a1dc84fabc1fe3
[PipelineLODHash] lod=1 tiles=448 density=0xce6e713828b2a20a mesh=0x1cf408c25d67d18c payload=0x8a2b5d20571c997e topology=0x51cd985fe8e7d100
[PipelineLODHash] lod=2 tiles=320 density=0x8581c27a6966cccd mesh=0x610a6eb3ef75fe11 payload=0xdecaf47d125efd5c topology=0xa5ea883ebeb628e4
[PipelineLODHash] lod=3 tiles=208 density=0x5d87341a6428ea0b mesh=0x662096703512f5e2 payload=0x5703acca3ad9c2c7 topology=0xc8fb411f09c46f60
[PipelineLODHash] lod=4 tiles=240 density=0x0be8798407724eaf mesh=0x131c303cbbe51434 payload=0x2a90ecf0a2411ca0 topology=0xe3e054a371152e08
[PipelineLODHash] lod=5 tiles=144 density=0xf20897ad272103a9 mesh=0x13d00b6253ae1f72 payload=0x0db3880693a962fa topology=0x462bd87a80f15e89
[PipelineLODHash] lod=6 tiles=96 density=0xc040fcac457d6313 mesh=0x1ed132eeb718fb62 payload=0xcd0214e35577c6e3 topology=0x2c56c69e6fdcfd64
[PipelineLODHash] lod=7 tiles=96 density=0xc2d419446355fdb8 mesh=0x4ea44452574fc84b payload=0xfd9779bedfc9ae0f topology=0x8948546e770c9b72
[PipelineLODHash] lod=8 tiles=306 density=0x62db31696f2da831 mesh=0xd373f9e9481532fe payload=0x540a60526e0e3e46 topology=0xc725cb901e6dc335
[PipelineExactQueryHash] probes=9 density=0xc949c4df2ce6f9d9 unclamped=0xc949c4df2ce6f9d9 detail=0x114c501e2d830d94 collision=0xe6011d3a248d4849 path=direct-Core-queries
+133 -4
View File
@@ -89,6 +89,9 @@ TILE_HASH_RE = re.compile(
r"^\[PipelineTileHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) " r"^\[PipelineTileHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) "
r"density=(0x[0-9a-f]+) mesh=(0x[0-9a-f]+) " r"density=(0x[0-9a-f]+) mesh=(0x[0-9a-f]+) "
r"payload=(0x[0-9a-f]+) topology=(0x[0-9a-f]+)$", re.M) r"payload=(0x[0-9a-f]+) topology=(0x[0-9a-f]+)$", re.M)
DENSITY_DISPATCH_RE = re.compile(
r"^\[DensityDispatch\] strate=(\d+) generator=([^ ]+) recipe=([^ ]+) "
r"version=(\d+) bound_path=([^ ]+) adapter=([^ ]+)$", re.M)
TILE_MATERIAL_HASH_RE = re.compile( TILE_MATERIAL_HASH_RE = re.compile(
r"^\[PipelineTileMaterialHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) " r"^\[PipelineTileMaterialHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) "
r"material=(0x[0-9a-f]+)$", re.M) r"material=(0x[0-9a-f]+)$", re.M)
@@ -122,11 +125,11 @@ RECIPE_RE = re.compile(
def owner_hash_baseline(recipe: str, style: int) -> tuple[Path, str]: def owner_hash_baseline(recipe: str, style: int) -> tuple[Path, str]:
if recipe == "surface-world" and style == 1: if recipe == "surface-world" and style == 1:
return (BASELINES / "owner-hash/canonical/owner-hash-surface-world-style1-v225.baseline", return (BASELINES / "owner-hash/canonical/owner-hash-surface-world-style1-v227.baseline",
"L125-v225-stamped-passage-sweep") "L130-v227-reach-units")
if recipe == "floating-islands" and style == 2: if recipe == "floating-islands" and style == 2:
return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v225.baseline", return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v227.baseline",
"L125-v225-stamped-passage-sweep") "L130-v227-reach-units")
raise VerificationFailure(f"no pinned owner hash baseline for {recipe} style {style}") raise VerificationFailure(f"no pinned owner hash baseline for {recipe} style {style}")
@@ -493,6 +496,131 @@ def run_new_owner_plan_stage(runner: VerificationRunner, binary: Path) -> None:
+ ", ".join(deferred_failures)) + ", ".join(deferred_failures))
def run_density_dispatch_report(runner: VerificationRunner, binary: Path) -> None:
"""Report selected owner density paths and generated tile shares by path."""
cameras = {
0: (8.0, 16.0, -372.2626953125),
1: (25.8369140625, 3.19921875, -737.0),
2: (19.166015625, -14.2001953125, -753.3740234375),
3: (-20.0625, -5.068359375, -900.353515625),
}
aggregate: dict[int, dict[str, int]] = {}
bound_paths: dict[int, tuple[str, str, str, int, str]] = {}
failures: list[str] = []
for focus_strate, position in cameras.items():
name = f"owner-density-dispatch-s{focus_strate}-clang"
camera = "--camera=" + ",".join(
f"{value:.12f}" for value in (*position, 0.0, 0.0))
layout_path = OUT / "layouts" / f"{name}.json"
try:
row, output = runner.run_cpp(name, binary, OWNER_NEW, "surface-world",
["--density-dispatch-report", "--render-distance-chunks=0", camera],
expected_style=1, workers=8, max_level=8, clip_radius=3,
layout_path=layout_path)
except VerificationFailure as error:
failures.append(f"{name}: {error}")
continue
dispatch_rows = DENSITY_DISPATCH_RE.findall(output)
dispatch = {int(index): (path, generator, recipe, int(version), adapter)
for index, generator, recipe, version, path, adapter in dispatch_rows}
if len(dispatch_rows) != 4 or set(dispatch) != {0, 1, 2, 3}:
failures.append(f"{name}: expected four unique dispatch rows, got {dispatch_rows}")
continue
if not layout_path.is_file():
failures.append(f"{name}: resolved layout missing at {layout_path}")
continue
try:
layout = json.loads(layout_path.read_text(encoding="utf-8"))
strata = {int(value["strate_index"]): value for value in layout.get("strates", [])}
if set(strata) != {0, 1, 2, 3}:
raise ValueError(f"expected four strata, got {sorted(strata)}")
voxel_size_um = int(strata[0]["voxel_size_um"])
if voxel_size_um <= 0:
raise ValueError(f"invalid voxel_size_um={voxel_size_um}")
tunnel_network = strata[2]
if (tunnel_network.get("generator_kind") != "TunnelNetwork"
or tunnel_network.get("recipe_id") != "Core.Caves.PrimitiveField"
or int(tunnel_network.get("recipe_version", 0)) != 1):
failures.append(f"{name}: Tunnel Network did not bind Core cave recipe v1: "
f"generator={tunnel_network.get('generator_kind')} "
f"recipe={tunnel_network.get('recipe_id')} "
f"version={tunnel_network.get('recipe_version')}")
if dispatch.get(2) != ("Caves-native", "TunnelNetwork",
"Core.Caves.PrimitiveField", 1,
"FCavesFieldAdapter"):
failures.append(f"{name}: Tunnel Network dispatch is not native caves: "
f"{dispatch.get(2)}")
except (OSError, json.JSONDecodeError, KeyError, TypeError, ValueError) as error:
failures.append(f"{name}: cannot classify tiles from resolved layout: {error}")
continue
tile_rows = TILE_HASH_RE.findall(output)
camera_counts = {index: 0 for index in strata}
for lod_text, _x, _y, z_text, *_hashes in tile_rows:
lod, z = int(lod_text), int(z_text)
if lod < 0 or lod > 30:
failures.append(f"{name}: invalid tile lod={lod}")
continue
extent_voxels = 32 << lod
center_um = (z * extent_voxels + extent_voxels // 2) * voxel_size_um
matches = [index for index, stratum in strata.items()
if int(stratum["interior_bottom_um"]) <= center_um
< int(stratum["interior_top_um"])]
if len(matches) == 1:
index = matches[0]
camera_counts[index] += 1
aggregate.setdefault(index, {}).setdefault(dispatch[index][0], 0)
aggregate[index][dispatch[index][0]] += 1
if not tile_rows:
failures.append(f"{name}: --density-dispatch-report emitted no tile hashes")
observed = sum(camera_counts.values())
print(f"[DensityDispatchView] focus_strate={focus_strate} tiles={len(tile_rows)} "
f"classified_by_center={observed} status={'PASS' if observed else 'FAIL'}",
flush=True)
runner.require(f"{name}:dispatch-and-tile-counts",
observed > 0 and row["status"] == "PASS",
f"dispatch={dispatch} center_attributed_tiles={camera_counts} "
f"hashed_tiles={len(tile_rows)}")
for index, value in dispatch.items():
previous = bound_paths.get(index)
if previous is not None and previous != value:
failures.append(f"strate {index}: bound path changed between views: "
f"{previous} vs {value}")
bound_paths[index] = value
global_path_counts: dict[str, int] = {}
for index in range(4):
path, generator, recipe, version, adapter = bound_paths.get(
index, ("MISSING", "?", "?", 0, "?"))
path_counts = aggregate.get(index, {})
total = sum(path_counts.values())
for path_name, count in path_counts.items():
global_path_counts[path_name] = global_path_counts.get(path_name, 0) + count
details = ",".join(
f"{key}:{value}/{total} ({100.0 * value / max(1, total):.1f}%)"
for key, value in sorted(path_counts.items())) or "no tile centers observed"
print(f"[DensityDispatchStrate] strate={index} generator={generator} recipe={recipe} "
f"version={version} bound_path={path} adapter={adapter} "
f"tile_centers={total} path_shares={details}", flush=True)
if path == "MISSING" or total == 0:
failures.append(f"strate {index}: dispatch path or tile-center share missing")
else:
runner.require(f"owner-density-dispatch-strate-{index}", True,
f"bound_path={path} tile_center_path_shares={details}")
all_tiles = sum(global_path_counts.values())
for path_name, count in sorted(global_path_counts.items()):
share = 100.0 * count / max(1, all_tiles)
print(f"[DensityDispatchPathShare] path={path_name} tiles={count}/{all_tiles} "
f"share={share:.1f}%", flush=True)
if not all_tiles:
failures.append("no tile centers were attributed to any bound path")
runner.require("owner-density-dispatch-report", not failures,
"; ".join(failures) if failures else
"four owner poses reported per-strate bound paths and tile-center path shares; "
"TN binds Core.Caves.PrimitiveField v1 through FCavesFieldAdapter")
def run_landmark_reachability(runner: VerificationRunner, def run_landmark_reachability(runner: VerificationRunner,
binaries: tuple[Path, ...]) -> None: binaries: tuple[Path, ...]) -> None:
for binary in binaries: for binary in binaries:
@@ -3144,6 +3272,7 @@ def main() -> int:
except VerificationFailure as error: except VerificationFailure as error:
new_plan_failure = str(error) new_plan_failure = str(error)
print(f"[{runner.tier}] new-owner-plan-stage FAIL {error}", flush=True) print(f"[{runner.tier}] new-owner-plan-stage FAIL {error}", flush=True)
run_density_dispatch_report(runner, CLANG)
run_landmark_reachability(runner, (CLANG,)) run_landmark_reachability(runner, (CLANG,))
run_regression_controls(runner) run_regression_controls(runner)
run_hash_pairs(runner, owners) run_hash_pairs(runner, owners)