feat: port FloatingIslands — the stack that runs backwards

6 of 8 archetypes ported. This one starts from VOID and FILLS where the
other four start from ROCK and CARVE, which is what it was worth doing:
neither end of the pile needed a new operator, only the opposite sign.

  FConstantRockSource -> FConstantFieldSource(+/-Base)   AllSolid <-> AllAir
  FSdfCarveOp         -> FSdfConvertOp(Sign = +/-1)      carve    <-> fill
  FSdfRoughnessMod                                       4th archetype, unchanged

Only the island blob source is new. Multiplying by +/-1 is exact in
IEEE-754, so the three already-green ports are bit-for-bit untouched.

ClassifyBox can return AllAir for the first time in the plugin, and an
island strate is by construction mostly empty — the test counts AllSolid
and AllAir separately so an aggregate cannot hide whether that fired.

Two bounds that would have been holes if assumed rather than derived:
the island bound is one-sided (a hairline thread of matter hangs below
each island down its axis, so only the TOP may reject), and the domain
warp displaces X and Y independently, so the pad needs WarpAmp*sqrt(2).

Also: AUDIT C1 was NOT closed. The 2026-07-27 sweep matched `SeedF * K`
and this archetype's warp spells it `(float)S * K`, so one site survived
— at seed 2e9 the warp flattens and every island snaps back to a perfect
circle. Fixed in both paths in one pass so the equivalence test stays a
valid oracle. Expect island silhouettes to change at large seeds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 17:50:36 +02:00
parent f5d5a03ad1
commit 96e75abe57
11 changed files with 901 additions and 35 deletions
+106
View File
@@ -1797,3 +1797,109 @@ things Phase 2 invented (height space, `IVoxelBiomeField`), and the method lesso
in build cycles.
---
## 2026-07-28 — FloatingIslands ported. 6 of 8. The stack runs BACKWARDS, and §C1 was NOT closed.
**The portage that tests the AXIS, not the fidelity.** `VerticalShafts` measured reuse *by identity*
— three of Maze's ops, not a line changed. This one measures something stronger and riskier for the
abstraction: **reuse by INVERSION**.
The four archetypes ported so far all start from ROC and CARVE. FloatingIslands starts from the VOID
and FILLS. If the abstract axis chosen back in §0.1 — the *sign* of the internal density — is the
right one, then both ends of the pile must be the same operators negated:
```
FConstantFieldSource(+Base) ←→ FConstantFieldSource(-Base) ClassifyBox: AllSolid ←→ AllAir
FSdfConvertOp(Sign = -1) ←→ FSdfConvertOp(Sign = +1) carve ←→ fill
FSdfRoughnessMod ←→ FSdfRoughnessMod 4ᵉ archétype, inchangé
```
And it holds: **the only new operator in this port is the island blob.** 7 ops total.
Two classes were merged rather than duplicated (`FConstantRockSource``FConstantFieldSource`,
`FSdfCarveOp``FSdfConvertOp`), each with two factories so the *authoring* vocabulary keeps saying
"rock"/"void" and "carve"/"fill". Multiplying by ±1 is exact in IEEE-754, so the three green ports
are bit-for-bit untouched — that claim is load-bearing and the next run tests it.
### `ClassifyBox` can say **AllAir** for the first time
No cave archetype has ever proved "all air"; `FConstantVoidSource` can, trivially and exactly, and a
floating-island strate is *by construction* mostly empty. That is `OPSTACK-DECOMPOSITION §7`'s claim,
and the test counts AllSolid and AllAir **separately** — an aggregate "N proved" would have hidden
precisely the number that matters. If AllAir comes back 0, the stack is still sound and the perf
argument simply did not fire (the VerticalShafts situation); the test says so out loud rather than
looking green.
### ⚠️ The bound is ONE-SIDED, and assuming otherwise would have been a hole
`Sdf ≥ WorldZ TopSurf` bounds an island from above. **There is no bound from below:** under an
island the SDF degenerates to ≈ `DistXY`, so a hairline thread of matter hangs down the axis to the
strate floor. Rejecting a box for sitting below an island would be a hole in the original's own
geometry. Only the top rejects.
Second trap, caught by doing the arithmetic rather than eyeballing it: the domain warp displaces X
and Y with **two independent** noise samples, so the point moves along the diagonal — the pad needs
`WarpAmp·√2`, not `WarpAmp`. A 1× pad is wrong by 41 % exactly where both noises saturate together:
rare, plausible-looking, and effectively unreachable by random testing.
### ⚠️⚠️ `AUDIT §C1` was reported closed on 2026-07-27. It was one site short.
Found by reading `GetFloatingIslandDensity` line by line to port it:
```cpp
const float WX = WorldX + FractalNoise3D(FVector(WorldX * 0.04f + (float)S * 0.0007f, ...));
```
**The sweep matched `SeedF * K`; this site spells it `(float)S * K`.** A textual sweep finds a
spelling, not a bug. And the property test could not compensate — `LargeSeedSurvives` asserts the
*heightfield* still varies, while this site perturbs an *island outline*: at seed 2e9 the term hits
~1.4e6, ULP 0.125 against a 0.04 voxel step, so the warp flattens and every island snaps back to a
perfect circle. Cosmetic, not catastrophic, which is exactly why nothing screamed for two days.
Fixed in **both** paths in one pass so `FloatingIslandEquivalence` stays a valid oracle. Recorded in
`AUDIT §C1` with the sharp edge that came with it: `SeedOffset` quantises its site key by ×100, so
`0.0007f` lands on site **0** — unique today (every other key is ≥ 0.19), silently collidable
tomorrow.
**This is the third time this session that a confident premise failed a check.** C1's *documented*
fix was wrong; "C9 is gone after FPSemantics" was wrong; now "C1 is closed, 0 left behind" was wrong.
The pattern is stable enough to plan around: **a claim about the code is evidence about whatever was
actually examined, and nothing else.**
### Deviation from `§7`, stated
`§7` sketched a `FRAME IslandWarp` wrapping the source. The warp stays **inside** the op. Frames are
worth building at the *second* real user, and two of the three (TunnelNetwork's cave warp, its tunnel
warp) are not ported. Designing an abstraction against one example is what this refactor has avoided
throughout — `IVoxelBiomeField` exists because a second, concrete need appeared. Revisit at
TunnelNetwork.
Also carried over from the shaft port: the 3×3 memo key includes `BoundarySealThickness`, **which the
original omits** although `SpreadZ` reads it. Same family as `§C2` and as the overhang regression of
2026-07-27. Adding a field to a cache key can only cost a recompute; leaving one out costs a wrong
world, invisibly.
**Ported: Maze · FlatPlain · CrystalChamber · SurfaceWorld (biomes incl.) · VerticalShafts ·
FloatingIslands — 6 of 8.** The two remaining are really one: `Underwater` *is* TunnelNetwork plus
`WaterLevelRelative` (§8), so the `switch` loses both cases in a single port.
**UNVERIFIED:** nothing here is compiled. Likely spots, in order: the two class renames
(`FConstantRockSource` / `FSdfCarveOp` no longer exist — every reference should go through a factory,
but a missed one is a clean C2065); `FFloatingIslandParams` reaching `VoxelDensityOpStack.cpp`
(it comes via `VoxelStrateTypes.h`, already included, so this should be free); the nested `FCells`
declared before its returning functions (the `FShaftFieldSource` C4430 trap, avoided deliberately);
and `MakeUnique<FIslandBlobSource>` being called from the factory namespace, which is fine only
because the class sits above the end-of-anonymous-namespace line.
**Next single action:** build, run the `VoxelForge` filter — **12 tests** now, the new one is
`VoxelForge.OpStack.FloatingIslandEquivalence`. Watch three numbers in its output: samples inside
island rock (0 ⇒ the equivalence proved only that two voids agree), the AllAir verdict count (0 ⇒ the
perf argument did not fire), and of course the diff count.
**⚠️ EXPECT ISLAND SILHOUETTES TO CHANGE** wherever the seed is large — that is the C1 fix, it is
intended, and §2.6.1 covers it.
Then `Underwater` + `TunnelNetwork` (§8 / §2, **last**, with §8.4's window-invariance discipline).
Perf still parked by Jahni until the transition is complete.
---