From 8a33bcb42a277ba5a43aa820442221bff51ade0c Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Mon, 27 Jul 2026 02:33:00 +0200 Subject: [PATCH] docs: CODEMAP rows + progress entry for the Phase 1 Maze port CODEMAP gains 3.2c (VoxelDensityPrimitives), 3.2d (the operator stack and its factories), FVoxelOpSample under 3.2b, and the two new tests under 3.12. Co-Authored-By: Claude Opus 5 --- CODEMAP.md | 25 +++++++++++++++++++++++++ OPSTACK-PROGRESS.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/CODEMAP.md b/CODEMAP.md index 43e5b1c..db68b96 100644 --- a/CODEMAP.md +++ b/CODEMAP.md @@ -106,8 +106,31 @@ Paths relative to `Source/VoxelForge/`. `Public/` = headers, `Private/` = impl. | `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. | +| `FVoxelOpSample` | The state threaded through the stack: **two** channels, `Density` (INTERNAL convention, **positive = SOLID**, negated to MC once by the caller) and `Sdf` (standard SDF, negative = inside). ⚠️ `min()` therefore means opposite things on the two channels. | | `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.2c Structural primitives — `Public/VoxelDensityPrimitives.h` +`VF_ApplyOriginSpine` · `VF_ApplyBoundarySeal` · `VF_ApplyPassageCarving` — the three world +invariants every archetype appends, **moved here 2026-07-27** so the generator and the operator +stack share ONE copy. `VoxelGenerator.cpp` keeps same-named `static FORCEINLINE` forwarders so its +~20 call sites are unchanged; bodies are byte-identical. Also `VoxelDensityReach::SpineBlend` / +`PassageBlend`, the blend radii `ClassifyTile` currently hand-duplicates. +**Convention: INTERNAL (positive = solid).** + +### 3.2d Operator stack — `Public/VoxelDensityOpStack.h` + `Private/VoxelDensityOpStack.cpp` +⚠️ **Feeds nothing yet.** `GetDensityAt`/`ClassifyTile` are untouched; the archetype `switch` is +still the only production path. Exercised solely by `VoxelForge.OpStack.MazeEquivalence`. + +| Symbol | Role | Notes | +|--------|------|-------| +| `FVoxelOpStack` | — | Ordered `TUniquePtr` list. `PrepareChunk` / `EvalInternal` / `EvalMC` / `ClassifyBox` (the fold, with an early-out when both hypotheses die). | +| `FVoxelOpStack::AppendStructuralPost` | 4 | Appends spine → seal → passage **in that fixed order**. An author cannot omit or reorder them. The diff layer is NOT here yet — it still lives in `GetDensityAt` after the MC negate, with disturbances. | +| `VoxelDensityOps::MakeConstantRockSource` | 1 | `Density = BaseDensity`. `ClassifyBox` → **AllSolid**, exact and free. Shared by TunnelNetwork, Maze, VerticalShafts and bedrock gaps. | +| `VoxelDensityOps::MakeLatticeCorridorSource` | 1 | Maze corridors, SDF channel. Edge identity = `hash(lower node, axis)` ⇒ adjacent chunks cannot disagree (AUDIT §6.4's preferred pattern). Its `EffectOverBox` answers for the source+carve **pair** (Phase 1 simplification) so it must be told the downstream `ExtraReach`. | +| `VoxelDensityOps::MakeSdfRoughnessMod` | 3 | Wall roughness in **SDF** space (Maze/Shafts/Islands variant). TunnelNetwork's density-space roughness is a **different op** — see OPSTACK-DECOMPOSITION §1. | +| `VoxelDensityOps::MakeSdfCarve` | 2 | SDF → density carve. The same six lines currently copied in three archetypes. | +| `VoxelDensityOps::BuildMazeStack` | — | The 7-op Maze stack. If this ever becomes one op, the refactor failed its own test (§2.5). | + ### 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). @@ -344,6 +367,8 @@ The plugin's first tests (`OPSTACK-PLAN.md` Phase 0.5). Run them from the editor | ″ | `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`. | +| `VoxelForgeClassifyTileTest.cpp` | `VoxelForge.OpStack.BoxVerdictFold` | Pure-logic walk of the fold in `VoxelDensityOp.h`, case by case — including the seal-forces-AllSolid case that justifies `ClassifyBox` existing. Also the only `.cpp` that includes the op header, so the build actually sees it. | +| `VoxelForgeOpStackMazeTest.cpp` | `VoxelForge.OpStack.MazeEquivalence` | **Phase 1's load-bearing test.** The 7-op Maze stack vs `GetMazeDensity` over 20k points (aiming for bit-identity; a side-of-iso disagreement is the hard fail), plus purity across workers and brute force on every box verdict the stack emits. Reports how many tiles the stack can prove uniform — today's `ClassifyTile` proves **zero** for any cave archetype. | ## 4. The density pipeline (most-edited hot path) diff --git a/OPSTACK-PROGRESS.md b/OPSTACK-PROGRESS.md index 57791c7..d05cde5 100644 --- a/OPSTACK-PROGRESS.md +++ b/OPSTACK-PROGRESS.md @@ -211,3 +211,48 @@ edits to `VoxelDensityOp.h` and `VoxelGenerator.cpp` (include the primitives, de **Next single action:** write those files, then STOP. --- + +## 2026-07-27 — Phase 1 done (written, not built). Maze DOES decompose. + +**The Phase 1 question is answered.** `OPSTACK-PLAN §4`'s stop-trigger asked whether the +source/modifier split falls out naturally from the existing code. It does — Maze becomes seven ops +with no contortion, and three of them are already shared with other archetypes: + +``` +FConstantRockSource ← also TunnelNetwork's and VerticalShafts' first line +FLatticeCorridorSource ← Maze only (role 1: what makes a maze a maze) +FSdfRoughnessMod ← also VerticalShafts, FloatingIslands +FSdfCarveOp ← the same six lines currently copied in three archetypes +FOriginSpineOp ─┐ +FBoundarySealOp ├─ identical in all six density functions +FPassageCarveOp ┘ +``` + +No revert, no stop-trigger. Commit `4c53d3b`. + +**What is deliberately NOT wired:** `GetDensityAt` and `ClassifyTile` are untouched, so nothing in a +running world can change. `OPSTACK-PLAN §4` Phase 1 step 3 ("GetDensityAt gains one branch") is +**held back until the build is green** — wiring an uncompiled stack into the hot path would be +exactly the stacked-unverified-work pattern `AUDIT §P3` documents. The port is validated instead by +`VoxelForge.OpStack.MazeEquivalence`. + +**Two contract decisions taken (Jahni delegated them):** two-channel `Eval`, and INTERNAL sign +convention inside the stack. Both are argued in the previous entry and in the commit message. The +second **reverses** what `VoxelDensityOp.h` said on 2026-07-26 — if a later context finds MC-in-stack +written anywhere, that text is stale. + +**One pre-existing hairline bug found and NOT silently patched:** at the inner edge of a seal band, +`1 - Dist/Thickness` can round to exactly `0.0f`, so `SealFactor·BaseDensity` is 0, internal density +lands on 0, and the mesher's `D >= IsoLevel` counts that point as AIR. Today's `ClassifyTile` +excludes those z from column testing and can therefore emit `AllSolid` over them. It needs the +archetype to produce air at exactly that z, so the window is hairline — but a false `AllSolid` is a +hole. The **new** seal op keeps a 1-voxel safety margin before it forces. The old path is untouched; +`VoxelForge.Determinism.ClassifyTileSoundness` would catch it if it ever fires. + +**UNVERIFIED:** everything, still. Nothing has been compiled. + +**Next single action: BUILD.** Then, in order: fix what the tests report → read +`MazeEquivalence`'s two numbers (how many samples differ, and how many tiles the stack can prove +uniform) → only then wire the stack into `GetDensityAt` behind a per-strate opt-in. + +---