132 Commits
Author SHA1 Message Date
Fr0zkaandClaude Opus 5.5 9d3c50ca95 wip(assembler): round S4 -- continuous far ceiling at the strate's upper boundary
S3's far cap sat up to 8.2 m above the near roof and read as floating pieces.
The far render cap now projects onto the owning strate's upper boundary
(density unchanged), on a global XY lattice, LOD4 16-voxel grid + LOD5-8
128-voxel. Cap agreement 0.05 cm (100 cm control detected), 2520/2520 upward
rays hit roof on the owner walk. Far recipe v2; LOD0-3 hashes equal S2/S3.
Tile p95 flat; cold clipmap +4.4%. Far-cliff fluting left open (coarse MC step).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 00:41:13 +02:00
Fr0zkaandClaude Opus 5.5 d0f97bf79a wip(assembler): round S3 -- far ground from the native heightfield; LOD7/8 tiles 36-66% cheaper
Far-shell repair (81-96% of S2's LOD7/8 tile time) is skipped for certified
SurfaceWorld tiles; ground and cap come from the shared planar heightfield.
Near LOD0-3 hashes equal S2; far recipe version 1 salts LOD5+ identities.
Cold first view 6.6 -> 3.4 s (aim 1.57 s). Not a visual sign-off: distant cap
shows floating pieces, sprint streaming arrives late.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-18 21:29:52 +02:00
Fr0zkaandClaude Opus 5.5 6ddb834b1c wip(assembler): round S2 (Sol) -- shared per-tile feature queries for every Core family; island placement 30x faster
Island-only worker context replaced by a shared worker-local candidate domain
(WorldAssemblyFeatureTileListV1 / ScopedWorldAssemblyFeatureQueriesV1) serving
SurfaceWorld, FloatingIslands and far-shell queries; a per-domain cost hint keeps the
full scan on small plans. Content placement prepares the same queries and skips proven
air: owner island cell p95 7.07 -> 0.24 ms; populated fixture 572 exact placements,
3.27 s -> 29 ms. Full/candidate hashes equal with dropped-feature controls on both
families; S1 default W1/W8/W16 hashes reproduced; Core-off and M2 fixtures pass.
Default contract for future families in ARCHITECTURE 8.23. Run on gpt-6.1-sol medium.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-18 19:50:45 +02:00
Fr0zkaandClaude Opus 5.5 d6e69420e8 wip(assembler): round S1 -- one Core voxel-size source; per-tile feature lists (islands)
AVoxelWorld::CoreVoxelSizeCm (default 25, 32 voxels/tile) flows through plan, field,
mesher, content, water, atmosphere, edits and collision. Staged-owner BuildHost passes at
20 cm (SurfaceWorld + islands, W1/W8/W16, anchors, BP edits, collision) with a fixed-25cm
negative control; default-size hashes match round 4; Core off stays legacy 12.5 cm.
Reusable BuildWorldAssemblyFeatureTileListV1; wired into FloatingIslands only so far.
256-island stress: tile p95 112.5 -> 2.0 ms, 0 mismatches, dropped-feature control fails.
Content placement measured (no populated cells): island ground search 7.25 ms/cell vs
legacy 0.29 ms -- open. Outside-review evaluation in Round5/REPORT.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-18 18:48:38 +02:00
Fr0zkaandClaude Opus 5.5 111cf3d262 wip(assembler): M3 round 4 -- plans built inside Unreal from the owner's strates; no import needed
SurfaceWorld and FloatingIslands plans generate at startup from the resolved seed,
strate types and heights (hashed, cached). Imported JSON is an opt-in override (off by
default); an invalid override shows an on-screen notice and falls back to legacy.
BuildHost on staged owner assets: SurfaceWorld cross-system checks, islands at owner
height (route, collision, anchors, BP edits), seed 137501, W1/W8/W16, Core-off legacy.
XY-column cache verified (hashes kept); island LOD0 p95 91 ms vs 133 ms sprint deadline.
Not exercised: season-seed path (no Season in staged settings), rendered notice pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 22:48:46 +02:00
Fr0zkaandClaude Opus 5.5 48fa028086 wip(assembler): M3 round 3 -- separated islands + Unreal island route (owner build 18:46)
Islands read as separate (91.89% open sky at mid-height), underside cavity closed,
rim teeth were capture shading. Island manifest import + BuildHost proof passed on the
pre-cache tree; M1 SurfaceWorld and M2 V1/V2 gates passed.

Known open: final XY-column cache compiled in the owner build but is unverified;
island LOD0 p95 ~155 ms misses the 133 ms sprint deadline (legacy ~43 ms); the island
manifest only fits the BuildHost fixture layout (owner's world: Core init fails,
empty world) -> round 4 builds plans inside Unreal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 20:53:03 +02:00
Fr0zkaandClaude Opus 5.5 4afea7c802 wip(assembler): M3 round 2 -- islands get real mass: deep crust, separate tapered roots, arched spans
Pinch cause: SmoothStep taper drove the underside radius to zero at one point. Version 2: depth 0.75x
diameter (thin-pad control at 0.30 fails), 3-4 seeded roots, bounded contour warp + roughness, one
off-route side island, 3 m rock spans. Bounds/route/W1-W8-W16 pass. Open: legacy probe metadata says
25 cm but production legacy is 12.5 cm -- round 3 must fix the import scale contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 12:24:35 +02:00
Fr0zkaandClaude Opus 5.5 53a1e69af7 wip(assembler): M3 round 1 -- native FloatingIslands recipe in Core (standalone)
Bounds 517 boxes, 0 missed, negative control caught; capsule route 1044 centers, raised-route control
fails; W1/W8/W16 stable (field bf3ce7da876431e2); 39 ms native vs 64 ms legacy. My review: islands read
as thin flat pads with a radial pinch under them, not rock; next round fixes the look.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 11:36:32 +02:00
Fr0zkaandClaude Opus 5.5 4661a445be wip(assembler): M2 round 4 -- WorldAssemblerHybridV2: exact contacts, tagged tiles 1.18-1.27x MC
The round-3 'field contact error' measured hit-to-anchor distance; V2 contacts are 0 cm and on the zero
isosurface. Tagged-tile cost 5.7-6.7x -> 1.175-1.270x MC; organic 0.998x. Bound, shelf-thickness and route
audits rerun with negative controls; W1/W8/W16 stable; V1 and MarchingCubesV1 unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 10:41:43 +02:00
Fr0zkaandClaude Opus 5.5 22a6b30494 wip(assembler): M2 round 3 -- opt-in hybrid sharp mesher (WorldAssemblerHybridV1) in the Unreal Core route
Default MarchingCubesV1 unchanged (W1/W8/W16 = round-6 hashes). Hybrid DC only in LOD0 interior cells
with tagged features; tile-border cells and LOD1+ stay MC. BuildHost: fixtures repeat exactly, cooked
collision bounds = render bounds, same-LOD borders pass, sampled SurfaceWorld cliff bit-identical to MC.
Open: tagged tiles cost 5.7-6.7x MC; corner-top field contact error 20 cm; 5 MC doorway LOD cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 09:53:04 +02:00
Fr0zkaandClaude Opus 5.5 a3b7d2015e wip(assembler): M2 round 2 -- same-LOD borders watertight for MC/hybrid/DC; LOD0/1 transitions still open
Shared halo + deterministic border-quad ownership + T-junction conformance: 625/625 same-LOD for all three.
LOD 2:1 prototype (collar zipper) fails MC 5 / hybrid 38 / DC 40 cases (corners, overhangs).
Hybrid is bit-identical to MC on a sampled native SurfaceWorld cliff; DC rougher. Standalone only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 03:56:28 +02:00
Fr0zkaandClaude Opus 5.5 6953254349 wip(assembler): M2 round 1 -- sharp-mesher spike: hybrid MC+DC keeps edges, borders and LOD seams not solved yet
Standalone only. Hybrid/DC cut sharp residual 16.7 -> 6.25 cm and split creases ~100%, organic wall
unchanged under hybrid, +4.4% mesh cost, W1/W8/W16 identical. Gate failed: hybrid open edges on 500/625
tagged tile borders; every method fails LOD0/LOD1 (no transition templates yet). Binaries and OBJ meshes not committed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 01:32:27 +02:00
Fr0zkaandClaude Opus 5.5 61633cb492 wip(assembler): M1 round 6 -- owner walk fixes: 25 cm geometry scale, one height datum, carve/fill sign, faster relief meshing
Owner saw content floating above native terrain, spiky triangles, reversed carve/fill and late relief.
Causes: shared mesher placed Core tiles on a 12.5 cm grid; content/atmosphere asked strate ranges in
12.5 cm voxels; Core edits add density where the legacy diff subtracts. Horizon LOD8 bridge edge
5.657 -> 2.265 cells. Relief mesh p95 60.8 -> 42.0 ms (LOD0). New BuildHost checks for scale, height,
blueprint carve/fill and per-kind streaming lag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 20:39:24 +02:00
Fr0zkaandClaude Opus 5.5 f67b519e75 wip(assembler): M1 round 5 -- native SurfaceWorld manifest delivered into the Unreal Core route
Manifest imported into UVoxelSettings (hash 8933693d9560ba9f), folded into the season
content hash only when Core is on. BuildHost: W1/W8/W16 stable, 2553 surface tiles match
standalone, 432 LOD transitions, collision on all 36 expected tiles. Matched timing
96.8 s native vs 197.3 s bridge (2.04x). Collision support tiles wait for Core region admission.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 15:31:48 +02:00
Fr0zkaandClaude Opus 5.5 2d0cec90e4 wip(assembler): M1 round 4 -- cap rebuilt (no hiding), sub-voxel audit, routes + mesh-free queries
Luna, M1 round 4 (resumed after the night stop):
- Cliff: sub-voxel audit (29,929 half-voxel XY columns, min run 3.75 voxels, 0 subvoxel runs;
  old taper control exposes 20,963) and a root-line shelf at the two-voxel minimum.
- Horizon slab = aliased LOD8 cap ceiling. The first fix hid the ceiling at LOD8 (challenged:
  enclosed, interior-lit strates); now the LOD8 cap is rebuilt deterministically from the
  native heightfield, skirts preserved; looking up and out shows the cap present.
- Routes: review manifest with 33 stable-ID route points + capsule/support constraints; capsule
  sweep 0 blocked samples / 0 unsupported centres, negative control detected.
- Server path: World::densityAt answers without render meshes. Physics cooking not available
  standalone.
- Hashes (evaluator + 2,226-tile stream) identical at W1/W8/W16. LOD8: 4.446 s for 529 tiles,
  14.25 M field calls, 165 unknown tiles.
- Captures at 60 fps cap + vsync (owner's GPU coil whine). One cliff probe crossed the ~8 GB
  guard (7.69 GiB) and was stopped before capturing.
My review: cap view matches legacy closely, no slab; native cliff is a clean attached ledge
(cleaner than legacy); a few small spikes on the ridge top.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 14:23:38 +02:00
Fr0zkaandClaude Opus 5.5 20c6d86f0e wip(assembler): M1 round 4 PARTIAL -- stopped at owner's request (GPU coil whine at night)
Unverified mid-round state (cliff plates / horizon slab / routes in progress). Resume from here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 02:12:39 +02:00
Fr0zkaandClaude Opus 5.5 2c20f9eaea wip(assembler): M1 repair round -- raised block and cliff fixed; native 7x faster than legacy
Luna, M1 repair round: the rectangular raised block and the broken cliff shared one cause,
a type-2 solid AABB in the generated plan; it is now a tapered, terrain-following shelf, and
a negative-control audit catches the old box behaviour; seam classification check passes.
Bound audit 298 boxes / 8,046 samples, 0 misses. Evaluator and 120-tile density/mesh/payload/
topology hashes identical at W1/W8/W16. Cave regression: walkability 5,642/5,642, 64/64 tiles.
Legacy mismatch unchanged (0 biome mismatches, 18.14% signs).
Speed (matched workloads): 120-tile full detail 0.089 s native vs 0.625 s legacy; 529-tile
clipmap 3.858 s vs 11.687 s -- 2.29 s above the ~1.57 s aim; LOD8 dominates (165 unknown
tiles, 13.27 M field calls, 10.88 M column-cache misses).
My capture review: coast matches legacy closely; surface clean but a dark flat slab on the far
horizon; cliff shelf renders as a stack of thin plates with back faces showing (sub-voxel
shelf thickness suspected).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 01:22:30 +02:00
Fr0zkaandClaude Opus 5.5 fcb16fd265 wip(assembler): M1 round 2 -- surface recipe closer to legacy; native still slower and visibly buggy
Luna, M1 round 2 of 4: round 1's mismatch was an underfit recipe (two synthetic profiles,
missing large-scale surface terms). Now: biome IDs match 4,096/4,096, mean height offset
-1.366 vox, density MAE 274.04 -> 17.23 vox, sign mismatches 55.2% -> 18.14%. Bound audit
298 boxes / 8,046 points, 0 misses, negative control caught. Hash c5e0371612e927af at
W1/W8/W16. Surface query 8-14 ms / 4,096 points. Matched 3D capture pairs added.
Problems (my review of the captures + her timings):
- matched 120-tile views: full-detail readiness 5.227 s native vs 2.882 s legacy -- SLOWER;
- surface capture shows a rectangular flat-topped raised block with vertical walls
  (tile/patch-level height bug, inferred);
- cliff capture is broken (floating shard, debris trail), not a cliff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 00:38:55 +02:00
Fr0zkaandClaude Opus 5.5 874c796c65 wip(assembler): M1 round 1 -- manifest format, block contract, bound audit, native Heightfield2D
Luna, M1 round 1 of 4 (plan in Standalone/ASSEMBLER_M1_REPORT.md):
- Versioned fixed-scale world manifest (the shipped plan format) and the Core block
  contract: point evaluation, interval-or-unknown, influence bound, detail envelope, channels.
- Interval audit: 256 boxes / 6,912 samples, 0 missed, 0 unknown; a deliberately false bound
  is detected (negative control).
- Native Heightfield2D with biome input: 4,096-point hash fae2721c052aec5c identical at
  W1/W8/W16; point queries 2.059 ms vs 62.115 ms legacy -- but NOT a match yet (mean density
  error 274 voxels, 2,261 sign mismatches vs the legacy surface). 45 mesh-free Core queries
  match direct evaluation exactly. Existing cave world unchanged (walkability, 64/64 tiles).
- Open: collision readiness, a valid full-view timing (viewer captures were occluded/empty;
  top-down height maps are the only usable pair).
Also carries the design doc's owner decisions (fixed water; player edits unrestricted, route
law for automatic changes only) and three plugin files + BuildHost scripts that earlier round
commits had missed (also fixed on unreal/core-integration as 2574b56).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 00:04:30 +02:00
Fr0zkaandClaude Opus 5.5 d5ce5d675d design: world assembler -- Luna D3 + Agreed design v1, signed off by both
Plan per finite strate shipped in the season manifest (fixed-scale, hashed); peers evaluate.
Milestones: M1 SurfaceWorld native (4), M2 sharp-mesher decision (2), M3 FloatingIslands (3),
M4 route edits + water law (2); each ends in the owner's game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 23:28:50 +02:00
Fr0zkaandClaude Opus 5.5 1cf25eff30 design: world assembler -- Luna D2 + Claude response
Design-only. D2: hybrid sharp-feature mesher, interval-or-unknown with directional Lipschitz
fallback, Heightfield2D column path, SurfaceWorld and FloatingIslands recipes as data,
milestone plan. Claude: plan shipped in season manifest?, surface first, every milestone in game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 23:24:25 +02:00
Fr0zkaandClaude Opus 5.5 61ce0dee4b design: world assembler discussion -- Luna D1 + Claude response
Design-only (no code): planner + fast shape assembler for all strate types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 23:12:59 +02:00
Fr0zkaandClaude Opus 5.5 7661fe9d24 wip(unreal): round 5 (stopped early) -- all 15 passage/strate probes walkable
Luna, round 5 goal 1 only (stopped by owner decision before the SurfaceWorld port):
- Worm and Cascading passages now pass walkability; the wide floor stays clipped while the
  narrow route core stays open.
- Hidden third failure found and fixed: long VerticalShafts diagonal legs were dropped from
  Core's spatial bins by a 100,000-bin safety cutoff (invisible to density queries);
  conservative bins restore them (18,570/18,570).
- All 15 saved probes pass a walkability-only gate; build-time walkability gate added.
NOT re-verified before the stop: W1/W8/W16 hashes, BuildHost UBT/actor run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 22:50:54 +02:00
Fr0zkaandClaude Opus 5.5 d696463c68 wip(unreal): round 4 -- strate-type coverage matrix; SurfaceWorld via exact legacy bridge
Luna, integration round 4 (owner requirement: Core must know every strate type).
Coverage, Core vs legacy owner field (one probe per type, 686,000 LOD0 samples):
  TunnelNetwork approximate (2.17% signs differ); FlatPlain / CrystalChamber native slab
  fields missing (generic cave, 1.28%); Maze missing (13.3%); VerticalShafts missing (no
  shaft surface); FloatingIslands missing (95.8%); Underwater approximate + no water.
  SurfaceWorld: exact density (0 sign changes, LOD0-3 hashes match) through the copied
  legacy evaluator -- but at legacy cost: full view 23.1 s vs legacy 23.9 s (Core caves
  without surface 0.785 s).
  Passages: SlopedTunnel/VerticalShaft/Spiral pass walkability; Cascading (365/1,495) and
  Worm (109/1,490) FAIL. Transitions approximate (1.2-1.3%).
BuildHost UBT + actor validation pass (SurfaceWorld layout: 2,553 visual keys, 448 LOD
transitions, biome query). Small owner/Core capture pairs in core_strate_coverage/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 22:29:15 +02:00
Fr0zkaandClaude Opus 5.5 75210e56f3 wip(unreal): round 3 -- owner's in-game defects classified; opening room fixed; Core biome query
Luna, integration round 3, from the owner's four in-game screenshots:
- Capsule rings at the opening and the shaft continuing through the cave room were Core
  world defects: the synthetic bore is removed and the opening gets a finite origin room
  and floor (solid below). New world version, W1/W8/W16 identical: density
  a8bcb72d41092503, mesh 90f29ba15e24579f, payload 82c6b489e7bb7fd7, topology
  7b99448a1f844f2c.
- B-side landing and surface: headless coverage found no missing tiles or LOD-transition
  gaps (960 / 640 fine tiles compiled after travel); best explained by Core's missing
  SurfaceWorld representation. Null-RHI cannot rule out a presentation-only issue.
- Core mode GetBiomeAtWorldLocation now answers (e.g. Biome_Surface_Plain); biome
  terrain itself is not generated yet (proposal: native Core SurfaceWorld evaluator).
Owner checked the Step29 viewer himself: the surface strate type is absent there too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 21:45:49 +02:00
Fr0zkaandClaude Opus 5.5 075e58187e wip(unreal): AVoxelWorld Core mode -- streaming, collision and carving through VoxelForgeCore
Luna, integration round 2 (BuildHost only): AVoxelWorld::bUseVoxelForgeCore (default
false) routes desired tiles, generation, upload/collision and carving through Core.
Core-only CoreClipRadius=3 / CoreMaxClipLevel=8 (owner's asset untouched); 25 cm Core
voxels in every cm<->voxel conversion; legacy brush sizes scaled to keep physical size;
UE-task JobScheduler, epoch-checked Mpsc results, existing RMC upload + collision cook;
carving via sequenced applyEditTransaction. Switch off: BuildDesiredTiles takes the old
path unchanged.
BuildHost proof (actor commandlet, staged copies of the owner's assets): all 2,226
initial tiles hash-match standalone Core; collision bodies as expected; anchor move
requests 1 / releases 1 tile; carved tile hash matches standalone; peak ~2.68 GiB.
Legacy CrossPlatformDigest automation: 115,000 samples pass (wrapper flagged an
analytics write outside isolation).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 20:33:46 +02:00
Fr0zkaandClaude Opus 5.5 29ca99af7f wip(unreal): VoxelForgeCore UBT module + asset adapter reproduce the standalone world
Luna, integration round 1 (BuildHost only; owner's project and assets untouched):
- New runtime module VoxelForgeCore: one shim per Standalone/Core/core_sources.txt row
  (sources stay canonical under Standalone/), NoPCH/no unity, manifest check; VoxelForge
  links it (no runtime routing yet). UE link-safety audit: 18 shims, 83,843 symbol lines
  vs 399 Unreal-style names, 0 clashes.
- Adapter (editor commandlet path) turns staged copies of the owner's DA_* assets into
  schema-v1 JSON; with clip_radius=3/max_clip_level=8 it reproduces all four Core hashes
  (0xdb2991c73215af4c, 0x8236c5edd7fc8e4f, 0x4c48fa75a543d58e, 0xbea22e76b34a4dfc) and
  walkability 5,642/5,642 at worker limits 1, 8 and 16.
- Noted: the owner's settings asset says max clip level 6 (standalone used 8); Core uses
  25 cm voxels vs the plugin runtime's 12.5 cm.
Not done yet: AVoxelWorld switch and Core runtime route (round 2).
Docs: CODEMAP, ARCHITECTURE, SHARED_CPP_LIBRARY_PLAN, UNREAL_INTEGRATION_REPORT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 19:38:18 +02:00
Fr0zkaandClaude Opus 5.5 a1dfc0422e wip(standalone): close the GPU port chapter at step 29 (owner decision)
Step 30 was stopped minutes in; its partial diff is not applied. Final standing on the
owner's machine, same session, 0xFFFF: CPU route 1.04-1.12 s; GPU cached 1.18-1.21 s
(step 27 routing) and 1.29-1.37 s (step 29). Next: bring the Core library into Unreal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 18:39:20 +02:00
Fr0zkaandClaude Opus 5.5 61106be142 wip(standalone): GPU step 29 -- viewer no longer self-resets affinity; per-exe shader bundles; W16; CPU route now fastest
Luna: the viewer reset itself to BelowNormal (since step 3) and 0xFF00 (since step 11)
at startup, overriding the runner; every earlier "full machine" race started on 8 CPUs
until the runner reapplied the mask. Fixed in viewer and agreement runner. Core
worker clamp raised to 16 (W1/W8/W16 hashes identical). Shader binaries now pinned per
executable (precompiled/<exe>/); Step27 control rebuilt from 7d1808f with only the
bundle lookup backported. Tried CPU fallback for skirted near tiles: -0.74 GiB GPU
memory but 2,934-4,086 missing pixels in four remote views, so GPU skirts stay default.
Audits: hashes W1/W8/W16, walk 5,642/5,642, near/far HW+WARP 0 diffs, both survivals
pass (7.00 GiB), ten captures 0 missing pixels.

My runs outside the sandbox (driver cache warm, marker removed first):
  0xFFFF Normal: CPU 1.121 / 1.081 / 1.040 s
                 GPU miss 1.324 s; cached 1.340 / 1.353 / 1.293 s (2,073-2,074 GPU tiles,
                 timestamps 463-471 ms)
  0xFF00 BelowNormal: CPU 1.305 / 1.302 s; GPU cached 1.678 / 1.692 / 1.756 s
  Same-session, own shader bundles, 0xFFFF: Step27 GPU cached 1.205 / 1.212 / 1.198 / 1.180 s;
                 Step29 GPU cached 1.359 / 1.367 / 1.366 / 1.333 s
=> the GPU route now loses to the CPU route by ~0.25 s on the full machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 18:30:39 +02:00
Fr0zkaandClaude Opus 5.5 5aa285c3ef wip(standalone): GPU step 28 -- skirted near tiles on GPU (correct, but SLOWER); 16 shape workers
Luna: near skirts were CPU-only by implementation gap, not correctness. Ported with
near-resolution refinement and overflow fallback; near-probe preparation split out so
the skirt kernels no longer inline the near field (first attempt: two fxc /O3 compiles
ran 10.5 h and were stopped). Shape compile proven identical at W1/W8/W16.
-SkipCpuDetailBand retired (154/640 skirt audit failures, +174 ms planning).
Audits: hashes W1/W8/W16, walk 5,642/5,642, whole-near 960 tiles + 640 skirt faces
0 sign/topology diffs (one 0.001 cm vertex-key delta), far/thin 0 diffs, both
survivals pass with 2,074 candidates at 7.00 / 7.02 GiB (was ~6.2), ten captures pass.

My runs outside the sandbox (Step28.exe). NOTE the machine was ~25% slower than last
night for every build (step 27 CPU full machine 0.97-1.02 s at night, 1.22-1.29 s now):
  0xFF00: CPU 1.337 / 1.291 / 1.295 s; GPU miss 1.337 s;
          GPU cached 1.663 / 1.424 / 1.617 s (GPU tiles 1,427 / 2,072 / 1,222)
  0xFFFF: CPU 1.221 / 1.231 / 1.236 s (step 27 same session 1.285 / 1.222 s)
          GPU cached 1.582 / 1.550 / 1.416, 1.635 / 1.441 / 1.470 / 1.445 s
          (GPU tiles vary 593-2,074; timestamps up to ~470 ms)
=> GPU route now slower than the CPU route measured alongside it. Step 27 GPU control
   was not clean (both exes load the same precompiled .cso files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 17:07:40 +02:00
Fr0zkaandClaude Opus 5.5 7d1808fdf2 wip(standalone): GPU step 27 -- slot-overflow fallback, -SkipCpuDetailBand (slower), -AffinityMask; full machine cached 0.93 s
Luna: normal completion path reads the raw vertex count before residency; an
over-cap count or failed readback falls back to CPU meshing. Max seen 18,153 of
19,456 vertices at (2,-9,-4,lod1) -- 1,303 headroom for this seed only.
Consumer map: 320 GPU near, 640 CPU near (skirted), 1,114 GPU far, 152 CPU far.
Exceptions are exported separately from roughness. Opt-in -SkipCpuDetailBand skips
CPU roughness for GPU-owned near visuals (lazy exact reconstruction on query).
Runner gains -AffinityMask (default 0xFF00) and -NormalPriority.

My runs outside the sandbox (Step27.exe, marker removed first):
  half machine (0xFF00, BelowNormal), interleaved:
    CPU 1.374 / 1.285 / 1.296 s
    GPU miss 1.330 s; cached 1.151 / 1.143 / 1.129 s (1,434 GPU tiles)
    GPU cached + -SkipCpuDetailBand 1.316 / 1.303 / 1.307 s -- SLOWER: detail apply
      331 -> 297 ms, but intervals+signs 112 -> 286 ms (the plan), shape 557 -> 692 ms
  full machine (0xFFFF, Normal), interleaved:
    CPU 1.004 / 1.015 / 0.974 s
    GPU cached 0.931 / 0.930 / 0.925 s -- first sub-second launches
Audits (Luna): hashes w1/w8, walk 5,642/5,642, near (skip) / far 0 sign/topology
diffs vs CPU and WARP, auto + cold survival pass, ten captures pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 04:11:23 +02:00
Fr0zkaandClaude Opus 5.5 7d92e148a9 wip(standalone): GPU step 26 -- first launch fixed again; device-removed was our readback overrun
Luna: on a marker miss the deferred warmup no longer gates the initial view
(starts after the full CPU view is presented). The forced-cold DXGI 0x887A0005 was
ours: GetDeviceRemovedReason = DEVICE_HUNG after the audit readback of tile
(-7,-7,0,lod8), slot 1433 -- the readback copied the theoretical 61,440-vertex maximum
from a 19,456-vertex pooled slot, 41,984 vertices past the end of the last chunk's
buffer. Readback now clamps to slot capacity (audit-only path; normal launches never
read back). Auto and forced-cold survival both pass (1,434 submissions, ~44.6 s, <8 GB).
Added a no-write option for the warmup marker. Direction answer in LUNA_DIALOGUE.md.

My runs outside the sandbox (Step26.exe, marker removed first, interleaved, -Timeline):
  CPU 1.299 / 1.291 / 1.266 s
  GPU marker miss 1.319 s (all-CPU, = CPU again; step 25 was 2.015 s)
  GPU cached 1.151 / 1.122 / 1.138 s, all 1,434 GPU tiles (timestamps 243 / 307 / 241 ms,
      max dispatch 6.3 / 66.7 / 5.2 ms)
Audits: hashes w1/w8, walk 5,642/5,642, near/far 0 sign/topology diffs vs CPU and
WARP on protected/thin, ten captures pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 03:22:38 +02:00
Fr0zkaandClaude Opus 5.5 20acf57678 wip(standalone): GPU step 25 -- shape profile, roughness endpoint reuse, export before CPU frame; first launch REGRESSED
Luna: shape_compile profiled (8 workers, detail band ~323 ms of ~545 ms over 9.39M
candidates; roughness dominates). Exact vertical roughness endpoint reuse in
ShapeWorldInternal.h, hashes unchanged: ~548-577 -> ~535-548 ms. Overlapping
pipeline/pool with shape or streaming made things slower, so it stays serial (pool
precompile now 4-20 ms). Export starts before the first CPU frame. Marker-miss stall
reproduced and gated differently; the forced-cold survival audit now hits
DXGI 0x887A0005 (device removed) on a timestamp query and is NOT passing. Auto
survival passes (6.16 GB). The vertex count at InitialClipmapReady counts CPU
batches only (GPU-resident tiles excluded) -- confirmed counting, not missing geometry.

My runs outside the sandbox (Step25.exe, marker removed first, interleaved, -Timeline):
  CPU 1.329 / 1.295 / 1.318 s
  GPU cached 1.152 / 1.170 / 1.137 s, all 1,434 GPU tiles (timestamps 292 / 260 / 356 ms,
      max dispatch 19.0 / 15.9 / 75.2 ms)
  GPU marker miss 2.015 s -- REGRESSION vs step 24's 1.336 s. CPU had all 2,226 tiles by
      ~1.3 s, but near_all_tiles_ready waited for the post-view shader warmup
      (queue 1,025 ms, finished 1,955 ms; handoff cpu_started=1434 takeover=0).
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, ten captures 2+2 edge pixels as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 02:55:32 +02:00
Fr0zkaandClaude Opus 5.5 94d50edb47 wip(standalone): GPU step 24 -- warmup policy off the critical path; first launch = CPU
Luna: warmup policy (marker read, hashes, adapter query) now resolves on a
background thread; startup fflush(stdout) removed (a redirected flush measured
136 ms). Device-init -> generation-setup gap 0.056 ms on the miss route.
Deferred-batch replay runner reproduces 85-86% of the viewer's GPU time
(240 vs 279 ms); the old "10x" compared one LOD2 tile to a mixed LOD0-8 mean.
Remaining sequence-dependent spikes (11-29 ms on tiles that take ~0.1 ms alone)
are unexplained. Open: a marker-miss survival audit once stalled after
[CpuReadyFrameBeforeGpu].

My runs outside the sandbox (Step24.exe, marker removed first, interleaved, -Timeline):
  CPU 1.350 / 1.281 / 1.266 s
  GPU marker miss 1.336 s (all-CPU fallback, generation setup at 88 ms)
  GPU cached 1.167 / 1.166 / 1.168 s (GPU timestamps 220 / 255 / 205 ms,
      max dispatch 5.6 / 22.0 / 1.0 ms; 1,134-1,236 GPU tiles)
  Steadier than step 23 (1.07-1.17) but not faster; <=1.0 s not reached.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival 6.19 GB, ten captures 2+2 edge pixels as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 02:06:49 +02:00
Fr0zkaandClaude Opus 5.5 bb082f3ae9 wip(standalone): GPU step 23 -- skip empty connector-ring dispatch; cached 1.07-1.17 s
Connector-ring kernel skipped when a tile has no rings, no connector vertices and no
visible skirts (skirted tiles keep it: an earlier variant lost remote coverage, caught
by the captures). Outlier controls: the step 22 67 ms tile runs in ~23 us isolated,
also after a pool touch; no-present control did not remove outliers. Cause unknown.
My runs outside the sandbox (Step23.exe, marker removed first, interleaved, -Timeline):
  CPU 1.360 / 1.294 / 1.287 s
  GPU cached 1.168 / 1.072 / 1.151 s (GPU timestamps 295 / 245 / 264 ms,
      max dispatch 52.7 / 9.2 / 35.0 ms)
  GPU marker miss 1.449 s. Measured: on the miss route, device-init end to
      generation-setup begin takes ~132-155 ms (s22 a/e, s23 a) vs ~3.5 ms on the
      match route; s22 f (miss) was 3.5 ms. That gap explains the slow first launches.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival ~6.14 GB, ten captures as step 22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 00:32:13 +02:00
Fr0zkaandClaude Opus 5.5 052b6de354 wip(standalone): GPU step 22 -- startup multi-batch pump, pool before handoff: cached 1.12-1.20 s
Per-batch GPU timeline (submit, timestamps, query, consume). Startup Draws submit
several ready batches per pump (80 ms startup budget, 16 ms per batch); the output
pool is reserved before the warm-up-pending deferral.
My runs outside the sandbox (Step22.exe, marker removed first, interleaved):
  CPU 1.295 / 1.314 / 1.284 / 1.342 s
  GPU cached (marker match, -Timeline): 1.197 / 1.121 / 1.133 s
      (1,124 / 1,380 / 1,415 GPU tiles; output pool at 655-680 ms in all three)
  GPU marker miss: 1.446 (-Timeline), 1.447, 1.312 s (no -Timeline). The 1.447 run's
      shape compile ended at 776 ms vs 634-643 ms in its neighbours, so this is
      startup variance, not a clear regression; to be watched.
Known tradeoff: startup pumps can stall the main thread 65-108 ms (submit calls run
past the budget); fine before the first view, to check in an interactive viewer.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival ~6.2 GiB, ten captures as step 18.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 00:00:06 +02:00
Fr0zkaandClaude Opus 5.5 38f6009200 wip(standalone): GPU step 21 -- GPU input export shares the 8 workers (3:1 priority); no wall gain
Export tasks interleave with CPU tile jobs on the existing pool. Slow-dispatch keys
are logged (>5 ms, with -Timeline).
My runs outside the sandbox (Step21.exe, marker removed first, interleaved, GPU with
-Timeline):
  CPU 1.356 / 1.321 / 1.277 s
  GPU 1.453 (marker miss, 0 GPU tiles) / 1.249 / 1.216 / 1.217 s (match)
Export now starts at 686 ms in run c (was ~850), but the whole view is unchanged
(1.216 s): from first input (721 ms) to ready (1,216 ms) the GPU path spends ~500 ms
wall for ~255 ms of GPU timestamps. In runs b/d the output pool itself began late
(~860-875 ms). Slow tiles: (0,-6,-5,lod1) 28.5 ms, (-4,-15,-3,lod0) 11.3 ms,
(-3,-13,-2,lod0) 8.2 ms, all ordinary near-detail tiles.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival 6.13 GiB, ten captures as step 18.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 23:20:42 +02:00
Fr0zkaandClaude Opus 5.5 2cb3344e1b wip(standalone): GPU step 20 -- detail band batched by 8 columns; gap instrumented
ApplyDetailBand claims eight adjacent columns per batch, keeping each column's scalar
order: shape stage ~586 -> ~566 ms in Luna's profile. Hashes w1/w8 identical.
My runs outside the sandbox (Step20.exe, marker removed first, interleaved):
  CPU 1.319 / 1.287 / 1.288 s
  GPU 1.320 (marker miss, 0 GPU tiles) / 1.244 / 1.215 / 1.219 s (match, ~1,400 GPU)
Shape compile 549-570 ms in both modes. The 669-821 ms gap in cached runs is still
there: export starts only after [ChunkCrossingWork] reports the ~840 CPU tiles
(LOD0-2) finished, so GPU export waits behind CPU meshing on the shared workers.
Slowest GPU tile 1.7 / 18.4 / 27.3 ms in runs b/c/d.
Audits: hashes, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and WARP,
thin 353, survival 6.14 GB, ten captures as step 18. Far vertex-distance diagnostic:
247 bound failures (max 64.56 cm), unchanged since step 16.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 22:35:22 +02:00
Fr0zkaandClaude Opus 5.5 89fd9a0021 wip(standalone): GPU step 19 -- streamed GPU input export; RUNNERS.md
GPU tile inputs are published as workers finish (LOD then camera distance); first
batch at 32 ready, later batches up to 256. Export wall 224 -> ~105 ms. A camera-near
partial shape compile was tried and removed (duplicated work, missing inputs).
My runs outside the sandbox (Step19Stream.exe, marker removed first, interleaved):
  CPU 1.380 / 1.332 / 1.302 s
  GPU 1.358 (marker miss: 0 GPU tiles) / 1.409 / 1.263 / 1.246 s (marker match,
      ~1,400 GPU tiles, warm-up queue 5-6 ms)
Cached timeline (run d): shape compile 70-652 ms, idle gap 698-850 ms, export
850-954 ms, GPU tail to 1,246 ms (235 ms GPU timestamps). Run b: one 15 ms tile,
ready 1.409 s. Target <= 1.0 s not met.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival pass, ten captures as step 18, peak 6.17 GB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 22:09:29 +02:00
Fr0zkaandClaude Opus 5.5 2fea2710f7 wip(standalone): GPU step 18 -- warm-up marker: hybrid now wins cached and ties first launch
App-owned marker (bin/VoxelForgeGpuWarmup.state: adapter, driver, shader sources and
binaries). Miss -> CPU builds the first view, shader warm-up runs after it. Match ->
warm-up overlaps startup, at Normal thread priority (was Idle).
My runs outside the sandbox (Step18.exe, marker removed first, interleaved):
  CPU 1.395 / 1.363 / 1.329 s
  GPU 1.409 (marker miss, cold driver: 0 GPU tiles, warm-up 683 ms after present)
      / 1.290 / 1.278 / 1.289 s (marker match: warm-up queue 4.6 / 3.6 / 191 ms,
      GPU took 1,394 / 1,410 / 1,115 tiles, CPU mesh sum 5.1 -> 1.3-2.0 s,
      GPU busy 206-227 ms)
Cached target was <= 1.26 s: not met. With the GPU taking almost every tile, the
wall barely moves, so the rest of the 1.28 s is outside meshing. Known risk: if the
marker matches but the driver cache was evicted, the launch pays ~2.2 s (Luna's
sandbox runs).
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 tiles 0 diffs vs CPU
and WARP, thin 353 cells, survival 6.14 GB, ten captures (2+2 edge pixels as step 17).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 21:07:05 +02:00
Fr0zkaandClaude Opus 5.5 6f2d33e0a9 wip(standalone): GPU step 17 -- far skirt-depth fix (LOD3/4 back on GPU); adaptive start trades cached speed for first-launch
Coverage: the broad missing patch on far tiles was GPU skirt depth using the LOD key
step instead of the exported coarse mesher step (800 vs 1,600 cm at (6,2,0,lod3)).
Fixed; tile pixel-identical to CPU, 680/680 + 64/64 triangles, 1,434 GPU-eligible
tiles (was 1,082). Audits: hashes w1/w8, walk 5,642/5,642, thin/protected 0 diffs
vs CPU and WARP, survival 6.15 GB.

Adaptive start: CPU owns the first view while the shader queue is pending.
My runs outside the sandbox (Step17.exe, interleaved):
  CPU 1.375 / 1.363 / 1.354 s
  GPU 2.040 (first launch of build; 0 GPU tiles, CPU worker sum 6.98 vs 5.05 s)
      / 1.279 / 1.337 / 1.328 s
Cached warm-up queue now 329-632 ms (step 16: ~3 ms), so GPU took 258 tiles
once and 0 twice: cached GPU ~= CPU, a regression from step 16's 1.26 s.
First launch improved 3.09 -> 2.04 s but still loses to CPU. Also adds
STEP16_REPORT.md, missed in 666dfb8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 20:36:40 +02:00
Fr0zkaandClaude Opus 5.5 666dfb811c wip(standalone): GPU step 16 -- CPU cold drain; hybrid beats CPU once shaders are driver-cached
CPU uploads now bypass the 4-batch / 2 ms cap until the first resident view.
Luna's race (inside the Codex sandbox): CPU 1.354 s vs GPU 3.152 s, warm-up
queue 1.16-1.21 s on every launch. My runs outside the sandbox, same binary
(Step16FairRace, RunGpuFarViewer, interleaved): CPU 1.497 / 1.359 / 1.334 s;
GPU 3.088 (first launch of this build, warm-up 1,287 ms) / 1.262 / 1.260 s
(warm-up 3.2 / 3.1 ms). AMD DxCache was written at my GPU launches (08:46:57,
09:33:37) and never during Luna's runs: the sandbox cannot persist the driver
shader cache, which is why every one of Luna's launches paid the compile.
Audits: hashes, walk 5,642/5,642, 0 diffs vs CPU/WARP, survival 5.10 GB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 09:34:22 +02:00
Fr0zkaandClaude Opus 5.5 c152708a67 wip(standalone): GPU step 15 -- cold-start drain: hybrid whole view 3.2 -> 1.25-1.68 s
Timeline showed the post-export window was frame-paced: the 4-batch / 2 ms
per-frame upload budget held CPU-fallback tiles up to 755 ms after they
finished, and 35 GPU batches drained one per frame. Before the first whole
view, GPU work now drains as one batch and CPU fallback starts with GPU input
preparation (--gpu-far-no-cold-drain = paced control).

Luna's race: CPU 1.393 s vs GPU 1.677 s. My interleaved runs (Step15Timeline,
RunGpuFarViewer): GPU 2.902 (first launch) / 1.252 / 1.209 s; CPU 2.196 /
2.250 / 2.061 s. The CPU viewer still uses the paced upload budget at cold
start (stream 674 -> 2250 ms in my runs vs 741 -> 1422 in Luna's), so the
race is not yet fair. Audits: hashes, walk 5,642/5,642, 0 diffs vs CPU/WARP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 08:47:36 +02:00
Fr0zkaandClaude Opus 5.5 f322b1c089 wip(standalone): GPU step 14 -- warm-up 2.04 -> 1.05 s; cold path now waits on streaming, not shaders
Per-kernel fences in the production viewer put 1.73-1.98 s on the near-detail
Flush (shader creation 17-19 ms, dispatch calls ~0): skipping only that
dispatch cut idle warm-up 1.893 -> 0.521 s. Near detail rewritten with a
shared owner-roughness loop and shared Perlin samples: DXBC 448,588 -> 120,860
bytes, cold first use 1,596 -> 709 ms. Warm-up median 1.052 s, yet whole view
is unchanged (CPU 1.352 s vs hybrid 3.196 s): after input export the hybrid
spends 1.78-2.00 s streaming 1,082 GPU + 1,144 CPU-fallback tiles.
Audits: 0 diffs near/far vs CPU and WARP, hashes, walk 5,642/5,642, survival.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 08:03:00 +02:00
Fr0zkaandClaude Opus 5.5 a96fd0b95b wip(standalone): GPU step 13 -- near base/skirts/connectors cheaper to compile; warm-up still ~2.0 s
Cold first use: near base 4.2 ms, near detail 1,595 ms (the floor), connector
rings 630 -> 409, skirt classify+emit ~1,409 -> 366. Production warm-up is
still ~2.03-2.06 s; whole view median CPU 1.356 s vs hybrid 3.044 s.
Near 960 + far 1,114 tiles: 0 topology/protected/thin diffs vs CPU and WARP;
hashes, walk 5,642/5,642, survival pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 07:41:31 +02:00
Fr0zkaandClaude Opus 5.5 05b1523c54 wip(standalone): GPU step 12 -- far evaluator cold use 1704 -> 3 ms; near detail now the floor
Split ordinary far evaluation from near-detail evaluation; [loop] in connector
refinement and skirt clipping. Cold first use: EvaluateFarField 1704 -> 3.3 ms,
connector rings 775 -> 630, skirt emit 948 -> 909, near detail 1783 ms (the
floor). Warm-up still ~2.2 s; whole view CPU 1.351 s vs hybrid 3.210 s.
Output-slot reuse fixed: crossing probe 1/1, 2,226/2,226 tiles. Audits: 0
topology/protected/thin diffs vs CPU and WARP, hashes + walk unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 06:51:53 +02:00
Fr0zkaandClaude Opus 5.5 7f64e2e0aa wip(standalone): add missing GPU skirt shader binaries and step 6-8 scripts/report
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 06:14:04 +02:00
Fr0zkaandClaude Opus 5.5 b6ac950073 wip(standalone): GPU step 11 -- per-shader cold cost measured; far EmitCS split, still CPU wins
Cold first-use per shader (queue ms): EvaluateFarField 1757, EmitNear 2429,
FarConnectorRings 784, FarSkirts 507+975; the far EmitCS dropped 2327 -> 3.2
by splitting out the near midpoint fallback into EmitNearCS. Warm-up under
0xFF00 ~2.0 s (worker count and thread priority do not change it; 0xFFFF run:
1.71 s). Whole view median CPU 1.428 s vs hybrid 3.154 s. Audits unchanged.
AMD DxCache was last written 2026-10-06 23:27 -- shaders may no longer be
cached across launches (unverified).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 06:13:59 +02:00
Fr0zkaandClaude Opus 5.5 10580650cb wip(standalone): GPU step 10 -- first-submit stall bisected to cold shader use under 8 workers
Top-down A/B/A controls: dispatching the 11 real compute shaders once removes
the 1.3-1.7 s first GPU Present (resource creation alone does not); 1 worker
also removes it and 8 workers bring it back. Default --gpu-far now prewarms
shaders asynchronously during shape compile (--gpu-far-no-prewarm = control).
The warm-up queue itself takes ~2.75 s, so whole view is unchanged:
CPU 1.354 s vs GPU 3.559 s (viewer clock), 1.473 vs 3.731 s (host clock).
Audits: 0 sign/topology diffs vs CPU/WARP, hashes + walk 5,642/5,642, survival.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 05:38:14 +02:00
Fr0zkaandClaude Opus 5.5 b0da995978 wip(standalone): GPU step 9 -- remote band closed by CPU fallback; CPU still wins whole view
Re-verified f2de8fe (hashes, walk 5,642/5,642, far topology 0 diffs vs CPU and
WARP, GPU survival). The remote LOD3/4 band was a direct-GPU coverage loss
(overlapping pixels identical, surface pieces missing, e.g. (6,2,0,lod3));
LOD3/4 now mesh on CPU, root cause in the GPU route still unidentified.
A minimal first-submit repro (pools, first-touch dispatch, device flags) did
not reproduce the 2.4 s stall. Whole view: CPU 1.438 s vs GPU 4.704 s median.
Crossing probe overflowed GPU output slots: no valid crossing latency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 04:36:34 +02:00
Fr0zkaandClaude Opus 5.5 f2de8fe9c6 wip(standalone): GPU step 8 partial -- GPU skirts, first-present hold; remote gap open
Luna's session ended on an API "Bad Request" before a final report; state is
unverified by me. In the log: GPU clipped-skirt emitter (count/scan/emit,
protected signs and critical air honoured), exposed-skirt audit passing at
the end; first-present stall reproduced on every launch (2.36-2.59 s, not
exe identity/idle/CPU run) and hidden by holding the CPU-ready frame until
GPU residency. Remote LOD3-4 band still differs from CPU; draw ranges and the
one inspected LOD3 tile match, cause not yet found (one-tile probe mid-fix).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-07 02:19:38 +02:00
Fr0zkaandClaude Opus 5.5 90421d0655 wip(standalone): GPU step 7 -- viewer cold start 2.45 -> 1.49 s on CPU; GPU skirts missing
Resumed round: checkpoint rebuilt and re-verified (parity 3,436/3,436, walk
5,642/5,642, W1/W8 hashes 44/44, far topology 0 diffs vs CPU and WARP, far
survival PASS). Remote GPU holes were missing clipped LOD skirts: tiles with
visible skirt faces now fall back to the CPU mesh path, leaving only 16 far
tiles GPU-rendered until a GPU skirt emitter exists.

My interleaved runs (SkirtSeamVerify, process start -> whole view):
CPU 1.522/1.488/1.430 s; GPU 4.271/1.749/1.659 s. A ~2.8 s first Present
stall hit only my first GPU launch; Luna saw it on every fresh launch, so the
trigger is still unidentified. Headless CPU clipmap 0.789 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 22:39:40 +02:00
Fr0zkaandClaude Opus 5.5 41716f00b8 wip(standalone): GPU step 7 partial -- parity check deferred off cold path (stopped early)
Luna measured the startup density-parity check at 1,049 ms, nearly all of the
~1.13 s before shape compile; it now runs after first display and still passes
3,436/3,436. CPU hashes 44/44 at 1 and 8 workers, walkability 5,642/5,642,
headless 8-worker clipmap 0.769 s. Stopped by the owner mid-round, while
auditing the remote far holes; first-launch stall and hybrid speed untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 20:17:37 +02:00
Fr0zkaandClaude Opus 5.5 b0a14262b1 wip(standalone): GPU step 6 -- near field on GPU, exact; cold start still slower than CPU
Near LOD0/1 (detail band, relief, root refinement, normals) now evaluated and
meshed on the GPU. 960 near tiles: zero sign/topology differences vs CPU and
WARP, vertex max 0.0049 mm (was 15 cm); far survival audit passes; CPU hashes
44/44 unchanged. Far connector-panel outlier (64.56 cm, LOD8) remains a
tessellation difference, and remote captures show missing far geometry on GPU.

Cold readiness (viewer clock, my interleaved runs): CPU 2.51/2.49/2.40 s,
GPU 5.05/2.68/2.80 s. The ~2.1 s Present stall hit only the first GPU launch.
Viewer spends ~1.13 s before shape compile starts (startup checks), which is
the gap between the 0.95 s benchmark and ~2.4 s on-screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 20:03:10 +02:00
Fr0zkaandClaude Opus 5.5 0e730cdbba wip(standalone): GPU step 5 -- far GPU work 646 -> 112 ms; hybrid whole view equal to CPU
Whole far field on the RX 7900 XTX: 112 ms total (connector rings 61 ms). Cold hybrid 2.303 s vs
all-CPU 2.314 s (within noise): the far field is no longer on the wall-clock critical path.
0 sign/topology differences vs CPU and WARP incl. 127,401 protected and 353 thin-feature cells;
repeat GPU runs identical; far survival passes on GPU meshes; CPU hashes unchanged.
Open: CPU/GPU vertex distance mean 1.15 mm but max 64.56 cm, 247 tiles over the quantisation bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 13:50:07 +02:00
Fr0zkaandClaude Opus 5.5 247682a8e0 wip(standalone): GPU step 4 -- batched far generation, connector panels on GPU; far survival passes
Up to 32 tiles per deferred command list, one polled event per batch, no per-tile waits; CPU
upload/enqueue 723 -> 46 ms. GPU far meshes pass far survival (7,200 strata boundaries, 10,976
connectors, 0 missing/holes). But hybrid cold 4.36 s vs CPU 2.29 s: whole-tile GPU timestamps sum to
646 ms (0.58 ms/tile; step 3's 0.03 ms covered part of the work only), divergent per-thread shape
loops; CPU far-input export 1.6 worker-s. CPU 44/44 hashes unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 11:01:03 +02:00
Fr0zkaandClaude Opus 5.5 782f195bdb wip(standalone): GPU step 3 -- GPU decides far rock/air, sparse CPU exceptions, hybrid viewer (H)
1,114 of 1,266 far tiles meshed on the GPU; vs CPU and vs WARP: 0 sign/topology differences,
including the protected/thin exception audit; max symmetric vertex distance 0.125 cm; repeat RX runs
identical; GPU field+mesh 0.029 ms/tile average. Strata survival 7,200/7,200, but connector survival
fails (GPU lacks the connector repair panels). Hybrid cold clipmap 5.74 s vs CPU 2.23 s: dispatch and
readback overhead, not compute (~33 ms of GPU work in total).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 09:14:22 +02:00
Fr0zkaandClaude Opus 5.5 b61ccff549 wip(standalone): GPU field step 2 -- far-field shapes/strata/relief in shared C++/HLSL, GPU field+mesh
Seven LOD2-8 tiles: RX 7900 XTX field+meshing 0.335 ms vs CPU generateTile 15.81 ms; repeat runs
bit-identical; CPU W1/W8 44/44 hashes unchanged. Not yet a real gain: rock/air signs still come from
CPU masks for every sample. Viewer does not draw GPU tiles yet; no whole-view timing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 08:02:02 +02:00
Fr0zkaandClaude Opus 5.5 d4b4377d30 research(standalone): GPU first port -- marching cubes in D3D11 compute, exact vs WARP; field still CPU
- Compute shaders classify cells from CPU sign bits, scan output offsets, emit vertex/index buffers
  and indirect-draw args. Two 32^3 tiles: 0 topology differences hardware vs WARP; two runs on the
  RX 7900 XTX identical; max CPU/GPU vertex distance 0.000122 cm (LOD0), 0.000244 cm (LOD1).
- GPU kernels 0.049 / 0.071 ms vs CPU tile 2.33 / 1.99 ms, but CPU input preparation dominates
  (7.25 / 2.84 ms wall): no whole-view gain until the field itself runs on the GPU.
- VoxelForgeFieldShared.h: first shared C++/HLSL field math (integer noise hash, ...).
CPU path unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 06:44:43 +02:00
Fr0zkaandClaude Opus 5.5 566d1f342a standalone: rock detail restored in lattice-resolvable bands (4-16 voxels), owner-band gate added
- Relief per wavelength band compared with the owner field; the 4-8 and 8-16 voxel bands were lost.
  Restored within the partition-of-unity blend; max detail gradient 0.545 (limit 0.55).
- New owner-band gate: fails all six views on de14f00, passes all six now.
- W1/W8 44/44; path hash both directions; routes 5642/5642; stair-distance, ring, thin-sheet,
  coarse-panel, far-survival audits pass; bad-shell control fails.
- Cold 1.127 s vs 1.108 s (within spread); nearest-tile readiness 12.20 vs 12.16 ms.
Caveats: the 143-ray GridStair normal audit is at 62.2% vs its 60% limit (was 76.9%); in the tunnel
capture the floor shows a regular dimple pattern, and the passage shaft close-up is still smooth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 05:43:31 +02:00
Fr0zkaandClaude Opus 5.5 de14f0042d standalone: owner LOD0 defects -- relief blended as partition of unity (stairs gone), exposed skirts 0
- Stairs: cave, floor, strata and passage relief were effectively summed in overlaps (owner ray
  ~0.94 voxel of displacement); now partition-of-unity weights: -0.288 voxel there, max detail
  gradient 0.323 voxel/voxel. High-detail patch scan 133 -> 0; exact midpoint edge roots kept
  (removing them: 7,993 bad roots, 46 patches).
- Fins: exposed skirt triangle scan 1,776 -> 0; thin sheets 0; coarse off-surface panels 0.
- Path hash both directions equal; W1/W8 equal; routes 5642/5642; passage detail ratio 0.850;
  far survival passes, bad-shell fails; 30-crossing flight: nearest tile 15.1 ms median, 124 ms max.
Caveats seen in the captures: the owner-ray wall has visibly less relief than before; the fin
before/after captures do not show the fins (evidence is the scan only). Cold StreamFullClipmap
0.758 s vs 0.741 s (+17 ms, Luna's interleaved run). Owner to judge: bin\VoxelForgeLookCheck4.exe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 02:42:56 +02:00
Fr0zkaandClaude Opus 5.5 b3af0e589e wip(standalone): owner LOD0 defects -- skirt curtains clipped, exact edge roots; interrupted (OpenAI capacity)
Not verified as a whole. In the tree when Luna's session ended ("model at capacity"):
- fins: LOD seam skirts could hang into tunnel air; skirt triangles are now clipped against the solid
  half-space and carry a per-face mask so a host can hide skirts between same-LOD neighbours.
- choppy LOD0 stairs: marching cubes' linear edge interpolation misplaced vertices where the relief
  bends within one cell; edge crossings corrected with the exact field (82 patches, midpoint-edge
  errors 18,818 -> 3,288); normal halo now ignores the +-4.125 fallback samples.
- an explicit unclamped Core distance query for audits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-06 00:17:14 +02:00
Fr0zkaandClaude Opus 5.5 08ceed200d standalone: cheap chunk crossings -- exact-tile slab compile, nearest tiles first (~1.2 s -> 121 ms peak)
- Core compiles exact-tile analytic slabs for entering tiles; a Z-column sampler reuses the coherent
  noise context along each tile column; the viewer keeps residents and prioritises nearby tiles.
- 30-crossing straight flight: nearest tile ready 15.8 ms median / 27.6 ms max; full crossing wall
  121 ms peak; worker CPU per crossing 545 ms median, 834 ms max.
- W1/W8 hashes equal; routes 5642/5642; look, passage, stair, thin-sheet, coarse-panel and far
  audits pass; bad-shell control fails; path-hash audit kept.
Cold whole view, measured by me interleaved (busy machine): 1.242 s vs 1.255 s for 3b37d57, parity.
Still open from the owner (LOD0): fins along a strata seam, choppy stair facets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 20:12:51 +02:00
Fr0zkaandClaude Opus 5.5 3b37d5702e standalone: owner review fixes -- paged streaming in Core, no LOD fins, LOD0 stair patches fixed
From the owner's fly-through ("very impressive, in some cases slightly better than the original"):
- Streaming: Core compiles world-aligned fine-detail pages for tiles entering the view and releases
  them when they leave; the viewer keeps resident tiles. One chunk crossing: 144 new tiles, 1,822
  kept (was a full rebuild). A tile built as a late page hashes identically (density, mesh, payload,
  topology) to the same tile from an initial build.
- LOD>=1 fins: off-surface planar connector panels are now checked against the source surface and
  projected onto it; coarse audit 0 off-surface panels; far survival passes, bad-shell still fails.
- LOD0 stair patches: 2,630 sign-changing edges had one endpoint at the fixed +-4.125 fallback
  (carve surfaces were excluded from distance storage); distances now completed analytically, 462
  packed signs corrected; the audit now fails on any sign edge missing either endpoint (0 of 952,997).
- W1/W8 hashes equal; routes 5642/5642; look, passage and far audits pass.
Verified by me, interleaved (8 workers, cold): 1.014 s vs 1.008 s for aeeedaa, no regression.
Open: a chunk crossing still costs ~1.2 s of page compilation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 18:17:25 +02:00
Fr0zkaandClaude Opus 5.5 aeeedaa73d standalone: VoxelForgeCore link-safe next to Unreal -- internals in vfcore::detail, symbol check
- The copied generator, mesher and Compat stand-ins compile inside vfcore::detail.
- CheckCoreSymbols.ps1 (run by Build.ps1) fails on any unqualified Unreal-style symbol in the
  library: 16,255 on 557f6aa, 0 now. A UE 5.7 CoreMinimal.h + VoxelForgeCore.h smoke compile passes.
- Output identical: 11 hash rows at W1/W8, routes 5642/5642, look/passage/far audits pass,
  bad-shell control fails, six captures byte-identical.
Cost, measured by me interleaved (8 workers, cold, 4 runs each, first run excluded): 0.963 s vs
0.937 s for 557f6aa, ~27 ms in shape compilation; same math, so codegen/inlining. To be found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 15:20:29 +02:00
Fr0zkaandClaude Opus 5.5 557f6aac30 standalone: VoxelForgeCore library -- the fast generator behind a clean C++20 API, output unchanged
- Standalone/Core: public header VoxelForgeCore.h (no Unreal types), static library target,
  ResolvedWorldSnapshot (from the schema-v1 JSON), World (compile, generateTile, density/sign,
  point/segment solid, classifyBox), host-supplied JobScheduler (the core starts no threads),
  sequenced, idempotent EditTransaction returning affected tiles per LOD.
- The viewer, benchmarks, edits and audits run on Core; StreamingWorld stays host code.
- Same as round 11: 11 hash rows at W1 and W8, routes 5642/5642, look/passage/far audits pass,
  bad-shell control fails, six fixed captures byte-identical; cold W8 1.025 s vs 1.041 s reference.
- SHARED_CPP_LIBRARY_PLAN.md rewritten for a UBT module compiling the same sources via shims.
Open issue before Unreal: Core's implementation still compiles the Compat stand-ins named like
Unreal types (TArray, FVector...); inside a UE binary that is an ODR clash. Must be namespaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 11:39:02 +02:00
Fr0zkaandClaude Opus 5.5 631dd57eb4 standalone: landing plate removed; passage shaft rings reduced with the rock detail kept
- AddLanding no longer adds a shallow positive ellipsoid (floor plate check 71.3% -> 0%).
- Round 10 had removed all relief on passage walls to pass the ring check (smooth tube; rejected).
  Cause found instead: the owner roughness's 8-voxel Z anchors with linear interpolation. Passage-
  controlled walls now use 2-voxel anchors, blended smoothly into the 8-voxel ones.
- Ring gate (lag-20 correlation) 0.376 -> 0.038 AND shaft relief RMS 1.294 (smooth-tube control
  0.276 fails); passage/cave relief ratio 1.152. Faint rings remain deep in the shaft.
- Whole view 0.984 s cold @8w (interleaved, full payload); W1/W8 44/44 hash fields equal; routes
  5642/5642; look and far-survival audits pass; bad-shell control fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 09:56:11 +02:00
Fr0zkaandClaude Opus 5.5 2656d81ac9 standalone: whole view under one second -- 0.955 s cold @8 workers, full payload, world unchanged
- Eight-sample X batching of fine density rows, reuse of solved Z intervals in the candidate mask,
  AVX2 marching-cubes cell classification. LOD0/1 density-grid time -61/-62%; interval/sign stage
  140 -> 97 ms; candidate-axis allocation -18 MiB. Shape stage ~413 ms, detail band ~219 ms.
- W1/W8 hashes equal across 44 fields and equal to round 8; routes 5642/5642; look and far-survival
  audits pass; bad-shell control still fails; six fixed captures pixel-identical.
  Remote-view recapture pending (camera 500,0,0).
Verified by me, interleaved (8 workers, cold, BelowNormal 0xFF00, 2,226 tiles, 110.9 MiB payload):
1.012 / 0.927 / 0.926 s vs round 8 1.100 / 1.099 / 1.088 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 05:15:19 +02:00
Fr0zkaandClaude Opus 5.5 16612acba4 standalone: canonical AVX2 noise + factored lattice hashes -- whole view 1.40 -> 1.09 s, world unchanged
- Octave noise evaluated in AVX2 batches (one canonical path), fBM and ridged share samples, lattice
  hash products factored; the outputs are preserved: W1/W8 and pre-change hashes equal across 44
  fields, six fixed captures pixel-identical, remote views differ by at most one pixel.
- Routes 5642/5642, shape and far-survival audits pass, bad-shell control still fails.
Verified by me, interleaved head to head (8 workers, cold, BelowNormal 0xFF00): 1.092 / 1.098 s
vs 1.408 / 1.396 s for round 7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 02:58:46 +02:00
Fr0zkaandClaude Opus 5.5 d4fa89204d standalone: cheap far-survival repairs -- whole view 1.80 -> 1.40 s, payload 277 -> 111 MiB
- Far repairs are source-aware (only where the sampled mesh lost a shell) and redundant radius probes
  trimmed; ~175k two-triangle repair panels remain (+18 MiB over the no-shell 92.6 MiB).
- Pipelined start builds the shape world once; the 27-tile neighbourhood is ready ~0.73 s from
  process start; pipelined whole view is no slower than direct.
- Far audit passes, bad-shell control still fails (16 missing / 16 holes); look audits pass; routes
  5642/5642; 60 W1/W8 + direct/pipeline hash fields equal; six fixed captures pixel-identical.
Verified by me head to head (8 workers, cold, BelowNormal 0xFF00, quiet machine, two runs each):
1.40 s vs 1.80 s for the previous default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 01:56:41 +02:00
Fr0zkaandClaude Opus 5.5 a40b0454b3 standalone: far-survival audit + LOD8 default with explicit far shells; neighbourhood-first viewer
- FarSurvivalAudit: checks every stratum top/bottom boundary (4x4 columns per far tile) and every
  in-view far connector (sections <=128 voxels, 4 radial rays) against the emitted mesh. Falsified:
  removing one tile's shell triangles -> 16 missing, 16 holes, exit 14. Passes at LOD6/7/8.
- Far connectors follow source radial crossings; strata patches emitted at LOD7/8. Default LOD8.
- Viewer: separate near ShapeWorld for the 27 player tiles, full world built on its own thread;
  27/27 tiles hash-identical to the direct build. W1/W8 60 hash fields equal; routes 5642/5642.
Cost, measured by me head to head (8 workers, cold, BelowNormal 0xFF00): LOD8 without the shells
1.17-1.25 s, LOD8 with the shells 1.80 s; payload 92.6 -> 276.7 MiB. The shells are far too
expensive, and the pipelined benchmark whole view is 2.92 s (shape compiled twice): both to fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 22:51:44 +02:00
Fr0zkaandClaude Opus 5.5 5602a281b9 research(standalone): far-field round -- measured only; LOD8 1.35 s but unproven, LOD6 default 1.74 s
No source change kept. LOD8 changes a remote view (29k of 1.02 M pixels) and no check proves distant
strata survive; LOD7 renders the same remote view as LOD8. Default stays LOD6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 19:26:11 +02:00
Fr0zkaandClaude Opus 5.5 9ffe296cd1 standalone: speed round 3 -- local detail envelope + 8-voxel owner roughness lattice (band ~590 -> ~500 ms)
- Profiled the band: detail noise ~70% of worker time, workers balanced (2.5% spread).
- Local amplitude envelope skips detail on 34% of candidates; owner cave roughness evaluated on
  global 8-voxel Z anchors and interpolated. Detail evaluations 7.07 M -> 3.53 M.
- The field changes slightly (mean channel delta ~1.7-5.4 /255 in the six captures); all look
  audits pass, routes 5642/5642, W1/W8 hashes equal on all nine outputs.
- Whole view 1.953 s @8w full payload: no end-to-end gain shown (stream timing noise ~10%).
- Rejected: detail band radius 5 (fails GridStair).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 18:12:08 +02:00
Fr0zkaandClaude Opus 5.5 a3cb26b5c8 standalone: speed round 2 -- far biome colour grid (payload -365 ms), whole view 1.90 s @8w full payload
- Per-vertex biome lookup was the payload cost (~365 ms); far tiles now take the biome palette from a
  per-tile grid. Same channels (palette, slope, border blend, UV) and ceiling/ground sections.
- Opt-in --max-clip-level=8: 6,498 -> 2,226 tiles, stream 0.876 -> 0.428 s, 1.451 s process wall.
  Kept opt-in: six fixed views cannot prove every distant thin stratum survives.
- W1/W8 hashes equal across 11 outputs; routes 5642/5642; five captures pixel-identical, passage
  382 px differ (max delta 16).
- Rejected with measurements: LOD6 empty-block proof (-21% samples, no wall gain), far supertiles
  (no complete groups exist).
Remaining long pole: shape compile 0.854 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 15:21:19 +02:00
Fr0zkaandClaude Opus 5.5 6ec4a8f856 standalone: speed round closed -- full-payload whole view 2.16 s @8 workers; UV/colour/groups cost 0.62 s
Takeover after the PC reset. Verified at 1 and 8 workers: density, geometry, full payload and
topology hashes equal for all nine outputs; routes 5642/5642; crater transaction 17 ms; player
neighbourhood mesh-ready 23 ms after streaming starts.
- The plugin's materials consume UV, the packed biome/slope/blend colours and the ceiling/ground
  sections, so the viewer-only skips stay off by default (diagnostic only: 1.53 s without them).
- UVs staged as FVector2f (matches the game stream; -20 MB, no speed change). Far-bin reserve 24x.
- LOD6 is the long pole: 32.7 M density samples, 1.83 M vertices, ~1.1 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 13:27:03 +02:00
Fr0zkaandClaude Opus 5.5 ee5b05b357 wip(standalone): speed round -- interrupted by a PC hard reset at 10:16 (Kernel-Power 41)
Verified before the reset (density + full mesh hashes equal at 1/8/16 workers, routes 5642/5642,
look audits green):
- merged field lookups, parallel mask merge, compact inline spatial bins, line-local relief hash
  cache, lattice-specific mesher entry, fused sign+magnitude evaluation, far connectors as one
  capsule each (181,836 -> 16,491 far primitives).
- crater edit (sphere+box+capsule, one transaction across LOD0/1): 15-27 ms; player 3x3x3
  neighbourhood meshes ready ~18 ms after streaming starts.
NOT yet verified (in the tree, unhashed): standalone-only skips of UV/colour arrays and of the
ceiling/ground material grouping (best cold whole view 0.958 s @16 workers) and a far-bin reserve
tweak. Those skips drop data the plugin's materials may need: not a game-path win as-is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 12:18:48 +02:00
Fr0zkaandClaude Opus 5.5 97c0455939 standalone: shape-world look round 2 -- shards gone, calmer layered relief, still 0.72 s / 1.6 s cold
- Removed the thin automatic room-support slabs that left floating caps (in-view detached thin
  sheets 9 -> 0; the audit now also fails on isolated small components).
- Bounded 16-voxel floor/wall blend (18 rejected: it created a sub-voxel sheet), 3-voxel
  strata/cave blend, C1 tunnel radius profile (0 radius/slope steps at joins).
- Surface relief: broad octave with amplitude modulation instead of uniform bumps.
- Cold, no cache: 64 m full detail 0.924 s (8 workers) / 0.715 s (16); whole view 2.016 / 1.605 s.
- 1-worker and 6-worker hashes match; 5,642/5,642 passage samples open.
Not solved: regular bands in the narrow passage shafts, a few slivers on tile boundaries, faint
wall/floor plates; close-view mean surface distance to the owner field got worse (see REPORT.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 04:09:59 +02:00
Fr0zkaandClaude Opus 5.5 7e3f926352 wip(standalone): shape-world join blends (room 12, strata/cave 3) -- source restored to last passing set, not rebuilt
Luna's continuation stopped when Windows could not start new processes (0xC0000142, low memory).
Fixed-view join step: chamber 0.041, tunnel 0.434 (bar 0.450, barely). Routes 5642/5642 open.
Shape stages 0.963 s / 0.870 s. Floor join plates still visible; final checks not run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-04 00:07:00 +02:00
Fr0zkaandClaude Opus 5.5 4f4e67c8ca wip(standalone): shape-world look round, stopped by low memory mid-check
The look round was stopped by the system for low memory (not a code fault) while
checking worker-independent hashes and refreshed owner-distance comparisons.
Saved state, not yet reviewed:
- passage floors carry rock detail (flat-floor check passes)
- tunnel radius step between joined segments 0.0000 voxels across 240 joins (was 2.9156)
- 3-octave surface relief
- cold 64 m full detail 0.982 s (0.703 shapes + 0.279 mesh), whole view 2.490 s, 8 workers
Pending: determinism 1 vs 6 workers, owner-distance numbers, final captures, report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-03 18:01:35 +02:00
Fr0zkaandClaude Opus 5.5 8c068ee2cd research(standalone): shape world -- 64 m full detail in 0.96 s, whole view in 2.27 s, cold; look not there
Owner's rooms, tunnels, passages, landings and strate shells compiled as shapes, solved along
lattice lines with packed signs; rock detail only in a band sized by its supremum; far levels from
the same shapes; multithreaded meshing. 8 workers, BelowNormal, affinity 0xFF00, zero cache hits:
  full detail (960 tiles): 669 ms shapes + 290 ms mesh = 0.959 s   (was 20.3 s)
  full clipmap (6,498 tiles): 870 ms + 1.398 s = 2.268 s           (was 170.6 s)
Checks: 5,642 passage centreline samples open; smoothed and per-voxel-noise variants both fail the
detail audit; same hash across runs and 1 vs 6 workers. V toggles owner field / shape world.
Look: 6-12% of lattice signs changed; captures show flat planes without detail (passage floor, a
wall band), telescoping tunnel segments, blocky stair patches. Not accepted as the owner's world yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-03 11:44:45 +02:00
Fr0zkaandClaude Opus 5.5 5441c7da5c standalone: 64 m full-detail override, detailed round tunnels, optional mesh cache -- generation itself NOT faster
ClipRadius override 3 (64 m at 25 cm). Uncached full detail 20.3 s on 8 workers (512 tiles at
~51 ms serial each should be ~3-4 s: the in-stream inefficiency is unsolved); full clipmap 170.6 s.
The 1 s / 5 s targets were met only from an on-disk mesh cache (.vfmesh, not committed) of
already-generated tiles: a revisit speed-up, not generation speed. Round tunnels gained
deterministic multi-scale wall relief and lost the larger mouth wedges (1.54% of lattice signs
changed, mean 0.20 m). Hashes stable across runs and 1 vs 6 workers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-03 08:59:46 +02:00
Fr0zkaandClaude Opus 5.5 81b7d6d0ff standalone: per-tile speed at plugin level, streaming like the game, softer lighting
Cause found by profiling: room/SDF graph-cache churn (exact XY bounds + sample order rebuilt it
constantly). Region grouping (one LOD3 tile: 1,084 -> 229 builds), taller-tile traversal for LOD6
(36,480 -> 12,160), extent-dependent cache slots, lambdas instead of recursive std::function.
Serial ms/tile, 64 nearest tiles per level (before -> after):
  L0 50.4 -> 50.6, L1 112 -> 110, L2 24 -> 22, L3 223 -> 73, L4 269 -> 134, L5 393 -> 127, L6 283 -> 145.
Hashes of all levels unchanged; parity 3,436/3,436 bit-identical.
Full clipmap, 16 workers BelowNormal: full detail 66.6 s, all 12,054 tiles 175 s. In-stream L0 tail is
high (median 83 ms, mean 198 ms, p95 669 ms) vs 51 ms serial.
Streaming: recentre keeps the intersection, generates only missing tiles nearest/finest first;
one crossing = exactly 441 new tiles in 6.1 s, no stale results.
Lighting: normals already come from the density gradient; the striping was the squared headlamp;
broad linear fill + warmer palette. Daytime CPU rule documented in Standalone/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-03 06:49:41 +02:00
Fr0zkaandClaude Opus 5.5 3f300b912a wip(standalone): paused by the owner -- hashed maps + inline allocator (no speedup yet), streaming in progress
Hashed TMap/TSet and real TInlineAllocator storage did not move level 0 (72.1 -> 74.1 s, within
noise): the slowness is elsewhere. StreamingWorld.h started (first build hit an int64/uint64
mismatch), library plan drafted (SHARED_CPP_LIBRARY_PLAN.md), before-normal captures saved.
Uncompiled/unmeasured parts: streaming. Resume by forking session 01a0fd2e-3c3b-7050-b4e9-628fdcc4d553.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-02 18:01:49 +02:00
Fr0zkaandClaude Opus 5.5 05f62cff36 standalone: the owner's world outside Unreal -- bit-identical density, D3D11 fly-through viewer
Standalone/ (C++20, MSVC /O2 /arch:AVX2 /fp:precise, Build.ps1): copied density pipeline, op stack,
cave morphology, passages, strate manager/composer, noise, DetSinCos, mesher, clipmap selection,
with a UE compatibility layer (Compat/). Owner data exported once to Standalone/data/owner_world.json
by a default-off explore-commandlet exporter (the only plugin-source change). 25 cm voxels.
Parity: seed 0, 3,436 samples (strate edges, negative/far coords, rooms, tunnels, passages,
landings): bit-identical. Viewer: full 7-level clipmap, per-level toggles 1-7, T = round winding
tunnels (standalone option), N/P seed, F wireframe.
Timing NOT yet a baseline: 515 s for 12,054 tiles (L0 70 s, L6 296 s); the compat TMap/TSet are
vector-backed with linear lookup, which the plugin's caches do not expect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-02 12:14:04 +02:00
Fr0zkaandClaude Opus 5.5 a443eb8546 research(intervals): blends, dual contouring, surface relief -- look better, not there; new stages single-threaded
Bounded smooth blends at junctions (3.1% of cells), QEF dual contouring on the exact crossings and
analytic normals, band-limited surface relief (bound 0.60 m, observed 0.40-0.44 m, R toggles it),
sparser strata. Exact signs vs the blended reference at every node, thin features kept, same hash
across worker counts. Mesh has 0 boundary edges but 3-7 overused edge groups: not collision-ready.
Near CPU total, 6 workers: 241 -> 836 ms. The new stages all run on ONE thread: blend index 186 ms,
blend evaluation 201 ms, dual contouring 322 ms, relief 61 ms.
Look: slab stack gone, joins rounder; chambers read as broad crumpled facets, junction seams still
flaky. Viewer built as interval_viewer2.exe (the owner had the first one open).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-02 04:23:38 +02:00
Fr0zkaandClaude Opus 5.5 8d29c09512 research(intervals): OBJ export switch and a D3D11 fly-through viewer for the spike
Viewer generates the world at startup with the spike's code (timing shown), free-fly camera,
wireframe, near/far toggles, seed cycling. Benchmark hashes unchanged (e5ad62303de5f641).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-02 01:27:18 +02:00
Fr0zkaandClaude Opus 5.5 61b1c9c329 research(intervals): one-day spike -- whole near region in 241 ms (6 workers), exact signs, far proxy 8.6 ms
Standalone C++20 (MSVC /O2 /arch:AVX2), no Unreal. A 372-shape corpus (rooms at the hardest
profile's scale, winding tunnels, pillars, stalactites, shelves, floating masses, thin strata,
rotated shapes, crowded junctions, craters), built two ways: point-by-point reference vs column
intervals on 3 axes + 64-bit sign words + bit-scanned active cells + meshing from analytic crossings.
64 m near region at 25 cm (17.4 M nodes), 5 seeds, fresh processes:
  workers  near CPU total (median/p95/worst)  interval stage   mesh stage (single-threaded)
  1        349 / 358 / 358 ms                  154 ms           188 ms
  6        241 / 280 / 288 ms                   48 ms           191 ms
  8        240 / 271 / 277 ms                   43 ms           193 ms
Near mesh ~563k triangles, 13.5 MB; total output 19.9 MB; busiest line 58 candidates (< 64).
Correctness: 0 sign mismatches vs reference at every node, 0 missing crossings/open contours, thin
strata with no lattice node inside get explicit triangles (dropping them is caught), same hash on
1/6/8 workers and a repeated run. Far field: 47k nodes / 92k patches in ~8.6 ms, but fixed-grid
ellipsoid sheets, not a validated cave hierarchy: unproven. No noise detail in this corpus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-02 00:45:59 +02:00
Fr0zkaandClaude Opus 5.5 413983f2d3 docs(research): Astra round 3 -- interval columns, packed sign words, surface-emitting hierarchy, 900 ms budget
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-01 23:40:47 +02:00
Fr0zkaandClaude Opus 5.5 8b87749771 docs(research): Astra round 2 -- shared shoreline curves, flowing stone, material-space deformation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-01 23:15:09 +02:00
Fr0zkaandClaude Opus 5.5 35e3d149d2 docs(research): Astra free-thinking round -- coarse structure + real surface relief, boundary-aware generator
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-01 21:55:23 +02:00
Fr0zkaandClaude Opus 5.5 bc0f9ee791 wip(step3): 12.5 cm voxels, fall-through fixed, brick evaluator in game, organic tunnels -- speed gate FAILS
Stopped by the owner's decision (in-game lit captures were eating the round); he judges the look in
game instead.
1. Fall-through with the research field: fixed. Deep drop, gate on: 2 holds (0.50 s), min foot
   clearance +43 cm; gate off: penetrates (-278 cm). Stress fixture offsets converted to metres.
2. VOXEL_SIZE 25 -> 12.5 cm. Physical sizes kept in metres (player, steps, gate, insets, research
   field sizes, dig radii, test thresholds); class defaults that count chunks/voxels doubled
   (ClipRadius 6, RenderDistanceChunks 1536, ViewDistance*, StrateViewMargin 6,
   PawnCollisionRadius 2, SkirtCells 4, EdgeSealThickness 128, decoration spacings, density volume
   res 256). Asset values override these defaults: the owner must double his own.
   Ring scale, six workers, idle, step-2f field before the look changes: 64 m cube (519^3) 4.55 s;
   32 m cube (259^3) 0.95 s.
3. In game: tiles go through the brick evaluator (proved by a log marker); profile switch rebuilds
   the loaded world (~5,800 tiles); full-detail fill 12.4 s after spawn in the visible-window test.
4. Look: round winding tunnels, per-column ellipsoid/lattice caches. All step-2f geometry gates pass
   at 12.5 cm (0 crumbs, 1 solid component per profile, solid share 69-87%). 259^3 ring, six workers:
   1.08-2.02 s (worst HyperComplex) -- the 600 ms gate FAILS. Lit captures failed (hidden/off-screen
   window, then blank frames); not pursued.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-01 05:50:20 +02:00
Fr0zkaandClaude Opus 5.5 d6555df5fa wip(step2f): coherent field -- flakes, speckle, grid, roof fixed; 3 crumbs + 636 ms left; last edits UNCOMPILED
Stopped by low memory (another app at 14 GB; processes failed with 0xC0000142).
r6 (compiled and measured, owner playing during the run), step 2e -> step 2f r6:
  crumbs < 1 m^3 (max per profile)   2,898 -> 3        FAIL (0,0,0,0,2,1,3,2)
  flakes (shard share / p95 angle)   40.7% / 84.2 deg -> 2.1% / 33.1 deg   pass
  lattice-scale detail energy        0.832 -> 0.016    pass
  grid signature (autocorrelation)   0.666 -> 0.349    pass
  min solid share / roof openings    2.7%, 26,657 -> 79.2%, 0   pass
  worst profile ring time            642.8 -> 636.3 ms FAIL (6 workers, 259^3)
Step 2e measured 642.8 ms here vs 570 ms idle earlier: the game was costing ~12% during this run.
Brick proofs + audit, seams + injected seam, 1 vs 6 worker determinism pass. After r6, feature
probabilities were lowered to remove the last crumbs: NOT compiled, NOT measured. TraversalSeeds,
in-game captures and the LOD-rejection figure for 2e are still to do. Reject screenshots included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 19:50:42 +02:00
Fr0zkaandClaude Opus 5.5 0a7492ffab research(step2e): 8 parameter profiles, worst 570 ms -- speed gate met, look NOT yet accepted
Editor-only, default off. Seeded chamber graph (jittered XY grid, spanning tree, optional loops,
tunnels, shafts, pillars, ledges, spires, floating masses, basins), 8 profiles, four detail bands
at every surface-affecting 25 cm sample, sound brick proofs. 259^3 ring, six workers, medians of 3:
404-570 ms (worst TerracedBasin 570.0 ms). Detail, seam, bound-audit (0 violations, undersized
control caught), determinism (1 vs 6 workers) and TraversalSeeds all pass; live tests untouched.

Not accepted yet: the profiles are 81-97% open air. In the slices a "TightNetwork" is a large void
with scattered solid blobs (rooms of 0.5-0.76 x pitch overlap and merge), and the finest detail
band shows as salt-and-pepper speckle with 22-109 separate 1 m components per ring: fast partly
because air bricks skip, and the look needs the owner's judgement. Also missing vs live: maze,
flat/plain, crystal-chamber, surface-world and underwater grammars; water geometry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 17:47:36 +02:00
Fr0zkaandClaude Opus 5.5 f8a46ecf8d wip(step2e): parameterised chamber-graph model -- compiles, traversal test PASS; benchmark not run
Stopped by low memory (another app held 12.5 GB; processes failed with 0xC0000142).
Editor-only, default off: a seeded connected room graph (varied chambers on a spanning network,
optional loops, shafts, rock features, ledges, basin floors), eight parameter profiles, the
step-2d brick driver and detail bands, a new test VoxelForge.ParameterizedGeneration.TraversalSeeds
(PASS in step2e_functional_01, which also proves the tree compiles). Existing live tests untouched.
Not yet run: the eight-profile ring benchmark, variety metrics, slices, detail/artefact/determinism
checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 11:45:45 +02:00
Fr0zkaandClaude Opus 5.5 78641da0bb research(step2d): a simpler field is fast (81 ns/sample) -- but it is not the owner's world; NOT a pass
Editor-only, default off (voxel.ResearchRedefinedDensityField). The redefined field is a seeded,
periodic cross-corridor pattern per strate plus the authored passages, landings and a shaft tree,
with four value-noise detail bands at every near-surface 25 cm sample and sound 8^3 brick proofs.
259^3 ring, six workers: 308 ms median (467 ms without skips) vs 8.9 s for the live field.
Detail, boundary-artefact, determinism (1 vs 6 workers, two runs) and bound-audit checks pass.

Why it is not a pass: the slices show a mostly-solid field with a few straight corridors, where the
owner's world is mostly open space; rooms, the tunnel network, composer recipes and archetypes are
gone. Much of the speed comes from 60% of bricks being proven solid in that simpler world.
What it does show: detail at full resolution plus brick skipping costs ~80-130 ns/sample; the
cost of the live field is how rooms/tunnels/passages are represented, not the detail or the lattice.

The two walkability tests were redirected to the research field in the working tree; that change
is NOT committed (saved as scratchpad step2d-test-redirect.patch) so they keep testing the live world.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 09:06:25 +02:00
Fr0zkaandClaude Opus 5.5 d6e021a80b docs(research): step 2 verdict -- exact per-sample density cannot reach 600 ms
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 04:18:58 +02:00
Fr0zkaandClaude Opus 5.5 43c11ccaa8 measure(step2c): setup once per chunk, still 1.6 us/sample -- exact per-sample density misses 600 ms
Editor-only, default off. 259^3 ring split into 1,000 aligned 32^3 chunks; cache/index builds
bounded by chunks (948 room/SDF caches, 800 active-op lists, 8,532 index tables); 800 block setups
+ 201,243 scalar fallback points (1.16%); no per-point GetDensityAt on supported routes.
Six workers: live 7.07 s wall / 1.678 us per sample; prepared 7.92 s / 1.595 us.
Candidate visits 21.2 -> 15.8 per sample, evaluations 6.31 -> 5.75; room / tunnel / tunnel-core
evaluations unchanged (48.1 M / 5.0 M / 38.4 M for 17.4 M samples). Passage evals 18.1 M -> 8.4 M.
Exact: 0 Q16/sign disagreements, five seeds, same hash on 1 and 6 workers, reach shrink caught.
Luna's verdict: 600 ms is not reachable while every lattice point evaluates the exact current
density; 27.7 worker-CPU seconds need >= 7.7x less work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 04:18:44 +02:00
Fr0zkaandClaude Opus 5.5 fd9f809c1c measure(step2b): per-brick setup rebuilds the chunk caches -- 10x slower; live cost attributed
Editor-only brick API (<= 8^3 points per call, TunnelNetwork/Underwater op-stack routes, 1.16%
scalar fallback). Exact: 0 Q16/sign disagreements on the full 259^3 ring and the five-seed probe
set, same hash on 1 and 6 workers, reach shrink still caught (18,107 Q16 / 735 sign).
Speed: 69.19 s wall, 17.6 us/sample vs live 7.04 s, 1.65 us/sample. Stack sampler (inclusive):
BuildChunkCache 93.5%, PropagateSdfOverBox 92.5%, BuildActiveOpList 92.6% -- each brick rebuilds
the cache windows and active-op lists; EvalBlock itself is 3.6%.
Live path attribution (inclusive, 68,934 stacks): op-stack RoomGraph EvalBlock 54.4%, SDF and
spatial candidate scans ~28%, tunnel core 18.4%, passage floor segment 15.0%, structural posts
14.6%, noise octaves 2.6%, disturbances 0.6%, diff layer 0.3%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 03:10:22 +02:00
Fr0zkaandClaude Opus 5.5 41a9265ebd measure(step2a): candidate bins alone are slower -- the per-sample GetDensityAt call must go
Editor-only ablation (explore commandlet, -regioneval, default off): 8^3 bins for rooms,
room-floor joins and tunnel chains, but every point still goes through GetDensityAt.
259^3 ring, seed 1337, six workers:
  live  : 6.776 s wall, 29.16 s worker CPU, 1.679 us/sample, 21.2 candidate visits/sample
  binned: 19.54 s wall, 50.02 s worker CPU, 2.879 us/sample, 16.4 candidate visits/sample
Correctness: zero Q16 (x16, ties away from zero) disagreements on the full ring and ~284k probes
on 4 more seeds (composer recipes, warped rooms, passages, landings, strate edges, edits);
shrinking room reach to 0.5 -> 18,107 Q16 / 735 sign differences (the check bites).
Not the planned region program: the step-2 question is still open. Baseline for the next attempt:
1.68 us CPU per sample on the live path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 01:19:11 +02:00
Fr0zkaandClaude Opus 5.5 3b4d6855fe docs(research): record step 1 verdicts in the build plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 00:08:08 +02:00
Fr0zkaandClaude Opus 5.5 41b001b652 measure(step1): cook is 1.5-2.3 ms, RMC's route is 8-11 s; CMC cannot take a field floor; cap 16 uploads
Rebuild plan step 1 (RESEARCH-SPEED-2026-09.md), all behind default-off switches.
A. Direct cook on a dedicated thread, 27-tile support cohort: core cook p50 1.50 ms, p95 2.28 ms;
   a 3,949-tri tile 1.67 ms extract+cook. PASSES the 10 ms gate. Through RMC's route the same
   support bodies became ready 8.46 s p50 / 10.80 s p95 after their meshes existed: FAILS 150 ms.
   Exact-target capsule sweep confirmed (0.75 ms after body update).
B. 100 rapid underfoot digs on CMC with a field override: 883 false-support frames, 0 false-air,
   0 invented velocity. CMC's floor comes from collision-scene traces -> step 4 = field-aware Mover.
C. 341 prequeued tiles, render on: cap 4 = 238 applies/s, cap 8 = 476, cap 16 = 639 (drain p95
   2.22 ms vs 2 ms gate); first visible 1 frame after release; no RT/RHI backlog. Tune drain,
   no grouping needed yet.
D. Same 128^3 region, 64/64 tile hashes equal in export and game. The "263 ns/sample" was export
   WALL time with 15 workers sharing a lattice (~3.4 us CPU/sample); game tile time includes
   meshing and duplicated borders (+22%); contention cap 1 vs 14: 74 vs 112 ms per tile.
   Profiling is not the cause.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 00:07:59 +02:00
Fr0zkaandClaude Opus 5.5 97a1874498 wip(step1): collision/upload clocks behind default-off switches -- partial, stopped on low memory
Compiled and run (cap 14 / 12 / 8), then stopped: another app held 12.5 GB, free memory fell
under the 6 GB launch floor and processes failed to start (STATUS_DLL_INIT_FAILED).

Measured so far (owner world, 120 s windows):
- 343 L0 tiles, request->collision-ready p50/p95: cap 14 = 262/440 ms, cap 8 = 196/238 ms;
  scheduler queue wait p95 141 ms -> 0.9 ms. Initial support tile at cap 14: RMC submit->body
  7.47 s, first check->ready 9.73 s; at cap 12: 0.33 s / 2.54 s; at cap 8 a 3,949-tri tile
  reached body update 2.8 ms after RMC submission. Waiting, not cooking, dominates.
- NOT confirmed: a capsule sweep hitting the exact target body (the 0.75 ms hit was another
  component); the 10 ms core-cook and 150 ms/27-tile thresholds are unscored.
- CMC cannot take a same-frame density floor: FindFloor/ComputeFloorDist trace the collision
  scene. Underfoot digs (100-dig test), upload throughput (render-enabled caps 4/8/16) and the
  8x per-sample question are not run. Per-sample profiling was fully disabled in these runs, so
  it does not explain the 8x.
Switches (all default off): voxel.Step1GenerationCap, Step1PauseGenerationAfterSeconds,
Step1MaxMeshApplies, Step1PrequeueVisibleLOD0, Step1UploadDisableCollision, Step1CollisionProbe,
Step1CoreCookBenchmark; harness -EnableRender. Also: plan step 3b, AVX2 need not match scalar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 17:53:57 +02:00
Fr0zkaandClaude Opus 5.5 6d2a068308 docs(research): step 3b -- AVX2 check against the new evaluator (owner's request)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 16:25:14 +02:00
Fr0zkaandClaude Opus 5.5 153798f436 docs(research): consolidated decision -- float region programs, quantized lattice, gated build plan
Round three (Sol fork): keep precise float procedural math, restructure evaluation (region
programs, feature binning, brick certificates, shared lattice), a versioned 16-bit lattice as the
common mesh/edit/collision cache, integer edit composition on GPU for craters. Seven ordered steps,
each with a numeric pass/fail gate; day one instruments collision and upload clocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 16:21:03 +02:00
Fr0zkaandClaude Opus 5.5 3de7ce1420 docs(research): round two -- wider search, AVX2 integer probe, crater transaction design
Fork of the Sol research session with the owner's answers (player-only 1 s collision; bounded
digging but fast crater spells). AVX2 integer kernel 4.25-5.86x over its scalar reference with
exact parity; radius-10 m crater = 714 bricks, cached-field edit < 1 ms (render not measured).
Cost estimate revised to 6-12 engineer-months; kill test gate <= 0.35 s worker wall on real plans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 15:58:39 +02:00
Fr0zkaandClaude Opus 5.5 1568f76ca2 docs(research): speed research -- sparse compiled bricks, field collision, edit transactions
Sol 6 (high, web search) research round: recommendation, three architectures, determinism
contract, migration with a 5-day standalone kill test. Standalone probe in
Saved/Research/brick_kernel.cpp (not VoxelForge code): 16.8M integer samples in ~0.36-0.48 s,
brick-classified ~0.1 s, 0 sign mismatches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 15:31:58 +02:00
Fr0zkaandClaude Opus 5.5 bf22f0ffe0 perf(collision): cook stages measured, no standing on dug-out ground; review F/H/E measured
1. Collision cook breakdown (request -> ready), critical support tile:
   spawn (0,0,-1): generate 107 ms, apply < 1 ms, RMC submit->body 479 ms, total 587 ms;
   deep floor (0,0,-7): generate 82 ms, RMC 1,441 ms, total 1,548 ms.
   Almost all of the wait is inside RMC (end-of-frame update + async pool + Chaos cook + body
   install, not separable from our side). Critical priority does not shorten it. Collision is
   async, complex-as-simple, fast-cook off; skirts are ~16-18% of collision triangles.
   No cook change made. Startup outlier: first support tile waited 2.09 s for its game-thread apply.
   Underfoot dig: a carving brush that intersects the pawn's capsule and support tile now
   disables the old body and withdraws readiness on the carve frame: 0 frames standing on removed
   ground (replacement 91.8 ms later). 8 neighbouring digs: 0 gate frames. Gap audit 0.
2. Stack sampler, 49.4 s game path: top resolved leaf was the profiler's own
   VoxelDensityProfile::BucketFromName (4.86%). Linear label scan -> CRC-keyed lookup with exact
   compare: 59.26 -> 57.87 ms/tile (-2.35%), 980/980 tile hashes identical.
3. Measured only: F (classifier mode 1, no exact check) +48% worker CPU, 0/980 tiles differ ->
   not worth it. H (handoff component scans) p95 0.07 ms/frame -> not worth it.
   E (FullResClipLevels=1): worker CPU 56.7 -> 35.7 s, settle 9.6 -> 7.0 s, crossing samples
   -30%; changes the look, owner's decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 07:52:18 +02:00
Fr0zkaandClaude Opus 5.5 ce6812dbeb fix(streaming): no false gate on digs, no see-through at tile handoffs; density volume off
- Dig near the pawn: a same-tile collision recook withdrew readiness although the old body was
  still installed, so the gate held a standing pawn (957 of 1,931 frames over 8 near digs).
  A tile now stays ready during a recook while collision is still wanted and its existing body is
  valid. After: 0 gate frames, 0 falling frames over 2,604 frames. Dig directly under the pawn
  still falls (~127 cm, max ~502 cm/s). The reported "accelerating toward the ground" was not
  reproduced; the false hold is the measured part.
- See-through: the retire logic only waited for replacements that overlapped the old tile in
  volume and missed desired neighbours touching at a face/edge/corner. Handoff now includes
  contact. New surface-witness gap audit (24 surface points per tile, skirts excluded, catches an
  injected early hide). 6000 cm/s walk, 75 s: 3 gaps / 51 frames / longest 603 ms -> 0.
  Dig session: 0 -> 0. Overlap audit and edit ghost stay at 0.
- bEnableDensityVolume defaults to false; when off: no volume, no fill thread, uploads or tick
  work, no volume-dependent proof bypass; terrain MIDs get VolP5.z = 0 (enable off), other VolP*
  black, VolTex* null, so M_Test1's volume nodes return their neutral result.

Gate: deep gate-on passes (79.9 m, 3 holds, -0.101 cm), gate-off penetrates (-5.1 cm).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 04:53:57 +02:00
Fr0zkaandClaude Opus 5.5 0bcd67a016 perf(review): gate never deadlocks, collision radius, 20x faster digs
From the outside performance review (items 1-4; item 5 prototyped and reverted).

1. Collision gate: a support request outside the stamped desired set was cancelled by the rebuild
   it forced, and the gate held the pawn with gravity 0. Baseline deep-drop (floor at (0,0,-7),
   outside the aligned L0 ring): 1 hold of 30.0 s, never released. Fix: the target and 3 tiles
   below are retained through rebuilds at CollisionCritical; hold only when the swept capsule is
   about to enter pending support; release restores movement mode, gravity and downward speed.
   After: 3 holds, 2.21 s total, 112.8 m horizontal progress; gate off still penetrates (-7.4 cm).
2. Suspected cleanup regression (out-of-layout stacks): not real. 44 paired boundary tiles,
   30.9 ms vs 32.6 ms at b4ac996, hashes 44/44 identical. No change.
3. PawnCollisionRadiusChunks (new, default 1): L0 tiles outside the collision set stay visible
   without Chaos cooks; anchors and the gate's support column are unioned in. Cook requests
   ~217/min -> ~30/min (-86%). Critical support ready time did NOT improve (9.5 s -> 10.6 s in the
   deep-drop fixture; the wait is the cook itself, ~7.4 s cook-to-ready there).
4. Dig: exact pre-edit L0 density cached (one 35^3 grid, 171.5 KB), carve replays current mods on
   it. Sync remesh 99.4 ms -> 4.7 ms. 16/16 random carves hash-identical to full regeneration.
5. Parallel grid fill for the critical tile: 16/16 hash parity, but the waited-on tile was already
   meshed; the wait was collision cooking. Reverted.

Review G answered: M_Test1 still ray-marches VolTex0 (custom HLSL mini-sun shadow), so the density
volume is live. Known and left alone: outside the strate layout the fallback density can be solid
while the gate treats those tiles as air.
Tests: VoxelForgeCollisionDesiredTilesTest, VoxelForgePreEditDensityCacheTest; no-overlap, edit
ghost, descent corridor pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 02:26:44 +02:00
Fr0zkaandClaude Opus 5.5 116b57f01e wip(gate): support column kept through rebuilds, hold only on swept contact -- NEVER COMPILED OR RUN
Stopped mid-round: the PC ran out of memory (another app held 11 GB), new processes failed to start
(0xC0000142), and the round ended without verifying anything after the edit.

Confirmed in code (performance review finding 1): a support request outside the stamped desired set
forces a rebuild that cancels it, the gate asks for one unresolved tile at a time, and it holds
before the pending tile is anywhere near the pawn's next step.

Uncompiled edits: a short collision-only support column retained through desired rebuilds and
prioritised; hold only when the swept capsule is about to enter a pending tile; downward speed
restored on release; opt-in deep-drop fixture (carved shaft) in the stress harness.

Evidence so far: shallow 10-voxel fall, gate on, before the edit: 1 hold of 1.51 s, clearance
+120.6 cm. The 80/160-voxel attempts spawned OUTSIDE the strate layout (support unavailable,
zero holds) and failed the penetration check at -11.1 cm: not the reviewed loop, but worth a look.
Compile risks: GetGravityZ(), TSet pruning in the rebuild, the fixture's carve/log arguments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-28 19:54:28 +02:00
Fr0zka 0b8fbc5591 Merge pull request #14 from Fr0zka/cleanup/2026-09
Cleanup/2026 09
2026-09-28 18:12:41 +02:00
Claude 593dfac1d8 docs: the operator stack is the only density path
- CODEMAP: data-flow diagram, §3.2b/§3.2d, generator and strate-manager
  rows, test rows and §4.2/§5 now describe the stack-only path; the
  deleted archetype functions, bUseOperatorStack and the Not Op Stack
  stats are gone; line hints re-synced for VoxelGenerator.cpp,
  VoxelStrateManager.cpp and VoxelStrateDefinition.h.
- ARCHITECTURE §8.1: archetype table lists stack builders; §8.10 surface
  column and shaft cache entries name their current owners.
- REVIEW_FINDINGS: tick "Operator stack as the only density path"; add
  the no-strate-manager fallback world as its own open item.
- fable-idea: T1.a status names the current column caches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 16:09:59 +00:00
Claude f83c4bd8c1 refactor(generator): delete the pre-operator-stack archetype functions
With the operator stack as the only density path, the per-archetype
density functions have no caller left in the game:

- Delete UVoxelGenerator::GetSlabDensity, GetMazeDensity,
  GetSurfaceDensity, SurfaceDensityFromColumn, GetVerticalShaftDensity
  and GetFloatingIslandDensity (~1000 lines) with their declarations.
- Drop GetDensityWithParams' OutWarpedPosition out-parameter; only
  the native GetDensityAt branch removed in the previous commit used it.
- OpStack Slab/Maze/VerticalShaft/FloatingIsland and SurfaceHeight
  tests: remove the stack-vs-legacy comparisons; keep decomposition,
  finiteness, liveness, window-invariance, cache-cost and box-verdict
  checks (Shaft cache timing now measures the stack source).
- PassageLandsInOpenSpace: the source-only footing probe evaluates
  the archetype stack built without a strate manager instead of the
  deleted functions.
- Comments that cited the deleted functions now cite the operators
  (FSlabVoidSource, FLatticeCorridorSource, FIslandBlobSource, ...).
  GSurfColCache is documented as ClassifyTile's column cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 16:04:51 +00:00
Claude 8117d118eb refactor(opstack): the operator stack is the only density path
Every strate now generates through its operator stack: authored
definitions, cooked-season entries (recipe or native) and editor
composer candidates alike. The per-strate opt-in and the archetype
switch it guarded are gone.

- Remove UVoxelStrateDefinition::bUseOperatorStack and the season
  wiring that set it; UsesOperatorStackForChunk now only says whether
  the slot has a known archetype.
- GetDensityAt always builds a stack (composer, then recipe, then the
  native archetype stack); a degenerate zero-height strate stays air.
- Remove the startup opt-in warning, the operator_stack startup-trace
  field and the four "Cave Bail Not Op Stack" stats (the remaining
  bail is counted by Cave Bail No Stack).
- Explore commandlet: drop -opstack and the operator_stack fields;
  VoxelForgeTest.ps1 no longer passes -opstack=1.
- Tests: FTestWorld::Build loses its bool parameter; legacy-vs-stack
  comparisons (MazeSeam, VerticalShaftSeam, TerrainDetailLiveness,
  WorldEdgeSeal, TilePostReach) collapse to the single stack path.

This changes terrain for any strate asset that did not already have
the opt-in ticked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 15:56:19 +00:00
Claude 63fb71fe6a docs(review-findings): tick removed settings, add the stack-only decision
Follows the sheet/settings removal commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:54:51 +00:00
Claude 6a2d701eab docs(fable-idea): F18 settings and selector branch are deleted too
Follows the sheet/settings removal commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:54:51 +00:00
Claude 20619e587d docs(architecture): sheet ring and ceiling settings are gone, not just unused
Follows the sheet/settings removal commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:54:51 +00:00
Claude 41fcef692a docs(codemap): no sheet settings or sheet sweep cases left
Follows the sheet/settings removal commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:54:51 +00:00
Claude b6529719dd refactor(sheets, settings): drop the last sheet-ring remnants and dead settings
The far sheet ring was already gone from the runtime; this removes what
was left of it:
- UVoxelSettings::bFarSheetRing / FarSheetSpanLevels, the
  voxel.TestFarSheetRing switch, and their log/trace/dump fields.
- The sheet branch of VoxelClipmapDesiredTiles (FParameters sheet fields,
  OuterShell sheet level, sheet ring emission). Output for the runtime
  configuration (sheet off) is unchanged: same rings, same alignment.
- Tests: VerticalStrateReach now asserts the sheet-free selector (its
  expected counts were stale since the aligned-ring change 85a803b:
  LOD0 343 -> 512); ClipmapNoOverlap drops the sheet sweep cases and the
  frozen 7486c29 oracle's sheet branch. New expected values were computed
  with a Python port of the selector that reproduces the test's existing
  635/594487 and 508/74295 measurements exactly; all 5,145 sweep cases
  pass (no overlap, full coverage).
- Tools/VoxelForgeTest.ps1 no longer passes voxel.TestFarSheetRing or
  reports far_sheet_ring. The sheet_tile dump field is kept (the parity
  compare reads it).

Also removes settings nothing reads: CeilingViewMultiplier,
CeilingBandChunks (pre-clipmap ceiling band) and DensityVolumeMaxTasks
(the volume fill runs on its own thread). Saved assets that still carry
these values load normally; the values are ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:53:25 +00:00
Claude 3bd0fd36df docs(architecture): flag the unread biome WaterMaterial override
The single player-following water plane only uses the strate's
WaterMaterial; UVoxelBiomeDefinition::WaterMaterial is never read. The
design (§8.14 content profile) still lists it, so record the gap as an
open owner decision instead of silently dropping it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:36:37 +00:00
Claude c4397a5fb7 docs(comments): content manager header matches the region-build flow
ProcessDecoResults folds cell results into BuildId-matched region builds
(MergeCellResult) and applies whole regions under the per-frame budget;
there is no epoch guard on decoration results. Comment-only change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:34:31 +00:00
Claude e960b9c7e3 docs(claude): name all four MPSC result queues in the async rule
Workers return tile results through CriticalProcessQueue,
EditedProcessQueue, NearProcessQueue or ProcessQueue (VoxelWorld.h), all
EQueueMode::Mpsc; the rule named only ProcessQueue. Rules and links are
otherwise unchanged (all links resolve, the other stated facts match the
code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:34:18 +00:00
Claude c43eebbf9a docs(review-findings): dedupe with fable-idea, list owner decisions
- Replace the T2.b/T2.c/T2.d/F2 detail in the batch-3 note with a pointer:
  those items are tracked in fable-idea.md (and ARCHITECTURE §8.10).
- Tick the 2026-09 cleanup under Dead code.
- New "Owner decisions" checklist: reflected settings/fields and plain
  enums that nothing in Source/ reads but that assets, Blueprints, tools
  or tests may reference by name, so they were kept (sheet-ring settings,
  ceiling settings, DensityVolumeMaxTasks, unused audio/creature fields,
  biome WaterMaterial, EVoxelFace / EVoxelSeasonSelectionReason /
  EVoxelOpCombine, MaxDisplacement hooks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:33:51 +00:00
Claude 46f7f5f7c1 docs(fable-idea): tick what the code already does, close F18
- Header: drop the false note that .gitignore ignores this file (the
  whitelist keeps every *.md) and the "nothing here is implemented" line.
- Perf §0 / F3 ticked: stat VoxelForge group (VoxelStats.h) + Insights
  scopes exist. F6 ticked (Stage 1): mesher writes FVoxelMeshData::Colors
  from MaterialPaletteIndex (ARCHITECTURE §8.15). F7 marked partly done
  (landmark AnchorMode/exclusion, FTerrainCondition, FDecoCompanion; SDF
  stamps not built).
- F18 far-field sheets: marked REMOVED (owner decision) with what remains
  (unused settings, runtime passes false).
- Suggested-order list annotated with what is done and what is open.
- T1.b status no longer repeats the dead-trio removal (tracked in
  REVIEW_FINDINGS dead code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:33:51 +00:00
Claude 26fbf0c999 docs(architecture): drop the sheet ring, fix content/water to match code
- §8.10 render-distance paragraph: remove the F18 far "sheet" ring
  description (GenerateSheetMesh, SheetHole*, sheet re-queue). The ring
  is plain level-MaxClipLevel MC tiles; bFarSheetRing/FarSheetSpanLevels
  are documented as unused settings the runtime always passes as false.
  All other §8.10 invariants are kept; the budgeted-teardown note now says
  what UnloadTile does today (park the component), not ClearChunk.
- Fix the claim that GetLODForChunk/LODToStep/IsChunkInRange/
  GetStrateChunkZBounds and the ViewDistance settings are dead (the first
  three are gone, the last two are used); only the ceiling settings are.
- §8.5: decoration results are BuildId-guarded region builds applied by
  ApplyRegion (no ReadyResults/DecoEpoch/ApplyDecoResult); water is one
  player-following plane (UpdateWater), not per-tile planes; palette tags
  are per-grid EntryBiome; FStrateSetPiece no longer "coming" (folded into
  landmarks); drop the dead ContentMaxLevel/MaxLODLevel notes.
- §9.3: anchors fold in after the player clipmap (no sheet ring).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:31:24 +00:00
Claude c91b63d7c2 docs(codemap): refresh line hints, drop removed symbols
- Refresh every numeric line hint in the §3 tables, the §4 pipeline step
  anchors, the §5 "go here" table and the inline (h:NNN) / (NNN) hints;
  most pointed at a much older file layout (e.g. AVoxelWorld at 12,
  now 176; GetDensityWithParams at 277, now 3851).
- Remove rows/mentions of symbols that no longer exist: the far sheet
  ring (GenerateSheetMesh, SheetHole*, sheet-tile routing in LoadTile and
  BuildDesiredTiles), EvaluateSDF, the generator's ApplyPassageCarving
  forwarder, VoxelTypes chunk-local helpers (LocalToIndex & co.,
  CHUNK_SIZE_SQUARED/CHUNK_VOLUME), GetLODForChunk/LODToStep,
  VF_RollStrateParams (now VF_RollStrateParamsDetailed), and the stale
  AVoxelWorld storage list (Chunks/ChunkMeshes/ChunkLODs/PendingChunkCoord
  -> LoadedTiles/TileComponents/PendingTiles + the four MPSC queues).
- bFarSheetRing / FarSheetSpanLevels are documented as unused settings.
- Add the 13 test files that were missing from §3.12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:27:51 +00:00
Claude 26984d38d8 docs(comments): drop history narration, keep the why
Rewrite comments that told the story of past rounds instead of the
current code: dated "fixed on / tried on / reverted on" notes, "this used
to...", "stale paragraph removed", "Phase 1/2" progress labels, and the
"ClassifyTile still does not use ClassifyBox" claim (it does now, via
VF_ClassifyBoxWithWarpRetry). Every invariant, unit, sign convention,
trap and measurement that justified the design is kept, rephrased in the
present tense and in its original language (French or English).

Also fix comments that named symbols removed in the dead-code commit
(EvaluateSDF wrapper, FTunnelCoreWorldEvaluation support-floor fields,
BlendChunks -> TransitionBlendChunks) and the stale function name in the
VF_RollStrateParamsDetailed determinism checkf message.

Comment and message text only; no code token changed. Tests untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:18:36 +00:00
Claude 6fd8a88986 chore(logs): drop hard-coded temporary probe logs
Remove three one-off investigation aids pinned to one seed/coordinate:
- [VoxelForgeExplore][TemporaryShaftConnectors] in the shaft connector
  op's Eval (a thread_local flag + coordinate test in the density path);
- [VoxelForgeExplore][TemporaryVerticalLanding] in the vertical-shaft
  landing search (seed 14, bottom Z -736 only);
- [VoxelForgeExplore][TemporaryShaftProbe] in the explore commandlet walk
  mode (seed 14 VerticalShafts only; its own comment said it would be
  removed once the seam was fixed).

No tool, test or script matches these tags (Tools/*.ps1 only parse
[VoxelForgeExplore][OwnerAssets] under that prefix). The probe's density
reads were log-only, so generated geometry and reports are unchanged.
All cvar-gated diagnostics and test result lines are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:10:36 +00:00
Claude 3f629f407a refactor(dead-code): remove functions, members and types with no caller
Each symbol below had zero references in Source/ (tests included), Tools/,
Config/ and the build files after comment/string stripping.

Functions: EvaluateTunnelCoreSDF, EvaluateSDF (uncached wrapper),
IsTunnelSupportFloorWorldPoint, BuildTunnelSupportFloorColumn,
IsTunnelSupportFloorColumnZ, GetTunnelSupportFloorColumnBand,
VF_TunnelFloorAtSegment, VF_ProjectSweptTunnelFloor, FVoxelWormField
EvaluateExact, ApplyPassageLandingAirMC, ApplyPassageTunnelAirMC,
GetGenerationParamsFingerprint, FVoxelStrateCorpus::LoadFromSettings,
GetSkippedDefinitions, VF_RollStrateParams, VF_ArePlayerFitConnectedWithSampler,
VF_GetVoxelSeasonSelectionReasonName, UVoxelSeasonAsset::GetManifestContentHash,
FVoxelStackSampler::IsRunning, CompareSkippedPostValue,
MakeSurfaceColumnSource, WriteObjTile, ExploreEffectName,
ExploreTileClassName, and the VoxelTypes.h chunk-local helpers
(WorldToLocalCoord, ChunkToWorldPos, LocalToIndex, IndexToLocal,
IsValidLocalCoord, GetFaceDirection, GetFaceNormal, CHUNK_SIZE_SQUARED,
CHUNK_VOLUME).

Members/types: write-only floor flags (FVoxelOpSample::
bProtectedStructuralFloor, StructuralFloorMinimumDensity,
bTunnelCoreSupportFloor; bSupportFloor on the tunnel evaluations;
FTunnelFloorSegmentProfile::NumSteps), UVoxelStrateManager::BlendChunks,
unused FSummary counters in the stack sampler, two unused
FVoxelSeasonCompositionSettings aliases, and a few unused locals/constants.

No UPROPERTY, UFUNCTION, reflected type or enum was touched. Behaviour is
unchanged: removed code was unreachable or only written, never read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:09:05 +00:00
Claude 4753416d4a refactor(sheets): remove the far sheet ring from the runtime
The owner dropped the far sheet ring. Remove it from the streaming path:
- UVoxelMarchingCubesMesher::GenerateSheetMesh (declaration + definition);
- sheet-tile branches in LoadTile / GenerateTileResult / SyncRemeshTile and
  the bSheetTile / SheetChunkZ / Hole* parameters;
- the SheetHole* XY-hole state and its per-crossing re-queue block;
- VF_MaxMarchingCubesLevel (equal to the clamped MaxClipLevel without sheets);
- the sheet-only band arming and the 'sheet' field of the desired-level
  startup-trace events (no tool reads it).

AVoxelWorld now always passes bFarSheetRing=false to the tile selector, and
the descendant index covers levels up to the MaxClipLevel clamp (8) instead
of the old sheet maximum (12); no tile above 8 can exist any more.

Kept on purpose: UVoxelSettings::bFarSheetRing / FarSheetSpanLevels
(UPROPERTYs, now marked unused), the voxel.TestFarSheetRing switch and the
far_sheet_ring / sheet_tile fields the test harness compares (sheet_tile is
always false), and the selector's sheet parameters in
VoxelClipmapDesiredTiles.h, which two automation tests still exercise.
2026-09-28 13:50:55 +00:00
Claude 1de44af7cc chore(repo): archive historical docs under Docs/archive, drop tracked pycache
Move closed audits, task briefs, design rounds, perf reports, the OPSTACK
plan/progress logs and WORK-NEXT.md into Docs/archive/ (git mv, history kept).
Whitelist Docs/ in the strict .gitignore, ignore Tools/**/__pycache__/, and
remove the tracked .pyc. Repoint references in the kept docs and in non-test
source comments to the new paths.
2026-09-28 13:46:17 +00:00
1390 changed files with 296989 additions and 5671 deletions

No files matched your search

+16 -1
View File
@@ -9,11 +9,26 @@
!/Source/ !/Source/
!/Source/** !/Source/**
# Portable CPU generator and D3D11 viewer.
!/Standalone/
!/Standalone/**
/Standalone/Build/
/Standalone/bin/
# The guarded single-entry test harness is source, not generated output. # The guarded single-entry test harness is source, not generated output.
!/Tools/ !/Tools/
!/Tools/** !/Tools/**
/Tools/**/__pycache__/
# Closed / historical documents.
!/Docs/
!/Docs/**
!VoxelForge.uplugin !VoxelForge.uplugin
# Keep every design / doc markdown at any depth (AUDIT P1 — these were untracked) # Keep every design / doc markdown at any depth
!*.md !*.md
/Standalone/data/*.log
/Standalone/data/*.err
/Standalone/data/*.vfmesh
/Standalone/Research/**/*.log
+460 -103
View File
@@ -13,9 +13,9 @@ each strate can be a fundamentally different *archetype*, connected at (0,0).
### 8.0 Human-scale generation contract (2026-09-06) ### 8.0 Human-scale generation contract (2026-09-06)
All authored lengths are voxel-space values, converted at the boundary by `0.25 m/voxel`. The body Voxel-space authored lengths convert at `0.125 m/voxel`. The body reference is a **0.34 m radius,
reference is a **0.34 m radius, 1.76 m tall** capsule (`1.36 × 7.04` voxels). The default strate 1.76 m tall** capsule (`2.72 × 14.08` voxels). The default strate height is **16 chunks = 512 voxels
height is now **8 chunks = 256 voxels = 64 m**, which is intentionally larger than the 3-4 chunk = 64 m**, intentionally larger than the 3-4 chunk
boss-arena anchor so an archetype can reserve seals, vertical play, and a high ceiling together. boss-arena anchor so an archetype can reserve seals, vertical play, and a high ceiling together.
The default size families are deliberately independent: The default size families are deliberately independent:
@@ -23,7 +23,7 @@ The default size families are deliberately independent:
| Feature | Default metre derivation | Coupling kept sound | | Feature | Default metre derivation | Coupling kept sound |
|---------|--------------------------|---------------------| |---------|--------------------------|---------------------|
| Room network | radius 4-10 m (8-20 m diameters), origin radius 12 m (24 m hub), cell spacing 32 m | `MaxRoomRadius 40 < RoomSpacing/2 64`; a 6 m radial margin remains around the largest room | | Room network | radius 4-10 m (8-20 m diameters), origin radius 12 m (24 m hub), cell spacing 32 m | `MaxRoomRadius 40 < RoomSpacing/2 64`; a 6 m radial margin remains around the largest room |
| Round passages | 1.5-2 m radius (3-4 m bores); maze corridor radius 3.125 m (6.25 m nominal bore) | `0.7 × 2 × (12.5−3.75) × 0.25 = 3.0625 m` usable floor after worst roughness; Maze `12.5 < CellSize/2 32` | | Round passages | 1.5-2 m radius (3-4 m bores); maze corridor radius 3.125 m (6.25 m nominal bore) | `0.7 × 2 × (25−7.5) × 0.125 = 3.0625 m` usable floor after worst roughness; Maze `25 < CellSize/2 64` |
| Slab | floor at 16 m, ceiling at 38.4 m, 22.4 m open span | floor/ceiling noise and 4-8 m column diameters remain inside the open span | | Slab | floor at 16 m, ceiling at 38.4 m, 22.4 m open span | floor/ceiling noise and 4-8 m column diameters remain inside the open span |
| SurfaceWorld | 20 m terrain relief, 12 m warp, 60.8 m sky cap | terrain features are bounded below the 64 m strate envelope | | SurfaceWorld | 20 m terrain relief, 12 m warp, 60.8 m sky cap | terrain features are bounded below the 64 m strate envelope |
| VerticalShafts | 4-7 m shaft diameters, 3 m connector bore, 8 m ledge interval | shaft spacing is 20 m; connector/ledge values are derived from the roughness envelope | | VerticalShafts | 4-7 m shaft diameters, 3 m connector bore, 8 m ledge interval | shaft spacing is 20 m; connector/ledge values are derived from the roughness envelope |
@@ -36,7 +36,7 @@ radius, **0.300** against the Maze corridor radius, **0.469/0.625** against shaf
**0.234** against the minimum island radius (**0.484** including its 6-voxel blend), and **0.234** against the minimum island radius (**0.484** including its 6-voxel blend), and
**0.625/0.938** against slab column radius for floor/ceiling roughness. SurfaceWorld is a **0.625/0.938** against slab column radius for floor/ceiling roughness. SurfaceWorld is a
heightfield, so its **0.047** ratio is against its 80-voxel relief scale, not a fictitious radial heightfield, so its **0.047** ratio is against its 80-voxel relief scale, not a fictitious radial
feature. FlatPlain and CrystalChamber intentionally share `GetSlabDensity` and the same slab feature. FlatPlain and CrystalChamber intentionally share `BuildSlabStack` and the same slab
parameter block; they cannot acquire separate scale geometry without a generator change. parameter block; they cannot acquire separate scale geometry without a generator change.
This scale contract is informed by [Epic's Unreal level-blockout guidance](https://dev.epicgames.com/documentation/en-us/unreal-engine/designer-01-project-setup-and-level-blockout-in-unreal-engine), which recommends a player-sized reference and gives 2-3 m hall / 3-4 m height starting guidance. The This scale contract is informed by [Epic's Unreal level-blockout guidance](https://dev.epicgames.com/documentation/en-us/unreal-engine/designer-01-project-setup-and-level-blockout-in-unreal-engine), which recommends a player-sized reference and gives 2-3 m hall / 3-4 m height starting guidance. The
@@ -62,22 +62,23 @@ are not landing or box-verdict violations.
### 8.1 Archetypes (`ECaveGeneratorType`, VoxelStrateTypes.h) ### 8.1 Archetypes (`ECaveGeneratorType`, VoxelStrateTypes.h)
Each archetype has its own param `USTRUCT` (on `UVoxelStrateDefinition`, EditCondition-gated Each archetype has its own param `USTRUCT` (on `UVoxelStrateDefinition`, EditCondition-gated
by `GeneratorType`) and its own density function in `VoxelGenerator.cpp`, dispatched by the by `GeneratorType`) and its own **operator stack** builder in `VoxelDensityOpStack.cpp`, selected per
`switch` in `GetDensityAt`. chunk by `VF_BuildOpStackForChunk` in `GetDensityAt` (the same factory `ClassifyTile` uses). The stack
is the only density path; a degenerate (zero-height) strate has none and is open air.
| Archetype | Params struct | Density fn | Idea | | Archetype | Params struct | Stack builder | Idea |
|-----------|---------------|------------|------| |-----------|---------------|------------|------|
| TunnelNetwork | `FStrateGenerationParams` | `GetDensityWithParams` | rooms+tunnels (original) | | TunnelNetwork | `FStrateGenerationParams` | `BuildTunnelNetworkStack` (fused evaluator: `GetDensityWithParams`) | rooms+tunnels (original) |
| FlatPlain / CrystalChamber | `FSlabGenerationParams` | `GetSlabDensity` | floor/ceiling void (original) | | FlatPlain / CrystalChamber | `FSlabGenerationParams` | `BuildSlabStack` | floor/ceiling void (original) |
| Maze | `FMazeGenerationParams` | `GetMazeDensity` | origin-directed spanning-tree corridors on a 3D lattice, with capped loop edges and a thread-local per-cell capsule cache | | Maze | `FMazeGenerationParams` | `BuildMazeStack` | origin-directed spanning-tree corridors on a 3D lattice, with capped loop edges and a thread-local per-cell capsule cache |
| SurfaceWorld | `FSurfaceGenerationParams` | `GetSurfaceDensity` | heightfield terrain: domain-warped continents+ridged mtns+detail, a low-freq **relief map** (`M`) that scales mountains/elevation for plains↔highland variety, **F20 heightfield terrain ops** (`Surface|Ops` — all default off ⇒ byte-identical): **Cliff** (slope-gated steepening — where the analytic structural slope > `CliffSlopeThreshold`, push the height away from the local mean by `CliffSharpness` ⇒ gentle slopes become sheer walls/canyon faces that hug real steep ground, gentle areas untouched; 4 structural resamples only when enabled), **Terrace** (relief-gated plateau quantize + `TerraceHardness` soft-round↔crisp-mesa), **LayerLines** (sedimentary sine shelves, slope-expressed) — pure per-column height remaps in the single oracle `ComputeSurfaceTerrainZ` (`SampleSurfaceStructuralZ` = pre-op raw height, re-sampled at an XY offset for Cliff's slope), biome-selected + border-blended for free via each biome's `SurfaceParams` + the height output-lerp; plus **phase-2 Overhang** (the first VOLUMETRIC op — real jutting shelves like a cliff lip): in `SurfaceDensityFromColumn`, for AIR voxels in the window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height and unioned in. Low in the window the shift is ~0 (borrows nearby low rock ⇒ stays air over the void); high up it reaches the far cliff (solid) ⇒ a shelf attached to the cliff, tapering out over the void with air underneath. Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill dir `(DirX,DirY)` are resolved once in `ComputeSurfaceColumn` — the gradient sampled at the REACH scale (`OverhangReach`) so a point out over the void can "see" the cliff to know which way is uphill — and cached on `FSurfaceColumn`. It's genuine 3D (per-voxel structural re-eval), so it's gated hard to steep overhang columns; the union only ADDS rock (never removes), capped at `TerrainZ+OverhangHeight`, so `ClassifyTile` forces Mixed only in `(TerrainZ, TerrainZ+OverhangMargin]` (upward-only; margin = max `OverhangHeight`) — a shelf never holes a trivially-skipped tile, and only the thin cliff-edge band of air tiles is woken (NOT far-field like phase-3 spikes/holes). Overhang undersides classify as ground rock by the F17 rule (down-facing but below `TerrainZ`). Known v1 limit: applies at all LODs (Step-agnostic) — may alias far; gate to fine tiles later. Beaches at water line, high sky-cap ceiling. The cap is shapeable terrain in its own right (`ComputeSurfaceCeiling`, `Surface|Sky` params: `CeilingUndulation` broad inverted hills/valleys, `CeilingRidgeStrength` hanging ridgelines, `CeilingRoughness`+freq fine bumps, `CeilingWarp*`) — defaults (strengths 0, freq 0.04) = old flat-ish cap. (`Surface|Macro` params = the cheap precursor to biomes; `ReliefStrength=0` ⇒ old uniform terrain.) **Sky-cap vs ground is a PER-TRIANGLE surface class (F17), not a per-tile verdict**: the mesher classifies each unique vertex on the **worker** — only down-facing verts (`N.Z<-0.1`) pay a memoized `GetSurfaceHeightAt` column query; nearer `CeilSurf` ⇒ sky-cap, nearer `TerrainZ` ⇒ terrain overhang stays ground (a future SurfaceWorld cave roof — down-facing but below `TerrainZ` — also lands ground by the same rule; non-surface strates always ground). Triangles take the majority class of their 3 verts and are packed as **two contiguous index runs** (ground then cap, `FVoxelMeshData::NumCeilingTriangles`; skirts inherit their source triangle's run) → RMC **polygroups 0/1** → one **section per non-empty group** (`ApplyMeshToTile`, material slot = group index): slot 0 = strate `OverrideMaterial`/default (min-corner chunk), slot 1 = `CeilingMaterial` resolved at the tile's **TOP chunk** (mid as gap fallback, then min; fallback: ground material) — a coarse tile is 2^level chunks tall, so its min corner can sit in a lower strate/gap while the cap belongs to the strate above (this was the "far cap = ground material" residue), so the shadowless overhead rock is tinted separately instead of reading flat/bright. Shadow is **per section** via `FRealtimeMeshSectionConfig::bCastsShadow` (NOT the component `SetCastShadow` — RMC's proxy ignores the component flag; this is also why level≥2 far tiles only stopped casting once the section flag was wired): ground casts at level≤1, the cap section never casts, so the rock ceiling never shadows the terrain below it. History: v1 was a game-thread centre height-oracle (misclassified coarse far tiles → terrain material on the cap underside); v2 a whole-tile worker normal VOTE — which painted **mixed coarse tiles** (one far tile spanning terrain AND cap) entirely with the winner's material, and put sky material under terrain overhangs. The per-triangle class fixes both and is the identity channel caves/F8 will reuse. | | SurfaceWorld | `FSurfaceGenerationParams` | `BuildSurfaceStack` | heightfield terrain: domain-warped continents+ridged mtns+detail, a low-freq **relief map** (`M`) that scales mountains/elevation for plains↔highland variety, **F20 heightfield terrain ops** (`Surface|Ops` — all default off ⇒ byte-identical): **Cliff** (slope-gated steepening — where the analytic structural slope > `CliffSlopeThreshold`, push the height away from the local mean by `CliffSharpness` ⇒ gentle slopes become sheer walls/canyon faces that hug real steep ground, gentle areas untouched; 4 structural resamples only when enabled), **Terrace** (relief-gated plateau quantize + `TerraceHardness` soft-round↔crisp-mesa), **LayerLines** (sedimentary sine shelves, slope-expressed) — pure per-column height remaps in the single oracle `ComputeSurfaceTerrainZ` (`SampleSurfaceStructuralZ` = pre-op raw height, re-sampled at an XY offset for Cliff's slope), biome-selected + border-blended for free via each biome's `SurfaceParams` + the height output-lerp; plus **phase-2 Overhang** (the first VOLUMETRIC op — real jutting shelves like a cliff lip): in `FOverhangShelfMod`, for AIR voxels in the window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height and unioned in. Low in the window the shift is ~0 (borrows nearby low rock ⇒ stays air over the void); high up it reaches the far cliff (solid) ⇒ a shelf attached to the cliff, tapering out over the void with air underneath. Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill dir `(DirX,DirY)` are resolved once in `ComputeSurfaceColumn` — the gradient sampled at the REACH scale (`OverhangReach`) so a point out over the void can "see" the cliff to know which way is uphill — and cached on `FSurfaceColumn`. It's genuine 3D (per-voxel structural re-eval), so it's gated hard to steep overhang columns; the union only ADDS rock (never removes), capped at `TerrainZ+OverhangHeight`, so `ClassifyTile` forces Mixed only in `(TerrainZ, TerrainZ+OverhangMargin]` (upward-only; margin = max `OverhangHeight`) — a shelf never holes a trivially-skipped tile, and only the thin cliff-edge band of air tiles is woken (NOT far-field like phase-3 spikes/holes). Overhang undersides classify as ground rock by the F17 rule (down-facing but below `TerrainZ`). Known v1 limit: applies at all LODs (Step-agnostic) — may alias far; gate to fine tiles later. Beaches at water line, high sky-cap ceiling. The cap is shapeable terrain in its own right (`ComputeSurfaceCeiling`, `Surface|Sky` params: `CeilingUndulation` broad inverted hills/valleys, `CeilingRidgeStrength` hanging ridgelines, `CeilingRoughness`+freq fine bumps, `CeilingWarp*`) — defaults (strengths 0, freq 0.04) = old flat-ish cap. (`Surface|Macro` params = the cheap precursor to biomes; `ReliefStrength=0` ⇒ old uniform terrain.) **Sky-cap vs ground is a PER-TRIANGLE surface class (F17), not a per-tile verdict**: the mesher classifies each unique vertex on the **worker** — only down-facing verts (`N.Z<-0.1`) pay a memoized `GetSurfaceHeightAt` column query; nearer `CeilSurf` ⇒ sky-cap, nearer `TerrainZ` ⇒ terrain overhang stays ground (a future SurfaceWorld cave roof — down-facing but below `TerrainZ` — also lands ground by the same rule; non-surface strates always ground). Triangles take the majority class of their 3 verts and are packed as **two contiguous index runs** (ground then cap, `FVoxelMeshData::NumCeilingTriangles`; skirts inherit their source triangle's run) → RMC **polygroups 0/1** → one **section per non-empty group** (`ApplyMeshToTile`, material slot = group index): slot 0 = strate `OverrideMaterial`/default (min-corner chunk), slot 1 = `CeilingMaterial` resolved at the tile's **TOP chunk** (mid as gap fallback, then min; fallback: ground material) — a coarse tile is 2^level chunks tall, so its min corner can sit in a lower strate/gap while the cap belongs to the strate above (this was the "far cap = ground material" residue), so the shadowless overhead rock is tinted separately instead of reading flat/bright. Shadow is **per section** via `FRealtimeMeshSectionConfig::bCastsShadow` (NOT the component `SetCastShadow` — RMC's proxy ignores the component flag; this is also why level≥2 far tiles only stopped casting once the section flag was wired): ground casts at level≤1, the cap section never casts, so the rock ceiling never shadows the terrain below it. History: v1 was a game-thread centre height-oracle (misclassified coarse far tiles → terrain material on the cap underside); v2 a whole-tile worker normal VOTE — which painted **mixed coarse tiles** (one far tile spanning terrain AND cap) entirely with the winner's material, and put sky material under terrain overhangs. The per-triangle class fixes both and is the identity channel caves/F8 will reuse. |
| VerticalShafts | `FVerticalShaftParams` | `GetVerticalShaftDensity` | full-height shafts + horizontal connectors + partial ledges | | VerticalShafts | `FVerticalShaftParams` | `BuildVerticalShaftStack` | full-height shafts + horizontal connectors + partial ledges |
| FloatingIslands | `FFloatingIslandParams` | `GetFloatingIslandDensity` | asymmetric islands: flat land top + underside tapering to a point, lobed (domain-warped) outline, in an open void | | FloatingIslands | `FFloatingIslandParams` | `BuildFloatingIslandStack` | asymmetric islands: flat land top + underside tapering to a point, lobed (domain-warped) outline, in an open void |
| Underwater | `FStrateGenerationParams` + water | (reuses `GetDensityWithParams`) | tunnel rock + high water table | | Underwater | `FStrateGenerationParams` + water | (reuses `BuildTunnelNetworkStack`) | tunnel rock + high water table |
All density fns share the convention: internal **positive=solid**, apply origin spine → All stacks share the convention: internal **positive=solid**, and `AppendStructuralPost` ends every
vertical boundary seal → inter-strate passage tube + landing/floor → XY edge seal, then `return -Density` (MC: stack with origin spine → vertical boundary seal → inter-strate passage tube + landing/floor → XY
negative=solid). The final MC-facing edge pass in `GetDensityAt` is repeated after disturbances so edge seal; `EvalMC` negates once (MC: negative=solid). The final MC-facing edge pass in `GetDensityAt` is repeated after disturbances so
the global rim cannot be reopened by a post-process; the diff layer remains the explicit player the global rim cannot be reopened by a post-process; the diff layer remains the explicit player
override. override.
StrateManager provides params per chunk via `GetMaze/Surface/VerticalShaft/FloatingIslandParamsForChunk` StrateManager provides params per chunk via `GetMaze/Surface/VerticalShaft/FloatingIslandParamsForChunk`
@@ -127,12 +128,9 @@ threshold** before the law gate. No corridor or threshold tuning was added. The
gate is therefore retained as a design blocker; see `COMPOSER-NOTES.md §3.2b` and the validation log gate is therefore retained as a design blocker; see `COMPOSER-NOTES.md §3.2b` and the validation log
in §10. in §10.
⚠️ **The `switch` above is no longer the only density path.** All 8 archetypes also exist as The operator-stack design lives in `Docs/archive/OPSTACK-PLAN.md` /
**operator stacks**, selected per strate by `Docs/archive/OPSTACK-DECOMPOSITION.md`, the symbol index in `CODEMAP §3.2d` — not repeated here.
`bUseOperatorStack` and evaluated instead of the `switch`; each is bit-identical to the function in What matters for *this* document: the archetype table describes what the world IS.
its row. The design lives in `OPSTACK-PLAN.md` / `OPSTACK-DECOMPOSITION.md`, the symbol index in
`CODEMAP §3.2d` — not repeated here. What matters for *this* document: the archetype table describes
what the world IS, and both paths compute it.
### 8.2 (0,0) spine & hybrid connections ### 8.2 (0,0) spine & hybrid connections
- `ApplyOriginSpine` (VoxelGenerator.cpp, static helper) builds a finite, rounded landing room with - `ApplyOriginSpine` (VoxelGenerator.cpp, static helper) builds a finite, rounded landing room with
@@ -177,8 +175,8 @@ arrival→departure flood-fill at the independently placed passage endpoints.
### 8.2a XY edge seal (bounded-world invariant) ### 8.2a XY edge seal (bounded-world invariant)
`UVoxelSettings::WorldRadiusVoxels` and `EdgeSealThickness` define the bounded world in actor-space `UVoxelSettings::WorldRadiusVoxels` and `EdgeSealThickness` define the bounded world in actor-space
XY around `(0,0)`. The default radius is 8192 voxels (~2.05 km at 25 cm/voxel) and the default XY around `(0,0)`. The legacy 8192-voxel radius is ~1.024 km at 12.5 cm/voxel; use 16384 voxels
solid ramp is 64 voxels. A radius of **0 is a true no-op**: the hot path returns before doing any for the same ~2.05 km world radius. The source edge-seal default is 128 voxels. A radius of **0 is a true no-op**: the hot path returns before doing any
arithmetic, so the legacy unbounded field is unchanged. From `R - Thickness` to `R`, arithmetic, so the legacy unbounded field is unchanged. From `R - Thickness` to `R`,
`VF_ApplyXYEdgeSeal` raises the internal positive-solid density with the same `SmoothStep01` ramp `VF_ApplyXYEdgeSeal` raises the internal positive-solid density with the same `SmoothStep01` ramp
idiom as the vertical seal; at and beyond `R` it forces positive `BaseDensity`. The generator idiom as the vertical seal; at and beyond `R` it forces positive `BaseDensity`. The generator
@@ -230,8 +228,8 @@ they are never needed for the connectivity proof.
The evaluation window is exactly the lower nodes in `{-1,0}³`, an 8-node / 2×2×2 local halo. The The evaluation window is exactly the lower nodes in `{-1,0}³`, an 8-node / 2×2×2 local halo. The
edge predicate checks the adjacent `+1` endpoint's parent locally, so it does not need a wider edge predicate checks the adjacent `+1` endpoint's parent locally, so it does not need a wider
collect region and cannot disagree at a chunk boundary. Both `GetMazeDensity` and collect region and cannot disagree at a chunk boundary. `MakeLatticeCorridorSource` rebuilds those
`MakeLatticeCorridorSource` rebuild those decisions in their thread-local per-cell cache; the decisions in its thread-local per-cell cache; the
voxel loop evaluates only cached capsule SDFs. This is deliberately independent of voxel loop evaluates only cached capsule SDFs. This is deliberately independent of
`WorldRadiusVoxels` (which remains `0`) and the still-gated lateral-region system. `WorldRadiusVoxels` (which remains `0`) and the still-gated lateral-region system.
@@ -291,9 +289,11 @@ when close (separate HISMs → crossing the near boundary never touches the far
(per grid, sharing ONE `MaxConcurrentDecorationTasks` budget — each throttles against the other's in-flight count): (per grid, sharing ONE `MaxConcurrentDecorationTasks` budget — each throttles against the other's in-flight count):
snapshot the update's decoration palette (built once on the GAME thread — biome, see below) then fire an async `UE::Tasks` march snapshot the update's decoration palette (built once on the GAME thread — biome, see below) then fire an async `UE::Tasks` march
(`BuildCellSpawns`, `BackgroundNormal`, capped at `MaxConcurrentDecorationTasks` in flight via `InFlightCells`). (`BuildCellSpawns`, `BackgroundNormal`, capped at `MaxConcurrentDecorationTasks` in flight via `InFlightCells`).
**(3)** `ProcessDecoResults`: drain finished tasks' results (`Mpsc` queue → `ReadyResults`), epoch-guarded **(3)** `ProcessDecoResults`: drain finished cell marches (`DecoResults`, `Mpsc`) and fold each into its
(`DecoEpoch`, bumped on clear/strate-change so stale in-flight results are discarded) + range-checked, and grid's region build (`MergeCellResult`; a result whose build is gone or whose `BuildId` no longer matches —
**apply (spawn) budgeted** (`MaxDecorationCellsPerFrame` — the only game-thread cost, SpawnActor/AddInstance). region cleared + re-marched — is discarded), then **apply completed regions budgeted**
(`MaxDecorationCellsPerFrame` regions/frame via `ApplyRegion` — the only game-thread cost; a region that left
range while it marched is dropped unbuilt).
`BuildCellSpawns` (worker) finds each column's surface point(s) and rolls the entries there (shared `BuildCellSpawns` (worker) finds each column's surface point(s) and rolls the entries there (shared
`PlaceAtCrossing`). Candidate columns are **snapped to INTEGER voxel XY** (integer jitter) so the generator's `PlaceAtCrossing`). Candidate columns are **snapped to INTEGER voxel XY** (integer jitter) so the generator's
surface-column cache (T1.a, §8.10) applies — FRACTIONAL XY bypasses it and recomputes the noise-heavy surface-column cache (T1.a, §8.10) applies — FRACTIONAL XY bypasses it and recomputes the noise-heavy
@@ -321,7 +321,7 @@ result's `Entries` snapshot. `DecorationMaxCrossingsPerColumn` caps cave columns
task count before UObject teardown (tasks read the Generator); `BeginDestroy` is the backstop. **Determinism:** task count before UObject teardown (tasks read the Generator); `BeginDestroy` is the backstop. **Determinism:**
pure hash of (cell, column, crossing, entry, seed) + the density surface snap. **Decorations exist ONLY in pure hash of (cell, column, crossing, entry, seed) + the density surface snap. **Decorations exist ONLY in
the player's current strate** (march is strate-bounded) → a strate change wipes + rebuilds them, and there the player's current strate** (march is strate-bounded) → a strate change wipes + rebuilds them, and there
is **no cross-strate light bleed to cull** (the old `SetActiveStrate` light-culling pass is SUBSUMED — gone). is **no cross-strate light bleed to cull**.
**Render paths:** `ActorClass` → real actors (lights/logic, pricey game-thread spawn); `InstancedMesh` → HISM **Render paths:** `ActorClass` → real actors (lights/logic, pricey game-thread spawn); `InstancedMesh` → HISM
(no tick/actor/collision, emissive glows far), per-cell-per-entry. **Per-entry HISM tuning for dense groundcover** (no tick/actor/collision, emissive glows far), per-cell-per-entry. **Per-entry HISM tuning for dense groundcover**
(`FStrateDecoration`, only the InstancedMesh path): `CullDistance` (cm; 0 = no cull — the lever that makes dense (`FStrateDecoration`, only the InstancedMesh path): `CullDistance` (cm; 0 = no cull — the lever that makes dense
@@ -332,8 +332,8 @@ grass affordable: placed thickly, drawn only near → GPU cost bounded by area-w
`MinSlopeAngle` (lower companion to Max — band a prop onto a tilt range, e.g. 30..70 = slopes only), `MinSlopeAngle` (lower companion to Max — band a prop onto a tilt range, e.g. 30..70 = slopes only),
`bWallExcludeOverhangs` (wall-only-upright: drop normals with N.Z < 0 so downward overhangs don't take wall props). `bWallExcludeOverhangs` (wall-only-upright: drop normals with N.Z < 0 so downward overhangs don't take wall props).
**Shared vocabulary (2026-07-06):** these gates + spawn/transform/render fields now live on `FPlacementProfile` **Shared vocabulary (2026-07-06):** these gates + spawn/transform/render fields now live on `FPlacementProfile`
(embedded as `Profile` on `FStrateDecoration`, `FStrateLandmark`, and the coming `FStrateSetPiece`), so all three (embedded as `Profile` on `FStrateDecoration` and `FStrateLandmark`), so both scatter primitives are
scatter primitives are authored identically. Rotation unified to `RotationOffset` (fixed) + `RandomRotation` authored identically. Rotation unified to `RotationOffset` (fixed) + `RandomRotation`
(per-axis hash-random) — decoration's ctor defaults `RandomRotation.Yaw = 360` (full random heading, replacing the (per-axis hash-random) — decoration's ctor defaults `RandomRotation.Yaw = 360` (full random heading, replacing the
old `bRandomYaw`/`MinYaw`/`MaxYaw`; banded yaw = offset + a smaller random range). Distribution is unchanged; the old `bRandomYaw`/`MinYaw`/`MaxYaw`; banded yaw = offset + a smaller random range). Distribution is unchanged; the
exact per-instance yaw values reshuffle once (different hash mix). exact per-instance yaw values reshuffle once (different hash mix).
@@ -345,7 +345,7 @@ SampleMoisture / SampleBiomeAt) on demand, so it's deterministic + worker-safe (
strate's already-resolved `FBiomeContext`, so no re-resolve). Opt-in per entry (empty list = zero cost); strate's already-resolved `FBiomeContext`, so no re-resolve). Opt-in per entry (empty list = zero cost);
wired into both the deco worker (`BuildCellSpawns`) and landmark placement (`SpawnLandmarkInstance`). This wired into both the deco worker (`BuildCellSpawns`) and landmark placement (`SpawnLandmarkInstance`). This
is the shared core of the coming quest `FindFeature` locator (same predicate, run as an outward search) and is the shared core of the coming quest `FindFeature` locator (same predicate, run as an outward search) and
the anchor gate for `FStrateSetPiece`. The BP bridge `AVoxelWorld::GetVoxelSurfaceHeightAt` exposes the the landmark anchor gate. The BP bridge `AVoxelWorld::GetVoxelSurfaceHeightAt` exposes the
trace-free deterministic ground/ceiling height so authored ruin/set-piece Blueprints self-arrange on the trace-free deterministic ground/ceiling height so authored ruin/set-piece Blueprints self-arrange on the
real surface before it meshes. Next types (water-edge band, relief-peak local-max, slope) are additive. real surface before it meshes. Next types (water-edge band, relief-peak local-max, slope) are additive.
**F7 COMPANIONS (relational decoration, `FDecoCompanion`):** each `FStrateDecoration` may list `Companions` **F7 COMPANIONS (relational decoration, `FDecoCompanion`):** each `FStrateDecoration` may list `Companions`
@@ -397,12 +397,12 @@ Static-mobility HISM re-caches its proxy — fine for player-paced digging.) Not
grass on a freshly-exposed ledge / regrowth after fill-back — both self-correct on the next natural re-stream. **Freeze note:** a huge set-piece mesh hitches on register (game-thread proxy/distance-field build — grass on a freshly-exposed ledge / regrowth after fill-back — both self-correct on the next natural re-stream. **Freeze note:** a huge set-piece mesh hitches on register (game-thread proxy/distance-field build —
NOT async-fixable; the spawn is game-thread by engine rule; the asset is a hard ref so already resident) → NOT async-fixable; the spawn is game-thread by engine rule; the asset is a hard ref so already resident) →
mitigate ASSET-side (Nanite on the mesh, bake distance fields), optionally budget spawns across frames. mitigate ASSET-side (Nanite on the mesh, bake distance fields), optionally budget spawns across frames.
`ApplyDecoResult` buckets spawns per entry and builds each HISM with ONE batched `AddInstances` `ApplyRegion` buckets a region's spawns per mesh (all its cells merged) and builds each HISM with ONE batched
(single cluster-tree build, set cull/shadow BEFORE `RegisterComponent`) — the game-thread hitch-killer for dense cells. `AddInstances` (single cluster-tree build, set cull/shadow BEFORE `RegisterComponent`) — the game-thread
hitch-killer for dense cells.
**Per-entry tier (`StreamTier`, default Far)** picks NearGrid or FarGrid; radius + column spacing are PER-GRID **Per-entry tier (`StreamTier`, default Far)** picks NearGrid or FarGrid; radius + column spacing are PER-GRID
settings, never per-entry (a per-entry radius would re-introduce the in-place re-stream flicker — see the two-grid settings, never per-entry (a per-entry radius would re-introduce the in-place re-stream flicker — see the two-grid
rationale above). This REPLACES the vestigial `MaxLODLevel`; the dead `DecorationActorRadiusChunks` setting is rationale above). `CullDistance` still bounds GPU draw on top (orthogonal to which grid
repurposed as `DecorationNearRadiusChunks`. `CullDistance` still bounds GPU draw on top (orthogonal to which grid
streams the entry). **No LOD area-density compensation** (placement is per real streams the entry). **No LOD area-density compensation** (placement is per real
surface point, density-stable with distance). **SpawnDensity semantics CHANGED** vs the old vertex scatter: it surface point, density-stable with distance). **SpawnDensity semantics CHANGED** vs the old vertex scatter: it
rolls per column surface-point (not per mesh vertex) → expect a one-time density re-tune. **Settings rolls per column surface-point (not per mesh vertex) → expect a one-time density re-tune. **Settings
@@ -436,22 +436,23 @@ null entry = "evaluated, nothing placed" so it isn't retried). Deterministic (ha
Strate-bounded (wiped on strate change, like decorations). This is the real home for "rare prop at all Strate-bounded (wiped on strate change, like decorations). This is the real home for "rare prop at all
distances" — the job the decoration FarGrid could approximate at moderate range but not at extreme radius. distances" — the job the decoration FarGrid could approximate at moderate range but not at extreme radius.
**(B) WATER — tile-driven, level-0 only (continuous plane, never pops).** `PopulateTileWater(tile)` in **(B) WATER — ONE strate-global ocean plane that follows the player (`UpdateWater`, from `Tick`).** A single
`ApplyMeshToTile` (level-0 tiles), `ClearTileWater(tile)` in `UnloadTile`. One scaled engine plane scaled engine plane (`/Engine/BasicShapes/Plane`, no collision, no shadow) snapped to a coarse cell around the
(`/Engine/BasicShapes/Plane`) per water-surface chunk (per-chunk-Z plane logic assumes a single chunk's player, so it only repositions when the player crosses a cell; terrain pokes through it, so it reads as water at
vertical span — hence level-0 only), keyed `TMap<FIntVector, UStaticMeshComponent*>` (reflected UPROPERTY). every LOD and to the horizon with no per-tile gaps (one draw). Water Z: `bHasWater` + `WaterLevelRelative` →
Water Z: `bHasWater` + `WaterLevelRelative` → `StrateManager::GetWaterLevelWorldZForChunk`. Biome `StrateManager::GetWaterLevelWorldZForChunk` (no water in the current strate ⇒ plane hidden). Material:
`WaterMaterial` overrides `UVoxelStrateDefinition::WaterMaterial` (level stays strate-global). `UVoxelStrateDefinition::WaterMaterial`. ⚠️ OPEN: `UVoxelBiomeDefinition::WaterMaterial` (the biome
content profile's water override, §8.14) is not read by the single-plane path — owner to decide whether
the biome override should come back (REVIEW_FINDINGS, *Owner decisions*).
`ClearAll`/`SetSeed` on `ChangeSeed`/regenerate clears both subsystems (decorations re-stream on the next `ClearAll`/`SetSeed` on `ChangeSeed`/regenerate clears both subsystems (decorations re-stream on the next
Tick via the INT_MIN sentinels). **Per-biome content (§8.14):** decorations resolve the dominant biome **PER COLUMN** on the worker Tick via the INT_MIN sentinels). **Per-biome content (§8.14):** decorations resolve the dominant biome **PER COLUMN** on the worker
(`ResolveBiomeSampleAt` via the strate's `FBiomeContext`, box-cached → one rebuild per chunk footprint). The (`ResolveBiomeSampleAt` via the strate's `FBiomeContext`, box-cached → one rebuild per chunk footprint). The
update builds ONE flat decoration palette (every biome's list concatenated; `CurrentEntryBiome[i]` tags entry update builds ONE flat decoration palette per grid (every biome's list concatenated; `EntryBiome[i]` tags entry
`i` with its context-biome index, -1 = strate fallback), and `PlaceAtCrossing` rolls only the entries the `i` with its context-biome index, -1 = strate fallback), and `PlaceAtCrossing` rolls only the entries the
column's biome owns. This replaced the old per-CELL `GetDominantBiomeAt` (one biome for a whole 8 m cell → column's biome owns. Borders follow the warped-Voronoi field at column resolution, no straight lines (a
axis-aligned border snapping); borders now follow the warped-Voronoi field at column resolution, no straight per-CELL biome would snap borders to the 4 m cell grid). `Initialize` also takes `UVoxelSettings*` (for the
lines. Water still uses the chunk-centre `GetDominantBiomeAt` for its material. `Initialize` now also takes grid tunables).
`UVoxelSettings*` (for the grid tunables). `ContentMaxLevel` is now legacy/dead for decorations.
### 8.6 Atmosphere — `VoxelAtmosphereManager.h/.cpp` (NEW) ### 8.6 Atmosphere — `VoxelAtmosphereManager.h/.cpp` (NEW)
`UVoxelAtmosphereManager` (owned by `AVoxelWorld`, gated by `bManageAtmosphere`). `UVoxelAtmosphereManager` (owned by `AVoxelWorld`, gated by `bManageAtmosphere`).
@@ -521,7 +522,7 @@ flat floor. **There is no connector to the origin landing room at `(0,0)`.** The
those radial roads after a playtest (`d97373c`, 2026-09-08: they cut straight through everything); those radial roads after a playtest (`d97373c`, 2026-09-08: they cut straight through everything);
room joins are flattened to walkable height instead. A source-fit landing therefore stays local to room joins are flattened to walkable height instead. A source-fit landing therefore stays local to
the room it was fitted into (`GeneratePassages`, the `VF_BuildPassageLanding` calls). the room it was fitted into (`GeneratePassages`, the `VF_BuildPassageLanding` calls).
⚠️ Open gap (Sol's code review, `DESIGN-SOL-2026-09-15-CODE.md` §1.3/§2.4): when the footing query ⚠️ Open gap (Sol's code review, `Docs/archive/DESIGN-SOL-2026-09-15-CODE.md` §1.3/§2.4): when the footing query
declines, the landing falls back to the historical random reach, and nothing proves that pose joins declines, the landing falls back to the historical random reach, and nothing proves that pose joins
the walk network. The canonical capability gate covers one scenario, not every seed or mix. the walk network. The canonical capability gate covers one scenario, not every seed or mix.
Proposed fix, not built: retry deterministically at other candidate spots instead of falling back. Proposed fix, not built: retry deterministically at other candidate spots instead of falling back.
@@ -534,14 +535,12 @@ named 15° floor-gradient limit as the default inter-strate tunnel. The room and
therefore real walkable geometry, not a point anchor or nearest-component guess, and no therefore real walkable geometry, not a point anchor or nearest-component guess, and no
per-passage ordering is involved. per-passage ordering is involved.
The chamber dimensions come directly from the capsule: player diameter `2×0.34/0.25 = 2.72` The chamber dimensions come directly from the capsule: player diameter `2×0.34/0.125 = 5.44`
voxels; required floor width `3/0.25 = 12` voxels; capsule height `2×0.88/0.25 = 7.04` voxels; voxels; required floor width `3/0.125 = 24` voxels; capsule height `2×0.88/0.125 = 14.08` voxels;
one metre of headroom adds 4 voxels. `HalfWidth=max(7, MouthRadius+2)` leaves one metre of headroom adds 8 voxels. `HalfWidth=max(12, MouthRadius+4)` gives a 24-voxel
`2×(HalfWidth−1) >= 12` voxels of flat support after the one-voxel wall inset; the authored room minimum clear floor width (3 m). Height is `max(24, 14.08+8, 2×MouthRadius+4)`; the minimum
is therefore at least 14 voxels (3.5 m) wide. Height is room height is 24 voxels (3 m). `FloorZ=StandingPoint.Z−1`, floor thickness is 6 voxels (0.75 m),
`max(12, 7.04+4, 2×MouthRadius+2)`; the stock 6-voxel mouth is a 14-voxel (3.5 m) room with and the tube centreline
an 8-voxel half-width (4 m full width).
`FloorZ=StandingPoint.Z−0.5`, floor thickness is 3 voxels (0.75 m), and the tube centreline
meets the room at `FloorZ+MouthRadius`, so its lower tangent is the floor and there is no step lip. meets the room at `FloorZ+MouthRadius`, so its lower tangent is the floor and there is no step lip.
The final-density floor audit samples the flat plane and checks the player-fit capsule above it. The The final-density floor audit samples the flat plane and checks the player-fit capsule above it. The
default tunnel audit samples every route step against the final density and checks the analytic and default tunnel audit samples every route step against the final density and checks the analytic and
@@ -554,8 +553,8 @@ The landing is part of the structural post, in the fixed order
after the passage carve and after MC-space disturbances as a support backstop, still before the after the passage carve and after MC-space disturbances as a support backstop, still before the
final XY edge seal. Room/floor Z is clamped inside the vertical seal; the XY edge seal is last and final XY edge seal. Room/floor Z is clamped inside the vertical seal; the XY edge seal is last and
wins over every landing or tube near the rim. Thus no landing can breach a strate boundary or the wins over every landing or tube near the rim. Thus no landing can breach a strate boundary or the
world edge. The op-stack `FPassageCarveOp` calls the same manager operation as the legacy path, world edge. The op-stack `FPassageCarveOp` calls the manager's `ApplyPassageModifier`, including
including the floor, and `ClassifyTile` kills both uniform hypotheses around the bidirectional the floor, and `ClassifyTile` kills both uniform hypotheses around the bidirectional
landing floor (`Both` in the stack), so an air proof cannot delete support. landing floor (`Both` in the stack), so an air proof cannot delete support.
The query remains pure — it does not construct an operator stack, touch a cache, or call back into The query remains pure — it does not construct an operator stack, touch a cache, or call back into
@@ -621,8 +620,7 @@ driven by `EditorBrush*` props.
inner 3×3 shafts and their capsules for the per-voxel loop, while spatial culling selects cached inner 3×3 shafts and their capsules for the per-voxel loop, while spatial culling selects cached
connectors. The complete ±2 parent and ±3 fallback windows plus connector reach are contained connectors. The complete ±2 parent and ±3 fallback windows plus connector reach are contained
by that halo, so no parent decision depends on the evaluation cell. No cell or pair hash, widened by that halo, so no parent decision depends on the evaluation cell. No cell or pair hash, widened
neighbourhood scan, or tree construction may move into the per-voxel loop; the operator-stack neighbourhood scan, or tree construction may move into the per-voxel loop (`FShaftFieldSource`).
source follows the same cache contract.
- **Maze field cache**: parent/loop decisions are rebuilt only when the thread-local cell, seed, or - **Maze field cache**: parent/loop decisions are rebuilt only when the thread-local cell, seed, or
loop parameters change. The rebuild evaluates the 24 canonical lower-node/axis edges in the loop parameters change. The rebuild evaluates the 24 canonical lower-node/axis edges in the
local `{-1,0}³` window and emits at most 24 capsules; the per-voxel loop performs no hash, local `{-1,0}³` window and emits at most 24 capsules; the per-voxel loop performs no hash,
@@ -684,13 +682,27 @@ driven by `EditorBrush*` props.
position → seamless across chunk borders (both sides use identical pure samples). NO per-vertex position → seamless across chunk borders (both sides use identical pure samples). NO per-vertex
`GetDensityAt` (was ~6/vertex, often as costly as the whole grid). `ComputeGradientNormal` is now `GetDensityAt` (was ~6/vertex, often as costly as the whole grid). `ComputeGradientNormal` is now
unused. Only NORMALS changed vs the old path; geometry is identical. unused. Only NORMALS changed vs the old path; geometry is identical.
- **Surface column cache (T1.a)** (`FSurfaceColumnCache` = LRU of `FSurfaceColumnBox`, `GetDensityAt` - **Core hybrid sharp mesher (M2)** (`CoreMesherRecipe` / `MesherRecipe`): the property remains
SurfaceWorld branch): the heightfield + sky-cap + biome blend are a PURE function of (XY, seed, opt-in and defaults to `MarchingCubesV1`; `bUseVoxelForgeCore` remains separately default-off.
V1 preserves the round-3 feature-aware dual-contouring path for repeatability. V2 reuses the
existing density grid and MC case/triangle topology, then projects eligible LOD0 edge roots onto
exact type-2 plan-box planes with feature-split normals. Only edges whose complete incident-cell
ring is inside the tile can snap; border roots and every LOD1+ cell stay MC, with skirts unchanged.
The recipe version participates in Core content identity. Do not alter density queries, worker
task/epoch handling, thread-local MC grid caches, or LOD0-only collision policy. Keep
`MarchingCubesV1` and the Core-off legacy path unchanged. V1 measured 5.58–7.11× MC on tagged
fixtures; V2 measured at most 1.27× and passed five exact cooked contacts plus the independent
same-LOD border check. Keep both recipes opt-in. The standalone stitched-transition suite still
has five MC doorway topology failures. Production skirts cover these seams in the static audit
view but their strips are visible; the owner should review them at the target camera. Skirts do not
make the mesh topologically watertight.
- **Surface column cache (T1.a)** (density path: the surface stack's `FSurfaceColumnSource` memo;
`ClassifyTile`: `FSurfaceColumnCache` = LRU of `FSurfaceColumnBox`): the heightfield + sky-cap + biome blend are a PURE function of (XY, seed,
strate) — **ZERO Z dependence** (climate/Voronoi are pure-XY; surface params are per-strate constant strate) — **ZERO Z dependence** (climate/Voronoi are pure-XY; surface params are per-strate constant
under Hard transitions) — yet sampled ~33× per column (once per Z grid-point). Cached per integer XY under Hard transitions) — yet sampled ~33× per column (once per Z grid-point). Cached per integer XY
(box-valid, like the SDF cache) and reused down the column. **Keyed by (XY box, StrateKey, Seed), NOT (box-valid, like the SDF cache) and reused down the column. **Keyed by XY box + strate identity + seed
ChunkZ** (`StrateKey = round(StrateBottomWorldZ)`, taken from the params so it can't disagree with (plus layout version / params fingerprint), NOT ChunkZ** (the strate identity is derived from
them) and held as a small **LRU of 6 boxes** so the WHOLE vertical view-distance stack — and XY `StrateBottomWorldZ`, taken from the params so it can't disagree with them) and held as a small **LRU of 6 boxes** so the WHOLE vertical view-distance stack — and XY
neighbours the scheduler interleaves — share one another's heavy column noise instead of each neighbours the scheduler interleaves — share one another's heavy column noise instead of each
recomputing it ~once per vertical chunk (this was the dominant `GenerateMesh` cost: the same 2D recomputing it ~once per vertical chunk (this was the dominant `GenerateMesh` cost: the same 2D
heightfield recomputed per altitude). It also makes pure-air / pure-solid chunks cheap (they hit the heightfield recomputed per altitude). It also makes pure-air / pure-solid chunks cheap (they hit the
@@ -764,28 +776,14 @@ driven by `EditorBrush*` props.
inside a strate band), then the OUTERMOST shell keeps generating tiles outward until it covers inside a strate band), then the OUTERMOST shell keeps generating tiles outward until it covers
the requested distance (`VF_OuterShell`, shared by `BuildDesiredTiles` and `IsTileInClipRange` the requested distance (`VF_OuterShell`, shared by `BuildDesiredTiles` and `IsTileInClipRange`
so the cull sees the same horizon; the ring's dz sweep is pre-clamped to the vertical strate so the cull sees the same horizon; the ring's dz sweep is pre-clamped to the vertical strate
band). As MC tiles the ring cost grows with (distance/2^MaxClipLevel)² — which is why the ring band). The ring is made of level-MaxClipLevel MC tiles, so its cost grows with
defaults to **F18 SHEETS** (`bFarSheetRing`): in an open strate the far field is exactly two (distance/2^MaxClipLevel)². The far "sheet" ring (heightfield tiles past `MaxClipLevel`) was
heightfields (TerrainZ + CeilSurf, per-column oracle), so the ring streams tiles at level dropped by the owner's decision; it no longer exists in the selector or the settings.
`MaxClipLevel + FarSheetSpanLevels` (one sheet = 2^span MC footprints per axis → 4-16× fewer (A "step cap" variant — raising CoarseTileCells per level so far levels keep a fine Step — was
components) meshed by `GenerateSheetMesh` as two displaced grids — ground polygroup 0 / cap tried and rejected 2026-07-06.)
polygroup 1, tags true by construction, same materials/UVs/F6 masks, ~3-6× cheaper gen per
area than band-cut MC. **XY hole**: a partially-covered sheet renders its whole footprint, so
the MC-covered box around the player (level-MaxClipLevel box, shrunk 1 tile for a seam-overlap
ring) is CUT out of the sheet at cell granularity (`SheetHole*Vox`, passed to
`GenerateSheetMesh`); when the player crosses a MaxClipLevel tile the hole moves and the
overlapping sheets re-queue via `BandRemeshQueue` — the shrink means the newly-cut area's MC
tiles were already desired one crossing earlier (loaded before uncovered). Sheet tiles take
the strate from the BAND (mid chunk): band unarmed
(inter-strate gap) ⇒ empty ring until landing; non-open strates ⇒ empty sheets (their far
ring was enclosed rock anyway); carved features (passages/spine/chasms/diff) don't show at
sheet distance — the MC shells keep them near. (A "step cap" variant — raising CoarseTileCells
per level so far levels keep a fine Step — was tried and rejected 2026-07-06.)
Trade-offs accepted: other strates simply don't render at far LOD (they're sealed/enclosed — Trade-offs accepted: other strates simply don't render at far LOD (they're sealed/enclosed —
invisible except through passage mouths, which read as dark holes); passage tubes crossing the gap invisible except through passage mouths, which read as dark holes); passage tubes crossing the gap
are cut at coarse levels only (near levels mesh full). are cut at coarse levels only (near levels mesh full).
`GetLODForChunk` / `LODToStep` / `IsChunkInRange` / `GetStrateChunkZBounds` and the
ViewDistance/LOD/strate-Z/ceiling settings are now DEAD/unused (left in place).
- **SKIRTS — LOD-seam crack filler** (`GenerateMesh`, after the cell loop; `VoxelSettings::bGenerateSkirts` - **SKIRTS — LOD-seam crack filler** (`GenerateMesh`, after the cell loop; `VoxelSettings::bGenerateSkirts`
+ `SkirtCells`, wired onto the mesher at setup). Neighbouring shells mesh at different resolutions so + `SkirtCells`, wired onto the mesher at setup). Neighbouring shells mesh at different resolutions so
their iso-surfaces don't meet along the shared face → a thin see-through crack. After meshing, every their iso-surfaces don't meet along the shared face → a thin see-through crack. After meshing, every
@@ -818,9 +816,8 @@ driven by `EditorBrush*` props.
drain; `VoxelSettings::MaxUnloadsPerFrame`): the cull APPROVES removals (strict load-before-unload) but drain; `VoxelSettings::MaxUnloadsPerFrame`): the cull APPROVES removals (strict load-before-unload) but
doesn't destroy in place — it queues them. `ProcessUnloadQueue` runs at most `MaxUnloadsPerFrame` doesn't destroy in place — it queues them. `ProcessUnloadQueue` runs at most `MaxUnloadsPerFrame`
`UnloadTile`s/frame (scaled up to 4× with backlog, capped so a huge backlog can't re-spike). WHY: a `UnloadTile`s/frame (scaled up to 4× with backlog, capped so a huge backlog can't re-spike). WHY: a
fast traversal culls a whole shell's worth of tiles in ONE frame, and each `UnloadTile` does fast traversal culls a whole shell's worth of tiles in ONE frame, and each `UnloadTile` strips and
`DestroyComponent` + (level 0) `ContentManager::ClearChunk` → `Destroy()` of every decoration actor — parks its component (T2.c pool) — an unbudgeted burst = a game-thread spike ("stuff torn down behind you" at speed). Mesh APPLIES were
an unbudgeted burst = a game-thread spike ("stuff torn down behind you" at speed). Mesh APPLIES were
already budgeted; this matches it for DESTROYS. Re-desired tiles are cancelled out of the queue (still already budgeted; this matches it for DESTROYS. Re-desired tiles are cancelled out of the queue (still
loaded → no reload). `PendingUnload` is cleared in `RegenerateAllChunks`/`EndPlay` (tiles already gone). loaded → no reload). `PendingUnload` is cleared in `RegenerateAllChunks`/`EndPlay` (tiles already gone).
- **Collision only at LEVEL 0** (`ApplyMeshToTile`): `UpdateSectionConfig(..., Tile.Level==0)`. Far tiles - **Collision only at LEVEL 0** (`ApplyMeshToTile`): `UpdateSectionConfig(..., Tile.Level==0)`. Far tiles
@@ -833,7 +830,7 @@ driven by `EditorBrush*` props.
= 0; `=1` restores it as an A/B instrument). Measured on the headless game path with nested = 0; `=1` restores it as an A/B instrument). Measured on the headless game path with nested
refinement off, the classifier plus its exact validation cost more than they saved. Mode 0 had refinement off, the classifier plus its exact validation cost more than they saved. Mode 0 had
ready p95 182 vs 190-195 ms, generation p95 125-127 vs 139-141 ms, and 18% fewer density calls, ready p95 182 vs 190-195 ms, generation p95 125-127 vs 139-141 ms, and 18% fewer density calls,
with 841/841 tiles triangle-identical (WORK-NEXT.md). Proving a 33³ core uniform costs almost with 841/841 tiles triangle-identical (Docs/archive/WORK-NEXT.md). Proving a 33³ core uniform costs almost
as much as meshing 35³. The history below explains why it existed. Its soundness tests still as much as meshing 35³. The history below explains why it existed. Its soundness tests still
guard it. guard it.
- **Trivial-empty tile reject (T1.d) — v2 SHIPPED (2026-07-05); v1 was reverted 2026-06-26.** - **Trivial-empty tile reject (T1.d) — v2 SHIPPED (2026-07-05); v1 was reverted 2026-06-26.**
@@ -844,9 +841,11 @@ driven by `EditorBrush*` props.
wasted) and gen throughput had become the felt gameplay limit ("standing waiting for generation"). wasted) and gen throughput had become the felt gameplay limit ("standing waiting for generation").
**Why v1 failed & how v2 avoids it:** v1 used a GLOBAL analytic ceiling bound — not conservative when **Why v1 failed & how v2 avoids it:** v1 used a GLOBAL analytic ceiling bound — not conservative when
the cap hangs low (`CeilingRoughness`/`RidgeStrength`) → holes in the roof. v2 makes NO amplitude the cap hangs low (`CeilingRoughness`/`RidgeStrength`) → holes in the roof. v2 makes NO amplitude
guesses: it evaluates `ComputeSurfaceColumn` (the SAME function as the density path, via the SHARED guesses: it evaluates `ComputeSurfaceColumn` (cached per thread in `GSurfColCache`) — the column
`GSurfColCache`) **on the exact lattice the mesher would sample** (margin ring included) — same the surface stack's height operators reproduce (ground height pinned bit for bit by
functions + same inputs ⇒ the same floats ⇒ the verdict is exact at the lattice, not an estimate. `VoxelForge.OpStack.SurfaceHeightEquivalence`) — **on the exact lattice the mesher would sample**
(margin ring included) — same inputs ⇒ the same floats ⇒ the verdict is exact at the lattice, not
an estimate.
Per-z rules: gap chunk = solid; surface seal band (ApplyBoundarySeal inequalities, `BaseDensity>0`) Per-z rules: gap chunk = solid; surface seal band (ApplyBoundarySeal inequalities, `BaseDensity>0`)
= solid; surface interior z: air ⇔ `TerrainZ ≤ z ≤ CeilSurf` (MC `D ≥ 0`); ANY other archetype / = solid; surface interior z: air ⇔ `TerrainZ ≤ z ≤ CeilSurf` (MC `D ≥ 0`); ANY other archetype /
out-of-layout chunkZ ⇒ `Mixed` (cave interiors are not provable in v1 of this classifier — a future out-of-layout chunkZ ⇒ `Mixed` (cave interiors are not provable in v1 of this classifier — a future
@@ -860,7 +859,7 @@ driven by `EditorBrush*` props.
A false `Mixed` only costs CPU; the code must NEVER emit a false AllSolid/AllAir (that's a hole). A false `Mixed` only costs CPU; the code must NEVER emit a false AllSolid/AllAir (that's a hole).
Capture tiles (`bWantCapture`, density-volume shadow window) always generate — the volume wants the Capture tiles (`bWantCapture`, density-volume shadow window) always generate — the volume wants the
grid even for uniform cells. A sparse ~5×5 column pre-pass exits Mixed fast on surface-crossing grid even for uniform cells. A sparse ~5×5 column pre-pass exits Mixed fast on surface-crossing
tiles; a Mixed verdict leaves its columns warm in `GSurfColCache` for the GenerateMesh that follows. tiles; the columns stay warm in `GSurfColCache` for the full pass and for neighbouring tiles.
- **Worker-built StreamSet (T1.f)** (`BuildTileStreamSet`, `LoadTile` task → `FChunkResult::Streams`): - **Worker-built StreamSet (T1.f)** (`BuildTileStreamSet`, `LoadTile` task → `FChunkResult::Streams`):
the RMC vertex/index buffers (`FRealtimeMeshStreamSet`) are built ON THE GEN WORKER, not on the game the RMC vertex/index buffers (`FRealtimeMeshStreamSet`) are built ON THE GEN WORKER, not on the game
thread. The per-vertex builder loop was the dominant game-thread streaming cost (measured: game thread. The per-vertex builder loop was the dominant game-thread streaming cost (measured: game
@@ -981,9 +980,9 @@ change *anything*, e.g. frequencies, which scalar multipliers couldn't.)
- **Consumption — SURFACE (output-blend).** Per chunk, `CP_SurfaceBiomeParams[]` holds each biome's - **Consumption — SURFACE (output-blend).** Per chunk, `CP_SurfaceBiomeParams[]` holds each biome's
resolved surface params (its override when `bOverrideTerrain` + GeneratorType matches, else the resolved surface params (its override when `bOverrideTerrain` + GeneratorType matches, else the
strate's) with **structural fields forced from the strate** (Z bounds, seal, base density, water strate's) with **structural fields forced from the strate** (Z bounds, seal, base density, water
level). Per voxel: `ResolveBiomeSampleAt` → dominant `PD` (+ neighbour `PN`); `GetSurfaceDensity` level). Per voxel: `ResolveBiomeSampleAt` → dominant `PD` (+ neighbour `PN`); the surface stack's
computes `ComputeSurfaceTerrainZ` for `PD` and, in the border band, for `PN`, and **lerps the biome-blend height source (and `ComputeSurfaceColumn`) computes the terrain height for `PD` and, in
resulting HEIGHTS**. Blending heights (not params) is seamless across *any* difference (frequencies the border band, for `PN`, and **lerps the resulting HEIGHTS**. Blending heights (not params) is seamless across *any* difference (frequencies
included) — what per-param blend never could. `PD==PN`, weight 0 ⇒ bit-identical, no biomes. included) — what per-param blend never could. `PD==PN`, weight 0 ⇒ bit-identical, no biomes.
- **Consumption — CAVES: structural overrides are NOT applied (determinism).** Rooms/tunnels are - **Consumption — CAVES: structural overrides are NOT applied (determinism).** Rooms/tunnels are
decided over a wide COLLECT region spanning chunks (§8.4); making room params vary by region would decided over a wide COLLECT region spanning chunks (§8.4); making room params vary by region would
@@ -1080,6 +1079,362 @@ timings are intentionally diagnostic metadata and do not affect world or image c
viewer remains a separate display-only consumer: it must load this OBJ/manifest and never evaluate viewer remains a separate display-only consumer: it must load this OBJ/manifest and never evaluate
density or generate geometry. density or generate geometry.
### 8.17 Standalone Core in Unreal — integration round 2
**Status (2026-10-08):** `AVoxelWorld::bUseVoxelForgeCore` now selects a Core runtime route and is
false by default. Core mode has independent `CoreClipRadius=3` and `CoreMaxClipLevel=8` properties;
it does not read or write the older clip properties or owner assets. BuildHost UBT and the Core link
symbol audit pass. A headless BuildHost actor proof matches all initial tile hashes against standalone
Core, checks collision readiness against the expected non-empty level-0 tiles, moves a streaming
anchor, and applies a transaction edit. The current-plugin switch-off baseline is the narrow
`VoxelForge.Determinism.CrossPlatformDigest`; it passes but does not spawn an actor with the switch
false, so it is a legacy-generator baseline rather than an actor-level A/B proof.
The game-thread asset adapter emits the immutable schema-v1 input and resolves it through
`ResolvedWorldSnapshot::FromJson`; Core workers receive no UObjects. The Core route asks
`World::desiredTiles` and `prioritizeTiles`, compiles and generates through Unreal tasks, and carries
the generation `Epoch` through MPSC results. The shared payload builder, RealtimeMesh upload, and
collision cook remain the final path. Workers observe `bShuttingDown`, and `EndPlay` drains active
tasks. Preserve §8.10 streaming, cancellation, epoch, collision-handoff, and per-frame-budget rules.
Core `Vec3d` positions and tile keys are in voxel space, one voxel is 25 cm, and Core mesh vertex
positions are already in centimetres. The bridge copies those mesh positions directly; player,
anchor, query, and edit inputs convert world centimetres using the Core scale. High-level brushes
that historically express dimensions and density strength in the plugin's 12.5 cm voxels scale
radius, extent, falloff, and strength by 12.5/25 to preserve their physical effect. The low-level
`ApplyModification` path expects active
voxel units. Tile keys remain Core keys; only legacy material lookup maps Core tile spans into the
existing strate chunk layout.
Carving drains in-flight Core calls, applies an increasing-sequence `applyEditTransaction`, advances
the epoch, and regenerates the returned affected keys. This leaves a transaction boundary for later
replication; no transport is implemented. Existing streaming anchors are included in Core desired
tiles; the BuildHost shift check covers `CollisionOnly`. `FullVisual` is wired through the same
anchor reconciliation but has not had an independent headless assertion. The owner-editor visual
and physical play check remains the hand-off step. See
[`Standalone/UNREAL_INTEGRATION_REPORT.md`](Standalone/UNREAL_INTEGRATION_REPORT.md) for evidence,
known limits, and instructions.
### 8.18 Core world correction and biome gap — integration round 3
**Status (2026-10-08):** the owner confirmed that Core runs in his game. His first visual review found
two shape-world defects and two suspected streaming/feature defects. The finite origin room and its
floor now follow the owner layout; the synthetic 256-voxel entry bore was removed, and invalid
passage descriptors no longer create micro-landings. This is an intentional Core world-version
change. The standalone viewer's old hashes are not compatible with this corrected field; the new
BuildHost initial clipmap hashes match at W1/W8/W16:
`density=a8bcb72d41092503`, `mesh=90f29ba15e24579f`, `payload=82c6b489e7bb7fd7`,
`topology=7b99448a1f844f2c`.
The BuildHost actor proof now travels to the B-side landing and the surface strate. At each position,
the desired visual tile-key set is complete, tile payload hashes match standalone Core, and adjacent
same-LOD and transition faces have full coverage. Fine tiles compiled around the travelled player
(960 at B and 640 at the surface position). The surface actor phase had extra stale/support keys in
its loaded collection, so the check filters collision-only support and asserts the exact 2,553-key
visual set; it does not claim every collision-support compile had quiesced. Null rendering still
means this is streaming/payload evidence, not an Unreal screenshot.
`GetBiomeAtWorldLocation` now queries the copied owner biome evaluator in Core mode at the legacy
12.5 cm query scale expected by that evaluator. It returns biome identity/data for the surface sample
(`Biome_Surface_Plain`, index 2); this query does not add biome-driven terrain or decorations to the
Core shape field. A full biome surface remains a separate feature. Recommended next design: make
strate evaluation explicit in the resolved Core snapshot, with a deterministic `SurfaceWorld` path
that consumes resolved biome/height parameters and uses fixed-order noise plus per-column caching.
This is a medium-to-large port with controllable per-voxel cost and straightforward worker
determinism. Routing SurfaceWorld through the copied legacy evaluator is a smaller fidelity bridge,
but it duplicates the field boundary, costs roughly the legacy evaluation per sample, and inherits
more float/platform constraints; keep it as a temporary compatibility route only if exact behavior
is required before the port. See the round-3 table in the integration report for per-defect evidence.
Round 4 supersedes the SurfaceWorld status above: Core now has the temporary legacy density bridge
described in §8.19. The native evaluator, water presentation, and other archetypes remain open work.
### 8.19 SurfaceWorld bridge and all-strate coverage — integration round 4
**Status (2026-10-08):** the copied owner evaluator is now called inside Core for samples whose
resolved strate type is SurfaceWorld. The bridge covers Core density entry points used by the
marching-cubes path: analytic, lattice, unclamped, and packed-run sampling. SurfaceWorld boxes fail
closed as Mixed in the primitive-only box classifier so it cannot skip the biome heightfield. Core
edit brushes are applied after the owner density result. The reader also retains SlabParams,
MazeParams, VerticalShaftParams, and FloatingIslandParams in the standalone snapshot.
The owner/Core comparator found identical density hashes at LOD0–LOD3 in the matched-camera
SurfaceWorld matrix and on a 686,000-sample full-resolution surface patch. At LOD0 the measured
symmetric sampled surface distance was 0.0767 m mean / 1.0117 m max; at LOD2 it was 0.1737 m mean /
5.264 m max with 63 unmatched sampled vertices. The viewer captures still show a visible mesh or
presentation difference. Core does not emit the legacy water plane, biome material palette,
decorations, or atmosphere from this density bridge.
The bridge is deterministic across the tested BuildHost W1/W8/W16 worker counts. It is intentionally
not a platform-independent arithmetic port: it inherits the copied evaluator's float behavior. Its
cost is substantial. A matched CPU viewer run with 1,294 tiles through LOD8 took 23.085 s wall and
180.562 worker-seconds in Core, versus 23.914 s and 188.705 worker-seconds in the owner route. The
Step29 Core baseline without this route took 0.785 s, but omitted the surface. Use the bridge as a
fidelity fallback while a native heightfield with fixed-order noise and per-column biome/height
caching is developed.
The probe table in the [integration report](Standalone/UNREAL_INTEGRATION_REPORT.md) covers all
eight generator types, five passage types, four passage styles, and three transitions. These are the
round-4 recommendations; the SurfaceWorld row is superseded by §8.20:
| Field | Recommended Core route | Reason and current gap |
|---|---|---|
| TunnelNetwork | Keep the native primitive representation; add missing density operators as measured. | Existing rooms, tunnels, and passage chains work, but the 16-tile probe still changed 2.17% of signs and had a 1.01 m mean sampled surface distance. |
| FlatPlain and CrystalChamber | Native slab operator using the already copied slab parameters. | Floor, ceiling, columns, and crystal ceiling are a distinct low-cost structure; generic cave primitives do not express them. |
| Maze | Native deterministic cell graph and corridor field using FMazeGenerationParams. | The legacy topology is seed-stable and compact; current generic caves changed 13.34% of signs and had a 5.92 m maximum sampled surface distance. |
| SurfaceWorld | Keep the exact bridge as the Unreal runtime path until the standalone native evaluator is integrated and owner-reviewed. | The native fixed-scale heightfield, route plan and field sweep now exist in `Standalone/Core`; the plugin season/hash path and in-game review remain open. The bridge still lacks render-side water and biome materials. |
| VerticalShafts | Native deterministic shaft/ledge field; use legacy fallback only as a short compatibility step. | Generic cave shapes produced no matching surface in the probe and failed the passage-centerline audit. |
| FloatingIslands | Native island SDF with deterministic placement and smooth unions. | The current probe changed 95.75% of signs; an island layout is sparse and does not map well to room/tunnel primitives. |
| Underwater | Reuse native TunnelNetwork geometry and add an explicit water render payload. | The density field can reuse cave geometry; water material/plane is separate from signed density and remains absent. |
| Passage types and styles | Keep the generated control-point chains, but preserve each type's radius/floor contract and assert walkability. | The broad chain is already available. Worm and Cascading have solid centerline samples in the current Core audit. |
| Strate transitions | Implement the three boundary functions in the Core layer field. | Gradient, Hard, and Interleaved produced different owner hashes but the same Core hash in the probe. |
The copied evaluator remains a compatibility route when exact density is required and no reviewed
native manifest is attached. For low-cost discrete fields such as slabs, mazes, shafts, and islands,
native support keeps the worker path explicit and avoids charging every sample for a second field
implementation. BuildHost verifies the opt-in Unreal route and its 25 cm Core scale; it does not
certify the missing archetypes.
**Standalone SurfaceWorld M1 progress (2026-10-09):** `WorldAssemblyManifest` evaluator 4 now
serializes a finite-strate surface route with stable waypoint IDs, a 0.34 m × 1.76 m player capsule,
a 15° grade limit and underfoot support constraints. The final composed field must pass a capsule
sweep before a route candidate is accepted. Cliff shelves carry a two-Core-voxel minimum thickness.
The route law applies to generation and automatic changes; player carve/fill edits remain unrestricted.
At the end of round 4 this was standalone evidence, not yet connected to the settings/content-hash
path. Round 5 connects the reviewed manifest to `UVoxelSettings`; see §8.20. The owner still needs to
build and review it in his game.
The round-4 horizon audit traced the false low slab to the LOD8 three-dimensional marching-cubes cap
surface sampled on a 256-voxel lattice; the Core mesh's cap classification also consulted the legacy
owner height oracle. LOD8 now samples the planned cap directly as a 2D heightfield on a 128-voxel grid,
clips cells at the ground and tile-Z limits, and keeps existing ceiling seam skirts. This preserves a
closed cap surface rather than deleting its index run. The viewer defaults to a 60 FPS ceiling and
accepts `--frame-limit=1..60`, including while its window is occluded. See
[`Standalone/ASSEMBLER_M1_REPORT.md`](Standalone/ASSEMBLER_M1_REPORT.md) for the matched up-and-out
captures, cap tile counts, determinism hashes, route sweep and query/collision readiness split.
### 8.20 Native SurfaceWorld manifest in Unreal — integration round 5
`UVoxelSettings` now owns optional canonical world-assembly JSON plus its Core digest. In the editor,
`SourceWorldAssemblyManifestJson` selects the reviewed JSON and `ImportWorldAssemblyManifestJson`
validates, canonicalizes and stores it in the cooked settings asset. Import rejects a recipe seed that
differs from the effective settings seed. `GetCurrentSeasonContentHash()` includes the assembly
digest only when `bUseVoxelForgeCore` is enabled; with Core disabled, it returns the existing season
hash before reading optional assembly data. That keeps the legacy world identity stable on switch-off.
During Core initialization, `FVoxelForgeCoreAdapter` validates the manifest seed and requires each
planned stratum to match exactly one resolved `SurfaceWorld` slot by stratum index and top/bottom
chunk bounds. It inserts the canonical manifest and hash into the Core snapshot before the snapshot
is moved to Core. A missing manifest leaves the existing cave-only Core layout untouched; malformed,
wrong-seed, unmatched, duplicate or incomplete plans fail initialization. Core workers still receive
only the immutable resolved snapshot, not UObjects.
Core mesh/collision ordering has one SurfaceWorld-specific guard: a collision-support tile requested
outside the current desired set is promoted into the next desired rebuild, then deferred until Core
region compilation admits it. This prevents an early isolated support job from racing the native
region compile and publishing a tile whose hash differs from the standalone region snapshot. The
existing `Epoch` and MPSC result paths remain in force, and collision bodies are cooked by Unreal's
RealtimeMesh/Chaos route on the game side.
BuildHost passed the reviewed manifest `8933693d9560ba9f` (recipe `c37de78b759c0a4c`, plan
`1ca84619c8600ee5`) with W1/W8/W16 determinism, cave streaming, anchor movement and carve. At the
surface travel point `(37,165,-2011)` Core voxels, 2,553 expected visual hashes matched independent
standalone Core output; the check covered 573 local tiles, 3,438 adjacent faces and 432 LOD
transitions. Collision expectations covered 125 near tiles: all 36 non-empty cases had cooked bodies
and all 89 empty cases had none. The native field sampler visited 4,096 points over 3,200 voxels and
found three biome IDs. The complete pass log is under `Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/`.
The switch-off audit was a source-level branch/hash review, not a flag-off actor replay. It confirms
the legacy generation branch and previous content hash remain unchanged when `bUseVoxelForgeCore`
is false. Same-session BuildHost timing on the same 2,553 keys with hashes enabled was 96.816887 s
native versus 197.346153 s through the legacy bridge, including region compilation (2.038× native
speedup). This is serial headless tile generation. The final actor rerun passed in 9m20s with a
3.61 GiB peak sampled private working set. The approximately 1.57 s full-view aim is still
unmet: the last matched 529-tile LOD8 stream took 4.446 s. No viewer benchmark ran in round 5.
The owner handoff sets the manifest on the existing `DA_Settings` asset (seed 0), saves it, and
enables `AVoxelWorld::bUseVoxelForgeCore`. Coordinates and expected surface/coast/cliff/cap views are
in [`Standalone/ASSEMBLER_M1_REPORT.md`](Standalone/ASSEMBLER_M1_REPORT.md). BuildHost does not
replace the owner's project build or visual walk; cap lighting and the pointed native ridge peaks
remain owner review items.
### 8.21 Native SurfaceWorld height, brush and far-mesh corrections — integration round 6
Core tile keys and density coordinates use 25 cm voxels; the shared plugin mesher and
`UVoxelStrateManager` retain the legacy 12.5 cm scale. The Core bridge now maps Core-local height
queries to manager coordinates by 0.5 and maps returned bounds back by 2.0. Native content, water,
landmark, decoration and atmosphere anchors use the same manifest `Heightfield2D` ground/cap data
as the terrain. These conversions are gated by `bUseVoxelForgeCore`; the legacy branch keeps its
prior units and behavior. `UVoxelMarchingCubesMesher` has an explicit geometry voxel size: Core sets
25 cm, while legacy callers retain the 12.5 cm default. Material and biome lookups remain in their
legacy coordinate space.
Blueprint-callable carve/fill and high-level brush dimensions cross the scale boundary at 0.5,
including strength. Legacy diff modifications subtract their strength; Core edit evaluation adds its
density offset, so the adapter negates the legacy strength once. Public carve forces the airward sign
and fill forces the solidward sign. Low-level active-Core modification units remain Core units.
LOD8 far-stratum boundary repairs now sample crossings across a subdivided 4×4 coarse patch and only
emit a patch when each sample has a source-field crossing. This replaces the former broad single
quad. The BuildHost geometry audit keeps a two-cell maximum edge at LOD0/1 and allows four coarse
cells at LOD7+ to include shallow far-LOD transitions while still rejecting the prior 5.66-cell
horizon slab. It checks near-LOD normal direction and unit normals at every audited LOD. The mesher
emits reversed triangle order (`0,2,1`) for RealtimeMesh; the audit reports that expected order
separately from normal direction. The opt-in native heightfield path narrows vertical mesher rows to
sampled ground/cap heights plus the Z bounds of intersecting type-2/type-4 manifest features. Hash captures and tiles
with overlapping player edits or another strate keep the full mesh range. See round 6 of the M1
report for per-kind streaming results and validation limits.
### 8.22 Core voxel scale, per-tile features and placement measurements — World Assembler M3 Round 5
The opt-in Core world has one physical voxel-size source on AVoxelWorld: CoreVoxelSizeCm, default
25 cm and constrained to the supported 12.5–25 cm range for this integration. GetActiveVoxelSizeCm()
is the runtime conversion point for Core coordinates. CoreTileVoxelCount is 32 voxels; tile length
is derived as voxel size × 32 × 2^LOD. Core-off remains on the legacy 12.5 cm VOXEL_SIZE path.
Owner/Core compatibility conversions use the resolved values and must not assume that a Core voxel
is twice a legacy voxel.
The resolved physical size is rounded to microns and carried with tileVoxelCount in the immutable
Core world configuration. Non-default Core size participates in plan and season identity. The
default 25 cm value is omitted from the added identity suffix so recorded default SurfaceWorld and
M2 recipe/fixture identities keep their prior form. Water remains fixed for the season; player
carve/fill remains unrestricted. Collision support, clipmap keys and LOD stay in Core voxel units
and derive centimetres at the adapter/mesh boundary.
Floating Islands builds a deterministic candidate feature list per tile using conservative
inclusive bounds. Candidate IDs retain canonical feature order. A worker-scoped context lets density
samples evaluate only the tile's candidate islands and connectors. If bounds cannot prove a feature
irrelevant, it stays in the candidate list; future caves, shafts, pits and passages should use the
same common tile-list builder. The Round 5 audit reports exact full-field/candidate equality and
deliberately removes a feature as a negative control. This API does not change feature planning or
field identity. Air-side pruning uses a conservative lower bound for the version-2 scaled root
ellipsoids and their smooth-min reductions; a geometric AABB distance alone can overstate their
positive density and change hashes outside the visible island bounds. BuildHost compared the staged
owner plan's candidate field against the full scan at every queried sample and restored the Round 4
island aggregate hashes.
The current configuration is per-world. A later per-strate size needs a physical-space field-query
contract at boundaries, transition meshes with deterministic LOD ancestry, edit rasterization that
can touch both resolutions without gaps or double application, and a stable, canonically ordered
strate-ID-to-size map in plan/season identity. Border and LOD hashes must cover both sides; mixed-size
edits and replays must match on every worker count. Until those rules exist, one world size is the
supported contract.
Round 5 measured content evaluation per placement-grid cell and stationary manager update on staged
owner settings. SurfaceWorld uses its known heightfield and made no density queries in the Core
measurement. Floating Islands still uses the exact generic density march; a top-anchor shortcut
measured slower and was reverted. The sampled authored entries produced no placements, so these
figures measure anchor-search work and steady update overhead, not spawn/asset-instancing cost.
See Standalone/Research/WorldAssemblerM3/Round5/REPORT.md for the test matrix, owner-plan hash proof,
and content-placement measurements.
### 8.23 Shared assembler query domains — World Assembler S2 / M3 Round 6
Meshing, far-shell field queries and placement share `ScopedWorldAssemblyFeatureQueriesV1`.
Each family prepares conservative candidates for an inclusive domain, including normal halos.
Scopes borrow immutable recipe/plan pointers from the owning world/snapshot, live on the worker,
restore the previous scope when nested, and fall back to full evaluation outside their domain.
Queues, generation epochs, edit locks, streaming policy and canonical field arithmetic are unchanged.
`candidateEvaluationUseful` is a cost hint resolved once per domain: the measured one-feature
SurfaceWorld and four-island plans prefer the full evaluator. Larger plans use candidates. Validation
can force candidates to prove the bounds even for a small plan; this never changes recipe identity.
The default family contract for caves, shafts, pits, passages and rivers is:
1. Declare conservative inclusive influence bounds, including blend, displacement and warp.
Unknown influence retains the feature. Keep canonical operation order.
2. If remote features can determine exact positive density, provide sound value-side certificates
or evaluate them. Geometric AABBs alone cannot preserve arbitrary density values; island root
ellipsoids retain S1's slope and smooth-min certificate. Optional union-group data belongs to the
family proof, not to the engine's notion of Euclidean distance.
3. Families with a proven air base may declare conservative solid-support bounds. The shared column
union skips only proven air; solid-base caves and unknown support retain their ordinary march.
Preserve the original sampling phase, crossing count, bisection and final interpolation.
4. Audit full-scan versus candidates, halos and outside-domain fallback with equal hashes and a
deliberately removed needed feature before enabling a new proof.
SurfaceWorld's biome-site search remains its bounded local lattice. Cached XY column evaluation
uses finite feature candidates; types 1/3 affect every Z, while types 2/4 are neutral outside their
validated boxes. Shelf source-column queries outside the scope use the full column evaluator.
Core meshing prepares both native families, including cross-strate tiles. FarShells remains derived
repair geometry with its existing connector/stratum filtering; its canonical field calls benefit
from the same scope wherever their samples lie inside the domain.
Content snapshots declare support bounds once. Decoration cells and landmark columns prepare the
shared domain with a gradient halo, and retain exact crossings. Core-off retains its legacy march.
Validation compares complete placement transforms and canonical anchor residuals, and applies then
clears an isolated populated HISM fixture under BuildHost null RHI. These CPU costs do not measure
rendering or GPU upload. See the Round 6 report for results and limits.
### 8.24 Shared native far horizon — World Assembler M3 Round 7
The shared Core tile generator reads each family declaration from the immutable assembly snapshot.
`WorldAssemblyPlanV1::strates` declares SurfaceWorld ground and cap profiles through `Heightfield2D`;
`floatingIslands` declares bounded 3D features and has no planar ground/cap datum. This keeps the
horizon rule in the shared worker path while allowing each family to provide the shape it can prove.
At LOD5 and above, an unedited SurfaceWorld tile builds its ground from the native heightfield on
a globally aligned 128-voxel grid, using the tile step below 128. The cap remains on the 128-voxel
grid so adjacent far levels share samples. Ground and cap are clipped to the tile-Z bounds and to
`cap >= ground`; shared ground vertices replace nearby upward-facing coarse MC ground triangles,
while existing ceiling seam skirts remain. At LOD7/8, a tile that intersects a native SurfaceWorld
profile but intersects no island plan skips the derived stratum/connector repair
shells: the exact native ground and cap already provide its exterior closure. Player-edited tiles
keep the real edited field and repair path at every visible LOD. Islands-only and mixed island tiles
keep the shared 3D field and far-shell path.
For the 3D connector repair path, LOD7/8 use the same 256-voxel axial station/ring spacing. LOD6 and
nearer far-shell work retain 128-voxel stations. This reduces source-field probes while keeping a
continuous repair mesh at the resolution used by the far tile. It does not alter canonical density
arithmetic, feature order, water datum, `Epoch`, `ModsLock`, task ownership, collision policy, or the
Core-off legacy route. All horizon work runs on the resolved immutable worker snapshot.
`TileGenerationStats` exposes classification, XY column, MC meshing, shell and horizon times, repair
field calls, skipped repairs and replaced ground triangles. `farHorizonVersion` is part of the tile
generation recipe and salts far mesh, payload and topology hashes at LOD5+; it does not salt density
or LOD0–LOD3 identities. Increase it when changing far geometry again. Round 7's staged owner checks
measure eight warmed keys per LOD and compare the shared LOD7/8 surface lattice against exact near-field
density roots, including a 100 cm datum-offset negative control. See
`Standalone/Research/WorldAssemblerM3/Round7/REPORT.md` for results and the visual evidence limits.
### 8.25 Continuous planar ceiling — World Assembler S4 / M3 Round 8
The cap remains a family declaration in `WorldAssemblyPlanV1::strates` and is evaluated by the shared
Core heightfield path. `Heightfield2D::farSurfaceCapHeightSubvoxels()` clips only the rendered far
surface to `(topChunkZ + 1) * 32` voxels, the owning strate's upper boundary; it leaves
`Heightfield2D::point()` and its density values unchanged. `capHeightBounds()` reports conservative
bounds for that clipped surface. Evaluator v5 opts into this render-surface contract; older serialized
evaluators keep their recorded cap behavior. The clip joins the exact near density root at the owning
boundary without changing LOD0–3 density hashes. Future families with planar roofs must declare their
cap height and bounds through the family plan/provider contract; the shared tile generator consumes
that declaration. FloatingIslands declares no planar roof, so open sky above a surface island is the
expected view.
For each requested far SurfaceWorld XY column at LOD4–8, desired-tile selection adds cap-height
bands only when they do not overlap an existing or already accepted 3D clipmap tile. The original
clipmap remains a non-overlapping partition. Cap samples use a global XY lattice, are clipped to the
owned strate boundary, tile Z bounds and `cap >= ground`, and share edges across tiles. LOD4 uses a 16-voxel cap step;
LOD5–8 use 128 voxels. Core-off stays on its legacy path, and edits continue through the authoritative
edited field. Runtime material sections are explicitly created/updated from their ground/cap index
ranges so a cap-only far tile cannot rely on an auto-created section being present.
The far recipe version is 2. It salts mesh, payload and topology hashes for LOD4–8; density hashes
and LOD0–LOD3 identities do not change. The owner audit counts requested cap bands and emitted
positive-area bands, compares 128 shared cap points with the near density root at 2 cm tolerance,
checks a 100 cm datum-offset negative control, and traces upward rays from route and camera points
against emitted triangles. Round 8 records these counters, the matched captures, warmed BuildHost
timings, clipmap overlap validation and remaining failures in
`Standalone/Research/WorldAssemblerM3/Round8/REPORT.md`.
The Round 8 owner walk audit emitted every requested cap band at LOD4–8 (385/385), agreed with 128
near roots to 0.0504 cm (2 cm gate), detected a 100 cm offset control, and hit the cap on all 2,520
upward rays from 35 walk/camera points. No route ray missed the cap or clipmap boundary.
The far-cliff fluting is coarse marching-cubes geometry on steep faces: with 16 coarse cells per tile,
LOD7 samples every 256 voxels (64 m at 25 cm) and LOD8 every 512 voxels (128 m). The native ground
replacement removes only upward-facing coarse faces; near-vertical faces remain on that lattice.
LOD7/8 shell repair is skipped for certified SurfaceWorld tiles, the renderer does not cull
backfaces, and the strips predate S4 in S2/S3. Refining all steep faces would multiply far mesh work;
S4 leaves that cost/quality tradeoff open.
## 9. Multiplayer model (listen-server first, dedicated-friendly) ## 9. Multiplayer model (listen-server first, dedicated-friendly)
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a > **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a
@@ -1092,10 +1447,12 @@ density or generate geometry.
The world is a pure function of **(seed, strate layout)** (§8.4). So terrain is reconstructed identically The world is a pure function of **(seed, strate layout)** (§8.4). So terrain is reconstructed identically
on every peer from a tiny amount of shared state — it is **never streamed as geometry over the wire**: on every peer from a tiny amount of shared state — it is **never streamed as geometry over the wire**:
- Replicate the **effective seed + strate layout identity** ONCE (at join). For a cooked season the - Replicate the **effective seed + strate layout identity** ONCE (at join). For a cooked season the
layout is the reviewed manifest, not a pool shuffle: peers must load the same season asset and compare layout is the reviewed season manifest; when Core is enabled with a native SurfaceWorld, the
`AVoxelWorld::GetCurrentSeasonContentHash()` before accepting play. For the legacy path the layout remains reviewed world-assembly digest is also part of `AVoxelWorld::GetCurrentSeasonContentHash()`. Peers
deterministic from the effective seed. Every peer then generates terrain locally; geometry never crosses compare that active content hash before accepting play. With Core disabled the optional assembly
the wire. The transport/RPC that performs this join handshake remains out of tree. does not change the legacy hash. For the legacy path, layout remains deterministic from the
effective seed. Every peer generates terrain locally; geometry never crosses the wire. The
transport/RPC that performs this join handshake remains out of tree.
- The **diff layer is the ONLY non-deterministic terrain state** (§3.9, [[voxelforge-difflayer-threading]]) - The **diff layer is the ONLY non-deterministic terrain state** (§3.9, [[voxelforge-difflayer-threading]])
→ it is the only thing that must sync. Since carving is a minor feature, this traffic is small. → it is the only thing that must sync. Since carving is a minor feature, this traffic is small.
@@ -1117,7 +1474,7 @@ pending).** `AVoxelWorld::RegisterStreamingAnchor(Actor, Policy, RadiusChunks)`
(BlueprintCallable) add an actor to `StreamingAnchors`. `UpdateChunksAroundPosition` prunes dead anchors + (BlueprintCallable) add an actor to `StreamingAnchors`. `UpdateChunksAroundPosition` prunes dead anchors +
detects chunk crossings (rebuilds the desired set when any anchor crosses a level-0 boundary — same cadence detects chunk crossings (rebuilds the desired set when any anchor crosses a level-0 boundary — same cadence
as player movement, coalesced into one rebuild). `AddAnchorDesiredTiles` (inside `BuildDesiredTiles`, after as player movement, coalesced into one rebuild). `AddAnchorDesiredTiles` (inside `BuildDesiredTiles`, after
the player clipmap + sheet ring) folds each anchor's Chebyshev box of **level-0** tiles into the SAME the player clipmap) folds each anchor's Chebyshev box of **level-0** tiles into the SAME
`DesiredStamped`/`DesiredSorted` set (deduped vs the clipmap by stamp) → the existing delta cull releases an `DesiredStamped`/`DesiredSorted` set (deduped vs the clipmap by stamp) → the existing delta cull releases an
anchor's tiles automatically when it moves away / unregisters. Zero cost when no anchors (empty loop). The box anchor's tiles automatically when it moves away / unregisters. Zero cost when no anchors (empty loop). The box
defaults to a THIN shape (its chunk + 1 horizontal ring + 1 chunk below for ground safety, nothing above — defaults to a THIN shape (its chunk + 1 horizontal ring + 1 chunk below for ground safety, nothing above —
+9 -2
View File
@@ -25,11 +25,18 @@ try to run anything.
## Hard rules (these prevent real bugs — verify against code, don't assume) ## Hard rules (these prevent real bugs — verify against code, don't assume)
- **Density sign: negative = solid, positive = air** (MC convention at the mesher). - **Density sign: negative = solid, positive = air** (MC convention at the mesher).
`FVoxelModification::Strength` negative = carve. *The #1 source of confusion.* `FVoxelModification::Strength` negative = carve. *The #1 source of confusion.*
- **Density functions take VOXEL coords, not cm.** Conversions live in `VoxelTypes.h`. - **Density functions take VOXEL coords, not cm.** The legacy plugin field uses 12.5 cm voxels;
the opt-in Core field uses 25 cm voxels and its own tile keys. One legacy 32³ chunk is 4 m.
Conversions live in `VoxelTypes.h` and the Core bridge; do not mix scales in queries, anchors, edits,
or surface lookups.
- **Determinism:** all randomness = hash of (coord, seed, strateIndex). No RNG state — same seed ⇒ - **Determinism:** all randomness = hash of (coord, seed, strateIndex). No RNG state — same seed ⇒
same world. Player edits (diff layer) are the only non-deterministic overlay. same world. Player edits (diff layer) are the only non-deterministic overlay.
- **Core world invariants:** Core density keeps the same sign (negative solid, positive air), but Core
voxel coordinates are 25 cm. Worker-count changes must preserve hashes; Core world edits are
intentional world-version changes and must retain finite, walkable strate entrances and exits.
- **Async safety:** worker tasks only READ Generator/Mesher, must check `bShuttingDown`, and return via - **Async safety:** worker tasks only READ Generator/Mesher, must check `bShuttingDown`, and return via
`ProcessQueue` (must stay `EQueueMode::Mpsc`). `EndPlay` blocks on `ActiveTaskCount → 0`. the result queues — `ProcessQueue` and its `CriticalProcessQueue` / `EditedProcessQueue` /
`NearProcessQueue` siblings (all must stay `EQueueMode::Mpsc`). `EndPlay` blocks on `ActiveTaskCount → 0`.
- **Carry the `Epoch`** through any new async path (stale results are dropped on mismatch). - **Carry the `Epoch`** through any new async path (stale results are dropped on mismatch).
- **Don't "optimize" the perf invariants** in ARCHITECTURE `§8.10` — the `thread_local` box-valid - **Don't "optimize" the perf invariants** in ARCHITECTURE `§8.10` — the `thread_local` box-valid
caches, two-pass MC loop, SSE noise, and clipmap streaming are intentional. caches, two-pass MC loop, SSE noise, and clipmap streaming are intentional.
+473 -226
View File
@@ -5,7 +5,8 @@
> as code is edited, so trust the **symbol name** first and the line as a hint. > as code is edited, so trust the **symbol name** first and the line as a hint.
> >
> Plugin root: `Source/VoxelForge/` · ~8,300 lines of C++ across 25 files. > Plugin root: `Source/VoxelForge/` · ~8,300 lines of C++ across 25 files.
> Comments in the code are mixed **French + English**. UE module = `VoxelForge` (Runtime). > Comments in the code are mixed **French + English**. UE modules: `VoxelForge` and
> `VoxelForgeCore` (Runtime), plus `VoxelForgeEditor` (Editor).
--- ---
@@ -28,7 +29,7 @@ A **density-field voxel terrain** plugin for Unreal Engine, built around undergr
### Core terminology ### Core terminology
| Term | Meaning | | Term | Meaning |
|------|---------| |------|---------|
| Chunk | 32×32×32 voxels (`CHUNK_SIZE`). Voxel = 25 cm (`VOXEL_SIZE`). | | Chunk | 32×32×32 voxels (`CHUNK_SIZE`). Voxel = 12.5 cm (`VOXEL_SIZE`), so a chunk is 4 m. |
| Density | Scalar field. `< 0` solid, `> 0` air, `0` = surface (`IsoLevel`). | | Density | Scalar field. `< 0` solid, `> 0` air, `0` = surface (`IsoLevel`). |
| Strate | Vertical layer of the world, stacked downward. Has its own generator + params. | | Strate | Vertical layer of the world, stacked downward. Has its own generator + params. |
| Generator type | Archetype per strate: `TunnelNetwork`, `FlatPlain`, `CrystalChamber`, `Maze`, `SurfaceWorld`, `VerticalShafts`, `FloatingIslands`, `Underwater`. See §8. | | Generator type | Archetype per strate: `TunnelNetwork`, `FlatPlain`, `CrystalChamber`, `Maze`, `SurfaceWorld`, `VerticalShafts`, `FloatingIslands`, `Underwater`. See §8. |
@@ -54,9 +55,12 @@ A **density-field voxel terrain** plugin for Unreal Engine, built around undergr
▼ ▼
UVoxelGenerator::GetDensityAt(x,y,z) ← THE density entry point UVoxelGenerator::GetDensityAt(x,y,z) ← THE density entry point
│ asks StrateManager which strate/params/generator-type applies │ asks StrateManager which strate/params/generator-type applies
├─ TunnelNetwork → GetDensityWithParams() (rooms+tunnels+ops+worms+seal+passages) ├─ builds the chunk's operator stack once per chunk (VF_BuildOpStackForChunk;
│ editor composer / cooked-season recipe stacks first) → every archetype
│ TunnelNetwork/Underwater → fused evaluator = GetDensityWithParams()
│ └─ VoxelCaveMorphology::BuildChunkCache + EvaluateSDFCached (room/tunnel SDF) │ └─ VoxelCaveMorphology::BuildChunkCache + EvaluateSDFCached (room/tunnel SDF)
├─ FlatPlain / CrystalChamber → GetSlabDensity() (floor+ceiling+columns+seal+passages) │ others → FVoxelOpStack::EvalMC (source → modifiers → structural posts)
├─ + disturbances + structural tail (landing floor, tunnel core, XY seal)
└─ + UVoxelDiffLayer::GetDensityOffset() (player carve/fill) └─ + UVoxelDiffLayer::GetDensityOffset() (player carve/fill)
│ │
▼ FVoxelMeshData (verts/tris/uvs/normals) ▼ FVoxelMeshData (verts/tris/uvs/normals)
@@ -73,17 +77,55 @@ what generator type, and is there a passage/elevator SDF near here?"
Paths relative to `Source/VoxelForge/`. `Public/` = headers, `Private/` = impl. Paths relative to `Source/VoxelForge/`. `Public/` = headers, `Private/` = impl.
### 3.0 Human-scale generation contract (2026-09-06) ### 3.0 Human-scale generation contract (2026-10-01)
`VoxelStrateTypes.h` is now authored against the confirmed player capsule, not against naked voxel `VOXEL_SIZE` is **12.5 cm**; 32³ voxels make each chunk **4 m**. The gameplay capsule remains
counts: one voxel is **0.25 m**, the capsule is **1.36 voxels / 0.34 m radius** and **7.04 voxels / **0.34 m radius / 1.76 m tall** (**2.72 / 14.08 voxels**). The default strate envelope is
1.76 m tall**, and the default strate envelope is **8 chunks / 256 voxels / 64 m**. Core defaults are **16 chunks / 512 voxels / 64 m**; serialized owner strate assets that still say 8 chunks must be
therefore: room diameters **8-20 m**, origin hub **24 m**, room cells **32 m**, round tunnel bores changed to 16. Research-field dimensions are authored in metres and converted at runtime. The live
**3-4 m**, maze cells **16 m** with a **6.25 m nominal bore** (**3.125 m** radius; **3.0625 m** strate generator continues to read voxel-valued parameters from its assets, so those authored
worst-case usable floor after roughness), slab open span **22.4 m**, shaft diameters dimensions become half as large in metres unless their asset values are doubled.
**4-7 m**, island diameters **12-24 m**, and ordinary inter-strate bores **3-4 m**. These are
different derivations, not a global multiplier: passage widths follow the 2.5-4 m walking/fight The intended metre-scale families remain: room diameters **8-20 m**, origin hub **24 m**, room cells
target, room/cell spacing preserves a rock web, and the 64 m vertical envelope reserves space for **32 m**, round tunnel bores **3-4 m**, maze cells **16 m** with a **6.25 m nominal bore** (**3.125 m**
tall rooms, surface sky, and vertical traversal. radius; **3.0625 m** worst-case usable floor after roughness), slab open span **22.4 m**, shaft
diameters **4-7 m**, island diameters **12-24 m**, and ordinary inter-strate bores **3-4 m**. Source
new fixed physical dimensions are authored in metres and converted to voxel counts at the boundary.
The owner's voxel-valued live strate assets are not migrated by C++: unchanged saved values therefore
produce half-size rooms, tunnels, features and noise periods in metres. See the step-3 migration list
in the handoff report for the exact `DA_Settings` fields and values.
**Owner asset migration (no packages were edited):** preserve `DA_Settings` distances by changing
`ViewDistanceXY 32→64`, `ViewDistanceUp/Down 10→20`, `PawnCollisionRadiusChunks 2→4`,
`StrateViewMarginChunks 6→12`, `ClipRadius 6→12`, `RenderDistanceChunks 1536→3072`,
`EdgeSealThickness 128→256 voxels`, decoration radius/near radius `12/6→24/12 chunks`, region
size `8→16 cells`, spacing/far spacing `8→16 voxels`, march step `4→8 voxels`, column depth
`320→640 voxels`, `OriginSpineRadius 28→56 voxels`, `MaxBrushRadius 30→60 voxels`, and
`SkirtCells 1→2`. If the density volume is enabled, set resolution `256→512` and debug radius
`12→24 cells`; it is currently disabled. Keep `MaxClipLevel=6` and zero-valued disabled/unbounded
limits unchanged; double finite world radii and gaps, and multiply finite voxel-volume caps by 8.
The loaded `Crystal Cave` slot in `DA_Strate3` is 64 chunks tall; use 128 chunks to preserve its
former 512 m span. Its voxel-authored live-generator lengths remain half-size until that asset is
re-authored; the editor-only research field is the step-3 target.
Step-3 six-worker scale benchmark (idle machine, `Saved/Research/step3/scale/step3_scale_cacheperf_v1`):
the 519³ field took **7.133834–7.825292 s** across three runs (median **7.347496 s**); the 259³
field took **1.098521–1.194640 s** (median **1.169567 s**). The inclusive sample lattices span
64.75 m and 32.25 m between end samples respectively (the requested approximately 64 m / 32 m
fields). Both runs were deterministic across three trials. This records evaluator time only, not game
streaming. Six-worker 4³-brick proof, sound-bound audit, seam injection, and 1-vs-6-worker hashes
pass. Reusing exact detail-lattice hash corners across adjacent mixed bricks reduced the earlier
step-3 259³ median from 1.502908 s to 1.169567 s; the separate step-2f 600 ms worst-profile timing
bar still does not pass at the finer voxel size.
The opt-in Core route resolves physical voxel size from `AVoxelWorld::CoreVoxelSizeCm` (default
**25 cm**) and keeps **32 voxels per Core tile**. `GetActiveVoxelSizeCm()` is the runtime conversion
point for coordinates, mesh vertices, collision, streaming, LOD, content, water, atmosphere, and
brushes; `WorldConfiguration::voxelSizeMicrons` plus `tileVoxelCount` carries the resolved values
through Core. A nondefault size participates in Core snapshot/season identity, while 25 cm preserves
the recorded default hashes. Core-off continues using the legacy 12.5 cm grid. This is currently a
per-world setting; per-strate sizes still need physical-coordinate sampling, transition stitching,
cross-boundary edit rasterization, per-strate LOD agreement, and a canonically ordered size map in the
hashed identity.
The reachable roughness envelope is `roughness * VOXEL_NOISE_SCALE * 1.5`, or `roughness * 1.875` The reachable roughness envelope is `roughness * VOXEL_NOISE_SCALE * 1.5`, or `roughness * 1.875`
voxels. The new defaults keep it below the relevant feature radius: **0.625** of the minimum voxels. The new defaults keep it below the relevant feature radius: **0.625** of the minimum
@@ -112,33 +154,43 @@ remains `0`, and lateral regions remain gated off.
### 3.1 Module & build ### 3.1 Module & build
| File | Role | | File | Role |
|------|------| |------|------|
| `../../VoxelForge.uplugin` | Plugin manifest. Runtime module `VoxelForge` plus the Editor-only `VoxelForgeEditor` commandlet module. Beta. | | `../../VoxelForge.uplugin` | Plugin manifest. Runtime modules `VoxelForge` and `VoxelForgeCore`, plus the Editor-only `VoxelForgeEditor` commandlet module. Beta. |
| `VoxelForge.Build.cs` | Deps: Core, CoreUObject, Engine, **GameplayTags**, **RealtimeMeshComponent**. | | `VoxelForge.Build.cs` | Deps: Core, CoreUObject, Engine, **GameplayTags**, **RealtimeMeshComponent**, `VoxelForgeCore`. `AVoxelWorld::bUseVoxelForgeCore` opts into the Core runtime route; it defaults off. |
| `Tools/VoxelForgeTest.ps1` | The single guarded test entry point. `-Scenario canonical|owner|probe|perf|tests|surface-fall|crossing|gate-stress|parity|horizon|horizon-render|determinism`, optional `-Build`, `-Assets owner|default`, `-Cvars`, absolute `-Out`, `-Label`, `-HorizonLevel`, `-HorizonRenderDistanceChunks`, and `-SharedDDC`; stages the host/assets, isolates user/DDC/config state, serializes one Unreal launch at a time, enforces the 8 GiB/30-minute guards, and emits compact `result.json`/`summary.txt` plus full logs. Horizon traces report desired/loaded tiles, resident stream geometry, worker seconds, steady wall time, and peak private memory; `horizon-render` captures the owner view; `determinism` compares per-tile hashes across the default CRT path and `-voxel.CrtFma3=0`. | | `../../VoxelForgeCore/VoxelForgeCore.Build.cs` | C++20 runtime module. Reads `Standalone/Core/core_sources.txt`, validates one include shim per canonical source, and fails UBT on missing, extra, or mismatched shims. No unity/PCH; private copied implementation stays under `vfcore::detail`. |
| `../../VoxelForgeCore/Private/Manifest/VFCore_*.cpp` | Eighteen one-source UBT shims that include canonical files under `Standalone/`; they do not copy Core implementation. The API-boundary row includes `Core/src/VoxelForgeCore.cpp` directly. |
| `../../VoxelForgeCore/Private/ModuleEntry/` | Unreal module entry and an isolated `CoreMinimal.h` shim for the actual engine header. |
| `../../Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h` | Standard-library-only public Core API. `VOXELFORGECORE_API` exports the API from the Unreal module and is empty for standalone builds. |
| `../../Standalone/Core/src/FloatingIslandsInternal.h` | Canonical standalone/Core floating-island planner, geometry-versioned field, manifest serializer, influence bounds, capsule-route checks, and thickness audit. |
| `Tools/VoxelForgeTest.ps1` | The single guarded test entry point. `-Scenario canonical|owner|probe|perf|tests|surface-fall|crossing|gate-stress|visual-edit|visual-lod|visual-gap-walk|visual-gap-inject|dig-motion|parity|horizon|horizon-render|determinism|region-eval|step2d|step2e|step2f|step2f-capture|step3-scale|step3-game`, optional `-Build`, `-Assets owner|default`, `-Cvars`, absolute `-Out`, `-Label`, `-HorizonLevel`, `-HorizonRenderDistanceChunks`, `-Step2fProfile=0..7`, `-Step2fCaptureView=inside|wall-closeup`, `-ParityExportSize=32|64|128`, `-ParityExportTile=x,y,z`, `-SharedDDC`, `-SampleDensityStacks`, `-StackSampleIntervalUs`, and `-RegionHashProbe -RegionWorkers N -RegionReferenceHash HASH`; stages the host/assets, isolates user/DDC/config state, serializes one Unreal launch at a time, enforces the 8 GiB/30-minute guards, waits for 6 GiB free RAM before launches, and emits compact `result.json`/`summary.txt` plus full logs. Parity can render its game leg with `-EnableRender` and compares the selected export region against the exact streamed tile hashes. `visual-gap-walk` records camera-frustum surface-point coverage at 6000 cm/s; `visual-gap-inject` proves an early component hide is detected; `dig-motion` logs per-frame vertical velocity, gate, support readiness, and movement mode. `-Cvars @{ 'voxel.TestGapAuditLegacyHandoff'=1 }` replays the old visibility handoff for A/B audit baselines. Horizon traces report desired/loaded tiles, resident stream geometry, worker seconds, steady wall time, and peak private memory; `horizon-render` captures the owner view; `step2f-capture` selects one of eight parameter sets, finds a positive-density camera point and aims at a sampled rock wall for an inside or wall-closeup view, then saves it under `Saved/Research/step2f/captures` (pass the persistent render DDC with `-SharedDDC`); `determinism` compares per-tile hashes across the default CRT path and `-voxel.CrtFma3=0`; `region-eval` compares the live path with the default-off editor chunk program on the 259³ lattice and five-seed probe set; `step2d` retains the earlier independent field; `step2e` retains its earlier benchmark; `step2f` compares frozen step 2e and the coherent field for all eight profiles on the same 259³ ring, applies geometry gates, and checks sound bounds, seams, and 1-vs-6-worker hashes. `-RegionHashProbe -RegionWorkers N -RegionReferenceHash HASH` checks a full-ring hash at another partition count. `-SampleDensityStacks` captures profiles, and `-StackSampleIntervalUs` sets the sampling interval.
| Step-3 scale and game validation | `step3-scale` measures the same 519³ and 259³ six-worker lattices at 12.5 cm. The cached medians are 7.347496 s and 1.169567 s respectively; determinism and brick sound-proof audits pass. `step3-game` routes in-game generation through the brick evaluator and records tile timings, spawn fill, moving frames, and profile-switch rebuilds. The visible D3D12 run measured tile generation p50/p95/max 3.326/31.045/169.867 ms, full-detail L0 fill 12.369 s for 2,765 tiles, and moving-frame p50/p95/p99/max 4.856/8.922/13.090/80.575 ms. Switching profiles rebuilt 5,815 loaded tiles. Both high-resolution screenshot requests were logged, but the harness found only one fresh PNG and it is a blank pale viewport; no lit comparison capture is accepted. Raw output: `Saved/TestHarness/step3_game_visible_final`; scale output: `Saved/Research/step3/scale/step3_scale_cacheperf_v1`. |
| `Public/VoxelForgeModule.h` / `Private/VoxelForgeModule.cpp` | `FVoxelForgeModule` boilerplate (Startup/Shutdown just log). | | `Public/VoxelForgeModule.h` / `Private/VoxelForgeModule.cpp` | `FVoxelForgeModule` boilerplate (Startup/Shutdown just log). |
| `Source/VoxelForgeEditor/VoxelForgeEditor.Build.cs` / `Private/VoxelForgeExploreCommandlet.*` | Editor/commandlet-only `-run=VoxelForgeExplore`; owns bounded render/`VF_` walk/OBJ export orchestration and is not linked by Game/Shipping. | | `Source/VoxelForgeEditor/VoxelForgeEditor.Build.cs` / `Private/VoxelForgeExploreCommandlet.*` | Editor/commandlet-only `-run=VoxelForgeExplore`; schema-v1 owner-asset export check and BuildHost Core actor tests. `-coreworldassembly=PATH` supplies a reviewed manifest transiently to `-coreworldtest=1`; it does not save a data asset. `FStandaloneCoreAdapterScheduler` runs Core work at 1/8/16 workers; `VerifyStandaloneCoreAdapter` checks hashes and walkability. Existing bounded render/`VF_` walk/OBJ orchestration and research options remain editor/commandlet-only and are not linked by Game/Shipping. |
| `Source/VoxelForge/Public/VoxelSettings.h` / `Private/VoxelSettings.cpp` | `UVoxelSettings` owns optional canonical world-assembly JSON and its Core digest. The editor file picker plus `ImportWorldAssemblyManifestJson` imports the reviewed manifest into the cooked settings asset and validates the recipe seed against the effective settings seed. |
| `Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp` | Validates the optional manifest against each resolved `SurfaceWorld` slot (unique strate index and exact top/bottom chunks), then places canonical `world_assembly` JSON and its hash in the Core snapshot. Invalid plans fail Core initialization. |
| `Source/VoxelForgeEditor/Private/VoxelForgeRegionEvalExperiment.*` | Six-worker lattice drivers for step 2c through step 2f. The original path still compares live `GetDensityAt` with the editor chunk program. Research modes assign contiguous batches dynamically so surface-heavy work balances across workers; steps 2e and 2f use 4³ proof bricks and stage each evaluated brick before copying contiguous X rows, while step 2d retains 8³. Each skip requires a macro Lipschitz bound plus the complete final-detail supremum; uncertain bricks retain every 12.5 cm detail sample. Timings split classification, point evaluation, and uniform fill; step 2f compares all profiles and hashes workers 1 and 6. |
| `Public/VoxelRedefinedDensityField.h` / `Private/VoxelRedefinedDensityField.cpp` | Two default-off `WITH_EDITOR` research fields, selected by `voxel.ResearchRedefinedDensityField` (step 2d) and `voxel.ResearchParameterizedCave` (steps 2e/2f). Step 2f retains a metre-scaled 2e baseline for its rejection audit. New profiles use strongly jittered, individually rotated elliptical rooms capped against nearest neighbors and curved cubic-Bezier passages with deterministic centerline and width variation along each edge. Room heights and feature rolls vary by cell; ledges, occasional pillars, stalactites/stalagmites, basin floors, and bounded hanging masses remain features. Point and brick detail sampling share independently rotated 96/48/24-sample bands and a smooth ridge response, preserving 12/6/3 m wavelengths at 12.5 cm per sample. The parameterized mesher evaluates the exact field through prepared 4³ bricks and the same sound skip proof in commandlet and editor-game meshing. In-editor and in-game profile changes invalidate and rebuild all loaded tiles. In an editor game with this field enabled, sealed-solid, out-of-layout, and outer-classifier verdicts are declined without changing their console switches. |
| `Private/Tests/VoxelForgeParameterizedGenerationTest.cpp` | Separate three-seed functional sweep for the step 2f parameterized field; passage landing air/floor brackets and arrival-to-exit walking routes are checked without changing the live-world tests. |
| `Private/Tests/VoxelForgeRedefinedDensityFieldTest.cpp` | Three-seed functional sweep for the research field: authored passage endpoints must bracket open air and solid floor, and each tested strate must have a walkable arrival-to-exit route. |
| `Public/VoxelGeometryHash.h` | Header-only geometry digest shared by commandlet export and runtime tile-hash dump; hashes mesh counts and the vertex/normal/UV/color/triangle arrays with the historical CRC order. | | `Public/VoxelGeometryHash.h` | Header-only geometry digest shared by commandlet export and runtime tile-hash dump; hashes mesh counts and the vertex/normal/UV/color/triangle arrays with the historical CRC order. |
| `Public/VoxelStats.h` / `Private/VoxelStats.cpp` | `stat VoxelForge` DWORD counters for tile classification, skipping, meshing, operator-stack verdicts, and cave-bail diagnosis. The former ambiguous `Cave Bail Not Op Stack` is split into `Sole Slot`, `Boundary Tile`, `No Layout`, and late `Recheck` counters, so each increment names one guard/context. | | `Public/VoxelStats.h` / `Private/VoxelStats.cpp` | `stat VoxelForge` DWORD counters for tile classification, skipping, meshing, operator-stack verdicts, and cave-bail diagnosis. Each `Cave Bail *` counter names one guard; `Cave Bail No Stack` counts slots with no buildable stack (no definition, or a degenerate strate). |
### 3.2 Foundational types — `Public/VoxelTypes.h` (no UClass, everyone includes it) ### 3.2 Foundational types — `Public/VoxelTypes.h` (no UClass, everyone includes it)
| Symbol | Line | Notes | | Symbol | Line | Notes |
|--------|------|-------| |--------|------|-------|
| `CHUNK_SIZE` (32), `CHUNK_SIZE_SQUARED`, `CHUNK_VOLUME` | 19-21 | Chunk dimensions. (64³ tried for fewer draws → reverted: streaming too bursty. fps is fixed render-side instead.) | | `CHUNK_SIZE` (32) | 211 | Chunk dimensions. 64³ would cut draw calls but makes streaming too bursty; fps is fixed render-side instead. |
| `VOXEL_SIZE` (25.0f cm) | 23 | World scale. | | `VOXEL_SIZE` (12.5f cm) | 213 | World scale; one 32³ chunk is 4 m. |
| `EVoxelFace` enum + `GetFaceDirection` / `GetFaceNormal` | 33-61 | 6 cube faces. | | `EVoxelFace` enum | 266 | 6 cube faces. |
| `WorldToChunkCoord` / `WorldToLocalCoord` / `ChunkToWorldPos` | 74-104 | Coord-space conversions (handle negatives via floor/positive-modulo). | | `WorldToChunkCoord` | 287 | World cm → chunk coord (handles negatives via floor). |
| `LocalToIndex` / `IndexToLocal` / `IsValidLocalCoord` | 107-131 | Flat-array 3D↔1D indexing. | | `VoxelMath::IsFiniteFast` / `IsFinite` | 24 / 45 | Exact IEEE bit test (`voxel.FastIsFinite` A/B switch). |
| `VoxelMath::IsFiniteFast` / `IsFinite` | ~24-53 | Exact IEEE bit test (`voxel.FastIsFinite` A/B switch). | | `VoxelMath::DetSinCos` / `DetSin` / `DetCos` | 63 / 181 / 189 | **The only sin/cos allowed where the world is decided** (2026-09-16, `10fd0b6`). Fixed double period reduction and float polynomial, no CRT: `FMath::Sin/Cos` are CRT `sinf/cosf`, whose FMA3 variant is picked by CPU. Pinned by `VoxelForge.Determinism.DetSinCos`; `-voxel.CrtFma3=0` flips the CRT path to prove it. |
| `VoxelMath::DetSinCos` / `DetSin` / `DetCos` | ~63-195 | **The only sin/cos allowed where the world is decided** (2026-09-16, `10fd0b6`). Fixed double period reduction and float polynomial, no CRT: `FMath::Sin/Cos` are CRT `sinf/cosf`, whose FMA3 variant is picked by CPU. Pinned by `VoxelForge.Determinism.DetSinCos`; `-voxel.CrtFma3=0` flips the CRT path to prove it. | | `SmoothStep01` | 306 | 3x²-2x³ — used everywhere for blends. |
| `SmoothStep01` | 140 | 3x²-2x³ — used everywhere for blends. | | `VOXEL_NOISE_SCALE` (1.25f) | 313 | Rescales UE PerlinNoise3D to ~[-1,1]. |
| `VOXEL_NOISE_SCALE` (1.25f) | 147 | Rescales UE PerlinNoise3D to ~[-1,1]. | | `EVoxelTileClass` enum (`Mixed`/`AllSolid`/`AllAir`) | 231 | T1.d verdict. Lives here so `VoxelDensityOp.h` can share it without a UCLASS dependency. A false `AllSolid`/`AllAir` is a HOLE; a false `Mixed` only costs CPU. |
| `EVoxelTileClass` enum (`Mixed`/`AllSolid`/`AllAir`) | — | T1.d verdict. **MOVED here from `VoxelGenerator.h` 2026-07-27** so `VoxelDensityOp.h` can share it without a UCLASS dependency. A false `AllSolid`/`AllAir` is a HOLE; a false `Mixed` only costs CPU. | | `FVoxelMeshData` struct | 365-400 | Mesher output (Vertices/Triangles/UVs/Normals/**Colors**). Plain C++, not USTRUCT. `NumSurfaceVertices` marks the real isosurface prefix before LOD seam skirts are appended; visibility audit witnesses sample only this prefix. `NumSkirtTriangles` counts appended render skirts for collision-cook profiling without changing indices. `Colors` = F6 material masks (R=dominant biome palette, G=slope, B=border blend weight, A=neighbour biome palette). §8.15. |
| `FVoxelMeshData` struct | 157-173 | Mesher output (Vertices/Triangles/UVs/Normals/**Colors**). Plain C++, not USTRUCT. `Colors` = F6 material masks (R=dominant biome palette, G=slope, B=border blend weight, A=neighbour biome palette). §8.15. |
### 3.2b Density operator stack contract — `Public/VoxelDensityOp.h` (plain C++, no UHT) ### 3.2b Density operator stack contract — `Public/VoxelDensityOp.h` (plain C++, no UHT)
**Production path behind a per-strate opt-in** — the stack is built/evaluated by `GetDensityAt` for **The production density path** — `GetDensityAt` builds and evaluates a stack for every strate of
all eight archetypes when `bUseOperatorStack` is enabled, while the legacy `switch` remains the all eight archetypes; there is no other path. See [Docs/archive/OPSTACK-PLAN.md](Docs/archive/OPSTACK-PLAN.md) for the decomposition.
reference path for opt-out strates. See [OPSTACK-PLAN.md](OPSTACK-PLAN.md) for the decomposition.
| Symbol | Notes | | Symbol | Notes |
|--------|-------| |--------|-------|
@@ -159,33 +211,29 @@ manager passage post, **moved here 2026-07-27** so the generator and
the operator stack share ONE copy. The XY helper is a radial smooth ramp in actor-space and is a the operator stack share ONE copy. The XY helper is a radial smooth ramp in actor-space and is a
true no-op when `WorldRadiusVoxels == 0`; `VF_XYEdgeSealForcedMarginOverBox` is the shared sound true no-op when `WorldRadiusVoxels == 0`; `VF_XYEdgeSealForcedMarginOverBox` is the shared sound
proof for `ClassifyBox` and T1.d's global shell skip. `VoxelGenerator.cpp` keeps same-named proof for `ClassifyBox` and T1.d's global shell skip. `VoxelGenerator.cpp` keeps same-named
`static FORCEINLINE` forwarders for the original three helpers; the new edge helper is called `static FORCEINLINE` forwarders for `ApplyBoundarySeal` and `ApplyOriginSpine`; the passage and
directly. **Convention: INTERNAL (positive = solid); `VF_ApplyXYEdgeSealMC` is the output wrapper.** edge helpers are called directly. **Convention: INTERNAL (positive = solid); `VF_ApplyXYEdgeSealMC` is the output wrapper.**
### 3.2d Operator stack — `Public/VoxelDensityOpStack.h` + `Private/VoxelDensityOpStack.cpp` ### 3.2d Operator stack — `Public/VoxelDensityOpStack.h` + `Private/VoxelDensityOpStack.cpp`
⚠️ **Feeds the game, behind a per-strate opt-in** (Phase 1 step 3). `GetDensityAt` builds the stack ⚠️ **This IS the game's generation path.** `GetDensityAt` builds the stack in its per-chunk refetch
in its per-chunk refetch block and evaluates it *instead of* the `switch` only when block for every strate — **all 8 archetypes**: Maze, FlatPlain, CrystalChamber, SurfaceWorld,
`UVoxelStrateManager::UsesOperatorStackForChunk` says so — strate ticked `bUseOperatorStack` **and** VerticalShafts, FloatingIslands, TunnelNetwork, Underwater. An editor composer candidate or a
archetype in the ported list, which is now **all 8 of 8**: Maze, FlatPlain, CrystalChamber, cooked-season recipe supplies its own stack; otherwise (or if that stack cannot be built) the
SurfaceWorld, VerticalShafts, FloatingIslands, TunnelNetwork, Underwater. A strate that has not archetype's native stack is used. A degenerate (zero-height) strate has no stack and is open air.
ticked the box still takes the `switch`, unchanged — the flag is the only thing that switches paths. `UVoxelStrateManager::UsesOperatorStackForChunk` only says whether a slot has a known archetype.
**`ClassifyTile` IS wired now**, for CAVE archetypes only and behind the same per-strate opt-in: **`ClassifyTile`**, for CAVE archetypes, builds the strate's stack through the *same* factory
where it used to `return Mixed` without a call, it builds the strate's stack through the *same* `GetDensityAt` uses (`VF_BuildOpStackForChunk`) and folds `ClassifyBox`. SurfaceWorld and bedrock
factory `GetDensityAt` uses (`VF_BuildOpStackForChunk`) and folds `ClassifyBox`. SurfaceWorld and gaps keep their hand-written proofs — the exact-lattice column test is better than any box bound.
bedrock gaps keep their hand-written proofs — the exact-lattice column test is better than any box Guards, all failing to `Mixed`: one cave slot per tile, no mixed cave/surface/gap tile, a stack on
bound. Guards, all failing to `Mixed`: one cave slot per tile, no mixed cave/surface/gap tile, the *every* chunk the box touches, the params **bit-identical** across every chunk coord the box touches
opt-in true on *every* chunk the box touches, the params **bit-identical** across every chunk coord (blended transition bands make one stack unable to represent the tile — `AUDIT §C2`), a
the box touches (blended transition bands make one stack unable to represent the tile — `AUDIT §C2`), 27-chunk-coord cap, and the disturbances folded in by hand since they are applied after the stack.
a 27-chunk-coord cap, and the disturbances folded in by hand since they are applied after the stack.
Brute-forced end to end by `VoxelForge.OpStack.ClassifyTileSoundness`. Brute-forced end to end by `VoxelForge.OpStack.ClassifyTileSoundness`.
⛔ Never run both paths in one world. **Comparing them IS legitimate now** — the ~1 ULP residue of
AUDIT §C10 is gone since `FPSemantics = Precise`, and all eight equivalence tests compare bit for
bit. They are port-correctness oracles, not fidelity checks: the acceptance bar is §2.6.1 (same seed
⇒ same world on every peer), which does not require resembling the pre-refactor world.
| Symbol | Role | Notes | | Symbol | Role | Notes |
|--------|------|-------| |--------|------|-------|
| `FVoxelOpStack` | — | Ordered move-only list of `TUniquePtr` entries. `Add` snapshots channel metadata; `PrepareChunk` / `EvalInternal` / `EvalMC` / `ClassifyBox` (the fold lets a later forcing post overwrite a dead hypothesis). | | `FVoxelOpStack` | — | Ordered move-only list of `TUniquePtr` entries. `Add` snapshots channel metadata; `PrepareChunk` / `EvalInternal` / `EvalMC` / `ClassifyBox` (the fold lets a later forcing post overwrite a dead hypothesis). |
| `VoxelDensityProfile::BucketFromName` (`Private/VoxelDensityProfile.cpp`) | 710 | Maps operator stack labels to profiler buckets. The sampled game stack attributed 4.86% of resolved leaves here; the linear name scan is now a CRC-keyed candidate lookup with exact string comparison for collision-safe bucket identity. |
| `FVoxelOpStack::ValidateChannelOrder` | — | Assembly/diagnostic-only channel/resource DAG validator. It requires prior producers, rejects write-only clobbers and invalid additive declarations, and requires room/shaft/surface state providers before consumers. It permits the one root SDF identity fold used by Maze. `VoxelForge.OpStack.ChannelDAG` validates all 8 shipping stacks. | | `FVoxelOpStack::ValidateChannelOrder` | — | Assembly/diagnostic-only channel/resource DAG validator. It requires prior producers, rejects write-only clobbers and invalid additive declarations, and requires room/shaft/surface state providers before consumers. It permits the one root SDF identity fold used by Maze. `VoxelForge.OpStack.ChannelDAG` validates all 8 shipping stacks. |
| `FVoxelOpStack::AppendStructuralPost` | 4 | Appends spine → vertical seal → passage tube/landing floor → XY edge seal **in that fixed order**. An author cannot omit or reorder them. The diff layer is NOT here yet — it still lives in `GetDensityAt` after the MC negate, with disturbances. | | `FVoxelOpStack::AppendStructuralPost` | 4 | Appends spine → vertical seal → passage tube/landing floor → XY edge seal **in that fixed order**. An author cannot omit or reorder them. The diff layer is NOT here yet — it still lives in `GetDensityAt` after the MC negate, with disturbances. |
| `VoxelDensityOps::MakeConstantRockSource` | 1 | `Density = BaseDensity`. `ClassifyBox` → **AllSolid**, exact and free. Shared by TunnelNetwork, Maze, VerticalShafts and bedrock gaps. Class is `FConstantFieldSource` (one class, two factories). | | `VoxelDensityOps::MakeConstantRockSource` | 1 | `Density = BaseDensity`. `ClassifyBox` → **AllSolid**, exact and free. Shared by TunnelNetwork, Maze, VerticalShafts and bedrock gaps. Class is `FConstantFieldSource` (one class, two factories). |
@@ -197,7 +245,7 @@ bit. They are port-correctness oracles, not fidelity checks: the acceptance bar
| `VoxelDensityOps::MakeSlabVoidSource` | 1 | Floor surface + ceiling surface → void field. **XY-pure** since §3.1, which is what gives it an **exact `ClassifyBox` with no sampling**: the proved FBM supremum `1.5` bounds both surfaces into known Z bands. Serves FlatPlain **and** CrystalChamber. | | `VoxelDensityOps::MakeSlabVoidSource` | 1 | Floor surface + ceiling surface → void field. **XY-pure** since §3.1, which is what gives it an **exact `ClassifyBox` with no sampling**: the proved FBM supremum `1.5` bounds both surfaces into known Z bands. Serves FlatPlain **and** CrystalChamber. |
| `VoxelDensityOps::MakeGridColumnMod` | 3 | Infinite-height cylinders on a world grid, 3×3 cell memo. Adds solid only ⇒ `FillOnly` when a column reaches the box, `Identity` otherwise — and that `Identity` is what lets the source's `AllAir` verdict survive. | | `VoxelDensityOps::MakeGridColumnMod` | 3 | Infinite-height cylinders on a world grid, 3×3 cell memo. Adds solid only ⇒ `FillOnly` when a column reaches the box, `Identity` otherwise — and that `Identity` is what lets the source's `AllAir` verdict survive. |
| `FShaftFieldSource` (internal) | 1 | VerticalShafts' memoised inner 3×3 shaft/capsule source. Each rebuild uses a direct-indexed wider halo (default 9×9 tree-emission / 15×15 roll) so every emitted shaft has its complete 5×5 parent window plus capsule reach; the nearest strictly more-central shaft in ±2 cells is the deterministic parent, with a deterministic lower-origin ±3 fallback and a direct `(0,0)` spine fallback. Thus every shaft has a provable path to the spine. Existing `VoxelHash::Pair`/`Spacing*1.6` links remain additive texture/loops. Tree and spine capsules use a radius strictly above `sup|FBM|=1.5`, and tree Z is chosen in a ledge-free band; the structural post still owns the column and the ledge modifier keeps its mathematical axis boundary open. The source publishes a conservative interval covering both shafts and connector capsules; it does not answer for the ledge or converter. | | `FShaftFieldSource` (internal) | 1 | VerticalShafts' memoised inner 3×3 shaft/capsule source. Each rebuild uses a direct-indexed wider halo (default 9×9 tree-emission / 15×15 roll) so every emitted shaft has its complete 5×5 parent window plus capsule reach; the nearest strictly more-central shaft in ±2 cells is the deterministic parent, with a deterministic lower-origin ±3 fallback and a direct `(0,0)` spine fallback. Thus every shaft has a provable path to the spine. Existing `VoxelHash::Pair`/`Spacing*1.6` links remain additive texture/loops. Tree and spine capsules use a radius strictly above `sup|FBM|=1.5`, and tree Z is chosen in a ledge-free band; the structural post still owns the column and the ledge modifier keeps its mathematical axis boundary open. The source publishes a conservative interval covering both shafts and connector capsules; it does not answer for the ledge or converter. |
| `VoxelDensityOps::BuildSlabStack` | — | 6 ops, **no branch on archetype**: FlatPlain and CrystalChamber differ only in defaults, exactly as `GetSlabDensity` already had it. 8 archetypes → 7. | | `VoxelDensityOps::BuildSlabStack` | — | 6 ops, **no branch on archetype**: FlatPlain and CrystalChamber differ only in defaults. |
| `FSurfaceColumnSource` (internal) | 1 | The bridge between the two spaces: consumes the ground + sky-cap **height** stacks and produces density. `IsXYPure()` **false** — the heights are XY-pure, a distance to them never is. Owns a **six-box spatial LRU** of direct-indexed per-column cells, keyed by `PrepareChunk` on `(StrateBottomWorldZ, LayoutVersion, Seed, ParamsFingerprint)` so it is **shared down the whole vertical strate stack**, exactly like `GSurfColCache`. Six 81×81 boxes preserve hot columns across interleaved regions at roughly 0.79 MiB TLS before padding (more memory, fewer whole-cache recenter/recompute misses). Fractional XY remains direct-compute. `FSurfaceColumn` is deliberately trivial: all five outputs are written before the cache's `Computed` publish, so removing its default initialization removes a TLS constructor leaf without changing a value. | | `FSurfaceColumnSource` (internal) | 1 | The bridge between the two spaces: consumes the ground + sky-cap **height** stacks and produces density. `IsXYPure()` **false** — the heights are XY-pure, a distance to them never is. Owns a **six-box spatial LRU** of direct-indexed per-column cells, keyed by `PrepareChunk` on `(StrateBottomWorldZ, LayoutVersion, Seed, ParamsFingerprint)` so it is **shared down the whole vertical strate stack**, exactly like `GSurfColCache`. Six 81×81 boxes preserve hot columns across interleaved regions at roughly 0.79 MiB TLS before padding (more memory, fewer whole-cache recenter/recompute misses). Fractional XY remains direct-compute. `FSurfaceColumn` is deliberately trivial: all five outputs are written before the cache's `Computed` publish, so removing its default initialization removes a TLS constructor leaf without changing a value. |
| `VoxelDensityOps::BuildSurfaceStack` | — | SurfaceWorld, complete: column + overhang + 4 structural, plus biome blending when `PerBiomeParams` is non-empty. Takes ownership of an `IVoxelBiomeField`. | | `VoxelDensityOps::BuildSurfaceStack` | — | SurfaceWorld, complete: column + overhang + 4 structural, plus biome blending when `PerBiomeParams` is non-empty. Takes ownership of an `IVoxelBiomeField`. |
| `VoxelDensityOps::BuildVerticalShaftStack` | — | 9 ops, and **three are Maze's reused unchanged** (`ConstantRock`, `SdfRoughness`, `SdfCarve`) with different tuning (freq 0.1 vs 0.12, window `rough+4` vs `R+rough+2`). Passes the runtime spine radius into `FShaftFieldSource`, which mirrors the generator's connector-only origin endpoint and roughness-safe spine capsule. The measured proof of `OPSTACK-PLAN §2.5`'s reuse claim. | | `VoxelDensityOps::BuildVerticalShaftStack` | — | 9 ops, and **three are Maze's reused unchanged** (`ConstantRock`, `SdfRoughness`, `SdfCarve`) with different tuning (freq 0.1 vs 0.12, window `rough+4` vs `R+rough+2`). Passes the runtime spine radius into `FShaftFieldSource`, which mirrors the generator's connector-only origin endpoint and roughness-safe spine capsule. The measured proof of `OPSTACK-PLAN §2.5`'s reuse claim. |
@@ -213,7 +261,7 @@ bit. They are port-correctness oracles, not fidelity checks: the acceptance bar
| `FRoomGraphSource::LocalParams()` | — | **The per-room op override** (DECOMPOSITION §2's "no clean home"). Strate params + the nearest room's `UVoxelTerrainOpDefinition::ApplyTo`, memoised once per voxel and read by eleven modifiers. One op owns the shared state, the rest read it — the same pattern as `FOverhangShelfMod` ← `FSurfaceColumnSource`. The detail consumers use the propagated interval for their shared near/far gate; unknown remains conservative, and the per-room parameter override is never used to claim an unproved box identity. | | `FRoomGraphSource::LocalParams()` | — | **The per-room op override** (DECOMPOSITION §2's "no clean home"). Strate params + the nearest room's `UVoxelTerrainOpDefinition::ApplyTo`, memoised once per voxel and read by eleven modifiers. One op owns the shared state, the rest read it — the same pattern as `FOverhangShelfMod` ← `FSurfaceColumnSource`. The detail consumers use the propagated interval for their shared near/far gate; unknown remains conservative, and the per-room parameter override is never used to claim an unproved box identity. |
| `FIslandBlobSource` (internal) | 1 | Hash-placed tapered flat-top blobs, `SmoothMin`'d, in a **domain-warped XY frame** (the warp stays inside the op — see the deviation note vs DECOMPOSITION §7). SDF channel only. The source publishes an interval covering the full authored radius envelope (`max(MinRadius, MaxRadius)`), the two-axis warp·**√2** envelope, top surface and SmoothMin dip; the fill converter makes its own decision. **No lower Z bound exists** — a hairline thread of matter hangs below each island down its axis, so only the TOP may reject. | | `FIslandBlobSource` (internal) | 1 | Hash-placed tapered flat-top blobs, `SmoothMin`'d, in a **domain-warped XY frame** (the warp stays inside the op — see the deviation note vs DECOMPOSITION §7). SDF channel only. The source publishes an interval covering the full authored radius envelope (`max(MinRadius, MaxRadius)`), the two-axis warp·**√2** envelope, top surface and SmoothMin dip; the fill converter makes its own decision. **No lower Z bound exists** — a hairline thread of matter hangs below each island down its axis, so only the TOP may reject. |
| `VoxelDensityOps::BuildFloatingIslandStack` | — | 8 ops, and **the stack runs backwards**: void source + fill instead of rock source + carve, using the *same* classes with the opposite sign. Only the blob source is new. Reuse by **inversion** — a stronger result than reuse by identity, since it says the abstract axis (the density sign) is the right one. | | `VoxelDensityOps::BuildFloatingIslandStack` | — | 8 ops, and **the stack runs backwards**: void source + fill instead of rock source + carve, using the *same* classes with the opposite sign. Only the blob source is new. Reuse by **inversion** — a stronger result than reuse by identity, since it says the abstract axis (the density sign) is the right one. |
| `VoxelDensityOps::BuildMazeStack` | — | The 8-op Maze stack (4 structural posts included). Its lattice source mirrors the origin-directed tree + capped loops and rebuilds the 2×2×2 lower-node window once per cell; the per-voxel loop only evaluates cached capsules. If this ever becomes one op, the refactor failed its own test (§2.5). Callers must skip it on a **degenerate strate** (top−bottom ≤ 0): `GetMazeDensity` early-outs to air there and the stack has no such early-out by design — `GetDensityAt` falls back to the `switch`. | | `VoxelDensityOps::BuildMazeStack` | — | The 8-op Maze stack (4 structural posts included). Its lattice source mirrors the origin-directed tree + capped loops and rebuilds the 2×2×2 lower-node window once per cell; the per-voxel loop only evaluates cached capsules. If this ever becomes one op, the refactor failed its own test (§2.5). Callers must skip it on a **degenerate strate** (top−bottom ≤ 0): the stack has no such early-out by design — `VF_BuildOpStackForChunk` refuses it and `GetDensityAt` returns air. |
#### Lateral region parent (Tier 4d) #### Lateral region parent (Tier 4d)
@@ -252,29 +300,32 @@ is a **column** property, or one opaque op (`OPSTACK-PLAN §2.5`'s failure mode)
| `VoxelHeightOps::BuildSurfaceHeightStack` | 5 ops in `ComputeSurfaceTerrainZ`'s order — structural → cliff → terrace → layer lines → beach. **Order is not negotiable.** | | `VoxelHeightOps::BuildSurfaceHeightStack` | 5 ops in `ComputeSurfaceTerrainZ`'s order — structural → cliff → terrace → layer lines → beach. **Order is not negotiable.** |
### 3.3 Chunk identity ### 3.3 Chunk identity
`VoxelChunk.h` (the old `FVoxelChunk` coord wrapper) was DELETED — dead since the tile Tile identity lives in `FVoxelTileKey` (VoxelTypes.h:326).
redesign; tile identity lives in `FVoxelTileKey` (VoxelWorld.h).
### 3.4 Settings — `Public/VoxelSettings.h` ### 3.4 Settings — `Public/VoxelSettings.h`
`UVoxelSettings : UPrimaryDataAsset` — the single tuning asset assigned on `AVoxelWorld`. `UVoxelSettings : UPrimaryDataAsset` — the single tuning asset assigned on `AVoxelWorld`.
| Group | Fields (line) | | Group | Fields (line) |
|-------|---------------| |-------|---------------|
| Streaming | `ViewDistanceXY=16`, `ViewDistanceUp/Down=5`, `MaxConcurrentTasks=16`, `MaxMeshAppliesPerFrame=4` (defaults — actual values live on the data asset) | | Streaming | `ViewDistanceXY=16`, `ViewDistanceUp/Down=5`, `MaxConcurrentTasks=16`, `MaxMeshAppliesPerFrame=4`, `PawnCollisionRadiusChunks=1` (defaults — actual values live on the data asset; the collision radius is independent from the render clipmap) |
| Clipmap | `ClipRadius`, `MaxClipLevel=5`, `FullResClipLevels`, `CoarseTileCells`, `RenderDistanceChunks=768` (measured ship defaults; custom horizontal reach: the outermost shell keeps generating until it covers this many chunks; 0 = off), `bFarSheetRing=false` by default, plus `FarSheetSpanLevels` (F18 — the optional render-distance ring streams per-surface SHEETS: level MaxClipLevel+span heightfield tiles instead of MC, see `GenerateSheetMesh`), skirts, `LODOctaveDrop` (T2.b octave drop on coarse tiles — 0 = off/byte-identical). Existing serialized data assets retain their authored values until deliberately migrated. | | Clipmap | `ClipRadius`, `MaxClipLevel=5`, `FullResClipLevels`, `CoarseTileCells`, `RenderDistanceChunks=768` (measured ship defaults; custom horizontal reach: the outermost shell keeps generating until it covers this many chunks; 0 = off), skirts, `LODOctaveDrop` (T2.b octave drop on coarse tiles — 0 = off/byte-identical). Existing serialized data assets retain their authored values until deliberately migrated. |
| World bounds | `WorldRadiusVoxels=8192` and `EdgeSealThickness=64` (actor-space XY radial shell; radius 0 = true legacy no-op) | | World bounds | `WorldRadiusVoxels=8192` and `EdgeSealThickness=64` (actor-space XY radial shell; radius 0 = true legacy no-op) |
| Lighting | `bEnableDensityVolume` + DensityVolume* tunables (§3.11 density clipmap / mini-sun shadows) | | Lighting | `bEnableDensityVolume=false` + DensityVolume* tunables (§3.11 density clipmap / mini-sun shadows). The disabled default creates no volume object, fill thread, uploads, or Tick updates; terrain MIDs receive neutral `VolP0..4=black`, `VolP5=(0,25,0)` (`z` enable is 0), `VolP6..9=black`, and `VolTex0..2=null`. |
| Rendering | `VoxelMaterial` (61) | | Rendering | `VoxelMaterial` (377) |
| Strates | Optional cooked `Season`; effective seed/spine/radius/season accessors read it when assigned. Otherwise `Seed`, `CurrentSeason=1`, `StratePool`, `FixedStrates`, and `TotalStrates=10` retain the authored path. | | Strates | Optional cooked `Season`; effective seed/spine/radius/season accessors read it when assigned. Otherwise `Seed`, `CurrentSeason=1`, `StratePool`, `FixedStrates`, and `TotalStrates=10` retain the authored path. |
| Carving budget | `MaxModifications=0` (97), `MaxBrushRadius=15` (102), `MaxTotalVolume=0` (107). 0 = unlimited. | | Carving budget | `MaxModifications=0` (449), `MaxBrushRadius=15` (454), `MaxTotalVolume=0` (459). 0 = unlimited. |
### 3.5 World orchestrator — `Public/VoxelWorld.h` + `Private/VoxelWorld.cpp` ### 3.5 World orchestrator — `Public/VoxelWorld.h` + `Private/VoxelWorld.cpp`
`AVoxelWorld : AActor` — owns everything, drives streaming. Also `FChunkResult` struct `AVoxelWorld : AActor` — owns everything, drives streaming. Also `FChunkResult` struct
(VoxelWorld.h:35) = async task payload (coord, chunk, meshdata, LOD, **Epoch**, request timing, (VoxelWorld.h:99) = async task payload (coord, chunk, meshdata, LOD, **Epoch**, request timing,
worker-built stream geometry counts/bytes, desired-set epoch and obsolete/cancellation state). worker-built stream geometry counts/bytes, desired-set epoch and obsolete/cancellation state). Explicit
visual-gap audits may add 24 sparse pre-skirt surface witnesses per non-empty tile; normal streaming
does not capture or retain them.
**Owned objects (UPROPERTY):** `Settings`, `Generator`, `Mesher`, `StrateManager`, **Owned objects (UPROPERTY):** `Settings`, `Generator`, `Mesher`, `StrateManager`,
`DiffLayer` (VoxelWorld.h:55-78). **Storage:** `Chunks` map, `ChunkMeshes` map, `DiffLayer`, `ContentManager`, `AtmosphereManager`, `DensityVolume` (VoxelWorld.h:188-228).
`ChunkLODs`, `ProcessQueue` (TQueue), `PendingChunkCoord` (TSet) (VoxelWorld.h:85-312). **Storage:** `LoadedTiles` (TSet), `TileComponents` map + `TileComponentPool` (VoxelWorld.h:260-308),
the four MPSC result queues `CriticalProcessQueue` / `EditedProcessQueue` / `NearProcessQueue` /
`ProcessQueue`, and `PendingTiles` (TSet) (VoxelWorld.h:908-912).
**Async state:** `bShuttingDown`, `ActiveTaskCount` (atomics), `GenerationEpoch`, and the **Async state:** `bShuttingDown`, `ActiveTaskCount` (atomics), `GenerationEpoch`, and the
game-thread `PendingTileCancellation` map whose per-request atomic flags are read-only on workers. game-thread `PendingTileCancellation` map whose per-request atomic flags are read-only on workers.
@@ -293,45 +344,58 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error**
| Method | .cpp line | Role | | Method | .cpp line | Role |
|--------|-----------|------| |--------|-----------|------|
| `AVoxelWorld()` ctor | 12 | Enables Tick. | | `AVoxelWorld()` ctor | 176 | Enables Tick. |
| `RegenerateAllChunks()` | 21 | Bumps epoch, unloads all → Tick reloads. CallInEditor button. | | `RegenerateAllChunks()` | 530 | Bumps epoch, unloads all → Tick reloads. CallInEditor button. |
| `ValidateDeterminism()` | — | **F2 CallInEditor button (PIE)**: re-samples boundary points under left- vs right-chunk cache warm-ups + a same-alignment repeat; any non-zero delta = window-invariance regression (§8.4). Run after every "bit-identical" hot-path refactor. | | `ValidateDeterminism()` | — | **F2 CallInEditor button (PIE)**: re-samples boundary points under left- vs right-chunk cache warm-ups + a same-alignment repeat; any non-zero delta = window-invariance regression (§8.4). Run after every "bit-identical" hot-path refactor. |
| `GetMaxConcurrentTasks()` | — | T2.d — asset `MaxConcurrentTasks` capped to logical cores − 2 (all three budget checks use it). | | `GetMaxConcurrentTasks()` | — | T2.d — asset `MaxConcurrentTasks` capped to logical cores − 2 (all three budget checks use it). |
| `PostEditChangeProperty` | 45 | Editor live-edit hook. | | `GetActiveGenerationTaskCount()` | — | Normal mode returns the existing pending count. With `voxel.Step1PrequeueVisibleLOD0>0`, completed held results are excluded from active worker admission so a full render corpus can be generated before the upload release. |
| `OnObjectModifiedInEditor` | 58 | Regenerates when a strate asset is edited (if `bLiveEditStrates`). | | `PostEditChangeProperty` | 1000 | Editor live-edit hook. |
| `EndPlay` | 140 | Sets `bShuttingDown`, **waits for `ActiveTaskCount`→0**, unbinds delegate. | | `OnObjectModifiedInEditor` | 1022 | Regenerates when a strate asset is edited (if `bLiveEditStrates`). |
| `BeginPlay` | 177 | Constructs Generator/Mesher/StrateManager/DiffLayer, wires services, seeds. | | `EndPlay` | 1118 | Sets `bShuttingDown`, **waits for `ActiveTaskCount`→0**, unbinds delegate. |
| `Tick` | 220 | `UpdateChunksAroundPosition(player, heading)` + `ProcessPendingChunks()`; the clean LOD0 latency summary is flushed at `EndPlay`. | | `BeginPlay` | 1576 | Constructs Generator/Mesher/StrateManager/DiffLayer, wires services, seeds. Creates `UVoxelDensityVolume` only when `bEnableDensityVolume` is true; otherwise seeds terrain MIDs with the disabled neutral parameters and null volume textures. Explicitly parses command-line profiling switches and the `voxel.TestFullResClipLevels` measurement override because headless module CVar startup ordering can otherwise leave the authored/default value active. |
| `GetPlayerPosition` | 231 | Pawn position or zero. (`GetLODForChunk`/`LODToStep`/`IsChunkInRange` removed — dead since the clipmap.) | | `RebuildStrates` | 724 | Reinitializes the volume only when enabled; when disabled, shuts down and clears any existing volume and reapplies neutral terrain material params. |
| `ProcessPendingChunks` | 301 | Drains ProcessQueue under per-frame budget; reads the per-request cancellation token before removing its map entry, drops obsolete/aborted results, then applies each live result via `ApplyTileResult`. | | `Tick` | 5715 | `UpdateChunksAroundPosition(player, heading)` + `ProcessPendingChunks()`; the clean LOD0 latency summary is flushed at `EndPlay`. Density-volume update/material work is inside the non-null volume branch, so it is absent when disabled. |
| `GetPlayerPosition` | 4831 | Pawn position or zero. |
| `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. |
| `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 critical, edited, near, then ordinary result queues under the per-frame budget; reads the per-request cancellation token before removing its map entry, drops obsolete/aborted results, then applies each live result via `ApplyTileResult`. Collision profiling timestamps the result's arrival here to separate worker completion from game-thread apply wait. Step 1's `voxel.Step1PrequeueVisibleLOD0` holds completed results until the requested non-empty L0 count is ready, then releases them through this same drain and emits the `VFStep1UploadCorpusReleased` CSV event for UE's delayed CSV capture. |
| `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). |
| `GenerateTileResult` | — | **Shared worker-side gen** for one tile (async `LoadTile` task + sync `SyncRemeshTile`): [ClassifyTile (T1.d) + exact validation, only when `voxel.OuterClassifierMode=1`; **default 0 since 2026-09-13**] → `GenerateMesh`/`GenerateSheetMesh` → `BuildTileStreamSet`; records vertex count and resident stream allocation bytes for the horizon trace. Reads Generator/Mesher only → safe on a worker or the game thread; fills `FChunkResult`, no enqueue. | | `GenerateTileResult` | — | **Shared worker-side gen** for one tile (async `LoadTile` task + sync `SyncRemeshTile`): sealed-solid proof, out-of-layout air proof, optional ClassifyTile (T1.d) + exact validation only when `voxel.OuterClassifierMode=1`; **default 0 since 2026-09-13** → `GenerateMesh` → `BuildTileStreamSet`; records vertex count and resident stream allocation bytes for the horizon trace. Density capture is requested only when the enabled volume can use that L0 tile; with the volume off, neither proof nor classifier is bypassed for capture. The explicit gap audit may request sparse surface witnesses, otherwise none are retained. Near the player it can capture the exact pre-edit L0 lattice into the result; a cache-hit sync remesh passes an edited per-call shared lattice. Reads Generator/Mesher only → safe on a worker or the game thread; fills `FChunkResult`, no enqueue. |
| `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 (`GenerateTileResult` + `ApplyTileResult` inline, Cells=CHUNK_SIZE/Step=1 + strate band, no capture). Used for the tile under the brush centre so a carve is visible THIS frame; one full-res gen on the game thread. | | `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. |
| `UpdateChunksAroundPosition` | 362 | Builds desired set, cancels pending keys that left it, sorts from the **true pawn position**, submits the occupied/support LOD0 tile and the next tile along the pawn heading as an absolute `BackgroundHigh` prefix, then loads/unloads and handles LOD changes. **Delta cull**: `BuildDesiredTiles` returns the LEAVERS (stamped `DesiredStamped` map, one sweep) — only those + `TransitionHold` are considered per crossing, not every loaded tile. `BuildDesiredTiles` also applies `RenderDistanceChunks`: the outermost shell widens to cover the distance — as level-MaxClipLevel MC tiles, or (F18 `bFarSheetRing`) as a SHEET ring at level MaxClipLevel+span (covered-check vs the MaxClipLevel box; `VF_OuterShell` shared with `IsTileInClipRange` so the cull sees the same horizon), dz pre-clamped to the vertical band. **§9.3 anchors:** also prunes dead `StreamingAnchors` + detects their chunk crossings → rebuilds the desired set when an anchor moves/(un)registers (`bAnchorsMoved`/`bForceDesiredRebuild`), same cadence as player movement. §8.10. | | `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. |
| `AdvanceHeadlessCollisionGateStressTest` / `ObserveHeadlessCollisionGateStressTest` / `MaybeFinishHeadlessCollisionGateStressTest` | — | Explicit `-voxel.TestCollisionGateStress=1` harness only. The `GetDensityAt` column at capsule feet defines the local air/solid surface; 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; support submission/readiness, travel, and holds are diagnostics. Result logs include the terminal density column and the first crossing below spawn height. | | `UpdateChunksAroundPosition` | 8603 | Builds desired set, cancels pending keys that left it, sorts from the **true pawn position**, submits the occupied/support LOD0 tile and the next tile along the pawn heading as an absolute `BackgroundHigh` prefix, then loads/unloads and handles LOD changes. **Delta cull**: `BuildDesiredTiles` returns the LEAVERS (stamped `DesiredStamped` map, one sweep) — only those + `TransitionHold` are considered per crossing, not every loaded tile. `BuildDesiredTiles` also applies `RenderDistanceChunks`: the outermost shell widens to cover the distance as level-MaxClipLevel MC tiles (`VF_OuterShell` shared with `IsTileInClipRange` so the cull sees the same horizon), dz pre-clamped to the vertical band. **§9.3 anchors:** also prunes dead `StreamingAnchors` + detects their chunk crossings → rebuilds the desired set when an anchor moves/(un)registers (`bAnchorsMoved`/`bForceDesiredRebuild`), same cadence as player movement. §8.10. |
| `BuildDesiredTiles` | ~722 | Builds `DesiredSorted`+`DesiredStamped` (player clipmap shells + F18 sheet ring) then `AddAnchorDesiredTiles()` before the leaver sweep; it orders from the true local pawn position and builds `CriticalDesiredTiles` for the occupied/support and heading tiles. Also rebuilds `DesiredTransitionDescendants` (ancestor → desired descendants) for the reconcile pass. | | `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. |
| `VoxelClipmapDesiredTiles::Build` / `AlignedRingBounds` (`Private/VoxelClipmapDesiredTiles.h`) | — | The pure clipmap selector. **Every finer ring snaps outward to a child-pair boundary** (`AlignedRingBounds`, alignment 2; the last MC ring aligns to the sheet grouping width), so a coarser tile is wholly inside or wholly outside the finer ring: **no two desired tiles of different levels overlap** (a 7-wide odd ring used to leave one half-covered coarse tile per axis side, drawn over the fine one: 635 pairs at the owner config). Cost: LOD0 ring 343 → 512 tiles. Guarded by `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage`. Anchor tiles appended later are outside this guarantee. | | `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. |
| `ReconcileReadyTransitionVisibility` | — | Visibility-only pass while streaming is unsettled: hides a retired tile once all desired tiles over its footprint are loaded, reveals new tiles once no visible retired tile overlaps them. **Indexed** (dyadic ancestors + `DesiredTransitionDescendants`, visible retired tiles collected once per pass): moving p95 32 ms → 0.31 ms. `voxel.MeasureReadyTransitionVisibility=1` logs per-frame samples. | | `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. |
| `RequestCollisionGateSupportTile` / `UpdatePawnCollisionGate` | — | The gate submits the pawn's level-0 support tile **immediately** as `CollisionCritical`, even before the first desired set exists (the desired rebuild then adopts the same pending key). The gate holds downward Z only, with **no timeout** (the old 2 s release fired at every startup, while the game thread applied nothing for ~2.2 s). | | `IsPawnCapsuleOverlappingPendingFloor` / `UpdatePawnCollisionGate` | 4987 / 5195 | Uses the current-to-next-step swept capsule bounds to start a hold only when the pending support tile is about to intersect the pawn. When current support is ready, it also requests and checks the next-step support tile before a horizontal boundary crossing. Character holds use gravity-free `MOVE_Flying` with horizontal input live, then restore movement mode, gravity, and saved downward speed when collision is ready. |
| `AddAnchorDesiredTiles` | — | **§9.3 multi-anchor:** folds each `FVoxelStreamingAnchor`'s thin level-0 box (`XYRadiusChunks`/`ZBelowChunks`/`ZAboveChunks`) into the SAME desired set (deduped by stamp) so AI/remote players keep collision loaded around them; delta cull releases them on move/unregister. **§9.4:** a tile only a CollisionOnly anchor wants (clipmap didn't stamp it) → `CollisionOnlyTiles` → hidden at apply. Zero cost when no anchors. | | `VoxelClipmapDesiredTiles::Build` / `AlignedRingBounds` (`Private/VoxelClipmapDesiredTiles.h`) | — | The pure clipmap selector. **Every finer ring snaps outward to a child-pair boundary** (`AlignedRingBounds`, alignment 2; the outermost ring needs none), so a coarser tile is wholly inside or wholly outside the finer ring: **no two desired tiles of different levels overlap** (an unaligned 7-wide odd ring leaves one half-covered coarse tile per axis side, drawn over the fine one: 635 pairs at the owner config). Cost: LOD0 ring 343 → 512 tiles. Guarded by `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage`. Anchor tiles appended later are outside this guarantee. |
| `VF_AppendTouchingAncestors` / `ReconcileReadyTransitionVisibility` / `LogReadyTransitionVisibilityMeasure` | 5041 / 7939 / 5599 | Boundary-safe visibility handoff: a desired tile sharing a closed face/edge with a retired tile counts as a replacement, so the old surface remains visible until the boundary neighbor is loaded. `NeedsHold` uses the same contact-inclusive readiness. Contact does not imply volumetric overlap; overlap audit remains strict. **Indexed** (dyadic ancestors + `DesiredTransitionDescendants`, visible retired tiles collected once per pass): moving p95 32 ms → 0.31 ms. `voxel.MeasureReadyTransitionVisibility=1` logs per-frame game-thread time, sampled component counts, and 128-component count buckets. |
| `IsTileCollisionDesired` / `ReconcileCollisionDesiredTiles` / `ApplyMeshToTile` | — | Collision is independent of rendering: the player's level-0 `PawnCollisionRadiusChunks` box, each anchor's authored box, and the gate's retained support column union into `CollisionDesiredTiles`. Level-0 meshes outside that set remain visible with `NoCollision` and do not submit an RMC cook. Entering the set queues a remesh for already-loaded render tiles; leaving disables their component immediately; a late cook cannot restore readiness outside the set. |
| `VoxelCollisionDesiredTiles::AddBox` (`Private/VoxelCollisionDesiredTiles.h`) | — | Shared pure box selector for the pawn collision radius and anchor extents; tested for pawn radius bounds and an independent anchor box. |
| `RequestCollisionGateSupportTile` / `UpdatePawnCollisionGate` | — | The gate submits the pawn's level-0 support tile **immediately** as `CollisionCritical`, even before the first desired set exists, and retains up to three more support-column tiles across desired-set rebuilds. A previously rendered no-collision tile is remeshed before the gate can accept it. The gate holds only at swept contact, keeps horizontal input live, has no timeout, and restores movement mode, gravity and saved fall speed on release. |
| `AddAnchorDesiredTiles` | — | **§9.3 multi-anchor:** folds each `FVoxelStreamingAnchor`'s thin level-0 box (`XYRadiusChunks`/`ZBelowChunks`/`ZAboveChunks`) into the SAME desired set and the independent collision set, so AI/remote players keep collision loaded around them; delta cull releases them on move/unregister. **§9.4:** a tile only a CollisionOnly anchor wants (clipmap didn't stamp it) → `CollisionOnlyTiles` → hidden at apply. Zero cost when no anchors. |
| `ReconcileAnchorTileVisibility` | — | **§9.4:** after each rebuild, toggle `SetVisibility` on ALREADY-LOADED tiles that flipped render↔collision-only (diff `CollisionOnlyTiles` vs prev — bounded, no O(loaded) scan; unhide only if still desired). No-op without CollisionOnly anchors. | | `ReconcileAnchorTileVisibility` | — | **§9.4:** after each rebuild, toggle `SetVisibility` on ALREADY-LOADED tiles that flipped render↔collision-only (diff `CollisionOnlyTiles` vs prev — bounded, no O(loaded) scan; unhide only if still desired). No-op without CollisionOnly anchors. |
| `RegisterStreamingAnchor` / `UnregisterStreamingAnchor` | — | **BlueprintCallable §9.3:** add/remove an actor as a streaming anchor (`EVoxelAnchorPolicy` CollisionOnly/FullVisual + XY/ZBelow/ZAbove box). Idempotent; forces a rebuild next Tick. §9.4: CollisionOnly tiles cook collision but are hidden (no draw/VSM) unless the player clipmap wants them too. | | `RegisterStreamingAnchor` / `UnregisterStreamingAnchor` | — | **BlueprintCallable §9.3:** add/remove an actor as a streaming anchor (`EVoxelAnchorPolicy` CollisionOnly/FullVisual + XY/ZBelow/ZAbove box). Idempotent; forces a rebuild next Tick. §9.4: CollisionOnly tiles cook collision but are hidden (no draw/VSM) unless the player clipmap wants them too. |
| `LoadTile` | 445 | Budget check → `UE::Tasks::Launch` background gen+mesh; RAII task guard. Worker runs `Generator->ClassifyTile` first (T1.d): AllSolid/AllAir ⇒ skip `GenerateMesh`, tile stays empty (capture tiles always generate). It carries `GenerationEpoch`/desired epoch and checks `bShuttingDown`, generation pause, and its read-only cancellation token between expensive stages; obsolete work returns through `ProcessQueue`. STRATE CONTENT CUT: tiles ≥ `StrateContentCutMinLevel` pass the player-strate band (`MeshBandChunkLo/Hi` → voxels) to `GenerateMesh` + stamp it on `FChunkResult::BandChunkLo/Hi`; band change re-queues via `BandRemeshQueue` (see `UpdateChunksAroundPosition`). TOO-COARSE SKIP: if one cell is taller than the band (`Step > band height` — level ≥7 territory) the tile is enqueued EMPTY without launching a task (cell-granular cut could only render garbage). F18: `Tile.Level > MaxClipLevel` = SHEET tile → routed to `GenerateSheetMesh` (band mid-chunk = strate ref; band unarmed ⇒ empty; MC-ring sampling density, cells capped 128/axis; carries the XY hole `SheetHole*Vox` — hole moves ⇒ overlapping sheets re-queue via `BandRemeshQueue`, see `UpdateChunksAroundPosition`). §8.10. | | `LoadTile` | 9033 | Budget check → `UE::Tasks::Launch` background gen+mesh; RAII task guard. Worker runs `Generator->ClassifyTile` first (T1.d): AllSolid/AllAir ⇒ skip `GenerateMesh`, tile stays empty (capture tiles always generate). It carries `GenerationEpoch`/desired epoch and checks `bShuttingDown`, generation pause, and its read-only cancellation token between expensive stages; obsolete work returns through `ProcessQueue`. STRATE CONTENT CUT: tiles ≥ `StrateContentCutMinLevel` pass the player-strate band (`MeshBandChunkLo/Hi` → voxels) to `GenerateMesh` + stamp it on `FChunkResult::BandChunkLo/Hi`; band change re-queues via `BandRemeshQueue` (see `UpdateChunksAroundPosition`). `voxel.TestFullResClipLevels` is a measurement-only runtime override; default -1 uses the authored setting. TOO-COARSE SKIP: if one cell is taller than the band (`Step > band height` — level ≥7 territory) the tile is enqueued EMPTY without launching a task (cell-granular cut could only render garbage). §8.10. |
| `UnloadTile` | — | Clears tile state; marks any matching generation request obsolete without removing its pending slot, so a late result cannot clear a replacement request; the component is PARKED in the pool (T2.c), not destroyed. | | `UnloadTile` | — | Clears tile state; marks any matching generation request obsolete without removing its pending slot, so a late result cannot clear a replacement request; the component is PARKED in the pool (T2.c), not destroyed. |
| `ApplyMeshToTile` | — | Upload geometry. One component per tile (clipmap keeps count low; supersedes the old region batching); worker-built streams (T1.f) → `CreateSectionGroup(MoveTemp)`. Reuses the component's existing `URealtimeMesh` (no per-apply mesh alloc). **F17: two polygroups** (0 ground / 1 sky-cap, per-triangle class from the mesher) → RMC auto-section per non-empty group; slot 0 = override/default material, slot 1 = `CeilingMaterial` (fallback ground); config gated by `FChunkResult::bHasGroundTris/bHasCeilingTris`. Takes `FChunkResult&`; strate lookups clamp Z into `Result.BandChunkLo/Hi` (strate content cut) — ground at clamped bottom chunk, cap at clamped top (mid = gap fallback). Collision level-0 only (T1.c) both groups; shadow per SECTION: ground casts at level≤1, cap never. **§9.4:** `SetVisibility(false)` when the tile is in `CollisionOnlyTiles` (anchor-only, hidden — collision still cooks). §8.10 + ARCHITECTURE SurfaceWorld row. | | `ApplyMeshToTile` | 9838 | Upload geometry. One component per tile (clipmap keeps count low; supersedes the old region batching); worker-built streams (T1.f) → `CreateSectionGroup(MoveTemp)`. Reuses the component's existing `URealtimeMesh` (no per-apply mesh alloc). **F17: two polygroups** (0 ground / 1 sky-cap, per-triangle class from the mesher) → RMC auto-section per non-empty group; slot 0 = override/default material, slot 1 = `CeilingMaterial` (fallback ground); config gated by `FChunkResult::bHasGroundTris/bHasCeilingTris`. Takes `FChunkResult&`; strate lookups clamp Z into `Result.BandChunkLo/Hi` (strate content cut) — ground at clamped bottom chunk, cap at clamped top (mid = gap fallback). Collision level-0 only (T1.c) both groups; shadow per SECTION: ground casts at level≤1, cap never. Ordinary same-tile re-cooks retain the installed body while it is valid. A carve intersecting the pawn's current support tile and capsule bounds suppresses that old body until the replacement cook is installed. `voxel.ProfileCollisionCookStages` reports generation/apply waits, combined RMC time, completion hops, triangle/skirt counts and collision settings. With `voxel.Step1PrequeueVisibleLOD0>0`, logs apply frame and waits for `GetLastRenderTime` to advance for first-visible frame; `voxel.Step1UploadDisableCollision=1` isolates render upload. Routes each terrain base through shared MIDs; disabled volume MIDs bind neutral `VolP*` and null `VolTex*`. **§9.4:** `SetVisibility(false)` when the tile is in `CollisionOnlyTiles` (anchor-only, hidden — collision still cooks). §8.10 + ARCHITECTURE SurfaceWorld row. |
| `HandleRealtimeMeshCollisionBodyUpdated` / `HandleTileCollisionCookComplete` / `PollStep1SupportSweepProbes` | 6964 / 7192 / — | Correlate RMC body-install event and collision-future completion. `voxel.ProfileCollisionCookStages=1` logs worker queue/gen, result wait, apply/upload/config submission, RMC submit→body update, body event→future, future→game-thread hop, submit frames, pending VoxelForge cooks, priority, collision flags, and triangle/skirt totals; it also reports mesh-generation-end→ready. `voxel.Step1CollisionProbe=1` sweeps the pawn capsule against the exact component pointer, logs both the first hit and whether it is the target body, and checks every body in the captured support cohort. RMC's end-frame dequeue, async worker start, geometry extraction and core cook remain inside the external plugin and are not individually timestampable from this module. |
| `FVoxelStep1CoreCookWork` / `FStep1CoreCookPoolOwner` | — | Optional `voxel.Step1CoreCookBenchmark=1` captures the pawn's first exact radius box (normally 27 L0 tiles) on the game thread, tags those results through worker meshing, and sends their streams plus any out-of-box critical support tile to a dedicated one-thread high-priority pool. It records queue delay, extraction and core cook separately. One approximately 4k-triangle cohort stream is retained and replayed after `Step1PauseGenerationAfterSeconds` pauses generation. The benchmark sidecar is not installed as the tile BodySetup and is disabled by default. |
| Step 1 measurement CVars | — | `voxel.Step1GenerationCap=-1` uses the normal cap (`0` pauses, positive values cap worker tasks); `voxel.Step1ExitAfterFrames=0` disables a frame-counted clean test exit; `voxel.Step1PauseGenerationAfterSeconds=0` disables the timed pause; `voxel.Step1MaxMeshApplies=0` uses the asset cap; `voxel.Step1PrequeueVisibleLOD0=0` disables the held upload corpus; `voxel.Step1UploadDisableCollision=0`, `voxel.Step1CollisionProbe=0`, `voxel.Step1CoreCookBenchmark=0`, and `voxel.Step1CsvCaptureOnBeginPlay=0`. All default off. The launch-only `voxel.TestClipRadius` transiently duplicates settings and clamps a positive requested radius to 1–16; it is absent by default and leaves the authored asset unchanged. The prequeue release emits the default-global `VFStep1UploadCorpusReleased` CSV event; `voxel.Step1CsvCaptureOnBeginPlay=1` emits `VFStep1GameBeginPlay`. Use UE's `-csvStartOnEvent=<event>` to begin capture after RHI initialization. |
| `AcquireTileComponent` / `ReleaseTileComponent` | — | **T2.c component pool** (`TileComponentPool`, bounded): park on unload (geometry+collision stripped, hidden, stays registered), pop on apply — no `NewObject`/`RegisterComponent`/GC churn during travel & regen. §8.10. | | `AcquireTileComponent` / `ReleaseTileComponent` | — | **T2.c component pool** (`TileComponentPool`, bounded): park on unload (geometry+collision stripped, hidden, stays registered), pop on apply — no `NewObject`/`RegisterComponent`/GC churn during travel & regen. §8.10. |
| `GetStrateAtPosition` | 965 | Gameplay query → strate index. | | `GetStrateAtPosition` | 8351 | Gameplay query → strate index. |
| `GetBiomeAtWorldLocation` | — | **BlueprintCallable** biome probe at a world point (undoes actor xf → voxel → `Generator::QueryBiomeAt`). Returns `FVoxelBiomeQuery` for BP debug ("what biome / how many decos under the cursor?"). | | `GetBiomeAtWorldLocation` | — | **BlueprintCallable** biome probe at a world point (undoes actor xf → voxel → `Generator::QueryBiomeAt`). Returns `FVoxelBiomeQuery` for BP debug ("what biome / how many decos under the cursor?"). |
| `GetVoxelSurfaceHeightAt` | ~1534 | **BlueprintCallable** ground finder (F7 bridge): world XY → terrain + sky-cap world-Z via `Generator::GetSurfaceHeightAt`, NO trace/collision, deterministic, available before the area meshes → self-arranging prefab/ruin BPs snap their parts to the real ground. False (outs=input Z) on non-SurfaceWorld strates; ignores passage/spine carving. | | `GetVoxelSurfaceHeightAt` | 8385 | **BlueprintCallable** ground finder (F7 bridge): world XY → terrain + sky-cap world-Z via `Generator::GetSurfaceHeightAt`, NO trace/collision, deterministic, available before the area meshes → self-arranging prefab/ruin BPs snap their parts to the real ground. False (outs=input Z) on non-SurfaceWorld strates; ignores passage/spine carving. |
| `CarveAtPosition` / `FillAtPosition` | 691 / 709 | Build `FVoxelModification` → `ApplyModification`. | | `CarveAtPosition` / `FillAtPosition` | 10230 / 10239 | Build `FVoxelModification` → `ApplyModification`. |
| `ApplyModification` | ~1581 | Single funnel for all brushes: DiffLayer → **sync-remesh the brush-centre level-0 tile** (`SyncRemeshTile`, instant hole; skipped if that tile is mid-gen — would race a stale in-flight result) → `RemeshDirtyChunks(..., excludeCenter)` for the neighbours → `RemoveDecorationsInSphere`. | | `ApplyModification` | 10248 | Single funnel for all brushes: DiffLayer → when a negative brush intersects the pawn's capsule bounds and affects its ready support tile, disable that old body and withdraw readiness before remesh → **sync-remesh the brush-centre level-0 tile** (`SyncRemeshTile`, instant hole; skipped if that tile is mid-gen — would race a stale in-flight result) → `RemeshDirtyChunks(..., ExcludeTile)` for the neighbours → `RemoveDecorationsInSphere`. A successful replacement cook restores collision/readiness; neighboring edits that miss the capsule bounds keep the same-tile recook handoff. |
| `ClearAllModifications` | 726 | Clears diff layer, regenerates. | | `ClearAllModifications` | 8696 | Clears diff layer, regenerates. |
| `ChangeSeed` | 740 | **Legacy season reset**: new seed everywhere, clear diffs, bump season, reload. Rejected while a cooked season owns the seed. | | `ChangeSeed` | 8710 | **Legacy season reset**: new seed everywhere, clear diffs, bump season, reload. Rejected while a cooked season owns the seed. |
| `GetCurrentSeed` / `GetCurrentSeason` / `GetCurrentSeasonContentHash` | — | Effective cooked-or-legacy identity; the hash is the join-time stale-season comparison value. | | `GetCurrentSeed` / `GetCurrentSeason` / `GetCurrentSeasonContentHash` | — | Effective cooked-or-legacy identity; the hash is the join-time stale-season comparison value. The optional world-assembly digest joins the hash only while `bUseVoxelForgeCore` is active, so switch-off retains the previous legacy hash. |
| `RemeshDirtyChunks` | 798 | Queue loaded level-0 dirty tiles onto `DirtyRemeshQueue` (async re-mesh, no pop) + `MarkDirtyVoxelBox` the volume. Optional `ExcludeTile` = the sync'd centre. Drained FIRST in the submit loop at **BackgroundHigh** (ahead of streaming/band) so a dig never waits behind streaming; in-flight tiles stay QUEUED (not dropped) so a stale pre-carve result is corrected once it lands — fixes "hole shows up a beat late / not until I move". | | `RemeshDirtyChunks` | 8801 | Queue loaded level-0 dirty tiles onto `DirtyRemeshQueue` (async re-mesh, no pop) + `MarkDirtyVoxelBox` the volume. Optional `ExcludeTile` = the sync'd centre. Drained FIRST in the submit loop at **BackgroundHigh** (ahead of streaming/band) so a dig never waits behind streaming; in-flight tiles stay QUEUED (not dropped) so a stale pre-carve result is corrected once it lands — fixes "hole shows up a beat late / not until I move". |
> **Game-thread profiling (Perf):** `AVoxelWorld::Tick` and its sub-steps are wrapped in `TRACE_CPUPROFILER_EVENT_SCOPE` — `VoxelForge_Tick / UpdateChunks / BuildDesiredTiles / CullTiles / SubmitTiles / ProcessPending / ProcessUnload / UpdateDecorations / UpdateWater`. Capture a `Count/Incl/Excl` Insights timer export and read the `Excl` column to see which step owns the per-frame cost (the actor tick shows as `BP_VoxelWorld_C` if subclassed in BP). `VoxelForge_ClassifyTile` (T1.d) / `VoxelForge_GenerateMesh` + `VoxelForge_BuildStreams` are worker-side (off the frame): the RMC `FRealtimeMeshStreamSet` is now built on the gen worker (`BuildTileStreamSet`) and carried on `FChunkResult::Streams` (TSharedPtr), so `ApplyMeshToTile` is game-thread-cheap — just material/ceiling resolve + `CreateSectionGroup(MoveTemp)`. See ARCHITECTURE §8.10 "Worker-built StreamSet (T1.f)". > **Game-thread profiling (Perf):** `AVoxelWorld::Tick` and its sub-steps are wrapped in `TRACE_CPUPROFILER_EVENT_SCOPE` — `VoxelForge_Tick / UpdateChunks / BuildDesiredTiles / CullTiles / SubmitTiles / ProcessPending / ProcessUnload / UpdateDecorations / UpdateWater`. Capture a `Count/Incl/Excl` Insights timer export and read the `Excl` column to see which step owns the per-frame cost (the actor tick shows as `BP_VoxelWorld_C` if subclassed in BP). `VoxelForge_ClassifyTile` (T1.d) / `VoxelForge_GenerateMesh` + `VoxelForge_BuildStreams` are worker-side (off the frame): the RMC `FRealtimeMeshStreamSet` is now built on the gen worker (`BuildTileStreamSet`) and carried on `FChunkResult::Streams` (TSharedPtr), so `ApplyMeshToTile` is game-thread-cheap — just material/ceiling resolve + `CreateSectionGroup(MoveTemp)`. See ARCHITECTURE §8.10 "Worker-built StreamSet (T1.f)".
@@ -343,31 +407,31 @@ This is **where terrain shape lives.**
| Symbol | .cpp line | Role | | Symbol | .cpp line | Role |
|--------|-----------|------| |--------|-----------|------|
| `UVoxelGenerator` / `DensityCacheOwnerId` | — | Constructor allocates a process-unique integer identity (relaxed atomic, once per object). `GetDensityAt` includes it in the `CP_*` thread-local key, preventing a worker from serving another generator/world's params, biome context, `CP_UseOpStack`, or stack when `(ChunkCoord, LayoutVersion)` happens to match. Hot-path cost: one `uint64` compare per voxel. Scope is deliberately only the proved `CP_*` path. | | `UVoxelGenerator` / `DensityCacheOwnerId` | — | Constructor allocates a process-unique integer identity (relaxed atomic, once per object). `GetDensityAt` includes it in the `CP_*` thread-local key, preventing a worker from serving another generator/world's params, biome context, `CP_UseOpStack`, or stack when `(ChunkCoord, LayoutVersion)` happens to match. Hot-path cost: one `uint64` compare per voxel. Scope is deliberately only the proved `CP_*` path. |
| `FractalNoise3D` (static) | 25 | fBM (layered Perlin). | | `FractalNoise3D` (static) | 931 | fBM (layered Perlin). |
| `RidgedNoise3D` (static) | 55 | Ridged multifractal — craggy. | | `RidgedNoise3D` (static) | 949 | Ridged multifractal — craggy. |
| `CellularNoise3D` (static) | 101 | Worley/cellular — grotto/scallop. | | `CellularNoise3D` (static) | 976 | Worley/cellular — grotto/scallop. |
| `ApplyBoundarySeal` (static) | 170 | Solidifies strate top/bottom shells. | | `ApplyBoundarySeal` (static) | 996 | Solidifies strate top/bottom shells. |
| `ApplyPassageCarving` (static) | 197 | Punches passages/elevator through the seal. | | `InitializeSettings` | 2691 | Copies the effective seed, spine radius, and global XY world-bound settings from `UVoxelSettings` (the season owns all three when assigned); radius 0 preserves the unbounded field. |
| `InitializeSettings` | 211 | Copies the effective seed, spine radius, and global XY world-bound settings from `UVoxelSettings` (the season owns all three when assigned); radius 0 preserves the unbounded field. | | **`GetDensityAt`** | 2712 | **Entry point.** Picks strate + generator type and, on the per-chunk refetch, builds the chunk's operator stack (editor composer → cooked-season recipe → native archetype stack); evaluates it (TunnelNetwork/Underwater through the fused `GetDensityWithParams`), then adds disturbances and structural posts. Optional `OutPreEditDensity` receives the exact final procedural field immediately before the player DiffLayer tail, allowing the near-player dig cache to replay edits without regenerating the operator stack. Its ordinary one-argument behavior is unchanged. A degenerate strate has no stack and is air; `CP_*` state remains keyed by `(DensityCacheOwnerId, ChunkCoord, LayoutVersion)`. |
| **`GetDensityAt`** | 218 | **Entry point.** Picks strate + generator type, materialises a cooked-season recipe on the existing per-chunk refetch when present, dispatches, then adds disturbances/diff. Its `CP_*` state is keyed by `(DensityCacheOwnerId, ChunkCoord, LayoutVersion)`; workers copy immutable recipe/vector data and never read editor composer state. | | **`EvaluateDensityChunk`** | 2728 | **Editor-only, default-off research API.** Accepts a lattice region contained by one 32-voxel chunk. Routes once, reuses `FVoxelDensityBlockSession::FillChunk` / `EvalBlock` for supported TunnelNetwork and Underwater operator stacks, and applies the existing post-stack writers across all chunk points before returning. Unsupported routes use scalar `GetDensityAt` and return both fallback and general-path entry counts. The region evaluator prepares bounded cache/index state once per region or chunk. The API is absent from non-editor builds; streaming and gameplay continue through the original scalar API. |
| **`GetDensityWithParams`** | 277 | TunnelNetwork pipeline (~1000 lines). See §4. ⚠️ Takes **required** `ParamsFingerprint` + `LayoutVersion` since the AUDIT §C2 fix (2026-07-28) — they go into the SDF cache key so a chunk can no longer be evaluated against a neighbour's rooms. Callers compute the CRC **once per chunk** (`CP_TunnelFP`), never per voxel. The optional `voxel.WormBlockSkip` path (default 0) uses `VF_TryGetWormBlockSkip` + a worker-local 4³ lattice-block cache to prove `|N1| >= WormThreshold` and remove both worm noise calls only; it never changes `NetworkMask` or the fallback field path. | | **`GetDensityWithParams`** | 3749 | TunnelNetwork pipeline (~1000 lines) — the fused evaluator of the TunnelNetwork/Underwater stack in `GetDensityAt`. See §4. ⚠️ Takes **required** `ParamsFingerprint` + `LayoutVersion` since the AUDIT §C2 fix (2026-07-28) — they go into the SDF cache key so a chunk can no longer be evaluated against a neighbour's rooms. Callers compute the CRC **once per chunk** (`CP_TunnelFP`), never per voxel. The optional `voxel.WormBlockSkip` path (default 0) uses `VF_TryGetWormBlockSkip` + a worker-local 4³ lattice-block cache to prove `|N1| >= WormThreshold` and remove both worm noise calls only; it never changes `NetworkMask` or the fallback field path. |
| **`GetSlabDensity`** | 1306 | FlatPlain/CrystalChamber pipeline. See §4.2. |
| `SampleSurfaceStructuralZ` | — | **F20:** the RAW SurfaceWorld heightfield (continents+mountains+detail), BEFORE any terrain op; returns terrain Z + relief M. Cliff re-samples it at an XY offset for a cheap analytic slope. | | `SampleSurfaceStructuralZ` | — | **F20:** the RAW SurfaceWorld heightfield (continents+mountains+detail), BEFORE any terrain op; returns terrain Z + relief M. Cliff re-samples it at an XY offset for a cheap analytic slope. |
| `ComputeSurfaceTerrainZ` / `GetSurfaceDensity` | — | SurfaceWorld heightfield → terrain Z, then density; biome **output-blend** lerps dominant/neighbour heights (`ParamsD`/`ParamsN`/weight). **F20 surface ops** (`FSurfaceGenerationParams`, biome-selected + slope/relief-conditioned, all default off): Cliff (slope-gated STEEPENING — push height from local mean where steep ⇒ sheer walls; 4 structural resamples only when on), Terrace (relief-gated + `TerraceHardness`), LayerLines (sedimentary shelves) — pure per-column height REMAPS applied here so the single height oracle stays consistent (MC/sheets/ClassifyTile/deco/BP bridge). **Phase 2 OVERHANG** (volumetric — real jutting shelves): in `SurfaceDensityFromColumn`, for AIR voxels in a window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height (tiny low ⇒ air over the void, full high ⇒ borrows the far cliff rock) and unioned in ⇒ a shelf attached to the cliff, tapering out over the void with air beneath (the sketch). Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill `(DirX,DirY)` resolved once in `ComputeSurfaceColumn` (gradient sampled at the REACH scale so a spot over the void can see the cliff), cached on `FSurfaceColumn`. Genuine 3D (per-voxel structural re-eval, gated to steep overhang columns). Off ⇒ byte-identical. The field-preserving LOD0 pass removes only needless temporaries/default construction; every output remains bit-identical. §8.14. | | `ComputeSurfaceTerrainZ` / `ComputeSurfaceColumn` | — | SurfaceWorld heightfield → terrain Z (+ sky-cap ceiling and overhang gate per column); biome **output-blend** lerps dominant/neighbour heights (`ParamsD`/`ParamsN`/weight). **F20 surface ops** (`FSurfaceGenerationParams`, biome-selected + slope/relief-conditioned, all default off): Cliff (slope-gated STEEPENING — push height from local mean where steep ⇒ sheer walls; 4 structural resamples only when on), Terrace (relief-gated + `TerraceHardness`), LayerLines (sedimentary shelves) — pure per-column height REMAPS applied here so the single height oracle stays consistent (MC/ClassifyTile/deco/BP bridge). **Phase 2 OVERHANG** (volumetric — real jutting shelves): in the stack's `FOverhangShelfMod`, for AIR voxels in a window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height (tiny low ⇒ air over the void, full high ⇒ borrows the far cliff rock) and unioned in ⇒ a shelf attached to the cliff, tapering out over the void with air beneath (the sketch). Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill `(DirX,DirY)` resolved once in `ComputeSurfaceColumn` (gradient sampled at the REACH scale so a spot over the void can see the cliff), cached on `FSurfaceColumn`. Genuine 3D (per-voxel structural re-eval, gated to steep overhang columns). Off ⇒ byte-identical. The field-preserving LOD0 pass removes only needless temporaries/default construction; every output remains bit-identical. §8.14. |
| `VF_BuildOpStackForChunk` (file-static) | — | **The archetype → stack mapping, written down once.** `GetDensityAt` and `ClassifyTile` both call it; params are passed in, never fetched here. A second copy would be the worst bug available in this file — a tile skipped on the verdict of a stack that is not the one producing its density is a hole. Returns false (⇒ caller falls back to the `switch`) for an unported archetype, missing params, or a **degenerate strate**, since five archetype functions early-out to air there and the stack deliberately has no such early-out. `Refs.Surface == nullptr` makes it refuse SurfaceWorld, which is how `ClassifyTile` keeps its own exact-lattice proof. | | `VF_BuildOpStackForChunk` (file-static) | — | **The archetype → stack mapping, written down once.** `GetDensityAt` and `ClassifyTile` both call it; params are passed in, never fetched here. A second copy would be the worst bug available in this file — a tile skipped on the verdict of a stack that is not the one producing its density is a hole. Returns false for an unknown archetype, missing params, or a **degenerate strate** (the stack deliberately has no degenerate early-out): `GetDensityAt` then returns air and `ClassifyTile` returns `Mixed`. `Refs.Surface == nullptr` makes it refuse SurfaceWorld, which is how `ClassifyTile` keeps its own exact-lattice proof. |
| `ClassifyTile` | — | **T1.d trivial-tile reject. ⛔ Not called by streaming by default since 2026-09-13** (`voxel.OuterClassifierMode=0`; net-negative in game, see ARCHITECTURE §8.10). Cooked/editor recipes now build the exact recipe stack and call its proved `ClassifyBox`; a tile crossing a slot/gap, any diff, or any disturbance returns `Mixed`. This replaces the old global `HasComposerRecipeOverride()` force-to-Mixed. Native cave/surface/gap proofs and all §8.10 caches remain unchanged. | | `ClassifyTile` | — | **T1.d trivial-tile reject. ⛔ Not called by streaming by default since 2026-09-13** (`voxel.OuterClassifierMode=0`; net-negative in game, see ARCHITECTURE §8.10). Cooked/editor recipes now build the exact recipe stack and call its proved `ClassifyBox`; a tile crossing a slot/gap, any diff, or any disturbance returns `Mixed`. This replaces the old global `HasComposerRecipeOverride()` force-to-Mixed. Native cave/surface/gap proofs and all §8.10 caches remain unchanged. |
| `SampleRelief` / `SampleMoisture` | — | Climate fields (pure XY, [0,1]). Relief = shared source of truth for the relief map M. §8.14. | | `SampleRelief` / `SampleMoisture` | — | Climate fields (pure XY, [0,1]). Relief = shared source of truth for the relief map M. §8.14. |
| `SampleBiomeAt` | — | Warped-Voronoi + climate biome query (dominant + neighbour + weight). Reference used by the preview bake + `GetDominantBiomeAt`. §8.14. | | `SampleBiomeAt` | — | Warped-Voronoi + climate biome query (dominant + neighbour + weight). Reference used by the preview bake + `GetDominantBiomeAt`. §8.14. |
| `ResolveBiomeSampleAt` / `RebuildBiomeGrid` | — | Hot-path biome resolve (FBiomeSample) via a box-validated per-chunk cell-grid cache. Bit-identical to `SampleBiomeAt`. §8.14, §8.10. | | `ResolveBiomeSampleAt` / `RebuildBiomeGrid` | — | Hot-path biome resolve (FBiomeSample) via a box-validated per-chunk cell-grid cache. Bit-identical to `SampleBiomeAt`. §8.14, §8.10. |
| `GetDominantBiomeAt` | — | Game-thread query → dominant biome ASSET (content/atmosphere). §8.14. | | `GetDominantBiomeAt` | — | Game-thread query → dominant biome ASSET (content/atmosphere). §8.14. |
| `QueryBiomeAt` | — | Rich game-thread biome probe → `FVoxelBiomeQuery` (dominant/neighbour asset, relief/moisture, blend weight, dominant deco count). Diagnostic behind `AVoxelWorld::GetBiomeAtWorldLocation`. §8.14. | | `QueryBiomeAt` | — | Rich game-thread biome probe → `FVoxelBiomeQuery` (dominant/neighbour asset, relief/moisture, blend weight, dominant deco count). Diagnostic behind `AVoxelWorld::GetBiomeAtWorldLocation`. §8.14. |
| `EvaluateTerrainConditions` | ~2548 | **F7 aware placement:** AND-evaluate an entry's `FTerrainCondition[]` (relief/moisture/biome-border) at a candidate voxel XY. Empty = true (zero cost). Pure query (SampleRelief/SampleMoisture/SampleBiomeAt) → worker-safe + game-thread; caller passes the strate's `FBiomeContext` (freq/contrast + Voronoi map). Consumed by deco `BuildCellSpawns` + `SpawnLandmarkInstance`; shared core of the future quest FindFeature locator. | | `EvaluateTerrainConditions` | 6973 | **F7 aware placement:** AND-evaluate an entry's `FTerrainCondition[]` (relief/moisture/biome-border) at a candidate voxel XY. Empty = true (zero cost). Pure query (SampleRelief/SampleMoisture/SampleBiomeAt) → worker-safe + game-thread; caller passes the strate's `FBiomeContext` (freq/contrast + Voronoi map). Consumed by deco `BuildCellSpawns` + `SpawnLandmarkInstance`; shared core of the future quest FindFeature locator. |
> **Per-voxel hot-path memos (perf pass 2, all bit-identical — same hashes/math, hoisted per > **Per-voxel hot-path memos (perf pass 2, all bit-identical — same hashes/math, hoisted per
> chunk/cell):** `GetDensityAt` uses the DiffLayer snapshot cache (§3.9); `GetDensityWithParams` > chunk/cell):** `GetDensityAt` uses the DiffLayer snapshot cache (§3.9); `GetDensityWithParams`
> memoizes the strate index per (chunkZ, `GetLayoutVersion()`); `thread_local` per-cell lattice > memoizes the strate index per (chunkZ, `GetLayoutVersion()`); `thread_local` per-cell lattice
> bakes cover slab columns (`GetSlabDensity` step 4), maze open edges, vertical shafts + > bakes cover slab columns (`FGridColumnMod`), maze open edges (`FLatticeCorridorSource`), vertical
> cross-connectors, floating-island constants, and the disturbance chasms/bridges/ridges; worm > shafts + cross-connectors (`FShaftFieldSource`), floating-island constants (`FIslandBlobSource`),
> and the disturbance chasms/bridges/ridges; worm
> tunnels short-circuit the 2nd Perlin when N1 ≥ WormThreshold (N2 ≥ 0 ⇒ can't carve). With > tunnels short-circuit the 2nd Perlin when N1 ≥ WormThreshold (N2 ≥ 0 ⇒ can't carve). With
> `voxel.WormBlockSkip=1`, a worker-local cache proves whole 4³ lattice blocks only when the > `voxel.WormBlockSkip=1`, a worker-local cache proves whole 4³ lattice blocks only when the
> actual 3D Perlin bound (`L = 8.118988160`, including `VOXEL_NOISE_SCALE`) plus a `1e-3` > actual 3D Perlin bound (`L = 8.118988160`, including `VOXEL_NOISE_SCALE`) plus a `1e-3`
@@ -432,28 +496,36 @@ serialization before they are written. The commandlet keeps `WorldRadiusVoxels=0
authored asset or diff layer, and enforces the default 25-minute / maximum 30-minute wall-clock authored asset or diff layer, and enforces the default 25-minute / maximum 30-minute wall-clock
budget, reporting partial artifacts and completed modes on truncation. `-gameconfig=1` opts into the budget, reporting partial artifacts and completed modes on truncation. `-gameconfig=1` opts into the
authored settings' skirts/LOD octave drop and permits export-only parity of a single-strate asset; authored settings' skirts/LOD octave drop and permits export-only parity of a single-strate asset;
the historical interior-slot requirement remains in force for walk/probe runs. `VoxelGeometryHash.h` the optional `-exporttilex/y/z` arguments choose a specific level-0 tile for export/game parity,
is the shared digest used by `Tools/VoxelForgeTest.ps1 -Scenario parity`. while omitting them preserves center-derived region selection. The historical interior-slot
requirement remains in force for walk/probe runs. `VoxelGeometryHash.h` is the shared digest used
by `Tools/VoxelForgeTest.ps1 -Scenario parity`.
### 3.7 Cave morphology (SDF rooms/tunnels) — `Public/VoxelCaveMorphology.h` + `.cpp` ### 3.7 Cave morphology (SDF rooms/tunnels) — `Public/VoxelCaveMorphology.h` + `.cpp`
Header is rich with inline docs. Two namespaces + a per-chunk cache system. Header is rich with inline docs. Two namespaces + a per-chunk cache system.
- `namespace VoxelSDF` (h:46): `Sphere`, `Ellipsoid`, `Capsule`, `RoundedBox`, - `namespace VoxelSDF` (h:78): `Sphere`, `Ellipsoid`, `Capsule`, `RoundedBox`,
`TaperedCapsule`, `SmoothMin`, `SmoothMax` — all FORCEINLINE SDF primitives. `TaperedCapsule`, `SmoothMin`, `SmoothMax` — all FORCEINLINE SDF primitives.
- `namespace VoxelHash` (h:158): `Mix`, `Cell`, `Pair`, `ToFloat01`, `ToFloatSigned` - `namespace VoxelHash` (h:210): `Mix`, `Cell`, `Pair`, `ToFloat01`, `ToFloatSigned`
— deterministic hashing for room/tunnel placement (no storage, infinite worlds). — deterministic hashing for room/tunnel placement (no storage, infinite worlds).
- Cache structs (h:224-330): `FCachedRoom`, `FCachedTunnel`, `FCachedPit`, - Cache structs (h:520-950): `FCachedRoom`, `FCachedTunnel`, `FCachedPit`,
`FCachedChimney`, `FCachedColumn`, `FChunkSDFCache`. `FCachedChimney`, `FCachedColumn`, `FChunkSDFCache`.
- `FChunkSDFBrickIndex` is an editor-only-at-use 8-voxel 3D bin beside the existing XY index. The
default-off `SetResearchBrickBinningEnabled` selector builds/selects it only for the explore
commandlet. `VF_BuildResearchBrickIndices` uses the cached room cull reach (shape, floor relief,
blend and terrain-op envelope), room-floor-join bounds, and the union of SDF/world tunnel
centerline influence and bounding-sphere bounds. Source item indices stay in ascending order;
invalid or oversized grids fall back to today's XY broad phase. This step-2 candidate-bin
ablation still routes every lattice point through the canonical `GetDensityAt` final field.
- `namespace VoxelCaveMorphology`: - `namespace VoxelCaveMorphology`:
| Function | .cpp line | Role | | Function | .cpp line | Role |
|----------|-----------|------| |----------|-----------|------|
| `BuildChunkCache` | 47 | **Phase 1** (once/chunk): collect rooms, guaranteed backbone (`bTunnelsFlowTowardOrigin`: tree rooted at the (0,0) hub — every room reachable, links flow inward; false = legacy NN forest), slope-aware link metric (`TunnelHorizontalBias` now applies to backbone too), decide tunnels, **cull zero-connection rooms** (no sealed bubbles), store rooms by their OWN reach (fixes origin-room clipping at `MaxInfluence`), pre-bake pits/chimneys/columns via the shared `BakeRoomFeature` hash-placement skeleton (one gate/XY/radius pattern + per-type Emit lambda), hash-roll per-room terrain op. | | `BuildChunkCache` | 6782 | **Phase 1** (once/chunk): collect rooms, guaranteed backbone (`bTunnelsFlowTowardOrigin`: tree rooted at the (0,0) hub — every room reachable, links flow inward; false = legacy NN forest), slope-aware link metric (`TunnelHorizontalBias` now applies to backbone too), decide tunnels, **cull zero-connection rooms** (no sealed bubbles), store rooms by their OWN reach (fixes origin-room clipping at `MaxInfluence`), pre-bake pits/chimneys/columns via the shared `BakeRoomFeature` hash-placement skeleton (one gate/XY/radius pattern + per-type Emit lambda), hash-roll per-room terrain op. |
| `EvaluateSDFCached` | 757 | **Phase 2** (per voxel): SmoothMin over cached rooms/tunnels; returns nearest room idx for terrain-op lookup. **Signature changed (perf pass 2): `RoomShapeVariety` param REMOVED** — the shape roll + capsule trig are pre-baked into `FCachedRoom` (`ShapeType/ShapeA/ShapeB/ShapeR`) by `BuildChunkCache`, bit-identical. | | `EvaluateSDFCached` | 8569 | **Phase 2** (per voxel): SmoothMin over cached rooms/tunnels; returns nearest room idx for terrain-op lookup. **Signature changed (perf pass 2): `RoomShapeVariety` param REMOVED** — the shape roll + capsule trig are pre-baked into `FCachedRoom` (`ShapeType/ShapeA/ShapeB/ShapeR`) by `BuildChunkCache`, bit-identical. |
| `EvaluateSDF` | 738 | Convenience wrapper (builds temp cache) for one-off queries. |
`MakeStrateSeed` (h:~477) is the shared pure world-seed/strate-index salt used by both `MakeStrateSeed` (h:965) is the shared pure world-seed/strate-index salt used by both
`BuildChunkCache` and the TunnelNetwork/Underwater destination landing queries. `BuildChunkCache` and the TunnelNetwork/Underwater destination landing queries.
`VF_SuggestLandingPoint` (h:~550, implemented in `VoxelCaveMorphology.cpp`) is the pure player-fit `VF_SuggestLandingPoint` (h:1121, implemented in `VoxelCaveMorphology.cpp`) is the pure player-fit
landing-site query: it evaluates the local archetype source through the same capsule/support landing-site query: it evaluates the local archetype source through the same capsule/support
contract as the measurement pass — support within step height, walkable slope, and full capsule contract as the measurement pass — support within step height, walkable slope, and full capsule
clearance. Room queries use a room-floor core, slabs keep their requested XY, and the sparse clearance. Room queries use a room-floor core, slabs keep their requested XY, and the sparse
@@ -467,71 +539,70 @@ SurfaceWorld remains refused because its production height can depend on manager
context. It never touches a generator, operator stack, manager, cache, or mutable state. context. It never touches a generator, operator stack, manager, cache, or mutable state.
`FVoxelPassageLanding`, `VF_BuildPassageLanding`, `VF_EvaluatePassageLandingSDF`, and `FVoxelPassageLanding`, `VF_BuildPassageLanding`, `VF_EvaluatePassageLandingSDF`, and
`VF_IsPassageLandingFloor` (h:~56/~189, implemented near `.cpp:2059`) turn each inter-strate mouth `VF_IsPassageLandingFloor` (h:57/184-199, implemented near `.cpp:6468`) turn each inter-strate mouth
into a deterministic rounded room plus a hard support slab. The room is sized from the player into a deterministic rounded room plus a hard support slab. The room is sized from the player
capsule (minimum 12 voxels / 3 m flat floor, 12 voxels / 3 m clear height, and at least 14 voxels / capsule (minimum 24 voxels / 3 m flat floor and clear height); the `(0,0)` origin landing is enlarged
3.5 m authored width); the `(0,0)` origin landing is enlarged for the reserved future shaft, while for the reserved future shaft, while
inter-strate mouths remain local rooms and deliberately have no landing-to-root connector road. inter-strate mouths remain local rooms and deliberately have no landing-to-root connector road.
The manager's shared thread-local passage shortlist serves tube, room, and floor evaluation; no The manager's shared thread-local passage shortlist serves tube, room, and floor evaluation; no
landing search occurs per voxel. `GeneratePassages` stores the full descriptors and the landing search occurs per voxel. `GeneratePassages` stores the full descriptors and the
order-independence test compares them bit-for-bit. order-independence test compares them bit-for-bit.
Performance note (h:209-220): caching rooms/tunnels once per chunk instead of per Performance note (h:520-531): caching rooms/tunnels once per chunk instead of per
voxel is the single biggest CPU win. voxel is the single biggest CPU win.
### 3.8 Strate system ### 3.8 Strate system
**`Public/VoxelStrateTypes.h`** — shared structs/enums (1228 lines, the data vocabulary): **`Public/VoxelStrateTypes.h`** — shared structs/enums (1228 lines, the data vocabulary):
| Symbol | Line | Role | | Symbol | Line | Role |
|--------|------|------| |--------|------|------|
| `EVoxelPassageType` | 38 | Sloped/Vertical/Spiral/Cascading/Crack passage shapes. | | `EVoxelPassageType` | 42 | Sloped/Vertical/Spiral/Cascading/Crack passage shapes. |
| `ESurfaceType` | 78 | Floor/Wall/Ceiling/Any (decoration placement). | | `ESurfaceType` | 81 | Floor/Wall/Ceiling/Any (decoration placement). |
| `EVoxelNoiseType` | 99 | FBM/Ridged/Mixed/Cellular. | | `EVoxelNoiseType` | 128 | FBM/Ridged/Mixed/Cellular. |
| `ECaveGeneratorType` | 146 | TunnelNetwork / FlatPlain / CrystalChamber. | | `ECaveGeneratorType` | 175 | TunnelNetwork / FlatPlain / CrystalChamber. |
| `EVoxelStrateTransition` | 183 | Gradient / Hard / Interleaved boundary blends. | | `EVoxelStrateTransition` | 228 | Gradient / Hard / Interleaved boundary blends. |
| **`FStrateGenerationParams`** | ~350 | The giant TunnelNetwork param bag (rock, worms, rooms, tunnels, warp, roughness, live terrain-detail fields, dead terrain-op transport slots, boundary seal). `Lerp()` static blends two sets at boundaries — it expands the **`VF_STRATE_PARAM_FIELDS` X-macro** (defined just above the struct): **adding a field to the struct? add it to that list** or blends silently reset it to default. | | **`FStrateGenerationParams`** | 373 | The giant TunnelNetwork param bag (rock, worms, rooms, tunnels, warp, roughness, live terrain-detail fields, dead terrain-op transport slots, boundary seal). `Lerp()` static blends two sets at boundaries — it expands the **`VF_STRATE_PARAM_FIELDS` X-macro** (defined just above the struct): **adding a field to the struct? add it to that list** or blends silently reset it to default. |
| `FStrateTerrainOpEntry` | 965 | Soft-ptr to a terrain op + Weight + Probability. | | `FStrateTerrainOpEntry` | 1169 | Soft-ptr to a terrain op + Weight + Probability. |
| **`FSlabGenerationParams`** | 1019 | Floor/ceiling heights, roughness, columns, seal — for slab generators. | | **`FSlabGenerationParams`** | 1223 | Floor/ceiling heights, roughness, columns, seal — for slab generators. |
| **`FPlacementProfile`** | ~1747 | **Shared placement vocabulary** for every scatter primitive (`FStrateDecoration`, `FStrateLandmark`, coming `FStrateSetPiece`): spawn (ActorClass/InstancedMesh), Filter gates (surface/slope/overhang/water/RequiredBiome + **F7 awareness `Conditions[]`** — `FTerrainCondition` relief/moisture/biome-border predicates, AND-ed, evaluated by `Generator::EvaluateTerrainConditions`), Transform (align/offsets/RotationOffset+RandomRotation/scale), Render (cull/shadow). Each primitive embeds it as `Profile` + keeps only its own DISTRIBUTION fields. Per-primitive defaults set in each struct's ctor (deco: scale 0.8-1.2 + RandomRotation.Yaw=360; landmark: Ceiling + no align). | | **`FPlacementProfile`** | 2000 | **Shared placement vocabulary** for every scatter primitive (`FStrateDecoration`, `FStrateLandmark`; set-pieces are landmarks): spawn (ActorClass/InstancedMesh), Filter gates (surface/slope/overhang/water/RequiredBiome + **F7 awareness `Conditions[]`** — `FTerrainCondition` relief/moisture/biome-border predicates, AND-ed, evaluated by `Generator::EvaluateTerrainConditions`), Transform (align/offsets/RotationOffset+RandomRotation/scale), Render (cull/shadow). Each primitive embeds it as `Profile` + keeps only its own DISTRIBUTION fields. Per-primitive defaults set in each struct's ctor (deco: scale 0.8-1.2 + RandomRotation.Yaw=360; landmark: Ceiling + no align). |
| `FStrateDecoration` / `FStrateLandmark` | ~1830 / ~1900 | `Profile` + distribution: deco = StreamTier/SpawnDensity/MaxPerChunk; landmark = SpacingChunks/JitterFraction/SpawnProbability/StreamRadiusChunks + Light-Orb block. | | `FStrateDecoration` / `FStrateLandmark` | 2207 / 2271 | `Profile` + distribution: deco = StreamTier/SpawnDensity/MaxPerChunk; landmark = SpacingChunks/JitterFraction/SpawnProbability/StreamRadiusChunks + Light-Orb block. |
| `ELandmarkAnchor` (on `FStrateLandmark`) | ~1940 | F7: `AnchorMode` = HashLattice / PassageMouth + passage toggles + exclusion (`ExclusionRadiusChunks`/`Priority`) folded into `FStrateLandmark` (set-pieces merged in — one primitive, one `Landmarks` list, `UpdateLandmarks`). | | `ELandmarkAnchor` (on `FStrateLandmark`) | 2251 | F7: `AnchorMode` = HashLattice / PassageMouth + passage toggles + exclusion (`ExclusionRadiusChunks`/`Priority`) folded into `FStrateLandmark` (set-pieces merged in — one primitive, one `Landmarks` list, `UpdateLandmarks`). |
| `FStrateAmbientActor` / `FStrateCreature` | ~2090 / ~2110 | Content spawn entries (consumed by future systems). | | `FStrateAmbientActor` / `FStrateCreature` | 2405 / 2425 | Content spawn entries (consumed by future systems). |
**`Public/VoxelStrateDefinition.h`** — `UVoxelStrateDefinition : UPrimaryDataAsset` **`Public/VoxelStrateDefinition.h`** — `UVoxelStrateDefinition : UPrimaryDataAsset`
(line 36). One asset = one strate *type*. Fields: identity, `StrateHeightInChunks`(60), (line 46). One asset = one strate *type*. Fields: identity, `StrateHeightInChunks`(75),
`TransitionType`(79)/`TransitionBlendChunks`(89), `GeneratorType`(102), `TransitionType`(93)/`TransitionBlendChunks`(103), `GeneratorType`(116),
`GenerationParams`(113), `SlabParams`(124), `Biomes[]`+`BiomeMapParams` (the biome list + `GenerationParams`(137), `SlabParams`(148), `Biomes[]`+`BiomeMapParams` (the biome list +
field tuning — empty ⇒ unchanged world, §8.14), `TerrainOperations`(147), visuals/fog/light, field tuning — empty ⇒ unchanged world, §8.14), `TerrainOperations`(252), visuals/fog/light,
content lists, audio, `GameplayTags`(223), `bUseOperatorStack` (the OPSTACK A/B opt-in — only bites content lists, audio, `GameplayTags`(393). EditConditions show/hide param groups by generator
if the archetype is in `UsesOperatorStackForChunk`'s ported list). EditConditions show/hide param type.
groups by generator type.
**`Public/VoxelStrateManager.h` + `.cpp`** — `UVoxelStrateManager : UObject` (h:108). **`Public/VoxelStrateManager.h` + `.cpp`** — `UVoxelStrateManager : UObject` (h:152).
Maps depth→strate at runtime; owns passages. Maps depth→strate at runtime; owns passages.
- `FVoxelPassage` (h:39): standing endpoints, radius, type, wandering control points, and - `FVoxelPassage` (h:46): standing endpoints, radius, type, wandering control points, and
upper/lower local landing descriptors (room, floor, and door; no root connector). upper/lower local landing descriptors (room, floor, and door; no root connector).
- `FStrateSlot` (h:84): definition + chunk-Z range + index. - `FStrateSlot` (h:128): definition + chunk-Z range + index.
| Method | .cpp line | Role | | Method | .cpp line | Role |
|--------|-----------|------| |--------|-----------|------|
| `Initialize` | 10 | Keeps the existing sequencing through `GeneratePassages`, but selects one slot source: a validated cooked season (exact ordered bounds/vector/recipe) or the unchanged authored fixed+sorted/shuffled pool. Assigned-invalid seasons fail closed. Authored source definitions are duplicated as content bags; generated slots use deterministic C++ defaults and zero auto-passages because schema 2 does not store passage configuration. | | `Initialize` | 1164 | Keeps the existing sequencing through `GeneratePassages`, but selects one slot source: a validated cooked season (exact ordered bounds/vector/recipe) or the unchanged authored fixed+sorted/shuffled pool. Assigned-invalid seasons fail closed. Authored source definitions are duplicated as content bags; generated slots use deterministic C++ defaults and zero auto-passages because schema 2 does not store passage configuration. |
| `GeneratePassages` | 247 | Deterministic passages between consecutive strates (per-strate `PassageConfig::Style` control points; auto passages retain the existing `EVoxelPassageType` default); placement and shape values are independently salted hashes of seed + boundary slot + connection index. Each mouth independently queries its own strate's pure player-fit `VF_SuggestLandingPoint` with its contract seed (cave room salt, otherwise world seed), may move within its archetype-specific budget, then receives a body-sized local room/floor. The origin landing is reserved for the future shaft; no landing-to-root road is generated. Unsupported/no-fit answers preserve the old random reach; a deterministic level dog-leg is inserted when the final ramp needs it to stay ≤44°. All descriptors and bounds are generated without reading another passage. | | `GeneratePassages` | 1659 | Deterministic passages between consecutive strates (per-strate `PassageConfig::Style` control points; auto passages retain the existing `EVoxelPassageType` default); placement and shape values are independently salted hashes of seed + boundary slot + connection index. Each mouth independently queries its own strate's pure player-fit `VF_SuggestLandingPoint` with its contract seed (cave room salt, otherwise world seed), may move within its archetype-specific budget, then receives a body-sized local room/floor. The origin landing is reserved for the future shaft; no landing-to-root road is generated. Unsupported/no-fit answers preserve the old random reach; a deterministic level dog-leg is inserted when the final ramp needs it to stay ≤44°. All descriptors and bounds are generated without reading another passage. |
| `EvaluateModifierSDF` | 357 | SDF of passage tubes plus local landing rooms at a point. A per-chunk `thread_local` shortlist (`PassagesVersion`-stamped) → far chunks return `FLT_MAX` without walking `Passages`; the same cache serves floor membership. `FPassageEvaluationCache::FloorProjections` reuses native/generic floor projections and walkable-air results once per `(sample, passage)` using exact double XYZ equality and unchanged float outputs; `voxel.FloorRound1PassageProjectionCache=0` disables only this A/B cache. §8.10. | | `EvaluateModifierSDF` | 2643 | SDF of passage tubes plus local landing rooms at a point. A per-chunk `thread_local` shortlist (`PassagesVersion`-stamped) → far chunks return `FLT_MAX` without walking `Passages`; the same cache serves floor membership. `FPassageEvaluationCache::FloorProjections` reuses native/generic floor projections and walkable-air results once per `(sample, passage)` using exact double XYZ equality and unchanged float outputs; `voxel.FloorRound1PassageProjectionCache=0` disables only this A/B cache. §8.10. |
| `ApplyPassageModifier` | 1319 | Shared legacy/op-stack passage post: carves tube and local landing air through the idempotent landing primitive, then restores the local landing support slab in internal positive-solid density. | | `ApplyPassageModifier` | 2772 | Shared legacy/op-stack passage post: carves tube and local landing air through the idempotent landing primitive, then restores the local landing support slab in internal positive-solid density. |
| `VF_ApplyPassageLandingCarving` | — | Idempotent density-independent landing-air threshold shared by the structural pass and the post-disturbance MC backstop; keeps legacy and operator-stack results bit-identical. | | `VF_ApplyPassageLandingCarving` | — | Idempotent density-independent landing-air threshold shared by the structural pass and the post-disturbance MC backstop, so the two agree bit for bit. |
| `ApplyPassageLandingFloorMC` | 1347 | Reasserts the same support slab after MC-space disturbances and before the final XY edge seal. | | `ApplyPassageLandingFloorMC` | 2983 | Reasserts the same support slab after MC-space disturbances and before the final XY edge seal. |
| `AnyPassageNearBox` | — | Conservative sphere-vs-AABB test of every passage's bound against a voxel box (+carve blend pad). Per TILE (ClassifyTile guard), never per voxel. | | `AnyPassageNearBox` | — | Conservative sphere-vs-AABB test of every passage's bound against a voxel box (+carve blend pad). Per TILE (ClassifyTile guard), never per voxel. |
| `AnyPassageLandingFloorNearBox` | — | Conservative local-landing-floor AABB guard. It kills the `AllAir` hypothesis wherever a proved landing floor can occur; the op-stack returns `Both` for the same box. | | `AnyPassageLandingFloorNearBox` | — | Conservative local-landing-floor AABB guard. It kills the `AllAir` hypothesis wherever a proved landing floor can occur; the op-stack returns `Both` for the same box. |
| `FindSlotIndexForChunkZ` | 427 | Z → layout index. | | `FindSlotIndexForChunkZ` | 6111 | Z → layout index. |
| `GetStrateAt` / `GetStrateIndex` | 443 / 455 | World-Z queries. | | `GetStrateAt` / `GetStrateIndex` | 6127 / 6139 | World-Z queries. |
| `GetLayoutVersion` | h:161 (inline) | Layout/passage generation counter (= `PassagesVersion`, bumped by every `Initialize` and by the editor composer override). Hot-path callers key `thread_local` memos on it (strate-index memo in `GetDensityWithParams`, passage shortlist) so live changes never serve stale data. | | `GetLayoutVersion` | h:250 (inline) | Layout/passage generation counter (= `PassagesVersion`, bumped by every `Initialize` and by the editor composer override). Hot-path callers key `thread_local` memos on it (strate-index memo in `GetDensityWithParams`, passage shortlist) so live changes never serve stale data. |
| `GetStrateForChunk` | 466 | Chunk → definition. | | `GetStrateForChunk` | 6150 | Chunk → definition. |
| `GetGeneratorTypeForChunk` | 476 | Chunk → generator type. | | `GetGeneratorTypeForChunk` | 6176 | Chunk → generator type. |
| `UsesOperatorStackForChunk` | 559 | Chunk → should `GetDensityAt` take the operator stack? `bUseOperatorStack` on the definition **AND** archetype in the ported list — **now all 8 of 8** (Maze, FlatPlain, CrystalChamber, SurfaceWorld, VerticalShafts, FloatingIslands, TunnelNetwork, Underwater). **That list is written down here and nowhere else.** With every archetype ported the flag is now the *only* thing that decides the path, so ticking the box is no longer a no-op anywhere — it is a real switch onto the operator stack for that strate. | | `UsesOperatorStackForChunk` | 6203 | Chunk → does its slot have an operator stack? True for any in-layout slot with a definition whose archetype is one of the 8 (Maze, FlatPlain, CrystalChamber, SurfaceWorld, VerticalShafts, FloatingIslands, TunnelNetwork, Underwater); false outside the layout. **That list is written down here and nowhere else.** `ClassifyTile` guards on it; `GetDensityAt` uses it to share TunnelNetwork stacks by chunk Z. |
| `GetRecipeForChunk` | — | Runtime immutable recipe/vector/strate-seed copy for cooked seasons; editor slot overrides use the same worker hand-off. | | `GetRecipeForChunk` | — | Runtime immutable recipe/vector/strate-seed copy for cooked seasons; editor slot overrides use the same worker hand-off. |
| `GetSlabParamsForChunk` | 490 | Slab params with runtime Z bounds (no blend — slabs use Hard). | | `GetSlabParamsForChunk` | 6263 | Slab params with runtime Z bounds (no blend — slabs use Hard). |
| `GetBiomeContextForChunk` | — | Flatten the strate's `Biomes[]` + `BiomeMapParams` into a POD `FBiomeContext` for the biome field. Empty ⇒ biomes disabled. §8.14. | | `GetBiomeContextForChunk` | — | Flatten the strate's `Biomes[]` + `BiomeMapParams` into a POD `FBiomeContext` for the biome field. Empty ⇒ biomes disabled. §8.14. |
| `GetGenerationParams` | 515 | **Blended** TunnelNetwork params (handles Gradient/Hard/Interleaved transitions). | | `GetGenerationParams` | 6509 | **Blended** TunnelNetwork params (handles Gradient/Hard/Interleaved transitions). |
| `SetComposerOverrideForStrate` / `GetComposerOverrideForChunk` | editor-only | Temporary density-only candidate overlay for one existing slot. Copies immutable params/recipe into the worker refetch path, leaves layout/content/passages intact, and bumps `PassagesVersion` to invalidate generator memos. | | `SetComposerOverrideForStrate` / `GetComposerOverrideForChunk` | editor-only | Temporary density-only candidate overlay for one existing slot. Copies immutable params/recipe into the worker refetch path, leaves layout/content/passages intact, and bumps `PassagesVersion` to invalidate generator memos. |
| `BuildParamsFromDefinition` (static) | 1399 | Returns the definition's base `GenerationParams` only. Terrain-op assets are passed separately to `BuildChunkCache`, which applies one selected op per room and pre-bakes Column/Pit/Chimney features. | | `BuildParamsFromDefinition` (static) | 6815 | Returns the definition's base `GenerationParams` only. Terrain-op assets are passed separately to `BuildChunkCache`, which applies one selected op per room and pre-bakes Column/Pit/Chimney features. |
**`Public/VoxelTerrainOpDefinition.h` + `.cpp`** — `UVoxelTerrainOpDefinition : UPrimaryDataAsset` **`Public/VoxelTerrainOpDefinition.h` + `.cpp`** — `UVoxelTerrainOpDefinition : UPrimaryDataAsset`
(h:67). One asset = one terrain op. `EVoxelTerrainOpType` (h:36): Terrace, LayerLines, (h:67). One asset = one terrain op. `EVoxelTerrainOpType` (h:36): Terrace, LayerLines,
@@ -556,7 +627,7 @@ atmosphere override, `WaterMaterial`, `MaterialPaletteIndex` (F6 — baked to ve
`UVoxelStrateDefinition::Biomes[]`. Generator-agnostic (surface biomes now, cave biomes later). §8.14. `UVoxelStrateDefinition::Biomes[]`. Generator-agnostic (surface biomes now, cave biomes later). §8.14.
**`Public/VoxelStrateComposer.h` + `Private/VoxelStrateComposer.cpp`** (Tier 4a/4b + Tier 5) — offline **`Public/VoxelStrateComposer.h` + `Private/VoxelStrateComposer.cpp`** (Tier 4a/4b + Tier 5) — offline
`FVoxelStrateCorpus`, `VF_RollStrateParams`, and `VF_RollStrateStructure`. `LoadFromAssetRegistry` enumerates every project `FVoxelStrateCorpus`, `VF_RollStrateParamsDetailed`, and `VF_RollStrateStructure`. `LoadFromAssetRegistry` enumerates every project
`UVoxelStrateDefinition` through the Asset Registry, excludes `Saved/Autosaves` and `Saved/Cooked` `UVoxelStrateDefinition` through the Asset Registry, excludes `Saved/Autosaves` and `Saved/Cooked`
copies, and adds one `VoxelStrateTypes.h` default vector for each of the eight exact archetypes. copies, and adds one `VoxelStrateTypes.h` default vector for each of the eight exact archetypes.
The corpus is grouped by exact `ECaveGeneratorType`: the 2026-09-04 run loaded **4 project The corpus is grouped by exact `ECaveGeneratorType`: the 2026-09-04 run loaded **4 project
@@ -668,31 +739,31 @@ text plus independently copied metadata/hash, and retains soft references to any
`UVoxelSettings::Season` is the only activation switch. `UVoxelSettings::Season` is the only activation switch.
### 3.9 Player edits — `Public/VoxelDiffLayer.h` + `.cpp` ### 3.9 Player edits — `Public/VoxelDiffLayer.h` + `.cpp`
`UVoxelDiffLayer : UObject` (h:77). Stores `FVoxelModification` (h:43: Center/Radius/Strength; `UVoxelDiffLayer : UObject` (h:137). Stores `FVoxelModification` (h:60: Center/Radius/Strength;
**negative Strength = carve, positive = fill**) grouped by chunk in `TMap ChunkMods`. **negative Strength = carve, positive = fill**) grouped by chunk in `TMap ChunkMods`.
| Method | .cpp line | Role | | Method | .cpp line | Role |
|--------|-----------|------| |--------|-----------|------|
| `SetBudget` | 10 | From VoxelSettings carving caps. | | `SetBudget` | 102 | From VoxelSettings carving caps. |
| `CanModify` | 20 | Budget check (no consume) — for UI. | | `CanModify` | 112 | Budget check (no consume) — for UI. |
| `GetRemainingModifications` / `GetRemainingVolume` | 47 / 53 | -1 = unlimited. | | `GetRemainingModifications` / `GetRemainingVolume` | 169 / 175 | -1 = unlimited. |
| `ApplyModification` | 63 | Enforces budget, stores in all overlapped chunks, returns dirty coords. | | `ApplyModification` | 185 | Enforces budget, stores in all overlapped chunks, returns dirty coords. |
| `GetDensityOffset` | 131 | Per-voxel combined diff (smoothstep falloff, additive). | | `GetDensityOffset` | 299 | Per-voxel combined diff (smoothstep falloff, additive). |
| `HasModifications` | 160 | Fast reject for hot path. | | `HasModifications` | 421 | Fast reject for hot path. |
| `HasAnyMods` / `GetModsVersion` | h:238 / h:241 (inline) | Lock-free atomics: any-mod-exists flag + monotonic mod-state version (bumped by `ApplyModification`/`Clear`). | | `HasAnyMods` / `GetModsVersion` | h:246 / h:249 (inline) | Lock-free atomics: any-mod-exists flag + monotonic mod-state version (bumped by `ApplyModification`/`Clear`). |
| `GetChunkModsSnapshot` | — | Copy one chunk's mod list under ONE read lock. Workers snapshot per (chunk, version) instead of locking per voxel — `GetDensityAt` keys a `thread_local` 64-slot direct-mapped cache on it (~27 lock ops per tile task instead of ~86k once any carve exists). | | `GetChunkModsSnapshot` | — | Copy one chunk's mod list under ONE read lock. Workers snapshot per (chunk, version) instead of locking per voxel — `GetDensityAt` keys a `thread_local` 64-slot direct-mapped cache on it (~27 lock ops per tile task instead of ~86k once any carve exists). |
| `HasAnyModInChunkRange` | — | Any modified chunk key in an inclusive chunk box? One key walk under a read lock — ClassifyTile's diff guard (per tile, conservative by construction: mods are stored in every chunk their radius overlaps). | | `HasAnyModInChunkRange` | — | Any modified chunk key in an inclusive chunk box? One key walk under a read lock — ClassifyTile's diff guard (per tile, conservative by construction: mods are stored in every chunk their radius overlaps). |
| `EvaluateMods` (static) | — | Lock-free pure evaluation of a mod list at a voxel — shared core of `GetDensityOffset` and the generator's snapshot path. | | `EvaluateMods` (static) | — | Lock-free pure evaluation of a mod list at a voxel — shared core of `GetDensityOffset` and the generator's snapshot path. |
| `Clear` | 170 | Wipe all (season reset). | | `Clear` | 435 | Wipe all (season reset). |
| `GetTotalModificationCount` / `GetModifiedChunkCount` | 182 / 192 | Stats. | | `GetTotalModificationCount` / `GetModifiedChunkCount` | 454 / 465 | Stats. |
### 3.10 Mesher — `Public/VoxelMarchingCubesMesher.h` + `.cpp` ### 3.10 Mesher — `Public/VoxelMarchingCubesMesher.h` + `.cpp`
`UVoxelMarchingCubesMesher : UObject` (h:21). Holds `Generator` ptr, `IsoLevel=0`, skirt params. `UVoxelMarchingCubesMesher : UObject` (h:42). Holds `Generator` ptr, `IsoLevel=0`, skirt params.
(The dead trio `GetDensity`/`InterpolateEdge`/`ComputeGradientNormal` + `GradientOffset` was The pre-sampled grid supplies positions AND gradients inline (T1.b).
removed — since T1.b the pre-sampled grid supplies positions AND gradients inline.)
| Method | .cpp line | Role | | Method | .cpp line | Role |
|--------|-----------|------| |--------|-----------|------|
| **`GenerateSheetMesh`** | ~455 | **F18 far-field SHEET** (render-distance ring, `Tile.Level > MaxClipLevel`): two displaced heightfield grids per tile — ground (polygroup 0) + sky-cap (polygroup 1) from `GetSurfaceHeightAt` columns (StrateChunkZ = band mid identifies the strate; non-SurfaceWorld ⇒ empty). Classes true BY CONSTRUCTION (no vote/probes). Same conventions as `GenerateMesh` (world-cm positions, planar UVs, F6 colour masks, −N double-faced perimeter skirts per bucket, ground‖cap + `NumCeilingTriangles`). Margin ring keeps normals continuous between sheets. XY HOLE params: cells fully inside the MC-covered box around the player (`AVoxelWorld::SheetHole*Vox`, shrunk 1 tile for seam overlap) are skipped — a partially-covered sheet must not overlay near terrain; hole-edge cells get no skirt. Carved features (passages/spine/chasms) + diff layer NOT represented — accepted at sheet distance. | | **`GenerateMesh`** | 96 | The MC loop over cells; `Step` controls LOD sampling. Sets the T2.b octave bias for the tile (`TGuardValue` on `VoxelGenLOD::OctaveBias`, from `LODOctaveDrop` × log2(Step); 0 at LOD0/off). Edge `t` + grid-gradient normals computed inline (`SampleG`/`GradAt`). Optional `OutCaptureGrid` (4th arg) captures the already-sampled `CHUNK_SIZE³` R8 density clipmap. Optional `OutPreEditDensityGrid` captures exact float samples before player edits for full-res L0 tiles; optional `InSharedDensityGrid` is per-call (not the mesher's shared mutable setting) so sync carve reuse cannot race async tile tasks. Capture/reuse leaves §8.10's thread-local scratch, two-pass MC and density-grid semantics intact. **F17 surface class**: each unique vertex is classified sol/sky-cap in `GetOrCreateVertex` (down-facing only → memoized `GetSurfaceHeightAt`, nearer `CeilSurf` = cap); triangles bucket by majority into `GroundTris`/`CapTris` (thread_local), skirts emit per bucket and increment diagnostic `NumSkirtTriangles`, then `Triangles = ground‖cap` + `FVoxelMeshData::NumCeilingTriangles` (→ polygroups in `BuildTileStreamSet`). Same tris, only index ORDER changes. STRATE CONTENT CUT: optional `BandZMin/MaxVox` params restrict the cz cell loop (+ gz sampling rows) to the player-strate band — coarse straddling tiles mesh ONE strate; meshed cells bit-identical. |
| **`GenerateMesh`** | ~15 | The MC loop over cells; `Step` controls LOD sampling. Sets the T2.b octave bias for the tile (`TGuardValue` on `VoxelGenLOD::OctaveBias`, from `LODOctaveDrop` × log2(Step); 0 at LOD0/off). Edge `t` + grid-gradient normals computed inline (`SampleG`/`GradAt`). Optional `OutCaptureGrid` (4th arg) = CAPTURE-DURING-MESHING: when non-null + full-res (`CellsPerAxis==CHUNK_SIZE`), copies the already-sampled `CHUNK_SIZE³` density grid (quantized via `VF_QuantizeDensity`, VoxelTypes.h) so the density clipmap reuses it instead of re-sampling `GetDensityAt`. Pure read of the grid — §8.10 untouched. **F17 surface class**: each unique vertex is classified sol/sky-cap in `GetOrCreateVertex` (down-facing only → memoized `GetSurfaceHeightAt`, nearer `CeilSurf` = cap); triangles bucket by majority into `GroundTris`/`CapTris` (thread_local), skirts emit per bucket, then `Triangles = ground‖cap` + `FVoxelMeshData::NumCeilingTriangles` (→ polygroups in `BuildTileStreamSet`). Same tris, only index ORDER changes. STRATE CONTENT CUT: optional `BandZMin/MaxVox` params restrict the cz cell loop (+ gz sampling rows) to the player-strate band — coarse straddling tiles mesh ONE strate (kills far-LOD inter-strate aliasing holes); meshed cells bit-identical. | | `FVoxelMeshData::NumSurfaceVertices` | 375 | Prefix length for density-isosurface vertices before seam skirt vertices are appended. The test-only visual-gap audit samples this prefix so artificial skirts cannot become expected terrain points. |
| `FVoxelMeshData::NumSkirtTriangles` | — | Counts skirt triangles appended by `GenerateMesh`; consumed only by opt-in collision-cook stage reports. |
**`Public/MarchingCubesTables.h`** — `EdgeTable` + `TriTable` reference data (Paul **`Public/MarchingCubesTables.h`** — `EdgeTable` + `TriTable` reference data (Paul
Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs editing. Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs editing.
@@ -702,7 +773,7 @@ Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs ed
|------|------| |------|------|
| `Public/Private/VoxelContentManager.h/.cpp` | `UVoxelContentManager` — distance-based world-grid decoration scatter (no LOD pop, surface-snapped via `GetDensityAt`) + level-0 water planes. **F7 companions:** each `FStrateDecoration` may list `FDecoCompanion` satellites (rocks/mushrooms around a tree) — emitted per placed parent in `PlaceAtCrossing`, deterministic (pure fn of the parent hash), inherit the parent's surface point; `FDecoSpawn::CompanionIdx` routes each back to its `Companions[ci].Profile` at apply. **TWO streaming grids** (`FDecoGrid` Near/Far, picked per entry via `FStrateDecoration::StreamTier`): NearGrid = short radius + fine column grid (groundcover); FarGrid = full radius + coarse grid (cheap rare/large props). **Plus `UpdateLandmarks`** — rare deliberately-placed objects: mini-suns, ruins, shrines, monuments (`FStrateLandmark`; set-pieces folded in 2026-07-06). Per entry an `AnchorMode`: **HashLattice** (coarse hash lattice, cell = SpacingChunks chunks → cheap at any radius, no per-chunk freeze) or **PassageMouth** (`GetPassages()` endpoints landing in this strate). Gated by `Profile` + `Profile.Conditions`; optional deterministic **exclusion radius** (Priority+hash rank, `ExclusionRadiusChunks`; 0 = off → pure scatter is unchanged, skips the O(n²) resolve). Synchronous, deterministic, strate-wide; spawns via `SpawnFromProfile` (+ orb wrapper). Exclusion is POP-FREE (gathers a `MaxExcl` ring of suppress-only candidates so a piece's fate is player-position-independent). `bSuppressDecorationsUnder`/`SuppressRadiusChunks` clear grass under a footprint (on spawn + re-cleared in `ApplyRegion`). **`RemoveDecorationsInSphere`** (world sphere → both grids' HISMs via `GetInstancesOverlappingSphere`+`RemoveInstances`) is the shared primitive, also called by `AVoxelWorld::ApplyModification` so player digging removes floating grass instantly. Owned by `AVoxelWorld`. §8.5. | | `Public/Private/VoxelContentManager.h/.cpp` | `UVoxelContentManager` — distance-based world-grid decoration scatter (no LOD pop, surface-snapped via `GetDensityAt`) + level-0 water planes. **F7 companions:** each `FStrateDecoration` may list `FDecoCompanion` satellites (rocks/mushrooms around a tree) — emitted per placed parent in `PlaceAtCrossing`, deterministic (pure fn of the parent hash), inherit the parent's surface point; `FDecoSpawn::CompanionIdx` routes each back to its `Companions[ci].Profile` at apply. **TWO streaming grids** (`FDecoGrid` Near/Far, picked per entry via `FStrateDecoration::StreamTier`): NearGrid = short radius + fine column grid (groundcover); FarGrid = full radius + coarse grid (cheap rare/large props). **Plus `UpdateLandmarks`** — rare deliberately-placed objects: mini-suns, ruins, shrines, monuments (`FStrateLandmark`; set-pieces folded in 2026-07-06). Per entry an `AnchorMode`: **HashLattice** (coarse hash lattice, cell = SpacingChunks chunks → cheap at any radius, no per-chunk freeze) or **PassageMouth** (`GetPassages()` endpoints landing in this strate). Gated by `Profile` + `Profile.Conditions`; optional deterministic **exclusion radius** (Priority+hash rank, `ExclusionRadiusChunks`; 0 = off → pure scatter is unchanged, skips the O(n²) resolve). Synchronous, deterministic, strate-wide; spawns via `SpawnFromProfile` (+ orb wrapper). Exclusion is POP-FREE (gathers a `MaxExcl` ring of suppress-only candidates so a piece's fate is player-position-independent). `bSuppressDecorationsUnder`/`SuppressRadiusChunks` clear grass under a footprint (on spawn + re-cleared in `ApplyRegion`). **`RemoveDecorationsInSphere`** (world sphere → both grids' HISMs via `GetInstancesOverlappingSphere`+`RemoveInstances`) is the shared primitive, also called by `AVoxelWorld::ApplyModification` so player digging removes floating grass instantly. Owned by `AVoxelWorld`. §8.5. |
| `Public/Private/VoxelAtmosphereManager.h/.cpp` | `UVoxelAtmosphereManager` — per-strate fog/skylight + persistent ceiling/floor layer actors + full `AtmosphereActor` override. Owned by `AVoxelWorld`. §8.6. | | `Public/Private/VoxelAtmosphereManager.h/.cpp` | `UVoxelAtmosphereManager` — per-strate fog/skylight + persistent ceiling/floor layer actors + full `AtmosphereActor` override. Owned by `AVoxelWorld`. §8.6. |
| `Public/Private/VoxelDensityVolume.h/.cpp` | `UVoxelDensityVolume` — player-centred DENSITY CLIPMAP (N toroidal R8 levels, fine near / coarse far) streamed to GPU `UVolumeTexture`s for the mini-sun raymarched shadow march. Fills run on ONE dedicated thread (`FVoxelDensityFillRunnable`, off the task pool); level 0 is mostly fed by CAPTURE-DURING-MESHING (mesher grid reuse, gated by `IsTileCaptureUseful` so only tiles near the shadow window pay the capture). Carve → `MarkDirtyVoxelBox` refills locally. `VolumeEpoch` drops stale fills. Owned by `AVoxelWorld` (`bEnableDensityVolume`); shader params pushed via shared per-base-material MIDs (`AVoxelWorld::UpdateTerrainMaterialParams`, change-detected). | | `Public/Private/VoxelDensityVolume.h/.cpp` | `UVoxelDensityVolume` — opt-in player-centred DENSITY CLIPMAP (`bEnableDensityVolume` defaults false) with N toroidal R8 levels streamed to GPU `UVolumeTexture`s for mini-sun shadows. When enabled, fills run on ONE dedicated thread (`FVoxelDensityFillRunnable`); level 0 is mostly fed by CAPTURE-DURING-MESHING (gated by `IsTileCaptureUseful`), and carve → `MarkDirtyVoxelBox` refills locally. `VolumeEpoch` drops stale fills. When disabled, no object/thread/fill/upload/Tick update/capture/proof bypass exists; `SetDensityVolumeMaterialParamsDisabled` sets `VolP0..4`/`VolP6..9` black, `VolP5=(0,25,0)`, and `VolTex0..2` null on shared per-base MIDs. `SetVolumeParamsOnMID` carries these values to newly created MIDs. |
> The big 2026 redesign (8 archetypes, (0,0) spine, inter-strate gap, per-strate passages, > The big 2026 redesign (8 archetypes, (0,0) spine, inter-strate gap, per-strate passages,
> disturbances, content/atmosphere, brush shapes, perf invariants) is documented in **§8** — > disturbances, content/atmosphere, brush shapes, perf invariants) is documented in **§8** —
@@ -711,8 +782,10 @@ Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs ed
--- ---
### 3.12 Automation tests — `Private/Tests/` (added 2026-07-27, `#if WITH_DEV_AUTOMATION_TESTS`) ### 3.12 Automation tests — `Private/Tests/` (added 2026-07-27, `#if WITH_DEV_AUTOMATION_TESTS`)
The plugin's first tests (`OPSTACK-PLAN.md` Phase 0.5). Run them from the editor's The plugin's first tests (`Docs/archive/OPSTACK-PLAN.md` Phase 0.5). Run them from the editor's
**Session Frontend → Automation**, filter `VoxelForge`. **Session Frontend → Automation**, filter `VoxelForge`. The `OpStack.*Equivalence` names are
historical: those tests check the shipped stacks themselves and no longer compare against a
second density path.
| File | Test name | What it proves | | File | Test name | What it proves |
|------|-----------|----------------| |------|-----------|----------------|
@@ -724,61 +797,235 @@ The plugin's first tests (`OPSTACK-PLAN.md` Phase 0.5). Run them from the editor
| `VoxelForgeClassifyTileTest.cpp` | `VoxelForge.OpStack.BoxVerdictFold` | Pure-logic walk of the fold in `VoxelDensityOp.h`, case by case — including the seal-forces-AllSolid case that justifies `ClassifyBox` existing. Also the only `.cpp` that includes the op header, so the build actually sees it. | | `VoxelForgeClassifyTileTest.cpp` | `VoxelForge.OpStack.BoxVerdictFold` | Pure-logic walk of the fold in `VoxelDensityOp.h`, case by case — including the seal-forces-AllSolid case that justifies `ClassifyBox` existing. Also the only `.cpp` that includes the op header, so the build actually sees it. |
| `VoxelForgeHeightStackTest.cpp` | `VoxelForge.OpStack.SurfaceHeightEquivalence` | The height-space stack vs `ComputeSurfaceTerrainZ`, in **altitudes**. Runs twice: defaults, then **all F20 terrain ops ON** — the load-bearing pass, since the ops are off by default and the defaults pass exercises only the structural source. Also brute-forces `MaxDisplacement` (a false bound would be a hole). Bar is bit-identity; a height delta is a visibly different world, not rounding. | | `VoxelForgeHeightStackTest.cpp` | `VoxelForge.OpStack.SurfaceHeightEquivalence` | The height-space stack vs `ComputeSurfaceTerrainZ`, in **altitudes**. Runs twice: defaults, then **all F20 terrain ops ON** — the load-bearing pass, since the ops are off by default and the defaults pass exercises only the structural source. Also brute-forces `MaxDisplacement` (a false bound would be a hole). Bar is bit-identity; a height delta is a visibly different world, not rounding. |
| `VoxelForgeCrossPlatformTest.cpp` | `VoxelForge.Determinism.CrossPlatformDigest` | SHAPE digest (sign of density = the world) + FIELD digest (bit-for-bit) over a fixed integer grid, plus `NearIso` bounding how many samples could flip sign. Reports rather than asserts until pinned. Run on Windows and Linux and compare. | | `VoxelForgeCrossPlatformTest.cpp` | `VoxelForge.Determinism.CrossPlatformDigest` | SHAPE digest (sign of density = the world) + FIELD digest (bit-for-bit) over a fixed integer grid, plus `NearIso` bounding how many samples could flip sign. Reports rather than asserts until pinned. Run on Windows and Linux and compare. |
| `VoxelForgeOpStackSlabTest.cpp` | `VoxelForge.OpStack.SlabEquivalence` | **Phase 2's first port.** The same 6-op slab stack vs `GetSlabDensity` over 20k points, run twice — FlatPlain **and** CrystalChamber — which is what demonstrates the two archetypes really are one op. Plus window-invariance and box-verdict brute force. Compares against the reference **as it is now** (post Z-term removal), so green = pure refactor and any visual delta is attributable to §3.1 alone. | | `VoxelForgeOpStackSlabTest.cpp` | `VoxelForge.OpStack.SlabEquivalence` | The same 6-op slab stack run three times — FlatPlain, CrystalChamber and a tuned CrystalChamber (the worst case for the `ClassifyBox` amplitude bounds): finite density over 20k points, window-invariance across order and threads, and box-verdict brute force. |
| `VoxelForgeOpStackTunnelTest.cpp` | `VoxelForge.OpStack.TunnelNetworkSpineEquivalence` | **Stage A of the last port.** Zeroes the 13 detail-op amplitudes so the *original* takes the path stage A ported — that is what makes an incomplete stack verifiable now. Samples in **clusters** (24 chunks × 250 points), because the SDF cache rebuilds when a query leaves its box and uniform sampling would rebuild per point on both paths. Check 3 (two param sets, A/B interleaved) compares each stack **to itself alone, never to the original** — the original would fail it, see AUDIT §C2. Asserts **zero** box verdicts, which is the honest stage-A result. | | `VoxelForgeOpStackTunnelTest.cpp` | `VoxelForge.OpStack.TunnelNetworkSpineEquivalence` | **Stage A of the last port.** Zeroes the 13 detail-op amplitudes so the *original* takes the path stage A ported — that is what makes an incomplete stack verifiable now. Samples in **clusters** (24 chunks × 250 points), because the SDF cache rebuilds when a query leaves its box and uniform sampling would rebuild per point on both paths. Check 3 (two param sets, A/B interleaved) compares each stack **to itself alone, never to the original** — the original would fail it, see AUDIT §C2. Asserts **zero** box verdicts, which is the honest stage-A result. |
| `VoxelForgeOpStackShaftTest.cpp` | `VoxelForge.OpStack.VerticalShaftEquivalence` | The port that tests **reuse**, not fidelity: three of the five ops are Maze's, unchanged. Forces connectors + ledges on, because both are off or negligible at defaults and a resting param is an untested operator. The source and mirror now share a deterministic inner-3×3-emitted drainage tree whose parents come from rebuild-only ±2/±3 windows in a wider direct-indexed halo (default 9×9 emit / 15×15 roll), plus the old random links. Tree connector Z is exact and ledge-free; tree and random capsules are spatially culled from the cached rebuild. **Fixed 2026-07-29:** its box query used to return `CarveOnly` because a shaft merely *existed* within a `Spacing*1.6` halo — true almost everywhere at `ShaftSpacing 55 / ShaftDensity 0.6`, hence **0 of 60** tiles. It now publishes an isolated interval for tree and random capsules (same row-major cell order ⇒ same `VoxelHash::Pair`, so symmetry of `Pair()` is not assumed), with **Z exact** and XY conservative; the ledge and converter decide their own effects. The sampler was also widened from ±48 voxels to ±440. Every proved tile is brute-forced over its full lattice, and the test reports rebuild-vs-hot cache cost. | | `VoxelForgeOpStackShaftTest.cpp` | `VoxelForge.OpStack.VerticalShaftEquivalence` | The port that tests **reuse**, not fidelity: three of the five ops are Maze's, unchanged. Forces connectors + ledges on, because both are off or negligible at defaults and a resting param is an untested operator. The source and mirror now share a deterministic inner-3×3-emitted drainage tree whose parents come from rebuild-only ±2/±3 windows in a wider direct-indexed halo (default 9×9 emit / 15×15 roll), plus the old random links. Tree connector Z is exact and ledge-free; tree and random capsules are spatially culled from the cached rebuild. **Fixed 2026-07-29:** its box query used to return `CarveOnly` because a shaft merely *existed* within a `Spacing*1.6` halo — true almost everywhere at `ShaftSpacing 55 / ShaftDensity 0.6`, hence **0 of 60** tiles. It now publishes an isolated interval for tree and random capsules (same row-major cell order ⇒ same `VoxelHash::Pair`, so symmetry of `Pair()` is not assumed), with **Z exact** and XY conservative; the ledge and converter decide their own effects. The sampler was also widened from ±48 voxels to ±440. Every proved tile is brute-forced over its full lattice, and the test reports rebuild-vs-hot cache cost. |
| `VoxelForgeOpStackIslandTest.cpp` | `VoxelForge.OpStack.FloatingIslandEquivalence` | The port that runs the stack **backwards** — void + fill vs rock + carve, same classes with the opposite sign. Counts interior-solid and open-void samples separately (on this archetype an aggregate "N solid" is dominated by the seal bands and says nothing about the islands). Counts `AllSolid` and `AllAir` verdicts **separately** too: `AllAir` is the one no cave archetype could ever prove, and it is the entire perf argument here. | | `VoxelForgeOpStackIslandTest.cpp` | `VoxelForge.OpStack.FloatingIslandEquivalence` | The port that runs the stack **backwards** — void + fill vs rock + carve, same classes with the opposite sign. Counts interior-solid and open-void samples separately (on this archetype an aggregate "N solid" is dominated by the seal bands and says nothing about the islands). Counts `AllSolid` and `AllAir` verdicts **separately** too: `AllAir` is the one no cave archetype could ever prove, and it is the entire perf argument here. |
| `VoxelForgeOpStackMazeTest.cpp` | `VoxelForge.OpStack.MazeEquivalence` | **Phase 1's load-bearing test.** The 8-op Maze stack vs `GetMazeDensity` over 20k points (bit-identity; a side-of-iso disagreement is the hard fail), plus purity across workers, a 64-seed spanning-tree/connectivity audit, corridor morphology, cache rebuild-vs-hot timing, and brute force on every box verdict the stack emits. Latest audit: **0 tree edge-count violations, 0 tree-disconnected, 0 Maze-disconnected**; interior degrees `d1=15.91%`, `d2=36.60%`, `d3=31.73%`, `d4=12.95%`, `d5=2.56%`, `d6=0.25%`, mean **2.504** versus a cubic grid's degree-6/mean-6.000; runs `min=1 / median=1 / p90=3 / max=18`. Latest focused cache timing: **1.436 μs rebuild**, **0.150 μs hot call**; final namespace timing: **1.130 μs rebuild**, **0.145 μs hot call**. Latest box audit: **104 proved, 152 Mixed, 138,424 voxels, 0 violations**. | | `VoxelForgeOpStackMazeTest.cpp` | `VoxelForge.OpStack.MazeEquivalence` | The 8-op Maze stack: decomposition, purity across workers, a 64-seed spanning-tree/connectivity audit, corridor morphology, cache rebuild-vs-hot timing, and brute force on every box verdict the stack emits. Latest audit: **0 tree edge-count violations, 0 tree-disconnected, 0 Maze-disconnected**; interior degrees `d1=15.91%`, `d2=36.60%`, `d3=31.73%`, `d4=12.95%`, `d5=2.56%`, `d6=0.25%`, mean **2.504** versus a cubic grid's degree-6/mean-6.000; runs `min=1 / median=1 / p90=3 / max=18`. Latest focused cache timing: **1.436 μs rebuild**, **0.150 μs hot call**; final namespace timing: **1.130 μs rebuild**, **0.145 μs hot call**. Latest box audit: **104 proved, 152 Mixed, 138,424 voxels, 0 violations**. |
| `VoxelForgeStrateParamCoverageTest.cpp` | `VoxelForge.Determinism.StrateParamBlendCoverage` | **The X-macro guard** (added 2026-08-17). `FStrateGenerationParams::Lerp` blends the hand-written `VF_STRATE_PARAM_FIELDS` list, **not** the struct — so a field added to one and not the other compiles, tests green, and silently takes its **default** inside every Gradient/Interleaved transition band. This expands the X-macro a **third** way (after LERP and SNAP), into a name list, and diffs it against the struct's UObject reflection. Pure shape test: no fixture, no world, instant. `GExemptFieldNames` is **empty** — every reflected field is covered today, and any exemption must be written down as a decision. Stakes rise with the world composer, which intends to invent parameter sets through this same `Lerp` (`COMPOSER-NOTES.md`). | | `VoxelForgeStrateParamCoverageTest.cpp` | `VoxelForge.Determinism.StrateParamBlendCoverage` | **The X-macro guard** (added 2026-08-17). `FStrateGenerationParams::Lerp` blends the hand-written `VF_STRATE_PARAM_FIELDS` list, **not** the struct — so a field added to one and not the other compiles, tests green, and silently takes its **default** inside every Gradient/Interleaved transition band. This expands the X-macro a **third** way (after LERP and SNAP), into a name list, and diffs it against the struct's UObject reflection. Pure shape test: no fixture, no world, instant. `GExemptFieldNames` is **empty** — every reflected field is covered today, and any exemption must be written down as a decision. Stakes rise with the world composer, which intends to invent parameter sets through this same `Lerp` (`COMPOSER-NOTES.md`). |
| `VoxelForgeComposerParameterRollTest.cpp` | `VoxelForge.Composer.ParameterRoll` | Asset-Registry corpus audit + complete per-archetype spread/exclusion/clamp table; asserts bit-identical deterministic rerolls and same-archetype parents; measures 64 transient candidate strates with `VF_MeasureStrate` plus the exact unsnapped arrival→departure law; brute-forces every rolled production box verdict. World-scale run: **1,422 applications / 168 distinct fields**; **36/64 survival**, 60 non-vacuous, 58 largest-component, 40 exact-law passes; **1,411 Mixed + 731 AllSolid + 418 AllAir = 1,149 proved boxes**, **1,529,319 lattice voxels checked, 0 violations**, 148.387 s. | | `VoxelForgeComposerParameterRollTest.cpp` | `VoxelForge.Composer.ParameterRoll` | Asset-Registry corpus audit + complete per-archetype spread/exclusion/clamp table; asserts bit-identical deterministic rerolls and same-archetype parents; measures 64 transient candidate strates with `VF_MeasureStrate` plus the exact unsnapped arrival→departure law; brute-forces every rolled production box verdict. World-scale run: **1,422 applications / 168 distinct fields**; **36/64 survival**, 60 non-vacuous, 58 largest-component, 40 exact-law passes; **1,411 Mixed + 731 AllSolid + 418 AllAir = 1,149 proved boxes**, **1,529,319 lattice voxels checked, 0 violations**, 148.387 s. |
| ″ | `VoxelForge.Composer.TerrainDetailLiveness` | Fixed 4,096-point `GetDensityAt` lattice, legacy and operator-stack paths; changes one terrain-detail group at a time with an empty terrain-op pool. Proves 9 live groups / 23 fields and 3 dead groups / 11 fields; all 24 rows match. | | ″ | `VoxelForge.Composer.TerrainDetailLiveness` | Fixed 4,096-point `GetDensityAt` lattice on the TunnelNetwork slot; changes one terrain-detail group at a time with an empty terrain-op pool. Proves 9 live groups / 23 fields and 3 dead groups / 11 fields; all 12 rows must match. |
| `VoxelForgeComposerPromotionTest.cpp` | `VoxelForge.Composer.Promotion` | Re-measures the 12 project/default members, simulates five deterministic 24-candidate seasons with normalized measured-metric novelty (`<0.20`), cap 6, cumulative JSON promotion, provenance counts, corpus-hash checks, fresh-load gate verification, spread/survival reporting, and deliberate stale-metric corruption. The final landing validation passed the strict existing gates; no assertion was weakened. | | `VoxelForgeComposerPromotionTest.cpp` | `VoxelForge.Composer.Promotion` | Re-measures the 12 project/default members, simulates five deterministic 24-candidate seasons with normalized measured-metric novelty (`<0.20`), cap 6, cumulative JSON promotion, provenance counts, corpus-hash checks, fresh-load gate verification, spread/survival reporting, and deliberate stale-metric corruption. The final landing validation passed the strict existing gates; no assertion was weakened. |
| `VoxelForgeComposerStructureRollTest.cpp` | `VoxelForge.Composer.StructureRoll` | Rolls root polarity → legal shape source → polarity-derived conversion → 4–8 declaration-legal modifiers → mandatory structural posts; blends the six native parameter families independently, measures 64 novel stacks, captures the same grid for the deterministic filled/contour XZ/XY preview, runs a separate step-1 radius-64 ROI pass for the 33 survivors centred on `LargestComponentPoint`, checks exact arrival→departure connectivity, rerolls every recipe/stack for determinism, and brute-forces every uniform box verdict. World-scale run: **33/64 survival (51.6%)**, **64 distinct recipes**, **0 invalid recipes**, **715 proved custom boxes / 951,665 lattice voxels / 0 violations**, blank plan/card **4/33→5/33**, **174.854 s**, **0 refusals**. | | `VoxelForgeComposerStructureRollTest.cpp` | `VoxelForge.Composer.StructureRoll` | Rolls root polarity → legal shape source → polarity-derived conversion → 4–8 declaration-legal modifiers → mandatory structural posts; blends the six native parameter families independently, measures 64 novel stacks, captures the same grid for the deterministic filled/contour XZ/XY preview, runs a separate step-1 radius-64 ROI pass for the 33 survivors centred on `LargestComponentPoint`, checks exact arrival→departure connectivity, rerolls every recipe/stack for determinism, and brute-forces every uniform box verdict. World-scale run: **33/64 survival (51.6%)**, **64 distinct recipes**, **0 invalid recipes**, **715 proved custom boxes / 951,665 lattice voxels / 0 violations**, blank plan/card **4/33→5/33**, **174.854 s**, **0 refusals**. |
| `VoxelForgeComposerShowcaseTest.cpp` | `VoxelForge.Composer.Showcase` | Exhausts the bounded parameter-roll set (seeds **0, 7331**, indices **0–63**), excludes multi-region rolls while the lateral gate is off, and measures every missing-family candidate in all six interior target slots at step 4 / radius 256 / `MaxCells=8,000,000`. Hard gates are non-vacuous, largest air share ≥ **0.50**, and exact unsnapped arrival→departure connectivity; selection score is floor-area fraction + clearance tie-break + projected-surface tie-break. It asserts roll/manifest determinism, density sign, zero `WorldRadiusVoxels`, bit-identical metric reruns, and writes one alphabetized card per archetype to `Saved/VoxelForge/Showcase/index.html`, with step-1 radius-64 filled/contour plan + vertical ROI images centred on `LargestComponentPoint`. The final landing regeneration evaluated **256** rolls (**82** eligible single-region, **174** skipped multi-region), produced **7 fine renders, 1 refusal, 0 blanks**, and retained a diagnostic hard-gate survivor set of **0/8**. | | `VoxelForgeComposerShowcaseTest.cpp` | `VoxelForge.Composer.Showcase` | Exhausts the bounded parameter-roll set (seeds **0, 7331**, indices **0–63**), excludes multi-region rolls while the lateral gate is off, and measures every missing-family candidate in all six interior target slots at step 4 / radius 256 / `MaxCells=8,000,000`. Hard gates are non-vacuous, largest air share ≥ **0.50**, and exact unsnapped arrival→departure connectivity; selection score is floor-area fraction + clearance tie-break + projected-surface tie-break. It asserts roll/manifest determinism, density sign, zero `WorldRadiusVoxels`, bit-identical metric reruns, and writes one alphabetized card per archetype to `Saved/VoxelForge/Showcase/index.html`, with step-1 radius-64 filled/contour plan + vertical ROI images centred on `LargestComponentPoint`. The final landing regeneration evaluated **256** rolls (**82** eligible single-region, **174** skipped multi-region), produced **7 fine renders, 1 refusal, 0 blanks**, and retained a diagnostic hard-gate survivor set of **0/8**. |
| `VoxelForgeComposerSeasonTest.cpp` | `VoxelForge.Composer.Season` | Existing compose/review/393,216-sample round trip plus schema hash tamper rejection, two independent runtime manager/generator instances, season-authoritative seed/spine/radius, unset-season regression, and brute-force verification of every sampled non-Mixed recipe tile verdict (up to 64). | | `VoxelForgeComposerSeasonTest.cpp` | `VoxelForge.Composer.Season` | Existing compose/review/393,216-sample round trip plus schema hash tamper rejection, two independent runtime manager/generator instances, season-authoritative seed/spine/radius, unset-season regression, and brute-force verification of every sampled non-Mixed recipe tile verdict (up to 64). |
| `VoxelForgeComposerCorpusFreeTest.cpp` | `VoxelForge.Composer.CorpusFree` | Shares each structure recipe across today's corpus blend, naive independent uniform rolls, and constraint-sampled rolls. The completed equal-arm world-scale run uses **16 candidates per arm**, step 4 / radius 256 / `MaxCells=8,000,000`, fixed passage-law mouths, and 40 box probes per candidate. Result: **10/16, 9/16, 8/16** survival; survivor feature-scale ranges are **8..268**, **8..60**, **8..92** voxels. Box checks: **185/246,235**, **260/346,060**, **299/397,969** proved/voxels, **0 violations** in every arm; constraint parameter and box-stop violations were also 0. | | `VoxelForgeComposerCorpusFreeTest.cpp` | `VoxelForge.Composer.CorpusFree` | Shares each structure recipe across today's corpus blend, naive independent uniform rolls, and constraint-sampled rolls. The completed equal-arm world-scale run uses **16 candidates per arm**, step 4 / radius 256 / `MaxCells=8,000,000`, fixed passage-law mouths, and 40 box probes per candidate. Result: **10/16, 9/16, 8/16** survival; survivor feature-scale ranges are **8..268**, **8..60**, **8..92** voxels. Box checks: **185/246,235**, **260/346,060**, **299/397,969** proved/voxels, **0 violations** in every arm; constraint parameter and box-stop violations were also 0. |
| `VoxelForgeLayoutOrderIndependenceTest.cpp` | `VoxelForge.Determinism.LayoutOrderIndependence` | Builds a known transient soft-pointer pool, then rebuilds it in original, reversed, and swapped orders. Requires a non-empty layout and passage set, and compares every slot's definition/Z/height plus passage endpoints, landing descriptors, control geometry, and bounds bit-for-bit. | | `VoxelForgeLayoutOrderIndependenceTest.cpp` | `VoxelForge.Determinism.LayoutOrderIndependence` | Builds a known transient soft-pointer pool, then rebuilds it in original, reversed, and swapped orders. Requires a non-empty layout and passage set, and compares every slot's definition/Z/height plus passage endpoints, landing descriptors, control geometry, and bounds bit-for-bit. |
| `VoxelForgePassageOpenSpaceTest.cpp` | `VoxelForge.Determinism.PassageLandsInOpenSpace` | Uses the real fixture density path to check both generated mouths against independent source queries, then audits all inter-strate landing ends: body-derived room dimensions, final-density flat-floor slope, local floor/join geometry, seal containment, hot-call/rebuild timing, and targeted `ClassifyTile` box soundness. Reports source-fit/refused mouths and per-archetype landing slopes; fails on any floor, join, seal, or box violation. | | `VoxelForgePassageOpenSpaceTest.cpp` | `VoxelForge.Determinism.PassageLandsInOpenSpace` | Uses the real fixture density path to check both generated mouths against independent source queries (Maze/VerticalShafts/FloatingIslands footings are probed on the archetype stack built without a strate manager, i.e. without passage carving), then audits all inter-strate landing ends: body-derived room dimensions, final-density flat-floor slope, local floor/join geometry, seal containment, hot-call/rebuild timing, and targeted `ClassifyTile` box soundness. Reports source-fit/refused mouths and per-archetype landing slopes; fails on any floor, join, seal, or box violation. |
| `VoxelForgeStrateConnectivityTest.cpp` | `VoxelForge.Generation.StrateConnectivity` / `VoxelForge.Generation.StrateConnectivityRefinement` / `VoxelForge.Generation.VerticalShaftSeamFreedom` | Bounded strate metrics with density-polarity and solid-gap controls, deterministic route rechecks, and refinement sweeps. The refinement test uses the bounded 4-chunk density fixture, fits each measurement AABB to the arrival/departure mouth pair plus three shaft spacings (the prescribed local tree fallback), and caps the fitted control at 120,000,000 cells. It reports exact before/after seed-6 cell counts, reruns every negative with the fitted margin doubled, measures tree orphan candidates/path reachability and roughness-bubble proxies, asserts mouths remain inside the deterministic shaft feature core (the exact axis is an unsupported open cylinder), checks source/mirror physical paths, and requires 16/16 effective arrival→departure results across the 16 VerticalShafts seeds. Margin-binding negatives are reported as measurement limits, never as gap findings. The shaft seam test re-evaluates cell-boundary positions after warming distinct neighbouring chunk contexts. | | `VoxelForgeStrateConnectivityTest.cpp` | `VoxelForge.Generation.StrateConnectivity` / `VoxelForge.Generation.StrateConnectivityRefinement` / `VoxelForge.Generation.VerticalShaftSeamFreedom` | Bounded strate metrics with density-polarity and solid-gap controls, deterministic route rechecks, and refinement sweeps. The refinement test uses the bounded 4-chunk density fixture, fits each measurement AABB to the arrival/departure mouth pair plus three shaft spacings (the prescribed local tree fallback), and caps the fitted control at 120,000,000 cells. It reports exact before/after seed-6 cell counts, reruns every negative with the fitted margin doubled, measures tree orphan candidates/path reachability and roughness-bubble proxies, asserts mouths remain inside the deterministic shaft feature core (the exact axis is an unsupported open cylinder), checks source/mirror physical paths, and requires 16/16 effective arrival→departure results across the 16 VerticalShafts seeds. Margin-binding negatives are reported as measurement limits, never as gap findings. The shaft seam test re-evaluates cell-boundary positions after warming distinct neighbouring chunk contexts. |
| `VoxelForgeMazeSeamTest.cpp` | `VoxelForge.Generation.MazeSeamFreedom` | Re-evaluates **42** cell-boundary probes after warming six different neighbouring chunk contexts in both legacy and operator-stack paths. Latest result: **0 legacy mismatches, 0 operator mismatches**. The Maze source's 2×2×2 lower-node window is deliberately local; no wide collect is involved. | | `VoxelForgeMazeSeamTest.cpp` | `VoxelForge.Generation.MazeSeamFreedom` | Re-evaluates **42** cell-boundary probes through `GetDensityAt` after warming six different neighbouring chunk contexts; must report **0 mismatches**. The Maze source's 2×2×2 lower-node window is deliberately local; no wide collect is involved. |
| `VoxelForgeClipmapNoOverlapTest.cpp` | `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage` | Sweeps 6,174 centres (negative coords, every parity) × radius/level/render-distance/vertical-clamp/sheet combinations: **zero overlapping desired pairs AND coverage ⊇ the frozen `7486c29` selector**. Failed on `7486c29` (1,022,679 overlapping pairs) as intended. | | `VoxelForgeClipmapNoOverlapTest.cpp` | `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage` | Sweeps 5,145 cases (343 centres with negative coords and every parity × 5 radius/level/render-distance settings × 3 vertical clamps): **zero overlapping desired pairs AND coverage ⊇ the frozen `7486c29` selector**. Failed on `7486c29` (1,022,679 overlapping pairs) as intended. |
| `VoxelForgeCollisionDesiredTilesTest.cpp` | `VoxelForge.Streaming.CollisionDesiredTiles.PawnAndAnchors` | Checks the pawn collision box stays within its configured radius, includes support tiles below, and unions an independent anchor box at its authored XY/Z extents. |
| `VoxelForgePreEditDensityCacheTest.cpp` | `VoxelForge.Determinism.PreEditDensityCacheCarveParity` | Captures one exact 35³ L0 pre-edit lattice, applies 16 deterministic random carves cumulatively, and requires each cached remesh's complete geometry hash to match full `GetDensityAt` regeneration; also requires non-empty carve geometry. |
| `VoxelForgeDensityBlockEquivalenceTest.cpp` | `VoxelForge.Density.OperatorBlockMatchesScalarGrid` | Block evaluation (`EvalBlock` after `BuildActiveOpList` pruning) vs scalar, **bit-identical**: 5.1M mesher-grid samples (6 seeds × 8 archetypes × levels 0–5, with edits) + 300k direct `EvalBlock` samples. Scopes `voxel.UseFusedEvaluator=0` so the real block path runs. An injected optimistic `FPassageCarveOp::EffectOverBox` produced 5,612 mismatches. | | `VoxelForgeDensityBlockEquivalenceTest.cpp` | `VoxelForge.Density.OperatorBlockMatchesScalarGrid` | Block evaluation (`EvalBlock` after `BuildActiveOpList` pruning) vs scalar, **bit-identical**: 5.1M mesher-grid samples (6 seeds × 8 archetypes × levels 0–5, with edits) + 300k direct `EvalBlock` samples. Scopes `voxel.UseFusedEvaluator=0` so the real block path runs. An injected optimistic `FPassageCarveOp::EffectOverBox` produced 5,612 mismatches. |
| `VoxelForgeWorldEdgeSealTest.cpp` | `VoxelForge.Generation.WorldEdgeSeal` | Positive samples outside the radius across every fixture strate, bit-identical interior negative control against radius 0, monotonic smooth-ramp check, radius-0 no-op, radial `ClassifyBox`/T1.d proofs brute-forced for every reported voxel, and synthetic plus generated near-rim passage coverage. Emits one aggregate summary with proved-box and voxel counts. | | `VoxelForgeWorldEdgeSealTest.cpp` | `VoxelForge.Generation.WorldEdgeSeal` | Positive samples outside the radius across every fixture strate, bit-identical interior negative control against radius 0, monotonic smooth-ramp check, radius-0 no-op, radial `ClassifyBox`/T1.d proofs brute-forced for every reported voxel, and synthetic plus generated near-rim passage coverage. Emits one aggregate summary with proved-box and voxel counts. |
| `VoxelForgeCorrectnessRoundTest.cpp` | `VoxelForge.Determinism.DiffApiBoundary` / `DiffLayerIdentity` / `EmptyPassageVersion` / `SurfaceBiomeCacheIdentity` / `TileCacheContextSwitch` / `ZeroTransitionBlend` | Regression coverage for the cold-audit correctness fixes (diff-layer API edges, cache identities, empty-passage layout version, zero transition blend). |
| `VoxelForgeDescentCorridorTest.cpp` | `VoxelForge.Descent.OwnerPassageCorridor` / `VoxelForge.Descent.OwnerSeedSweep` | Samples the authored A-to-B passage as a body-sized walking corridor in the final owner field. |
| `VoxelForgeDetSinCosTest.cpp` | `VoxelForge.Determinism.DetSinCos` | Pinned contract for `VoxelMath::DetSin` / `DetCos` / `DetSinCos`. |
| `VoxelForgeIsFiniteTest.cpp` | `VoxelForge.Math.IsFiniteExact` | Bit-exact equivalence of the generation-path finite predicate (`VoxelMath::IsFinite`). |
| `VoxelForgeLargeSeedTest.cpp` | `VoxelForge.Determinism.LargeSeedSurvives` | AUDIT C1: the world must still be a world at a large seed (bounded `VoxelHash::SeedOffset`). |
| `VoxelForgeLateralRegionsTest.cpp` | `VoxelForge.Composer.LateralRegions` | Tier 4d lateral region partition, density blending, conservative box proofs, and the arrival→departure gate (asserts `VF_LateralRegionsAreShippable` stays false while the seam rate is imperfect). |
| `VoxelForgeOpStackChannelTest.cpp` | `VoxelForge.OpStack.ChannelDAG` | Channel/resource declarations are executable stack-assembly metadata (`FVoxelOpStack::ValidateChannelOrder`). |
| `VoxelForgeScaleDiagnosisTest.cpp` | `VoxelForge.Composer.ScaleDiagnosis` | Measurement-only scale diagnosis for the player-fit showcase. |
| `VoxelForgeSealedSolidProofTest.cpp` | `VoxelForge.Determinism.SealedSolidProofSoundness` | Sealed-solid tile proof soundness and invalidation (one-sided: may say false too often, never a wrong true). |
| `VoxelForgeTilePostReachTest.cpp` | `VoxelForge.Correctness.TilePostReachProof` | The tile-reach proof must fail loudly when its envelope is deliberately made too small. |
| `VoxelForgeVerticalStrateReachTest.cpp` | `VoxelForge.Streaming.VerticalStrateReach` | Surface-strate streaming keeps the ground visible when the pawn is high in the same strate. |
| `VoxelForgeWormBlockSkipTest.cpp` | `VoxelForge.Determinism.WormBlockSkipSoundness` | Soundness and coverage of the worker-local worm N1 block proof (`voxel.WormBlockSkip`). |
| `VoxelForgePlayerFitWindow.h` | — | Shared measurement-window policy for player-fit diagnostics (test/editor side only). |
### 3.13 Core runtime integration (rounds 2–5)
Scale symbols: AVoxelWorld::CoreVoxelSizeCm is the opt-in per-world Core size setting and
AVoxelWorld::GetActiveVoxelSizeCm() is the authoritative runtime conversion. CoreTileVoxelCount in
the public Core API is 32 voxels per tile. UVoxelContentManager::MeasurePlacementCostForValidation
is a headless diagnostic only; it measures placement-grid cell queries and steady manager update
cost without changing normal spawning. FloatingIslandTileListAudit.cpp and
BuildFloatingIslandTileListAudit.ps1 under WorldAssemblerM3/Round5 hold the candidate-list
benchmark and dropped-feature negative control. See ARCHITECTURE §8.22.
The public Core API is linked as the `VoxelForgeCore` runtime module, with canonical implementation
sources still under `Standalone/`. UBT validates the 18-row manifest and its shims; BuildHost linked
the module and passed `CheckCoreSymbols.ps1` against the actual shim objects. See
[`Standalone/UNREAL_INTEGRATION_REPORT.md`](Standalone/UNREAL_INTEGRATION_REPORT.md) for build and
headless evidence and the run ledger.
World assembler field types and `AuditHeightfieldBounds`, `AuditHeightfieldShelfThickness`, and
`AuditHeightfieldRoute` are declared in
[`Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h)
and implemented in [`Standalone/Core/src/WorldAssemblerInternal.h`](Standalone/Core/src/WorldAssemblerInternal.h).
`FloatingIslandRecipeV1`, `FloatingIslandManifestV1`, `FloatingIslandFieldV1`,
`AuditFloatingIslandBounds`, `AuditFloatingIslandRoute`, and `AuditFloatingIslandThickness` are in the
same public header; their canonical implementation is
[`Standalone/Core/src/FloatingIslandsInternal.h`](Standalone/Core/src/FloatingIslandsInternal.h).
M3 round 2 added geometry version 2's thick crust, lobed roots, bounded detail and arched spans while
preserving version-1 field and hash behavior. The standalone audit driver remains under
[`Standalone/Research/WorldAssemblerM3/Round1/FloatingIslandsAudit.cpp`](Standalone/Research/WorldAssemblerM3/Round1/FloatingIslandsAudit.cpp);
round-2 matched capture source and results are under
[`Standalone/Research/WorldAssemblerM3/Round2/`](Standalone/Research/WorldAssemblerM3/Round2/).
`RefineLod8NativeCap` in [`Standalone/Core/src/VoxelForgeCore.cpp`](Standalone/Core/src/VoxelForgeCore.cpp)
replaces coarse LOD8 ceiling surfaces with the planned 2D cap field while retaining ceiling skirts.
The standalone SurfaceWorld plan composer and capture/audit entry point live in
[`Standalone/Source/StandaloneViewer.cpp`](Standalone/Source/StandaloneViewer.cpp).
`RunStandaloneWorldExport` in `VoxelForgeExploreCommandlet.cpp` serializes owner assets on the game
thread to schema-v1 JSON. The adapter parses it through `ResolvedWorldSnapshot::FromJson` and keeps
UObjects off Core workers. The runtime switch is `AVoxelWorld::bUseVoxelForgeCore` (default false),
with independent `CoreClipRadius=3` and `CoreMaxClipLevel=8`; it does not read or mutate the old clip
settings or assets.
When enabled, `AVoxelWorld` uses Core desired keys and priorities, compiles/generates through Unreal
tasks, carries `Epoch` through the existing MPSC result queues, and uploads converted payloads through
RealtimeMesh and its collision cook. Core voxel coordinates use the size resolved from
`CoreVoxelSizeCm` (25 cm by default); Core tile keys remain in 32-voxel units, and mesh vertices are
in centimetres. Legacy brush dimensions and density strength are converted from the 12.5 cm owner
grid using the resolved Core size, preserving their physical effect. SurfaceWorld owner-chunk mapping
uses the owner/Core ratio as well. Edits use sequenced `applyEditTransaction` calls after in-flight
Core work drains, then regenerate Core's affected keys under a new epoch. Existing streaming anchors
participate; the headless move proof covers `CollisionOnly` policy.
The reusable `BuildWorldAssemblyFeatureTileListV1` accepts conservative inclusive feature bounds.
`ScopedWorldAssemblyFeatureQueriesV1` replaces the island-only ShapeWorld worker context: meshing,
derived shells, decoration cells and landmark columns share its nestable worker scope. Recipe/plan
identity and an inclusive domain guard each query; outside queries fall back to the full evaluator.
See ARCHITECTURE §8.23 and WorldAssemblerM3/Round6/REPORT.md.
| Symbol | Canonical file | Role |
|---|---|---|
| `WorldAssemblyFeatureTileListV1`, `WorldAssemblyFeatureGroupBoundsV1`, `WorldAssemblyFeatureQueryV1` | `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h` | Stable-order candidates, optional exact value-side group certificates and borrowed recipe/plan identity. Old island names remain aliases. |
| `ScopedWorldAssemblyFeatureQueriesV1::{find,ScopedWorldAssemblyFeatureQueriesV1}`, `WorldAssemblyFeatureTileListV1::candidateEvaluationUseful` | `Standalone/Core/src/WorldAssemblerInternal.h` / public header | Worker-local domain, cost hint resolved once per query, forced validation, nested restoration and full-scan fallback; replaces ShapeWorld's island context. |
| `Heightfield2D::buildFeatureTileList`, `world_assembly_detail::FeatureRange` | `Standalone/Core/src/WorldAssemblerInternal.h` | XY bounds for column writers; exact no-op bounds for finite boxes/shelves; canonical operation order. |
| `FloatingIslandFieldV1::{solidSupportBounds,buildFeatureTileList,densitySubvoxels}` | `Standalone/Core/src/FloatingIslandsInternal.h` | Finite support declarations, root-aware distance certificates, automatic shared-scope consumption. |
| `WorldAssemblyFeatureColumnRangeV1`, `BuildWorldAssemblyFeatureColumnRangeV1` | Public Core header / `Standalone/Core/src/WorldAssemblerInternal.h` | Conservative column support union for air-base families; invalid bounds are unknown. |
| `VF_ContentFeatureQueries`, `VF_ConstrainContentMarch` | `Private/VoxelContentManager.cpp` | Immutable queries prepared per cell/landmark; skips proven air while preserving march phase and exact crossing refinement. |
| `UVoxelContentManager::MeasurePlacementCostForValidation` | `Private/VoxelContentManager.cpp` | Authored/populated full-versus-candidate transforms, canonical anchor checks, batch and isolated HISM application cost. |
| `UVoxelContentManager::ApplyRegion` | `Private/VoxelContentManager.cpp` / `Public/VoxelContentManager.h` | Normal region application retains landmark footprints; its default-true footprint flag isolates the temporary populated validation fixture. |
| `RunCoreSharedFeatureCostAudit`, `RunCoreContentPlacementCostAudit` | `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp` | Paired owner-plan tile/shell timings and hashes, placement metrics and transient BuildHost plan exports. |
| `TileGenerationOptions::useAssemblyFeatureQueries`, `TileGenerationStats::{featureListMilliseconds,farShellMilliseconds}` | Public Core header / `Standalone/Core/src/VoxelForgeCore.cpp` | Default-on candidate route, full-scan validation control and stage timing; no identity change. |
| `SharedFeatureListAudit.cpp`, `BuildSharedFeatureListAudit.ps1` | `Standalone/Research/WorldAssemblerM3/Round6/` | Owner-plan/stress hashes, shared-scope probes and deliberately dropped needed features. |
`-coreworldtest=1` in the BuildHost editor commandlet validates initial tiles, expected collision
bodies/empty tiles, anchor movement, a carve transaction, travel to the B landing and native surface
strate, full desired visual-key coverage, neighbour/LOD face coverage, and W1/W8/W16 hash parity.
`-coreworldassembly=PATH` supplies a reviewed manifest transiently for this test. The native surface
probe samples `Heightfield2D` directly from the Core snapshot; every one of its 2,553 expected visual
tile hashes is compared with an independent standalone Core reference. The 125 collision-desired
surface tiles are checked for expected cooked bodies or empty geometry. `VoxelForge.Determinism.CrossPlatformDigest`
remains the standalone legacy digest check. Switch-off was reviewed at the source branch and hash
boundary; there is no separate flag-off actor replay. See the report for exact hashes, timing, run
ledger, and owner-editor instructions. Core remains opt-in and the legacy path remains default.
Round 4 adds a temporary SurfaceWorld density bridge in
[ShapeWorldInternal.h](Standalone/Core/src/ShapeWorldInternal.h). SurfaceWorld samples, including
biome resolution and terrain operations, route through the copied owner evaluator at full resolution;
the Core edit layer is applied afterward. Surface density hashes match the owner field across the
tested LOD0–LOD3 tiles, and the BuildHost actor layout identifies Biome_Surface_Plain at the surface
travel point. The CPU bridge costs approximately the legacy field route: 23.085 s wall / 180.562
worker-seconds for the matched 1,294-tile CPU view.
Round 5 adds the native surface route. `UVoxelSettings` stores canonical reviewed manifest JSON and
its Core digest. The `SourceWorldAssemblyManifestJson` file picker plus
`ImportWorldAssemblyManifestJson` editor button populate the cooked data asset. The Core adapter
requires an exact unique match for every planned `SurfaceWorld` stratum and adds the canonical plan
to the resolved Core snapshot. `GetCurrentSeasonContentHash()` includes this digest only while
`bUseVoxelForgeCore` is enabled; disabling Core retains the legacy generation route and legacy
content hash. The collision gate defers out-of-desired-set Core support tiles until Core region
compilation admits them, so collision work cannot publish a competing tile before the native
surface region snapshot. Existing cave Core strates still use the no-manifest snapshot path.
Round 6 corrects the Core 25 cm ↔ legacy 12.5 cm boundary in `VoxelWorld.cpp`,
`VoxelContentManager.cpp`, and `VoxelAtmosphereManager.cpp`. `VoxelMarchingCubesMesher` now exposes
`GeometryVoxelSizeCm` so Core geometry is emitted at 25 cm while legacy stays at its 12.5 cm
default. The Core bridge adapts carve/fill signs and scales Blueprint brush radius, extent, falloff
and strength. `Standalone/Core/src/FarShellsInternal.h` subdivides LOD7+ far-stratum repairs, and
`Standalone/Core/src/VoxelForgeCore.cpp` enables a guarded heightfield Z-band path for unhashed
native tiles, combining ground/cap heights with intersecting bounded 3D feature extents; overlapping
edits keep the full mesh path. The BuildHost commandlet adds height-anchor agreement, brush
sign/size, geometry and per-tile-kind pawn-lag checks; see round 6 in
[`Standalone/ASSEMBLER_M1_REPORT.md`](Standalone/ASSEMBLER_M1_REPORT.md).
M2 round 3 adds `EVoxelForgeCoreMesherRecipe` / `CoreMesherRecipe` in
[`Source/VoxelForge/Public/VoxelWorld.h`](Source/VoxelForge/Public/VoxelWorld.h), defaulting to
`MarchingCubesV1`; `VoxelWorld.cpp` maps opt-in hybrid choices to Core `MesherRecipe` values and
adds the selected version to Core content identity. V1 retains the original feature-aware DC path.
V2 keeps MC topology and snaps only eligible interior LOD0 edge roots to exact type-2 box planes;
the mesher support is in `VoxelHybridSharpMesher.h` and `VoxelMarchingCubesMesher.cpp`. Coarse LODs
and roots whose incident cells reach a tile border remain MC. `VoxelForgeExploreCommandlet.cpp`
owns the five production-mesher fixtures, border probes and Core actor collision contacts. BuildHost evidence
and remaining doorway transition failures are recorded in
[`Standalone/UNREAL_INTEGRATION_REPORT.md`](Standalone/UNREAL_INTEGRATION_REPORT.md) and
[`Standalone/Research/WorldAssemblerM2/REPORT.md`](Standalone/Research/WorldAssemblerM2/REPORT.md).
Likely compile touchpoints are `VoxelWorld.h/.cpp`, `VoxelMarchingCubesMesher.h/.cpp`,
`VoxelHybridSharpMesher.h`, `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`,
`Standalone/Core/src/VoxelForgeCore.cpp`, and the editor commandlet/build rules.
[OwnerWorldLoader.h](Standalone/Source/OwnerWorldLoader.h) now reads SlabParams, MazeParams,
VerticalShaftParams, and FloatingIslandParams. The shape-world evaluator still lacks their native
terrain fields: FlatPlain, CrystalChamber, Maze, VerticalShafts, FloatingIslands, and water
presentation remain missing or approximate. Passage type/style chains are extracted from manager
control points, but Worm and Cascading currently fail the centerline walkability audit. Gradient,
Hard, and Interleaved transitions produce the same Core probe hash and are not faithfully represented.
See the [round-4 coverage table and small matched captures](Standalone/UNREAL_INTEGRATION_REPORT.md)
and [probe artifacts](Standalone/Research/OwnerShapeWorld/core_strate_coverage/).
World Assembler M3 Round 7 adds a shared far-horizon path. `WorldAssemblyPlanV1::strates` is the
SurfaceWorld declaration: `World::generateTile` evaluates native ground on a globally aligned grid
with step min(tile step, 128 voxels), and cap on the shared 128-voxel grid. It clips both surfaces
to the tile and to `cap >= ground`, and replaces nearby upward coarse-MC ground faces. An unedited
SurfaceWorld tile at LOD7/8 that intersects a native surface plan and no island plan uses those
profile surfaces instead of derived far stratum/connector repairs. Tiles with edits or intersecting
FloatingIslands plans keep the canonical field and repairs. FloatingIslands keeps the shared 3D field
and repair path, with a common 256-voxel axial station/ring grid at LOD7/8; closer LODs retain
128-voxel stations. `TileGenerationStats` reports classification, column, meshing, shell and horizon
time, repair field calls, skipped repairs and replaced triangles. `farHorizonVersion` salts far
mesh/payload/topology hashes only. The BuildHost commandlet owns warmed eight-key timings and the
exact-density-root LOD7/8 agreement audit with its datum-offset negative control.
World Assembler S4 repairs the planar cap datum and far tile selection. `Heightfield2D::farSurfaceCapHeightSubvoxels`
clips the rendered cap to `(topChunkZ + 1) * 32` voxels while preserving `Heightfield2D::point` density;
`capHeightBounds` provides the matching conservative bands. `World::desiredTiles` adds only non-overlapping
cap-height keys at LOD4–8, and `RefineFarNativeCap` emits the globally aligned cap surface (16 voxels at
LOD4, 128 at LOD5–8). Evaluator v5 opts into this render-surface contract; older serialized evaluator
versions retain their cap behavior. Far recipe v2 salts mesh/payload/topology from LOD4 upward. The
standalone viewer audit measures cap-band census, near-root agreement and upward-ray coverage; the editor
BuildHost check measures the staged owner actor and W1/W8/W16. `VoxelWorld::ApplyMeshToTile` uses explicit
section ranges for ground and cap. Likely compile spots are the Core public/internal heightfield API and
`VoxelForgeCore.cpp`, `VoxelWorld.cpp`, the editor commandlet, and the standalone viewer. See
ARCHITECTURE §8.25 and
[`Standalone/Research/WorldAssemblerM3/Round8/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round8/REPORT.md).
## 4. The density pipeline (most-edited hot path) ## 4. The density pipeline (most-edited hot path)
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:277 ### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749
Stage order (negative=solid throughout). Each stage's anchor: Stage order (negative=solid throughout). Each stage's anchor:
| Step | Line | What | | Step | Line | What |
|------|------|------| |------|------|------|
| 1 — Vertical scale | 307 | Stretch Z before noise (`VerticalScale`). | | 1 — Vertical scale | 3799 | Stretch Z before noise (`VerticalScale`). |
| 2 — Base density | 316 | Everything starts solid at `BaseDensity`. | | 2 — Base density | 3808 | Everything starts solid at `BaseDensity`. |
| 3 — Cave warp | 321 | Domain-warp the SDF query coords (organic shapes). | | 3 — Cave warp | 3813 | Domain-warp the SDF query coords (organic shapes). |
| 4 — SDF morphology | 368 | Rooms+tunnels via `BuildChunkCache`/`EvaluateSDFCached`. | | 4 — SDF morphology | 3863 | Rooms+tunnels via `BuildChunkCache`/`EvaluateSDFCached`. |
| 4b — Surface roughness | 564 | Volumetric noise near surfaces (fBM/Ridged/Mixed). | | 4b — Surface roughness | 4267 | Volumetric noise near surfaces (fBM/Ridged/Mixed). |
| 4c–4h — Terrain ops | 688 | Per-room op applied near surfaces. Sub-anchors below. | | 4c–4h — Terrain ops | 4395 | Per-room op applied near surfaces. Sub-anchors below. |
| · Terracing | 727 | Step-like ledges. | | · Terracing | 4437 | Step-like ledges. |
| · Layer lines | 823 | Horizontal grooves (sin of Z). | | · Layer lines | 4534 | Horizontal grooves (sin of Z). |
| · Ribbing | 853 | Parallel ridges (sin of Z). | | · Ribbing | 4564 | Parallel ridges (sin of Z). |
| · Overhangs | 882 | Low-Z-freq noise shelves. | | · Overhangs | 4593 | Low-Z-freq noise shelves. |
| · Cliff sharpening | 918 | Amplify vertical gradient. | | · Cliff sharpening | 4629 | Amplify vertical gradient. |
| · Scallop | 962 | Cellular erosion bowls. | | · Scallop | 4673 | Cellular erosion bowls. |
| · Arch/Bridge | 998 | Hash-placed capsules across voids. | | · Arch/Bridge | 4709 | Hash-placed capsules across voids. |
| 4d — Columns | 1053 | Pre-baked vertical cylinders. | | 4d — Columns | 4738 | Pre-baked vertical cylinders. |
| 4g — Domes | 1077 | Room-relative hemispherical ceilings. | | 4g — Domes | 4766 | Room-relative hemispherical ceilings. |
| 4h — Pinch | 1142 | Passage bottlenecks. | | 4h — Pinch | 4831 | Passage bottlenecks. |
| 5 — Worm tunnels | 1241 | abs(noise1)+abs(noise2), masked by distance-to-network (`WormNetworkRange`: braids hugging rooms/tunnels, no far-field speckle; 0 = legacy unmasked). | | 5 — Worm tunnels | 4905 | abs(noise1)+abs(noise2), masked by distance-to-network (`WormNetworkRange`: braids hugging rooms/tunnels, no far-field speckle; 0 = legacy unmasked). |
| 6 — Boundary seal | 1274 | Solid top/bottom shells (`ApplyBoundarySeal`). | | 6 — Boundary seal | 4987 | Solid top/bottom shells (`ApplyBoundarySeal`). |
| 7 — Inter-strate passages + landings | `GetDensityWithParams`: 2217; `GetSlabDensity`: 2434+ | Carve wandering passage tubes and deterministic local landing rooms, restore their support floors, then leave the final XY seal to the outer MC post (`ApplyPassageModifier` / `ApplyPassageLandingFloorMC`). | | 7 — Inter-strate passages + landings | 5008 | Carve wandering passage tubes and deterministic local landing rooms, restore their support floors, then leave the final XY seal to the outer MC post (`ApplyPassageModifier` / `ApplyPassageLandingFloorMC`). |
### 4.2 `GetSlabDensity` (FlatPlain / CrystalChamber) — VoxelGenerator.cpp:1306 ### 4.2 `BuildSlabStack` (FlatPlain / CrystalChamber) — VoxelDensityOpStack.cpp
| Step | Line | What | | Step | Op | What |
|------|------|------| |------|----|------|
| 1 — Floor surface | 1317 | Noisy floor height. | | 1 — Floor surface | `FSlabVoidSource` | Noisy floor height (XY-pure). |
| 2 — Ceiling surface | 1343 | Formations hang downward (`abs(noise)`). | | 2 — Ceiling surface | `FSlabVoidSource` | Formations hang downward (`abs(noise)`). |
| 3 — Void→base density | 1379 | Solid outside [floor,ceiling]. | | 3 — Void→base density | `FSlabVoidSource` | Solid outside [floor,ceiling]. |
| 4 — Columns | 1399 | World-space hash grid, full-height. | | 4 — Columns | `FGridColumnMod` | World-space hash grid, full-height. |
| 5+6 — Seal + passages | 1461 | Same seal, passage-tube, landing-room, connector, and support-floor path as TunnelNetwork. | | 5+6 — Seal + passages | `AppendStructuralPost` | Spine, vertical seal, passage tube/landing floor, XY seal — the same structural post as every stack. |
--- ---
@@ -786,25 +1033,25 @@ Stage order (negative=solid throughout). Each stage's anchor:
| Goal | Location | | Goal | Location |
|------|----------| |------|----------|
| Chunk size / voxel scale | `VoxelTypes.h:19-23` (rebuild everything). | | Chunk size / voxel scale | `VoxelTypes.h:211-213` (rebuild everything). |
| View distance / task budget / LOD distances | `VoxelSettings.h` (no recompile of logic — data asset). | | View distance / task budget / LOD distances | `VoxelSettings.h` (no recompile of logic — data asset). |
| LOD step mapping | `AVoxelWorld::LODToStep` VoxelWorld.cpp:268; `GetLODForChunk` :242. | | LOD step mapping | `LoadTile` VoxelWorld.cpp:9033, effective cell selection near :9071: extent `CHUNK_SIZE << Level`, cells from `FullResClipLevels`/`CoarseTileCells`, `Step = Extent / Cells`; tile selection in `VoxelClipmapDesiredTiles::Build` (`Private/VoxelClipmapDesiredTiles.h`). |
| How chunks stream in/out | `UpdateChunksAroundPosition` VoxelWorld.cpp:362. | | How chunks stream in/out | `UpdateChunksAroundPosition` VoxelWorld.cpp:6870. |
| Async threading / stale-result handling | `LoadTile` :445, `ProcessPendingChunks` :301, generation Epoch + per-request cancellation token. | | Async threading / stale-result handling | `LoadTile` :9033, `ProcessPendingChunks` :7435, generation Epoch + per-request cancellation token. |
| Add a new cave feature / terrain op | Add enum in `VoxelTerrainOpDefinition.h:36`, params there, `ApplyTo` (.cpp:6), transport fields in `FStrateGenerationParams`, consume it in a new Step inside `GetDensityWithParams`. | | Add a new cave feature / terrain op | Add enum in `VoxelTerrainOpDefinition.h:36`, params there, `ApplyTo` (.cpp:6), transport fields in `FStrateGenerationParams`, consume it in a new Step inside `GetDensityWithParams`. |
| Tweak room/tunnel shapes | `VoxelCaveMorphology.cpp` `BuildChunkCache` :47 / `EvaluateSDFCached` :757. | | Tweak room/tunnel shapes | `VoxelCaveMorphology.cpp` `BuildChunkCache` :6782 / `EvaluateSDFCached` :8569. |
| Worm tunnel behavior | `GetDensityWithParams` Step 5, VoxelGenerator.cpp:1241. | | Worm tunnel behavior | `GetDensityWithParams` Step 5, VoxelGenerator.cpp:4905. |
| Strate stacking / which strate where | `UVoxelStrateManager::Initialize` :10. | | Strate stacking / which strate where | `UVoxelStrateManager::Initialize` :1164. |
| Publish/use an offline season | Create `UVoxelSeasonAsset`, set `SourceManifestJson`, press `ImportSeasonManifestJson`, then assign it to `UVoxelSettings::Season`. | | Publish/use an offline season | Create `UVoxelSeasonAsset`, set `SourceManifestJson`, press `ImportSeasonManifestJson`, then assign it to `UVoxelSettings::Season`. |
| Boundary blend between strates | `GetGenerationParams` :515 + `FStrateGenerationParams::Lerp` (expands `VF_STRATE_PARAM_FIELDS`, StrateTypes.h — new fields go in that list). | | Boundary blend between strates | `GetGenerationParams` :6509 + `FStrateGenerationParams::Lerp` (expands `VF_STRATE_PARAM_FIELDS`, StrateTypes.h — new fields go in that list). |
| Passages / landing geometry between strates | `GeneratePassages` :146 + `EvaluateModifierSDF` :371 + `ApplyPassageModifier` / `ApplyPassageLandingFloorMC` (StrateManager.cpp) + `ClassifyTile` floor guard (Generator.cpp). | | Passages / landing geometry between strates | `GeneratePassages` :1659 + `EvaluateModifierSDF` :2643 + `ApplyPassageModifier` / `ApplyPassageLandingFloorMC` (StrateManager.cpp) + `ClassifyTile` floor guard (Generator.cpp). |
| Player carve/fill | `CarveAtPosition`/`FillAtPosition` VoxelWorld.cpp:691/709 → `UVoxelDiffLayer::ApplyModification` :63. | | Player carve/fill | `CarveAtPosition`/`FillAtPosition` VoxelWorld.cpp:10230/10239 → `AVoxelWorld::ApplyModification` :10248 → `UVoxelDiffLayer::ApplyModification` :185. |
| Mesh smoothness / normals | Grid-gradient in `GenerateMesh` (`GradAt` lambda), `IsoLevel` (h). | | Mesh smoothness / normals | Grid-gradient in `GenerateMesh` (`GradAt` lambda), `IsoLevel` (h). |
| New slab/flat-world generator | `GetSlabDensity` Generator.cpp:1306 + `FSlabGenerationParams` (StrateTypes.h:1019). | | New slab/flat-world generator | `VoxelDensityOps::BuildSlabStack` + `FSlabVoidSource` / `FGridColumnMod` (VoxelDensityOpStack.cpp) + `FSlabGenerationParams` (StrateTypes.h:1223). |
| Biome placement / layout | `BiomeMapParams` on the strate (cell size, warp, climate freqs) + each biome's climate box. Bake `AVoxelWorld::BakeBiomePreview` to tune. §8.14. | | Biome placement / layout | `BiomeMapParams` on the strate (cell size, warp, climate freqs) + each biome's climate box. Bake `AVoxelWorld::BakeBiomePreview` to tune. §8.14. |
| What a biome does to terrain | A full archetype param override on the biome (`bOverrideTerrain` + `SurfaceParams`); surface output-blends dominant/neighbour heights in `GetSurfaceDensity`. Caves = content/atmosphere only (determinism, §8.14). | | What a biome does to terrain | A full archetype param override on the biome (`bOverrideTerrain` + `SurfaceParams`); surface output-blends dominant/neighbour heights in the surface stack (`BuildSurfaceStack` with per-biome params) and in `ComputeSurfaceColumn`. Caves = content/atmosphere only (determinism, §8.14). |
| Add a biome / biome content | New `UVoxelBiomeDefinition` asset → add to the strate's `Biomes[]`. §8.14 / §8.12. | | Add a biome / biome content | New `UVoxelBiomeDefinition` asset → add to the strate's `Biomes[]`. §8.14 / §8.12. |
| Season reset | `AVoxelWorld::ChangeSeed` :740. | | Season reset | `AVoxelWorld::ChangeSeed` :8710. |
--- ---
+3 -3
View File
@@ -36,7 +36,7 @@ composer fills in; nobody opens it in the editor any more.
### This is op-stack "Phase 3", and it is REOPENED ### This is op-stack "Phase 3", and it is REOPENED
The 2026-08-16 `OPSTACK-HANDOFF.md` says in bold *"Do not start Phase 3."* **That is superseded.** The 2026-08-16 `Docs/archive/OPSTACK-HANDOFF.md` says in bold *"Do not start Phase 3."* **That is superseded.**
Asked what the world was missing, Jahni described exactly Phase 3 plus a composer above it. Asked what the world was missing, Jahni described exactly Phase 3 plus a composer above it.
It is **not** a restart of the refactor. The eight operators are live, composable, and production. What It is **not** a restart of the refactor. The eight operators are live, composable, and production. What
@@ -1490,7 +1490,7 @@ roughness and should stay late in the stack.
Kept because each cost real time or would otherwise be repeated. Kept because each cost real time or would otherwise be repeated.
- ⛔ **"Do not start Phase 3" is SUPERSEDED.** `OPSTACK-HANDOFF.md` still says it; believe this file. - ⛔ **"Do not start Phase 3" is SUPERSEDED.** `Docs/archive/OPSTACK-HANDOFF.md` still says it; believe this file.
- ⛔ **Jahni does not want to author strates either.** The composer must invent them. An earlier reading - ⛔ **Jahni does not want to author strates either.** The composer must invent them. An earlier reading
had him authoring strates while the system arranged them — one level too shallow, and it shaped a had him authoring strates while the system arranged them — one level too shallow, and it shaped a
whole afternoon of wrong design. whole afternoon of wrong design.
@@ -1553,7 +1553,7 @@ build succeeded (14 actions). The full headless suite reported **22 succeeded, 0
determinism, box-verdict, and connectivity checks stayed green. The 4b→4h fuse was deliberately determinism, box-verdict, and connectivity checks stayed green. The 4b→4h fuse was deliberately
not applied after the intervening-channel audit above. not applied after the intervening-channel audit above.
**⭐ This closes the pending-verification thread opened by `OPSTACK-HANDOFF.md`.** That file flagged **⭐ This closes the pending-verification thread opened by `Docs/archive/OPSTACK-HANDOFF.md`.** That file flagged
commits `4d33321` (Sol's boundary fold) and `91585ea` as *built but NOT re-verified*, with two commits `4d33321` (Sol's boundary fold) and `91585ea` as *built but NOT re-verified*, with two
acceptance conditions. Both now met: acceptance conditions. Both now met:
- **`violations` = 0** everywhere. Measured: TunnelNetwork at production defaults **12 of 40 tiles - **`violations` = 0** everywhere. Measured: TunnelNetwork at production defaults **12 of 40 tiles
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+783
View File
@@ -0,0 +1,783 @@
# Astra (gpt-6-astra, max): how else could we do it? — 2026-10-02
Prompt: scratchpad astra-think.md (free thinking, nothing off the table). Answer verbatim below.
**I would bet on your coarse-structure/fine-surface idea, extended in two ways: generate surfaces directly wherever possible, and make edits immediately queryable without waiting for replacement meshes.**
The largest opportunity is to make the ordinary cost scale with **surface area, visible detail, and recent changes**. Dense volumetric work should be reserved for places where complicated three-dimensional intersections actually require it.
I read the [research report](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/RESEARCH-SPEED-2026-09.md>) and use the newer timings in your prompt below. All performance estimates here are arithmetic or architectural estimates, not measurements of these proposed designs.
At 100 ns per point, 140 million points require about **2.3 seconds on six perfectly scaling workers**, before anything else happens. That makes the central problem clear: further local optimization cannot carry the full burden.
There is also an important distinction inside “detail”:
| What the player perceives | What must represent it |
|---|---|
| A passage, hole, thin wall, or floating island | Actual solid/air topology |
| A foothold, climbable edge, or projectile obstruction | Gameplay geometry |
| Rock relief that changes silhouettes and stereo depth | Actual rendered surface displacement or geometry |
| Grain, mineral variation, tiny scratches, wetness | Shading and material information |
A finer lattice spends more on all four together. Your game can give each its own representation and still present one coherent world.
The difficult negotiation is whether every visible grain must also be an independently collidable, destructible object. If that is required at arbitrarily increasing resolution, the physical complexity must eventually be paid for. If the requirement is increasingly fine **visible rock**, there is much more room.
**1. Give fine detail a surface representation. This is the strongest immediate direction.**
A cave wall can have a relatively simple structural surface and an extremely detailed skin.
A useful version would contain:
- Structural geometry for chambers, passages, ledges, holes, and substantial protrusions.
- A bounded surface displacement for shallow fractures, erosion, and rock relief.
- Normal, roughness, colour, and wetness variation below that.
- Explicit small geometry for features such as thin stalactites that cannot be represented by shallow displacement.
This follows the useful idea behind film-style micropolygon rendering: keep a compact description of a surface, then subdivide and shade it according to what the image needs. Pixar’s original [Reyes architecture](https://graphics.pixar.com/library/Reyes/paper.pdf) is a foundational example.
The crucial improvement over “coarse geometry plus nicer materials” is that the surface layer should include **real geometric relief where stereo or silhouette makes it necessary**. Normal mapping alone will fail the owner’s close inspection. Parallax mapping has useful applications, but I would not make it responsible for rock silhouettes or convincing near-hand VR geometry.
There are two traps.
First, tessellating a coarse, faceted mesh does not automatically recover a smooth underlying shape. The refined vertices need a meaningful curved surface to follow: a procedural patch, a suitable subdivision surface, or projection onto a local structural field. Otherwise, finely tessellated facets remain facets.
Second, displacement cannot create arbitrary new topology. A six-centimetre hole through a wall needs an explicit hole or a locally finer volumetric representation. It cannot be recovered by adding surface roughness to a mesh that sealed it.
For your reference image, that distinction is favourable. Broad ledges, columns, hanging formations, and fractured slabs supply the geometry. Much of the apparent richness comes from correlated relief, material, moisture, lighting, and water.
**What it gives:** returning the structural lattice from 12.5 cm to 25 cm reduces the dense sample count by approximately **8×**, while the rendered surface can become finer than either lattice. This is a sample-count reduction, not an 8× whole-game guarantee.
**What it costs:** continuous GPU work, surface refinement, coherent material coordinates, and careful boundaries between patches. At 90 Hz, the entire frame has approximately **11.1 ms**; displacement and its shadows must share that budget.
**What it takes away:** automatic physical significance for every visible bump. You need an explicit displacement envelope and contact tolerance. Where precise hand placement or tool contact matters, the CPU can evaluate the same relief locally, rather than generating that detail throughout a 64 m cube. Robust contact queries still require more than checking a few points.
**Determinism:** retain canonical structural geometry and deterministic surface parameters. Any relief used for gameplay must have a canonical evaluator too. Lighting and final pixel calculations can remain device-dependent without changing the world’s gameplay answers.
A signal-processing lesson matters here: **preserve the detailed surface definition, but filter its image**. Trying to display every tiny frequency at every distance causes shimmer rather than fidelity. Procedural noise can be designed with controlled frequency content and filtering; [Gabor noise research](https://graphics.cs.kuleuven.be/publications/LLDD09PNSGC/) is one useful example. Finer detail can be inexpensive when it comes from a reusable filtered texture or compact surface function. Additional geometry, shadowing, and contact complexity are never literally free.
There is an Unreal-specific qualification. UE 5.7 documents Nanite tessellation as experimental, limited to Nanite meshes, and subject to displacement cracks at discontinuous attributes. Its standard `UDynamicMeshComponent` does not support Nanite. Nanite/Lumen XR support is also experimental and requires PC deferred rendering with DX12. Your PC-linked Quest fits the PC category, but this is an integration and performance risk, not a checkbox solution. [Nanite tessellation](https://dev.epicgames.com/documentation/en-us/unreal-engine/working-with-naniteenabled-content?application_version=5.7), [Dynamic Mesh limitations](https://dev.epicgames.com/documentation/en-us/unreal-engine/geometry-scripting-users-guide-in-unreal-engine?application_version=5.7), [XR support](https://dev.epicgames.com/documentation/en-us/unreal-engine/nanite-and-lumen-for-xr-in-unreal-engine?application_version=5.7).
I would validate the surface concept independently of whether Nanite becomes its eventual implementation.
**2. The radical direction: make the generator describe the cave boundary directly.**
Today, the generator knows how rooms and tunnels were constructed, but the mesher largely rediscovers their boundaries by asking millions of questions about space.
I would investigate preserving that construction knowledge all the way to geometry.
A chamber could generate floor patches, ceiling patches, and connecting wall patches. A tunnel could generate a swept cross-section along a curve. Columns and stalactites could generate tapered, deformed surfaces. Ledges could follow bedding planes. Floating masses could be closed procedural objects.
These are local two-dimensional surfaces embedded in three dimensions. Multiple overlapping levels and arbitrarily oriented patches provide overhangs and stacked caves; this does not require a globally single-valued heightfield.
At complicated junctions, blends, and heavily edited regions, retain your volumetric system. Those regions become exceptions with explicit boundaries.
The result is a world recipe containing:
- A graph describing connections and traversable routes.
- Surface-producing features with conservative bounds.
- Local volumetric regions for difficult topology.
- Surface detail parameters and player edits.
This changes the generator’s responsibility. A feature should be able to supply its boundary, its bounds, and its relevant gameplay queries—not just a density value at an arbitrary position.
There is an artistic reason to consider this too. Geology produces strong correlations: bedding, fracture directions, drainage paths, erosion, and deposition. Those correlations are an excellent form of compression. One bedding description can explain thousands of related ledges and fractures.
Research on [geologically coherent cave networks](https://perso.liris.cnrs.fr/apeytavi/website/publication/hal-03331697/) demonstrates useful controls through faults, horizons, permeability, and network skeletons. That paper ultimately constructs an SDF; my proposal is to retain additional boundary knowledge through the rendering pipeline.
Here is the potential dimensional advantage.
**Illustrative estimate:** suppose the nearby region contains 20,000 m² of rock surface. The actual area in your game must be measured.
| Representation at 12.5 cm spacing | Approximate sample locations |
|---|---:|
| Your full cube, including its sampling overhead | 140 million |
| A surface band with total thickness of four cells | 5.1 million |
| Direct surface sampling, roughly one location per \(h^2\) | 1.3 million |
The narrow-band estimate is:
\[
N \approx \frac{A(4h)}{h^3}=\frac{4A}{h^2}.
\]
For fixed surface area and complexity, halving spacing makes surface sampling roughly **4×** larger; dense volume sampling becomes **8×** larger.
These are counts, not interchangeable operation costs or promised speedups. Surface intersections, charts, normals, transitions, and acceleration structures all add work.
Most importantly, **your existing uniform-brick skipping already captures some of this benefit**. The table cannot be claimed as an additional 27× improvement over that implementation.
The new opportunity is to enumerate likely surface regions directly, or emit surface geometry directly, so that you avoid both expensive field evaluations and a large search through candidate bricks. If a proposed hierarchy still performs essentially the same uncertain-brick evaluations, it has not changed your problem.
For the volumetric exceptions, narrow-band storage is well established; [VDB](https://museth.org/Ken/Publications_files/Museth_TOG13.pdf) provides relevant techniques. Its data structure does not, by itself, make your procedural field cheaper. Your report’s failed generic interval experiment is a good reason to insist on cheap, construction-specific bounds.
A useful intermediate technique preserves fine detail exactly: give the coarse description a **conservative envelope containing the full detailed boundary**, then evaluate the complete fine function only inside that envelope. This can retain the original detail where it matters. It works only if the envelope is cheap and sufficiently tight. Sampling coarse corners alone cannot certify that a thin feature is absent.
**What it gives:** potentially the largest lasting reduction in generation work, faster distant representations, better artistic controls, and a natural place to construct guaranteed routes.
**What it costs:** a substantial generator redesign, patch stitching, solid/interior classification, and maintaining a volumetric fallback. Junctions are the hard part.
**What it takes away:** the convenience of expressing every shape as unrestricted noise and arbitrary field operators. You can preserve output freedom through exceptions, but a world composed almost entirely of tangled exceptions will recover the expensive behaviour.
**Determinism:** replicate feature IDs, parameters, graph/layout, content version, and edits. Use canonical rules for patch boundaries and gameplay queries. Each client can build different temporary caches while deriving the same defined geometry.
This also offers a better target for precomputation. Bake a reusable library of rock profiles, fracture relief, and formation families. For scale, **256 relief tiles at 256² texels and four bytes per texel occupy 64 MiB before mipmaps**. That is an illustrative shared asset library, not a storage estimate for your whole art set. It can be reused across every seed and season.
**3. Make a crater immediately evaluable, then rebuild its acceleration structures later.**
There is a useful geometric fact behind excavation.
After subtracting a volume from rock, the resulting surface consists of:
- Surviving parts of the old rock surface.
- Parts of the excavation boundary that lie inside the old rock.
For a spherical blast, the new interior surface comes from the sphere boundary. The volume in the middle becomes known air.
That suggests treating a spell as an immediately active geometric operation, with meshes serving as cached representations of its result.
For scale, a 10 m radius sphere has approximately **1,257 m²** of surface. At 12.5 cm spacing, that corresponds to roughly **80,000 surface locations**, before intersections and other overhead. Its 20 m bounding cube contains approximately **4.17 million lattice locations**.
This is not an 80,000-sample implementation promise. It explains why a boundary-oriented edit algorithm has a fundamentally different opportunity from rebuilding the affected volume.
A possible immediate path is:
1. Commit one canonical excavation command.
2. Make movement and terrain queries observe it immediately.
3. Clip affected old surfaces and render the newly exposed boundary through a local CSG or implicit rendering path.
4. Build the enduring mesh and collision caches in the background.
5. Replace the temporary rendering only when the replacement is complete and equivalent.
Image-based constructive solid geometry is an established family of techniques: [OpenCSG](https://opencsg.org/) renders Boolean combinations without first constructing their final mesh boundary. That establishes the possibility; it does not supply a ready-made Unreal terrain solution.
The hard parts are substantial:
- The new crater surface must be clipped correctly against existing caves and earlier excavations.
- Removing the first visible wall must reveal whatever is behind it.
- Depth, shading, shadows, and both eyes must agree.
- Independent cap and terrain tessellation need consistent intersection boundaries.
- The temporary and cached representations must describe the same surface closely enough to avoid a visible changeover.
A single regional implicit rendering path can avoid some mesh-boundary problems, but then it needs robust field traversal. Your signed density cannot automatically be treated as a safe sphere-tracing distance.
For overlapping spells, render the composed result. Independently displaying several crater shells produces internal surfaces that should not exist.
This is also where the database analogy helps. The seed/layout and ordered edits are the authoritative record. Meshes, collision bodies, and GPU pages are derived indexes. An edit can become semantically active before every index is rebuilt, provided queries correctly combine the old index with pending changes.
**What it gives:** a credible architecture for same-frame excavation whose latency does not depend on draining dozens of component updates.
**What it costs:** custom rendering and terrain-query integration, plus management of overlapping edits and temporary representations.
**What it takes away:** a cheap universal implementation for arbitrarily complicated edit operators. Spheres, capsules, and bounded procedural blasts are favourable. Unbounded numbers of overlapping edits still require work; the supported spell load needs a defined budget.
**Determinism:** distribute canonical edit parameters and ordering. Immediate queries and later meshes must implement the same composition rules. A CPU-produced canonical lattice or a specified integer GPU evaluator can provide agreement where the rendering path needs authoritative signs.
“Same frame” should mean the first submitted frame containing the accepted command. Remote machines necessarily see commands after network delivery; local prediction can hide that latency. Within each machine, the whole affected region must use the same edit state.
I would regard this as a complement to your surface-detail idea. Faster generation alone does not create atomic visual publication.
**4. Reopen GPU generation specifically through defined integer arithmetic.**
Rejecting ordinary cross-vendor floating-point generation was reasonable. There is still a separate option: a deliberately restricted, integer-defined world evaluator running on both CPU and GPU.
The reason to revisit fixed point would be access to GPU parallelism while preserving the numeric contract. It would not be an assumption that integers are inherently faster than your optimized CPU floats.
The contract needs explicit widths, rounding, overflow behaviour, lookup tables, coordinate decomposition, and operation ordering. Canonical decisions cannot quietly pass through vendor-dependent floating-point functions or texture filtering. Microsoft’s [DXIL specification](https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst) defines relevant integer operations, including full multiplication results and unfiltered data loads.
Quantizing an ordinary float result at the end is insufficient: two computations can land on opposite sides of a quantization boundary.
A practical division would be:
- GPU evaluates bulk geometry or surface refinement.
- CPU evaluates the small subset required for immediate gameplay.
- Both implement the same defined rules.
- Gameplay avoids waiting for GPU readback.
**What it gives:** potentially large throughput while retaining seed-and-edits networking.
**What it costs:** a numerical implementation project, validation across supported vendors, GPU scheduling, and less freedom in procedural operators. Integer divisions or wide arithmetic can be expensive.
**What it takes away:** convenient use of arbitrary floating-point shader math for authoritative geometry. It also consumes the GPU budget needed for stereo rendering.
**Determinism:** matching canonical outputs is a release requirement, tested across CPU and GPU implementations and supported devices. It is achievable through a carefully specified implementation; the word “integer” alone does not prove it.
For scale, 140 million 16-bit samples occupy approximately **280 MB** before halos and other buffers. Storage alone is manageable on your target GPU. Producing and using those values quickly enough remains the actual question.
I would pursue this after reducing the amount of work. Otherwise, every future resolution increase retains the same cubic growth.
There are three requirements that deserve independent treatment regardless of the representation.
**Player readiness should depend on the player’s reachable surroundings.** A 4 m cube at 12.5 cm spacing contains about **36,000 lattice points**. At your stated evaluator rate, that is roughly **3–5 ms of aggregate evaluation work**, excluding setup, meshing, and collision installation. It is an illustration of the scale difference, not a proposed universal collision neighbourhood.
A moving or falling player needs a region covering the swept capsule and its near-future motion, including walls and ceilings. A cold teleport needs a valid destination and immediate terrain queries or a small ready collision patch. None of that inherently requires the complete 64 m visual neighbourhood.
The engineering challenge is reliable swept contact and edit-aware support. Your measured cook queue problem reinforces the need to keep that deadline independent. Retaining an obsolete floor after excavation is as incorrect as temporarily losing a floor that still exists.
**Kilometres of visibility should be served by compact distant surfaces and conservative visibility.** Underground occlusion can eliminate large amounts of rendering work, while a vast open chamber still needs a cheap distant representation.
However, generating only currently visible surfaces is incompatible with guaranteed immediate revelation by blasting. Keep inexpensive distant representations available and detailed geometry within the region that nearby edits can expose. Destruction also needs to update any portal or occlusion assumptions.
**Walking connectivity must include support.** A connected graph of air spaces is insufficient. Construct at least one entrance-to-exit route with constrained slope, step height, capsule clearance, and continuous supporting floor. Bound later geometric decoration against that route.
If this guarantee must survive arbitrary player edits, there is a real design conflict: excavation can remove the only bridge while increasing air connectivity. Maintaining the guarantee then requires protected support, alternative routes, or repair. That is a gameplay rule that no representation can avoid.
I would spend the next experimental effort on **a deterministic structural world with genuinely detailed surface refinement and an immediate edit representation**.
The first prototype could retain your current generator at 25 cm for ordinary structure, preserve finer volumetric regions for small holes and thin features, and add the surface layer. Your measured 25 cm result makes this a much more grounded starting point than a complete renderer replacement.
I would judge it through four demonstrations:
- Close VR inspection: silhouettes, grazing views, near-hand relief, and patch boundaries meet the owner’s quality bar.
- A cold teleport: valid support becomes available independently of the full visual region.
- A 10 m blast: the complete result appears together, including an underfoot edit and an opening into another cave.
- Cross-machine comparison: canonical geometry and terrain-query results agree.
If the surface experiment fails because the required detail is truly topological or physically interactive, preserve that fine geometry and move harder toward direct surface generation and narrow volumetric exceptions.
My longer-term bet is the second direction: **a generator that retains explicit knowledge of its surfaces**. It fits the geological structure of your target, gives walkability a natural foundation, and offers a route to increasing detail without repeatedly multiplying the work throughout every cubic metre of the world.
---
# Round 2 (fork): shorelines, fluid-looking rock, invention — 2026-10-02
Prompt: scratchpad astra-think-2.md. Answer verbatim below.
**I would now give important contacts their own geometry, and give moving rock a stable “material space” in which cuts persist.** Those two additions address the new problems and strengthen the earlier surface-based direction.
The shoreline exposes a weakness in treating independently generated representations as if they will automatically agree. Moving rock exposes a different weakness: defining a world only by where its surface happens to be this frame.
Below, “proposed” means an architectural suggestion that needs testing. “Speculative” means I see a plausible mechanism, but would not plan production around it yet. Numerical estimates are arithmetic, not measured Unreal performance.
**1. Make the shoreline a shared geometric boundary. Established geometry techniques; proposed terrain integration.**
A flat water surface is a reasonable representation of a still cave pool. It is also an unusually effective instrument for revealing geometric error.
Consider a shallow bank whose height rises by 2 cm per horizontal metre. Locally,
\[
\text{shoreline position error}\approx
\frac{\text{terrain height error}}{\text{bank slope}}.
\]
A **1 cm height error on that 2% slope can move the shoreline by approximately 50 cm**. This explains why terrain that looks acceptable elsewhere can look conspicuously wrong at water level.
Marching cubes produces piecewise planar surfaces, so its intersection with a plane is piecewise linear. Additional stair-like appearance can come from density quantization, displacement, LOD transitions, or a water mask derived from another representation. Smooth normals can improve lighting, but cannot repair the geometric outline.
My proposal is to make the intersection an explicit curve shared by both surfaces.
For a flat pool at height \(H\), find the curve where the intended rock surface meets \(z=H\). Then:
- Make the nearby rock triangles conform to that curve.
- Make the water boundary use the same curve.
- Derive wetness, deposits, and near-bank effects from it.
- Refine the rock in a narrow surrounding strip to preserve the correct shape and normals.
The “intended rock surface” must include any displacement that materially changes the contact. Computing the curve from coarse collision geometry while rendering a substantially displaced bank simply introduces another disagreement.
This is related to boundary representation in CAD: intersections are first-class geometry. Guenter and Gavriliu’s [Generative CSG Models for Real Time Graphics](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-2005-43.pdf) explicitly represents intersection curves and uses them for adaptive triangulation. Its expensive preprocessing makes it a conceptual precedent, not evidence that arbitrary cave intersections can be reconstructed instantly.
The favourable part is dimensionality. **A shoreline is primarily a one-dimensional problem.**
For illustration, a 100 m shoreline sampled every 2 cm has approximately **5,000 curve locations**. A surrounding strip 40 cm wide, sampled at the same spacing, contains roughly **100,000 surface locations**. Root finding, transitions, normals, and topology add work, but this is a very different expenditure from refining a volume.
There is a useful rendering refinement here too: maintain separate tolerances for **position and normal direction**. Accurate contact positions fix the outline; accurate surface normals fix the faceted reflection. One tolerance does not guarantee the other.
- **Cost and loss:** substantial meshing work around intersections, plus handling curve splitting and merging after edits. Near tangencies, an intersection can appear or disappear, so tracing one existing curve is insufficient to discover every component.
- **Multiplayer:** derive the curve from the canonical shape, water level, and edit version using specified numeric rules. Different clients can tessellate between shared boundary constraints differently for rendering. Gameplay-relevant bank geometry remains canonical.
- **First cheap experiment:** use one analytically defined bowl with a ledge, intersected by flat water. Render unlit colours first, then reflective materials. Shift the sampling-grid origin while holding the underlying bowl fixed. Add a conforming shoreline strip and check whether the contact becomes stable without refining the whole bowl. Also raise the water through the ledge to expose topology failures.
**2. Generate pools from their shorelines and geological history. Proposed construction method, with strong natural precedents.**
For generated pools, there is an even cheaper possibility: **choose the shoreline before constructing the surrounding bank**.
The recipe could start with:
- Water elevation and an irregular closed boundary.
- A bank profile along that boundary: steep fracture, shallow shelf, undercut, sediment apron, or mineral lip.
- A basin interior.
- A transition into the surrounding cave surfaces.
The rock is then constructed to meet the water deliberately. Noise modifies that construction within limits that preserve the intended contact.
This is particularly suitable for your visual target because natural caves already provide expressive shoreline structures. Flowstone forms through mineral deposition by flowing films; rimstone dams hold pools and grow through deposition where water passes over them. The [National Park Service’s cave geology account](https://www.nps.gov/grba/planyourvisit/cave-geology-in-depth.htm) describes these processes and formations.
A pool could therefore have a fine scalloped mineral lip, a darker wet band, submerged sediment, and occasional fractured interruptions. Those features explain the contact visually and give the generator meaningful parameters.
Recent research also supports treating contours as primary terrain descriptions. [Huftier and colleagues’ 2026 contour-based terrain work](https://perso.liris.cnrs.fr/apeytavi/website/publication/hal-05608822/) generates and edits terrain through elevation contours. It addresses heightfield terrain, so extending it to cave overhangs and intersecting chambers remains a separate problem. I would apply the idea locally around pools.
The water itself can contribute appropriate detail:
- Small waves whose behaviour changes near banks.
- A restrained meniscus.
- Wetness and sediment colour gradients.
- Refraction that reveals the submerged bank.
- Foam where there is actual agitation.
However, a meniscus is a small-scale effect. Water’s capillary length is approximately **2.7 mm**, so it should not be expected to conceal decimetre-scale geometric problems in a broad pool. [MIT’s capillarity notes](https://web.mit.edu/nnf/education/wettability/gravity.html) give that scale. Similarly, a wide foam outline around every still cave pool would become its own procedural signature.
I would distinguish **current contact** from **historical deposits**. Lowering the water should reveal old mineral marks. Blasting a rim should remove it; the generator should not immediately grow a replacement decorative lip around the crater.
- **Cost and loss:** more authoring logic and a preference for pools with intentional bank structure. Freely changing water levels still require fresh intersection handling outside the originally constructed contact.
- **Multiplayer:** pool outlines, profiles, historical water levels, and structural deposits belong to the seeded recipe. Current water levels must be authoritative wherever swimming, drainage, or access depends on them. Tiny ripples and wet highlights can be cosmetic.
- **First cheap experiment:** generate one pool with three bank styles from a shared curve. Lower its water, then cut through its rim. The experiment succeeds only if the bank remains convincing, the old watermark stays put, and the excavation does not trigger decorative “healing.”
**3. Give stone the visual language of flowing material before moving its bulk. Established rendering techniques; artistic hypothesis.**
There are several distinct ways for rock to look fluid:
| Appearance | What actually changes |
|---|---|
| Frozen waves, folds, or drapery | Static geometry |
| Veins and surface patterns flowing over stone | Material coordinates and shading |
| Ripples crossing a wall | Shading plus shallow displacement |
| A wall visibly bulging into a passage | Gameplay geometry, if reachable |
| Stone pouring, splitting, or merging | Material transport and topology |
The first three can provide a large part of the desired effect at much lower cost than the last two.
For a living wall, I would try a combination of stationary large fractures and slowly travelling mineral ridges. The motion would have a direction and coherent scale: sweeping upward, curling around columns, spreading from impacts, or descending into basins.
A flow map supplies local direction and speed. Valve demonstrated this approach for animated normal and colour textures in [Water Flow in Portal 2](https://advances.realtimerendering.com/s2010/index.html#Water%20Flow%20in%20Portal%202). Applied to stone, it could transport veins, roughness variation, and shallow relief.
For more organic motion, [curl noise](https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph2007-curlnoise.pdf) supplies procedural fluid-like velocity fields with mechanisms for respecting boundaries. It does not require running a full fluid simulation. Transporting textures or particles through that field still needs an appropriate animation scheme; an attractive velocity field alone does not solve stretching, looping, or temporal consistency.
My artistic hypothesis is that **coherent folds and motion at a few scales will read as flowing stone more convincingly than many animated noise octaves**. Keep the reflectance recognizably mineral unless the intention is explicitly wet or molten material.
Frozen waves deserve their own inexpensive approach: run an expensive viscous simulation during asset creation, select compelling states, then turn them into reusable surface motifs. Research on [viscous buckling, folding, and coiling](https://www.cs.ubc.ca/labs/imager/tr/2008/Batty_ViscousFluids/) demonstrates precisely the kinds of shapes that could become stone draperies and frozen cascades. Runtime generation would place and deform the resulting recipes or assets; it would not reproduce the fluid simulation.
- **Cost and loss:** shader sampling, texture transport, and possibly fine surface geometry. Flowing shading has limited power at silhouettes and under close VR inspection. Frozen motifs need enough variation to avoid repetition.
- **Multiplayer:** fixed structural geometry remains the authority. Shared assets and seeds define frozen forms. Cosmetic motion can use a common phase without requiring identical pixels. Any displacement large enough to alter cover, contact, or digging must move into the authoritative category.
- **First cheap experiment:** use one static wall and compare three versions: flowing material only, material plus shallow relief, and a frozen folded surface. Inspect them in VR from the side and with a stationary hand or tool nearby. This quickly reveals how much physical movement the desired effect actually needs.
**4. Animate a few controlled deformation patterns. Established animation mathematics; proposed gameplay integration.**
For actual breathing geometry, I would begin with a small set of motion patterns:
\[
S(u,v,t)=S_0(u,v)+\sum_k a_k(t)\,B_k(u,v).
\]
Here, \(S_0\) is the resting surface. Each \(B_k\) is a pattern such as a bulge, travelling ridge, or twist. The coefficient \(a_k(t)\) controls its strength.
The useful property is that the shape can be evaluated directly at a requested time. A client arriving late does not have to simulate every previous frame.
Patterns can be designed to vanish along protected boundaries: the floor, a doorway, a pool edge, or the edges of an animated region. Where reflections reveal changes in slope, make the deformation’s first derivative vanish there too.
Impact-driven motion has a particularly relevant precedent: [Dynamic Kelvinlets](https://graphics.stanford.edu/~djames/publication/dynamic-kelvinlets-secondary-motions-based-on-fundamental-solutions-of-elastodynamics/). These provide analytical elastic responses such as ripples and waves from localized forces, avoiding a conventional volumetric simulation. They are models of elastic secondary motion, not complete fluid or fracture simulation.
A spell could therefore produce two effects:
1. A permanent excavation command.
2. A temporary elastic wave, defined by its origin, start time, strength, and decay.
For an initial version, I would keep the walkable floor stationary while nearby walls respond. Later, genuinely moving floors can use the same evaluator for geometry and terrain contacts.
**Motion speed and acceleration matter as much as displacement amplitude.** For a sinusoidal movement:
- An amplitude of 3 cm at 0.5 Hz gives peak acceleration of approximately **0.30 m/s²**.
- An amplitude of 20 cm at 2 Hz gives approximately **32 m/s²**, over three times gravity.
The latter is not a gently breathing floor. Correct physics would make it a powerful moving platform.
A rough work estimate: a 10 m square patch sampled every 5 cm has about **40,000 vertices**. Eight patterns require approximately **320,000 pattern contributions per geometry pass**. Stereo, shadows, normals, and shading increase that work. This is an operation count, not a frame-time prediction, but it shows why surface animation can be much cheaper than regenerating a density volume every frame.
- **Cost and loss:** deformation evaluation, updated normals and bounds, correct shadows and previous-frame positions, and custom terrain contact handling for substantial movement. Ordinary shader vertex displacement does not automatically update collision. The result has a controlled motion vocabulary rather than unrestricted fluid behaviour.
- **Multiplayer:** use authoritative ticks and a versioned evaluator. Evaluate coefficients from time and compact events, avoiding frame-by-frame accumulation. Gameplay-relevant arithmetic needs the same determinism discipline as terrain generation; a shared sine expression alone is insufficient.
- **First cheap experiment:** animate one wall with four patterns, pin its lower edge, and trigger one impact wave. Evaluate the same timestamps after different frame-rate histories and a late join. Then make a small platform move and test whether contact velocity is handled correctly. Stop if it requires recooking terrain collision every frame.
A deformation also needs limits on spatial variation. Small amplitude does not prevent very short waves from folding a surface over itself.
**5. Let moving stone remember cuts in material coordinates. Speculative architecture; established invertible-map mathematics.**
This is the direction I find most interesting.
Imagine every piece of rock carries permanent coordinates, like markings embedded throughout a block of clay. The clay can deform, but those coordinates stay attached to its material.
Define the rock and its excavations in those coordinates. A time-dependent mapping moves the material into the visible world:
\[
x=T_t(q),
\qquad
F_t(x)=F_{\text{edited}}\!\left(T_t^{-1}(x)\right).
\]
In plain terms:
- To render a point of rock, move it forward through the deformation.
- To query the rock at a world position, undo the deformation and query the stored shape.
- A hole follows the material because it is stored with the material.
Between edits, the reference geometry can remain cached. Animation changes its positions rather than repeatedly rediscovering its topology.
The difficult requirement is a cheap, reliable inverse.
One construction uses successive nonlinear shears. Shift \(x\) by an amount depending on \(y,z\); then shift \(y\) by an amount depending on the updated \(x,z\). Each step can be undone by subtraction in reverse order. Smooth additive shears preserve volume individually and remain invertible. The same mathematical construction appears as additive coupling layers in [NICE](https://arxiv.org/html/1410.8516v6); no neural network or training is required for this use.
My speculative application is a collection of such maps producing slow eddies, waves, and folds inside selected rock regions.
An additional experiment would be:
> Apply an x-shear, then a y-shear, then undo the x-shear, then undo the y-shear.
Because the operations act on already changed coordinates, they generally leave a deformation inside their interaction region. With appropriately localized shear functions, their effects can cancel outside a bounded surrounding region. That suggests a localized, invertible “stone eddy.” The support bounds and resulting distortion would need to be verified.
The digging rule is essential.
If a spherical blast occurs while the rock is bent, **pull the entire blast shape back into material coordinates**. Transforming only the sphere’s centre and keeping its radius would usually be wrong: a nonlinear deformation turns its inverse image into a more complicated shape.
A compact edit can retain the blast parameters and the frozen deformation parameters from the instant of impact. The cut then remains attached to the removed material as the rock continues moving.
A permanently world-fixed exclusion volume is another possible game mechanic, but it has different semantics. It continually clips whatever material enters it. I would make that distinction explicit rather than accidentally letting craters slide or appear to heal.
There are several important limits:
- Invertibility preserves topology during deformation; it does not preserve walkable slope or clearance.
- A deformed signed field is generally no longer a true distance field.
- Inverse-transforming a capsule does not leave an ordinary capsule under a nonlinear map.
- The exact smooth surface may remain valid while an insufficiently subdivided triangle approximation develops artifacts.
Consequently, this does not make collision trivial. It makes the shape and edit history compact and queryable.
- **Cost and loss:** a significant research and physics-integration effort, inverse mappings on queries, distortion bounds, and material-space edit evaluation. Deformation alone cannot reproduce fluid breakup or merging; those require separate topology operations.
- **Multiplayer:** canonical mapping parameters, simulation time, and material-space edits define the same world. Carefully defined fixed-point shears can even have exactly reversible coordinate updates when overflow is excluded, although that does not by itself prove the whole collision system correct.
- **First cheap experiment:** one deforming slab, one spherical excavation, and a few ray/contact queries. Cut it at maximum deformation, return to rest, then deform it again. Check that the same material stays missing. Replay at different frame rates and test forward/inverse round trips. This should be settled before attempting a whole animated chamber.
**6. Make contacts and protected routes part of the generator’s input. Speculative unifying design.**
The broader architectural change I would now investigate is to give a region an explicit collection of important geometric constraints:
- A pool’s contact boundary.
- A doorway’s edge.
- A continuously supported walking route.
- The attachment of a column.
- The boundary between stationary and moving rock.
- The rim created by a recent excavation.
Surface patches are then generated and refined around those constraints. Detail and motion have budgets relative to them.
For example, a chamber could contain a still pool and a stationary path beside it. The rock higher on the wall could ripple strongly. Its deformation would fade smoothly to zero at the protected boundaries. Fine mineral patterns could continue flowing through those zones because they do not alter support.
That permits visibly dramatic motion without moving every gameplay-critical surface.
It also suggests a better refinement policy. Allocate geometry according to the error that matters locally:
| Location | Important error |
|---|---|
| Water contact | Position of the intersection |
| Reflective rock | Surface-normal direction |
| Narrow passage | Minimum capsule clearance |
| Animated floor | Contact position and velocity |
| Ordinary distant wall | Projected image error |
A uniform spatial resolution is a poor proxy for these different requirements.
Motion envelopes help here. A generator can prove that a wall moving within a known envelope cannot enter a protected corridor. But that envelope is a clearance or broad-phase tool: it cannot replace the actual surface for close player contact without creating visible collision discrepancies.
I would also keep these constraints independent of each client’s camera. Freezing a wall only when the local player approaches is liable to create conflicting worlds. Use fixed protected zones or an explicit authoritative state change.
- **Cost and loss:** more information in the world recipe, coupled generation rules, and resolving conflicting constraints. Some combinations of motion, thin walls, and route clearance must be rejected or altered.
- **Multiplayer:** the constraints and motion limits belong to the canonical layout. Rendering can vary its subdivision while respecting the same boundaries and gameplay surface.
- **First cheap experiment:** construct one chamber containing a pool, a door, a walking strip, and an animated wall. Keep those contacts stable while varying the surrounding shape and motion. If this requires special-case fixes at every junction, the representation needs simplifying before expansion.
The division I would use for moving rock is therefore:
| Feature | May be cosmetic? |
|---|---|
| Colour, roughness, veins, fine normal variation | Yes, unless used to communicate gameplay state |
| Very small relief | Yes, within a tested visual/contact tolerance |
| Large movement in an unreachable region | Potentially; still needs appropriate bounds and visual interactions |
| Reachable bulges, footholds, cover, passage clearance | Requires authoritative geometry and queries |
| Excavation and material removal | Requires authoritative edit semantics |
For implementation research, **the shared shoreline curve is my strongest near-term bet**. It concentrates precision where the image is unusually sensitive and can be tested without replacing the whole terrain system.
For invention, **the invertible material-space wall is the experiment I would fund**. It offers a plausible way to combine motion, persistent digging, compact networking, and cached geometry. Its first success criterion should be modest and strict: one moving slab whose hole, visible surface, and contact queries continue to agree.
I would revise the earlier surface-first recommendation accordingly: retain explicit knowledge of the surfaces, their important intersections, and the material coordinates that survive animation. Those are useful facts the generator can provide once and reuse throughout rendering, editing, and gameplay.
---
# Round 3 (fork): performance only, unrealistic goal (whole view < 1 s) — 2026-10-02
Prompt: scratchpad astra-think-3.md. Answer verbatim below.
**I would bet on a generator that emits boundaries and solid intervals directly.** The distant world would come from a hierarchy of surface pieces. Nearby Boolean operations—joining caves, digging, blasting—would operate on intervals and packed bits. A density function could remain available for queries, but generating a view would no longer mean filling its volume with density samples.
I can construct a **900 ms engineering budget** for this design. That is a target to test, not a measured prediction. Its essential condition is that the world’s composition limits the amount of surface work required. An arbitrary field can contain arbitrarily much geometry; no representation can guarantee a second without some complexity contract.
The proposed architecture combines established techniques. The particular composition rules, division of work, and budget below are my proposal.
**There is a larger opportunity in your current far field than the word “clipmap” suggests.**
Your source supports expanding the outermost ring at a fixed maximum LOD. The checked-in settings use `RenderDistanceChunks=1536` and `MaxClipLevel=5`: that gives an outer radius of 48 tiles, approximately **97 × 97 horizontal positions per vertical slice**, before exclusions. Those are defaults, not a census of your running world. Nevertheless, this can turn the horizon into a large fixed-resolution grid. The settings explain why: coarser volume sampling can miss thin strata. [Settings](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Public/VoxelSettings.h:166>), [outer-ring calculation](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelClipmapDesiredTiles.h:46>).
That is a representation problem with a performance consequence. **A distant floor should survive because it is explicitly a floor, rather than because a coarse lattice happens to intersect it.**
There is a similarly useful distinction nearby: your column evaluator already shares setup across heights, but still loops through every Z sample. The opportunity below is to remove that loop by solving where the column enters and leaves rock. [Column evaluator](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Source/VoxelForge/Private/VoxelRedefinedDensityField.cpp:2851>).
**First, make each world piece know how to produce its surface.**
A room, tunnel segment, shelf, shaft, pillar or floating mass would carry a small description with:
- Its bounds and connections.
- A way to emit its surface at several resolutions.
- A way to intersect an axis-aligned line with its solid.
- Shared boundary curves for joining neighbouring pieces.
- A limit on how much refinement and Boolean work it can require.
An untouched tunnel would emit a swept surface. A chamber would emit patches with explicitly constructed openings. A floating mass would emit a closed surface. These operations evaluate positions **on the surface**, without searching a surrounding volume for it.
The difficult part is junctions. Drawing overlapping closed room and tunnel meshes leaves unwanted internal walls. I would begin with a bounded family of junction constructions whose openings, trimming and transitions are already defined. More unusual intersections would use the local interval compiler described below.
This is a substantial generator redesign. Its advantage is that it makes the cost of a feature predictable.
The hierarchy must also be **constructed from coarse to fine**. A parent knows the major chamber boundaries, floor sheets and passages before its children exist. Children add bounded shape variation and smaller features. A distant parent can therefore be rendered without generating and simplifying all its descendants.
That does **not** make a coarse mesh an exact copy of every fine surface. What can be exact is the parent’s enclosure, connection information and refinement contract. The visible surface remains an approximation with a specified error.
For deterministic connections, neighbouring pieces can derive their shared opening from the same integer face key. Each side then receives identical opening geometry. Matching-boundary composition has a precedent in Wang tiles; extending it to a hierarchical cave grammar with guaranteed connectors is my proposed adaptation. It needs a constructive connector for every allowed boundary configuration, otherwise a search problem returns. [Cohen et al., *Wang Tiles for Image and Texture Generation*](https://graphics.uni-konstanz.de/publikationen/Cohen2003WangTilesImage/index.html).
Build the initial walkable route into this hierarchy, with enforced clearance and floor slope. Its existence then follows from construction; discovering it does not require generating the whole cave and running navigation over it.
**The far field would contain selected surface patches, including explicit thin strata. It would contain no density tiles.**
All selected LODs must be ready by the deadline. Showing a coarse preview while the requested near surface is still missing would not count.
**Second, replace point evaluation with interval algebra wherever Boolean composition is necessary.**
Consider a spherical cavity. At a fixed horizontal position \((x,y)\), define
\[
d=r^2-(x-c_x)^2-(y-c_y)^2.
\]
If \(d<0\), that column misses the cavity. Otherwise its entire air interval is
\[
[c_z-\sqrt d,\;c_z+\sqrt d].
\]
One calculation has answered every height in that column.
Multiple rooms and tunnels produce lists of intervals. Joining cavities becomes interval union. Adding a pillar removes an interval from air. A crater adds an air interval. There is no need to evaluate the composed function at every Z coordinate.
This is related to **dexel representations**, used in machining and virtual sculpting: solids are represented by entry and exit depths along parallel lines, and cutting becomes interval operations. This is established machinery, not a new claim about solid modelling. [Peng et al., *Surface Reconstruction from Dexel Data for Virtual Sculpting*](https://scholarsmine.mst.edu/mec_aereng_facwork/1226/).
For your world, I would use:
- Quadrics for ellipsoids, cones and suitable chamber shapes.
- Piecewise analytic segments for tunnels.
- Explicit floor/ceiling intervals for shelves and room interiors.
- Bounded local exceptions for shapes outside that family.
Using line families along **all three axes** provides boundary intersections in each direction, including on vertical walls. A 256-cell cube has approximately **198,000 lattice lines across the three directions**, versus roughly 17 million lattice points. If each line encounters an average of 12 candidate primitives, that is about **2.4 million primitive-line tests**.
That is an operation-count estimate, not a speedup measurement: root solving and interval merging cost more than a single comparison. However, each test can replace many expensive calls to your complete density evaluator.
This method becomes unattractive if every line crosses hundreds of tiny features. The generator must keep that complexity bounded, or represent those features directly as surfaces.
**There are two useful ways to exploit 64-bit words here.**
The first is to represent which primitives are active while sweeping through sorted interval endpoints. With at most 64 local primitives, their membership fits in one word. At a crossing, update the corresponding bit. Between crossings, membership is constant, so the local Boolean expression has one answer for the entire span.
For a restricted construction such as “rock minus rooms and tunnels, plus pillars, minus craters,” the answer needs only a few tests of those membership masks. Arbitrary CSG trees require more work; the simple expression is a deliberate composition rule.
The second is to store **64 consecutive rock/air signs in one word**:
- Union, intersection and subtraction become OR, AND and AND-NOT.
- A long uniform span writes whole words.
- XOR with a neighbouring column identifies changed signs along X or Y.
- XOR with a shifted word identifies changed signs along Z.
- Bit scans enumerate only the interesting locations.
For marching cubes, align the eight corner-sign words for 64 cells. Then
\[
\text{active}=\operatorname{OR}(\text{eight words})
\;\land\;
\neg\operatorname{AND}(\text{eight words}).
\]
That identifies 64 potentially intersected cells using word operations. `std::countr_zero` and clearing the lowest set bit can enumerate the survivors. Binary greedy meshers demonstrate the usefulness of processing occupancy and face masks this way. Their block-shaped output is not what I propose using for cave surfaces. [Binary Greedy Meshing](https://github.com/cgerikj/binary-greedy-meshing).
The smooth surface comes from the **actual boundary crossings**, with quantized positions and normals, not from placing vertices halfway between binary samples.
Two qualifications matter:
1. Packing an arbitrary field into bits does not make generating those bits cheap. The interval construction is what removes the evaluations.
2. Equal corner signs can hide a thin feature entirely inside a cell. Such features need explicit primitive-intersection tags, refinement, or direct surface geometry. Binary signs alone cannot preserve them.
A near ring with 17.4 million signs needs approximately **2.2 MB**, compared with approximately **69.6 MB for float densities**. Boundary data and meshes are additional, but the interior representation becomes small.
**There is a particularly interesting mathematical family to investigate: piecewise triquadratic fields.**
This is an optional extension, not something the first prototype must depend on.
A polynomial that is quadratic separately in X, Y and Z has 27 coefficients. It offers two useful properties:
- Restrict it to any axis-aligned line, and it becomes a quadratic: at most two isolated crossings unless the polynomial degenerates.
- Express it in the Bernstein basis over a box, and its value lies between the smallest and largest coefficients.
Consequently, one small coefficient block supplies both cheap line intersections and conservative whole-box sign bounds. All coefficients positive means the entire box is air; all negative means rock. Subdivision can tighten the bounds.
Bernstein bounds and subdivision are established mathematics used in implicit-surface research. **My speculative proposal is to make this restricted polynomial family part of the cave’s native construction language**, rather than fit it to an already expensive noise field. [Bán and Valasek, *Generalized Lipschitz Tracing of Implicit Surfaces*](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.15202).
This distinction matters: fitting an arbitrary existing field would reintroduce sampling and approximation problems.
Bounded detail can be handled similarly. If a parent function has a known range \([L,U]\), and all omitted detail together is bounded by \(R\), then
\[
F_{\text{complete}}\in[L-R,\;U+R].
\]
Whenever that interval excludes zero, all descendants are irrelevant to occupancy there. The amplitude bound must come from the construction rules; estimating it from a few samples would not be a proof.
This approach spends a small, predictable amount on bounds supplied by each primitive. Your report’s failed generic interval experiment is good evidence against wrapping the existing entire evaluator in a more expensive arithmetic system.
**The most radical version would upload surface instructions instead of most vertices.**
An untouched surface patch can be described by its type, coefficients, transform, boundary identifiers and tessellation level. The GPU can draw a shared index pattern and calculate vertex positions from that description. This is ordinary rasterized geometry; it does not require raymarching the whole world each frame.
As an illustrative estimate:
- 20,000 patch descriptions × 128 bytes = **2.56 MB**.
- A conventional mesh with 1.2 million vertices and 2.4 million triangles, using 24-byte vertices and 32-bit indices, is **57.6 MB**.
Both still produce geometry for the GPU to process. The saving is CPU generation, storage, transfer and object setup.
Edited regions would switch to explicit generated patches. Untouched regions could remain compact procedural instances indefinitely.
The costs are a custom rendering path, restricted patch families, careful transitions, and GPU arithmetic repeated in depth and shadow passes. Some patches should therefore be expanded once into GPU buffers and retained. This is a credible research direction, but I would **not require it to pass the first CPU experiment**.
**The rest of the pipeline needs to be designed around bulk work.**
Your recorded **639 mesh applies per second** is sufficient to make thousands of separate publications a problem even if generation becomes free. Your report also separates fast collision cooking from multi-second queue delays. Those are measured reasons to change the publication and support paths for this larger objective. [Recorded results](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/RESEARCH-SPEED-2026-09.md:35>).
I would use pooled geometry buffers and a small number of spatial render pages. Tiles or patches retain their own bounds and update ranges inside those pages. A small edit must update its ranges rather than rebuild a large page.
GPU culling and indirect drawing can reduce CPU submission work while retaining many independently culled pieces. Direct3D provides the necessary indirect command mechanism; integrating it into Unreal’s scene, material and shadow paths is real implementation work. [Microsoft, *Indirect Drawing*](https://learn.microsoft.com/en-us/windows/win32/direct3d12/indirect-drawing).
Only the player’s immediate support neighbourhood belongs on the initial collision deadline. Give it a priority path through generation, cooking and installation. Treat “physics state installed and a capsule sweep succeeds” as completion, rather than “cook task submitted.”
Boundary ownership must also be explicit. Adjacent patches use identical quantized border samples and defined transition strips. Edited replacement patches meet those shared borders. This work belongs in the meshing budget; overlapping skirts are not a general proof of a watertight result.
**Here is where I would allocate the second.**
These are **proposed acceptance budgets**, not benchmark results. The workload assumed is:
- A 64 m near region at roughly **25 cm structural resolution**, with finer appearance handled without a finer volume lattice.
- All selected distant surfaces to the requested horizon, including stacked layers.
- At most about 50,000 hierarchy records, 2–4 million nearby primitive-line tests, and **2.4 million output triangles**.
- Six generation workers.
- No seed-dependent geometry or field cache.
The structural resolution and triangle count are explicit concessions. This budget does not establish the same result for every geometric feature in the current 12.5 cm field.
| Stage | Work assumption | Principal data | Wall-time budget |
|---|---|---:|---:|
| Generate/select hierarchy and connections | Up to 50,000 records; order of 10–20 M simple operations | 3–8 MB | 30 ms |
| Solve and compose nearby intervals | 2–4 M primitive-line tests; order of 100–300 M arithmetic/comparison operations, plus roots | Paged scratch, target ≤24 MB | 150 ms |
| Produce sign masks and active cells | Up to roughly 1 M word groups; tens of millions of bit operations | 2–8 MB | 30 ms |
| Emit all near/far meshes and transitions | 1.2 M vertices, 2.4 M triangles; order of 100–250 M geometry operations | 57.6 MB output | 220 ms |
| Cook and install immediate support collision | Approximately the current small support neighbourhood | A few MB | 65 ms |
| Bulk GPU upload and scene publication | At most roughly 64 page publications | Approximately 60 MB | 100 ms |
| Final fence and first completed frame | Includes frame scheduling | — | 25 ms |
| Allocation, imbalance and integration contingency | — | — | 280 ms |
| **Total** | | | **900 ms** |
The budgets are added conservatively; I have not counted overlapping stages as an extra saving.
At an **assumed effective upload rate of 10 GB/s**, 60 MB takes approximately 6 ms of transfer time. That illustrates why publication, allocation and synchronization could matter more than raw bytes. It is not a measurement of your Unreal path.
Likewise, the operation counts do not automatically imply the listed milliseconds. Roots, unpredictable branches and cache misses differ enormously in cost. The experiment must measure them.
The largest unknown is the **required output complexity**. If a representative complete view needs 20 million triangles under the chosen surface error, this particular budget fails. The remedy would have to be better surface construction, instancing or approximation—not a claim that packed occupancy removed the output cost.
“Cold” here means the application is running, shaders are available, and the seed/layout/edit data have arrived, but no world-specific result exists. Starting Unreal, compiling shaders or downloading an unbounded edit history requires a separate budget.
**The 5800X3D makes a compact implementation particularly attractive.**
AMD specifies eight cores, sixteen threads, 96 MB of L3, AVX2 and dual-channel DDR4-3200 for this processor. Its theoretical dual-channel memory rate is 51.2 GB/s; actual application bandwidth must be measured. [AMD specifications](https://www.amd.com/en/products/processors/desktops/ryzen/5000-series/amd-ryzen-7-5800x3d.html).
I would organize the implementation as follows:
- **Keep repeatedly accessed data around 40–60 MB.** The 96 MB is shared with the rest of the application. Stream finished mesh output rather than keeping every intermediate alive.
- **Process local pages with worker-owned scratch.** A 64³ sign block is only 32 KiB before borders. Finish its interval, boundary and mesh work while the relevant data are close.
- **Use arrays of primitive coefficients grouped by type.** AVX2 should process similar intersections together, without virtual dispatch or scattered feature lookups inside the loop.
- **Use AVX2 for batches, ordinary words for bitsets.** One vector handles four 64-bit masks. This is useful after the representation has removed most work; it is not a claimed 256-fold generator acceleration.
- **Exploit forward differences.** Along an integer scanline, \((x+1)^2-x^2=2x+1\). Quadratic terms can advance with additions; hash and coefficient setup happen once per feature or row.
- **Use Morton order between pages and simple linear order inside them.** This preserves spatial locality without expensive addressing in every inner iteration.
- **Measure six versus eight physical workers before using extra SMT workers.** Leave capacity for the game thread, renderer and collision path. Sixteen hardware threads do not supply sixteen independent cores.
Avoid per-column allocation, shared append locks and global vertex hash tables. Count locally, assign deterministic output ranges, and write disjoint buffers.
**Multiplayer agreement would come from a small, explicit numerical contract.**
Use integer world keys, a versioned hash, fixed parameter quantization and stable feature identifiers. Define:
- Rounding and sign-at-zero rules.
- Endpoint inclusion and coincident-crossing rules.
- Boolean operation order and material priority.
- Shared edge ownership and vertex quantization.
- Arithmetic ranges, widening and overflow behaviour.
For quadrics, an integer square root or an exact predicate can determine the canonical crossing bracket. A floating-point estimate may accelerate the search only if exact arithmetic verifies and corrects the result.
GPU floating-point surface shading can vary without changing gameplay. If GPU-generated geometry itself makes authoritative decisions, its integer decisions must match the CPU reference. Merely changing the language’s numeric type does not establish determinism.
Clients independently generate the same descriptions and process the same ordered edits. Physics simulation need not become deterministic lockstep merely because world geometry is deterministic.
For a 10 m spherical crater at 25 cm spacing, its projected footprint covers approximately
\[
\pi(10/0.25)^2 \approx 5{,}000
\]
columns per axis. That is roughly 15,000 affected lines across three directions, rather than evaluating the whole 20 m bounding cube.
That makes immediate editing much more promising. It does **not** prove same-frame publication. The replacement surfaces must still be generated and switched together across every affected page and LOD. CPU support queries must observe the new edit immediately, even while replacement collision bodies are pending.
**What this design gives up is mathematical generality, rather than three-dimensionality.**
Overhangs, stacked chambers, floating masses, shafts and craters remain possible. The restrictions are on how they are composed:
| Restriction | Performance obtained |
|---|---|
| A bounded primitive and junction family | Predictable surface emission and intersection cost |
| Limited overlap and crossings per local region | Short interval lists and cheap Boolean composition |
| Coarse structure constrains later refinement | Far geometry exists without generating descendants |
| Fine appearance does not all become collision topology | Surface work avoids growing with microscopic detail |
| Edited regions become local exceptions | The untouched majority keeps its compact representation |
| Finite supported scene and edit complexity | A meaningful loading-time target |
The long-term risk is that edits accumulate until most regions become expensive exceptions. Local edit histories therefore need deterministic pruning or compaction into a bounded replacement representation. An unlimited edit log cannot have a fixed cold-load guarantee.
A full-view raymarcher would remove mesh construction but move substantial work into every stereo frame. Global FFTs and tensor decompositions can be excellent for suitable restricted functions, but do not automatically survive arbitrary local cuts cheaply. I would pursue those after the boundary-producing construction has been tested.
**The first experiment should test the interval compiler and count the output—not attempt a new renderer.**
A one-day standalone C++ spike could answer the central question:
1. **Build a representative geometric corpus.** Use room and tunnel records resembling your actual parameter ranges, plus pillars, shelves, floating solids, thin strata, rotated shapes and intersecting craters. Include crowded junctions. A sparse collection of spheres is insufficient.
2. **Implement two versions of the same restricted world.** One evaluates occupancy point by point as a reference. The other generates intervals, composes them, fills sign words and extracts boundaries. They must describe the same new geometry; matching every detail of the old noise field is not the experiment.
3. **Produce real mesh buffers.** Include boundary positions, indices, borders and deterministic ownership. A fast occupancy-only result can conceal an expensive reconstruction stage.
4. **Exercise the far-world rule separately.** Generate the complete selected hierarchy and its coarse surface buffers without constructing fine descendants. Record nodes, patches, vertices and bytes. If implementing the full hierarchy exceeds the day, this part remains explicitly unproved.
5. **Measure fresh-process runs on the 5800X3D.** Include allocation and first-touch costs. Test one, six and eight workers; report median, p95 and worst across seeds. Validate outside the timed region against the pointwise reference, including negative coordinates, page borders, tangencies, thin features and coincident edits.
I would use these initial decision gates:
| Result | Decision |
|---|---|
| All CPU geometry fits approximately **430 ms**, with output around **60 MB** | Proceed to the Unreal publication and collision test |
| Interval work fits its **150 ms** allocation, but meshing exceeds the budget | Investigate direct patches, reconstruction and output size |
| Only the simple cases pass; representative junctions produce huge interval lists | Reject or restrict that composition family |
| Far geometry requires fine descendants or dense strata sampling | The proposed hierarchy has not removed the principal cost |
| Any missing thin feature or inconsistent shared boundary | Correctness fails regardless of speed |
The standalone test can establish whether the computational core deserves investment. The final proof is an Unreal run timed through **the first complete visible frame and successful player-support collision**, followed by a separate check that steady rendering meets the VR frame budget.
I would spend the first day on **analytic intervals plus direct far surfaces**. They attack both sources of unnecessary work: repeatedly discovering nearby boundaries, and reconstructing distant surfaces from a volume that the player will never interact with.
+220
View File
@@ -0,0 +1,220 @@
# VoxelForge speed research — 29 September 2026
## Consolidated decision
**Build the spatial/evaluation redesign first and keep the current precise float procedural math.** Compile the seed, layout, composer and operator stack once per region into a stable local program; bin rooms, tunnel segments and passages into 8³-cell bricks; compute cheap *final-field* solid/air certificates; share lattice samples across tiles and LODs; contour uncertain bricks. Use a versioned, explicitly rounded **16-bit lattice density** as the common mesh/edit/query cache. Keep the procedural float evaluator as the cache producer and on-demand *lattice-sample* fallback; player collision queries interpolate the same quantized lattice, including newly generated cells, so visual and collision signs cannot diverge through an off-grid float query. The quantization scale, zero tie and interpolation rule belong to the new world ABI. This changes the world version without requiring a full fixed-point rewrite of Perlin and every content operator. [Build flags](Source/VoxelForge/VoxelForge.Build.cs) already pin `FPSemanticsMode.Precise`; [noise](Source/VoxelForge/Public/VoxelNoise.h) has table-free Perlin and a scalar/SSE4.1 path written op for op; [geometry math](Source/VoxelForge/Public/VoxelTypes.h) uses `VoxelMath::DetSinCos`. This supports the current same-build CPU contract, although full-world hashes on the actual supported CPU/compiler combinations remain a release gate. MSVC documents both the ordering guarantees and limits of `/fp:precise`; it is not a GPU-vendor parity mechanism. [MSVC floating-point semantics](https://learn.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior?view=msvc-170), [MSVC cross-CPU cautions](https://learn.microsoft.com/en-us/cpp/porting/floating-point-migration-issues?view=msvc-170).
**For the player, provisionally choose field-aware movement against the versioned lattice, with cooked Chaos bodies as a compatible background path.** This is the route that can use the new edit epoch immediately when a dig removes support; a delayed cook cannot be authoritative in that frame. Before committing to Mover, instrument the existing cook pipeline and try reserving generation capacity. If a small set of player-support tiles cooks quickly *and* a CMC edit-time field override passes the same-frame floor/velocity test, retain CMC plus a prioritized-cook/field-overlay hybrid. A fast cook by itself does not solve an underfoot edit. RMC's [manager](../RealtimeMeshComponent/Source/RealtimeMeshComponent/Private/RealtimeMeshManaged.cpp) defers collision to end of frame, its [future helper](../RealtimeMeshComponent/Source/RealtimeMeshComponent/Public/Core/RealtimeMeshFuture.h) schedules work on `GThreadPool`, and its [data path](../RealtimeMeshComponent/Source/RealtimeMeshComponent/Private/Data/RealtimeMeshData.cpp) returns to the game thread to apply BodySetup/physics state; the observed 0.4–1.4 s is **submit-to-ready, not measured Chaos compute**. UE task priority does not preempt already running generation, and RMC's pool is a distinct queue whose threads still compete for CPU. [Epic UE Tasks](https://dev.epicgames.com/documentation/unreal-engine/tasks-systems-in-unreal-engine), [Epic BodySetup readiness](https://dev.epicgames.com/documentation/unreal-engine/API/Runtime/Engine/UBodySetup/CreatePhysicsMeshesAsync).
**For rendering, measure the current RMC drain before grouping.** `MaxMeshAppliesPerFrame=4` and `MaxMeshApplyMilliseconds=2` are settings, not a hardware limit. The source comment's “~0.1 ms per 32³ apply” is an estimate, not a captured sustained upload rate; no surviving `[VoxelForgeApplyFrame]`/render-thread trace in the repo establishes one. With ~341 visible nonuniform tiles and a 0.35 s generation budget, the remaining 0.65 s requires **≥525 visible applies/s**, or **9/frame at 60 Hz** and **6/frame at 90 Hz** if results are ready together. At 4/frame the caps are 240/s and 360/s, respectively. If all 512 tiles are nonuniform, the 60 Hz requirement rises to **14/frame**. Raise the cap in an isolated saturation test and measure game-thread drain, RMC render enqueue, RHI completion and first visible frame. Group 2³ tiles into at most 64 spatial pages, retaining independently updateable sections, **only if** per-component or render-thread overhead misses that rate. The archived 128³ export emitted 7.435 MB of logical mesh output for 64 tiles, an average 116 kB/tile; extrapolating that mix to 341 visible tiles is ~39.6 MB of payload, not a measured GPU transfer or component cost. [Local attribution](Docs/archive/PERF-ATTRIBUTION.md).
**A large spell still needs a single visual transaction.** At radius 10 m the round-two probe found 714 touched 8³ bricks and up to 64 current tiles; its cached-field composition was 0.861–0.914 ms, excluding upload. Keep a GPU-resident copy of the visible quantized lattice and apply an integer edit command to the whole affected region in one render dependency, with a complete-region mesh or bounded raymarch fallback. Match CPU and GPU quantized sign, interpolation and zero ties; publish all touched tiles/LODs at one edit sequence. The GPU need only perform integer **edit composition and mesh decisions**. Replacing the entire procedural float generator with integer is conditional on a later full-GPU base-generation win. This retains cross-machine world authority on the CPU and avoids treating the round-two integer AVX2 probe as a production speedup.
### Separate the two proposed changes
| Change | Evidence and expected contribution | Decision |
|---|---|---|
| **(a) Region-local evaluation and sparse sampling** | At the supplied ~2 µs/sample, shared lattice alone cuts 21.952 M requests to 17.374 M and an ideal six-worker clock from **7.32 s to 5.79 s**: only **1.26×**. The proposed **0.35 s** worker budget requires a further **16.5× effective reduction** in cost or evaluated points after sharing: `(17.374 M × 2 µs)/(6 × 0.35 s)`. This is the required result, **not a measured gain**. The scalar synthetic probe's ~170 ns of aggregate worker CPU/sample (six workers × ~0.47 s / 16.8 M points) shows hardware headroom but uses a different, much cheaper graph. Brick certificates, candidate binning, removal of per-point routing and local compiled ops are the mechanisms to test; no number from another workload is assigned to them. | **Necessary first experiment.** Keep float while measuring it, so gains can be attributed. |
| **(b) Full integer/fixed-point procedural generator** | The round-two explicit AVX2 kernel beat its *own integer scalar reference* **4.25–5.86×**; that comparison says nothing reliable about integer versus the plugin's optimized float/SSE noise or its feature-search cost. Merely changing numeric type leaves the ~30.7 counted candidate visits/sample and cache routing intact. There is **no measured production speed credit**, so assign **zero guaranteed speedup** toward the 16.5× goal. New world version permission removes compatibility cost, but not implementation or proof risk. | **Defer full conversion.** Use deterministic quantization at the lattice boundary and integer edit/mesh decisions on GPU. Convert base generation only if an exact CPU/GPU float-versus-integer production ablation proves a faster end-to-end path. |
The ~2 µs sample is an end-to-end `GetDensityAt` cost, not a Perlin operation. The available attribution closes **where** time goes but does **not** supply additive CPU shares for each nested operation. In the archived 128³ TunnelNetwork export, 590.923/606.128 ms (**97.49%**) was shared-grid fill; the following cell scan/mesh/merge was ~15.2 ms. Dividing 590.923 ms by its 2,248,091 unique samples yields only **263 ns/sample in that different workload/window**. It therefore cannot be substituted for the owner's current ~2 µs/sample or used as an additive timing breakdown of it. The export generated these *work counters*: **11.10 M room candidates / 1.73 M evaluated**, **28.95 M tunnel candidates / 3.46 M evaluated**, and **28.95 M tunnel-core candidates / 1.88 M evaluated**. That is about **4.94 + 12.88 + 12.88 = 30.7 candidate visits per sample** across the three reported counters, with about **3.15 actual evaluations/sample**. Candidate passes may revisit the same feature, and the counters are **not additive time buckets**. Binning local features once per brick attacks this work directly. [Local attribution](Docs/archive/PERF-ATTRIBUTION.md).
| Per-sample work visible in code | What the redesign removes; what it cannot claim |
|---|---|
| `GetDensityAt` recomputes chunk coordinates and checks manager/layout/lifetime, routing, tunnel cache, tile-window policy and diff version. The measured tunnel cache had **2,248,043 hits and 48 misses**: nearly every point pays lookup/key predicates despite few builds. `GetDensityWithParams` performs further box/fingerprint/terrain-op/tile-policy guards. | A prepared immutable region program moves these decisions to region/brick admission. This can remove repeated checks and indirection; their individual nanoseconds were not measured. These are multiple **key comparisons**, not a dozen expensive cache misses. |
| Rooms, tunnel segments, passage checks and warped search boxes are queried inside field evaluation; the candidate counts above show repeated local traversal. | Spatially bin features with conservative warp/blend reach, then evaluate only the local list. A false-negative bin is a geometry bug; exact sign tests across stratum and passage boundaries are mandatory. |
| The generic composer/operator path can visit roughly twenty ops, including locally inert ones. **But the archived primary TunnelNetwork run uses the already fused evaluator**, which bypasses `EvalMC`'s per-op dispatch. | Compiling away generic op visits helps custom/composer and other nonfused recipes; it **cannot** be credited for the primary fused TunnelNetwork timing. That route still contains room/tunnel candidate work, routing and the common tail. |
| `GetDensityAt` follows the core with disturbance/structural/passage/landing-floor repairs, XY seal and diff application. The attribution lists structural-tail, density-post and passage-post *sampled nested* work, not wall shares. | A region program can pre-resolve which post passes can affect a brick and fuse their ordered math; the route/floor invariant must survive. Do not subtract the nested timings from 590.923 ms or assume the tail disappears entirely. |
| The code enters per-sample profiling scopes. The owner's latest stack sample attributes **4.9% to the profiling leaf**; the archived same-argument profiled/unprofiled control differed by only **0.63%**. | Disable profiling for acceptance timing and retain a diagnostic build. The 4.9% sample share is not a demonstrated 4.9% clean-build gain; sampling and instrumentation mode differ. |
### Ordered build plan for the orchestrator and coding agent
The steps below describe **future plugin experiments**. This research pass makes no `Source/` change and does not run Unreal. Use the same machine, fixed seeds/layouts, cold world caches, and 20 spawn/teleport trials per UE gate; report the **maximum** as well as median because 20 trials are too few for a meaningful p99. Preserve gameplay while replacing one pipeline stage at a time.
1. **Day-one falsification: stage the existing collision and upload clocks.** Add timestamps for collision request, RMC end-of-frame dequeue, `GThreadPool` worker start, geometry extraction end, `CookComplexMesh` start/end, game-thread `ApplyCollisionUpdate` start/end, physics-state ready and first successful capsule sweep. Keep existing generation, mesh and gameplay behavior while profiling. Test one ~4,000-triangle tile and the pawn's current 3³=27-tile support neighborhood, idle at the current 14 generation tasks, and with the generation cap reduced to 12. If the cook's queue wait dominates, test a dedicated priority cook dispatched directly after player-support mesh extraction; compare its core cook and install times with RMC's route. In parallel, prequeue a 341-tile render corpus and run caps 4/8/16 with the same 2 ms drain guard; record visible applies/s, game/render/RHI clocks and first-visible frame. **Pass the cook shortcut only if** core cook p95 ≤10 ms, the 27-tile player support is ready ≤150 ms after meshes exist under load, and an edit-time field override can switch floor/support within the first simulated/rendered frame with zero invented velocity in 100 repeated underfoot digs. If any edit test fails, implement field-aware Mover as the player authority; if the shortcut passes all tests, use prioritized cooks for steady terrain plus field queries while an edit's cook is pending. **Pass the ungrouped upload path only if** it sustains ≥525 visible applies/s at 60 Hz with game-thread drain ≤2 ms/frame and no render/RHI backlog. These are decision thresholds, not existing measurements.
**Step 1 results (2026-09-30, commit `41b001b`):** core cook **1.50 ms p50 / 2.28 ms p95** (passes); through RMC's collision route the same support bodies were ready **8.5–10.8 s** after their meshes existed (fails): the wait is RMC's path, not Chaos, so player-support collision must bypass it. CMC with a field override: **883 false-support frames in 100 underfoot digs** (fails): step 4 is **field-aware Mover**. Uploads: cap 16 = **639 applies/s**, drain p95 2.22 ms (misses 2 ms by 0.22 ms), no render/RHI backlog, so tune the drain and **do not group yet**. The "8× per-sample gap" was a denominator artefact: 263 ns was export *wall* time with 15 workers on a shared lattice (≈3.4 µs CPU per sample); the real per-sample CPU cost is ~2–3.4 µs in both paths, so the 12–16× reduction target stands.
2. **Exact-float region-evaluator experiment.** Use an isolated, editor-only explore-commandlet path inside the plugin because the live composer, strate manager, generator and SDF cache depend on Unreal types. Keep gameplay default-off and compare at the same lattice points and operator order. The reference is the chosen **new-world float definition**, not old-season geometry. **Pass:** zero 16-bit quantized-value disagreements and zero route/capsule regression across at least five seeds; ≤**600 ms** for the full 259³ cold ring on six workers before certificates. Record candidate visits, cache checks and worker CPU. Failure means local compilation did not remove enough work; investigate before rewriting numeric math.
**Step 2 verdict (2026-09-30, attempts 2a/2b/2c, commits `41a9265`, `fd9f809`, `43c11cc`):** all exact (zero Q16/sign disagreements over five seeds, reach-shrink control caught, same hash on 1 and 6 workers), none fast. Best (2c): setup once per 32³ chunk, **1.595 µs/sample vs 1.678 live, 7.9 s vs 7.1 s wall**. Binning cut candidate *visits* 21→16 per sample, but room/tunnel/tunnel-core *evaluations* did not fall (48 M / 5 M / 38 M for 17.4 M points): the cost is the geometry evaluation itself, not the search; noise is ~2.6%. **600 ms is out of reach while every lattice point must reproduce today's density exactly.** That exactness bar was a comparison tool, not a requirement: the owner accepts a new world version, so the next step may *redefine* the evaluator (for example coarser room/tunnel geometry with interpolation, or skipping bricks proven uniform), judged on shape tolerance, walkability and cross-machine determinism instead of equality with the old path.
**Step 2 result (2026-09-30, attempt C; pass bar not met, full compiler untested):** five-seed Q16 comparison and two-run raw-float determinism pass. The deliberate room-bin reach reduction to 0.5 is caught (18,107 Q16 differences, 735 sign differences), with no saturated samples. On the 17,373,979-point cold ring, the live `GetDensityAt` p50 is **6.776 s**, **1.679 µs worker CPU/sample**, and **29.164 worker CPU seconds**; the editor-only 8³ SDF-bin variant is **19.543 s**, **2.879 µs worker CPU/sample**, and **50.023 worker CPU seconds**. Both measured variants observed six workers. Sampled visits fall from **21.19 to 16.38/sample** (about 23%), but wall time is 2.88× slower and the 600 ms target is missed. The variant still calls `GetDensityAt` for every lattice point; its bins cover rooms, room-floor joins and whole tunnel chains, while segment loops, route/cache decisions, passages, landing posts, disturbances and player edits remain in the canonical per-sample path. This is a **candidate-bin ablation, not the planned compiled region program**; it falsifies per-sample 3D bin lookup as a sufficient optimization but does not falsify hoisting region decisions or compiling the full ordered program. Step 2 remains open until that full program is measured; do not start step 3's certificate work against this slower baseline. Full report: `Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/TestHarness/step2_region_eval_20260930_c_region-eval/region_eval.json`.
**Launch record:** attempt A's guarded UBT invocation logged `Result: Failed` (JSON writer and `ParallelFor` overload compile errors); it exited before launching a commandlet. Attempt B built successfully but `-regioneval=1` was ignored by Unreal argument parsing, so the ordinary explore commandlet exited successfully without the expected JSON. Attempt C used a bare `-regioneval`, rebuilt the editor module, and completed the intended experiment. No attempt timed out, crossed the 8 GiB private-memory limit, or produced a crash report; C peaked at 7,137,738,752 private bytes. The runner was corrected to detect UBT's logged failure even when its process exit code is zero.
3. **Add certificates, shared lattice and contouring.** Prove final-sign bounds after all writers and edits; classify 8³ bricks, evaluate uncertain ones, share global corners, and mesh them with today's marching-cubes table. Keep the old tile route as a versioned reference while the new route is validated. **Pass:** zero false-uniform bricks and zero sign mismatch at all 17,373,979 ring lattice sites for the test corpus, cold worker generation ≤**350 ms**, and max render-visible/full ring ≤**900 ms** in the later UE gate. If certification consumes more time than it saves, fall back to evaluating uncertain regions without that proof; do not repeat the archived 0.588→2.057 s interval-control mistake.
3b. **AVX2 check against the new norm (owner's request, 2026-09-29).** Once step 3's brick evaluator is the baseline, try an explicit 8-lane AVX2 version of its hot loop (the plugin's current noise path is 4-lane SSE4.1). **The owner does not require AVX2 to match the scalar path bit for bit** (clarified 2026-09-29): only the shipped product must be deterministic across players' machines. If adopted, the AVX2 path *is* the canonical world definition (FMA allowed, as long as it is always used), and every machine must run that same path: require AVX2 at startup rather than falling back to a scalar path that would produce a different world. **Keep it only if** it is faster on the step-3 corpus and the AVX2 path gives the same lattice hash on two runs and on two different AVX2 CPU models; otherwise record the number and drop it. The round-two integer AVX2 probe (4.25–5.86× over its own scalar) is the reference for what explicit vectorisation can buy.
4. **Choose and implement player collision.** Use step 1 to select prioritized Chaos+edit overlay or field-aware Mover; keep ordinary collision with nonterrain actors and keep NPC/projectile field-query backup. Version floor/contact state by edit sequence. **Pass:** at every teleport the player can perform an actual blocked capsule movement on the intended terrain ≤**900 ms** after the request, 100 underfoot digs add **0** spurious velocity impulses, and 100 excavations with no cave below have **0** frames of false support or false air. The full visible ring must also meet the separate ≤1,000 ms hard deadline. A successful cook timing without the edit tests is insufficient.
5. **Decide render batching from measured throughput.** Keep per-tile RMC if it sustains ≥525 visible applies/s and the first-visible gate ≤900 ms. Otherwise group 2³ tiles per page while retaining tile-addressable sections and the same materials/LOD transition topology; rerun the same corpus and require ≥**99 page applies/s** for at most 64 pages in 0.65 s, ≤2 ms/frame game-thread drain and no render-thread tail past the 1 s deadline. If grouping causes whole-page reupload on small edits or still misses, use a dedicated batched render proxy. Compare first-visible time against the same ≤900 ms gate.
6. **Version the edit and GPU visual protocol.** Quantize float-produced lattice density with specified rounding/saturation; use fixed integer edit composition, stable per-cell triangle counts/prefix sums, global edge ownership and one edit sequence across touched pages/LODs. Keep CPU procedural generation, composer, route graph and ordered edit replication as the authority. Test AMD and NVIDIA output hashes against CPU reference; on a single machine, two full cold runs must yield the **same field/sign/mesh hash**. **Pass:** a radius-10 m crater (up to 714 bricks/64 tiles) appears throughout its region in the **first submitted render frame containing the edit**, no gap/ghost/ripple at borders or LOD seams, CPU/GPU sign disagreement **0**, and added GPU cost ≤**2 ms** within an 11.1 ms 90 Hz VR frame in 20 varied positions. If direct GPU remeshing exceeds that, test the complete-region raymarch overlay with the same visual and frame gates; do not publish a partial mesh.
7. **Long-range and multiplayer gate.** Provide LOD1 first at 16 cells per tile, refine later, reuse coincident lattice points and keep full 3D geometry for caves/overhangs/islands. Replay ordered edit transactions, including crater commands, on two supported CPU models and compare world-versioned hashes. **Pass:** first LOD1 preview ≤**3 s** after cold teleport, an 8 m/s five-minute run with **0** missing player support or visible near-ring gaps, equal hashes for two runs and both CPU models, and a successful late join after replay/checkpoint. If float parity fails on a supported CPU/compiler pair, lock an exact scalar FP path or then promote a full integer procedural ABI.
**Still unproved:** production float region-program throughput, brick-proof efficiency on the full composer, the actual RMC sustained apply rate, the Chaos queue-versus-compute split, whether CMC can support an edit-time field override, field-aware Mover query cost, and same-frame GPU/VR crater rendering. Each has an explicit gate above. The standalone integer result proves only that its synthetic 3D kernel can run quickly and match its own scalar reference on this Ryzen. Neither a source paper's scene timing nor a comment's estimated per-tile apply time closes these gaps.
## Earlier evidence and alternatives
**The consolidated choice above supersedes the round-two integer-first and unconditional grouping suggestions.** The useful core remains a spatially pruned 8³-brick evaluator, shared lattice, conservative certificates and transaction-wide crater visual. Keep the 25 cm lattice, volumetric operators, walkable route graph and seed/layout/edit replication. Recent work on pruning SDF construction trees directly supports region-local programs, but its speedups on other scenes are not VoxelForge measurements. [Lipschitz Pruning, Eurographics 2025](https://wbrbr.org/publications/LipschitzPruning/documents/LipschitzPruning_submitted_to_EG25.pdf), [Accelerating Signed Distance Functions, 2025](https://perso.liris.cnrs.fr/eric.galin/Articles/2025-lod.pdf), [OpenVDB paper](https://museth.org/Ken/Publications_files/Museth_TOG13.pdf).
**The one-second target is plausible but not proved.** The round-two six-worker AVX2 probe processed a *synthetic* 256³ composition in **80.3–107.5 ms** across three three-trial runs with exact scalar parity; 17.374 M unique ring points at that measured throughput extrapolate to **83.2–111.3 ms**, leaving roughly 0.89 s for classification, mesh extraction, upload and visibility. This is an arithmetic extrapolation across different data and cache patterns, not a measured UE ring. The scalar probe took **430.3–496.2 ms**, a **4.25–5.86×** paired speedup from explicit AVX2. The recommendation is conditional on a production-stack parity/timing experiment and a UE upload/VR edit spike. The arithmetic is decisive about what must change:
| Quantity | Arithmetic or measurement | Consequence |
|---|---:|---|
| Current tile requests | 512 × 35³ = **21,952,000** samples | At the supplied ~2 µs/sample this is 43.9 CPU-seconds before collision/upload. |
| Shared lattice for 64 m cube plus halo | 259³ = **17,373,979** unique points | Sharing alone removes 20.9% of requests, nowhere near enough. |
| Required density rate if generation gets 0.5 s on six cores | 17.374 M / 0.5 / 6 = **5.79 M samples/core/s**, or **173 ns/sample** | Current ~2 µs/sample needs about **11.6×** improvement even after sharing if every point is evaluated. This ignores meshing and upload. |
| Current render apply cap | ~341 nonuniform tiles if one third are uniform; 4 applies/frame = 85.3 frames, **1.42 s at 60 Hz** if all were queued together | This is the **default configured cap**, not measured sustained throughput. Raise it and measure game/render/RHI time first; group only if the actual rate misses the deadline. |
| Current collision | Owner's 0.4–1.4 s submit-to-ready **per tile** | This interval includes end-of-frame deferral, pool queueing, extraction, actual Chaos cook and game-thread installation; **the cook's own CPU duration is unknown**. Reserve capacity and instrument the stages before choosing field-only versus prioritized-cook hybrid. [Epic BodySetup API](https://dev.epicgames.com/documentation/unreal-engine/API/Runtime/Engine/UBodySetup/CreatePhysicsMeshesAsync), [RMC source](https://github.com/TriAxis-Games/RealtimeMeshComponent). |
| Running at 8 m/s ([local design brief](Docs/archive/DESIGN-SOL-2026-09-15.md)) | One 8 m tile crossing/s brings one 8×8 face = **64 new tiles/s**; 64 × 35³ = **2.744 M requests/s** | At current ~2 µs/request, that is ~5.49 CPU-seconds/s, or ~0.915 s on six ideal workers before upload/collision. A shared 32-cell slab is 259² × 32 = 2.147 M new points, still ~0.716 s on six ideal workers. Field collision on demand is also the safety net for unexpected movement or stalls. |
**Measured standalone feasibility probe on this machine:** A synthetic 256³ 3D field with 48 integer sphere features and three octaves of integer lattice noise took **471–481 ms** for 16,777,216 samples using six threads. Conservatively classifying 32,768 8³ bricks took **15.46 ms**; evaluating only the 7,563 uncertain bricks then took **106–130 ms**, with **zero sign mismatches** against the full evaluation over all 16,777,216 points. An eight-thread repeat took 349–367 ms full or 86–110 ms classified plus 15.59 ms classification. This is a *kernel probe*, not VoxelForge or a measured 512-tile load. Its primitive list and noise are far simpler than the current composer stacks. It shows that the required rate is physically credible on this CPU when work is converted to bounded integer kernels; it does **not** establish that the full game, render upload, or Chaos field integration finishes in one second. Benchmark details and limitations are below.
No evidence here proves that UE can present a cold 64 m cube in under 1 s or a radius-10 m crater in the same VR frame. The ordered gates above decide those questions. If they fail, change the render/collision implementation or explicitly revise the target; do not silently infer success from a synthetic density benchmark.
## Local evidence and what it means
The code currently requests 35³ values for each 32-cell marching-cubes tile, and `GetDensityAt` builds/evaluates the stack in `VoxelGenerator.cpp`; `VoxelMarchingCubesMesher.cpp` spends most worker time filling the grid. The existing attribution measured **590.923 ms of 606.128 ms** (97.49%) for a shared-grid fill in a 128³ TunnelNetwork export, with only ~15.2 ms for later meshing/merge. It also measured an interval-classifier route that grew from **0.588 s to 2.057 s** instead of helping. Thus “add generic interval recursion” is already a failed local control; the new plan must compile *cheap, conservative bounds and feature lists per brick*, and fall back to evaluation if a bound is uncertain. [Local attribution](Docs/archive/PERF-ATTRIBUTION.md), [failed early-out control](Docs/archive/CODEX-TASK-2-PERF-REPORT.md).
The owner's **0.4–1.4 s** collision figure is submit-to-ready in the current observation window. The archived 12 September headless run reported a **0.052945 s p95 cook stage** in its different workload and instrumentation window. Neither number is a per-triangle CPU cost, and they should not be pooled or multiplied into a 512-tile total. The planned Unreal spike must capture the new ring's cold maximum, not reuse either statistic. [Archived local timing](Docs/archive/PERF-ATTRIBUTION.md).
The runtime already prioritizes edited and collision-critical work, but it applies at most four visible meshes per frame by default (`VoxelSettings.h:65`) and `ApplyModification` synchronously remeshes only the center tile while neighbors are asynchronous (`VoxelWorld.cpp:10369`). Underfoot carving can disable the old support component until recook (`VoxelWorld.cpp:10290`), directly explaining a transient disappearance. RMC can preserve old cooked collision while a replacement cooks (`VoxelWorld.cpp:9858`), which explains ghost support if the removed surface is still in that body. The default pawn collision box is radius one tile, **3³ = 27 tiles**; the requested 512-tile collidable ring is a much stronger target than this implementation currently attempts (`VoxelSettings.h:88`, `VoxelWorld.cpp:8168`). These are code observations, not new timing measurements.
### Standalone measurement, reproducible
Code: [`Saved/Research/brick_kernel.cpp`](Saved/Research/brick_kernel.cpp). Built only this standalone program with installed MSVC; Unreal was not built or launched. From `Saved\Research` in PowerShell:
```powershell
cmd /c '"E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul && cl /nologo /O2 /std:c++20 /EHsc /arch:AVX2 brick_kernel.cpp /Fe:brick_kernel.exe'
./brick_kernel.exe
cmd /c '"E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul && cl /nologo /O2 /std:c++20 /EHsc /arch:AVX2 /DVF_WORKERS=6 brick_kernel.cpp /Fe:brick_kernel_6.exe'
./brick_kernel_6.exe
```
The `vcvars64.bat` initialization printed a harmless missing-`vswhere.exe` notice; `cl` compiled and both programs exited successfully. Values are wall milliseconds from `std::chrono::steady_clock`, three repeats, Windows on the stated machine:
| Stage | Eight workers (ms) | Six workers (ms) | Work |
|---|---|---|---|
| Full evaluation | 362.538, 367.161, 349.001 | 479.017, 470.581, 481.011 | 16,777,216 grid points, each checks 48 spheres and computes 3 octave integer value noise. |
| Brick classification | 15.590 | 15.464 | 32,768 bricks: 24,778 solid, 427 air, 7,563 uncertain. Conservative sphere/AABB extrema plus a proven ±224 noise envelope. |
| Classified evaluation | 109.641, 86.959, 86.439 | 106.184, 129.769, 108.978 | Full evaluation only for uncertain bricks; classified bricks retain their correct sign. |
| Verification | 0 sign mismatches / 16,777,216 | 0 sign mismatches / 16,777,216 | The sign of every classified point was checked against the full run. Density *magnitudes* in certified uniform bricks were intentionally not preserved. |
The probe lacks passages, smooth Boolean operators, strata boundaries, edits, meshing, CPU cache competition with UE, upload and collision. Its 76.9% uniform-brick rate is **synthetic** and must never be substituted for the game's observed one-third uniform *tile* rate. The timing is evidence for the kernel's potential only. Full production sign/mesh/collision parity across seeds, strates, edits and CPU models is a release gate. The probe uses scalar integer code compiled with `/arch:AVX2`; that switch alone does not prove SIMD vectorization. AVX2 is a later optimization target, not a factor claimed in these numbers.
## Three architectures considered
| Architecture | Representation and pipeline | Main advantage | Main cost/risk | Verdict |
|---|---|---|---|---|
| **A. Sparse compiled float bricks + CPU contouring + field-aware player edits** | Seed/layout compile to a stable feature plan. Bin primitives to touched bricks. Each 8³ brick stores a final-sign certificate, quantized density cache and edit version. Mesh uncertain bricks with marching cubes; measure per-tile RMC upload before grouping. Use field queries for immediate player edits; test prioritized cook for steady collision. GPU edit transactions cover crater-scale changes. | Eliminates repeated feature traversal, supports edits and 3D topology, and avoids waiting on all ring cooks for player readiness. | Conservative certificates, movement integration and same-frame crater rendering still need UE/VR proof. | **Recommended staged experiment.** |
| **B. Analytic CSG/BVH + raymarch render and collision** | Keep a spatially indexed graph of implicit primitives and bounded noise, no resident sampled terrain except caches. GPU raymarches image-space rays through an acceleration structure; CPU queries analytic field for physics. Edits add CSG nodes. | Very long view and same-frame edits can avoid remeshing entirely. | Full-screen/stereo ray cost, non-SDF operators break safe sphere tracing, shadows/materials/UE integration; many edits enlarge BVH and query cost. It changes visual appearance. | Radical alternative if meshing/upload still dominate after A. |
| **C. Integer GPU brick generation + compute meshing** | CPU generates canonical integer plan/brick list; compute shader evaluates fixed-point density and emits deterministic mesh indices by prefix-sum, while CPU uses the same integer evaluator for authoritative player collision. | Could offload 17–22 M sample and contour work without collision readback; GPU Gems 3 demonstrates fully 3D GPU generation rather than a heightfield. | Cross-vendor integer ABI, duplicate CPU/GPU evaluators, VR frame contention, runtime mesh integration. | Research branch only until exact AMD/NVIDIA and CPU/GPU hash parity is demonstrated. |
Architecture A should treat the **plan as the source of truth** and bricks as disposable caches. Compile op nodes once per region into a fixed order and collect local feature references by feature bounding box, with an explicit bound for noise, warps and edits. A brick certificate must enclose the **final** composed field: an operator that can add solid after a carve invalidates a carve-only proof. If any op cannot supply a safe bound, mark the brick uncertain. The current interval route's measured slowdown is a warning that a proof must cost less than the points it avoids. A local feature raster pass reverses the present “each point searches nearby features” loop and keeps shape data hot in cache. The sparse hierarchy stores occupied, empty and uncertain bricks; a compact, flat Morton or Z-major page table is likely better for the active 64 m cube than a pointer-heavy tree. OpenVDB provides a precedent for sparse, editable narrow-band fields, while sparse voxel octrees/DAGs are more compelling for static repeated geometry than for heavily edited procedural bricks. [OpenVDB](https://museth.org/Ken/Publications_files/Museth_TOG13.pdf), [Laine and Karras SVO report](https://research.nvidia.com/sites/default/files/pubs/2010-02_Efficient-Sparse-Voxel/laine2010tr1_paper.pdf), [Kämpe et al. sparse voxel DAG](https://www.cse.chalmers.se/~uffe/HighResolutionSparseVoxelDAGs.pdf).
Retain marching cubes initially: in the local measured export, evaluation dominates, so changing the mesher first has little expected payoff. A later surface-nets or dual-contouring trial may reduce vertex/triangle output or preserve sharp authored features, but QEF placement, topology and transitions add complexity; benchmark the *whole upload path* before changing. Dual contouring's paper addresses Hermite samples and adaptive octrees; Transvoxel has explicit transition cells for adjacent marching-cubes LODs. The current skirts can visually hide cracks but are not a geometric continuity proof. [Dual contouring paper](https://people.eecs.berkeley.edu/~jrs/meshpapers/JuLosassoSchaeferWarren.pdf), [Transvoxel author](https://transvoxel.org/), [surface nets paper](https://www.sciweavers.org/publications/constrained-elastic-surface-nets-generating-smooth-surfaces-binary-segmented-data).
For A's collision, store an immutable field snapshot and expose a broad-phase AABB per resident spatial page. Its exact local ray/shape queries must use the canonical quantized lattice or a triangle patch made from it. The provisional player route is a purpose-built field-aware capsule sweep whose support state belongs to the current edit epoch; the staged cook and CMC override experiment in the consolidated plan may justify retaining prioritized cooked collision for steady terrain. For arbitrary Chaos rigid bodies, test a custom `FImplicitObject`/geometry-query adapter only if field-query fallback is insufficient; UE 5.7's installed `Chaos/ImplicitObject.h` exposes virtual `PhiWithNormal` and `Raycast`, but that does **not** prove the complete broad-phase, GJK/contact, CharacterMovement, navmesh or replication integration. A conservative collision box cannot stand in for a cave: it would block playable air. A signed density is not necessarily a distance bound, so ordinary sphere tracing may step through a thin wall; use interval subdivision/voxel traversal or a certified Lipschitz bound. [Chaos API](https://dev.epicgames.com/documentation/unreal-engine/API/Runtime/Chaos), [Hart sphere tracing](https://experts.illinois.edu/en/publications/sphere-tracing-a-geometric-method-for-the-antialiased-ray-tracing/).
## Determinism contract
**A:** Preserve the current precise-float CPU operator math and deterministic feature order as the first implementation. Pin build flags and CPU feature path, retain the scalar/SSE parity checks, and run full ring/edited-world hashes on every supported CPU/compiler pair. Define a versioned *lattice* ABI: integer world coordinates, float-to-int16 rounding/clamping, sign at zero, fixed interpolation/ties, ordered integer edit composition and material choice. Brick jobs may finish in any order because each writes a disjoint key and shared corners have one owner. The GPU consumes the quantized cache; it need not reproduce float Perlin for edit visuals. A full fixed-point procedural generator and AVX2 path remain conditional experiments. FastNoise2's own FAQ warns that automatically selected floating SIMD levels can differ across machines, so do not import that mode as world authority without locked semantics and hash tests. [FastNoise2 FAQ](https://github.com/Auburn/FastNoise2/wiki/FAQ), [MSVC floating-point rules](https://learn.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior?view=msvc-170).
**B:** Analytic shape transforms and BVH construction need a stable sort and the same versioned CPU float/lattice contract. A GPU raymarch can remain a visual cache if it consumes canonical quantized samples or if its independently evaluated field passes CPU/GPU parity; arbitrary GPU float `sin`, `sqrt` and FMA cannot be assumed to match. `/fp:precise` helps source ordering but is not a cross-build magic guarantee, and CRT transcendental implementations can vary. The existing `DetSinCos` avoids one such dependency. [MSVC `/fp`](https://learn.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior?view=msvc-170), [CRT FMA3 selection](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-fma3-enable-set-fma3-enable?view=msvc-170).
**C:** Floating-point GPU density is ruled out for world decisions: Vulkan itself is not pixel exact, and available shader float controls have capability/operation limits. An integer GPU path is *potentially* deterministic only after restricting operations to defined 32-bit integer add/XOR/shift/multiply, explicitly emulating wider arithmetic if needed, forbidding unordered atomics for output order, using deterministic prefix sums, and hashing complete density/sign/mesh buffers on AMD, NVIDIA and CPU after every shader/compiler/driver class change. Until those parity tests pass, never make the GPU result authoritative for collision or replicated world state. [Vulkan invariance](https://docs.vulkan.org/spec/latest/appendices/invariance.html), [float controls proposal](https://github.com/KhronosGroup/Vulkan-Docs/blob/main/proposals/VK_KHR_shader_float_controls2.adoc). GPU Gems 3 demonstrates speed opportunities for 3D terrain, but its floating-point GPU pipeline is no determinism proof. [GPU Gems 3](https://developer.nvidia.com/gpugems/gpugems3/part-i-geometry/chapter-1-generating-complex-procedural-terrains-using-gpu).
## Preserving the game
**3D and composition.** The plan retains volumetric SDF/CSG regions, mountains, caves, overhangs and islands. A surface heightfield clipmap alone is disallowed: the original geometry-clipmap paper explicitly targets heightfields. Each current archetype, composer tag, structural post, passage and diff becomes a compiled op with a finite or explicitly unbounded influence declaration. [Geometry clipmaps paper](https://hhoppe.com/geomclipmap.pdf). The shipped examples support broad direction, not a claimed matching performance number: No Man's Sky describes its voxel-to-polygon pipeline and reports a later dual-marching-cubes rewrite improved loading; Astroneer describes re-polygonizing modified chunks; neither publishes a directly comparable one-second/64 m benchmark. Teardown's movable voxel volumes and packed occupancy hierarchy solve a different destructible-object problem, and Enshrouded's developer account confirms editable voxel terrain without exposing a transferable throughput figure. Dual Universe demonstrates a multiplayer editable voxel world, but its published material likewise gives no comparable generation latency. [NMS GDC talk](https://www.gdcvault.com/play/1024265/), [NMS update](https://www.nomanssky.com/worlds-part-I-update/), [Astroneer developer account](https://www.gamedeveloper.com/design/what-i-astroneer-i-s-devs-learned-while-leaving-early-access), [Teardown developer's voxel rendering account](https://blog.voxagon.se/2018/10/17/from-screen-space-to-voxel-space.html), [Enshrouded developer AMA](https://enshrouded.com/pl-PL/news/enshrouded-world-design-team-reddit-ama-recap), [Dual Universe developer diary](https://www.youtube.com/watch?v=MWcuwt9bjHo).
**Walk guarantee.** Keep the composer-generated entrance/exit route as an explicit graph of swept, capsule-clear walking corridors and landing floors. Apply that route as a late, protected operator whose minimum clearance and maximum slope have margins below the player capsule's mechanical limit; validate the final sampled collision representation with capsule sweeps. Decorative ops cannot narrow the protected clearance. For edits, protect a minimal support/clearance tube along one certified route; clip a crater's destructive part where it would remove the last walkable support or add an automatically generated bypass as part of the same transaction. A crater may therefore leave a thin protected bridge. This is a concrete rule that preserves the guarantee; the design of its visible cue is a gameplay/content task. The current route tests do not establish post-edit traversal, so verify it after each affected edit graph change.
**Clean digging.** Treat one edit as a transaction: allocate a monotonically numbered edit ID, compute every affected brick and one-cell halo from a single immutable pre-edit snapshot, and publish one canonical field/edit epoch before player movement in that frame. Border vertices use global lattice coordinates and a single owner, with identical edit IDs on both sides. The player queries the new quantized field immediately; a cooked body may serve steady terrain only while its edit epoch matches, and an obsolete cook cannot restore stale support. Character velocity is carried across an edit from actual displacement/input only; recompute grounded state without an invented downward impulse. A 1 m-radius interactive brush touches at most eight 2 m bricks at a grid corner before halo expansion. The render path switches the complete affected region in one frame using the GPU transaction described below, never a sequence of independent RMC tile updates. **This does not yet prove same-frame edits**: the UE/VR test must measure the whole transaction including GPU completion, stencil/depth, shadowing and stereo. Network edits carry server order and ID; prediction displays the same transaction locally and reconciles on authoritative order.
**Streaming and long view.** Keep a 3D clipmap but make LOD1 available first at 16 cells per tile: 19³/35³ = **0.160** of today's per-tile sample requests, an arithmetic count, not a measured 6.25× wall speedup. Applying that ratio to the supplied 16.5 M LOD1 requests yields **2.64 M requests**; at the old ~2 µs/request this is **5.28 CPU-seconds**, or **0.88 s on six ideally scaled cores**, before upload. This arithmetic makes “a few seconds” credible for the preview even without the new brick speed, but it is no end-to-end guarantee. Refine to 32 cells later. Reuse lattice coordinates when coarse samples coincide with resident fine samples; outside the fine cube, evaluate coarse bricks independently. A far shell should be surface-aware and perhaps use a much lower-resolution 3D contour plus impostor for sealed distant strata; preserve visible caves/islands rather than flattening to terrain height. Use Transvoxel or a tested equivalent when meshes at unequal LOD meet. Prioritize spawn/teleport center, movement direction, falling support and edits; apply back-pressure to far work. UE Tasks priorities affect scheduling but do not preempt a running expensive tile; divide work into cancellable bricks. [UE Tasks documentation](https://dev.epicgames.com/documentation/unreal-engine/tasks-systems-in-unreal-engine), [Transvoxel](https://transvoxel.org/).
**Multiplayer.** Replicate seed, numeric ABI/content hash, compact plan/layout ID and ordered edit log. Do not replicate bricks, meshes or cooked physics. Hash canonical brick signs, surface vertices and collision answers on clients; reject incompatible builds rather than silently diverging. An edit arrives as one transaction with all affected brick IDs, never independent tile messages. For late joins, bound and checkpoint the edit log: Teardown's developer describes why replaying a deterministic command stream saves bandwidth but can become too slow after many edits. This is a qualitative design warning, not a VoxelForge timing estimate. [Teardown multiplayer technical account](https://blog.voxagon.se/2026/03/13/teardown-multiplayer.html).
## Unreal rendering and Nanite
Keep RMC for the first experiment and measure its per-tile sustained apply rate with a raised frame cap. Group spatial pages and separate render pages from collision only if the measured game/render/RHI path misses the consolidated throughput gate. Replacing RMC with ProceduralMeshComponent only changes the wrapper around the same asynchronous cook; Epic documents `bUseAsyncCooking`, not a latency guarantee. `UDynamicMeshComponent` has useful edit representation but Epic lists no Nanite/LOD support and warns about render/memory efficiency. RMC's current upstream documentation claims runtime Nanite, and the locally installed RMC 5.4 source contains a runtime Nanite module and provider checks. **Revisit Nanite as an isolated *far/mostly static render* benchmark**, including PC VR stereo, page-build time, edit rebuild time and fallback mesh cost. It does not solve density evaluation or Chaos collision, and the first one-second ring should not wait for Nanite conversion. Nanite collision uses a fallback mesh, not Nanite triangles. [PMC API](https://dev.epicgames.com/documentation/unreal-engine/API/Plugins/ProceduralMeshComponent/UProceduralMeshComponent), [Epic dynamic-mesh guide](https://dev.epicgames.com/documentation/unreal-engine/geometry-scripting-users-guide-in-unreal-engine), [RMC docs](https://github.com/TriAxis-Games/RealtimeMeshComponent/blob/master/Docs/index.md), [Epic Nanite fallback details](https://dev.epicgames.com/documentation/unreal-engine/nanite-technical-details).
## Round two: wider search and revised engineering test
### What the new local probe measured
[`Saved/Research/round2_probe.cpp`](Saved/Research/round2_probe.cpp) is an independent C++20 program, not copied production code. It models the *kinds* found in `VoxelNoise.h`, `VoxelDensityOpStack.cpp`, and `VoxelGenerator.cpp`: hashed 3D gradient Perlin with a fixed-point quintic fade, three-octave fBm, ridged noise, one domain warp, upper/lower stratum bounds, a passage-like corridor, a room, and two smooth maxima. The gradient choice and fade shape follow the plugin, but the hash, coordinate scale, parameters, and composed graph differ. The code has a scalar reference and explicit eight-lane AVX2 integer evaluator; each lane writes exactly the same 16-bit value in the measured domain. The present plugin already has **SSE4.1 x4 Perlin batching across octaves**; the new speedup is therefore **against this program's scalar reference**, not against the production plugin's SSE path.
Build/run from `Saved\Research` on the owner's Ryzen/Windows machine:
```powershell
cmd /c '"E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul && cl /nologo /O2 /std:c++20 /EHsc /arch:AVX2 round2_probe.cpp /Fe:round2_probe.exe'
.\round2_probe.exe
```
MSVC compiled successfully; the installed setup script emitted a missing-`vswhere.exe` notice. Wall times below include six worker threads and output writes, exclude process startup and verification. Three three-trial runs with the same evaluation code (the second after dead-code cleanup, the third after adding the direct-query measurement) gave:
| Work | Measured wall time | Meaning |
|---|---:|---|
| 256³ complete scalar field | 492.0, 496.2, 469.6; 430.3, 456.2, 447.1; 478.6, 471.0, 444.3 ms | 16,777,216 output points. |
| Same field, explicit AVX2 | 101.3, 106.0, 92.5; 89.2, 107.5, 94.6; 85.8, 80.3, 82.7 ms | Paired speedups **4.25–5.86×**, zero value mismatches. |
| Full value comparison | 0 mismatches in all 16,777,216 points on each trial | One machine and coordinate domain, not cross-vendor proof. |
| 200,000 spatially scattered direct field queries | 63.030, 62.785, 62.799 ms | **313.9–315.1 ns/query**, one scalar thread; all checksums equal. A synthetic player-query latency proxy, not a capsule sweep. |
| Radius-10 m spherical edit against cached field | 0.865; 0.914; 0.861 ms | Six-worker max-composition over the 81³ bounding cube; excludes rendering and copy. |
| Copy entire 256³ 16-bit field | 1.623; 1.422; 1.394 ms | An intentionally wasteful 32 MiB copy; local brick copy-on-write would copy much less. |
| Touched 8³ bricks / changed lattice sites | 714 / 94,843 | Exact for the synthetic sphere at a brick/tile boundary; geometry and edit content affect changed count. |
**Inference, not measured production latency:** 17,373,979 ring points divided by the observed AVX2 throughput give **83.2–111.3 ms** for this operator mix. Even the slowest scalar trial extrapolates to ~514 ms; a 0.5 s worker budget is close without AVX2 and leaves no room for a more expensive real composer. The scattered-query result implies ~**635 queries in 0.2 ms** at this synthetic rate, versus **100** at the supplied current ~2 µs/sample. A real player capsule sweep must be bounded and separately timed; this arithmetic does not establish a 0.2 ms sweep. SIMD is one possible factor in the required ~12× reduction, not the entire answer. Probe limitations: the plugin uses float Perlin today, its ridged noise has feedback/squaring absent from this simplified ridge, several operator classes and dynamic world-composer decisions are absent, the program computes all points without brick classification, the six worker threads contend with no UE tasks, and no mesher/upload/collision/VR frame is timed. A full fixed-point replacement would visibly alter worlds, which the owner now permits through a new world version, but its production speed benefit has not been established. Avoid claiming the synthetic 5× is a drop-in speedup. [FastNoise2's fused SIMD graph and published 261.1 M 3D Perlin points/s on a different Intel CPU](https://github.com/Auburn/FastNoise2), [FastNoise2 exact-reproducibility FAQ](https://github.com/Auburn/FastNoise2/wiki/FAQ).
### New evidence that changes the architecture
| Source-measured or documented result | Transfer to VoxelForge |
|---|---|
| Eurographics 2025 **Lipschitz Pruning** reports 0.91–1.61 ms for 256³ discretization on a laptop RTX 4060 for scenes with 53–6,023 nodes, plus 0.72–14.29 ms pruning; its most complex model gained 629× in sphere-tracing against naïve evaluation. | Strong evidence for **region-specific compiled operator programs**, including smooth CSG, not a VoxelForge GPU speed prediction. The method assumes primitives/operators with computable 1-Lipschitz bounds; arbitrary warped scalar density needs derived bounds or a fallback. Its fluid scene with 27,977 close particles needs 125.5 ms pruning and 6.99 ms discretization, demonstrating layout sensitivity. [Paper and Table 1](https://wbrbr.org/publications/LipschitzPruning/documents/LipschitzPruning_submitted_to_EG25.pdf). |
| **Accelerating Signed Distance Functions** reports up to three orders of magnitude on its construction-tree scenes, using proxy/LOD nodes. It expressly warns that a discontinuous proxy below a smooth union causes artifacts, and that manual LOD authoring and extra nodes cost memory. | Compile local plans and conservative proxies **with operator-aware legality**; no generic “replace any far branch with a constant.” Preserving the exact zero set near transitions matters for walking and digging. [Paper](https://perso.liris.cnrs.fr/eric.galin/Articles/2025-lod.pdf). |
| Keeter's SIGGRAPH 2020 interval method simplifies complex closed-form expressions by region; Knoll et al. compared interval and affine arithmetic for implicit ray tracing. | Both are real alternatives for exotic operators, but the **local** blanket interval attempt worsened 0.588 s to 2.057 s. Use inexpensive analytic primitive influence and final-sign certificates first; profile any tighter affine bound by avoided evaluations per microsecond. [Keeter](https://www.mattkeeter.com/research/mpr/), [Knoll et al.](https://www.vacet.org/publications/Knoll_CGF-implicits-2009.pdf), [local control](Docs/archive/PERF-ATTRIBUTION.md). |
| Eurographics 2025 **Fast Sphere Tracing of Procedural Volumetric Noise** reports a detailed 3D procedural-noise scene at **16 ms for 1024² pixels** on an Nvidia 4080 Ti, versus 110 ms naïve; the authors report up to 12× on opaque/constant-transparency scenes using derivative-aware stepping and nested fBm bounds. | Strongest recent case for the analytic-render alternative. Linear scaling from 1.05 M to 4.15 M stereo pixels would be ~**63 ms** on that *different GPU/scene*, before VR lighting; this is a cautionary inference, not a measured Quest 3 figure. Reuse its nested fBm bound idea for local-region culling even if raster meshes remain primary. [Paper](https://diglib.eg.org/bitstream/handle/10.1111/cgf70072/cgf70072.pdf). |
| JCGT 2019 reports **15–20%** faster 3D cellular noise traversal than standard Worley on its benchmark (for 16 M queries, one table row falls from 13.69 s to 11.15 s). | Relevant only to the plugin's cellular roughness branch, not its dominant gradient-Perlin calls; do not multiply this into a ring-wide speedup. [JCGT paper](https://jcgt.org/published/0008/01/02/). |
| Voxel Plugin 2's own change log claims up to **2×** world generation from skipping cells far from surface, plus up to **40%** VRAM reduction in an experimental Nanite path. Its docs separate collision and render invokers. | Confirms that surface sparsity and collision/render separation are practical in UE, but vendor numbers are not a transferable end-to-end 12×. [Voxel Plugin 2 change log](https://docs.voxelplugin.com/resources/change-log/2.0p8-changelog), [collision design](https://docs.voxelplugin.com/2.0p8%20-%20v2/design-docs/collision-mesh-navmesh). |
| FastNoise2 reports **261.1 M** AVX2 3D Perlin points/s on an Intel 7820X; its FAQ says floating SIMD levels can differ and advises strict FP plus a locked SIMD level for exact output. | Fuse whole op graph in registers; do not import float FastNoise2 with automatic SIMD dispatch as the canonical multiplayer field. Our fixed integer benchmark shows a path to parity on one CPU; cross-machine parity still needs testing. [Library and benchmark](https://github.com/Auburn/FastNoise2), [FAQ](https://github.com/Auburn/FastNoise2/wiki/FAQ). |
| Epic's UE 5.7 Mover comparison says movement can use a root scene component without a collision primitive and custom movement modes; local UE 5.7 `CharacterMovementComponent.cpp` still makes floor and move sweeps through the engine collision scene. | Mover is the provisional field-aware movement route, preserving sweeps against nonterrain actors. A prioritized cook plus an edit-time CMC field override may be less invasive if the staged-cook and underfoot edit gates both pass. Simply leaving the old CMC capsule on an uncooked mesh does not fulfill “collidable.” Mover remains a migration risk under network prediction. [Epic Mover comparison](https://dev.epicgames.com/documentation/unreal-engine/comparing-mover-and-character-movement-component-in-unreal-engine). |
| Epic's RDG documentation supports compute passes, dependency barriers and async compute but notes platform fallback to the graphics pipe. | A GPU edit mesher can be integrated with a render-graph transaction, but “async” does **not** establish same-frame latency or free VR time. [Epic RDG guide](https://dev.epicgames.com/documentation/unreal-engine/render-dependency-graph-in-unreal-engine). |
### Crater spell: one transaction, up to 64 tiles
A radius-10 m sphere is **40 lattice intervals** at 25 cm. Its 20 m AABB reaches up to **4 tiles per axis** when centered on a tile boundary, hence **64 full-detail 8 m tiles**; at a tile center it reaches at most 27. The sphere's continuous volume is `4π40³/3 = 268,083` voxel cubes versus `81³ = 531,441` candidate lattice sites. The local exact sphere/AABB enumeration found **714 2 m bricks** touching the ball. Independently contouring each 8-cell brick needs `714×9³ = 520,506` corner-sample reads before sharing; the real distinct set is smaller, and normal or LOD halos can add reads. A naïve re-evaluation at today's 2 µs/sample would cost about **1.04 CPU-seconds** for those corners, so **cache the base field and compose the edit over it**. The measured synthetic cached-field edit took **0.865–0.914 ms**, but no extraction or upload was measured. CPU copy-on-write at touched bricks has a raw 16-bit interior payload of `714×8³×2 = 731,136 bytes`; a full near-field 32 MiB copy was measured at 1.422–1.623 ms. The crater itself is ~one command (center/radius/shape/seed/version), not 95,000 replicated voxels. A pathological marching-cubes upper bound is `714×512×5 = 1.83 million` triangles; at three unshared 16-byte vertices each that is **87.7 MB** of transient output. Realistic counts must be measured, and GPU buffer overflow must fall back to a complete-region visual path, never publish half a crater.
**Proposed fast path.** Commit an edit in a deterministic order with a monotonically increasing edit sequence. On the CPU, publish a new immutable edit-plan pointer for *all* 714 bricks before movement and render gather for that frame. A canonical signed integer field query composes the crater immediately, so player floor/capsule sweeps cannot see stale support; nonplayer traces use the same fallback until their optional Chaos mesh is ready. Never transfer a removed collision body's velocity or use old cooked floor data after the edit sequence changes. If the dig removes the floor, real gravity begins from the player's pre-edit velocity exactly once; if it adds terrain around the capsule, resolve penetration with a bounded positional correction that does not enter velocity. The entrance-to-exit route is a **protected graph corridor**: preflight the crater against a protected walkable tube and either clip its solid-adding part, carve a replacement bypass, or reject the spell by explicit gameplay rule. For pure excavation, connectivity cannot be reduced, though walking surfaces can be removed, so the route proof must include footholds/clearance, not only air connectivity.
For visuals, use one GPU edit transaction over the whole affected region. Keep the near lattice field resident on the GPU; upload the compact command, compute the changed bricks and their transition halo in a **single render-graph dependency**, generate canonical marching-cubes triangles with a stable per-cell prefix sum, and swap the entire region's draw descriptors together at a frame boundary. Suppress the old region only when its replacement is drawable; no per-tile apply loop. All visible coarser LOD footprints touched by the spell take the same edit sequence, using the corresponding coarse lattice and transition mesh; an unloaded distant region records the command for generation when it enters view. A local volume-raymarch overlay can cover the transaction while mesh extraction is late, using the same canonical cached field and edit command, with a one-cell overlap and exact depth writes. The overlay must be checked against the meshed boundary and stereo views to rule out cracks, double surfaces, and ghost shadows. **This is an engineering design, not a proved same-frame guarantee:** no UE/GPU measurement was authorized in this research pass. The decisive GPU/VR test must finish edit compute + descriptors + draw in the **first submitted render frame whose simulation includes the command**; an input received after that frame's render submission belongs to the next frame. No affected tile may show a different edit sequence in that frame. [Epic RDG synchronization](https://dev.epicgames.com/documentation/unreal-engine/render-dependency-graph-in-unreal-engine), [GPU Gems 3's 3D GPU marching cubes](https://developer.nvidia.com/gpugems/gpugems3/part-i-geometry/chapter-1-generating-complex-procedural-terrains-using-gpu).
The current crash mode is especially clear for crater scale: synchronous center-tile remesh followed by up to 63 independently queued neighbors produces a visible wave; disabling old collision and waiting 0.4–1.4 s for cooks creates a falling gap; retaining old BodySetup creates ghost support. The transaction above removes those three states. It does **not** assume that optional Chaos rigid bodies can all get exact crater collision in the same frame; the owner expressly accepts field-query fallback for NPCs, arrows and other physics until their mesh is ready.
### Serious alternatives, with quantified wins and losses
| Alternative | Where it could win | Why it is not the primary route now |
|---|---|---|
| **Whole-world analytic raymarch (radical B)** | No persistent triangle mesh; edit command changes the next render. Keeter and 2025 pruning show that complex trees can be raytraced after regional simplification. | At 1920×1080, **2.07 M rays per eye**, or **4.15 M stereo rays** before shadows/VR supersampling; at 32 field steps this is **133 M queries/frame**. At the measured synthetic AVX2 throughput of ~156–209 M *grid* points/s across six cores, that many CPU queries would take ~0.64–0.85 s; GPU may be far faster. The 2025 procedural-noise paper measures **16 ms at 1024²** on a 4080 Ti; simple stereo-pixel scaling to 4.15 M gives ~63 ms before VR lighting, a scene/hardware inference. Density is not necessarily a distance bound for safe sphere tracing, and a full GPU renderer is a larger rewrite. [Keeter](https://www.mattkeeter.com/research/mpr/), [EG 2025 noise tracing](https://diglib.eg.org/bitstream/handle/10.1111/cgf70072/cgf70072.pdf), [SDF-grid ray tracing, JCGT](https://jcgt.org/published/0011/03/06/). |
| **All-GPU integer generation and meshing (C)** | The Eurographics RTX 4060 paper's 256³ discretization at 0.91–1.61 ms shows enormous headroom in a suitable GPU expression, though its data/model differ. Could supply same-frame crater triangles. | Requires explicit 32/64-bit integer arithmetic rules, fixed division/rounding, no unordered atomics for vertex order, deterministic prefix sums, CPU/GPU/AMD/NVIDIA parity hashes, and an UE RHI path. Any float-transcendental or vendor-specific FMA noise breaks the canonical requirement. The paper's pruning and rendering are GPU-only and do not prove the CPU player query. [Lipschitz Pruning](https://wbrbr.org/publications/LipschitzPruning/documents/LipschitzPruning_submitted_to_EG25.pdf), [Khronos float controls](https://github.com/KhronosGroup/Vulkan-Docs/blob/main/proposals/VK_KHR_shader_float_controls2.adoc). |
| **Dual contouring / surface nets / adaptive power-diagram contouring** | Potentially fewer vertices and sharper features; JCGT's SurfaceNets work reports better triangle quality than classic MC for segmentation models. A 2025 power-diagram method reconstructs complex SDF geometry from fewer samples. | Generation is ~97% density time in the local attribution, so changing contouring *alone* can save at most the remaining ~3% of that worker window. Adaptive sampling could save more but needs a certified error/feature rule for narrow passages. SIGGRAPH Asia 2024's GPU occupancy dual contouring still describes runtimes of **a few seconds** for its distinct reconstruction problem, not subsecond terrain streaming. Seam dependencies and LOD transition rules become more complex; neither solves cook or atomic edit transactions. Benchmark later if upload dominates after density rewrite. [Dual contouring](https://people.eecs.berkeley.edu/~jrs/meshpapers/JuLosassoSchaeferWarren.pdf), [JCGT SurfaceNets](https://jcgt.org/published/0011/01/03/), [Eurographics 2025 power-diagram contouring](https://onlinelibrary.wiley.com/doi/10.1111/cgf.70037), [SIGGRAPH Asia 2024 occupancy dual contouring](https://occupancy-based-dual-contouring.github.io/), [chunk seam example](https://ngildea.blogspot.com/2014/09/dual-contouring-chunked-terrain.html). |
| **SVO/DAG or OpenVDB as source of truth** | Empty-space skipping and sparse memory; GPU SVDAG editing shows large 3D painting can be interactive in a different system. | The world's seed/composer already *is* a compact source. At full 25 cm resolution, the 64 m cube alone has 17.37 M unique samples or **34.7 MB at 16 bits** if fully resident; a global 6 km view volume is vastly larger. DAG sharing degrades with unique noise and edits; keep a sparse cache/hierarchy, not a required full-world DAG. The cited GPU editor's code uses CUDA, so it is no cross-vendor solution. [OpenVDB](https://museth.org/Ken/Publications_files/Museth_TOG13.pdf), [SVDAG editing paper](https://diglib.eg.org/items/ffd7acf6-01f7-4e53-87f8-4cded382c944), [author code](https://github.com/mathijs727/GPU-SVDAG-Editing). |
| **Prebake each seasonal world in the shipped game** | If seasons have a finite, known seed/layout set, local NVMe transfer could remove cold density evaluation and deterministic divergence. | For this 64 m cube, even raw 16-bit density is **34.7 MB** before meshes; ~6 km horizontal radius with only an **8 m thick** full-detail slab is `π×6000²×8/(0.25³) ≈ 57.9 billion` voxels, **116 GB raw** per season. That slab is an intentionally generous lower bound for a mountainous/cave world. Sparse compressed meshes could be far smaller, but their disk, decompression, upload, and arbitrary composer/layout variants need an actual content budget. Prebake a few spawn hubs or far-field impostors, not every possible volume. |
| **Server-streamed chunk data** | Server could send already generated chunks, avoiding client generation. | A raw 34.7 MB 64 m cube in 1 s requires **278 Mb/s payload** before meshes/protocol, and this contradicts the explicit requirement that only seed, layout and edits replicate. Compression helps bandwidth, but latency/server scaling and the hard protocol rule remain. |
| **Far-field impostors, 2.5D clipmaps, Nanite** | Far view can be far cheaper if represented by coarse meshes/impostors; RMC locally contains a runtime Nanite proxy path, and Voxel Plugin 2 reports experimental Nanite memory savings. | An exterior heightfield loses caves/overhangs/islands when used as the whole world. Impostors are acceptable **beyond** interaction range if the world returns to 3D on approach, with a continuous crossfade and occlusion policy. Nanite can help mostly static far render, but Epic's dynamic mesh guidance says `UDynamicMeshComponent` is not Nanite-compatible, and Nanite fallback geometry still serves collision; runtime build/edit/VR costs here are unmeasured. [Geometry clipmaps](https://hhoppe.com/geomclipmap.pdf), [Epic dynamic mesh guidance](https://dev.epicgames.com/documentation/unreal-engine/working-with-meshes-in-unreal-engine), [Nanite fallback](https://dev.epicgames.com/documentation/unreal-engine/nanite-technical-details), [RMC code](https://github.com/TriAxis-Games/RealtimeMeshComponent). |
### What was searched, and what was ruled out
The source sweep covered SIGGRAPH closed-form interval rendering and GPU Gems 3 volumetric marching cubes; Eurographics 2025 Lipschitz pruning, derivative-aware procedural-noise tracing, proxy/LOD SDFs and adaptive contouring; older affine-interval ray tracing; JCGT SDF-grid ray tracing, cellular noise and SurfaceNets; HPG/i3D-style GPU mesh shader and SDF work; OpenVDB, SVO, DAG and GPU SVDAG editing; FastNoise2, ISPC, Inigo Quilez's analytic noise-derivative slides and table-free hashed noise; UE 5.7 Mover/CMC source, RMC local source, Epic RDG/Nanite/dynamic mesh documentation; Voxel Plugin 2; and developer material for No Man's Sky, Astroneer, Teardown, Enshrouded, Space Engineers, Vintage Story, Dual Universe, Sebastian Lague, Nick Gildea and 0fps. Quilez's derivative formula is useful for normal or local bound construction, but a sampled derivative is **not** a certified maximum over a brick. The strongest transferable ideas were **region-pruned expression trees, fused SIMD evaluation, sparse near-surface work, direct player field collision, and whole-region render transactions**. No retrieved shipped-game account publishes a directly comparable **cold 64 m, 25 cm, 3D editable, UE/VR, all-visible-and-player-collidable <1 s** measurement; their performance figures must not be presented as proof of this target. [Quilez noise-derivative slides](https://iquilezles.org/articles/function2009/function2009.pdf), [NMS GDC](https://www.gdcvault.com/play/1024265/), [Astroneer developer](https://www.gamedeveloper.com/design/what-i-astroneer-i-s-devs-learned-while-leaving-early-access), [Teardown developer](https://blog.voxagon.se/2018/10/17/from-screen-space-to-voxel-space.html), [Space Engineers source](https://github.com/KeenSoftwareHouse/SpaceEngineers), [Vintage Story developer](https://www.vintagestory.at/blog.html/news/de-jank-and-game-juice-update-1170-pre1-r322/), [0fps smooth voxel terrain](https://0fps.net/2012/07/12/smooth-voxel-terrain-part-2/).
Specifically ruled out as a **complete** answer: greedy block meshing (different surface model), heightfield-only terrain, unbounded generic interval recursion (failed local control), auto-selected float SIMD noise (vendor/CPU parity risk), CUDA-only edit structures (AMD target), Nanite or another UE mesh component alone (density/cook bottlenecks), and one tile at a time crater updates. Isosurface mesher alternatives, affine arithmetic, full GPU generation, and far impostors remain research branches with explicit benchmark gates. [0fps block meshing](https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/), [FastNoise2 determinism FAQ](https://github.com/Auburn/FastNoise2/wiki/FAQ), [SVDAG editor code](https://github.com/mathijs727/GPU-SVDAG-Editing), [Epic PMC async cooking](https://dev.epicgames.com/documentation/unreal-engine/API/Plugins/ProceduralMeshComponent/UProceduralMeshComponent).
### Revised gates and migration sequence
The consolidated seven-step plan above supersedes this round-two sequence. Its decisive change is to **measure the existing collision and RMC apply stages first**, then benchmark a prepared *float* region evaluator against the actual composer before choosing a full integer procedural ABI, Mover or render grouping. Keep the composer/content schema, route graph, multiplayer edit log and old tile evaluator as an oracle while migrating. The final numeric ABI still specifies quantization scale, rounding/saturation, zero tie, interpolation, material ID and stable op ordering; scalar/SSE parity and two-CPU hashes are release gates. A cross-vendor GPU hash is required for integer edit and mesh decisions, but the GPU does not need to reproduce float procedural noise. The round-two standalone probe remains a lower-complexity feasibility bound, not the acceptance test for production latency.
## What not to do
- Do not infer a 512-tile deadline from one tile's p95, or from the synthetic 0.1 s classified benchmark. Queueing, bandwidth, upload and physics are separate clocks.
- Do not put all 512 Chaos triangle cooks on the **player** readiness path. The observed 0.4–1.4 s interval is submit-to-ready and may be mostly queue delay; prioritize and time the small player-support neighborhood before ruling out cooked steady-terrain collision.
- Do not assume a sparse octree/DAG or OpenVDB automatically speeds a procedural query. Their compression and hierarchy help only if the final-field signs/bounds can be computed cheaply; edits and arbitrary smooth operators can make most nodes uncertain.
- Do not rely on heightfield clipmaps, GPU float world decisions, unordered GPU atomics, or `sin`/CRT results as a network world ABI.
- Do not claim AVX2, dual contouring, Nanite or a different mesh component will erase the observed 97% density cost. Benchmark each in its proper stage.
- Do not publish one touched tile while its neighbors still show the old edit; do not suppress collision under a pawn before a consistent replacement exists. Those are the visible ripple, vanish and false-support failure modes in the present implementation.
The owner has clarified both contracts: collision inside one second applies to the player, while bounded digging and radius-scale spells both require atomic same-frame visuals. Immediate **field-aware edit support** and the transaction-wide visual path are required; Mover versus a prioritized-cook/CMC field-overlay hybrid is decided by the staged tests above. Their deadlines remain unproved until an authorized UE/VR integration gate can be run.
+39 -13
View File
@@ -49,19 +49,8 @@ Legend: ✅ verified against code · ◻️ checklist box.
> ("nothing is set in stone"). ✅ BUILT & WORKING together with pass 2 (ticked 2026-07-27): > ("nothing is set in stone"). ✅ BUILT & WORKING together with pass 2 (ticked 2026-07-27):
> • **Terracing gradient Z-only** (6→2 SDF samples — see the ticked item above). > • **Terracing gradient Z-only** (6→2 SDF samples — see the ticked item above).
> • **Lerp X-macro** + **BakeRoomFeature dedupe** (both ticked above, bit-identical). > • **Lerp X-macro** + **BakeRoomFeature dedupe** (both ticked above, bit-identical).
> • **T2.b LOD octave drop** — opt-in `UVoxelSettings::LODOctaveDrop` (default 0 = byte-identical); > • **T2.b / T2.c / T2.d and F2** shipped in the same batch — tracked in `fable-idea.md` (Tier 2
> `VoxelGenLOD::OctaveBias` thread_local set per tile in `GenerateMesh`, per-voxel fractal sites > status, F2) and ARCHITECTURE §8.10, not repeated here.
> wrapped in `VoxelGenLOD::Eff(N)`, XY-field noise deliberately excluded. ARCHITECTURE §8.10.
> • **T2.c tile component pool** — `TileComponentPool` + `Acquire/ReleaseTileComponent`; unload
> parks (RemoveSectionGroup strips geometry+collision, hidden, stays registered), apply pops.
> Bonus: `ApplyMeshToTile` reuses the existing `URealtimeMesh` (`GetRealtimeMeshAs`) — the old
> unconditional `InitializeRealtimeMesh` allocated + orphaned one mesh UObject PER APPLY.
> • **T2.d worker clamp** — `GetMaxConcurrentTasks()` caps the asset budget to logical cores − 2
> (the BackgroundNormal priority half had already shipped). **Tier 2 is now COMPLETE** —
> T2.a was already done (float SSE `VoxelNoise` core, §8.10).
> • **F2 determinism validator** — `AVoxelWorld::ValidateDeterminism` CallInEditor button:
> boundary points sampled under two cache-window alignments + a repeat pass; any non-zero
> delta = §8.4 regression. The tool that VERIFIES all the "bit-identical" claims above.
## Performance ## Performance
- [x] ~~**`VoxelGenerator.cpp` (terrain-op gradient)** — compute once per voxel and reuse.~~ - [x] ~~**`VoxelGenerator.cpp` (terrain-op gradient)** — compute once per voxel and reuse.~~
@@ -116,6 +105,43 @@ Legend: ✅ verified against code · ◻️ checklist box.
two-grid deco redesign (`StreamTier` / `DecorationNearRadiusChunks`). two-grid deco redesign (`StreamTier` / `DecorationNearRadiusChunks`).
**CORRECTION: `GetStrateChunkZBounds` is NOT dead** — `BuildDesiredTiles` uses it for the **CORRECTION: `GetStrateChunkZBounds` is NOT dead** — `BuildDesiredTiles` uses it for the
strate-aware vertical clamp; struck from the dead list. strate-aware vertical clamp; struck from the dead list.
- [x] **2026-09 cleanup (`cleanup/2026-09`)** — the far sheet ring runtime (`GenerateSheetMesh` + its
streaming path) and ~40 unreferenced functions/members/types removed (each proven by a search of
`Source/`, `Tools/`, `Config/` and build files; see that branch's `refactor(dead-code)` commit).
Reflected (`UPROPERTY`/`UFUNCTION`/`UENUM`…) and enum candidates were NOT removed — see
*Owner decisions* below.
## Owner decisions (2026-09 cleanup)
Items left for the owner: unused data that assets, Blueprints, tools or tests may reference by name,
and changes that would alter generated terrain.
- [x] `UVoxelSettings::bFarSheetRing` / `FarSheetSpanLevels`, `voxel.TestFarSheetRing` and the
selector's sheet branch — removed (owner go-ahead); the harness no longer passes the switch and the
`sheet_tile` dump field stays for the parity compare.
- [x] `UVoxelSettings::CeilingViewMultiplier` / `CeilingBandChunks` / `DensityVolumeMaxTasks` — removed
(never read).
- [x] **Operator stack as the only density path** (cleanup/2026-09, owner-approved). Every strate —
authored, cooked-season (recipe or native) and editor composer candidate — now generates through its
operator stack; a degenerate strate is air. Removed: `UVoxelStrateDefinition::bUseOperatorStack`, the
archetype `switch` in `GetDensityAt`, `GetSlabDensity` / `GetMazeDensity` / `GetSurfaceDensity` /
`SurfaceDensityFromColumn` / `GetVerticalShaftDensity` / `GetFloatingIslandDensity`, the four
`Cave Bail Not Op Stack *` stats, the startup opt-in warning, commandlet `-opstack`, and the
legacy-vs-stack comparisons in the tests. **Terrain changes for any strate asset that did not
already have the opt-in ticked.**
- [ ] **No-strate-manager fallback world.** A world whose settings have no season, pool or fixed
strates runs `GetDensityAt`'s fallback: default `FStrateGenerationParams` through
`GetDensityWithParams` with its direct-caller ("legacy") structural posts. It is the only caller of
that post path outside tests/commandlet diagnostics. Decide whether an empty configuration should
keep generating generic caves or refuse to generate.
- [ ] `UVoxelStrateDefinition::WaterColor`, `Creatures`, `AmbientSound`, `Music`, `MusicVolume` and the
`FStrateCreature` / `FStrateAmbientActor` spawn fields (`SpawnChancePerChunk`, `MaxPerStrate`,
`SpawnWeight`) — authored data with no consuming system yet (F9 audio, creatures).
- [ ] `UVoxelBiomeDefinition::WaterMaterial` — never read; the water plane uses the strate's
`WaterMaterial` only.
- [ ] Plain C++ enums with no user: `EVoxelFace` (`VoxelTypes.h`), `EVoxelSeasonSelectionReason`
(`VoxelSeasonManifest.h`), `EVoxelOpCombine` (`VoxelDensityOp.h` — its comment carries the
two-channel sign convention; keep that text if the enum goes).
- [ ] `IVoxelHeightOp::MaxDisplacement` / `FVoxelHeightStack::MaxTotalDisplacement` — no caller; kept as
the documented hook for a future heightfield `ClassifyBox`.
## Over-complexity (behavior-preserving splits, optional) ## Over-complexity (behavior-preserving splits, optional)
+127
View File
@@ -0,0 +1,127 @@
// Standalone throughput probe, not VoxelForge's generator or an Unreal integration.
// Build from a VS 2022 x64 Native Tools prompt:
// cl /nologo /O2 /std:c++20 /EHsc /arch:AVX2 brick_kernel.cpp /Fe:brick_kernel.exe
#include <algorithm>
#include <array>
#include <chrono>
#include <cstdlib>
#include <cstdint>
#include <cstdio>
#include <thread>
#include <vector>
constexpr int N = 256, B = 8, NB = N / B;
#ifndef VF_WORKERS
#define VF_WORKERS 8
#endif
struct Sphere { int x, y, z, r; };
static std::array<Sphere, 48> spheres;
static std::vector<int16_t> field(size_t(N) * N * N);
static std::vector<uint8_t> reference_sign(size_t(N) * N * N);
static volatile uint64_t sink = 0;
static uint32_t hash3(int x, int y, int z, uint32_t seed) {
uint32_t h = seed ^ (uint32_t(x) * 0x9e3779b1u) ^
(uint32_t(y) * 0x85ebca77u) ^ (uint32_t(z) * 0xc2b2ae3du);
h ^= h >> 16; h *= 0x7feb352du; h ^= h >> 15;
h *= 0x846ca68bu; return h ^ (h >> 16);
}
// Integer lattice noise: fixed shifts and products; no FP/CRT/FMA.
static int noise(int x, int y, int z) {
int sum = 0;
for (int octave = 0; octave != 3; ++octave) {
int s = 16 >> octave, ix = x / s, iy = y / s, iz = z / s;
int fx = (x % s) * 256 / s, fy = (y % s) * 256 / s, fz = (z % s) * 256 / s;
uint32_t seed = 1337u + uint32_t(octave) * 17u;
int yz[2];
for (int dz = 0; dz != 2; ++dz) {
int yy[2];
for (int dy = 0; dy != 2; ++dy) {
int a = int(hash3(ix, iy + dy, iz + dz, seed) & 255u);
int b = int(hash3(ix + 1, iy + dy, iz + dz, seed) & 255u);
yy[dy] = a + ((b - a) * fx >> 8);
}
yz[dz] = yy[0] + ((yy[1] - yy[0]) * fy >> 8);
}
int v = yz[0] + ((yz[1] - yz[0]) * fz >> 8);
sum += (v - 128) >> octave;
}
return sum; // in [-224, 222], conservatively use +/-224.
}
static int density(int x, int y, int z) {
int d = -10000;
for (const Sphere& s : spheres) {
int dx=x-s.x, dy=y-s.y, dz=z-s.z;
int q=s.r*s.r-dx*dx-dy*dy-dz*dz;
d=std::max(d,q);
}
return d + noise(x,y,z);
}
static size_t at(int x,int y,int z) { return (size_t(z)*N+y)*N+x; }
template<class F> static double run(F&& f) {
auto a=std::chrono::steady_clock::now(); f();
return std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-a).count();
}
static void parallel_z(auto fn) {
std::array<std::thread,VF_WORKERS> t;
for (int i=0;i<VF_WORKERS;++i) t[i]=std::thread([=]{
fn((i*NB/VF_WORKERS)*B, ((i+1)*NB/VF_WORKERS)*B);
});
for (auto& x:t) x.join();
}
int main() {
std::printf("workers=%d\n", VF_WORKERS);
for(size_t i=0;i<spheres.size();++i) {
uint32_t h=hash3(int(i),17,71,12345);
spheres[i]={int((h&255u)),int((h>>8)&255u),int((h>>16)&255u), 12+int((h>>24)%16u)};
}
for(int trial=0;trial<3;++trial) {
double full=run([]{parallel_z([](int lo,int hi){
for(int z=lo;z<hi;++z) for(int y=0;y<N;++y) for(int x=0;x<N;++x)
field[at(x,y,z)]=int16_t(density(x,y,z));
});});
uint64_t checksum=0; for(size_t i=0;i<field.size();i+=4093) checksum+=uint16_t(field[i]); sink=checksum;
std::printf("full trial=%d ms=%.3f checksum=%llu\n",trial,full,(unsigned long long)checksum);
}
for(size_t i=0;i<field.size();++i) reference_sign[i]=field[i]>0;
// Conservative sphere/brick bounds with a +/-224 noise interval.
std::array<uint8_t, size_t(NB)*NB*NB> kind{};
size_t solid=0,air=0,mixed=0;
double classify=run([&]{
for(int bz=0;bz<NB;++bz) for(int by=0;by<NB;++by) for(int bx=0;bx<NB;++bx) {
int x0=bx*B,y0=by*B,z0=bz*B,x1=x0+B-1,y1=y0+B-1,z1=z0+B-1;
bool possiblyAir=false, definitelyAir=false;
for(const Sphere& s:spheres) {
int dx=std::max({x0-s.x,0,s.x-x1});
int dy=std::max({y0-s.y,0,s.y-y1});
int dz=std::max({z0-s.z,0,s.z-z1});
int near2=dx*dx+dy*dy+dz*dz;
int fx=std::max(std::abs(x0-s.x),std::abs(x1-s.x));
int fy=std::max(std::abs(y0-s.y),std::abs(y1-s.y));
int fz=std::max(std::abs(z0-s.z),std::abs(z1-s.z));
int far2=fx*fx+fy*fy+fz*fz;
possiblyAir |= (s.r*s.r-near2+224>=0);
definitelyAir |= (s.r*s.r-far2-224>0);
}
uint8_t k=definitelyAir?2:(possiblyAir?1:0);
kind[(size_t(bz)*NB+by)*NB+bx]=k;
if(k==0)++solid; else if(k==2)++air; else ++mixed;
}
});
std::printf("classify ms=%.3f solid=%zu air=%zu mixed=%zu total=%zu\n",classify,solid,air,mixed,kind.size());
for(int trial=0;trial<3;++trial) {
double sparse=run([&]{parallel_z([&](int lo,int hi){
for(int bz=lo/B;bz<hi/B;++bz) for(int by=0;by<NB;++by) for(int bx=0;bx<NB;++bx) {
uint8_t k=kind[(size_t(bz)*NB+by)*NB+bx];
for(int z=bz*B;z<(bz+1)*B;++z) for(int y=by*B;y<(by+1)*B;++y)
for(int x=bx*B;x<(bx+1)*B;++x)
field[at(x,y,z)]= k==1?int16_t(density(x,y,z)):int16_t(k==2?1:-1);
}
});});
uint64_t checksum=0; for(size_t i=0;i<field.size();i+=4093) checksum+=uint16_t(field[i]); sink=checksum;
std::printf("sparse trial=%d ms=%.3f checksum=%llu\n",trial,sparse,(unsigned long long)checksum);
}
size_t mismatches=0;
for(size_t i=0;i<field.size();++i) mismatches+=(reference_sign[i]!=(field[i]>0));
std::printf("sign mismatches=%zu of %zu\n",mismatches,field.size());
}
+181
View File
@@ -0,0 +1,181 @@
# Analytic intervals and packed signs: one-day spike
Date: 2026-10-02
Result: the near-region interval/bit compiler is promising at this corpus size. The experiment does **not** prove the complete realistic visible world can be built in under one second: the near output is smaller than the proposed 60 MB workload, and the far-field generator is a coarse sheet proxy rather than the production cave hierarchy.
## What was built
`interval_spike.cpp` is a standalone C++20 program. It generates the same ordered quadric CSG world for both implementations:
- A point reference evaluates every one of the 17,373,979 near-region lattice nodes, using a 3D AABB index to select possible features.
- The interval path intersects the same fixed-point quadrics along all three 67,081-line axis families, sweeps sorted entry/exit events with a 64-bit membership word, composes the ordered edits, and fills packed sign words.
- `OR(8 corners) & ~AND(8 corners)` finds active cells 64 at a time. The mesher joins edge crossings around cell faces, uses an exact center sign for ambiguous faces, and emits indexed position/normal buffers. Near-region vertices come from quantized analytic quadric crossings, not edge midpoints.
- Sub-cell strata that have no lattice point inside them receive an explicit analytic surface patch. The patches are filtered against the final composed occupancy.
- The near cube has 256³ core cells at 25 cm and one node of halo on each side. Its cell region is split into 64 pages. Edge vertices are shared in one keyed buffer and assigned to a deterministic owner page.
- A separate far-field stage builds five stacked parent ellipsoid sheets as indexed patches, without generating children.
The corpus has **372 features** per seed: 18 rooms, 147 overlapping ellipsoid tunnel segments on winding and vertical connections, 40 pillars, 36 stalactites/stalagmites, 42 shelves, 24 floating masses, 12 craters, 40 crowded-junction edits, and 13 thin strata. It uses the current HyperComplex scale as a guide: 26 m room pitch and roughly 0.28–0.37 pitch radius fractions. Five deterministic seeds were measured.
The crowded cluster has 40 overlapping operations. Across the whole corpus the largest candidate list on a line was 58 shapes, below the 64-membership limit. The largest primitive-interval list was 52. No tested line overflowed the membership word.
## Toolchain and numeric contract
The host reports an AMD Ryzen 7 5800X3D with 16 logical processors. The build used the requested VS 2022 `vcvars64.bat` and `cl /std:c++20 /O2 /arch:AVX2 /EHsc`.
From the plugin root, the tested build command was:
```bat
call "E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
cl /nologo /std:c++20 /O2 /arch:AVX2 /EHsc /Fe:Saved\Research\intervals\interval_spike.exe /Fo:Saved\Research\intervals\interval_spike.obj Saved\Research\intervals\interval_spike.cpp
```
Each benchmark process takes `<seed> <workers>` arguments, for example `interval_spike.exe 1 6`, and writes one JSON record to stdout. The final per-process records are `final_seed1_w1.json` through `final_seed5_w8.json` in this directory.
Shape transforms are Q15 integer matrices; quadric coefficients are rounded once into Q28 per cm². Occupancy points and interval endpoints use signed 1/16 cm integer coordinates. A shape contains a point when its integer quadratic predicate is `<= 0`; the endpoint inclusion and ordered CSG operation rules are fixed. Line endpoints are found by integer bisection over the canonical coordinate grid, and mesh crossings are the midpoints between the last outside and first inside coordinates.
The sign paths use integer arithmetic only. With this corpus, local coordinates stay within each feature’s conservative AABB; the largest Q28 coefficient is bounded by the 4 cm minimum radius, and the largest possible quadratic terms stay below 10¹⁸, within signed 64-bit range. That makes the sign decision identical on x64 CPUs implementing the same C++ integer rules. The far parent surface root and explicit thin-patch parameterization use floating point for vertex placement, after the sign/classification decision; cross-vendor bitwise mesh identity was not tested. On this 5800X3D, final geometry hashes matched across worker counts and repeated fresh processes.
## Timings
Each row summarizes five fresh processes, one per seed. The p95 uses linear interpolation between ordered samples. The interval stage includes solving/composing all three axes **and writing packed sign words**; active-cell masks are timed separately. Meshing is single-threaded in this spike, so worker counts primarily affect interval generation.
Times are milliseconds, shown as median / p95 / worst.
| Stage | 1 worker | 6 workers | 8 workers |
|---|---:|---:|---:|
| Corpus and spatial hierarchy | 0.5 / 0.6 / 0.6 | 0.5 / 0.7 / 0.7 | 0.5 / 0.7 / 0.8 |
| Interval solve, compose, and sign words (3 axes) | 154.2 / 167.8 / 170.8 | 48.2 / 52.6 / 52.9 | 43.2 / 49.0 / 50.1 |
| Active-cell masks | 2.1 / 3.4 / 3.7 | 2.1 / 2.3 / 2.3 | 2.1 / 2.2 / 2.2 |
| Near mesh emission | 188.4 / 197.4 / 198.7 | 191.2 / 229.3 / 238.4 | 192.8 / 219.8 / 223.9 |
| Far-field proxy | 8.6 / 8.7 / 8.8 | 8.6 / 8.6 / 8.6 | 8.6 / 8.9 / 9.0 |
| **Near CPU total** | **349.3 / 357.9 / 358.1** | **241.1 / 280.3 / 288.3** | **240.2 / 270.9 / 276.7** |
| Near plus far proxy | 357.9 / 366.5 / 366.7 | 249.7 / 288.9 / 296.9 | 248.8 / 279.5 / 285.3 |
The interval stage fits its 150 ms allocation at 6 and 8 workers, but not at one worker. The near CPU total stays below 430 ms for every measured run. The 6-worker mesh p95 exceeds the proposed 220 ms mesh allocation; the 8-worker p95 is just below it, while its worst run is slightly over.
## Output and memory
| Result | Median across five seeds | Range |
|---|---:|---:|
| Near vertices | 282,404 | 272,704–296,660 |
| Near triangles | 563,052 | 543,828–591,512 |
| Near mesh buffers | 13,535,700 bytes (13.54 MB) | 13,072,232–14,219,284 bytes |
| Near plus far buffers | 19,848,460 bytes (19.85 MB) | 19,384,992–20,532,044 bytes |
| Process working set after validation | 45,191,168 bytes (43.1 MiB) | 44,199,936–46,628,864 bytes |
The proposed 60 MB output workload was not reached. This corpus emitted about 23% of the round-3 estimate of 2.4 million triangles, so the measured mesh time does not establish how a 60 MB result scales.
## Interval and crossing statistics
Each axis has 67,081 lines. The table gives the median line p50, the median of the five per-seed line p95 values, and the worst single-line value over all five seeds. The per-seed line p95 uses the sorted index `floor(0.95 * (N - 1))`.
| Axis | Candidate shapes per line: p50 / p95 / worst | Primitive intervals per line: p50 / p95 / worst | Composed crossings per line: p50 / p95 / worst | Occupancy runs per line: p50 / p95 / worst |
|---|---:|---:|---:|---:|
| X | 0 / 16 / 44 | 0 / 11 / 40 | 0 / 8 / 20 | 1 / 9 / 21 |
| Y | 0 / 17 / 46 | 0 / 12 / 40 | 0 / 10 / 24 | 1 / 9 / 23 |
| Z | 1 / 13 / 58 | 0 / 9 / 52 | 0 / 8 / 22 | 1 / 9 / 19 |
The busiest observed line used 58 candidate slots, leaving six membership bits unused. The long lists are localized; the median line has no candidate shape in X and Y and one in Z. The distributions and maxima were stable across worker counts.
## Correctness and determinism
- **Full sign agreement:** all 15 final benchmark processes compared the pointwise reference with all three interval-axis sign paths at every lattice node: zero mismatches.
- **Mesh crossings:** zero sign-changing lattice edges lacked an analytic crossing; zero contour-degree faults and zero open cell contours.
- **Page ownership:** all 64 near pages passed unique edge-key and owner-page checks. Shared border vertices use one canonical crossing record and quantized position.
- **Tangency:** an exact tangent line retained its single-point interval.
- **Coincident edits:** coincident carve/add intervals had identical roots, and both operation orders produced their defined last-operation result.
- **Negative coordinates:** the full lattice includes negative world X and Z coordinates and passed the point-reference comparison.
- **Thin strata:** 13 sub-cell strata were in each corpus. Across the five seeds, 1–10 had no structural lattice point inside; every such feature received explicit surface triangles (288–1,993 triangles per seed). The deliberately isolated 12 cm-thick air seam had no lattice sample and two crossing tags on its central line. The validation gate was also given a deliberately omitted thin surface and rejected it.
- **Determinism:** for each of five seeds, the combined geometry hash was identical at 1, 6, and 8 workers. A repeated fresh seed-1/one-worker process also matched. Per-seed hashes: `e5ad62303de5f641`, `17a0d35b9c500445`, `c74544dc1b78ce7b`, `69cef91397829b57`, `afb5e5e8fa54b8a9`.
## Far-field result and limit
The proxy selected **47,040 nodes**, **92,150 patches**, **94,090 vertices**, and **184,300 triangles** in **6,312,760 bytes**, with zero descendants. It takes about 8.6 ms. The grid spans ±6 km on each horizontal axis and has five stacked parent layers.
This is not a proof of the far cave hierarchy. The patches are all on the same 125 m grid; the reported LOD bands are metadata rather than different tessellation levels. The parent shapes are five broad ellipsoid sheets, not the room/tunnel grammar used in the near corpus. The square reaches about 8.5 km at its corners, and no far/near transition surface was built or validated. The production far field is therefore **unproven**.
## Gate decision
The near interval/bit core passes the **430 ms timing** portion of the first gate on this 372-feature corpus, and crowded junctions stay below the 64-shape line limit. The output is only 13.1–14.2 MB nearby, however, so the proposed ~60 MB mesh budget was not exercised. The 6-worker mesh p95 also exceeds its 220 ms sub-budget. Thin-feature and shared-boundary checks pass for the tested feature family.
This is a positive near-core result, not a whole-world pass. Do not treat the under-367 ms near-plus-proxy figure as evidence that the realistic six-kilometre cave world is ready for the Unreal publication/collision test; the far hierarchy and larger output workload remain unproven.
## Failed and retried builds/runs
- Build attempt 1 failed because Win32 `min`/`max` macros expanded `std::min`/`std::max` calls. `NOMINMAX` fixed that class of error.
- Build attempt 2 then failed on the room-center array dimensions and the Windows `far` macro colliding with a local variable name. Both were corrected; subsequent MSVC builds succeeded.
- `vcvars64.bat` printed that `vswhere.exe` was not recognized on each build, but it continued and launched `cl.exe`. The final build succeeded with only unused-parameter and bool-comparison warnings.
- The first benchmark launch attempt used PowerShell’s unsupported `Start-Process -PriorityClass` parameter and did not start the executable. The retry launched the process and immediately set its priority to `BelowNormal`.
- The first executable run completed but failed validation with 878,033 sign mismatches and no thin crossing tags. The line solver had omitted the shape-center offset when converting the quadratic vertex to an absolute coordinate. After correction, the full sign comparison passed; thin surfaces and explicit contour-integrity assertions were then added.
- A final-run preflight initially aborted at a self-imposed 6 GB free-memory threshold; no process started. The machine then had 4.5 GB free while another foreground process used about 12.3 GB. The benchmark working set was about 45 MB, so the final checks were retried below-normal with a 1 GB floor. All three checks passed, followed by all 15 final fresh-process runs.
All final runs exited 0. No Unreal process was launched, no `Source/` file was modified, and no commit, push, or stash was made.
## Next step
Extend the CPU corpus until it emits the intended ~60 MB / 2.4 million triangle workload, with page meshing parallelized deterministically. In parallel, replace the far sheet proxy with actual selected parent room/tunnel/strata shapes, radial horizon clipping, and a shared near/far transition. Re-run the same validation and gates; then decide whether the Unreal publication/collision test is justified.
## Round 2: bounded blends, QEF mesh, and surface relief
Date: 2026-10-02. The existing section above records the first-round measurements; this section records the changed field and mesh.
### What changed
- The corpus now has **363 shapes**: 18 rooms, 147 anisotropic tunnel segments with changing cross-sections, 40 pillars, 36 stalactite/stalagmite forms, 42 shelves, 24 floating masses, 12 craters, 40 crowded-junction edits, three sparse rock bands, and the isolated audit seam. The 12 repeated thin strata were removed. The three remaining bands have different orientations and are placed at a nearby air lattice node so the interval mesh sees their thin crossings.
- Smooth min/max is applied only in bounded overlap regions. At 70 cm blend width, the mask covers a median **3.08% of cell centers** (3.07–3.19% across five seeds) and **1.94% of lattice nodes** (1.94–1.98%). Everywhere else the original exact ordered interval field is used. The banded features keep their hard field so their crossings remain exact.
- The near mesh now uses per-cell QEF dual contouring with cached analytic Hermite crossings/normals, face-connectivity component splits, a deterministic ambiguous-face sign, and deterministic page ownership. Page-edge ownership is checked across all 64 pages. The 12 cm audit seam, which no lattice point can see, gets a closed 336-triangle analytic patch.
- A deterministic four-band value-noise displacement runs after meshing, with wavelengths **6.4, 3.2, 1.6, and 1.2 m**. The shortest band spans 4.8 voxel cells. The hard displacement bound is **0.60 m**; the five measured seeds reached 0.404–0.439 m. Vertex normals are rebuilt after displacement. No extra volume samples are taken for relief.
- `interval_viewer2.exe` renders the blended dual-contour mesh and keeps a separate displaced vertex buffer. Press **R** to switch displacement off/on. The world-space rock color mottling and D3D11 back-buffer PNG capture are part of this standalone viewer. Capture mode closes its own window after writing the screenshots.
### Timing comparison
The first-round values below are the medians from the original five-seed report. Round 2 reports the median/p95/worst of five fresh sequential processes per worker count; p95 is linearly interpolated at the 95th percentile. The blend-index row is the AABB overlap raster plus surface-pair filtering. Near total includes the listed stages; far-field time is separate.
| Stage (ms) | Before medians, 1 / 6 / 8 workers | After, 1 worker med / p95 / worst | After, 6 workers med / p95 / worst | After, 8 workers med / p95 / worst |
|---|---:|---:|---:|---:|
| Corpus/hierarchy, excluding blend-zone index | 0.5 / 0.5 / 0.5 | 0.96 / 1.03 / 1.04 | 0.96 / 0.97 / 0.97 | 0.96 / 1.08 / 1.10 |
| Blend-zone index and mask | — / — / — | 186.68 / 203.10 / 205.61 | 186.07 / 203.27 / 205.51 | 186.97 / 202.43 / 204.94 |
| Interval solve, compose, and sign words | 154.2 / 48.2 / 43.2 | 154.88 / 156.72 / 157.12 | 48.89 / 50.47 / 50.65 | 46.10 / 46.97 / 47.16 |
| Blend-zone pointwise evaluation and crossings | — / — / — | 203.85 / 225.44 / 229.87 | 200.79 / 205.81 / 205.98 | 201.49 / 208.96 / 209.65 |
| Active-cell masks | 2.1 / 2.1 / 2.1 | 2.62 / 3.82 / 3.86 | 2.85 / 4.05 / 4.28 | 3.58 / 4.29 / 4.29 |
| Near meshing: old contour emitter / QEF dual contour | 188.4 / 191.2 / 192.8 | 315.62 / 324.11 / 325.76 | 322.14 / 344.69 / 349.24 | 330.38 / 331.55 / 331.57 |
| Surface displacement and normal rebuild | — / — / — | 61.43 / 62.29 / 62.45 | 61.04 / 61.80 / 61.91 | 61.26 / 63.41 / 63.69 |
| **Near CPU total** | **349.3 / 241.1 / 240.2** | **931.93 / 957.68 / 959.08** | **836.06 / 848.28 / 849.57** | **824.27 / 854.37 / 861.33** |
| Far-field proxy | 8.6 / 8.6 / 8.6 | 8.59 / 8.61 / 8.62 | 8.57 / 9.51 / 9.58 | 8.58 / 8.60 / 8.61 |
| **Near plus far** | **357.9 / 249.7 / 248.8** | **940.54 / 966.28 / 967.68** | **844.62 / 857.37 / 858.80** | **832.85 / 862.93 / 869.89** |
That is a **2.67× / 3.47× / 3.43×** near-total cost against round 1 at 1/6/8 workers. Most of the new cost is visible in the table: roughly 187–190 ms to build the overlap mask, 201–204 ms to evaluate blended nodes/rebuild crossings, 316–330 ms for dual contouring, and about 61 ms for relief plus normal rebuild. The 6/8-worker interval speedup remains, but the zone work and mesher are single-threaded here.
The median output across the five seeds is **265,508 near vertices**, **531,164 triangles**, and **12,747,440 near-buffer bytes**. Median working sets were 69.6 / 70.0 / 70.4 MB at 1 / 6 / 8 workers. The proposed 60 MB mesh workload is still not exercised.
### Correctness, topology, and repeatability
- All 15 final processes (seeds 1–5 at 1, 6, and 8 workers) exited 0. Each compared the interval/blend signs against the new pointwise blended-field reference at every lattice node: **zero mismatches** and zero blend-boundary sign disagreements. All page ownership checks passed, with zero missing analytic crossings, zero cell-contour faults, and zero open cell contours.
- The three lattice-snapped bands are present in the structural mesh. The 12 cm audit seam has no lattice sample and is retained by its closed 336-triangle patch. The thin-surface omission check still rejects a deliberately omitted patch.
- The edge-manifold diagnostic reports **zero boundary edges** on all five seeds, but **3–7 overused edge groups** remain. The JSON `non_manifold_edges_zero` field is therefore false. These are localized dual-contour singularities around ambiguous/multiple-crossing cells; they are visible in the diagnostics and were not waived as a zero count. The page-ownership/sign/thin-feature validation result is reported separately from this remaining topology count.
- The QEF clamp counter is 141,729–155,046 across seeds. It increments per clamped coordinate axis, not per unique vertex. There are also 1,654–2,035 retained near-zero-area triangles; keeping them preserves edge connectivity after QEF clamping. These facts, plus the remaining overused edges, mean the mesher still needs topology work before treating the output as production collision geometry.
- For each seed, raw mesh, displaced mesh, and combined hashes match at all three worker counts. A fresh repeated seed-1/one-worker process also matched: mesh `20d4d80f439c17f6`, displaced mesh `0592d64cb4f269ca`, combined `fe846e12af9fef67`. Per-seed combined hashes: `fe846e12af9fef67`, `8f0994fe2295a34e`, `ee13abc933f14a3f`, `129dcc0c9bac3b3e`, `66361d9daff2b1a6`.
### Screenshots and visual judgement
These PNGs were captured from the viewer’s own D3D11 swap-chain back buffer, at 1280×800:
| View | Capture |
|---|---|
| Inside the first chamber, relief on | [`chamber.png`](views/chamber.png) |
| Same camera, relief off | [`chamber_relief_off.png`](views/chamber_relief_off.png) |
| Open point in the crowded junction | [`junction.png`](views/junction.png) |
| Close to the chamber wall | [`wall.png`](views/wall.png) |
Compared with the reject image, the repeated thin slab stack is gone, the color has rock-like variation, and many primitive junctions read as rounded transitions. The relief-on chamber is visibly less smooth than the relief-off view. The result is still well short of the reference cave: the close-wall view shows an obvious rounded ellipsoid, the chamber has broad flat-looking facets, and the material lacks the target’s rich rock texture, crystals, and water. I would describe this as a measurable improvement over the rejection, not a finished natural-rock look.
### Failed and retried Round 2 attempts
- Two early compile invocations passed the `vcvars64.bat` path with broken shell quoting, so `cl.exe` never started. An intermediate C++ build also failed because `near` was used as an identifier; it was renamed. The first D3D11 viewer link failed with unresolved `CoInitializeEx`/`CoUninitialize`/`CoCreateInstance`; linking `ole32.lib` fixed it. A later corpus-snap build failed once because `eval_quad` was used before declaration; a forward declaration fixed it. The corrected builds succeeded. MSVC continued to print that `vswhere.exe` was unavailable from `vcvars64.bat`, but it still launched `cl.exe`.
- `round2_probe3` through `round2_probe12` were intermediate dual-contour runs that exited on validation failures while the Hermite cache, component topology, degenerate triangles, and thin patches were being corrected. At probe 12, missing crossings had reached zero, but the mesh still reported about 3.7k non-manifold edges. Skipping the zero-area triangles produced thousands of boundary edges; a one-QEF-vertex-per-cell experiment made the overused-edge count worse (798), so both approaches were reverted. The final five-seed check has zero boundary edges and 3–7 overused groups.
- The first two D3D capture runs failed at WIC pixel-format selection (`0x88982f80`, unsupported RGBA); they left zero-byte PNGs. The capture path now swaps the D3D11 RGBA readback into WIC BGRA and writes all four non-empty images above. The capture-mode viewer closed itself; no previously open viewer was touched.
- MSVC continued to print that `vswhere.exe` was unavailable from `vcvars64.bat`, but all final builds completed. All 15 final benchmark runs and the repeated determinism run exited 0. No Unreal build or launch was done, no plugin `Source/` file changed, and nothing was committed, pushed, or stashed.
### Round 2 conclusion
Smooth joins and broad relief improve the rejected world, with only about 3.1% of cell centers in blend zones. The price is substantial: the near build rises from 241 ms to 836 ms at six workers and from 240 ms to 824 ms at eight workers. QEF clamping and the remaining 3–7 overused edges also keep this from being production-ready geometry. The viewer and measurements are ready for owner review; the screenshots show honestly that more corpus shaping/material work is needed to approach the reference.
@@ -0,0 +1 @@
{"seed":1,"workers":1,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.497,"interval_solve_compose_and_sign_words_3_axes":153.430,"active_cell_masks":1.978,"near_mesh_emission":172.709,"far_field":8.577,"validation_outside_timing":242.817,"near_cpu_total":328.614,"near_plus_far_total":337.191},"near":{"active_cells":272986,"vertices":275646,"triangles":549585,"bytes":13211864,"hash":"ff39dc05fa0b59e0","pages":64,"index_ranges":67,"page_border_vertices":14777,"ambiguous_faces":3870,"multi_cross_edges":416,"thin_detected":13,"thin_hidden_lattice":3,"thin_meshed":3,"explicit_thin_triangles":639},"interval_stats":{"x":{"lines":67081,"candidate_total":181692,"candidate_p50":0,"candidate_p95":16,"candidate_max":44,"interval_total":110023,"interval_p50":0,"interval_p95":11,"interval_max":40,"crossing_total":88770,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":193718,"candidate_p50":0,"candidate_p95":18,"candidate_max":45,"interval_total":111420,"interval_p50":0,"interval_p95":12,"interval_max":40,"crossing_total":91616,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":206488,"candidate_p50":0,"candidate_p95":13,"candidate_max":58,"interval_total":120012,"interval_p50":0,"interval_p95":9,"interval_max":52,"crossing_total":109308,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"ab4a669002ce586d"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":3,"audit_thin_crossing_events":2},"output_bytes_total":19524624,"combined_hash":"e5ad62303de5f641","working_set_bytes":44474368,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":1,"workers":1,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.493,"interval_solve_compose_and_sign_words_3_axes":151.928,"active_cell_masks":2.082,"near_mesh_emission":178.676,"far_field":8.558,"validation_outside_timing":241.893,"near_cpu_total":333.179,"near_plus_far_total":341.737},"near":{"active_cells":272986,"vertices":275646,"triangles":549585,"bytes":13211864,"hash":"ff39dc05fa0b59e0","pages":64,"index_ranges":67,"page_border_vertices":14777,"ambiguous_faces":3870,"multi_cross_edges":416,"thin_detected":13,"thin_hidden_lattice":3,"thin_meshed":3,"explicit_thin_triangles":639},"interval_stats":{"x":{"lines":67081,"candidate_total":181692,"candidate_p50":0,"candidate_p95":16,"candidate_max":44,"interval_total":110023,"interval_p50":0,"interval_p95":11,"interval_max":40,"crossing_total":88770,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":193718,"candidate_p50":0,"candidate_p95":18,"candidate_max":45,"interval_total":111420,"interval_p50":0,"interval_p95":12,"interval_max":40,"crossing_total":91616,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":206488,"candidate_p50":0,"candidate_p95":13,"candidate_max":58,"interval_total":120012,"interval_p50":0,"interval_p95":9,"interval_max":52,"crossing_total":109308,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"ab4a669002ce586d"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":3,"audit_thin_crossing_events":2},"output_bytes_total":19524624,"combined_hash":"e5ad62303de5f641","working_set_bytes":44490752,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":1,"workers":6,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.725,"interval_solve_compose_and_sign_words_3_axes":72.450,"active_cell_masks":3.939,"near_mesh_emission":254.653,"far_field":8.601,"validation_outside_timing":320.588,"near_cpu_total":331.767,"near_plus_far_total":340.367},"near":{"active_cells":272986,"vertices":275646,"triangles":549585,"bytes":13211864,"hash":"ff39dc05fa0b59e0","pages":64,"index_ranges":67,"page_border_vertices":14777,"ambiguous_faces":3870,"multi_cross_edges":416,"thin_detected":13,"thin_hidden_lattice":3,"thin_meshed":3,"explicit_thin_triangles":639},"interval_stats":{"x":{"lines":67081,"candidate_total":181692,"candidate_p50":0,"candidate_p95":16,"candidate_max":44,"interval_total":110023,"interval_p50":0,"interval_p95":11,"interval_max":40,"crossing_total":88770,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":193718,"candidate_p50":0,"candidate_p95":18,"candidate_max":45,"interval_total":111420,"interval_p50":0,"interval_p95":12,"interval_max":40,"crossing_total":91616,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":206488,"candidate_p50":0,"candidate_p95":13,"candidate_max":58,"interval_total":120012,"interval_p50":0,"interval_p95":9,"interval_max":52,"crossing_total":109308,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"ab4a669002ce586d"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":3,"audit_thin_crossing_events":2},"output_bytes_total":19524624,"combined_hash":"e5ad62303de5f641","working_set_bytes":44904448,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":1,"workers":8,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.501,"interval_solve_compose_and_sign_words_3_axes":49.895,"active_cell_masks":2.151,"near_mesh_emission":183.250,"far_field":9.053,"validation_outside_timing":243.429,"near_cpu_total":235.797,"near_plus_far_total":244.850},"near":{"active_cells":272986,"vertices":275646,"triangles":549585,"bytes":13211864,"hash":"ff39dc05fa0b59e0","pages":64,"index_ranges":67,"page_border_vertices":14777,"ambiguous_faces":3870,"multi_cross_edges":416,"thin_detected":13,"thin_hidden_lattice":3,"thin_meshed":3,"explicit_thin_triangles":639},"interval_stats":{"x":{"lines":67081,"candidate_total":181692,"candidate_p50":0,"candidate_p95":16,"candidate_max":44,"interval_total":110023,"interval_p50":0,"interval_p95":11,"interval_max":40,"crossing_total":88770,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":193718,"candidate_p50":0,"candidate_p95":18,"candidate_max":45,"interval_total":111420,"interval_p50":0,"interval_p95":12,"interval_max":40,"crossing_total":91616,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":206488,"candidate_p50":0,"candidate_p95":13,"candidate_max":58,"interval_total":120012,"interval_p50":0,"interval_p95":9,"interval_max":52,"crossing_total":109308,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"ab4a669002ce586d"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":3,"audit_thin_crossing_events":2},"output_bytes_total":19524624,"combined_hash":"e5ad62303de5f641","working_set_bytes":45068288,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":2,"workers":1,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.505,"interval_solve_compose_and_sign_words_3_axes":165.156,"active_cell_masks":2.084,"near_mesh_emission":184.966,"far_field":8.577,"validation_outside_timing":246.244,"near_cpu_total":352.711,"near_plus_far_total":361.288},"near":{"active_cells":283161,"vertices":286734,"triangles":569817,"bytes":13720900,"hash":"dab38d009700f1f0","pages":64,"index_ranges":74,"page_border_vertices":15582,"ambiguous_faces":2854,"multi_cross_edges":536,"thin_detected":13,"thin_hidden_lattice":10,"thin_meshed":10,"explicit_thin_triangles":1993},"interval_stats":{"x":{"lines":67081,"candidate_total":187058,"candidate_p50":0,"candidate_p95":17,"candidate_max":43,"interval_total":111999,"interval_p50":0,"interval_p95":11,"interval_max":40,"crossing_total":89658,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":199365,"candidate_p50":0,"candidate_p95":18,"candidate_max":44,"interval_total":120586,"interval_p50":0,"interval_p95":12,"interval_max":39,"crossing_total":104152,"crossing_p50":0,"crossing_p95":10,"crossing_max":22,"runs_p50":1,"runs_p95":11,"runs_max":19},"z":{"lines":67081,"candidate_total":204987,"candidate_p50":1,"candidate_p95":13,"candidate_max":56,"interval_total":124121,"interval_p50":0,"interval_p95":10,"interval_max":48,"crossing_total":112170,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"0293ad0bb4aab1f8"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":10,"audit_thin_crossing_events":2},"output_bytes_total":20033660,"combined_hash":"17a0d35b9c500445","working_set_bytes":45158400,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":2,"workers":6,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.499,"interval_solve_compose_and_sign_words_3_axes":51.718,"active_cell_masks":2.512,"near_mesh_emission":191.036,"far_field":8.609,"validation_outside_timing":269.096,"near_cpu_total":245.766,"near_plus_far_total":254.375},"near":{"active_cells":283161,"vertices":286734,"triangles":569817,"bytes":13720900,"hash":"dab38d009700f1f0","pages":64,"index_ranges":74,"page_border_vertices":15582,"ambiguous_faces":2854,"multi_cross_edges":536,"thin_detected":13,"thin_hidden_lattice":10,"thin_meshed":10,"explicit_thin_triangles":1993},"interval_stats":{"x":{"lines":67081,"candidate_total":187058,"candidate_p50":0,"candidate_p95":17,"candidate_max":43,"interval_total":111999,"interval_p50":0,"interval_p95":11,"interval_max":40,"crossing_total":89658,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":199365,"candidate_p50":0,"candidate_p95":18,"candidate_max":44,"interval_total":120586,"interval_p50":0,"interval_p95":12,"interval_max":39,"crossing_total":104152,"crossing_p50":0,"crossing_p95":10,"crossing_max":22,"runs_p50":1,"runs_p95":11,"runs_max":19},"z":{"lines":67081,"candidate_total":204987,"candidate_p50":1,"candidate_p95":13,"candidate_max":56,"interval_total":124121,"interval_p50":0,"interval_p95":10,"interval_max":48,"crossing_total":112170,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"0293ad0bb4aab1f8"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":10,"audit_thin_crossing_events":2},"output_bytes_total":20033660,"combined_hash":"17a0d35b9c500445","working_set_bytes":45715456,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":2,"workers":8,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.496,"interval_solve_compose_and_sign_words_3_axes":52.960,"active_cell_masks":2.198,"near_mesh_emission":191.148,"far_field":8.569,"validation_outside_timing":246.765,"near_cpu_total":246.802,"near_plus_far_total":255.372},"near":{"active_cells":283161,"vertices":286734,"triangles":569817,"bytes":13720900,"hash":"dab38d009700f1f0","pages":64,"index_ranges":74,"page_border_vertices":15582,"ambiguous_faces":2854,"multi_cross_edges":536,"thin_detected":13,"thin_hidden_lattice":10,"thin_meshed":10,"explicit_thin_triangles":1993},"interval_stats":{"x":{"lines":67081,"candidate_total":187058,"candidate_p50":0,"candidate_p95":17,"candidate_max":43,"interval_total":111999,"interval_p50":0,"interval_p95":11,"interval_max":40,"crossing_total":89658,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":199365,"candidate_p50":0,"candidate_p95":18,"candidate_max":44,"interval_total":120586,"interval_p50":0,"interval_p95":12,"interval_max":39,"crossing_total":104152,"crossing_p50":0,"crossing_p95":10,"crossing_max":22,"runs_p50":1,"runs_p95":11,"runs_max":19},"z":{"lines":67081,"candidate_total":204987,"candidate_p50":1,"candidate_p95":13,"candidate_max":56,"interval_total":124121,"interval_p50":0,"interval_p95":10,"interval_max":48,"crossing_total":112170,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"0293ad0bb4aab1f8"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":10,"audit_thin_crossing_events":2},"output_bytes_total":20033660,"combined_hash":"17a0d35b9c500445","working_set_bytes":45731840,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":3,"workers":1,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.508,"interval_solve_compose_and_sign_words_3_axes":155.158,"active_cell_masks":3.366,"near_mesh_emission":198.934,"far_field":8.589,"validation_outside_timing":264.253,"near_cpu_total":357.966,"near_plus_far_total":366.555},"near":{"active_cells":292580,"vertices":296660,"triangles":591512,"bytes":14219284,"hash":"600f405efd49ce29","pages":64,"index_ranges":65,"page_border_vertices":15757,"ambiguous_faces":7772,"multi_cross_edges":264,"thin_detected":13,"thin_hidden_lattice":1,"thin_meshed":1,"explicit_thin_triangles":288},"interval_stats":{"x":{"lines":67081,"candidate_total":190357,"candidate_p50":0,"candidate_p95":16,"candidate_max":42,"interval_total":112011,"interval_p50":0,"interval_p95":11,"interval_max":37,"crossing_total":95212,"crossing_p50":0,"crossing_p95":10,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":194781,"candidate_p50":0,"candidate_p95":17,"candidate_max":46,"interval_total":112410,"interval_p50":0,"interval_p95":12,"interval_max":40,"crossing_total":93860,"crossing_p50":0,"crossing_p95":10,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":215639,"candidate_p50":1,"candidate_p95":13,"candidate_max":57,"interval_total":124874,"interval_p50":0,"interval_p95":9,"interval_max":49,"crossing_total":118734,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"dbc331a49aa1719e"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":1,"audit_thin_crossing_events":2},"output_bytes_total":20532044,"combined_hash":"c74544dc1b78ce7b","working_set_bytes":45920256,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":3,"workers":6,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.518,"interval_solve_compose_and_sign_words_3_axes":51.474,"active_cell_masks":2.204,"near_mesh_emission":203.409,"far_field":8.579,"validation_outside_timing":247.255,"near_cpu_total":257.606,"near_plus_far_total":266.185},"near":{"active_cells":292580,"vertices":296660,"triangles":591512,"bytes":14219284,"hash":"600f405efd49ce29","pages":64,"index_ranges":65,"page_border_vertices":15757,"ambiguous_faces":7772,"multi_cross_edges":264,"thin_detected":13,"thin_hidden_lattice":1,"thin_meshed":1,"explicit_thin_triangles":288},"interval_stats":{"x":{"lines":67081,"candidate_total":190357,"candidate_p50":0,"candidate_p95":16,"candidate_max":42,"interval_total":112011,"interval_p50":0,"interval_p95":11,"interval_max":37,"crossing_total":95212,"crossing_p50":0,"crossing_p95":10,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":194781,"candidate_p50":0,"candidate_p95":17,"candidate_max":46,"interval_total":112410,"interval_p50":0,"interval_p95":12,"interval_max":40,"crossing_total":93860,"crossing_p50":0,"crossing_p95":10,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":215639,"candidate_p50":1,"candidate_p95":13,"candidate_max":57,"interval_total":124874,"interval_p50":0,"interval_p95":9,"interval_max":49,"crossing_total":118734,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"dbc331a49aa1719e"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":1,"audit_thin_crossing_events":2},"output_bytes_total":20532044,"combined_hash":"c74544dc1b78ce7b","working_set_bytes":46493696,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":3,"workers":8,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.499,"interval_solve_compose_and_sign_words_3_axes":43.016,"active_cell_masks":2.446,"near_mesh_emission":240.567,"far_field":8.808,"validation_outside_timing":267.854,"near_cpu_total":286.528,"near_plus_far_total":295.336},"near":{"active_cells":292580,"vertices":296660,"triangles":591512,"bytes":14219284,"hash":"600f405efd49ce29","pages":64,"index_ranges":65,"page_border_vertices":15757,"ambiguous_faces":7772,"multi_cross_edges":264,"thin_detected":13,"thin_hidden_lattice":1,"thin_meshed":1,"explicit_thin_triangles":288},"interval_stats":{"x":{"lines":67081,"candidate_total":190357,"candidate_p50":0,"candidate_p95":16,"candidate_max":42,"interval_total":112011,"interval_p50":0,"interval_p95":11,"interval_max":37,"crossing_total":95212,"crossing_p50":0,"crossing_p95":10,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":21},"y":{"lines":67081,"candidate_total":194781,"candidate_p50":0,"candidate_p95":17,"candidate_max":46,"interval_total":112410,"interval_p50":0,"interval_p95":12,"interval_max":40,"crossing_total":93860,"crossing_p50":0,"crossing_p95":10,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":215639,"candidate_p50":1,"candidate_p95":13,"candidate_max":57,"interval_total":124874,"interval_p50":0,"interval_p95":9,"interval_max":49,"crossing_total":118734,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"dbc331a49aa1719e"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":1,"audit_thin_crossing_events":2},"output_bytes_total":20532044,"combined_hash":"c74544dc1b78ce7b","working_set_bytes":46301184,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":4,"workers":1,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.485,"interval_solve_compose_and_sign_words_3_axes":153.462,"active_cell_masks":2.555,"near_mesh_emission":189.354,"far_field":8.573,"validation_outside_timing":242.167,"near_cpu_total":345.856,"near_plus_far_total":354.429},"near":{"active_cells":279309,"vertices":282404,"triangles":563052,"bytes":13535700,"hash":"4ed64f01c5df738b","pages":64,"index_ranges":69,"page_border_vertices":13975,"ambiguous_faces":3838,"multi_cross_edges":680,"thin_detected":13,"thin_hidden_lattice":5,"thin_meshed":5,"explicit_thin_triangles":1120},"interval_stats":{"x":{"lines":67081,"candidate_total":181609,"candidate_p50":0,"candidate_p95":16,"candidate_max":41,"interval_total":109240,"interval_p50":0,"interval_p95":11,"interval_max":37,"crossing_total":88640,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":17},"y":{"lines":67081,"candidate_total":191723,"candidate_p50":0,"candidate_p95":17,"candidate_max":42,"interval_total":114274,"interval_p50":0,"interval_p95":12,"interval_max":39,"crossing_total":98854,"crossing_p50":0,"crossing_p95":10,"crossing_max":24,"runs_p50":1,"runs_p95":9,"runs_max":23},"z":{"lines":67081,"candidate_total":201505,"candidate_p50":0,"candidate_p95":13,"candidate_max":56,"interval_total":124143,"interval_p50":0,"interval_p95":10,"interval_max":50,"crossing_total":113480,"crossing_p50":0,"crossing_p95":8,"crossing_max":22,"runs_p50":1,"runs_p95":9,"runs_max":17}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"75f729c8e51da37e"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":5,"audit_thin_crossing_events":2},"output_bytes_total":19848460,"combined_hash":"69cef91397829b57","working_set_bytes":45002752,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":4,"workers":6,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.478,"interval_solve_compose_and_sign_words_3_axes":51.348,"active_cell_masks":2.415,"near_mesh_emission":187.195,"far_field":8.576,"validation_outside_timing":243.163,"near_cpu_total":241.436,"near_plus_far_total":250.012},"near":{"active_cells":279309,"vertices":282404,"triangles":563052,"bytes":13535700,"hash":"4ed64f01c5df738b","pages":64,"index_ranges":69,"page_border_vertices":13975,"ambiguous_faces":3838,"multi_cross_edges":680,"thin_detected":13,"thin_hidden_lattice":5,"thin_meshed":5,"explicit_thin_triangles":1120},"interval_stats":{"x":{"lines":67081,"candidate_total":181609,"candidate_p50":0,"candidate_p95":16,"candidate_max":41,"interval_total":109240,"interval_p50":0,"interval_p95":11,"interval_max":37,"crossing_total":88640,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":17},"y":{"lines":67081,"candidate_total":191723,"candidate_p50":0,"candidate_p95":17,"candidate_max":42,"interval_total":114274,"interval_p50":0,"interval_p95":12,"interval_max":39,"crossing_total":98854,"crossing_p50":0,"crossing_p95":10,"crossing_max":24,"runs_p50":1,"runs_p95":9,"runs_max":23},"z":{"lines":67081,"candidate_total":201505,"candidate_p50":0,"candidate_p95":13,"candidate_max":56,"interval_total":124143,"interval_p50":0,"interval_p95":10,"interval_max":50,"crossing_total":113480,"crossing_p50":0,"crossing_p95":8,"crossing_max":22,"runs_p50":1,"runs_p95":9,"runs_max":17}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"75f729c8e51da37e"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":5,"audit_thin_crossing_events":2},"output_bytes_total":19848460,"combined_hash":"69cef91397829b57","working_set_bytes":45342720,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":4,"workers":8,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.485,"interval_solve_compose_and_sign_words_3_axes":40.148,"active_cell_masks":2.266,"near_mesh_emission":188.109,"far_field":8.564,"validation_outside_timing":242.086,"near_cpu_total":231.009,"near_plus_far_total":239.573},"near":{"active_cells":279309,"vertices":282404,"triangles":563052,"bytes":13535700,"hash":"4ed64f01c5df738b","pages":64,"index_ranges":69,"page_border_vertices":13975,"ambiguous_faces":3838,"multi_cross_edges":680,"thin_detected":13,"thin_hidden_lattice":5,"thin_meshed":5,"explicit_thin_triangles":1120},"interval_stats":{"x":{"lines":67081,"candidate_total":181609,"candidate_p50":0,"candidate_p95":16,"candidate_max":41,"interval_total":109240,"interval_p50":0,"interval_p95":11,"interval_max":37,"crossing_total":88640,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":17},"y":{"lines":67081,"candidate_total":191723,"candidate_p50":0,"candidate_p95":17,"candidate_max":42,"interval_total":114274,"interval_p50":0,"interval_p95":12,"interval_max":39,"crossing_total":98854,"crossing_p50":0,"crossing_p95":10,"crossing_max":24,"runs_p50":1,"runs_p95":9,"runs_max":23},"z":{"lines":67081,"candidate_total":201505,"candidate_p50":0,"candidate_p95":13,"candidate_max":56,"interval_total":124143,"interval_p50":0,"interval_p95":10,"interval_max":50,"crossing_total":113480,"crossing_p50":0,"crossing_p95":8,"crossing_max":22,"runs_p50":1,"runs_p95":9,"runs_max":17}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"75f729c8e51da37e"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":5,"audit_thin_crossing_events":2},"output_bytes_total":19848460,"combined_hash":"69cef91397829b57","working_set_bytes":45486080,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":5,"workers":1,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.470,"interval_solve_compose_and_sign_words_3_axes":172.633,"active_cell_masks":3.120,"near_mesh_emission":184.374,"far_field":8.605,"validation_outside_timing":244.536,"near_cpu_total":360.596,"near_plus_far_total":369.201},"near":{"active_cells":269847,"vertices":272704,"triangles":543828,"bytes":13072232,"hash":"885750d27c475ba9","pages":64,"index_ranges":70,"page_border_vertices":12496,"ambiguous_faces":2638,"multi_cross_edges":976,"thin_detected":13,"thin_hidden_lattice":6,"thin_meshed":6,"explicit_thin_triangles":1282},"interval_stats":{"x":{"lines":67081,"candidate_total":186280,"candidate_p50":0,"candidate_p95":17,"candidate_max":44,"interval_total":111929,"interval_p50":0,"interval_p95":11,"interval_max":38,"crossing_total":89242,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"y":{"lines":67081,"candidate_total":192484,"candidate_p50":0,"candidate_p95":17,"candidate_max":44,"interval_total":111223,"interval_p50":0,"interval_p95":12,"interval_max":36,"crossing_total":90196,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":214972,"candidate_p50":1,"candidate_p95":13,"candidate_max":56,"interval_total":122709,"interval_p50":0,"interval_p95":9,"interval_max":49,"crossing_total":111168,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"c100350769615273"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":6,"audit_thin_crossing_events":2},"output_bytes_total":19384992,"combined_hash":"afb5e5e8fa54b8a9","working_set_bytes":44163072,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":5,"workers":6,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.468,"interval_solve_compose_and_sign_words_3_axes":52.449,"active_cell_masks":2.217,"near_mesh_emission":184.168,"far_field":8.708,"validation_outside_timing":248.390,"near_cpu_total":239.302,"near_plus_far_total":248.010},"near":{"active_cells":269847,"vertices":272704,"triangles":543828,"bytes":13072232,"hash":"885750d27c475ba9","pages":64,"index_ranges":70,"page_border_vertices":12496,"ambiguous_faces":2638,"multi_cross_edges":976,"thin_detected":13,"thin_hidden_lattice":6,"thin_meshed":6,"explicit_thin_triangles":1282},"interval_stats":{"x":{"lines":67081,"candidate_total":186280,"candidate_p50":0,"candidate_p95":17,"candidate_max":44,"interval_total":111929,"interval_p50":0,"interval_p95":11,"interval_max":38,"crossing_total":89242,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"y":{"lines":67081,"candidate_total":192484,"candidate_p50":0,"candidate_p95":17,"candidate_max":44,"interval_total":111223,"interval_p50":0,"interval_p95":12,"interval_max":36,"crossing_total":90196,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":214972,"candidate_p50":1,"candidate_p95":13,"candidate_max":56,"interval_total":122709,"interval_p50":0,"interval_p95":9,"interval_max":49,"crossing_total":111168,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"c100350769615273"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":6,"audit_thin_crossing_events":2},"output_bytes_total":19384992,"combined_hash":"afb5e5e8fa54b8a9","working_set_bytes":44736512,"validation_pass":true}
@@ -0,0 +1 @@
{"seed":5,"workers":8,"shape_count":372,"near_cells":16777216,"near_nodes":17373979,"membership_limit":64,"overflow":false,"ms":{"corpus_hierarchy":0.471,"interval_solve_compose_and_sign_words_3_axes":43.064,"active_cell_masks":1.906,"near_mesh_emission":195.702,"far_field":8.607,"validation_outside_timing":264.699,"near_cpu_total":241.142,"near_plus_far_total":249.750},"near":{"active_cells":269847,"vertices":272704,"triangles":543828,"bytes":13072232,"hash":"885750d27c475ba9","pages":64,"index_ranges":70,"page_border_vertices":12496,"ambiguous_faces":2638,"multi_cross_edges":976,"thin_detected":13,"thin_hidden_lattice":6,"thin_meshed":6,"explicit_thin_triangles":1282},"interval_stats":{"x":{"lines":67081,"candidate_total":186280,"candidate_p50":0,"candidate_p95":17,"candidate_max":44,"interval_total":111929,"interval_p50":0,"interval_p95":11,"interval_max":38,"crossing_total":89242,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19},"y":{"lines":67081,"candidate_total":192484,"candidate_p50":0,"candidate_p95":17,"candidate_max":44,"interval_total":111223,"interval_p50":0,"interval_p95":12,"interval_max":36,"crossing_total":90196,"crossing_p50":0,"crossing_p95":8,"crossing_max":20,"runs_p50":1,"runs_p95":9,"runs_max":19},"z":{"lines":67081,"candidate_total":214972,"candidate_p50":1,"candidate_p95":13,"candidate_max":56,"interval_total":122709,"interval_p50":0,"interval_p95":9,"interval_max":49,"crossing_total":111168,"crossing_p50":0,"crossing_p95":8,"crossing_max":18,"runs_p50":1,"runs_p95":9,"runs_max":19}},"far":{"nodes":47040,"patches":92150,"vertices":94090,"triangles":184300,"bytes":6312760,"descendants":0,"lod_nodes":[240,880,3680,15040,11360,15840],"hash":"c100350769615273"},"validation":{"signs_ok":true,"mismatches":0,"negative_coordinates":true,"page_ownership_and_shared_edges":true,"tangency":true,"coincident_edits":true,"thin_surface_present":true,"omit_thin_surface_rejected":true,"thin_hidden_in_lattice":6,"audit_thin_crossing_events":2},"output_bytes_total":19384992,"combined_hash":"afb5e5e8fa54b8a9","working_set_bytes":44937216,"validation_pass":true}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,138 @@
#define main interval_spike_benchmark_main
#include "../interval_spike.cpp"
#undef main
#include <d3d11.h>
#include <d3dcompiler.h>
#include <dxgi.h>
#include <wincodec.h>
#include <windowsx.h>
#include <shellapi.h>
#include <memory>
#include <string>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "windowscodecs.lib")
#pragma comment(lib, "ole32.lib")
namespace {
struct V3 { float x,y,z; };
V3 add(V3 a,V3 b){return {a.x+b.x,a.y+b.y,a.z+b.z};}
V3 sub(V3 a,V3 b){return {a.x-b.x,a.y-b.y,a.z-b.z};}
V3 mul(V3 a,float s){return {a.x*s,a.y*s,a.z*s};}
float dot(V3 a,V3 b){return a.x*b.x+a.y*b.y+a.z*b.z;}
V3 cross(V3 a,V3 b){return {a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x};}
V3 norm(V3 a){float n=std::sqrt(std::max(1e-20f,dot(a,a)));return mul(a,1.0f/n);}
struct M4 { float m[4][4]{}; };
M4 identity(){M4 a{};for(int i=0;i<4;++i)a.m[i][i]=1;return a;}
M4 perspective(float fov,float aspect,float zn,float zf){M4 a{};float y=1/std::tan(fov*.5f);a.m[0][0]=y/aspect;a.m[1][1]=y;a.m[2][2]=zf/(zf-zn);a.m[2][3]=1;a.m[3][2]=-zn*zf/(zf-zn);return a;}
M4 view_matrix(V3 p,V3 f,V3 r,V3 u){M4 a=identity();a.m[0][0]=r.x;a.m[1][0]=r.y;a.m[2][0]=r.z;a.m[3][0]=-dot(r,p);a.m[0][1]=u.x;a.m[1][1]=u.y;a.m[2][1]=u.z;a.m[3][1]=-dot(u,p);a.m[0][2]=f.x;a.m[1][2]=f.y;a.m[2][2]=f.z;a.m[3][2]=-dot(f,p);return a;}
M4 transpose(M4 a){M4 b{};for(int i=0;i<4;++i)for(int j=0;j<4;++j)b.m[i][j]=a.m[j][i];return b;}
struct GPUVertex { float p[3],n[3]; };
struct CB { M4 viewProj; float camera[3],pad; float light[3],fogStart; float fogEnd,ambient,pad2[2]; };
struct App {
HWND hwnd{}; ID3D11Device* dev{}; ID3D11DeviceContext* ctx{}; IDXGISwapChain* swap{}; ID3D11RenderTargetView* rtv{}; ID3D11DepthStencilView* dsv{};
ID3D11VertexShader* vs{}; ID3D11PixelShader* ps{}; ID3D11InputLayout* layout{}; ID3D11Buffer* cb{}; ID3D11RasterizerState* solid{}; ID3D11RasterizerState* wire{};
ID3D11Buffer* nearVB{}; ID3D11Buffer* nearReliefVB{}; ID3D11Buffer* nearIB{}; ID3D11Buffer* farVB{}; ID3D11Buffer* farIB{}; UINT nearIndices{},farIndices{};
uint64_t seed=1;int workers=6;double generationMs{};bool showNear=true,showFar=true,wireframe=false,captured=false,showDisplacement=true,captureFailed=false;float yaw=0,pitch=0,speed=8;V3 pos{};POINT last{};int width=1280,height=800;Clock::time_point prev=Clock::now();double frameMs=0;bool keys[256]{};std::filesystem::path captureTarget;
void release_mesh(){if(nearVB)nearVB->Release();if(nearReliefVB)nearReliefVB->Release();if(nearIB)nearIB->Release();if(farVB)farVB->Release();if(farIB)farIB->Release();nearVB=nearReliefVB=nearIB=farVB=farIB=nullptr;nearIndices=farIndices=0;}
~App(){release_mesh();if(solid)solid->Release();if(wire)wire->Release();if(cb)cb->Release();if(layout)layout->Release();if(vs)vs->Release();if(ps)ps->Release();if(rtv)rtv->Release();if(dsv)dsv->Release();if(swap)swap->Release();if(ctx)ctx->Release();if(dev)dev->Release();}
bool make_buffer(ID3D11Buffer** out,const void* data,size_t bytes,UINT bind){D3D11_BUFFER_DESC d{};d.ByteWidth=static_cast<UINT>(bytes);d.Usage=D3D11_USAGE_DEFAULT;d.BindFlags=bind;D3D11_SUBRESOURCE_DATA s{};s.pSysMem=data;return SUCCEEDED(dev->CreateBuffer(&d,&s,out));}
bool generate(){
auto begin=Clock::now(); Corpus corpus;build_corpus(corpus,seed);AxisData axes[3];AxisStats stats[3];bool overflow=false;
for(int a=0;a<3;++a)axes[a]=solve_axis(corpus,a,workers,stats[a],overflow);
BlendStats blendStats{};apply_blend_zones(corpus,axes,stats,blendStats);
std::vector<uint64_t> active;active_cell_masks(axes[2],active);MeshOutput nearMesh=emit_dual_contour_mesh(corpus,axes,axes[2],active);FarOutput farMesh=build_far_world(seed);
std::vector<Vertex> reliefVertices;double maxDisplacement=0;apply_surface_displacement(nearMesh,reliefVertices,maxDisplacement);
generationMs=ms(begin,Clock::now());release_mesh();
std::vector<GPUVertex> nv;nv.reserve(nearMesh.vertices.size());for(const auto& v:nearMesh.vertices)nv.push_back({{v.v.x,v.v.y,v.v.z},{v.v.nx,v.v.ny,v.v.nz}});
std::vector<GPUVertex> rv;rv.reserve(reliefVertices.size());for(const auto& v:reliefVertices)rv.push_back({{v.x,v.y,v.z},{v.nx,v.ny,v.nz}});
std::vector<GPUVertex> fv;fv.reserve(farMesh.vertices.size());for(const auto& v:farMesh.vertices)fv.push_back({{v.x,v.y,v.z},{v.nx,v.ny,v.nz}});
if(!nv.empty()&&(!make_buffer(&nearVB,nv.data(),nv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&nearReliefVB,rv.data(),rv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&nearIB,nearMesh.indices.data(),nearMesh.indices.size()*sizeof(uint32_t),D3D11_BIND_INDEX_BUFFER)))return false;
if(!fv.empty()&&(!make_buffer(&farVB,fv.data(),fv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&farIB,farMesh.indices.data(),farMesh.indices.size()*sizeof(uint32_t),D3D11_BIND_INDEX_BUFFER)))return false;
nearIndices=static_cast<UINT>(nearMesh.indices.size());farIndices=static_cast<UINT>(farMesh.indices.size());
// The first generated room is centred near local (-13, -13, -8.5) metres.
// These offsets match its deterministic seed-1 center and keep the camera in its air volume.
Rng rng(seed ^ 0x0A57A20261002ull);int lx=-1300+rng.range(-170,170),ly=-1300+rng.range(-170,170),lz=-850+rng.range(-120,120);
pos={kWorldOriginCm[0]*.01f+lx*.01f,kWorldOriginCm[1]*.01f+ly*.01f,kWorldOriginCm[2]*.01f+lz*.01f};yaw=0;pitch=0;
return true;
}
bool init_device(){
DXGI_SWAP_CHAIN_DESC sd{};sd.BufferCount=1;sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;sd.OutputWindow=hwnd;sd.SampleDesc.Count=1;sd.Windowed=TRUE;sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
UINT flags=0;
#if defined(_DEBUG)
flags|=D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_FEATURE_LEVEL fl{};if(FAILED(D3D11CreateDeviceAndSwapChain(nullptr,D3D_DRIVER_TYPE_HARDWARE,nullptr,flags,nullptr,0,D3D11_SDK_VERSION,&sd,&swap,&dev,&fl,&ctx)))return false;
const char* shader="cbuffer C:register(b0){float4x4 VP;float3 Camera;float Pad;float3 LightDir;float FogStart;float FogEnd;float Ambient;float2 Pad2;} struct I{float3 p:POSITION;float3 n:NORMAL;};struct O{float4 p:SV_POSITION;float3 world:TEXCOORD0;float3 n:NORMAL;};O VS(I i){O o;o.p=mul(float4(i.p,1),VP);o.world=i.p;o.n=i.n;return o;}float hash3(float3 p){return frac(sin(dot(p,float3(127.1,311.7,74.7)))*43758.5453);}float noise3(float3 p){float3 i=floor(p),f=frac(p);f=f*f*(3-2*f);float a=lerp(hash3(i),hash3(i+float3(1,0,0)),f.x);float b=lerp(hash3(i+float3(0,1,0)),hash3(i+float3(1,1,0)),f.x);float c=lerp(hash3(i+float3(0,0,1)),hash3(i+float3(1,0,1)),f.x);float d=lerp(hash3(i+float3(0,1,1)),hash3(i+float3(1,1,1)),f.x);return lerp(lerp(a,b,f.y),lerp(c,d,f.y),f.z);}float4 PS(O i):SV_TARGET{float3 N=normalize(i.n);float3 L=normalize(-LightDir);float diffuse=saturate(dot(N,L))*0.72;float3 toCam=Camera-i.world;float dist=length(toCam);float head=saturate(dot(N,normalize(toCam)))*0.85/(1+dist*0.018);float light=Ambient+diffuse+head;float mottling=0.68*noise3(i.world*0.28)+0.32*noise3(i.world*1.0);float shade=0.45+mottling;float3 rock=float3(0.46,0.39,0.30)*shade*light;float fog=saturate((dist-FogStart)/(FogEnd-FogStart));return float4(lerp(rock,float3(0.075,0.095,0.12),fog),1);} ";
ID3DBlob *vb=nullptr,*pb=nullptr,*err=nullptr;if(FAILED(D3DCompile(shader,strlen(shader),nullptr,nullptr,nullptr,"VS","vs_4_0",0,0,&vb,&err)))return false;if(FAILED(D3DCompile(shader,strlen(shader),nullptr,nullptr,nullptr,"PS","ps_4_0",0,0,&pb,&err))){if(vb)vb->Release();return false;}
HRESULT hr=dev->CreateVertexShader(vb->GetBufferPointer(),vb->GetBufferSize(),nullptr,&vs);if(SUCCEEDED(hr))hr=dev->CreatePixelShader(pb->GetBufferPointer(),pb->GetBufferSize(),nullptr,&ps);
D3D11_INPUT_ELEMENT_DESC il[]={{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},{"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}};if(SUCCEEDED(hr))hr=dev->CreateInputLayout(il,2,vb->GetBufferPointer(),vb->GetBufferSize(),&layout);vb->Release();pb->Release();if(FAILED(hr))return false;
D3D11_BUFFER_DESC cbd{};cbd.ByteWidth=sizeof(CB);cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;if(FAILED(dev->CreateBuffer(&cbd,nullptr,&cb)))return false;
D3D11_RASTERIZER_DESC rd{};rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;rd.DepthClipEnable=TRUE;if(FAILED(dev->CreateRasterizerState(&rd,&solid)))return false;rd.FillMode=D3D11_FILL_WIREFRAME;if(FAILED(dev->CreateRasterizerState(&rd,&wire)))return false;
return resize(width,height);
}
bool resize(int w,int h){if(!dev||w<1||h<1)return true;width=w;height=h;if(rtv){rtv->Release();rtv=nullptr;}if(dsv){dsv->Release();dsv=nullptr;}if(FAILED(swap->ResizeBuffers(0,w,h,DXGI_FORMAT_UNKNOWN,0)))return false;ID3D11Texture2D* back{};if(FAILED(swap->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&back))))return false;HRESULT hr=dev->CreateRenderTargetView(back,nullptr,&rtv);back->Release();if(FAILED(hr))return false;D3D11_TEXTURE2D_DESC dd{};dd.Width=w;dd.Height=h;dd.MipLevels=1;dd.ArraySize=1;dd.Format=DXGI_FORMAT_D24_UNORM_S8_UINT;dd.SampleDesc.Count=1;dd.BindFlags=D3D11_BIND_DEPTH_STENCIL;ID3D11Texture2D* depth{};if(FAILED(dev->CreateTexture2D(&dd,nullptr,&depth)))return false;hr=dev->CreateDepthStencilView(depth,nullptr,&dsv);depth->Release();return SUCCEEDED(hr);}
bool save_backbuffer_png(const std::filesystem::path& path){
ID3D11Texture2D* back{};ID3D11Texture2D* staging{};IWICImagingFactory* factory{};IWICStream* stream{};IWICBitmapEncoder* encoder{};IWICBitmapFrameEncode* frame{};IPropertyBag2* props{};D3D11_TEXTURE2D_DESC desc{};D3D11_MAPPED_SUBRESOURCE mapped{};bool ok=false;HRESULT hr=S_OK;const char* stage="get swapchain buffer";
if(FAILED(hr=swap->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&back))))goto done;
back->GetDesc(&desc);desc.Usage=D3D11_USAGE_STAGING;desc.BindFlags=0;desc.CPUAccessFlags=D3D11_CPU_ACCESS_READ;desc.MiscFlags=0;
stage="create staging texture";if(FAILED(hr=dev->CreateTexture2D(&desc,nullptr,&staging)))goto done;
ctx->CopyResource(staging,back);
stage="map staging texture";if(FAILED(hr=ctx->Map(staging,0,D3D11_MAP_READ,0,&mapped)))goto done;
{
const UINT stride=desc.Width*4;std::vector<BYTE> pixels(static_cast<size_t>(stride)*desc.Height);
for(UINT y=0;y<desc.Height;++y){const BYTE* src=static_cast<const BYTE*>(mapped.pData)+static_cast<size_t>(y)*mapped.RowPitch;BYTE* dst=pixels.data()+static_cast<size_t>(y)*stride;for(UINT x=0;x<desc.Width;++x){dst[x*4]=src[x*4+2];dst[x*4+1]=src[x*4+1];dst[x*4+2]=src[x*4];dst[x*4+3]=src[x*4+3];}}
ctx->Unmap(staging,0);
stage="create WIC factory";if(FAILED(hr=CoCreateInstance(CLSID_WICImagingFactory,nullptr,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&factory))))goto done;
stage="create WIC stream";if(FAILED(hr=factory->CreateStream(&stream)))goto done;
stage="open PNG path";if(FAILED(hr=stream->InitializeFromFilename(path.c_str(),GENERIC_WRITE)))goto done;
stage="create PNG encoder";if(FAILED(hr=factory->CreateEncoder(GUID_ContainerFormatPng,nullptr,&encoder)))goto done;
stage="initialize PNG encoder";if(FAILED(hr=encoder->Initialize(stream,WICBitmapEncoderNoCache)))goto done;
stage="create PNG frame";if(FAILED(hr=encoder->CreateNewFrame(&frame,&props)))goto done;
stage="initialize PNG frame";if(FAILED(hr=frame->Initialize(props)))goto done;
stage="set PNG size";if(FAILED(hr=frame->SetSize(desc.Width,desc.Height)))goto done;
{WICPixelFormatGUID format=GUID_WICPixelFormat32bppBGRA;stage="set PNG pixel format";hr=frame->SetPixelFormat(&format);if(FAILED(hr)||format!=GUID_WICPixelFormat32bppBGRA){if(SUCCEEDED(hr))hr=WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT;goto done;}}
stage="write PNG pixels";if(FAILED(hr=frame->WritePixels(desc.Height,stride,static_cast<UINT>(pixels.size()),pixels.data())))goto done;
stage="commit PNG";if(FAILED(hr=frame->Commit())||FAILED(hr=encoder->Commit()))goto done;ok=true;
}
done:
if(!ok)std::fprintf(stderr,"Capture failed at %s (HRESULT 0x%08lx)\n",stage,static_cast<unsigned long>(hr));
if(staging)staging->Release();if(back)back->Release();if(frame)frame->Release();if(props)props->Release();if(encoder)encoder->Release();if(stream)stream->Release();if(factory)factory->Release();return ok;
}
V3 forward(){return norm({std::sin(yaw)*std::cos(pitch),std::cos(yaw)*std::cos(pitch),std::sin(pitch)});}
void title(){char b[256];snprintf(b,sizeof(b),"Interval spike | %.2f ms/frame | %u tris | relief %s | (%.1f, %.1f, %.1f) m | gen %.1f ms | seed %llu",frameMs,((showNear?nearIndices:0)+(showFar?farIndices:0))/3,showDisplacement?"ON":"OFF",pos.x,pos.y,pos.z,generationMs,(unsigned long long)seed);SetWindowTextA(hwnd,b);}
void draw(){auto now=Clock::now();float dt=std::clamp(std::chrono::duration<float>(now-prev).count(),0.0f,.1f);prev=now;frameMs=dt*1000;
V3 f=forward(),r=norm(cross(f,{0,0,1})),u=norm(cross(r,f));float step=speed*dt*(keys[VK_SHIFT]?4.0f:1.0f);if(keys['W'])pos=add(pos,mul(f,step));if(keys['S'])pos=sub(pos,mul(f,step));if(keys['A'])pos=sub(pos,mul(r,step));if(keys['D'])pos=add(pos,mul(r,step));if(keys[VK_SPACE]||keys['E'])pos.z+=step;if(keys[VK_CONTROL]||keys['Q'])pos.z-=step;
float clear[]={.075f,.095f,.12f,1};ctx->OMSetRenderTargets(1,&rtv,dsv);ctx->ClearRenderTargetView(rtv,clear);ctx->ClearDepthStencilView(dsv,D3D11_CLEAR_DEPTH,1,0);D3D11_VIEWPORT vp{0,0,(float)width,(float)height,0,1};ctx->RSSetViewports(1,&vp);ctx->RSSetState(wireframe?wire:solid);ctx->IASetInputLayout(layout);ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
CB c{};M4 v=view_matrix(pos,f,r,u);M4 p=perspective(1.0472f,(float)width/height,.05f,20000); // compose view and projection for row-vector shader
M4 out{};for(int i=0;i<4;++i)for(int j=0;j<4;++j)for(int k=0;k<4;++k)out.m[i][j]+=v.m[i][k]*p.m[k][j];c.viewProj=transpose(out);c.camera[0]=pos.x;c.camera[1]=pos.y;c.camera[2]=pos.z;c.light[0]=-.45f;c.light[1]=-.8f;c.light[2]=-.35f;c.fogStart=80;c.fogEnd=1200;c.ambient=.28f;ctx->UpdateSubresource(cb,0,nullptr,&c,0,0);ctx->VSSetConstantBuffers(0,1,&cb);ctx->PSSetConstantBuffers(0,1,&cb);ctx->VSSetShader(vs,nullptr,0);ctx->PSSetShader(ps,nullptr,0);
auto mesh=[&](ID3D11Buffer* vb,ID3D11Buffer* ib,UINT count){if(!vb||!ib||!count)return;UINT stride=sizeof(GPUVertex),offset=0;ctx->IASetVertexBuffers(0,1,&vb,&stride,&offset);ctx->IASetIndexBuffer(ib,DXGI_FORMAT_R32_UINT,0);ctx->DrawIndexed(count,0,0);};if(showNear)mesh(showDisplacement?nearReliefVB:nearVB,nearIB,nearIndices);if(showFar)mesh(farVB,farIB,farIndices);if(!captureTarget.empty()){if(save_backbuffer_png(captureTarget))std::printf("Captured %ls\n",captureTarget.c_str());else{std::fprintf(stderr,"Capture failed: %ls\n",captureTarget.c_str());captureFailed=true;}captureTarget.clear();}swap->Present(1,0);title();}
void capture(bool on){captured=on;if(on){SetCapture(hwnd);ShowCursor(FALSE);GetCursorPos(&last);POINT c{width/2,height/2};ClientToScreen(hwnd,&c);SetCursorPos(c.x,c.y);}else{ReleaseCapture();ShowCursor(TRUE);}}
};
App* g{};
LRESULT CALLBACK wndproc(HWND h,UINT m,WPARAM w,LPARAM l){if(!g)return DefWindowProc(h,m,w,l);switch(m){case WM_SIZE:if(w!=SIZE_MINIMIZED)g->resize(LOWORD(l),HIWORD(l));return 0;case WM_KEYDOWN:if(w<256)g->keys[w]=true;if(w==VK_ESCAPE){if(g->captured)g->capture(false);else DestroyWindow(h);}if(w=='F')g->wireframe=!g->wireframe;if(w=='R')g->showDisplacement=!g->showDisplacement;if(w=='N'||w=='P'){g->seed+=(w=='N'?1:static_cast<uint64_t>(-1));if(!g->generate())MessageBoxA(h,"Could not create mesh buffers.","Viewer error",MB_ICONERROR);}if(w=='M')g->showNear=!g->showNear;if(w=='B')g->showFar=!g->showFar;return 0;case WM_KEYUP:if(w<256)g->keys[w]=false;return 0;case WM_RBUTTONDOWN:g->capture(true);return 0;case WM_RBUTTONUP:g->capture(false);return 0;case WM_MOUSEMOVE:if(g->captured){POINT c{g->width/2,g->height/2};ClientToScreen(h,&c);POINT p{GET_X_LPARAM(l),GET_Y_LPARAM(l)};POINT sc=p;ClientToScreen(h,&sc);int dx=sc.x-c.x,dy=sc.y-c.y;if(dx||dy){g->yaw+=dx*.0035f;g->pitch=std::clamp(g->pitch-dy*.0035f,-1.55f,1.55f);SetCursorPos(c.x,c.y);}}return 0;case WM_MOUSEWHEEL:g->speed=std::clamp(g->speed*(GET_WHEEL_DELTA_WPARAM(w)>0?1.25f:.8f),.25f,1000.0f);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(h,m,w,l);}
}
int WINAPI WinMain(HINSTANCE instance,HINSTANCE,LPSTR,int){
int argc=0;LPWSTR* argv=CommandLineToArgvW(GetCommandLineW(),&argc);uint64_t seed=1;int workers=6,positional=0;std::filesystem::path captureDir;bool captureMode=false;
if(argv)for(int i=1;i<argc;++i){if(std::wcscmp(argv[i],L"--capture-dir")==0&&i+1<argc){captureDir=argv[++i];captureMode=true;}else if(std::wcscmp(argv[i],L"--seed")==0&&i+1<argc)seed=_wcstoui64(argv[++i],nullptr,10);else if(std::wcscmp(argv[i],L"--workers")==0&&i+1<argc)workers=std::clamp(_wtoi(argv[++i]),1,8);else if(positional==0){seed=_wcstoui64(argv[i],nullptr,10);++positional;}else if(positional==1){workers=std::clamp(_wtoi(argv[i]),1,8);++positional;}}
if(FAILED(CoInitializeEx(nullptr,COINIT_MULTITHREADED))){if(argv)LocalFree(argv);return 1;}
AllocConsole();FILE* stream{};freopen_s(&stream,"CONOUT$","w",stdout);std::printf("Controls: hold right mouse button to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel speed; F wireframe; R relief toggle; M near; B far; N/P next/previous seed; Esc quit.\n");
App app;g=&app;app.seed=seed;app.workers=workers;WNDCLASSA wc{};wc.lpfnWndProc=wndproc;wc.hInstance=instance;wc.lpszClassName="IntervalSpikeViewer2";wc.hCursor=LoadCursor(nullptr,IDC_ARROW);RegisterClassA(&wc);RECT rc{0,0,app.width,app.height};AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);app.hwnd=CreateWindowA(wc.lpszClassName,"Interval spike viewer 2",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,rc.right-rc.left,rc.bottom-rc.top,nullptr,nullptr,instance,nullptr);if(!app.hwnd||!app.init_device()){MessageBoxA(nullptr,"Direct3D 11 initialization failed.","Viewer error",MB_ICONERROR);CoUninitialize();if(argv)LocalFree(argv);return 1;}ShowWindow(app.hwnd,SW_SHOW);UpdateWindow(app.hwnd);if(!app.generate()){MessageBoxA(app.hwnd,"World generation or GPU buffer creation failed.","Viewer error",MB_ICONERROR);CoUninitialize();if(argv)LocalFree(argv);return 2;}
if(captureMode){
std::error_code ec;std::filesystem::create_directories(captureDir,ec);if(ec){std::fprintf(stderr,"Could not create capture directory: %ls\n",captureDir.c_str());CoUninitialize();if(argv)LocalFree(argv);return 3;}
auto capture=[&](const wchar_t* name){app.captureTarget=captureDir/name;app.draw();};
capture(L"chamber.png");
app.showDisplacement=false;capture(L"chamber_relief_off.png");app.showDisplacement=true;
app.pos={kWorldOriginCm[0]*.01f-2.5f,kWorldOriginCm[1]*.01f-.75f,kWorldOriginCm[2]*.01f-3.5f};app.yaw=0;app.pitch=0;capture(L"junction.png");
Rng rng(seed ^ 0x0A57A20261002ull);int lx=-1300+rng.range(-170,170),ly=-1300+rng.range(-170,170),lz=-850+rng.range(-120,120);
app.pos={kWorldOriginCm[0]*.01f+(lx+500)*.01f,kWorldOriginCm[1]*.01f+ly*.01f,kWorldOriginCm[2]*.01f+lz*.01f};app.yaw=1.5707963f;app.pitch=0;capture(L"wall.png");
const bool ok=!app.captureFailed;DestroyWindow(app.hwnd);g=nullptr;CoUninitialize();if(argv)LocalFree(argv);return ok?0:4;
}
if(argv)LocalFree(argv);MSG msg{};while(msg.message!=WM_QUIT){if(PeekMessage(&msg,nullptr,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}else app.draw();}CoUninitialize();return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

+31
View File
@@ -0,0 +1,31 @@
Machine: Ryzen 7 5800X3D, Windows, MSVC 2022, 2026-09-29.
Command from Saved/Research:
cmd /c '"E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul && cl /nologo /O2 /std:c++20 /EHsc /arch:AVX2 round2_probe.cpp /Fe:round2_probe.exe'
.\round2_probe.exe
First run:
full trial=0 scalar_ms=492.007 avx2_ms=101.294 speedup=4.857 mismatch=0
full trial=1 scalar_ms=496.238 avx2_ms=106.015 speedup=4.681 mismatch=0
full trial=2 scalar_ms=469.590 avx2_ms=92.511 speedup=5.076 mismatch=0
crater radius_vox=40 touched_bricks=714 tile_AABB_max=64 samples_AABB=531441
crater copy_ms=1.623 edit_ms=0.865 changed_lattice=94843 copy_bytes=33554432
Second run after dead-code cleanup (same evaluation code):
full trial=0 scalar_ms=430.286 avx2_ms=89.209 speedup=4.823 mismatch=0
full trial=1 scalar_ms=456.202 avx2_ms=107.464 speedup=4.245 mismatch=0
full trial=2 scalar_ms=447.117 avx2_ms=94.584 speedup=4.727 mismatch=0
crater radius_vox=40 touched_bricks=714 tile_AABB_max=64 samples_AABB=531441
crater copy_ms=1.422 edit_ms=0.914 changed_lattice=94843 copy_bytes=33554432
Third run after adding a scattered direct-query measurement (same field evaluation code):
full trial=0 scalar_ms=478.627 avx2_ms=85.763 speedup=5.581 mismatch=0
full trial=1 scalar_ms=471.015 avx2_ms=80.318 speedup=5.864 mismatch=0
full trial=2 scalar_ms=444.320 avx2_ms=82.728 speedup=5.371 mismatch=0
random_query trial=0 count=200000 ms=63.030 ns_per_query=315.1 checksum=-1501231
random_query trial=1 count=200000 ms=62.785 ns_per_query=313.9 checksum=-1501231
random_query trial=2 count=200000 ms=62.799 ns_per_query=314.0 checksum=-1501231
crater radius_vox=40 touched_bricks=714 tile_AABB_max=64 samples_AABB=531441
crater copy_ms=1.394 edit_ms=0.861 changed_lattice=94843 copy_bytes=33554432
Both MSVC builds succeeded. The vcvars script emitted a missing-vswhere.exe notice.
Synthetic workload; no Unreal build/launch or GPU benchmark.
+125
View File
@@ -0,0 +1,125 @@
// Standalone research probe. Synthetic operator composition inspired by VoxelForge;
// NOT extracted production code. Compile with MSVC /O2 /arch:AVX2 /std:c++20.
#include <immintrin.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <thread>
#include <vector>
constexpr int N=256, B=8, W=6;
using V=__m256i;
static std::vector<int16_t> scalarField(size_t(N)*N*N), vectorField(size_t(N)*N*N);
alignas(32) static int32_t fadeTable[256];
static inline uint32_t hashS(uint32_t x,uint32_t y,uint32_t z,uint32_t seed){
uint32_t h=seed^(x*0x9e3779b1u)^(y*0x85ebca77u)^(z*0xc2b2ae3du);
h^=h>>16; h*=0x7feb352du; h^=h>>15; h*=0x846ca68bu; return h^(h>>16);
}
static inline V hashV(V x,V y,V z,V seed){
V h=_mm256_xor_si256(seed,_mm256_xor_si256(_mm256_mullo_epi32(x,_mm256_set1_epi32(0x9e3779b1u)),
_mm256_xor_si256(_mm256_mullo_epi32(y,_mm256_set1_epi32(0x85ebca77u)),_mm256_mullo_epi32(z,_mm256_set1_epi32(0xc2b2ae3du)))));
h=_mm256_xor_si256(h,_mm256_srli_epi32(h,16)); h=_mm256_mullo_epi32(h,_mm256_set1_epi32(0x7feb352du));
h=_mm256_xor_si256(h,_mm256_srli_epi32(h,15)); h=_mm256_mullo_epi32(h,_mm256_set1_epi32(0x846ca68bu));
return _mm256_xor_si256(h,_mm256_srli_epi32(h,16));
}
static inline int lerpS(int a,int b,int t){return a+((b-a)*t>>8);}
static inline V lerpV(V a,V b,V t){return _mm256_add_epi32(a,_mm256_srai_epi32(_mm256_mullo_epi32(_mm256_sub_epi32(b,a),t),8));}
static inline int gradS(uint32_t h,int x,int y,int z){
h&=15;int u=(h&8)?y:x;int v=(h&12)==0?y:((h&13)==12?x:z);
return ((h&1)?-u:u)+((h&2)?-v:v);
}
static inline V selectV(V mask,V a,V b){return _mm256_blendv_epi8(b,a,mask);}
static inline V gradV(V h,V x,V y,V z){
V zero=_mm256_setzero_si256(); h=_mm256_and_si256(h,_mm256_set1_epi32(15));
V u=selectV(_mm256_cmpeq_epi32(_mm256_and_si256(h,_mm256_set1_epi32(8)),zero),x,y);
V isY=_mm256_cmpeq_epi32(_mm256_and_si256(h,_mm256_set1_epi32(12)),zero);
V isX=_mm256_cmpeq_epi32(_mm256_and_si256(h,_mm256_set1_epi32(13)),_mm256_set1_epi32(12));
V v=selectV(isY,y,selectV(isX,x,z));
V negU=_mm256_cmpeq_epi32(_mm256_and_si256(h,_mm256_set1_epi32(1)),_mm256_set1_epi32(1));
V negV=_mm256_cmpeq_epi32(_mm256_and_si256(h,_mm256_set1_epi32(2)),_mm256_set1_epi32(2));
return _mm256_add_epi32(_mm256_sub_epi32(_mm256_xor_si256(u,negU),negU),_mm256_sub_epi32(_mm256_xor_si256(v,negV),negV));
}
static inline int noiseS(int x,int y,int z,int shift,int seed){
int ix=x>>shift,iy=y>>shift,iz=z>>shift,fx=(x&((1<<shift)-1))<<(8-shift),fy=(y&((1<<shift)-1))<<(8-shift),fz=(z&((1<<shift)-1))<<(8-shift);
int ux=fadeTable[fx],uy=fadeTable[fy],uz=fadeTable[fz];
int yz[2]; for(int dz=0;dz<2;++dz){int yy[2];for(int dy=0;dy<2;++dy){
int a=gradS(hashS(ix,iy+dy,iz+dz,seed),fx,fy-dy*256,fz-dz*256);
int b=gradS(hashS(ix+1,iy+dy,iz+dz,seed),fx-256,fy-dy*256,fz-dz*256);
yy[dy]=lerpS(a,b,ux);}
yz[dz]=lerpS(yy[0],yy[1],uy);} return lerpS(yz[0],yz[1],uz)/4;
}
static inline V noiseV(V x,V y,V z,int shift,int seed){
V mask=_mm256_set1_epi32((1<<shift)-1), one=_mm256_set1_epi32(1);
V ix=_mm256_srai_epi32(x,shift),iy=_mm256_srai_epi32(y,shift),iz=_mm256_srai_epi32(z,shift);
V fx=_mm256_slli_epi32(_mm256_and_si256(x,mask),8-shift),fy=_mm256_slli_epi32(_mm256_and_si256(y,mask),8-shift),fz=_mm256_slli_epi32(_mm256_and_si256(z,mask),8-shift);
V ux=_mm256_i32gather_epi32(fadeTable,fx,4),uy=_mm256_i32gather_epi32(fadeTable,fy,4),uz=_mm256_i32gather_epi32(fadeTable,fz,4);
V yz[2]; for(int dz=0;dz<2;++dz){V yy[2];for(int dy=0;dy<2;++dy){
V y1=_mm256_add_epi32(iy,_mm256_set1_epi32(dy)),z1=_mm256_add_epi32(iz,_mm256_set1_epi32(dz));
V yyoff=_mm256_sub_epi32(fy,_mm256_set1_epi32(dy*256)),zzoff=_mm256_sub_epi32(fz,_mm256_set1_epi32(dz*256));
V a=gradV(hashV(ix,y1,z1,_mm256_set1_epi32(seed)),fx,yyoff,zzoff);
V b=gradV(hashV(_mm256_add_epi32(ix,one),y1,z1,_mm256_set1_epi32(seed)),_mm256_sub_epi32(fx,_mm256_set1_epi32(256)),yyoff,zzoff);
yy[dy]=lerpV(a,b,ux);}yz[dz]=lerpV(yy[0],yy[1],uy);}
V raw=lerpV(yz[0],yz[1],uz);V bias=_mm256_and_si256(_mm256_srai_epi32(raw,31),_mm256_set1_epi32(3));
return _mm256_srai_epi32(_mm256_add_epi32(raw,bias),2);
}
static inline int smoothMaxS(int a,int b){int h=std::max(0,64-std::abs(a-b));return std::max(a,b)+(h*h>>8);}
static inline V smoothMaxV(V a,V b){V d=_mm256_sub_epi32(a,b);V h=_mm256_max_epi32(_mm256_setzero_si256(),_mm256_sub_epi32(_mm256_set1_epi32(64),_mm256_abs_epi32(d)));
return _mm256_add_epi32(_mm256_max_epi32(a,b),_mm256_srai_epi32(_mm256_mullo_epi32(h,h),8));}
static inline V evalV(V x,V y,V z){
V c=_mm256_set1_epi32(1024), warp=_mm256_srai_epi32(noiseV(_mm256_add_epi32(x,c),_mm256_add_epi32(y,c),_mm256_add_epi32(z,c),4,17),4);
V nx=_mm256_add_epi32(_mm256_add_epi32(x,c),warp),ny=_mm256_sub_epi32(_mm256_add_epi32(y,c),warp),nz=_mm256_add_epi32(z,c);
V fb=_mm256_add_epi32(noiseV(nx,ny,nz,4,100),_mm256_add_epi32(_mm256_srai_epi32(noiseV(nx,ny,nz,3,101),1),_mm256_srai_epi32(noiseV(nx,ny,nz,2,102),2)));
V ridge=_mm256_sub_epi32(_mm256_set1_epi32(128),_mm256_abs_epi32(noiseV(nx,ny,nz,3,202)));
// Scalar signed division truncates toward zero; AVX2 shift rounds negative down.
// Correct sign before shift to make the two exact.
auto divPow2=[](V a,int shift){V bias=_mm256_and_si256(_mm256_srai_epi32(a,31),_mm256_set1_epi32((1<<shift)-1));return _mm256_srai_epi32(_mm256_add_epi32(a,bias),shift);};
// Exact truncation toward zero matches scalar division by powers of two.
V floor=_mm256_add_epi32(_mm256_sub_epi32(z,_mm256_set1_epi32(84)),divPow2(fb,2));
V ceil=_mm256_add_epi32(_mm256_sub_epi32(_mm256_set1_epi32(170),z),divPow2(ridge,2));
V stratum=_mm256_min_epi32(floor,ceil),dy=_mm256_sub_epi32(y,_mm256_set1_epi32(128)),dz=_mm256_sub_epi32(z,_mm256_set1_epi32(128));
V dy2=_mm256_mullo_epi32(dy,dy),dz2=_mm256_mullo_epi32(dz,dz),dx=_mm256_sub_epi32(x,_mm256_set1_epi32(128));
V passage=divPow2(_mm256_sub_epi32(_mm256_set1_epi32(18*18),_mm256_add_epi32(dy2,dz2)),3);
V room=divPow2(_mm256_sub_epi32(_mm256_set1_epi32(35*35),_mm256_add_epi32(_mm256_mullo_epi32(dx,dx),_mm256_add_epi32(dy2,dz2))),4);
return smoothMaxV(stratum,smoothMaxV(passage,room));
}
static inline int evalS(int x,int y,int z){
int warp=noiseS(x+1024,y+1024,z+1024,4,17)>>4; int nx=x+1024+warp,ny=y+1024-warp,nz=z+1024;
int fb=noiseS(nx,ny,nz,4,100)+(noiseS(nx,ny,nz,3,101)>>1)+(noiseS(nx,ny,nz,2,102)>>2);
int ridge=128-std::abs(noiseS(nx,ny,nz,3,202));
int stratum=std::min(z-84+fb/4,170-z+ridge/4);
int dy=y-128,dz=z-128,dx=x-128;
int passage=(18*18-dy*dy-dz*dz)/8,room=(35*35-dx*dx-dy*dy-dz*dz)/16;
return smoothMaxS(stratum,smoothMaxS(passage,room));
}
static inline size_t idx(int x,int y,int z){return(size_t(z)*N+y)*N+x;}
template<class F> static double ms(F&& f){auto t=std::chrono::steady_clock::now();f();return std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-t).count();}
template<class F> static void parallel(F fn){std::array<std::thread,W> threads;for(int k=0;k<W;++k)threads[k]=std::thread([=]{fn(k*N/W,(k+1)*N/W);});for(auto& t:threads)t.join();}
static void fillScalar(){parallel([](int a,int b){for(int z=a;z<b;++z)for(int y=0;y<N;++y)for(int x=0;x<N;++x)scalarField[idx(x,y,z)]=(int16_t)evalS(x,y,z);});}
static void fillVector(){parallel([](int a,int b){for(int z=a;z<b;++z)for(int y=0;y<N;++y)for(int x=0;x<N;x+=8){
V xx=_mm256_add_epi32(_mm256_set1_epi32(x),_mm256_setr_epi32(0,1,2,3,4,5,6,7)); V d=evalV(xx,_mm256_set1_epi32(y),_mm256_set1_epi32(z));
alignas(32) int32_t values[8];_mm256_store_si256((V*)values,d);for(int j=0;j<8;++j)vectorField[idx(x+j,y,z)]=(int16_t)values[j];}});}
static inline int dist2box(int a,int lo,int hi){int d=std::max({lo-a,0,a-hi});return d*d;}
int main(){
for(int t=0;t<256;++t){int64_t q=t;int64_t q2=q*q,q3=q2*q,q4=q3*q,q5=q4*q;
fadeTable[t]=int32_t((6*q5-15*q4*256+10*q3*256*256)/(256LL*256*256*256));}
for(int t=0;t<3;++t){double s=ms(fillScalar),v=ms(fillVector);size_t mismatch=0;for(size_t i=0;i<scalarField.size();++i)mismatch+=scalarField[i]!=vectorField[i];
std::printf("full trial=%d scalar_ms=%.3f avx2_ms=%.3f speedup=%.3f mismatch=%zu\n",t,s,v,s/v,mismatch);}
struct Point{int x,y,z;};std::vector<Point> points(200000);uint32_t rng=0x12345678;
for(auto& p:points){rng^=rng<<13;rng^=rng>>17;rng^=rng<<5;p.x=int(rng&255);
rng^=rng<<13;rng^=rng>>17;rng^=rng<<5;p.y=int(rng&255);
rng^=rng<<13;rng^=rng>>17;rng^=rng<<5;p.z=int(rng&255);}
for(int t=0;t<3;++t){int64_t checksum=0;double q=ms([&]{for(const auto& p:points)checksum+=evalS(p.x,p.y,p.z);});
std::printf("random_query trial=%d count=%zu ms=%.3f ns_per_query=%.1f checksum=%lld\n",t,points.size(),q,q*1e6/points.size(),(long long)checksum);}
// Radius 10m at 25cm grid, center on tile boundary. Count exact AABB touched bricks.
const int cx=128,cy=128,cz=128,r=40;int touched=0;for(int bz=0;bz<N/B;++bz)for(int by=0;by<N/B;++by)for(int bx=0;bx<N/B;++bx){
int x0=bx*B,x1=x0+B-1,y0=by*B,y1=y0+B-1,z0=bz*B,z1=z0+B-1;
if(dist2box(cx,x0,x1)+dist2box(cy,y0,y1)+dist2box(cz,z0,z1)<=r*r)++touched;}
std::printf("crater radius_vox=%d touched_bricks=%d tile_AABB_max=%d samples_AABB=%d\n",r,touched,64,81*81*81);
// One logical transaction: versioned edit, canonical max composition, copy-on-write buffer.
std::vector<int16_t> next(vectorField.size());double copy=ms([&]{next=vectorField;});size_t changed=0;double edit=ms([&]{parallel([&](int za,int zb){for(int z=std::max(za,cz-r);z<std::min(zb,cz+r+1);++z)for(int y=cy-r;y<=cy+r;++y)for(int x=cx-r;x<=cx+r;++x){
int dx=x-cx,dy=y-cy,dz=z-cz,q=r*r-dx*dx-dy*dy-dz*dz;
if(q>=0){int d=std::max<int>(vectorField[idx(x,y,z)],q/16);next[idx(x,y,z)]=(int16_t)d;}}});});
for(int z=cz-r;z<=cz+r;++z)for(int y=cy-r;y<=cy+r;++y)for(int x=cx-r;x<=cx+r;++x)changed+=next[idx(x,y,z)]!=vectorField[idx(x,y,z)];
std::printf("crater copy_ms=%.3f edit_ms=%.3f changed_lattice=%zu copy_bytes=%zu\n",copy,edit,changed,vectorField.size()*sizeof(int16_t));
}
@@ -0,0 +1,60 @@
# Step 2d launch ledger
All Unreal processes were launched through `Tools/VoxelForgeTest.ps1` and ran serially. The
guarded launches used below-normal priority, the shared persistent DDC where recorded,
`-MaxParallelActions=4`, a prelaunch 6 GiB memory check, an 8 GiB per-process limit, and a
30-minute timeout. The retained ledgers show **no crash reporters, no process above 8 GiB, and
no timeout**. Exit 255 below means automation assertions failed; it was not a native crash.
## Benchmark attempts
| Label | Build | Unreal process | Result |
|---|---|---|---|
| `step2d_rebuild_01` | UBT PID 25780, exit 6, peak 2.71 GiB | Not launched | Compile failed on the `ApplicationContextMask` automation flag; corrected in the next source pass. |
| `step2d_rebuild_02` | UBT PID 19420, passed, peak 1.57 GiB | PID 13996, exit 2, peak 1.83 GiB | 3.834 s optimized ring; brick/sample skipping was too low for the speed gate. |
| `step2d_rebuild_03` | Passed; PID not retained in the compact log | PID 15480, exit 2, peak 1.82 GiB | 3.918 s; detail spectrum failed. The smoothing negative control worked; the proof negative control did not. |
| `step2d_rebuild_04` | Not launched | Not launched | Harness rejected a relative `-Out` path before Unreal startup. |
| `step2d_rebuild_05` | UBT PID 23904, passed | PID 24708, exit 2, peak 1.82 GiB | Scratch reuse gave 233.27 density mismatches and hash/boundary failures; scratch ownership was corrected. |
| `step2d_rebuild_06` | UBT PID 7540, passed | PID 25828, exit 2, peak 1.82 GiB | Detail, smoothing, boundary, and hash checks passed, but 3.70 s and 1.46% sample skipping missed the speed gate. |
| `step2d_rebuild_07` | UBT PID 23800, passed | PID 5208, exit 2, peak 1.81 GiB | 3.66 s; the cached XY macro did not improve the timing. |
| `step2d_rebuild_08` | UBT PID 25304, passed | PID 7108, exit 2, peak 1.81 GiB | 2.62 s; 22.20% of bricks and 21.18% of samples skipped, still over target. |
| `step2d_rebuild_09` | UBT PID 25612, passed, peak 1.51 GiB | PID 24256, exit 0, peak 1.82 GiB | Superficially under 600 ms, but invalid as evidence: classifier gap/default/chunk semantics had not yet been fixed. Excluded. |
| `step2d_harness_10` | UBT PID 2544, passed, peak 1.49 GiB | PID 23204, exit 0, peak 1.84 GiB | 503.026 ms and hash/detail/boundary checks passed. Superseded because its deliberately undersized-bound control did not catch a wrong sign. |
| `step2d_harness_11` | UBT PID 2900, passed, peak 1.50 GiB | PID 10688, exit 0, peak 1.83 GiB | 497.518 ms; the scale-0 versus scale-4 undersized-bound control caught 35,847 wrong-sign samples. |
| `step2d_harness_12` | UBT PID 13868, passed, peak 1.87 GiB | PID 16872, exit 0, peak 1.81 GiB | Final dynamically scheduled run: 308.177 ms median, all hashes and research gates passed. |
## Functional runs
| Label | Build | Unreal process | Result |
|---|---|---|---|
| `step2d_functional_01` | UBT PID 21184, passed | PID 3444, exit 255, peak 3.26 GiB | Base connectivity passed; refinement failed on an endpoint self-query. The launcher detected a write under `C:\Users\Jahni\AppData\Local\UnrealEngine`; later runs used isolated per-run local data. |
| `step2d_functional_02` | UBT PID 11844, passed, peak 1.87 GiB | PID 5048, exit 255, peak 3.13 GiB | Same endpoint-snap failure; isolated LocalAppData was clean. |
| `step2d_functional_03` | UBT PID 2060, passed, peak 1.90 GiB | PID 25648, exit 255, peak 3.19 GiB | Origin probe compared raw density 8 with a coarse center at -3.534534 near Z=-1012. |
| `step2d_functional_04` | Failed launch; retained PID 23996, stage split not retained | Exit 255; peak not retained | Range-normalization fix did not resolve the origin probe. No crash was recorded. |
| `step2d_redefined_seeds_01` | Build passed; PID not retained | PID 21888, exit 255 | Initial multi-seed endpoint-snap expectation was stricter than the route's full-density segment check; expectation was revised. |
| `step2d_functional_05` | UBT PID 17000, passed | PID 7540, exit 255 | 12/16 fitted VerticalShafts seeds passed. |
| `step2d_functional_06` | Build passed; PID not retained | PID 21948, exit 255, peak 3.43 GiB | 12/16; seed 1337's physical arrival/departure audit found 13 bad samples of 15. |
| `step2d_redefined_seeds_02` | UBT PID 14672, exit 0, peak 1.63 GiB | PID 2220, exit 0, peak 2.61 GiB | Passed an earlier three-seed traversal version. |
| `step2d_functional_07` | Build/Unreal stage PID 15712 in retained summary | Exit 255, peak 3.42 GiB | 12/16; no native crash. |
| Relative-output-path prelaunch attempt | Not launched | Not launched | Harness stopped before Unreal startup; corrected to an absolute output directory. |
| `step2d_functional_08` | UBT PID 12596, passed, peak 1.87 GiB | PID 20624, exit 255, peak 3.42 GiB | 13/16; origin path passed, three measurement limits remained. |
| `step2d_functional_09` | UBT PID 10848, passed, peak 1.50 GiB | PID 16764, exit 255, peak 3.46 GiB | 13/16 after the cache-key fix. |
| `step2d_functional_10` | UBT PID 24704, passed, peak 1.44 GiB | PID 7512, exit 255, peak 3.56 GiB | 13/16 after connector-radius changes. |
| `step2d_functional_11` | UBT PID 17088, exit 6, peak 1.96 GiB | Not launched | Compile error C4456 from a duplicated `XYCount`; fixed before the next build. |
| `step2d_functional_12` | UBT PID 4760, passed, peak 1.95 GiB | PID 26520, exit 255, peak 3.49 GiB | 13/16 after explicit bridges. |
| `step2d_functional_13` | UBT PID 19416, passed, peak 1.50 GiB | PID 26444, exit 255, peak 3.50 GiB | 13/16; diagnostics showed 16 retries and both endpoints in the same large component. |
| `step2d_functional_14` | UBT PID 3156, passed, peak 1.50 GiB | PID 17892, exit 255, peak 3.49 GiB | 13/16 after widening the landing bridge. |
| `step2d_functional_15` | UBT PID 5648, passed, peak 1.49 GiB | PID 23788, exit 255, peak 3.53 GiB | 14/16 after adding the vertical cavern band. |
| `step2d_functional_16` | UBT PID 20028, passed, peak 1.13 GiB | PID 4916, exit 255, peak 3.60 GiB | Controlled-wall assumptions and the 14/16 refinement audit both failed. |
| `step2d_functional_17` | UBT PID 14952, passed, peak 1.50 GiB | PID 2604, exit 0, peak 2.69 GiB | Three-seed traversal test passed: 21 landing brackets and 18 routes. |
| `step2d_functional_18` | UBT PID 10496, passed, peak 1.49 GiB | PID 25636, exit 0, peak 2.70 GiB | Passage-open-space test passed. |
| `step2d_functional_19` | UBT PID 22588, passed, peak 1.48 GiB | PID 7316, exit 255, peak 3.71 GiB | Controlled wall returned conservative unknown after zero retries; refinement remained 14/16. |
| `step2d_functional_20` | UBT PID 14076, passed, peak 1.88 GiB | PID 23120, exit 255, peak 3.60 GiB | Wall samples were -2/-102/-2, so the chosen location was not an air/solid/air control; refinement remained 14/16. |
| `step2d_functional_21` | UBT PID 8224, passed, peak 1.58 GiB | PID 25652, exit 255, peak 3.67 GiB | `StrateConnectivity` passed; refinement remained 14/16 because the fallback had been placed in a skipped diagnostic branch. |
| `step2d_functional_22` | UBT PID 26528, passed, peak 1.62 GiB | PID 24396, exit 0, peak 3.68 GiB | Both required connectivity tests passed; the fitted audit directly confirmed 16/16 routes at step 2. |
| `step2d_functional_23` | Build not requested | PID 15176, exit 0, peak 2.44 GiB | `PassageLandsInOpenSpace` passed. |
| `step2d_functional_24` | Build not requested | PID 26336, exit 0, peak 2.42 GiB | `RedefinedFieldTraversalSeeds` passed across three seeds. |
The manual `Win32_OperatingSystem` memory probe was denied once before a launch; no Unreal process
was started by that probe. The harness's own native memory preflight succeeded on subsequent
launches. No files outside the workspace were deleted.
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

+131
View File
@@ -0,0 +1,131 @@
# Rebuild step 2d research report
Date: 2026-09-30
Branch: `rebuild/step2d-redefined`
Scope: `Plugins/VoxelForge`
## Result
The editor-only research field passes the 259³, 25 cm ring target on six workers. Its final
median is **308.177 ms** across three runs (295.325–317.807 ms), below both the 600 ms step-2
bar and the 350 ms step-3 target. The old field measured 8,900.965 ms median on the same ring.
Preparation, including the 135.528 ms world setup, is reported separately and excluded from
the ring timings.
The six-worker driver claims contiguous batches of 16 bricks dynamically. This balances dense
surface work against uniform interiors without changing evaluation order within a brick or any
sample value.
The old and new density fields intentionally differ. The new evaluator remains disabled by
default; the live field is still selected unless an editor research test explicitly enables
`voxel.ResearchRedefinedDensityField`.
## Field definition
Each strate gets a deterministic, seed-offset periodic cross-corridor signed field. Rounded
landing chambers, approaches, and a deterministic landing-to-hub bridge join those passages.
Authored passage carving and floor geometry remain active. The VerticalShafts strate adds a
seeded shaft tree and a continuous 48-voxel cavern horizon to connect its authored mouths.
Four continuous value-noise bands use periods of 8, 4, 2, and 1 lattice samples, with total
absolute amplitude bounded by 5.25 density units. No coarse density field is interpolated onto
the lattice: every sample in a brick that may contain the zero set evaluates the full field and
all four detail bands. Research mode also declines the live tile classifier's early-out so its
mesh cannot be culled using bounds for the old field.
## Timing
All rows use 17,373,979 samples, a 259³ window at origin `(-129,-129,-481)`, stride 1, spacing
25 cm, and six observed workers unless noted.
| Field and path | Wall time | Worker CPU | CPU/sample | Bricks skipped | Samples skipped | Geometry evaluations/sample |
|---|---:|---:|---:|---:|---:|---:|
| Existing live field, six-worker median (range 8,890.120–8,923.020 ms) | 8,900.965 ms | 37.70 s | 2,170.09 ns | 0% | 0% | 6.734 |
| Redefined field, all points evaluated, six workers | 467.212 ms | 2.313 s | 133.10 ns | 0% | 0% | 1.000 |
| Redefined field, sound 8³ proofs, six-worker median (range 295.325–317.807 ms) | **308.177 ms** | 1.41 s | 80.94 ns | 60.54% | 63.06% | 0.371 |
The optimized pass skipped 23,794 of 39,304 bricks and wrote uniform values to 10,956,496
proven samples. It evaluated full density at the remaining 6,417,483 samples. The 0.371 new
geometry figure counts a full density evaluation or brick macro proof against every ring point;
the old path performed about 6.734 room/tunnel/passage primitive evaluations per point. Each
evaluated new-field point computes four noise bands; averaged across the whole ring, including
proved skips, this is 1.477 noise-band evaluations per sample.
The final run's accumulated worker-phase timers were 1.79 s for full density evaluation, 0.04 s
for uniform fills, and 0.01 s for brick proofs. Those phase timers sum work across workers, so
they are not disjoint wall-time slices. The longest worker took 307.76 ms; measured parallel
overhead after that was 0.42 ms. The remaining wall time is in full-detail evaluation of the
6.42 million nonuniform samples.
## Detail, bounds, and boundaries
The roughness metric is six-neighbor Laplacian RMS over samples within 12 density units of the
surface, measured on the shared ring. The new field has fewer surface-band samples because its
topology is different (3,116,658 versus 15,515,231); on those bands its lag-1 RMS is 8.978 versus
6.248 for the old field, and lag-2 RMS is 9.169 versus 8.646. A deliberately smoothed new-field
control (detail scale 0) falls to 1.315 and 1.933. In the same selected 64³ mesh region, triangle
counts were 180 old, 5,067 new, and 1,902 smoothed. The metric and mesh both detected the
smoothing control. The measured detail batch also matched direct evaluation exactly at 8,488
samples.
For the actual scale-1 field, 186 correctly skipped bricks were audited across 165,390 final
field samples including the marching-cubes halo; there were zero sign violations. The bound
includes the full 5.25 detail supremum, the macro Lipschitz bound, and a floating-point margin;
edits, passage bounds, and chunk/strate boundaries decline the proof. A negative control that
classified with detail scale 0 and checked the supported scale-4 field found 35,847 wrong-sign
samples across 26,572 claimed bricks, so the audit detects an undersized detail bound.
Near-surface gradient-jump RMS was 3.420 at 8³ brick boundaries versus 8.694 in brick interiors,
3.423 at chunk boundaries, and 3.225 at 128-sample macro-cell boundaries versus 10.036 in those
cell interiors. These checks found no boundary spike consistent with stair-stepping. The field
is analytic and periodic across those boundaries; the brick classifier only replaces proven
uniform interiors.
## Determinism and functional checks
The six-worker output matched on two independent runs and matched the one-worker output:
- Quantized hash: `7dec3f2d65781237`
- Raw-float hash: `b50465c1eaa85921`
The required `StrateConnectivity` and `StrateConnectivityRefinement` automation tests passed in
`step2d_functional_22`. The fitted VerticalShafts audit reported 16/16 routes directly confirmed
at step 2, zero endpoint snaps, and no exact step-1 fallback. Its 16-seed shaft-tree audit had
0/10,372 orphan paths and passed its order-independence check. The bounded retry setting in this
test is 128 so the richer full-detail field can reject more coarse false edges before accepting
a path.
`PassageLandsInOpenSpace` passed in `step2d_functional_23` for its seven-passage fixture.
`RedefinedFieldTraversalSeeds` passed in `step2d_functional_24`: over three seeds, 21 authored
passage landings bracketed open air and 18 arrival-to-exit walking routes were checked; six
routes used a resolved coarse endpoint only after a full-density segment check.
## Slices
Each image places the old field on the left and the redefined field on the right. Light is air,
dark is solid, and cyan marks the near-zero surface.
- [Horizontal slice through the ring center](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2d/step2d_rebuild_12/slice_xy_center.png>)
- [Vertical slice at Y=0](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2d/step2d_rebuild_12/slice_xz_y0.png>)
- [Vertical slice at X=0](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2d/step2d_rebuild_12/slice_yz_x0.png>)
- [Vertical slice through an authored passage](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2d/step2d_rebuild_12/slice_xz_passage.png>)
Machine-readable measurements are in [step2d_report.json](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2d/step2d_rebuild_12/step2d_report.json>).
## Scaling to 12.5 cm and compile risk
At 12.5 cm, double macro lengths and broad/medium noise periods in lattice units to preserve
their world-space shape: the current noise periods 8/4/2/1 become 16/8/4/2 samples. Keep or add
a 1-sample fine band so detail stays at least as rich as the 25 cm version. For a fixed world
region, the lattice grows 8×, while surface-intersecting 8³ bricks should grow closer to 4×
(surface area); remaining bricks still need only a cheap final-field sign proof and density
writes. This scaling model is reasoned from the geometry and has not yet been timed at 12.5 cm.
The UE 5.7 Win64 Development build passed with `-ForceHeaderGeneration` and
`-MaxParallelActions=4`. No UCLASS/UPROPERTY layout changed, so no generated-class layout risk
was introduced. The sensitive code is the locally bounded landing-floor derivative and the
supremum used by `TryClassifyBrick`; future field terms must update those bounds and keep the
proof audit. The live path and ARCHITECTURE §8.10 were left unchanged when research mode is off.
No `.uasset` file was edited. Nothing was committed, pushed, or stashed.
See the exhaustive [launch ledger](</E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2d/step2d_launch_ledger.md>) for every benchmark, test run, failure, and prelaunch rejection.
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

@@ -0,0 +1,815 @@
{
"status": "pass",
"pass": true,
"worst_profile_meets_600ms": true,
"seed": 0,
"ring_dimension": 259,
"ring_samples": 17373979,
"workers_requested": 6,
"world_prepare_ms_excluded": 134.9404975771904,
"old_live_ring_ms": 9188.3964985609055,
"old_live_ring_raw_float_hash": "cf1e3a48b3bc1bda",
"field_definition": "Seeded jittered XY chamber cells form a deterministic origin-rooted spanning tree; selected loops, horizontal tunnels and full-strate shafts open the network. Each chamber can add pillars, annular ledges, paired stalactites/stalagmites, floating rock masses and a bowl floor with an explicit water level. Passage pads and anchor tunnels join the authored inter-strate route to the generated network.",
"parameter_space": "Eight named profiles can be assigned per strate from a stable seed hash (-1 mode), or forced for measurement. Each profile supplies nominal room, tunnel, shaft, feature-probability and basin values. Per-cell hashes deterministically roll room jitter and feature presence. Profile minima and maxima are recorded below; room height is clamped to the strate span, room radii to 0.20-0.72/0.80 of pitch, loop probability to 0-0.8, probabilities and water level to 0-1, and basin depth to 0-48 voxels.",
"parameter_ranges_across_eight_profiles":
{
"room_pitch_voxels":
{
"minimum_across_profiles": 96,
"maximum_across_profiles": 176
},
"room_radius_fraction_min":
{
"minimum_across_profiles": 0.5,
"maximum_across_profiles": 0.62000000476837158
},
"room_radius_fraction_max":
{
"minimum_across_profiles": 0.62000000476837158,
"maximum_across_profiles": 0.75999999046325684
},
"room_height_voxels":
{
"minimum_across_profiles": 216,
"maximum_across_profiles": 340
},
"floor_relief_voxels":
{
"minimum_across_profiles": 8,
"maximum_across_profiles": 20
},
"tunnel_radius_voxels":
{
"minimum_across_profiles": 20,
"maximum_across_profiles": 40
},
"tunnel_half_height_voxels":
{
"minimum_across_profiles": 22,
"maximum_across_profiles": 36
},
"loop_probability":
{
"minimum_across_profiles": 0.11999999731779099,
"maximum_across_profiles": 0.40000000596046448
},
"vertical_shaft_probability":
{
"minimum_across_profiles": 0.029999999329447746,
"maximum_across_profiles": 0.92000001668930054
},
"shaft_radius_voxels":
{
"minimum_across_profiles": 14,
"maximum_across_profiles": 28
},
"pillar_probability":
{
"minimum_across_profiles": 0.039999999105930328,
"maximum_across_profiles": 0.72000002861022949
},
"ledge_probability":
{
"minimum_across_profiles": 0.079999998211860657,
"maximum_across_profiles": 0.8399999737739563
},
"floating_mass_probability":
{
"minimum_across_profiles": 0.019999999552965164,
"maximum_across_profiles": 0.72000002861022949
},
"stalactite_stalagmite_probability":
{
"minimum_across_profiles": 0.11999999731779099,
"maximum_across_profiles": 0.75
},
"basin_probability":
{
"minimum_across_profiles": 0,
"maximum_across_profiles": 0.85000002384185791
},
"basin_depth_voxels":
{
"minimum_across_profiles": 0,
"maximum_across_profiles": 48
},
"water_level_fraction":
{
"minimum_across_profiles": 0.30000001192092896,
"maximum_across_profiles": 0.5
}
},
"resolution_contract": "One exact field sample per 25 cm lattice point. The four existing 8/4/2/1-sample detail bands are evaluated for every potentially surface-affecting point; no coarse field is interpolated onto the lattice. 4-cubed step 2e skips require the final macro field Lipschitz interval plus the full 5.25-unit detail supremum and marching-cubes halo.",
"finer_lattice_scaling": "For 12.5 cm voxels, double room pitch, room height, tunnel/feature dimensions and the broad-to-medium noise periods in sample units to preserve meter-scale shapes. Keep the 1-sample detail band or add a finer octave. The lattice grows 8x for a fixed world volume; surface bricks grow roughly with area while certified interior bricks remain cheap. This scale has not been timed.",
"variety_metric_method": "Open fraction uses every ring sample. Surface area estimates sign-changing lattice edges times 0.25 m squared. Nominal chamber-volume p10/p50/p90 values use the uniform per-cell radius roll and room height as circular-cylinder volumes. Tunnel-neck share and connected-component sizes use a 1 m downsampled six-neighbor occupancy grid. Verticality is the mean longest contiguous open run per XY column, with open-column coverage reported separately.",
"water_geometry_limit": "Basin profiles generate the bowl floor and water-level parameter; this density field does not render or simulate water.",
"worst_six_worker_ring_median_ms": 570.03279775381088,
"target_aim_ms": 350,
"hardest_profile": "HyperComplex",
"profiles": [
{
"profile_index": 0,
"name": "OpenVaults",
"parameters":
{
"room_pitch_voxels": 176,
"room_radius_fraction_min": 0.62000000476837158,
"room_radius_fraction_max": 0.75999999046325684,
"room_height_voxels": 340,
"floor_relief_voxels": 12,
"tunnel_radius_voxels": 40,
"tunnel_half_height_voxels": 36,
"loop_probability": 0.34999999403953552,
"vertical_shaft_probability": 0.14000000059604645,
"shaft_radius_voxels": 22,
"pillar_probability": 0.039999999105930328,
"ledge_probability": 0.11999999731779099,
"floating_mass_probability": 0.079999998211860657,
"stalactite_stalagmite_probability": 0.15000000596046448,
"basin_probability": 0,
"basin_depth_voxels": 0,
"water_level_fraction": 0.5
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 207803.41085068439,
"p50_m3": 246134.408195325,
"p90_m3": 287707.90649226471
},
"six_worker_ring_median_ms": 404.54889833927155,
"six_worker_ring_trials_ms": [
403.37140113115311,
404.54889833927155,
422.8769987821579
],
"open_air_fraction": 0.97343389214410814,
"estimated_surface_area_m2": 33145,
"estimated_surface_area_per_volume_m_inv": 0.12209523218601795,
"vertical_air_adjacency_share": 0.33298452070631224,
"vertical_air_column_share": 1,
"mean_longest_vertical_air_run_voxels": 241.88837375709963,
"longest_vertical_air_run_voxels": 259,
"one_meter_tunnel_neck_share": 0.014380675966669787,
"one_meter_connected_components": 22,
"largest_component_coarse_m3": 267001,
"components_1_to_8_coarse_m3": 21,
"components_9_to_64_coarse_m3": 0,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 213327,
"skipped_sample_fraction": 0.75428248186555302,
"evaluated_samples": 4269091,
"worker_cpu_seconds": 1.609375,
"brick_classification_worker_ms": 242.50604957342148,
"density_evaluation_worker_ms": 2076.3386078178883,
"uniform_fill_worker_ms": 53.428228944540024,
"longest_worker_ms": 399.43369850516319,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_00_openvaults"
},
{
"profile_index": 1,
"name": "TightNetwork",
"parameters":
{
"room_pitch_voxels": 104,
"room_radius_fraction_min": 0.51999998092651367,
"room_radius_fraction_max": 0.63999998569488525,
"room_height_voxels": 256,
"floor_relief_voxels": 8,
"tunnel_radius_voxels": 20,
"tunnel_half_height_voxels": 22,
"loop_probability": 0.11999999731779099,
"vertical_shaft_probability": 0.029999999329447746,
"shaft_radius_voxels": 14,
"pillar_probability": 0.059999998658895493,
"ledge_probability": 0.079999998211860657,
"floating_mass_probability": 0.019999999552965164,
"stalactite_stalagmite_probability": 0.11999999731779099,
"basin_probability": 0,
"basin_depth_voxels": 0,
"water_level_fraction": 0.5
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 38468.015011234471,
"p50_m3": 45722.767008321418,
"p90_m3": 53603.828575098079
},
"six_worker_ring_median_ms": 561.79890036582947,
"six_worker_ring_trials_ms": [
553.03480103611946,
561.79890036582947,
575.50980150699615
],
"open_air_fraction": 0.89594007221949556,
"estimated_surface_area_m2": 92336.125,
"estimated_surface_area_per_volume_m_inv": 0.34013578582085313,
"vertical_air_adjacency_share": 0.33327139189444377,
"vertical_air_column_share": 0.99737630625661511,
"mean_longest_vertical_air_run_voxels": 210.41539496300726,
"longest_vertical_air_run_voxels": 259,
"one_meter_tunnel_neck_share": 0.039870522719675165,
"one_meter_connected_components": 109,
"largest_component_coarse_m3": 245133,
"components_1_to_8_coarse_m3": 106,
"components_9_to_64_coarse_m3": 2,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 142897,
"skipped_sample_fraction": 0.50734353943906574,
"evaluated_samples": 8559403,
"worker_cpu_seconds": 2.328125,
"brick_classification_worker_ms": 241.08080938458443,
"density_evaluation_worker_ms": 3046.3547222316265,
"uniform_fill_worker_ms": 35.994317382574081,
"longest_worker_ms": 557.83749744296074,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_01_tightnetwork"
},
{
"profile_index": 2,
"name": "ChamberHalls",
"parameters":
{
"room_pitch_voxels": 144,
"room_radius_fraction_min": 0.55000001192092896,
"room_radius_fraction_max": 0.72000002861022949,
"room_height_voxels": 320,
"floor_relief_voxels": 18,
"tunnel_radius_voxels": 34,
"tunnel_half_height_voxels": 32,
"loop_probability": 0.2800000011920929,
"vertical_shaft_probability": 0.18000000715255737,
"shaft_radius_voxels": 22,
"pillar_probability": 0.079999998211860657,
"ledge_probability": 0.2199999988079071,
"floating_mass_probability": 0.10000000149011612,
"stalactite_stalagmite_probability": 0.2199999988079071,
"basin_probability": 0.05000000074505806,
"basin_depth_voxels": 20,
"water_level_fraction": 0.40000000596046448
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 104715.5070092809,
"p50_m3": 131338.58696522861,
"p90_m3": 160973.92909046411
},
"six_worker_ring_median_ms": 505.73009997606277,
"six_worker_ring_trials_ms": [
465.90260043740273,
505.73009997606277,
545.07719725370407
],
"open_air_fraction": 0.92935711502816942,
"estimated_surface_area_m2": 57662.9375,
"estimated_surface_area_per_volume_m_inv": 0.21241121564611076,
"vertical_air_adjacency_share": 0.33303864616767592,
"vertical_air_column_share": 1,
"mean_longest_vertical_air_run_voxels": 224.23037819949016,
"longest_vertical_air_run_voxels": 259,
"one_meter_tunnel_neck_share": 0.026658600207423239,
"one_meter_connected_components": 50,
"largest_component_coarse_m3": 254470,
"components_1_to_8_coarse_m3": 49,
"components_9_to_64_coarse_m3": 0,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 185011,
"skipped_sample_fraction": 0.6521041610560252,
"evaluated_samples": 6044335,
"worker_cpu_seconds": 1.78125,
"brick_classification_worker_ms": 237.52568662166595,
"density_evaluation_worker_ms": 2464.1959294676781,
"uniform_fill_worker_ms": 46.43239825963974,
"longest_worker_ms": 461.93370223045349,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_02_chamberhalls"
},
{
"profile_index": 3,
"name": "ShaftForest",
"parameters":
{
"room_pitch_voxels": 128,
"room_radius_fraction_min": 0.5,
"room_radius_fraction_max": 0.62000000476837158,
"room_height_voxels": 272,
"floor_relief_voxels": 12,
"tunnel_radius_voxels": 26,
"tunnel_half_height_voxels": 28,
"loop_probability": 0.11999999731779099,
"vertical_shaft_probability": 0.92000001668930054,
"shaft_radius_voxels": 28,
"pillar_probability": 0.05000000074505806,
"ledge_probability": 0.18000000715255737,
"floating_mass_probability": 0.039999999105930328,
"stalactite_stalagmite_probability": 0.20000000298023224,
"basin_probability": 0,
"basin_depth_voxels": 0,
"water_level_fraction": 0.5
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 57345.410351032719,
"p50_m3": 68601.687643874015,
"p90_m3": 80865.989806275087
},
"six_worker_ring_median_ms": 548.60490188002586,
"six_worker_ring_trials_ms": [
546.6328002512455,
548.60490188002586,
578.33020016551018
],
"open_air_fraction": 0.85949580116333746,
"estimated_surface_area_m2": 93586.625,
"estimated_surface_area_per_volume_m_inv": 0.34474221477993039,
"vertical_air_adjacency_share": 0.33316652784439366,
"vertical_air_column_share": 0.99175623499947829,
"mean_longest_vertical_air_run_voxels": 202.98173701298703,
"longest_vertical_air_run_voxels": 259,
"one_meter_tunnel_neck_share": 0.043446839263699355,
"one_meter_connected_components": 100,
"largest_component_coarse_m3": 235109,
"components_1_to_8_coarse_m3": 99,
"components_9_to_64_coarse_m3": 0,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 149078,
"skipped_sample_fraction": 0.52414844060764665,
"evaluated_samples": 8267435,
"worker_cpu_seconds": 2.09375,
"brick_classification_worker_ms": 232.56969451904297,
"density_evaluation_worker_ms": 2963.2366858422756,
"uniform_fill_worker_ms": 37.121176719665527,
"longest_worker_ms": 542.60710254311562,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_03_shaftforest"
},
{
"profile_index": 4,
"name": "FloatingArchipelago",
"parameters":
{
"room_pitch_voxels": 136,
"room_radius_fraction_min": 0.55000001192092896,
"room_radius_fraction_max": 0.68000000715255737,
"room_height_voxels": 216,
"floor_relief_voxels": 10,
"tunnel_radius_voxels": 28,
"tunnel_half_height_voxels": 28,
"loop_probability": 0.15000000596046448,
"vertical_shaft_probability": 0.10000000149011612,
"shaft_radius_voxels": 16,
"pillar_probability": 0.05000000074505806,
"ledge_probability": 0.10000000149011612,
"floating_mass_probability": 0.72000002861022949,
"stalactite_stalagmite_probability": 0.2800000011920929,
"basin_probability": 0,
"basin_depth_voxels": 0,
"water_level_fraction": 0.5
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 62161.040291956291,
"p50_m3": 74174.001993180806,
"p90_m3": 87247.530713802553
},
"six_worker_ring_median_ms": 559.36940014362335,
"six_worker_ring_trials_ms": [
556.77210167050362,
559.36940014362335,
564.77300077676773
],
"open_air_fraction": 0.81436451603861149,
"estimated_surface_area_m2": 77448.0625,
"estimated_surface_area_per_volume_m_inv": 0.285293081107097,
"vertical_air_adjacency_share": 0.3339729720652625,
"vertical_air_column_share": 0.98258821424844589,
"mean_longest_vertical_air_run_voxels": 196.60781636399497,
"longest_vertical_air_run_voxels": 248,
"one_meter_tunnel_neck_share": 0.034649779794953671,
"one_meter_connected_components": 65,
"largest_component_coarse_m3": 222906,
"components_1_to_8_coarse_m3": 64,
"components_9_to_64_coarse_m3": 0,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 150891,
"skipped_sample_fraction": 0.53115961519235178,
"evaluated_samples": 8145623,
"worker_cpu_seconds": 2.359375,
"brick_classification_worker_ms": 269.87878605723381,
"density_evaluation_worker_ms": 2985.2445237338543,
"uniform_fill_worker_ms": 37.513568997383118,
"longest_worker_ms": 552.79690027236938,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_04_floatingarchipelago"
},
{
"profile_index": 5,
"name": "PillaredCathedral",
"parameters":
{
"room_pitch_voxels": 160,
"room_radius_fraction_min": 0.55000001192092896,
"room_radius_fraction_max": 0.72000002861022949,
"room_height_voxels": 248,
"floor_relief_voxels": 12,
"tunnel_radius_voxels": 32,
"tunnel_half_height_voxels": 34,
"loop_probability": 0.25,
"vertical_shaft_probability": 0.079999998211860657,
"shaft_radius_voxels": 18,
"pillar_probability": 0.72000002861022949,
"ledge_probability": 0.30000001192092896,
"floating_mass_probability": 0.079999998211860657,
"stalactite_stalagmite_probability": 0.44999998807907104,
"basin_probability": 0,
"basin_depth_voxels": 0,
"water_level_fraction": 0.5
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 100190.76287925025,
"p50_m3": 125663.46283710146,
"p90_m3": 154018.26548778973
},
"six_worker_ring_median_ms": 500.73060020804405,
"six_worker_ring_trials_ms": [
499.61259961128235,
500.73060020804405,
516.76810160279274
],
"open_air_fraction": 0.92515940073370639,
"estimated_surface_area_m2": 67416.5625,
"estimated_surface_area_per_volume_m_inv": 0.248340348517746,
"vertical_air_adjacency_share": 0.33307025414258157,
"vertical_air_column_share": 1,
"mean_longest_vertical_air_run_voxels": 220.65106363948064,
"longest_vertical_air_run_voxels": 259,
"one_meter_tunnel_neck_share": 0.029466147835377512,
"one_meter_connected_components": 50,
"largest_component_coarse_m3": 253593,
"components_1_to_8_coarse_m3": 49,
"components_9_to_64_coarse_m3": 0,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 173832,
"skipped_sample_fraction": 0.61222175990888439,
"evaluated_samples": 6737251,
"worker_cpu_seconds": 2.0625,
"brick_classification_worker_ms": 249.71644952893257,
"density_evaluation_worker_ms": 2656.6633693873882,
"uniform_fill_worker_ms": 43.705981224775314,
"longest_worker_ms": 495.59580162167549,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_05_pillaredcathedral"
},
{
"profile_index": 6,
"name": "TerracedBasin",
"parameters":
{
"room_pitch_voxels": 136,
"room_radius_fraction_min": 0.51999998092651367,
"room_radius_fraction_max": 0.64999997615814209,
"room_height_voxels": 264,
"floor_relief_voxels": 14,
"tunnel_radius_voxels": 28,
"tunnel_half_height_voxels": 30,
"loop_probability": 0.20000000298023224,
"vertical_shaft_probability": 0.10000000149011612,
"shaft_radius_voxels": 18,
"pillar_probability": 0.10000000149011612,
"ledge_probability": 0.8399999737739563,
"floating_mass_probability": 0.05000000074505806,
"stalactite_stalagmite_probability": 0.25,
"basin_probability": 0.85000002384185791,
"basin_depth_voxels": 48,
"water_level_fraction": 0.30000001192092896
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 68093.558179425308,
"p50_m3": 82028.230406505769,
"p90_m3": 97259.151212849523
},
"six_worker_ring_median_ms": 570.03279775381088,
"six_worker_ring_trials_ms": [
564.51970338821411,
570.03279775381088,
573.95610213279724
],
"open_air_fraction": 0.83870016189152752,
"estimated_surface_area_m2": 94288.5625,
"estimated_surface_area_per_volume_m_inv": 0.34732792067954038,
"vertical_air_adjacency_share": 0.33312483627697398,
"vertical_air_column_share": 0.98552496235893916,
"mean_longest_vertical_air_run_voxels": 182.00520344879746,
"longest_vertical_air_run_voxels": 259,
"one_meter_tunnel_neck_share": 0.048149240524996313,
"one_meter_connected_components": 88,
"largest_component_coarse_m3": 230451,
"components_1_to_8_coarse_m3": 87,
"components_9_to_64_coarse_m3": 0,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 145055,
"skipped_sample_fraction": 0.50965412125800313,
"evaluated_samples": 8519259,
"worker_cpu_seconds": 2.34375,
"brick_classification_worker_ms": 249.46653842926025,
"density_evaluation_worker_ms": 3080.0332017242908,
"uniform_fill_worker_ms": 36.166064441204071,
"longest_worker_ms": 564.91680070757866,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_06_terracedbasin"
},
{
"profile_index": 7,
"name": "HyperComplex",
"parameters":
{
"room_pitch_voxels": 96,
"room_radius_fraction_min": 0.60000002384185791,
"room_radius_fraction_max": 0.74000000953674316,
"room_height_voxels": 280,
"floor_relief_voxels": 20,
"tunnel_radius_voxels": 30,
"tunnel_half_height_voxels": 28,
"loop_probability": 0.40000000596046448,
"vertical_shaft_probability": 0.55000001192092896,
"shaft_radius_voxels": 20,
"pillar_probability": 0.44999998807907104,
"ledge_probability": 0.68000000715255737,
"floating_mass_probability": 0.5,
"stalactite_stalagmite_probability": 0.75,
"basin_probability": 0.41999998688697815,
"basin_depth_voxels": 28,
"water_level_fraction": 0.40000000596046448
},
"nominal_chamber_volume_quantiles_m3":
{
"p10_m3": 47753.715763874934,
"p50_m3": 56861.724022144372,
"p90_m3": 66764.200185109366
},
"six_worker_ring_median_ms": 559.64479967951775,
"six_worker_ring_trials_ms": [
520.49140259623528,
559.64479967951775,
575.4614993929863
],
"open_air_fraction": 0.94705570900022384,
"estimated_surface_area_m2": 61014.1875,
"estimated_surface_area_per_volume_m_inv": 0.22475611372616486,
"vertical_air_adjacency_share": 0.33306194335010991,
"vertical_air_column_share": 1,
"mean_longest_vertical_air_run_voxels": 219.53374278856904,
"longest_vertical_air_run_voxels": 259,
"one_meter_tunnel_neck_share": 0.029053088791938297,
"one_meter_connected_components": 65,
"largest_component_coarse_m3": 259588,
"components_1_to_8_coarse_m3": 62,
"components_9_to_64_coarse_m3": 2,
"components_65_to_512_coarse_m3": 0,
"components_over_512_coarse_m3": 1,
"skipped_bricks": 165593,
"skipped_sample_fraction": 0.58573939798131447,
"evaluated_samples": 7197355,
"worker_cpu_seconds": 2.09375,
"brick_classification_worker_ms": 262.15906068682671,
"density_evaluation_worker_ms": 2771.4953608810902,
"uniform_fill_worker_ms": 41.310880333185196,
"longest_worker_ms": 516.51959866285324,
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_07_hypercomplex"
}
],
"slices": [
{
"profile": "OpenVaults",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_00_openvaults/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "OpenVaults",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_00_openvaults/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "OpenVaults",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_00_openvaults/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "OpenVaults",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_00_openvaults/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TightNetwork",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_01_tightnetwork/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TightNetwork",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_01_tightnetwork/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TightNetwork",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_01_tightnetwork/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TightNetwork",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_01_tightnetwork/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ChamberHalls",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_02_chamberhalls/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ChamberHalls",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_02_chamberhalls/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ChamberHalls",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_02_chamberhalls/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ChamberHalls",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_02_chamberhalls/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ShaftForest",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_03_shaftforest/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ShaftForest",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_03_shaftforest/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ShaftForest",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_03_shaftforest/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "ShaftForest",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_03_shaftforest/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "FloatingArchipelago",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_04_floatingarchipelago/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "FloatingArchipelago",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_04_floatingarchipelago/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "FloatingArchipelago",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_04_floatingarchipelago/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "FloatingArchipelago",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_04_floatingarchipelago/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "PillaredCathedral",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_05_pillaredcathedral/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "PillaredCathedral",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_05_pillaredcathedral/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "PillaredCathedral",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_05_pillaredcathedral/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "PillaredCathedral",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_05_pillaredcathedral/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TerracedBasin",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_06_terracedbasin/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TerracedBasin",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_06_terracedbasin/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TerracedBasin",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_06_terracedbasin/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "TerracedBasin",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_06_terracedbasin/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "HyperComplex",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_07_hypercomplex/slice_xy_center.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "HyperComplex",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_07_hypercomplex/slice_xz_y0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "HyperComplex",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_07_hypercomplex/slice_yz_x0.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
},
{
"profile": "HyperComplex",
"file": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30/set_07_hypercomplex/slice_xz_passage.png",
"layout": "today's live field on left; step 2e parameterized field on right; light=air, dark=solid, cyan=near zero"
}
],
"slice_directory": "E:/Projet Unreal/VoxelM/Plugins/VoxelForge/Saved/Research/step2e/step2e_profiles_30",
"detail_check":
{
"live_surface_band_samples": 15515231,
"new_surface_band_samples": 4880019,
"live_curvature_rms_lag1": 6.248069730899255,
"new_curvature_rms_lag1": 8.9351827746107304,
"smooth_control_curvature_rms_lag1": 1.9552341792313939,
"live_curvature_rms_lag2": 8.6459817485493211,
"new_curvature_rms_lag2": 9.3960370724645941,
"smooth_control_curvature_rms_lag2": 3.2348563389313552,
"detail_raw_parity_samples": 8488,
"detail_raw_parity_max_error": 0,
"detail_at_least_live_spectrum": true,
"deliberate_zero_detail_smoothing_detected": true,
"direct_vs_brick_detail_parity": true
},
"boundary_artifact_check":
{
"brick_boundary_gradient_jump_rms": 4.0223860554078321,
"interior_gradient_jump_rms": 3.475720295663749,
"chunk_boundary_gradient_jump_rms": 3.3855279003366117,
"feature_cell_boundary_gradient_jump_rms": 3.3785618604833521,
"feature_cell_interior_gradient_jump_rms": 3.4366715251600706,
"boundary_artifact_check_pass": true,
"deliberate_seam_samples_mutated": 706718,
"deliberate_seam_failure_caught": true
},
"brick_bound_audit":
{
"correctly_skipped_bricks_audited": 109,
"final_field_samples_checked_including_halo": 91890,
"correct_proof_sign_violations": 0,
"first_violation_brick_x": 0,
"first_violation_brick_y": 0,
"first_violation_brick_z": 0,
"first_violation_brick_size_x": 0,
"first_violation_brick_size_y": 0,
"first_violation_brick_size_z": 0,
"first_violation_sample_x": 0,
"first_violation_sample_y": 0,
"first_violation_sample_z": 0,
"first_violation_density": 0,
"first_violation_center_macro": 0,
"first_violation_sample_macro": 0,
"first_violation_sample_detail": 0,
"first_violation_macro_delta_per_voxel": 0,
"first_violation_path_max_gradient": 0,
"first_violation_tiny_step_max_gradient": 0,
"first_violation_inside_proven_brick": false,
"first_violation_claimed_air": false,
"undersized_bound_claims_checked": 20328,
"wrong_sign_samples_caught_by_undersized_bound": 14547,
"sound_supremum_audit_pass": true,
"deliberate_undersized_bound_failure_caught": true
},
"determinism":
{
"six_worker_run_1_quantized": "77439101d77e7750",
"six_worker_run_2_quantized": "77439101d77e7750",
"one_worker_quantized": "77439101d77e7750",
"six_worker_run_1_raw_float": "65bdd30af60254e1",
"six_worker_run_2_raw_float": "65bdd30af60254e1",
"one_worker_raw_float": "65bdd30af60254e1",
"same_across_two_runs_and_1_vs_6_workers": true,
"deliberate_quantized_hash_mismatch_rejected": true
},
"entrance_exit_walkability_test_added": true,
"inter_strate_passage_open_space_test_added": true,
"functional_test_filter": "VoxelForge.ParameterizedGeneration.TraversalSeeds"
}
File diff suppressed because it is too large. Load diff
Loaded 100 of 1390 files, more files were not shown because too many files have changed in this diff. Show more