docs: Q3 tick the stale PENDING BUILD markers + CODEMAP rows for the new symbols
Q3: fable-idea F20 phases 1/2 and F18, REVIEW_FINDINGS perf pass 2 and batch 3, and ARCHITECTURE's biome full-param redesign were all still carrying "CODE-COMPLETE, PENDING BUILD" markers dated 07-04/-06/-08. Jahni confirmed on 2026-07-26 that everything is built and working (AUDIT-2026-07.md §0), so the documents were misreporting project state. Ticked with the date they were ticked, not just the date they were built. Deliberately NOT ticked: ARCHITECTURE's F6 master material graph. Its C++ half is built, but the material graph itself is editor-side work that is genuinely still open, and ticking it would recreate the problem this queue item fixes. CODEMAP discipline for this batch: new §3.2b (the VoxelDensityOp contract), new §3.12 (Private/Tests), the EVoxelTileClass move into §3.2, and FChunkBiomeCache::Invalidate under the biome types row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+3
-2
@@ -625,7 +625,7 @@ change *anything*, e.g. frequencies, which scalar multipliers couldn't.)
|
||||
- **Preview tool.** `AVoxelWorld::BakeBiomePreview()` (CallInEditor) bakes biome / relief / moisture
|
||||
to `Saved/BiomePreview.png` via a transient generator (no PIE). Needs the `ImageWrapper` module.
|
||||
- **Status:** A (field+asset+preview), B (terrain), C (content/atmosphere) verified in-editor.
|
||||
Full-param redesign (surface output-blend) code-complete, pending build. Cave structural biomes
|
||||
Full-param redesign (surface output-blend) ✅ BUILT & WORKING (ticked 2026-07-27). Cave structural biomes
|
||||
deferred (determinism, see above). Per-voxel biome warp (+2 Perlin) & content `GetDominantBiomeAt`
|
||||
are future T1.a column-cache candidates.
|
||||
|
||||
@@ -657,7 +657,8 @@ generic biome field), not just SurfaceWorld. Empty `Biomes[]` ⇒ all-zero colou
|
||||
- **Perf.** Free where a strate has no biomes (`GetBiomeMaterialAt` early-outs to palette 0). Otherwise
|
||||
one biome resolve per unique vertex, bounded by the per-chunk biome cache (don't feed it a chunk key —
|
||||
keep the box validity, §8.10). Coarse far tiles have few vertices.
|
||||
- **Status:** C++ code-complete, pending in-editor build + the master material graph (editor-side work).
|
||||
- **Status:** C++ ✅ BUILT & WORKING (ticked 2026-07-27). The master material graph is still
|
||||
editor-side work and is deliberately NOT ticked — that half is Jahni's, not the code's.
|
||||
|
||||
## 9. Multiplayer model (listen-server first, dedicated-friendly)
|
||||
|
||||
|
||||
+32
@@ -90,8 +90,24 @@ Paths relative to `Source/VoxelForge/`. `Public/` = headers, `Private/` = impl.
|
||||
| `LocalToIndex` / `IndexToLocal` / `IsValidLocalCoord` | 107-131 | Flat-array 3D↔1D indexing. |
|
||||
| `SmoothStep01` | 140 | 3x²-2x³ — used everywhere for blends. |
|
||||
| `VOXEL_NOISE_SCALE` (1.25f) | 147 | Rescales UE PerlinNoise3D to ~[-1,1]. |
|
||||
| `EVoxelTileClass` enum (`Mixed`/`AllSolid`/`AllAir`) | — | T1.d verdict. **MOVED here from `VoxelGenerator.h` 2026-07-27** so `VoxelDensityOp.h` can share it without a UCLASS dependency. A false `AllSolid`/`AllAir` is a HOLE; a false `Mixed` only costs CPU. |
|
||||
| `FVoxelMeshData` struct | 157-173 | Mesher output (Vertices/Triangles/UVs/Normals/**Colors**). Plain C++, not USTRUCT. `Colors` = F6 material masks (R=dominant biome palette, G=slope, B=border blend weight, A=neighbour biome palette). §8.15. |
|
||||
|
||||
### 3.2b Density operator stack contract — `Public/VoxelDensityOp.h` (plain C++, no UHT)
|
||||
**Phase 1 skeleton, added 2026-07-27. Nothing is wired in yet** — `GetDensityAt`'s archetype
|
||||
`switch` is untouched and no operator exists. See [OPSTACK-PLAN.md](OPSTACK-PLAN.md) for the plan and
|
||||
[OPSTACK-DECOMPOSITION.md](OPSTACK-DECOMPOSITION.md) for the per-archetype breakdown.
|
||||
|
||||
| Symbol | Notes |
|
||||
|--------|-------|
|
||||
| `EVoxelOpRole` | The four roles: `FieldSource` (what the field IS) · `Combiner` (how fields merge) · `DetailModifier` (today's `UVoxelTerrainOpDefinition`) · `StructuralPost` (spine→seal→passage→diff, appended automatically, never author-omittable). |
|
||||
| `EVoxelOpCombine` | `Replace`/`Union`(min)/`Subtract`(max)/`SmoothUnion`/`SmoothSubtract`/`Add`/`Mask`. Sign reminder: negative = solid, so "add solid" is `min`. |
|
||||
| `EVoxelOpEffect` | `Identity`/`CarveOnly`/`FillOnly`/`Both`. Conservative: `Both` is always safe, the wrong one is a hole. |
|
||||
| `FVoxelOpContext` | Chunk-constant inputs. **Carries `LayoutVersion` by construction** so a new op cannot forget it (AUDIT C2). |
|
||||
| `IVoxelDensityOp` | `PrepareChunk` / `Eval` / `EffectOverBox` / `ClassifyBox` / `IsXYPure`. |
|
||||
| `IVoxelDensityOp::ClassifyBox` | ⚠️ **not source-only.** Forcing ops (the boundary seal inside its band) overwrite the input entirely, which pure direction cannot express. |
|
||||
| `FVoxelBoxHypotheses` + `VF_ForceHypotheses` / `VF_FoldEffect` / `VF_FoldOp` | The fold that turns a stack into an `EVoxelTileClass`. Reproduces today's hand-written `ClassifyTile` line for line — the mapping is written out in the header. |
|
||||
|
||||
### 3.3 Chunk identity
|
||||
`VoxelChunk.h` (the old `FVoxelChunk` coord wrapper) was DELETED — dead since the tile
|
||||
redesign; tile identity lives in `FVoxelTileKey` (VoxelWorld.h).
|
||||
@@ -263,6 +279,10 @@ border warp+blend / climate field freqs), `EBiomePreviewChannel` (preview-bake s
|
||||
`FVoxelBiomeQuery` (BlueprintType result of `GetBiomeAtWorldLocation` — dominant/neighbour asset,
|
||||
climate, blend weight, deco count), and plain runtime PODs `FBiomeResolved` / `FBiomeContext` /
|
||||
`FBiomeSample` / `FChunkBiomeCache` (the box-validated per-chunk grid cache). See §8.14.
|
||||
`FChunkBiomeCache::Invalidate()` (added 2026-07-27, AUDIT C2) — force a rebuild when the strate
|
||||
layout version moves. The validity BOX says nothing about the `FBiomeContext` the cells were
|
||||
classified against, so after a `RebuildStrates` the grid is stale even though the box still covers
|
||||
the query. Called by all four callers on a `GetLayoutVersion()` change.
|
||||
|
||||
**`Public/VoxelBiomeDefinition.h` + `.cpp`** (NEW) — `UVoxelBiomeDefinition : UPrimaryDataAsset`.
|
||||
One asset = one biome: identity + `DebugColor`, climate placement box (`ReliefMin/Max`,
|
||||
@@ -313,6 +333,18 @@ Bourke). Cube corner/edge layout documented at top (lines 7-37). Rarely needs ed
|
||||
|
||||
---
|
||||
|
||||
### 3.12 Automation tests — `Private/Tests/` (added 2026-07-27, `#if WITH_DEV_AUTOMATION_TESTS`)
|
||||
The plugin's first tests (`OPSTACK-PLAN.md` Phase 0.5). Run them from the editor's
|
||||
**Session Frontend → Automation**, filter `VoxelForge`.
|
||||
|
||||
| File | Test name | What it proves |
|
||||
|------|-----------|----------------|
|
||||
| `VoxelForgeTestFixture.h` | — | `FTestWorld`: a headless world (transient strate definitions → `UVoxelSettings` → a real `UVoxelStrateManager::Initialize`) so tests hit `GetDensityAt`, where the thread_local caches live. One strate per archetype, **pinned via `FixedStrates`** so slot index → archetype is stable across seeds (`SlotSurfaceWorld` etc.). |
|
||||
| `VoxelForgeDensityPurityTest.cpp` | `VoxelForge.Determinism.DensityPurity` | 10k points re-sampled in shuffled order, same thread **and** on N workers, asserting BIT equality. `ValidateDeterminism` is game-thread only and cannot see worker-cache divergence. Includes a flat-field canary (AUDIT C1) and a diff-layer pass. |
|
||||
| ″ | `VoxelForge.Determinism.LiveEditInvalidation` | AUDIT C2 regression: triple the heightfield params, `Initialize` again, require the density to MOVE. The edit does not move the strate, so only `LayoutVersion` changes. |
|
||||
| `VoxelForgeClassifyTileTest.cpp` | `VoxelForge.Determinism.ClassifyTileSoundness` | Scans for a non-`Mixed` verdict, then brute-forces the exact mesher lattice (`g ∈ [-1, Cells+1]`). **A false verdict is an invisible, collisionless hole** — T1.d v1 was reverted for exactly this. Errors out rather than passing if it found nothing to check. |
|
||||
| `VoxelForgeDiffLayerTest.cpp` | `VoxelForge.Determinism.DiffLayerContention` | N readers running the worker call mix while the game thread writes and `Clear()`s. Survival + monotonic `ModsVersion`. |
|
||||
|
||||
## 4. The density pipeline (most-edited hot path)
|
||||
|
||||
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:277
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ Legend: ✅ verified against code · ◻️ checklist box.
|
||||
> • **`UploadDirtyTextures` now calls `EnsureTextures`** — GPU upload toggled ON at runtime works.
|
||||
> Deliberately NOT done: the big behavior-preserving splits below (un-built tree; compile risk).
|
||||
|
||||
> **2026-07-04 perf pass 2 (per-voxel hot path, Fable 5)** — CODE-COMPLETE, pending build. All
|
||||
> **2026-07-04 perf pass 2 (per-voxel hot path, Fable 5)** — ✅ BUILT & WORKING (ticked 2026-07-27). All
|
||||
> bit-identical (same hashes/math, hoisted per chunk/cell):
|
||||
> • **DiffLayer snapshot API** (`HasAnyMods`/`GetModsVersion`/`GetChunkModsSnapshot`/static
|
||||
> `EvaluateMods` + `ModsVersion` atomic) — `GetDensityAt` snapshots a chunk's mods once per
|
||||
@@ -46,7 +46,7 @@ Legend: ✅ verified against code · ◻️ checklist box.
|
||||
> detection stays (MIDs own their values).
|
||||
|
||||
> **2026-07-04 batch 3 (Fable 5)** — Jahni green-lit multiple changes per build + visual deltas
|
||||
> ("nothing is set in stone"). PENDING BUILD together with pass 2:
|
||||
> ("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);
|
||||
|
||||
+4
-4
@@ -116,8 +116,8 @@ Voronoi field).** Original sketch (kept for the cave-biome extension): Determini
|
||||
|
||||
**F17 — Generator surface-class tag (ceiling/ground/cave material the *right* way). ✅ DONE 2026-07-05** (per-vertex semantic class in the mesher — down-facing verts query a memoized `GetSurfaceHeightAt`, nearer CeilSurf ⇒ sky-cap — per-tri majority → two contiguous polygroup runs → RMC section per group, slot 1 = `CeilingMaterial`, per-section shadow. Trigger: the whole-tile normal vote painted mixed coarse tiles with one material. Cave-roof discrimination hook is in place: down-facing below TerrainZ ⇒ ground/rock. Remaining polish idea: fully sideways cap-fold tris (all 3 verts |N.Z|≤0.1) default to ground.) Original design note: ★ do this when caves land. Today `ApplyMeshToTile` picks one material per tile from a ceiling test — first a height-oracle sample (midpoint), now a worker-side **normal vote** over the tile's mesh normals (down-facing ⇒ `bIsCeiling` ⇒ `CeilingMaterial` + no shadow; gated to SurfaceWorld by one `GetSurfaceHeightAt` probe). That's a **stopgap that only works because down-facing == sky-cap *while no caves exist*.** The moment a mountain-biome cave uses the same density/mesh system, its roof is also down-facing and would wrongly get the sky-cap material — orientation can't tell a cave ceiling from a surface ceiling. **The discriminator is semantic, not geometric, and only the generator knows it:** a sky-cap surface is the `ComputeSurfaceCeiling` (`CeilSurf`) boundary; a cave ceiling is a 3D-noise **carve** below `TerrainZ`. Cheap test the generator already has the inputs for — at a down-facing surface vertex, compare world Z to the column's `TerrainZ`/`CeilSurf` (both from the surface-column cache the mesher already holds): near `CeilSurf` ⇒ sky-cap, below `TerrainZ` ⇒ cave. **Plan:** stamp a discrete *surface class* (ground / sky-cap / cave-ceiling / cave-wall…) per vertex/triangle **at mesh time** in the mesher → carry it as the **polygroup** (already enabled, `Builder.EnablePolyGroups()`, every tri currently group 0) → `ApplyMeshToTile` maps polygroup → material slot (slot 0 terrain, slot 1 sky-cap, slot 2 cave-rock, biome-specific via the F5 palette mask) and sets per-section shadow. Discrete "which material" → polygroup/slot; continuous masks (biome blend, slope — F6) stay in `Colors`. This **subsumes** the current ground/ceiling split (it falls out as a special case), fixes the coarse mixed-tile horizon artifact exactly (per-triangle, not per-tile dominant-wins), and is the only version that survives caves. Pairs naturally with F5/F6/F8 (all want generator-stamped per-vertex material identity). Cost: a per-tri classify in the mesher (cheap, has the cache) + multi-slot setup in the apply path (RMC supports it; confirm the v5 per-section `UpdateSectionConfig` / slot-per-polygroup calls + empty-polygroup = no draw). Until then: the normal vote is fine — it's commented as "no caves yet → down == cap."
|
||||
|
||||
**F18 — Far-field per-surface SHEETS (the render-distance ring, cheap). ✅ code-complete 2026-07-06
|
||||
(pending build).** With `RenderDistanceChunks` the
|
||||
**F18 — Far-field per-surface SHEETS (the render-distance ring, cheap). ✅ BUILT & WORKING 2026-07-06**
|
||||
(marker ticked 2026-07-27). With `RenderDistanceChunks` the
|
||||
outermost ring can reach many km — as MC tiles that's 600-1000 primitives paying per-frame visibility/VSM
|
||||
forever, and each far tile runs full 3D marching cubes just to rediscover two heightfields. In an open
|
||||
strate the far field IS two heightfields the generator already computes per column (`GetSurfaceHeightAt`:
|
||||
@@ -204,7 +204,7 @@ fade with slope ⇒ seamless at biome borders; placement hashes are pure `(seed,
|
||||
"ops finally work on the surface" win; **(2)** overhang (3D band, slope-conditioned); **(3)** spike/hole (placed +
|
||||
shortlist + ClassifyTile guard) — most cost, do last.
|
||||
|
||||
**PHASE 1 — CODE-COMPLETE, PENDING BUILD (2026-07-08).** Heightfield ops shipped: **Cliff** (slope-gated STEEPENING —
|
||||
**PHASE 1 — ✅ BUILT & WORKING** (built 2026-07-08; marker ticked 2026-07-27 — confirmed working by Jahni 2026-07-26, see `AUDIT-2026-07.md §0`). Heightfield ops shipped: **Cliff** (slope-gated STEEPENING —
|
||||
push height from the local mean where steep ⇒ sheer walls; the slope-conditioned one, hugs steep terrain;
|
||||
v1 band-snap was too subtle, reformulated to steepening after Jahni's "doesn't change much"), **Terrace** (relief-gated plateau quantize, now with
|
||||
`TerraceHardness` soft-round↔crisp-mesa), **LayerLines** (sedimentary sine shelves, slope-expressed). *Design
|
||||
@@ -221,7 +221,7 @@ height oracle `ComputeSurfaceTerrainZ` (new `SampleSurfaceStructuralZ` helper =
|
||||
an XY offset for Cliff's slope) so MC/sheets/ClassifyTile/deco/BP-bridge all agree, no T1.d interference. Revisit
|
||||
the array+condition model for **phase 2 (overhangs)** where per-entry slope-gating earns its keep.
|
||||
|
||||
**PHASE 2 — CODE-COMPLETE, PENDING BUILD (2026-07-08).** Overhang (first VOLUMETRIC op) as
|
||||
**PHASE 2 — ✅ BUILT & WORKING** (built 2026-07-08; marker ticked 2026-07-27 — confirmed working by Jahni 2026-07-26, see `AUDIT-2026-07.md §0`). Overhang (first VOLUMETRIC op) as
|
||||
`FSurfaceGenerationParams` fields (`OverhangStrength/Reach/Height/Frequency/ZScale/SlopeThreshold`, default
|
||||
off). **Design NOTE — v1 additive-noise-band was WRONG (Jahni: "does nothing" + sketch of a real cliff lip):
|
||||
band-additive noise can only bump the surface where it already is, never make rock jut OUT over a void.**
|
||||
|
||||
Reference in New Issue
Block a user