Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ae5c4460d | ||
|
|
135957831b | ||
|
|
c0c204ba50 | ||
|
|
fbae82e024 | ||
|
|
6c6cc37e1d | ||
|
|
1e3e04bbac | ||
|
|
4e64b75044 | ||
|
|
aee1487410 | ||
|
|
e35a77f676 | ||
|
|
9458217d34 | ||
|
|
20f6c98ec5 | ||
|
|
408c14baff | ||
|
|
c42c785e2c | ||
|
|
75550256ef | ||
|
|
ca85e876ad | ||
|
|
b9ccff4ad6 | ||
|
|
6110f118e3 | ||
|
|
3073d1b2fd | ||
|
|
c091e4434f | ||
|
|
e016f6a20f | ||
|
|
f13b145f23 | ||
|
|
036bd530fc | ||
|
|
422dd7761a | ||
|
|
497e974178 | ||
|
|
2566f53734 | ||
|
|
1abee91721 | ||
|
|
efa9e89468 | ||
|
|
753cc9c259 | ||
|
|
88c54eab44 | ||
|
|
cf57c87daf | ||
|
|
945225d942 | ||
|
|
7f2d84d2c7 | ||
|
|
3e443bf81c | ||
|
|
8be1c4d9ef | ||
|
|
ba017077d1 | ||
|
|
49e29fcd36 | ||
|
|
b7a47bef95 | ||
|
|
d0158c6019 | ||
|
|
28069f0d98 | ||
|
|
103d8b471e | ||
|
|
63c7e563d0 | ||
|
|
f23ab7b9fa | ||
|
|
07b3fe2852 | ||
|
|
37a223046c | ||
|
|
ef789aaa00 | ||
|
|
cb9236c265 | ||
|
|
71d09a8c52 | ||
|
|
55c26a05a7 | ||
|
|
8753f6dbe4 | ||
|
|
5d5447dc32 | ||
|
|
6f9c7bd652 | ||
|
|
811c4b7b17 | ||
|
|
dcf0763ec5 | ||
|
|
87041290dd | ||
|
|
9a7c87e47b | ||
|
|
a6cd7cf339 | ||
|
|
a176d0743a | ||
|
|
976d012a86 | ||
|
|
32b8670833 | ||
|
|
b1cfaf53b8 | ||
|
|
b370908174 | ||
|
|
7650abb755 | ||
|
|
82813fb13b | ||
|
|
4b1108bf86 | ||
|
|
9898e057fd | ||
|
|
7770074503 | ||
|
|
046f2af495 | ||
|
|
303b7b9f24 | ||
|
|
1276fd11e3 | ||
|
|
ec2afe9c93 | ||
|
|
bd5e38594f | ||
|
|
87b0e5d8cd | ||
|
|
212814a75c | ||
|
|
dd9bfd3217 | ||
|
|
b90d63d0f7 | ||
|
|
e7586a1256 | ||
|
|
8440adc742 | ||
|
|
5eb9ee76ff | ||
|
|
b9c13c37c0 | ||
|
|
c0596c5f24 | ||
|
|
9d3c50ca95 | ||
|
|
d0f97bf79a | ||
|
|
6ddb834b1c | ||
|
|
d6e69420e8 | ||
|
|
111cf3d262 | ||
|
|
48fa028086 | ||
|
|
4afea7c802 | ||
|
|
53a1e69af7 | ||
|
|
4661a445be | ||
|
|
22a6b30494 | ||
|
|
a3b7d2015e | ||
|
|
6953254349 | ||
|
|
61633cb492 | ||
|
|
f67b519e75 | ||
|
|
2d0cec90e4 | ||
|
|
20c6d86f0e | ||
|
|
2c20f9eaea | ||
|
|
fcb16fd265 | ||
|
|
874c796c65 | ||
|
|
d5ce5d675d | ||
|
|
1cf25eff30 | ||
|
|
61ce0dee4b | ||
|
|
7661fe9d24 | ||
|
|
d696463c68 | ||
|
|
75210e56f3 | ||
|
|
075e58187e | ||
|
|
29ca99af7f | ||
|
|
a1dfc0422e | ||
|
|
61106be142 | ||
|
|
5aa285c3ef | ||
|
|
7d1808fdf2 | ||
|
|
7d92e148a9 | ||
|
|
20acf57678 | ||
|
|
94d50edb47 | ||
|
|
bb082f3ae9 | ||
|
|
052b6de354 | ||
|
|
38f6009200 | ||
|
|
2cb3344e1b | ||
|
|
89fd9a0021 | ||
|
|
2fea2710f7 | ||
|
|
6f2d33e0a9 | ||
|
|
666dfb811c | ||
|
|
c152708a67 | ||
|
|
f322b1c089 | ||
|
|
a96fd0b95b | ||
|
|
05b1523c54 | ||
|
|
7f64e2e0aa | ||
|
|
b6ac950073 | ||
|
|
10580650cb | ||
|
|
b0da995978 | ||
|
|
f2de8fe9c6 | ||
|
|
90421d0655 | ||
|
|
41716f00b8 | ||
|
|
b0a14262b1 | ||
|
|
0e730cdbba | ||
|
|
247682a8e0 | ||
|
|
782f195bdb | ||
|
|
b61ccff549 | ||
|
|
d4b4377d30 | ||
|
|
566d1f342a | ||
|
|
de14f0042d | ||
|
|
b3af0e589e | ||
|
|
08ceed200d | ||
|
|
3b37d5702e | ||
|
|
aeeedaa73d | ||
|
|
557f6aac30 | ||
|
|
631dd57eb4 | ||
|
|
2656d81ac9 | ||
|
|
16612acba4 | ||
|
|
d4fa89204d | ||
|
|
a40b0454b3 | ||
|
|
5602a281b9 | ||
|
|
9ffe296cd1 | ||
|
|
a3cb26b5c8 | ||
|
|
6ec4a8f856 | ||
|
|
ee5b05b357 | ||
|
|
97c0455939 | ||
|
|
7e3f926352 | ||
|
|
4f4e67c8ca | ||
|
|
8c068ee2cd | ||
|
|
5441c7da5c | ||
|
|
81b7d6d0ff | ||
|
|
3f300b912a | ||
|
|
05f62cff36 | ||
|
|
a443eb8546 | ||
|
|
8d29c09512 | ||
|
|
61b1c9c329 | ||
|
|
413983f2d3 | ||
|
|
8b87749771 | ||
|
|
35e3d149d2 | ||
|
|
bc0f9ee791 | ||
|
|
ca78352e43 | ||
|
|
d6555df5fa | ||
|
|
0a7492ffab | ||
|
|
f8a46ecf8d | ||
|
|
78641da0bb | ||
|
|
d6e021a80b | ||
|
|
43c11ccaa8 | ||
|
|
fd9f809c1c | ||
|
|
41a9265ebd | ||
|
|
3b4d6855fe | ||
|
|
41b001b652 | ||
|
|
97a1874498 | ||
|
|
6d2a068308 | ||
|
|
153798f436 | ||
|
|
3de7ce1420 | ||
|
|
1568f76ca2 | ||
|
|
bf22f0ffe0 | ||
|
|
ce6812dbeb | ||
|
|
0bcd67a016 | ||
|
|
116b57f01e | ||
|
|
0b8fbc5591 | ||
|
|
593dfac1d8 | ||
|
|
f83c4bd8c1 | ||
|
|
8117d118eb | ||
|
|
63fb71fe6a | ||
|
|
6a2d701eab | ||
|
|
20619e587d | ||
|
|
41fcef692a | ||
|
|
b6529719dd | ||
|
|
3bd0fd36df | ||
|
|
c4397a5fb7 | ||
|
|
e960b9c7e3 | ||
|
|
c43eebbf9a | ||
|
|
46f7f5f7c1 | ||
|
|
26fbf0c999 | ||
|
|
c91b63d7c2 | ||
|
|
26984d38d8 | ||
|
|
6fd8a88986 | ||
|
|
3f629f407a | ||
|
|
4753416d4a | ||
|
|
1de44af7cc |
No files matched your search
@@ -9,11 +9,26 @@
|
||||
!/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.
|
||||
!/Tools/
|
||||
!/Tools/**
|
||||
/Tools/**/__pycache__/
|
||||
|
||||
# Closed / historical documents.
|
||||
!/Docs/
|
||||
!/Docs/**
|
||||
|
||||
!VoxelForge.uplugin
|
||||
|
||||
# Keep every design / doc markdown at any depth (AUDIT P1 — these were untracked)
|
||||
# Keep every design / doc markdown at any depth
|
||||
!*.md
|
||||
/Standalone/data/*.log
|
||||
/Standalone/data/*.err
|
||||
/Standalone/data/*.vfmesh
|
||||
/Standalone/Research/**/*.log
|
||||
@@ -25,11 +25,21 @@ try to run anything.
|
||||
## Hard rules (these prevent real bugs — verify against code, don't assume)
|
||||
- **Density sign: negative = solid, positive = air** (MC convention at the mesher).
|
||||
`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 32³-voxel storage tile. The legacy 32³ chunk
|
||||
spans 4 m at 12.5 cm. Strate placement uses the separate world-level `StrateLayoutChunkSizeMeters`
|
||||
setting (8 m by default), resolved once and shared by every generator. At 25 cm that layout unit is
|
||||
32 Core voxels; at 20 cm it is 40. Never infer the layout unit from `CHUNK_SIZE` or the Core tile.
|
||||
Conversions live in `VoxelTypes.h`, the director, 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 ⇒
|
||||
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
|
||||
`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).
|
||||
- **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.
|
||||
|
||||
@@ -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
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
- ⛔ **"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
|
||||
had him authoring strates while the system arranged them — one level too shallow, and it shaped a
|
||||
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
|
||||
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
|
||||
acceptance conditions. Both now met:
|
||||
- **`violations` = 0** everywhere. Measured: TunnelNetwork at production defaults **12 of 40 tiles
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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):
|
||||
> • **Terracing gradient Z-only** (6→2 SDF samples — see the ticked item above).
|
||||
> • **Lerp X-macro** + **BakeRoomFeature dedupe** (both ticked above, bit-identical).
|
||||
> • **T2.b LOD octave drop** — opt-in `UVoxelSettings::LODOctaveDrop` (default 0 = byte-identical);
|
||||
> `VoxelGenLOD::OctaveBias` thread_local set per tile in `GenerateMesh`, per-voxel fractal sites
|
||||
> 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.
|
||||
> • **T2.b / T2.c / T2.d and F2** shipped in the same batch — tracked in `fable-idea.md` (Tier 2
|
||||
> status, F2) and ARCHITECTURE §8.10, not repeated here.
|
||||
|
||||
## Performance
|
||||
- [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`).
|
||||
**CORRECTION: `GetStrateChunkZBounds` is NOT dead** — `BuildDesiredTiles` uses it for the
|
||||
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)
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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}
|
||||
@@ -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;
|
||||
}
|
||||
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 406 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 429 KiB |
|
After Width: | Height: | Size: 498 KiB |
@@ -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.
|
||||
@@ -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.
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -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.
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
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"
|
||||
}
|
||||