feat: SurfaceWorld step 2a — the bridge to density space; fix IsXYPure on the slab
Height stack is green: bit-identical to ComputeSurfaceTerrainZ on both passes, including all four F20 terrain ops on. MaxDisplacement is loose (27% used) and left that way — loose only costs CPU, tight-but-wrong is a hole. FIX: FSlabVoidSource::IsXYPure() returned true and that was wrong. The contract is "Eval does not depend on Z", and Eval computes min(Z - floor, ceil - Z). Section 3.1 made the SURFACES XY-pure; the density is a distance to them and never can be. I conflated the two while writing the operator that quotes the warning against it. Latent only because nothing reads the flag yet — and step 2b is where it would have gone live, since a generic T1.a column cache keyed without ChunkZ would have shared one density down the whole vertical chunk stack. AUDIT 6.3 says that corrupts every chunk silently and ValidateDeterminism would not catch it. That is also the clearest argument for the height-space split: what is XY-pure is the HEIGHT, and in VoxelHeightOp.h it lives in a type with no Z to get wrong. Step 2a: - FSkyCapHeightSource: the ceiling is an altitude, so it belongs in height space rather than density space as section 5 had it — same category slip as the terrain ops. The subtraction happens later, in the combine. - FSurfaceColumnSource: consumes both height stacks, IsXYPure false. - BuildSurfaceStack: source + 3 structural, no per-column memo inside the op since T1.a already exists one level up and a second cache key is a second thing to get wrong. NOT covered, and the test header now says so: the overhang (GetSurfaceDensity passes OverhangAmp = 0, so only the cached path computes it) and biome blending. Both are step 2b; do not wire SurfaceWorld into a biome or overhang world before then. UNVERIFIED: not compiled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1275,3 +1275,65 @@ members being double in UE5 (cast at every use); and the `ComputeSurfaceTerrainZ
|
||||
(bounded seed offsets) still open.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-27 — height stack GREEN (bit-identical, both passes). Step 2a + a real bug in my own §3.1 work.
|
||||
|
||||
```
|
||||
SurfaceWorld(defaults): bit-identical across 20000 samples
|
||||
SurfaceWorld(all terrain ops on): bit-identical across 20000 samples
|
||||
MaxDisplacement: claims 16.300, worst observed 4.441 (27% of claim)
|
||||
```
|
||||
|
||||
**The second op family was the right call** — height space decomposes as cleanly as density did,
|
||||
including with all four F20 terrain ops on. `MaxDisplacement` is loose (27% used) because it sums
|
||||
each op's independent maximum and they never peak at the same XY. **Left loose deliberately:** a
|
||||
loose bound only costs CPU when the heightfield eventually gets a `ClassifyBox`; a tight-but-wrong
|
||||
one is a hole in the world.
|
||||
|
||||
### ⚠️ `FSlabVoidSource::IsXYPure()` was `true`, and that was WRONG
|
||||
|
||||
Found while writing `FSurfaceColumnSource` and having to decide the same flag.
|
||||
|
||||
The contract is *"**`Eval`** does not depend on Z"*. `FSlabVoidSource::Eval` computes
|
||||
`min(Z - floor, ceil - Z)` — Z-dependent in the most direct way possible. **§3.1 made the SURFACES
|
||||
XY-pure; the DENSITY never was and cannot be** — it is a distance to a surface. I conflated the two
|
||||
while writing the very operator that quotes the warning against doing so.
|
||||
|
||||
**Latent only because nothing reads the flag yet** — and step 2b is exactly where it would have gone
|
||||
live: a generic T1.a column cache keyed on (XY box, StrateKey, Seed) with **no ChunkZ** would have
|
||||
shared one density value down the entire vertical chunk stack. `AUDIT §6.3` says this corrupts every
|
||||
chunk silently and that `ValidateDeterminism` would not catch it, because it samples along an X
|
||||
boundary. Fixed, with the distinction written at the site.
|
||||
|
||||
**This is the clearest argument yet for the height-space split:** the thing that is XY-pure is the
|
||||
HEIGHT, and in `VoxelHeightOp.h` it lives in a type with no Z to get wrong. The bug is unrepresentable
|
||||
there.
|
||||
|
||||
### Step 2a — the bridge into density space
|
||||
|
||||
- `FSkyCapHeightSource` — the ceiling is an **altitude**, so it belongs in height space, not in
|
||||
density space as `§5` had it. Same category slip as the terrain ops; the subtraction happens later,
|
||||
in the combine. It gets tested window-invariance and type-enforced XY purity for free.
|
||||
- `FSurfaceColumnSource` — consumes both height stacks, produces
|
||||
`Density = max(TerrainZ - Z, Z - CeilSurf)`. `IsXYPure() = false`, correctly this time.
|
||||
- `BuildSurfaceStack` — source + 3 structural. **No per-column memo inside the op, on purpose:**
|
||||
T1.a already exists one level up in `GetDensityAt`, and a second cache key is a second thing to get
|
||||
wrong in exactly the way described above. Step 2b reuses the existing cache rather than inventing a
|
||||
second one.
|
||||
|
||||
**What step 2a does NOT cover, and the test now says so at the top:** the **overhang** (found while
|
||||
reading: `GetSurfaceDensity` passes `OverhangAmp = 0`, so it computes none — the only reference is
|
||||
the *cached* path, `ComputeSurfaceColumn`) and **biome blending** (weight 0 here; it is the `Mask`
|
||||
combiner and `§5`'s Phase 3 prototype). **Do not wire SurfaceWorld into a world with biomes or
|
||||
overhangs until 2b**, because nothing currently in the tests would say it is wrong.
|
||||
|
||||
**UNVERIFIED:** step 2a is not compiled. Likely spots: `FVoxelHeightStack` as a member of
|
||||
`FSurfaceColumnSource` (move-only member ⇒ the enclosing op is move-only too, which is fine since it
|
||||
lives behind `TUniquePtr`); the new `VoxelHeightOp.h` include in `VoxelDensityOpStack.cpp`; and
|
||||
`Gen->GetSurfaceDensity` taking two param refs plus a weight.
|
||||
|
||||
**Next single action:** build. Then step 2b — the overhang op (per-column gate + per-voxel union,
|
||||
referenced against the cached path), biome blending as the `Mask` combiner, and the wiring that
|
||||
reuses `GSurfColCache`. `§C1` still open.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user