docs: the operator stack is the only density path
- CODEMAP: data-flow diagram, §3.2b/§3.2d, generator and strate-manager rows, test rows and §4.2/§5 now describe the stack-only path; the deleted archetype functions, bUseOperatorStack and the Not Op Stack stats are gone; line hints re-synced for VoxelGenerator.cpp, VoxelStrateManager.cpp and VoxelStrateDefinition.h. - ARCHITECTURE §8.1: archetype table lists stack builders; §8.10 surface column and shaft cache entries name their current owners. - REVIEW_FINDINGS: tick "Operator stack as the only density path"; add the no-strate-manager fallback world as its own open item. - fable-idea: T1.a status names the current column caches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
This commit is contained in:
4 files changed
+150
-150
No files matched your search
+37
-38
@@ -36,7 +36,7 @@ radius, **0.300** against the Maze corridor radius, **0.469/0.625** against shaf
|
|||||||
**0.234** against the minimum island radius (**0.484** including its 6-voxel blend), and
|
**0.234** against the minimum island radius (**0.484** including its 6-voxel blend), and
|
||||||
**0.625/0.938** against slab column radius for floor/ceiling roughness. SurfaceWorld is a
|
**0.625/0.938** against slab column radius for floor/ceiling roughness. SurfaceWorld is a
|
||||||
heightfield, so its **0.047** ratio is against its 80-voxel relief scale, not a fictitious radial
|
heightfield, so its **0.047** ratio is against its 80-voxel relief scale, not a fictitious radial
|
||||||
feature. FlatPlain and CrystalChamber intentionally share `GetSlabDensity` and the same slab
|
feature. FlatPlain and CrystalChamber intentionally share `BuildSlabStack` and the same slab
|
||||||
parameter block; they cannot acquire separate scale geometry without a generator change.
|
parameter block; they cannot acquire separate scale geometry without a generator change.
|
||||||
|
|
||||||
This scale contract is informed by [Epic's Unreal level-blockout guidance](https://dev.epicgames.com/documentation/en-us/unreal-engine/designer-01-project-setup-and-level-blockout-in-unreal-engine), which recommends a player-sized reference and gives 2-3 m hall / 3-4 m height starting guidance. The
|
This scale contract is informed by [Epic's Unreal level-blockout guidance](https://dev.epicgames.com/documentation/en-us/unreal-engine/designer-01-project-setup-and-level-blockout-in-unreal-engine), which recommends a player-sized reference and gives 2-3 m hall / 3-4 m height starting guidance. The
|
||||||
@@ -62,22 +62,23 @@ are not landing or box-verdict violations.
|
|||||||
|
|
||||||
### 8.1 Archetypes (`ECaveGeneratorType`, VoxelStrateTypes.h)
|
### 8.1 Archetypes (`ECaveGeneratorType`, VoxelStrateTypes.h)
|
||||||
Each archetype has its own param `USTRUCT` (on `UVoxelStrateDefinition`, EditCondition-gated
|
Each archetype has its own param `USTRUCT` (on `UVoxelStrateDefinition`, EditCondition-gated
|
||||||
by `GeneratorType`) and its own density function in `VoxelGenerator.cpp`, dispatched by the
|
by `GeneratorType`) and its own **operator stack** builder in `VoxelDensityOpStack.cpp`, selected per
|
||||||
`switch` in `GetDensityAt`.
|
chunk by `VF_BuildOpStackForChunk` in `GetDensityAt` (the same factory `ClassifyTile` uses). The stack
|
||||||
|
is the only density path; a degenerate (zero-height) strate has none and is open air.
|
||||||
|
|
||||||
| Archetype | Params struct | Density fn | Idea |
|
| Archetype | Params struct | Stack builder | Idea |
|
||||||
|-----------|---------------|------------|------|
|
|-----------|---------------|------------|------|
|
||||||
| TunnelNetwork | `FStrateGenerationParams` | `GetDensityWithParams` | rooms+tunnels (original) |
|
| TunnelNetwork | `FStrateGenerationParams` | `BuildTunnelNetworkStack` (fused evaluator: `GetDensityWithParams`) | rooms+tunnels (original) |
|
||||||
| FlatPlain / CrystalChamber | `FSlabGenerationParams` | `GetSlabDensity` | floor/ceiling void (original) |
|
| FlatPlain / CrystalChamber | `FSlabGenerationParams` | `BuildSlabStack` | floor/ceiling void (original) |
|
||||||
| Maze | `FMazeGenerationParams` | `GetMazeDensity` | origin-directed spanning-tree corridors on a 3D lattice, with capped loop edges and a thread-local per-cell capsule cache |
|
| Maze | `FMazeGenerationParams` | `BuildMazeStack` | origin-directed spanning-tree corridors on a 3D lattice, with capped loop edges and a thread-local per-cell capsule cache |
|
||||||
| SurfaceWorld | `FSurfaceGenerationParams` | `GetSurfaceDensity` | heightfield terrain: domain-warped continents+ridged mtns+detail, a low-freq **relief map** (`M`) that scales mountains/elevation for plains↔highland variety, **F20 heightfield terrain ops** (`Surface|Ops` — all default off ⇒ byte-identical): **Cliff** (slope-gated steepening — where the analytic structural slope > `CliffSlopeThreshold`, push the height away from the local mean by `CliffSharpness` ⇒ gentle slopes become sheer walls/canyon faces that hug real steep ground, gentle areas untouched; 4 structural resamples only when enabled), **Terrace** (relief-gated plateau quantize + `TerraceHardness` soft-round↔crisp-mesa), **LayerLines** (sedimentary sine shelves, slope-expressed) — pure per-column height remaps in the single oracle `ComputeSurfaceTerrainZ` (`SampleSurfaceStructuralZ` = pre-op raw height, re-sampled at an XY offset for Cliff's slope), biome-selected + border-blended for free via each biome's `SurfaceParams` + the height output-lerp; plus **phase-2 Overhang** (the first VOLUMETRIC op — real jutting shelves like a cliff lip): in `SurfaceDensityFromColumn`, for AIR voxels in the window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height and unioned in. Low in the window the shift is ~0 (borrows nearby low rock ⇒ stays air over the void); high up it reaches the far cliff (solid) ⇒ a shelf attached to the cliff, tapering out over the void with air underneath. Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill dir `(DirX,DirY)` are resolved once in `ComputeSurfaceColumn` — the gradient sampled at the REACH scale (`OverhangReach`) so a point out over the void can "see" the cliff to know which way is uphill — and cached on `FSurfaceColumn`. It's genuine 3D (per-voxel structural re-eval), so it's gated hard to steep overhang columns; the union only ADDS rock (never removes), capped at `TerrainZ+OverhangHeight`, so `ClassifyTile` forces Mixed only in `(TerrainZ, TerrainZ+OverhangMargin]` (upward-only; margin = max `OverhangHeight`) — a shelf never holes a trivially-skipped tile, and only the thin cliff-edge band of air tiles is woken (NOT far-field like phase-3 spikes/holes). Overhang undersides classify as ground rock by the F17 rule (down-facing but below `TerrainZ`). Known v1 limit: applies at all LODs (Step-agnostic) — may alias far; gate to fine tiles later. Beaches at water line, high sky-cap ceiling. The cap is shapeable terrain in its own right (`ComputeSurfaceCeiling`, `Surface|Sky` params: `CeilingUndulation` broad inverted hills/valleys, `CeilingRidgeStrength` hanging ridgelines, `CeilingRoughness`+freq fine bumps, `CeilingWarp*`) — defaults (strengths 0, freq 0.04) = old flat-ish cap. (`Surface|Macro` params = the cheap precursor to biomes; `ReliefStrength=0` ⇒ old uniform terrain.) **Sky-cap vs ground is a PER-TRIANGLE surface class (F17), not a per-tile verdict**: the mesher classifies each unique vertex on the **worker** — only down-facing verts (`N.Z<-0.1`) pay a memoized `GetSurfaceHeightAt` column query; nearer `CeilSurf` ⇒ sky-cap, nearer `TerrainZ` ⇒ terrain overhang stays ground (a future SurfaceWorld cave roof — down-facing but below `TerrainZ` — also lands ground by the same rule; non-surface strates always ground). Triangles take the majority class of their 3 verts and are packed as **two contiguous index runs** (ground then cap, `FVoxelMeshData::NumCeilingTriangles`; skirts inherit their source triangle's run) → RMC **polygroups 0/1** → one **section per non-empty group** (`ApplyMeshToTile`, material slot = group index): slot 0 = strate `OverrideMaterial`/default (min-corner chunk), slot 1 = `CeilingMaterial` resolved at the tile's **TOP chunk** (mid as gap fallback, then min; fallback: ground material) — a coarse tile is 2^level chunks tall, so its min corner can sit in a lower strate/gap while the cap belongs to the strate above (this was the "far cap = ground material" residue), so the shadowless overhead rock is tinted separately instead of reading flat/bright. Shadow is **per section** via `FRealtimeMeshSectionConfig::bCastsShadow` (NOT the component `SetCastShadow` — RMC's proxy ignores the component flag; this is also why level≥2 far tiles only stopped casting once the section flag was wired): ground casts at level≤1, the cap section never casts, so the rock ceiling never shadows the terrain below it. History: v1 was a game-thread centre height-oracle (misclassified coarse far tiles → terrain material on the cap underside); v2 a whole-tile worker normal VOTE — which painted **mixed coarse tiles** (one far tile spanning terrain AND cap) entirely with the winner's material, and put sky material under terrain overhangs. The per-triangle class fixes both and is the identity channel caves/F8 will reuse. |
|
| SurfaceWorld | `FSurfaceGenerationParams` | `BuildSurfaceStack` | heightfield terrain: domain-warped continents+ridged mtns+detail, a low-freq **relief map** (`M`) that scales mountains/elevation for plains↔highland variety, **F20 heightfield terrain ops** (`Surface|Ops` — all default off ⇒ byte-identical): **Cliff** (slope-gated steepening — where the analytic structural slope > `CliffSlopeThreshold`, push the height away from the local mean by `CliffSharpness` ⇒ gentle slopes become sheer walls/canyon faces that hug real steep ground, gentle areas untouched; 4 structural resamples only when enabled), **Terrace** (relief-gated plateau quantize + `TerraceHardness` soft-round↔crisp-mesa), **LayerLines** (sedimentary sine shelves, slope-expressed) — pure per-column height remaps in the single oracle `ComputeSurfaceTerrainZ` (`SampleSurfaceStructuralZ` = pre-op raw height, re-sampled at an XY offset for Cliff's slope), biome-selected + border-blended for free via each biome's `SurfaceParams` + the height output-lerp; plus **phase-2 Overhang** (the first VOLUMETRIC op — real jutting shelves like a cliff lip): in `FOverhangShelfMod`, for AIR voxels in the window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height and unioned in. Low in the window the shift is ~0 (borrows nearby low rock ⇒ stays air over the void); high up it reaches the far cliff (solid) ⇒ a shelf attached to the cliff, tapering out over the void with air underneath. Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill dir `(DirX,DirY)` are resolved once in `ComputeSurfaceColumn` — the gradient sampled at the REACH scale (`OverhangReach`) so a point out over the void can "see" the cliff to know which way is uphill — and cached on `FSurfaceColumn`. It's genuine 3D (per-voxel structural re-eval), so it's gated hard to steep overhang columns; the union only ADDS rock (never removes), capped at `TerrainZ+OverhangHeight`, so `ClassifyTile` forces Mixed only in `(TerrainZ, TerrainZ+OverhangMargin]` (upward-only; margin = max `OverhangHeight`) — a shelf never holes a trivially-skipped tile, and only the thin cliff-edge band of air tiles is woken (NOT far-field like phase-3 spikes/holes). Overhang undersides classify as ground rock by the F17 rule (down-facing but below `TerrainZ`). Known v1 limit: applies at all LODs (Step-agnostic) — may alias far; gate to fine tiles later. Beaches at water line, high sky-cap ceiling. The cap is shapeable terrain in its own right (`ComputeSurfaceCeiling`, `Surface|Sky` params: `CeilingUndulation` broad inverted hills/valleys, `CeilingRidgeStrength` hanging ridgelines, `CeilingRoughness`+freq fine bumps, `CeilingWarp*`) — defaults (strengths 0, freq 0.04) = old flat-ish cap. (`Surface|Macro` params = the cheap precursor to biomes; `ReliefStrength=0` ⇒ old uniform terrain.) **Sky-cap vs ground is a PER-TRIANGLE surface class (F17), not a per-tile verdict**: the mesher classifies each unique vertex on the **worker** — only down-facing verts (`N.Z<-0.1`) pay a memoized `GetSurfaceHeightAt` column query; nearer `CeilSurf` ⇒ sky-cap, nearer `TerrainZ` ⇒ terrain overhang stays ground (a future SurfaceWorld cave roof — down-facing but below `TerrainZ` — also lands ground by the same rule; non-surface strates always ground). Triangles take the majority class of their 3 verts and are packed as **two contiguous index runs** (ground then cap, `FVoxelMeshData::NumCeilingTriangles`; skirts inherit their source triangle's run) → RMC **polygroups 0/1** → one **section per non-empty group** (`ApplyMeshToTile`, material slot = group index): slot 0 = strate `OverrideMaterial`/default (min-corner chunk), slot 1 = `CeilingMaterial` resolved at the tile's **TOP chunk** (mid as gap fallback, then min; fallback: ground material) — a coarse tile is 2^level chunks tall, so its min corner can sit in a lower strate/gap while the cap belongs to the strate above (this was the "far cap = ground material" residue), so the shadowless overhead rock is tinted separately instead of reading flat/bright. Shadow is **per section** via `FRealtimeMeshSectionConfig::bCastsShadow` (NOT the component `SetCastShadow` — RMC's proxy ignores the component flag; this is also why level≥2 far tiles only stopped casting once the section flag was wired): ground casts at level≤1, the cap section never casts, so the rock ceiling never shadows the terrain below it. History: v1 was a game-thread centre height-oracle (misclassified coarse far tiles → terrain material on the cap underside); v2 a whole-tile worker normal VOTE — which painted **mixed coarse tiles** (one far tile spanning terrain AND cap) entirely with the winner's material, and put sky material under terrain overhangs. The per-triangle class fixes both and is the identity channel caves/F8 will reuse. |
|
||||||
| VerticalShafts | `FVerticalShaftParams` | `GetVerticalShaftDensity` | full-height shafts + horizontal connectors + partial ledges |
|
| VerticalShafts | `FVerticalShaftParams` | `BuildVerticalShaftStack` | full-height shafts + horizontal connectors + partial ledges |
|
||||||
| FloatingIslands | `FFloatingIslandParams` | `GetFloatingIslandDensity` | asymmetric islands: flat land top + underside tapering to a point, lobed (domain-warped) outline, in an open void |
|
| FloatingIslands | `FFloatingIslandParams` | `BuildFloatingIslandStack` | asymmetric islands: flat land top + underside tapering to a point, lobed (domain-warped) outline, in an open void |
|
||||||
| Underwater | `FStrateGenerationParams` + water | (reuses `GetDensityWithParams`) | tunnel rock + high water table |
|
| Underwater | `FStrateGenerationParams` + water | (reuses `BuildTunnelNetworkStack`) | tunnel rock + high water table |
|
||||||
|
|
||||||
All density fns share the convention: internal **positive=solid**, apply origin spine →
|
All stacks share the convention: internal **positive=solid**, and `AppendStructuralPost` ends every
|
||||||
vertical boundary seal → inter-strate passage tube + landing/floor → XY edge seal, then `return -Density` (MC:
|
stack with origin spine → vertical boundary seal → inter-strate passage tube + landing/floor → XY
|
||||||
negative=solid). The final MC-facing edge pass in `GetDensityAt` is repeated after disturbances so
|
edge seal; `EvalMC` negates once (MC: negative=solid). The final MC-facing edge pass in `GetDensityAt` is repeated after disturbances so
|
||||||
the global rim cannot be reopened by a post-process; the diff layer remains the explicit player
|
the global rim cannot be reopened by a post-process; the diff layer remains the explicit player
|
||||||
override.
|
override.
|
||||||
StrateManager provides params per chunk via `GetMaze/Surface/VerticalShaft/FloatingIslandParamsForChunk`
|
StrateManager provides params per chunk via `GetMaze/Surface/VerticalShaft/FloatingIslandParamsForChunk`
|
||||||
@@ -127,12 +128,9 @@ threshold** before the law gate. No corridor or threshold tuning was added. The
|
|||||||
gate is therefore retained as a design blocker; see `COMPOSER-NOTES.md §3.2b` and the validation log
|
gate is therefore retained as a design blocker; see `COMPOSER-NOTES.md §3.2b` and the validation log
|
||||||
in §10.
|
in §10.
|
||||||
|
|
||||||
⚠️ **The `switch` above is no longer the only density path.** All 8 archetypes also exist as
|
The operator-stack design lives in `Docs/archive/OPSTACK-PLAN.md` /
|
||||||
**operator stacks**, selected per strate by
|
`Docs/archive/OPSTACK-DECOMPOSITION.md`, the symbol index in `CODEMAP §3.2d` — not repeated here.
|
||||||
`bUseOperatorStack` and evaluated instead of the `switch`; each is bit-identical to the function in
|
What matters for *this* document: the archetype table describes what the world IS.
|
||||||
its row. The design lives in `Docs/archive/OPSTACK-PLAN.md` / `Docs/archive/OPSTACK-DECOMPOSITION.md`, the symbol index in
|
|
||||||
`CODEMAP §3.2d` — not repeated here. What matters for *this* document: the archetype table describes
|
|
||||||
what the world IS, and both paths compute it.
|
|
||||||
|
|
||||||
### 8.2 (0,0) spine & hybrid connections
|
### 8.2 (0,0) spine & hybrid connections
|
||||||
- `ApplyOriginSpine` (VoxelGenerator.cpp, static helper) builds a finite, rounded landing room with
|
- `ApplyOriginSpine` (VoxelGenerator.cpp, static helper) builds a finite, rounded landing room with
|
||||||
@@ -230,8 +228,8 @@ they are never needed for the connectivity proof.
|
|||||||
|
|
||||||
The evaluation window is exactly the lower nodes in `{-1,0}³`, an 8-node / 2×2×2 local halo. The
|
The evaluation window is exactly the lower nodes in `{-1,0}³`, an 8-node / 2×2×2 local halo. The
|
||||||
edge predicate checks the adjacent `+1` endpoint's parent locally, so it does not need a wider
|
edge predicate checks the adjacent `+1` endpoint's parent locally, so it does not need a wider
|
||||||
collect region and cannot disagree at a chunk boundary. Both `GetMazeDensity` and
|
collect region and cannot disagree at a chunk boundary. `MakeLatticeCorridorSource` rebuilds those
|
||||||
`MakeLatticeCorridorSource` rebuild those decisions in their thread-local per-cell cache; the
|
decisions in its thread-local per-cell cache; the
|
||||||
voxel loop evaluates only cached capsule SDFs. This is deliberately independent of
|
voxel loop evaluates only cached capsule SDFs. This is deliberately independent of
|
||||||
`WorldRadiusVoxels` (which remains `0`) and the still-gated lateral-region system.
|
`WorldRadiusVoxels` (which remains `0`) and the still-gated lateral-region system.
|
||||||
|
|
||||||
@@ -557,8 +555,8 @@ The landing is part of the structural post, in the fixed order
|
|||||||
after the passage carve and after MC-space disturbances as a support backstop, still before the
|
after the passage carve and after MC-space disturbances as a support backstop, still before the
|
||||||
final XY edge seal. Room/floor Z is clamped inside the vertical seal; the XY edge seal is last and
|
final XY edge seal. Room/floor Z is clamped inside the vertical seal; the XY edge seal is last and
|
||||||
wins over every landing or tube near the rim. Thus no landing can breach a strate boundary or the
|
wins over every landing or tube near the rim. Thus no landing can breach a strate boundary or the
|
||||||
world edge. The op-stack `FPassageCarveOp` calls the same manager operation as the legacy path,
|
world edge. The op-stack `FPassageCarveOp` calls the manager's `ApplyPassageModifier`, including
|
||||||
including the floor, and `ClassifyTile` kills both uniform hypotheses around the bidirectional
|
the floor, and `ClassifyTile` kills both uniform hypotheses around the bidirectional
|
||||||
landing floor (`Both` in the stack), so an air proof cannot delete support.
|
landing floor (`Both` in the stack), so an air proof cannot delete support.
|
||||||
|
|
||||||
The query remains pure — it does not construct an operator stack, touch a cache, or call back into
|
The query remains pure — it does not construct an operator stack, touch a cache, or call back into
|
||||||
@@ -624,8 +622,7 @@ driven by `EditorBrush*` props.
|
|||||||
inner 3×3 shafts and their capsules for the per-voxel loop, while spatial culling selects cached
|
inner 3×3 shafts and their capsules for the per-voxel loop, while spatial culling selects cached
|
||||||
connectors. The complete ±2 parent and ±3 fallback windows plus connector reach are contained
|
connectors. The complete ±2 parent and ±3 fallback windows plus connector reach are contained
|
||||||
by that halo, so no parent decision depends on the evaluation cell. No cell or pair hash, widened
|
by that halo, so no parent decision depends on the evaluation cell. No cell or pair hash, widened
|
||||||
neighbourhood scan, or tree construction may move into the per-voxel loop; the operator-stack
|
neighbourhood scan, or tree construction may move into the per-voxel loop (`FShaftFieldSource`).
|
||||||
source follows the same cache contract.
|
|
||||||
- **Maze field cache**: parent/loop decisions are rebuilt only when the thread-local cell, seed, or
|
- **Maze field cache**: parent/loop decisions are rebuilt only when the thread-local cell, seed, or
|
||||||
loop parameters change. The rebuild evaluates the 24 canonical lower-node/axis edges in the
|
loop parameters change. The rebuild evaluates the 24 canonical lower-node/axis edges in the
|
||||||
local `{-1,0}³` window and emits at most 24 capsules; the per-voxel loop performs no hash,
|
local `{-1,0}³` window and emits at most 24 capsules; the per-voxel loop performs no hash,
|
||||||
@@ -687,13 +684,13 @@ driven by `EditorBrush*` props.
|
|||||||
position → seamless across chunk borders (both sides use identical pure samples). NO per-vertex
|
position → seamless across chunk borders (both sides use identical pure samples). NO per-vertex
|
||||||
`GetDensityAt` (was ~6/vertex, often as costly as the whole grid). `ComputeGradientNormal` is now
|
`GetDensityAt` (was ~6/vertex, often as costly as the whole grid). `ComputeGradientNormal` is now
|
||||||
unused. Only NORMALS changed vs the old path; geometry is identical.
|
unused. Only NORMALS changed vs the old path; geometry is identical.
|
||||||
- **Surface column cache (T1.a)** (`FSurfaceColumnCache` = LRU of `FSurfaceColumnBox`, `GetDensityAt`
|
- **Surface column cache (T1.a)** (density path: the surface stack's `FSurfaceColumnSource` memo;
|
||||||
SurfaceWorld branch): the heightfield + sky-cap + biome blend are a PURE function of (XY, seed,
|
`ClassifyTile`: `FSurfaceColumnCache` = LRU of `FSurfaceColumnBox`): the heightfield + sky-cap + biome blend are a PURE function of (XY, seed,
|
||||||
strate) — **ZERO Z dependence** (climate/Voronoi are pure-XY; surface params are per-strate constant
|
strate) — **ZERO Z dependence** (climate/Voronoi are pure-XY; surface params are per-strate constant
|
||||||
under Hard transitions) — yet sampled ~33× per column (once per Z grid-point). Cached per integer XY
|
under Hard transitions) — yet sampled ~33× per column (once per Z grid-point). Cached per integer XY
|
||||||
(box-valid, like the SDF cache) and reused down the column. **Keyed by (XY box, StrateKey, Seed), NOT
|
(box-valid, like the SDF cache) and reused down the column. **Keyed by XY box + strate identity + seed
|
||||||
ChunkZ** (`StrateKey = round(StrateBottomWorldZ)`, taken from the params so it can't disagree with
|
(plus layout version / params fingerprint), NOT ChunkZ** (the strate identity is derived from
|
||||||
them) and held as a small **LRU of 6 boxes** so the WHOLE vertical view-distance stack — and XY
|
`StrateBottomWorldZ`, taken from the params so it can't disagree with them) and held as a small **LRU of 6 boxes** so the WHOLE vertical view-distance stack — and XY
|
||||||
neighbours the scheduler interleaves — share one another's heavy column noise instead of each
|
neighbours the scheduler interleaves — share one another's heavy column noise instead of each
|
||||||
recomputing it ~once per vertical chunk (this was the dominant `GenerateMesh` cost: the same 2D
|
recomputing it ~once per vertical chunk (this was the dominant `GenerateMesh` cost: the same 2D
|
||||||
heightfield recomputed per altitude). It also makes pure-air / pure-solid chunks cheap (they hit the
|
heightfield recomputed per altitude). It also makes pure-air / pure-solid chunks cheap (they hit the
|
||||||
@@ -832,9 +829,11 @@ driven by `EditorBrush*` props.
|
|||||||
wasted) and gen throughput had become the felt gameplay limit ("standing waiting for generation").
|
wasted) and gen throughput had become the felt gameplay limit ("standing waiting for generation").
|
||||||
**Why v1 failed & how v2 avoids it:** v1 used a GLOBAL analytic ceiling bound — not conservative when
|
**Why v1 failed & how v2 avoids it:** v1 used a GLOBAL analytic ceiling bound — not conservative when
|
||||||
the cap hangs low (`CeilingRoughness`/`RidgeStrength`) → holes in the roof. v2 makes NO amplitude
|
the cap hangs low (`CeilingRoughness`/`RidgeStrength`) → holes in the roof. v2 makes NO amplitude
|
||||||
guesses: it evaluates `ComputeSurfaceColumn` (the SAME function as the density path, via the SHARED
|
guesses: it evaluates `ComputeSurfaceColumn` (cached per thread in `GSurfColCache`) — the column
|
||||||
`GSurfColCache`) **on the exact lattice the mesher would sample** (margin ring included) — same
|
the surface stack's height operators reproduce (ground height pinned bit for bit by
|
||||||
functions + same inputs ⇒ the same floats ⇒ the verdict is exact at the lattice, not an estimate.
|
`VoxelForge.OpStack.SurfaceHeightEquivalence`) — **on the exact lattice the mesher would sample**
|
||||||
|
(margin ring included) — same inputs ⇒ the same floats ⇒ the verdict is exact at the lattice, not
|
||||||
|
an estimate.
|
||||||
Per-z rules: gap chunk = solid; surface seal band (ApplyBoundarySeal inequalities, `BaseDensity>0`)
|
Per-z rules: gap chunk = solid; surface seal band (ApplyBoundarySeal inequalities, `BaseDensity>0`)
|
||||||
= solid; surface interior z: air ⇔ `TerrainZ ≤ z ≤ CeilSurf` (MC `D ≥ 0`); ANY other archetype /
|
= solid; surface interior z: air ⇔ `TerrainZ ≤ z ≤ CeilSurf` (MC `D ≥ 0`); ANY other archetype /
|
||||||
out-of-layout chunkZ ⇒ `Mixed` (cave interiors are not provable in v1 of this classifier — a future
|
out-of-layout chunkZ ⇒ `Mixed` (cave interiors are not provable in v1 of this classifier — a future
|
||||||
@@ -848,7 +847,7 @@ driven by `EditorBrush*` props.
|
|||||||
A false `Mixed` only costs CPU; the code must NEVER emit a false AllSolid/AllAir (that's a hole).
|
A false `Mixed` only costs CPU; the code must NEVER emit a false AllSolid/AllAir (that's a hole).
|
||||||
Capture tiles (`bWantCapture`, density-volume shadow window) always generate — the volume wants the
|
Capture tiles (`bWantCapture`, density-volume shadow window) always generate — the volume wants the
|
||||||
grid even for uniform cells. A sparse ~5×5 column pre-pass exits Mixed fast on surface-crossing
|
grid even for uniform cells. A sparse ~5×5 column pre-pass exits Mixed fast on surface-crossing
|
||||||
tiles; a Mixed verdict leaves its columns warm in `GSurfColCache` for the GenerateMesh that follows.
|
tiles; the columns stay warm in `GSurfColCache` for the full pass and for neighbouring tiles.
|
||||||
- **Worker-built StreamSet (T1.f)** (`BuildTileStreamSet`, `LoadTile` task → `FChunkResult::Streams`):
|
- **Worker-built StreamSet (T1.f)** (`BuildTileStreamSet`, `LoadTile` task → `FChunkResult::Streams`):
|
||||||
the RMC vertex/index buffers (`FRealtimeMeshStreamSet`) are built ON THE GEN WORKER, not on the game
|
the RMC vertex/index buffers (`FRealtimeMeshStreamSet`) are built ON THE GEN WORKER, not on the game
|
||||||
thread. The per-vertex builder loop was the dominant game-thread streaming cost (measured: game
|
thread. The per-vertex builder loop was the dominant game-thread streaming cost (measured: game
|
||||||
@@ -969,9 +968,9 @@ change *anything*, e.g. frequencies, which scalar multipliers couldn't.)
|
|||||||
- **Consumption — SURFACE (output-blend).** Per chunk, `CP_SurfaceBiomeParams[]` holds each biome's
|
- **Consumption — SURFACE (output-blend).** Per chunk, `CP_SurfaceBiomeParams[]` holds each biome's
|
||||||
resolved surface params (its override when `bOverrideTerrain` + GeneratorType matches, else the
|
resolved surface params (its override when `bOverrideTerrain` + GeneratorType matches, else the
|
||||||
strate's) with **structural fields forced from the strate** (Z bounds, seal, base density, water
|
strate's) with **structural fields forced from the strate** (Z bounds, seal, base density, water
|
||||||
level). Per voxel: `ResolveBiomeSampleAt` → dominant `PD` (+ neighbour `PN`); `GetSurfaceDensity`
|
level). Per voxel: `ResolveBiomeSampleAt` → dominant `PD` (+ neighbour `PN`); the surface stack's
|
||||||
computes `ComputeSurfaceTerrainZ` for `PD` and, in the border band, for `PN`, and **lerps the
|
biome-blend height source (and `ComputeSurfaceColumn`) computes the terrain height for `PD` and, in
|
||||||
resulting HEIGHTS**. Blending heights (not params) is seamless across *any* difference (frequencies
|
the border band, for `PN`, and **lerps the resulting HEIGHTS**. Blending heights (not params) is seamless across *any* difference (frequencies
|
||||||
included) — what per-param blend never could. `PD==PN`, weight 0 ⇒ bit-identical, no biomes.
|
included) — what per-param blend never could. `PD==PN`, weight 0 ⇒ bit-identical, no biomes.
|
||||||
- **Consumption — CAVES: structural overrides are NOT applied (determinism).** Rooms/tunnels are
|
- **Consumption — CAVES: structural overrides are NOT applied (determinism).** Rooms/tunnels are
|
||||||
decided over a wide COLLECT region spanning chunks (§8.4); making room params vary by region would
|
decided over a wide COLLECT region spanning chunks (§8.4); making room params vary by region would
|
||||||
|
|||||||
+97
-99
@@ -54,9 +54,12 @@ A **density-field voxel terrain** plugin for Unreal Engine, built around undergr
|
|||||||
▼
|
▼
|
||||||
UVoxelGenerator::GetDensityAt(x,y,z) ← THE density entry point
|
UVoxelGenerator::GetDensityAt(x,y,z) ← THE density entry point
|
||||||
│ asks StrateManager which strate/params/generator-type applies
|
│ asks StrateManager which strate/params/generator-type applies
|
||||||
├─ TunnelNetwork → GetDensityWithParams() (rooms+tunnels+ops+worms+seal+passages)
|
├─ builds the chunk's operator stack once per chunk (VF_BuildOpStackForChunk;
|
||||||
|
│ editor composer / cooked-season recipe stacks first) → every archetype
|
||||||
|
│ TunnelNetwork/Underwater → fused evaluator = GetDensityWithParams()
|
||||||
│ └─ VoxelCaveMorphology::BuildChunkCache + EvaluateSDFCached (room/tunnel SDF)
|
│ └─ VoxelCaveMorphology::BuildChunkCache + EvaluateSDFCached (room/tunnel SDF)
|
||||||
├─ FlatPlain / CrystalChamber → GetSlabDensity() (floor+ceiling+columns+seal+passages)
|
│ others → FVoxelOpStack::EvalMC (source → modifiers → structural posts)
|
||||||
|
├─ + disturbances + structural tail (landing floor, tunnel core, XY seal)
|
||||||
└─ + UVoxelDiffLayer::GetDensityOffset() (player carve/fill)
|
└─ + UVoxelDiffLayer::GetDensityOffset() (player carve/fill)
|
||||||
│
|
│
|
||||||
▼ FVoxelMeshData (verts/tris/uvs/normals)
|
▼ FVoxelMeshData (verts/tris/uvs/normals)
|
||||||
@@ -118,7 +121,7 @@ remains `0`, and lateral regions remain gated off.
|
|||||||
| `Public/VoxelForgeModule.h` / `Private/VoxelForgeModule.cpp` | `FVoxelForgeModule` boilerplate (Startup/Shutdown just log). |
|
| `Public/VoxelForgeModule.h` / `Private/VoxelForgeModule.cpp` | `FVoxelForgeModule` boilerplate (Startup/Shutdown just log). |
|
||||||
| `Source/VoxelForgeEditor/VoxelForgeEditor.Build.cs` / `Private/VoxelForgeExploreCommandlet.*` | Editor/commandlet-only `-run=VoxelForgeExplore`; owns bounded render/`VF_` walk/OBJ export orchestration and is not linked by Game/Shipping. |
|
| `Source/VoxelForgeEditor/VoxelForgeEditor.Build.cs` / `Private/VoxelForgeExploreCommandlet.*` | Editor/commandlet-only `-run=VoxelForgeExplore`; owns bounded render/`VF_` walk/OBJ export orchestration and is not linked by Game/Shipping. |
|
||||||
| `Public/VoxelGeometryHash.h` | Header-only geometry digest shared by commandlet export and runtime tile-hash dump; hashes mesh counts and the vertex/normal/UV/color/triangle arrays with the historical CRC order. |
|
| `Public/VoxelGeometryHash.h` | Header-only geometry digest shared by commandlet export and runtime tile-hash dump; hashes mesh counts and the vertex/normal/UV/color/triangle arrays with the historical CRC order. |
|
||||||
| `Public/VoxelStats.h` / `Private/VoxelStats.cpp` | `stat VoxelForge` DWORD counters for tile classification, skipping, meshing, operator-stack verdicts, and cave-bail diagnosis. The former ambiguous `Cave Bail Not Op Stack` is split into `Sole Slot`, `Boundary Tile`, `No Layout`, and late `Recheck` counters, so each increment names one guard/context. |
|
| `Public/VoxelStats.h` / `Private/VoxelStats.cpp` | `stat VoxelForge` DWORD counters for tile classification, skipping, meshing, operator-stack verdicts, and cave-bail diagnosis. Each `Cave Bail *` counter names one guard; `Cave Bail No Stack` counts slots with no buildable stack (no definition, or a degenerate strate). |
|
||||||
|
|
||||||
### 3.2 Foundational types — `Public/VoxelTypes.h` (no UClass, everyone includes it)
|
### 3.2 Foundational types — `Public/VoxelTypes.h` (no UClass, everyone includes it)
|
||||||
| Symbol | Line | Notes |
|
| Symbol | Line | Notes |
|
||||||
@@ -135,9 +138,8 @@ remains `0`, and lateral regions remain gated off.
|
|||||||
| `FVoxelMeshData` struct | 365-391 | 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. |
|
| `FVoxelMeshData` struct | 365-391 | 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)
|
### 3.2b Density operator stack contract — `Public/VoxelDensityOp.h` (plain C++, no UHT)
|
||||||
**Production path behind a per-strate opt-in** — the stack is built/evaluated by `GetDensityAt` for
|
**The production density path** — `GetDensityAt` builds and evaluates a stack for every strate of
|
||||||
all eight archetypes when `bUseOperatorStack` is enabled, while the legacy `switch` remains the
|
all eight archetypes; there is no other path. See [Docs/archive/OPSTACK-PLAN.md](Docs/archive/OPSTACK-PLAN.md) for the decomposition.
|
||||||
reference path for opt-out strates. See [Docs/archive/OPSTACK-PLAN.md](Docs/archive/OPSTACK-PLAN.md) for the decomposition.
|
|
||||||
|
|
||||||
| Symbol | Notes |
|
| Symbol | Notes |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
@@ -162,25 +164,20 @@ proof for `ClassifyBox` and T1.d's global shell skip. `VoxelGenerator.cpp` keeps
|
|||||||
edge helpers are called directly. **Convention: INTERNAL (positive = solid); `VF_ApplyXYEdgeSealMC` is the output wrapper.**
|
edge helpers are called directly. **Convention: INTERNAL (positive = solid); `VF_ApplyXYEdgeSealMC` is the output wrapper.**
|
||||||
|
|
||||||
### 3.2d Operator stack — `Public/VoxelDensityOpStack.h` + `Private/VoxelDensityOpStack.cpp`
|
### 3.2d Operator stack — `Public/VoxelDensityOpStack.h` + `Private/VoxelDensityOpStack.cpp`
|
||||||
⚠️ **Feeds the game, behind a per-strate opt-in** (Phase 1 step 3). `GetDensityAt` builds the stack
|
⚠️ **This IS the game's generation path.** `GetDensityAt` builds the stack in its per-chunk refetch
|
||||||
in its per-chunk refetch block and evaluates it *instead of* the `switch` only when
|
block for every strate — **all 8 archetypes**: Maze, FlatPlain, CrystalChamber, SurfaceWorld,
|
||||||
`UVoxelStrateManager::UsesOperatorStackForChunk` says so — strate ticked `bUseOperatorStack` **and**
|
VerticalShafts, FloatingIslands, TunnelNetwork, Underwater. An editor composer candidate or a
|
||||||
archetype in the ported list, which is now **all 8 of 8**: Maze, FlatPlain, CrystalChamber,
|
cooked-season recipe supplies its own stack; otherwise (or if that stack cannot be built) the
|
||||||
SurfaceWorld, VerticalShafts, FloatingIslands, TunnelNetwork, Underwater. A strate that has not
|
archetype's native stack is used. A degenerate (zero-height) strate has no stack and is open air.
|
||||||
ticked the box still takes the `switch`, unchanged — the flag is the only thing that switches paths.
|
`UVoxelStrateManager::UsesOperatorStackForChunk` only says whether a slot has a known archetype.
|
||||||
**`ClassifyTile` IS wired now**, for CAVE archetypes only and behind the same per-strate opt-in:
|
**`ClassifyTile`**, for CAVE archetypes, builds the strate's stack through the *same* factory
|
||||||
where it used to `return Mixed` without a call, it builds the strate's stack through the *same*
|
`GetDensityAt` uses (`VF_BuildOpStackForChunk`) and folds `ClassifyBox`. SurfaceWorld and bedrock
|
||||||
factory `GetDensityAt` uses (`VF_BuildOpStackForChunk`) and folds `ClassifyBox`. SurfaceWorld and
|
gaps keep their hand-written proofs — the exact-lattice column test is better than any box bound.
|
||||||
bedrock gaps keep their hand-written proofs — the exact-lattice column test is better than any box
|
Guards, all failing to `Mixed`: one cave slot per tile, no mixed cave/surface/gap tile, a stack on
|
||||||
bound. Guards, all failing to `Mixed`: one cave slot per tile, no mixed cave/surface/gap tile, the
|
*every* chunk the box touches, the params **bit-identical** across every chunk coord the box touches
|
||||||
opt-in true on *every* chunk the box touches, the params **bit-identical** across every chunk coord
|
(blended transition bands make one stack unable to represent the tile — `AUDIT §C2`), a
|
||||||
the box touches (blended transition bands make one stack unable to represent the tile — `AUDIT §C2`),
|
27-chunk-coord cap, and the disturbances folded in by hand since they are applied after the stack.
|
||||||
a 27-chunk-coord cap, and the disturbances folded in by hand since they are applied after the stack.
|
|
||||||
Brute-forced end to end by `VoxelForge.OpStack.ClassifyTileSoundness`.
|
Brute-forced end to end by `VoxelForge.OpStack.ClassifyTileSoundness`.
|
||||||
⛔ Never run both paths in one world. **Comparing them IS legitimate now** — the ~1 ULP residue of
|
|
||||||
AUDIT §C10 is gone since `FPSemantics = Precise`, and all eight equivalence tests compare bit for
|
|
||||||
bit. They are port-correctness oracles, not fidelity checks: the acceptance bar is §2.6.1 (same seed
|
|
||||||
⇒ same world on every peer), which does not require resembling the pre-refactor world.
|
|
||||||
|
|
||||||
| Symbol | Role | Notes |
|
| Symbol | Role | Notes |
|
||||||
|--------|------|-------|
|
|--------|------|-------|
|
||||||
@@ -196,7 +193,7 @@ bit. They are port-correctness oracles, not fidelity checks: the acceptance bar
|
|||||||
| `VoxelDensityOps::MakeSlabVoidSource` | 1 | Floor surface + ceiling surface → void field. **XY-pure** since §3.1, which is what gives it an **exact `ClassifyBox` with no sampling**: the proved FBM supremum `1.5` bounds both surfaces into known Z bands. Serves FlatPlain **and** CrystalChamber. |
|
| `VoxelDensityOps::MakeSlabVoidSource` | 1 | Floor surface + ceiling surface → void field. **XY-pure** since §3.1, which is what gives it an **exact `ClassifyBox` with no sampling**: the proved FBM supremum `1.5` bounds both surfaces into known Z bands. Serves FlatPlain **and** CrystalChamber. |
|
||||||
| `VoxelDensityOps::MakeGridColumnMod` | 3 | Infinite-height cylinders on a world grid, 3×3 cell memo. Adds solid only ⇒ `FillOnly` when a column reaches the box, `Identity` otherwise — and that `Identity` is what lets the source's `AllAir` verdict survive. |
|
| `VoxelDensityOps::MakeGridColumnMod` | 3 | Infinite-height cylinders on a world grid, 3×3 cell memo. Adds solid only ⇒ `FillOnly` when a column reaches the box, `Identity` otherwise — and that `Identity` is what lets the source's `AllAir` verdict survive. |
|
||||||
| `FShaftFieldSource` (internal) | 1 | VerticalShafts' memoised inner 3×3 shaft/capsule source. Each rebuild uses a direct-indexed wider halo (default 9×9 tree-emission / 15×15 roll) so every emitted shaft has its complete 5×5 parent window plus capsule reach; the nearest strictly more-central shaft in ±2 cells is the deterministic parent, with a deterministic lower-origin ±3 fallback and a direct `(0,0)` spine fallback. Thus every shaft has a provable path to the spine. Existing `VoxelHash::Pair`/`Spacing*1.6` links remain additive texture/loops. Tree and spine capsules use a radius strictly above `sup|FBM|=1.5`, and tree Z is chosen in a ledge-free band; the structural post still owns the column and the ledge modifier keeps its mathematical axis boundary open. The source publishes a conservative interval covering both shafts and connector capsules; it does not answer for the ledge or converter. |
|
| `FShaftFieldSource` (internal) | 1 | VerticalShafts' memoised inner 3×3 shaft/capsule source. Each rebuild uses a direct-indexed wider halo (default 9×9 tree-emission / 15×15 roll) so every emitted shaft has its complete 5×5 parent window plus capsule reach; the nearest strictly more-central shaft in ±2 cells is the deterministic parent, with a deterministic lower-origin ±3 fallback and a direct `(0,0)` spine fallback. Thus every shaft has a provable path to the spine. Existing `VoxelHash::Pair`/`Spacing*1.6` links remain additive texture/loops. Tree and spine capsules use a radius strictly above `sup|FBM|=1.5`, and tree Z is chosen in a ledge-free band; the structural post still owns the column and the ledge modifier keeps its mathematical axis boundary open. The source publishes a conservative interval covering both shafts and connector capsules; it does not answer for the ledge or converter. |
|
||||||
| `VoxelDensityOps::BuildSlabStack` | — | 6 ops, **no branch on archetype**: FlatPlain and CrystalChamber differ only in defaults, exactly as `GetSlabDensity` already had it. 8 archetypes → 7. |
|
| `VoxelDensityOps::BuildSlabStack` | — | 6 ops, **no branch on archetype**: FlatPlain and CrystalChamber differ only in defaults. |
|
||||||
| `FSurfaceColumnSource` (internal) | 1 | The bridge between the two spaces: consumes the ground + sky-cap **height** stacks and produces density. `IsXYPure()` **false** — the heights are XY-pure, a distance to them never is. Owns a **six-box spatial LRU** of direct-indexed per-column cells, keyed by `PrepareChunk` on `(StrateBottomWorldZ, LayoutVersion, Seed, ParamsFingerprint)` so it is **shared down the whole vertical strate stack**, exactly like `GSurfColCache`. Six 81×81 boxes preserve hot columns across interleaved regions at roughly 0.79 MiB TLS before padding (more memory, fewer whole-cache recenter/recompute misses). Fractional XY remains direct-compute. `FSurfaceColumn` is deliberately trivial: all five outputs are written before the cache's `Computed` publish, so removing its default initialization removes a TLS constructor leaf without changing a value. |
|
| `FSurfaceColumnSource` (internal) | 1 | The bridge between the two spaces: consumes the ground + sky-cap **height** stacks and produces density. `IsXYPure()` **false** — the heights are XY-pure, a distance to them never is. Owns a **six-box spatial LRU** of direct-indexed per-column cells, keyed by `PrepareChunk` on `(StrateBottomWorldZ, LayoutVersion, Seed, ParamsFingerprint)` so it is **shared down the whole vertical strate stack**, exactly like `GSurfColCache`. Six 81×81 boxes preserve hot columns across interleaved regions at roughly 0.79 MiB TLS before padding (more memory, fewer whole-cache recenter/recompute misses). Fractional XY remains direct-compute. `FSurfaceColumn` is deliberately trivial: all five outputs are written before the cache's `Computed` publish, so removing its default initialization removes a TLS constructor leaf without changing a value. |
|
||||||
| `VoxelDensityOps::BuildSurfaceStack` | — | SurfaceWorld, complete: column + overhang + 4 structural, plus biome blending when `PerBiomeParams` is non-empty. Takes ownership of an `IVoxelBiomeField`. |
|
| `VoxelDensityOps::BuildSurfaceStack` | — | SurfaceWorld, complete: column + overhang + 4 structural, plus biome blending when `PerBiomeParams` is non-empty. Takes ownership of an `IVoxelBiomeField`. |
|
||||||
| `VoxelDensityOps::BuildVerticalShaftStack` | — | 9 ops, and **three are Maze's reused unchanged** (`ConstantRock`, `SdfRoughness`, `SdfCarve`) with different tuning (freq 0.1 vs 0.12, window `rough+4` vs `R+rough+2`). Passes the runtime spine radius into `FShaftFieldSource`, which mirrors the generator's connector-only origin endpoint and roughness-safe spine capsule. The measured proof of `OPSTACK-PLAN §2.5`'s reuse claim. |
|
| `VoxelDensityOps::BuildVerticalShaftStack` | — | 9 ops, and **three are Maze's reused unchanged** (`ConstantRock`, `SdfRoughness`, `SdfCarve`) with different tuning (freq 0.1 vs 0.12, window `rough+4` vs `R+rough+2`). Passes the runtime spine radius into `FShaftFieldSource`, which mirrors the generator's connector-only origin endpoint and roughness-safe spine capsule. The measured proof of `OPSTACK-PLAN §2.5`'s reuse claim. |
|
||||||
@@ -212,7 +209,7 @@ bit. They are port-correctness oracles, not fidelity checks: the acceptance bar
|
|||||||
| `FRoomGraphSource::LocalParams()` | — | **The per-room op override** (DECOMPOSITION §2's "no clean home"). Strate params + the nearest room's `UVoxelTerrainOpDefinition::ApplyTo`, memoised once per voxel and read by eleven modifiers. One op owns the shared state, the rest read it — the same pattern as `FOverhangShelfMod` ← `FSurfaceColumnSource`. The detail consumers use the propagated interval for their shared near/far gate; unknown remains conservative, and the per-room parameter override is never used to claim an unproved box identity. |
|
| `FRoomGraphSource::LocalParams()` | — | **The per-room op override** (DECOMPOSITION §2's "no clean home"). Strate params + the nearest room's `UVoxelTerrainOpDefinition::ApplyTo`, memoised once per voxel and read by eleven modifiers. One op owns the shared state, the rest read it — the same pattern as `FOverhangShelfMod` ← `FSurfaceColumnSource`. The detail consumers use the propagated interval for their shared near/far gate; unknown remains conservative, and the per-room parameter override is never used to claim an unproved box identity. |
|
||||||
| `FIslandBlobSource` (internal) | 1 | Hash-placed tapered flat-top blobs, `SmoothMin`'d, in a **domain-warped XY frame** (the warp stays inside the op — see the deviation note vs DECOMPOSITION §7). SDF channel only. The source publishes an interval covering the full authored radius envelope (`max(MinRadius, MaxRadius)`), the two-axis warp·**√2** envelope, top surface and SmoothMin dip; the fill converter makes its own decision. **No lower Z bound exists** — a hairline thread of matter hangs below each island down its axis, so only the TOP may reject. |
|
| `FIslandBlobSource` (internal) | 1 | Hash-placed tapered flat-top blobs, `SmoothMin`'d, in a **domain-warped XY frame** (the warp stays inside the op — see the deviation note vs DECOMPOSITION §7). SDF channel only. The source publishes an interval covering the full authored radius envelope (`max(MinRadius, MaxRadius)`), the two-axis warp·**√2** envelope, top surface and SmoothMin dip; the fill converter makes its own decision. **No lower Z bound exists** — a hairline thread of matter hangs below each island down its axis, so only the TOP may reject. |
|
||||||
| `VoxelDensityOps::BuildFloatingIslandStack` | — | 8 ops, and **the stack runs backwards**: void source + fill instead of rock source + carve, using the *same* classes with the opposite sign. Only the blob source is new. Reuse by **inversion** — a stronger result than reuse by identity, since it says the abstract axis (the density sign) is the right one. |
|
| `VoxelDensityOps::BuildFloatingIslandStack` | — | 8 ops, and **the stack runs backwards**: void source + fill instead of rock source + carve, using the *same* classes with the opposite sign. Only the blob source is new. Reuse by **inversion** — a stronger result than reuse by identity, since it says the abstract axis (the density sign) is the right one. |
|
||||||
| `VoxelDensityOps::BuildMazeStack` | — | The 8-op Maze stack (4 structural posts included). Its lattice source mirrors the origin-directed tree + capped loops and rebuilds the 2×2×2 lower-node window once per cell; the per-voxel loop only evaluates cached capsules. If this ever becomes one op, the refactor failed its own test (§2.5). Callers must skip it on a **degenerate strate** (top−bottom ≤ 0): `GetMazeDensity` early-outs to air there and the stack has no such early-out by design — `GetDensityAt` falls back to the `switch`. |
|
| `VoxelDensityOps::BuildMazeStack` | — | The 8-op Maze stack (4 structural posts included). Its lattice source mirrors the origin-directed tree + capped loops and rebuilds the 2×2×2 lower-node window once per cell; the per-voxel loop only evaluates cached capsules. If this ever becomes one op, the refactor failed its own test (§2.5). Callers must skip it on a **degenerate strate** (top−bottom ≤ 0): the stack has no such early-out by design — `VF_BuildOpStackForChunk` refuses it and `GetDensityAt` returns air. |
|
||||||
|
|
||||||
#### Lateral region parent (Tier 4d)
|
#### Lateral region parent (Tier 4d)
|
||||||
|
|
||||||
@@ -343,30 +340,30 @@ This is **where terrain shape lives.**
|
|||||||
| Symbol | .cpp line | Role |
|
| Symbol | .cpp line | Role |
|
||||||
|--------|-----------|------|
|
|--------|-----------|------|
|
||||||
| `UVoxelGenerator` / `DensityCacheOwnerId` | — | Constructor allocates a process-unique integer identity (relaxed atomic, once per object). `GetDensityAt` includes it in the `CP_*` thread-local key, preventing a worker from serving another generator/world's params, biome context, `CP_UseOpStack`, or stack when `(ChunkCoord, LayoutVersion)` happens to match. Hot-path cost: one `uint64` compare per voxel. Scope is deliberately only the proved `CP_*` path. |
|
| `UVoxelGenerator` / `DensityCacheOwnerId` | — | Constructor allocates a process-unique integer identity (relaxed atomic, once per object). `GetDensityAt` includes it in the `CP_*` thread-local key, preventing a worker from serving another generator/world's params, biome context, `CP_UseOpStack`, or stack when `(ChunkCoord, LayoutVersion)` happens to match. Hot-path cost: one `uint64` compare per voxel. Scope is deliberately only the proved `CP_*` path. |
|
||||||
| `FractalNoise3D` (static) | 932 | fBM (layered Perlin). |
|
| `FractalNoise3D` (static) | 931 | fBM (layered Perlin). |
|
||||||
| `RidgedNoise3D` (static) | 950 | Ridged multifractal — craggy. |
|
| `RidgedNoise3D` (static) | 949 | Ridged multifractal — craggy. |
|
||||||
| `CellularNoise3D` (static) | 977 | Worley/cellular — grotto/scallop. |
|
| `CellularNoise3D` (static) | 976 | Worley/cellular — grotto/scallop. |
|
||||||
| `ApplyBoundarySeal` (static) | 997 | Solidifies strate top/bottom shells. |
|
| `ApplyBoundarySeal` (static) | 996 | Solidifies strate top/bottom shells. |
|
||||||
| `InitializeSettings` | 2694 | Copies the effective seed, spine radius, and global XY world-bound settings from `UVoxelSettings` (the season owns all three when assigned); radius 0 preserves the unbounded field. |
|
| `InitializeSettings` | 2691 | Copies the effective seed, spine radius, and global XY world-bound settings from `UVoxelSettings` (the season owns all three when assigned); radius 0 preserves the unbounded field. |
|
||||||
| **`GetDensityAt`** | 2715 | **Entry point.** Picks strate + generator type, materialises a cooked-season recipe on the existing per-chunk refetch when present, dispatches, then adds disturbances/diff. Its `CP_*` state is keyed by `(DensityCacheOwnerId, ChunkCoord, LayoutVersion)`; workers copy immutable recipe/vector data and never read editor composer state. |
|
| **`GetDensityAt`** | 2712 | **Entry point.** Picks strate + generator type and, on the per-chunk refetch, builds the chunk's operator stack (editor composer → cooked-season recipe → native archetype stack); evaluates it (TunnelNetwork/Underwater through the fused `GetDensityWithParams`), then adds disturbances, the structural tail and the diff layer. A degenerate strate has no stack and is air. Its `CP_*` state is keyed by `(DensityCacheOwnerId, ChunkCoord, LayoutVersion)`; workers copy immutable recipe/vector data and never read editor composer state. |
|
||||||
| **`GetDensityWithParams`** | 3851 | TunnelNetwork pipeline (~1000 lines). See §4. ⚠️ Takes **required** `ParamsFingerprint` + `LayoutVersion` since the AUDIT §C2 fix (2026-07-28) — they go into the SDF cache key so a chunk can no longer be evaluated against a neighbour's rooms. Callers compute the CRC **once per chunk** (`CP_TunnelFP`), never per voxel. The optional `voxel.WormBlockSkip` path (default 0) uses `VF_TryGetWormBlockSkip` + a worker-local 4³ lattice-block cache to prove `|N1| >= WormThreshold` and remove both worm noise calls only; it never changes `NetworkMask` or the fallback field path. |
|
| **`GetDensityWithParams`** | 3749 | TunnelNetwork pipeline (~1000 lines) — the fused evaluator of the TunnelNetwork/Underwater stack in `GetDensityAt`. See §4. ⚠️ Takes **required** `ParamsFingerprint` + `LayoutVersion` since the AUDIT §C2 fix (2026-07-28) — they go into the SDF cache key so a chunk can no longer be evaluated against a neighbour's rooms. Callers compute the CRC **once per chunk** (`CP_TunnelFP`), never per voxel. The optional `voxel.WormBlockSkip` path (default 0) uses `VF_TryGetWormBlockSkip` + a worker-local 4³ lattice-block cache to prove `|N1| >= WormThreshold` and remove both worm noise calls only; it never changes `NetworkMask` or the fallback field path. |
|
||||||
| **`GetSlabDensity`** | 5188 | FlatPlain/CrystalChamber pipeline. See §4.2. |
|
|
||||||
| `SampleSurfaceStructuralZ` | — | **F20:** the RAW SurfaceWorld heightfield (continents+mountains+detail), BEFORE any terrain op; returns terrain Z + relief M. Cliff re-samples it at an XY offset for a cheap analytic slope. |
|
| `SampleSurfaceStructuralZ` | — | **F20:** the RAW SurfaceWorld heightfield (continents+mountains+detail), BEFORE any terrain op; returns terrain Z + relief M. Cliff re-samples it at an XY offset for a cheap analytic slope. |
|
||||||
| `ComputeSurfaceTerrainZ` / `GetSurfaceDensity` | — | SurfaceWorld heightfield → terrain Z, then density; biome **output-blend** lerps dominant/neighbour heights (`ParamsD`/`ParamsN`/weight). **F20 surface ops** (`FSurfaceGenerationParams`, biome-selected + slope/relief-conditioned, all default off): Cliff (slope-gated STEEPENING — push height from local mean where steep ⇒ sheer walls; 4 structural resamples only when on), Terrace (relief-gated + `TerraceHardness`), LayerLines (sedimentary shelves) — pure per-column height REMAPS applied here so the single height oracle stays consistent (MC/ClassifyTile/deco/BP bridge). **Phase 2 OVERHANG** (volumetric — real jutting shelves): in `SurfaceDensityFromColumn`, for AIR voxels in a window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height (tiny low ⇒ air over the void, full high ⇒ borrows the far cliff rock) and unioned in ⇒ a shelf attached to the cliff, tapering out over the void with air beneath (the sketch). Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill `(DirX,DirY)` resolved once in `ComputeSurfaceColumn` (gradient sampled at the REACH scale so a spot over the void can see the cliff), cached on `FSurfaceColumn`. Genuine 3D (per-voxel structural re-eval, gated to steep overhang columns). Off ⇒ byte-identical. The field-preserving LOD0 pass removes only needless temporaries/default construction; every output remains bit-identical. §8.14. |
|
| `ComputeSurfaceTerrainZ` / `ComputeSurfaceColumn` | — | SurfaceWorld heightfield → terrain Z (+ sky-cap ceiling and overhang gate per column); biome **output-blend** lerps dominant/neighbour heights (`ParamsD`/`ParamsN`/weight). **F20 surface ops** (`FSurfaceGenerationParams`, biome-selected + slope/relief-conditioned, all default off): Cliff (slope-gated STEEPENING — push height from local mean where steep ⇒ sheer walls; 4 structural resamples only when on), Terrace (relief-gated + `TerraceHardness`), LayerLines (sedimentary shelves) — pure per-column height REMAPS applied here so the single height oracle stays consistent (MC/ClassifyTile/deco/BP bridge). **Phase 2 OVERHANG** (volumetric — real jutting shelves): in the stack's `FOverhangShelfMod`, for AIR voxels in a window `(TerrainZ, TerrainZ+OverhangHeight]` above a steep slope, the heightfield is re-sampled UPHILL (toward the cliff) by a reach that GROWS with height (tiny low ⇒ air over the void, full high ⇒ borrows the far cliff rock) and unioned in ⇒ a shelf attached to the cliff, tapering out over the void with air beneath (the sketch). Per-column `OverhangAmp`(=strength·slope-gate) + unit uphill `(DirX,DirY)` resolved once in `ComputeSurfaceColumn` (gradient sampled at the REACH scale so a spot over the void can see the cliff), cached on `FSurfaceColumn`. Genuine 3D (per-voxel structural re-eval, gated to steep overhang columns). Off ⇒ byte-identical. The field-preserving LOD0 pass removes only needless temporaries/default construction; every output remains bit-identical. §8.14. |
|
||||||
| `VF_BuildOpStackForChunk` (file-static) | — | **The archetype → stack mapping, written down once.** `GetDensityAt` and `ClassifyTile` both call it; params are passed in, never fetched here. A second copy would be the worst bug available in this file — a tile skipped on the verdict of a stack that is not the one producing its density is a hole. Returns false (⇒ caller falls back to the `switch`) for an unported archetype, missing params, or a **degenerate strate**, since five archetype functions early-out to air there and the stack deliberately has no such early-out. `Refs.Surface == nullptr` makes it refuse SurfaceWorld, which is how `ClassifyTile` keeps its own exact-lattice proof. |
|
| `VF_BuildOpStackForChunk` (file-static) | — | **The archetype → stack mapping, written down once.** `GetDensityAt` and `ClassifyTile` both call it; params are passed in, never fetched here. A second copy would be the worst bug available in this file — a tile skipped on the verdict of a stack that is not the one producing its density is a hole. Returns false for an unknown archetype, missing params, or a **degenerate strate** (the stack deliberately has no degenerate early-out): `GetDensityAt` then returns air and `ClassifyTile` returns `Mixed`. `Refs.Surface == nullptr` makes it refuse SurfaceWorld, which is how `ClassifyTile` keeps its own exact-lattice proof. |
|
||||||
| `ClassifyTile` | — | **T1.d trivial-tile reject. ⛔ Not called by streaming by default since 2026-09-13** (`voxel.OuterClassifierMode=0`; net-negative in game, see ARCHITECTURE §8.10). Cooked/editor recipes now build the exact recipe stack and call its proved `ClassifyBox`; a tile crossing a slot/gap, any diff, or any disturbance returns `Mixed`. This replaces the old global `HasComposerRecipeOverride()` force-to-Mixed. Native cave/surface/gap proofs and all §8.10 caches remain unchanged. |
|
| `ClassifyTile` | — | **T1.d trivial-tile reject. ⛔ Not called by streaming by default since 2026-09-13** (`voxel.OuterClassifierMode=0`; net-negative in game, see ARCHITECTURE §8.10). Cooked/editor recipes now build the exact recipe stack and call its proved `ClassifyBox`; a tile crossing a slot/gap, any diff, or any disturbance returns `Mixed`. This replaces the old global `HasComposerRecipeOverride()` force-to-Mixed. Native cave/surface/gap proofs and all §8.10 caches remain unchanged. |
|
||||||
| `SampleRelief` / `SampleMoisture` | — | Climate fields (pure XY, [0,1]). Relief = shared source of truth for the relief map M. §8.14. |
|
| `SampleRelief` / `SampleMoisture` | — | Climate fields (pure XY, [0,1]). Relief = shared source of truth for the relief map M. §8.14. |
|
||||||
| `SampleBiomeAt` | — | Warped-Voronoi + climate biome query (dominant + neighbour + weight). Reference used by the preview bake + `GetDominantBiomeAt`. §8.14. |
|
| `SampleBiomeAt` | — | Warped-Voronoi + climate biome query (dominant + neighbour + weight). Reference used by the preview bake + `GetDominantBiomeAt`. §8.14. |
|
||||||
| `ResolveBiomeSampleAt` / `RebuildBiomeGrid` | — | Hot-path biome resolve (FBiomeSample) via a box-validated per-chunk cell-grid cache. Bit-identical to `SampleBiomeAt`. §8.14, §8.10. |
|
| `ResolveBiomeSampleAt` / `RebuildBiomeGrid` | — | Hot-path biome resolve (FBiomeSample) via a box-validated per-chunk cell-grid cache. Bit-identical to `SampleBiomeAt`. §8.14, §8.10. |
|
||||||
| `GetDominantBiomeAt` | — | Game-thread query → dominant biome ASSET (content/atmosphere). §8.14. |
|
| `GetDominantBiomeAt` | — | Game-thread query → dominant biome ASSET (content/atmosphere). §8.14. |
|
||||||
| `QueryBiomeAt` | — | Rich game-thread biome probe → `FVoxelBiomeQuery` (dominant/neighbour asset, relief/moisture, blend weight, dominant deco count). Diagnostic behind `AVoxelWorld::GetBiomeAtWorldLocation`. §8.14. |
|
| `QueryBiomeAt` | — | Rich game-thread biome probe → `FVoxelBiomeQuery` (dominant/neighbour asset, relief/moisture, blend weight, dominant deco count). Diagnostic behind `AVoxelWorld::GetBiomeAtWorldLocation`. §8.14. |
|
||||||
| `EvaluateTerrainConditions` | 7549 | **F7 aware placement:** AND-evaluate an entry's `FTerrainCondition[]` (relief/moisture/biome-border) at a candidate voxel XY. Empty = true (zero cost). Pure query (SampleRelief/SampleMoisture/SampleBiomeAt) → worker-safe + game-thread; caller passes the strate's `FBiomeContext` (freq/contrast + Voronoi map). Consumed by deco `BuildCellSpawns` + `SpawnLandmarkInstance`; shared core of the future quest FindFeature locator. |
|
| `EvaluateTerrainConditions` | 6973 | **F7 aware placement:** AND-evaluate an entry's `FTerrainCondition[]` (relief/moisture/biome-border) at a candidate voxel XY. Empty = true (zero cost). Pure query (SampleRelief/SampleMoisture/SampleBiomeAt) → worker-safe + game-thread; caller passes the strate's `FBiomeContext` (freq/contrast + Voronoi map). Consumed by deco `BuildCellSpawns` + `SpawnLandmarkInstance`; shared core of the future quest FindFeature locator. |
|
||||||
|
|
||||||
> **Per-voxel hot-path memos (perf pass 2, all bit-identical — same hashes/math, hoisted per
|
> **Per-voxel hot-path memos (perf pass 2, all bit-identical — same hashes/math, hoisted per
|
||||||
> chunk/cell):** `GetDensityAt` uses the DiffLayer snapshot cache (§3.9); `GetDensityWithParams`
|
> chunk/cell):** `GetDensityAt` uses the DiffLayer snapshot cache (§3.9); `GetDensityWithParams`
|
||||||
> memoizes the strate index per (chunkZ, `GetLayoutVersion()`); `thread_local` per-cell lattice
|
> memoizes the strate index per (chunkZ, `GetLayoutVersion()`); `thread_local` per-cell lattice
|
||||||
> bakes cover slab columns (`GetSlabDensity` step 4), maze open edges, vertical shafts +
|
> bakes cover slab columns (`FGridColumnMod`), maze open edges (`FLatticeCorridorSource`), vertical
|
||||||
> cross-connectors, floating-island constants, and the disturbance chasms/bridges/ridges; worm
|
> shafts + cross-connectors (`FShaftFieldSource`), floating-island constants (`FIslandBlobSource`),
|
||||||
|
> and the disturbance chasms/bridges/ridges; worm
|
||||||
> tunnels short-circuit the 2nd Perlin when N1 ≥ WormThreshold (N2 ≥ 0 ⇒ can't carve). With
|
> tunnels short-circuit the 2nd Perlin when N1 ≥ WormThreshold (N2 ≥ 0 ⇒ can't carve). With
|
||||||
> `voxel.WormBlockSkip=1`, a worker-local cache proves whole 4³ lattice blocks only when the
|
> `voxel.WormBlockSkip=1`, a worker-local cache proves whole 4³ lattice blocks only when the
|
||||||
> actual 3D Perlin bound (`L = 8.118988160`, including `VOXEL_NOISE_SCALE`) plus a `1e-3`
|
> actual 3D Perlin bound (`L = 8.118988160`, including `VOXEL_NOISE_SCALE`) plus a `1e-3`
|
||||||
@@ -465,7 +462,7 @@ SurfaceWorld remains refused because its production height can depend on manager
|
|||||||
context. It never touches a generator, operator stack, manager, cache, or mutable state.
|
context. It never touches a generator, operator stack, manager, cache, or mutable state.
|
||||||
|
|
||||||
`FVoxelPassageLanding`, `VF_BuildPassageLanding`, `VF_EvaluatePassageLandingSDF`, and
|
`FVoxelPassageLanding`, `VF_BuildPassageLanding`, `VF_EvaluatePassageLandingSDF`, and
|
||||||
`VF_IsPassageLandingFloor` (h:57/184-199, implemented near `.cpp:6357`) turn each inter-strate mouth
|
`VF_IsPassageLandingFloor` (h:57/184-199, implemented near `.cpp:6468`) turn each inter-strate mouth
|
||||||
into a deterministic rounded room plus a hard support slab. The room is sized from the player
|
into a deterministic rounded room plus a hard support slab. The room is sized from the player
|
||||||
capsule (minimum 12 voxels / 3 m flat floor, 12 voxels / 3 m clear height, and at least 14 voxels /
|
capsule (minimum 12 voxels / 3 m flat floor, 12 voxels / 3 m clear height, and at least 14 voxels /
|
||||||
3.5 m authored width); the `(0,0)` origin landing is enlarged for the reserved future shaft, while
|
3.5 m authored width); the `(0,0)` origin landing is enlarged for the reserved future shaft, while
|
||||||
@@ -497,11 +494,10 @@ order-independence test compares them bit-for-bit.
|
|||||||
**`Public/VoxelStrateDefinition.h`** — `UVoxelStrateDefinition : UPrimaryDataAsset`
|
**`Public/VoxelStrateDefinition.h`** — `UVoxelStrateDefinition : UPrimaryDataAsset`
|
||||||
(line 46). One asset = one strate *type*. Fields: identity, `StrateHeightInChunks`(75),
|
(line 46). One asset = one strate *type*. Fields: identity, `StrateHeightInChunks`(75),
|
||||||
`TransitionType`(93)/`TransitionBlendChunks`(103), `GeneratorType`(116),
|
`TransitionType`(93)/`TransitionBlendChunks`(103), `GeneratorType`(116),
|
||||||
`GenerationParams`(160), `SlabParams`(171), `Biomes[]`+`BiomeMapParams` (the biome list +
|
`GenerationParams`(137), `SlabParams`(148), `Biomes[]`+`BiomeMapParams` (the biome list +
|
||||||
field tuning — empty ⇒ unchanged world, §8.14), `TerrainOperations`(275), visuals/fog/light,
|
field tuning — empty ⇒ unchanged world, §8.14), `TerrainOperations`(252), visuals/fog/light,
|
||||||
content lists, audio, `GameplayTags`(416), `bUseOperatorStack` (the OPSTACK A/B opt-in — only bites
|
content lists, audio, `GameplayTags`(393). EditConditions show/hide param groups by generator
|
||||||
if the archetype is in `UsesOperatorStackForChunk`'s ported list). EditConditions show/hide param
|
type.
|
||||||
groups by generator type.
|
|
||||||
|
|
||||||
**`Public/VoxelStrateManager.h` + `.cpp`** — `UVoxelStrateManager : UObject` (h:152).
|
**`Public/VoxelStrateManager.h` + `.cpp`** — `UVoxelStrateManager : UObject` (h:152).
|
||||||
Maps depth→strate at runtime; owns passages.
|
Maps depth→strate at runtime; owns passages.
|
||||||
@@ -510,26 +506,26 @@ Maps depth→strate at runtime; owns passages.
|
|||||||
- `FStrateSlot` (h:128): definition + chunk-Z range + index.
|
- `FStrateSlot` (h:128): definition + chunk-Z range + index.
|
||||||
| Method | .cpp line | Role |
|
| Method | .cpp line | Role |
|
||||||
|--------|-----------|------|
|
|--------|-----------|------|
|
||||||
| `Initialize` | 1165 | Keeps the existing sequencing through `GeneratePassages`, but selects one slot source: a validated cooked season (exact ordered bounds/vector/recipe) or the unchanged authored fixed+sorted/shuffled pool. Assigned-invalid seasons fail closed. Authored source definitions are duplicated as content bags; generated slots use deterministic C++ defaults and zero auto-passages because schema 2 does not store passage configuration. |
|
| `Initialize` | 1164 | Keeps the existing sequencing through `GeneratePassages`, but selects one slot source: a validated cooked season (exact ordered bounds/vector/recipe) or the unchanged authored fixed+sorted/shuffled pool. Assigned-invalid seasons fail closed. Authored source definitions are duplicated as content bags; generated slots use deterministic C++ defaults and zero auto-passages because schema 2 does not store passage configuration. |
|
||||||
| `GeneratePassages` | 1735 | Deterministic passages between consecutive strates (per-strate `PassageConfig::Style` control points; auto passages retain the existing `EVoxelPassageType` default); placement and shape values are independently salted hashes of seed + boundary slot + connection index. Each mouth independently queries its own strate's pure player-fit `VF_SuggestLandingPoint` with its contract seed (cave room salt, otherwise world seed), may move within its archetype-specific budget, then receives a body-sized local room/floor. The origin landing is reserved for the future shaft; no landing-to-root road is generated. Unsupported/no-fit answers preserve the old random reach; a deterministic level dog-leg is inserted when the final ramp needs it to stay ≤44°. All descriptors and bounds are generated without reading another passage. |
|
| `GeneratePassages` | 1659 | Deterministic passages between consecutive strates (per-strate `PassageConfig::Style` control points; auto passages retain the existing `EVoxelPassageType` default); placement and shape values are independently salted hashes of seed + boundary slot + connection index. Each mouth independently queries its own strate's pure player-fit `VF_SuggestLandingPoint` with its contract seed (cave room salt, otherwise world seed), may move within its archetype-specific budget, then receives a body-sized local room/floor. The origin landing is reserved for the future shaft; no landing-to-root road is generated. Unsupported/no-fit answers preserve the old random reach; a deterministic level dog-leg is inserted when the final ramp needs it to stay ≤44°. All descriptors and bounds are generated without reading another passage. |
|
||||||
| `EvaluateModifierSDF` | 2719 | SDF of passage tubes plus local landing rooms at a point. A per-chunk `thread_local` shortlist (`PassagesVersion`-stamped) → far chunks return `FLT_MAX` without walking `Passages`; the same cache serves floor membership. `FPassageEvaluationCache::FloorProjections` reuses native/generic floor projections and walkable-air results once per `(sample, passage)` using exact double XYZ equality and unchanged float outputs; `voxel.FloorRound1PassageProjectionCache=0` disables only this A/B cache. §8.10. |
|
| `EvaluateModifierSDF` | 2643 | SDF of passage tubes plus local landing rooms at a point. A per-chunk `thread_local` shortlist (`PassagesVersion`-stamped) → far chunks return `FLT_MAX` without walking `Passages`; the same cache serves floor membership. `FPassageEvaluationCache::FloorProjections` reuses native/generic floor projections and walkable-air results once per `(sample, passage)` using exact double XYZ equality and unchanged float outputs; `voxel.FloorRound1PassageProjectionCache=0` disables only this A/B cache. §8.10. |
|
||||||
| `ApplyPassageModifier` | 2848 | Shared legacy/op-stack passage post: carves tube and local landing air through the idempotent landing primitive, then restores the local landing support slab in internal positive-solid density. |
|
| `ApplyPassageModifier` | 2772 | Shared legacy/op-stack passage post: carves tube and local landing air through the idempotent landing primitive, then restores the local landing support slab in internal positive-solid density. |
|
||||||
| `VF_ApplyPassageLandingCarving` | — | Idempotent density-independent landing-air threshold shared by the structural pass and the post-disturbance MC backstop; keeps legacy and operator-stack results bit-identical. |
|
| `VF_ApplyPassageLandingCarving` | — | Idempotent density-independent landing-air threshold shared by the structural pass and the post-disturbance MC backstop, so the two agree bit for bit. |
|
||||||
| `ApplyPassageLandingFloorMC` | 3059 | Reasserts the same support slab after MC-space disturbances and before the final XY edge seal. |
|
| `ApplyPassageLandingFloorMC` | 2983 | Reasserts the same support slab after MC-space disturbances and before the final XY edge seal. |
|
||||||
| `AnyPassageNearBox` | — | Conservative sphere-vs-AABB test of every passage's bound against a voxel box (+carve blend pad). Per TILE (ClassifyTile guard), never per voxel. |
|
| `AnyPassageNearBox` | — | Conservative sphere-vs-AABB test of every passage's bound against a voxel box (+carve blend pad). Per TILE (ClassifyTile guard), never per voxel. |
|
||||||
| `AnyPassageLandingFloorNearBox` | — | Conservative local-landing-floor AABB guard. It kills the `AllAir` hypothesis wherever a proved landing floor can occur; the op-stack returns `Both` for the same box. |
|
| `AnyPassageLandingFloorNearBox` | — | Conservative local-landing-floor AABB guard. It kills the `AllAir` hypothesis wherever a proved landing floor can occur; the op-stack returns `Both` for the same box. |
|
||||||
| `FindSlotIndexForChunkZ` | 6187 | Z → layout index. |
|
| `FindSlotIndexForChunkZ` | 6111 | Z → layout index. |
|
||||||
| `GetStrateAt` / `GetStrateIndex` | 6203 / 6215 | World-Z queries. |
|
| `GetStrateAt` / `GetStrateIndex` | 6127 / 6139 | World-Z queries. |
|
||||||
| `GetLayoutVersion` | h:250 (inline) | Layout/passage generation counter (= `PassagesVersion`, bumped by every `Initialize` and by the editor composer override). Hot-path callers key `thread_local` memos on it (strate-index memo in `GetDensityWithParams`, passage shortlist) so live changes never serve stale data. |
|
| `GetLayoutVersion` | h:250 (inline) | Layout/passage generation counter (= `PassagesVersion`, bumped by every `Initialize` and by the editor composer override). Hot-path callers key `thread_local` memos on it (strate-index memo in `GetDensityWithParams`, passage shortlist) so live changes never serve stale data. |
|
||||||
| `GetStrateForChunk` | 6226 | Chunk → definition. |
|
| `GetStrateForChunk` | 6150 | Chunk → definition. |
|
||||||
| `GetGeneratorTypeForChunk` | 6252 | Chunk → generator type. |
|
| `GetGeneratorTypeForChunk` | 6176 | Chunk → generator type. |
|
||||||
| `UsesOperatorStackForChunk` | 6279 | Chunk → should `GetDensityAt` take the operator stack? `bUseOperatorStack` on the definition **AND** archetype in the ported list — **now all 8 of 8** (Maze, FlatPlain, CrystalChamber, SurfaceWorld, VerticalShafts, FloatingIslands, TunnelNetwork, Underwater). **That list is written down here and nowhere else.** With every archetype ported the flag is now the *only* thing that decides the path, so ticking the box is no longer a no-op anywhere — it is a real switch onto the operator stack for that strate. |
|
| `UsesOperatorStackForChunk` | 6203 | Chunk → does its slot have an operator stack? True for any in-layout slot with a definition whose archetype is one of the 8 (Maze, FlatPlain, CrystalChamber, SurfaceWorld, VerticalShafts, FloatingIslands, TunnelNetwork, Underwater); false outside the layout. **That list is written down here and nowhere else.** `ClassifyTile` guards on it; `GetDensityAt` uses it to share TunnelNetwork stacks by chunk Z. |
|
||||||
| `GetRecipeForChunk` | — | Runtime immutable recipe/vector/strate-seed copy for cooked seasons; editor slot overrides use the same worker hand-off. |
|
| `GetRecipeForChunk` | — | Runtime immutable recipe/vector/strate-seed copy for cooked seasons; editor slot overrides use the same worker hand-off. |
|
||||||
| `GetSlabParamsForChunk` | 6359 | Slab params with runtime Z bounds (no blend — slabs use Hard). |
|
| `GetSlabParamsForChunk` | 6263 | Slab params with runtime Z bounds (no blend — slabs use Hard). |
|
||||||
| `GetBiomeContextForChunk` | — | Flatten the strate's `Biomes[]` + `BiomeMapParams` into a POD `FBiomeContext` for the biome field. Empty ⇒ biomes disabled. §8.14. |
|
| `GetBiomeContextForChunk` | — | Flatten the strate's `Biomes[]` + `BiomeMapParams` into a POD `FBiomeContext` for the biome field. Empty ⇒ biomes disabled. §8.14. |
|
||||||
| `GetGenerationParams` | 6605 | **Blended** TunnelNetwork params (handles Gradient/Hard/Interleaved transitions). |
|
| `GetGenerationParams` | 6509 | **Blended** TunnelNetwork params (handles Gradient/Hard/Interleaved transitions). |
|
||||||
| `SetComposerOverrideForStrate` / `GetComposerOverrideForChunk` | editor-only | Temporary density-only candidate overlay for one existing slot. Copies immutable params/recipe into the worker refetch path, leaves layout/content/passages intact, and bumps `PassagesVersion` to invalidate generator memos. |
|
| `SetComposerOverrideForStrate` / `GetComposerOverrideForChunk` | editor-only | Temporary density-only candidate overlay for one existing slot. Copies immutable params/recipe into the worker refetch path, leaves layout/content/passages intact, and bumps `PassagesVersion` to invalidate generator memos. |
|
||||||
| `BuildParamsFromDefinition` (static) | 6911 | Returns the definition's base `GenerationParams` only. Terrain-op assets are passed separately to `BuildChunkCache`, which applies one selected op per room and pre-bakes Column/Pit/Chimney features. |
|
| `BuildParamsFromDefinition` (static) | 6815 | Returns the definition's base `GenerationParams` only. Terrain-op assets are passed separately to `BuildChunkCache`, which applies one selected op per room and pre-bakes Column/Pit/Chimney features. |
|
||||||
|
|
||||||
**`Public/VoxelTerrainOpDefinition.h` + `.cpp`** — `UVoxelTerrainOpDefinition : UPrimaryDataAsset`
|
**`Public/VoxelTerrainOpDefinition.h` + `.cpp`** — `UVoxelTerrainOpDefinition : UPrimaryDataAsset`
|
||||||
(h:67). One asset = one terrain op. `EVoxelTerrainOpType` (h:36): Terrace, LayerLines,
|
(h:67). One asset = one terrain op. `EVoxelTerrainOpType` (h:36): Terrace, LayerLines,
|
||||||
@@ -708,7 +704,9 @@ 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`)
|
### 3.12 Automation tests — `Private/Tests/` (added 2026-07-27, `#if WITH_DEV_AUTOMATION_TESTS`)
|
||||||
The plugin's first tests (`Docs/archive/OPSTACK-PLAN.md` Phase 0.5). Run them from the editor's
|
The plugin's first tests (`Docs/archive/OPSTACK-PLAN.md` Phase 0.5). Run them from the editor's
|
||||||
**Session Frontend → Automation**, filter `VoxelForge`.
|
**Session Frontend → Automation**, filter `VoxelForge`. The `OpStack.*Equivalence` names are
|
||||||
|
historical: those tests check the shipped stacks themselves and no longer compare against a
|
||||||
|
second density path.
|
||||||
|
|
||||||
| File | Test name | What it proves |
|
| File | Test name | What it proves |
|
||||||
|------|-----------|----------------|
|
|------|-----------|----------------|
|
||||||
@@ -720,23 +718,23 @@ The plugin's first tests (`Docs/archive/OPSTACK-PLAN.md` Phase 0.5). Run them fr
|
|||||||
| `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. |
|
| `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. |
|
||||||
| `VoxelForgeHeightStackTest.cpp` | `VoxelForge.OpStack.SurfaceHeightEquivalence` | The height-space stack vs `ComputeSurfaceTerrainZ`, in **altitudes**. Runs twice: defaults, then **all F20 terrain ops ON** — the load-bearing pass, since the ops are off by default and the defaults pass exercises only the structural source. Also brute-forces `MaxDisplacement` (a false bound would be a hole). Bar is bit-identity; a height delta is a visibly different world, not rounding. |
|
| `VoxelForgeHeightStackTest.cpp` | `VoxelForge.OpStack.SurfaceHeightEquivalence` | The height-space stack vs `ComputeSurfaceTerrainZ`, in **altitudes**. Runs twice: defaults, then **all F20 terrain ops ON** — the load-bearing pass, since the ops are off by default and the defaults pass exercises only the structural source. Also brute-forces `MaxDisplacement` (a false bound would be a hole). Bar is bit-identity; a height delta is a visibly different world, not rounding. |
|
||||||
| `VoxelForgeCrossPlatformTest.cpp` | `VoxelForge.Determinism.CrossPlatformDigest` | SHAPE digest (sign of density = the world) + FIELD digest (bit-for-bit) over a fixed integer grid, plus `NearIso` bounding how many samples could flip sign. Reports rather than asserts until pinned. Run on Windows and Linux and compare. |
|
| `VoxelForgeCrossPlatformTest.cpp` | `VoxelForge.Determinism.CrossPlatformDigest` | SHAPE digest (sign of density = the world) + FIELD digest (bit-for-bit) over a fixed integer grid, plus `NearIso` bounding how many samples could flip sign. Reports rather than asserts until pinned. Run on Windows and Linux and compare. |
|
||||||
| `VoxelForgeOpStackSlabTest.cpp` | `VoxelForge.OpStack.SlabEquivalence` | **Phase 2's first port.** The same 6-op slab stack vs `GetSlabDensity` over 20k points, run twice — FlatPlain **and** CrystalChamber — which is what demonstrates the two archetypes really are one op. Plus window-invariance and box-verdict brute force. Compares against the reference **as it is now** (post Z-term removal), so green = pure refactor and any visual delta is attributable to §3.1 alone. |
|
| `VoxelForgeOpStackSlabTest.cpp` | `VoxelForge.OpStack.SlabEquivalence` | The same 6-op slab stack run three times — FlatPlain, CrystalChamber and a tuned CrystalChamber (the worst case for the `ClassifyBox` amplitude bounds): finite density over 20k points, window-invariance across order and threads, and box-verdict brute force. |
|
||||||
| `VoxelForgeOpStackTunnelTest.cpp` | `VoxelForge.OpStack.TunnelNetworkSpineEquivalence` | **Stage A of the last port.** Zeroes the 13 detail-op amplitudes so the *original* takes the path stage A ported — that is what makes an incomplete stack verifiable now. Samples in **clusters** (24 chunks × 250 points), because the SDF cache rebuilds when a query leaves its box and uniform sampling would rebuild per point on both paths. Check 3 (two param sets, A/B interleaved) compares each stack **to itself alone, never to the original** — the original would fail it, see AUDIT §C2. Asserts **zero** box verdicts, which is the honest stage-A result. |
|
| `VoxelForgeOpStackTunnelTest.cpp` | `VoxelForge.OpStack.TunnelNetworkSpineEquivalence` | **Stage A of the last port.** Zeroes the 13 detail-op amplitudes so the *original* takes the path stage A ported — that is what makes an incomplete stack verifiable now. Samples in **clusters** (24 chunks × 250 points), because the SDF cache rebuilds when a query leaves its box and uniform sampling would rebuild per point on both paths. Check 3 (two param sets, A/B interleaved) compares each stack **to itself alone, never to the original** — the original would fail it, see AUDIT §C2. Asserts **zero** box verdicts, which is the honest stage-A result. |
|
||||||
| `VoxelForgeOpStackShaftTest.cpp` | `VoxelForge.OpStack.VerticalShaftEquivalence` | The port that tests **reuse**, not fidelity: three of the five ops are Maze's, unchanged. Forces connectors + ledges on, because both are off or negligible at defaults and a resting param is an untested operator. The source and mirror now share a deterministic inner-3×3-emitted drainage tree whose parents come from rebuild-only ±2/±3 windows in a wider direct-indexed halo (default 9×9 emit / 15×15 roll), plus the old random links. Tree connector Z is exact and ledge-free; tree and random capsules are spatially culled from the cached rebuild. **Fixed 2026-07-29:** its box query used to return `CarveOnly` because a shaft merely *existed* within a `Spacing*1.6` halo — true almost everywhere at `ShaftSpacing 55 / ShaftDensity 0.6`, hence **0 of 60** tiles. It now publishes an isolated interval for tree and random capsules (same row-major cell order ⇒ same `VoxelHash::Pair`, so symmetry of `Pair()` is not assumed), with **Z exact** and XY conservative; the ledge and converter decide their own effects. The sampler was also widened from ±48 voxels to ±440. Every proved tile is brute-forced over its full lattice, and the test reports rebuild-vs-hot cache cost. |
|
| `VoxelForgeOpStackShaftTest.cpp` | `VoxelForge.OpStack.VerticalShaftEquivalence` | The port that tests **reuse**, not fidelity: three of the five ops are Maze's, unchanged. Forces connectors + ledges on, because both are off or negligible at defaults and a resting param is an untested operator. The source and mirror now share a deterministic inner-3×3-emitted drainage tree whose parents come from rebuild-only ±2/±3 windows in a wider direct-indexed halo (default 9×9 emit / 15×15 roll), plus the old random links. Tree connector Z is exact and ledge-free; tree and random capsules are spatially culled from the cached rebuild. **Fixed 2026-07-29:** its box query used to return `CarveOnly` because a shaft merely *existed* within a `Spacing*1.6` halo — true almost everywhere at `ShaftSpacing 55 / ShaftDensity 0.6`, hence **0 of 60** tiles. It now publishes an isolated interval for tree and random capsules (same row-major cell order ⇒ same `VoxelHash::Pair`, so symmetry of `Pair()` is not assumed), with **Z exact** and XY conservative; the ledge and converter decide their own effects. The sampler was also widened from ±48 voxels to ±440. Every proved tile is brute-forced over its full lattice, and the test reports rebuild-vs-hot cache cost. |
|
||||||
| `VoxelForgeOpStackIslandTest.cpp` | `VoxelForge.OpStack.FloatingIslandEquivalence` | The port that runs the stack **backwards** — void + fill vs rock + carve, same classes with the opposite sign. Counts interior-solid and open-void samples separately (on this archetype an aggregate "N solid" is dominated by the seal bands and says nothing about the islands). Counts `AllSolid` and `AllAir` verdicts **separately** too: `AllAir` is the one no cave archetype could ever prove, and it is the entire perf argument here. |
|
| `VoxelForgeOpStackIslandTest.cpp` | `VoxelForge.OpStack.FloatingIslandEquivalence` | The port that runs the stack **backwards** — void + fill vs rock + carve, same classes with the opposite sign. Counts interior-solid and open-void samples separately (on this archetype an aggregate "N solid" is dominated by the seal bands and says nothing about the islands). Counts `AllSolid` and `AllAir` verdicts **separately** too: `AllAir` is the one no cave archetype could ever prove, and it is the entire perf argument here. |
|
||||||
| `VoxelForgeOpStackMazeTest.cpp` | `VoxelForge.OpStack.MazeEquivalence` | **Phase 1's load-bearing test.** The 8-op Maze stack vs `GetMazeDensity` over 20k points (bit-identity; a side-of-iso disagreement is the hard fail), plus purity across workers, a 64-seed spanning-tree/connectivity audit, corridor morphology, cache rebuild-vs-hot timing, and brute force on every box verdict the stack emits. Latest audit: **0 tree edge-count violations, 0 tree-disconnected, 0 Maze-disconnected**; interior degrees `d1=15.91%`, `d2=36.60%`, `d3=31.73%`, `d4=12.95%`, `d5=2.56%`, `d6=0.25%`, mean **2.504** versus a cubic grid's degree-6/mean-6.000; runs `min=1 / median=1 / p90=3 / max=18`. Latest focused cache timing: **1.436 μs rebuild**, **0.150 μs hot call**; final namespace timing: **1.130 μs rebuild**, **0.145 μs hot call**. Latest box audit: **104 proved, 152 Mixed, 138,424 voxels, 0 violations**. |
|
| `VoxelForgeOpStackMazeTest.cpp` | `VoxelForge.OpStack.MazeEquivalence` | The 8-op Maze stack: decomposition, purity across workers, a 64-seed spanning-tree/connectivity audit, corridor morphology, cache rebuild-vs-hot timing, and brute force on every box verdict the stack emits. Latest audit: **0 tree edge-count violations, 0 tree-disconnected, 0 Maze-disconnected**; interior degrees `d1=15.91%`, `d2=36.60%`, `d3=31.73%`, `d4=12.95%`, `d5=2.56%`, `d6=0.25%`, mean **2.504** versus a cubic grid's degree-6/mean-6.000; runs `min=1 / median=1 / p90=3 / max=18`. Latest focused cache timing: **1.436 μs rebuild**, **0.150 μs hot call**; final namespace timing: **1.130 μs rebuild**, **0.145 μs hot call**. Latest box audit: **104 proved, 152 Mixed, 138,424 voxels, 0 violations**. |
|
||||||
| `VoxelForgeStrateParamCoverageTest.cpp` | `VoxelForge.Determinism.StrateParamBlendCoverage` | **The X-macro guard** (added 2026-08-17). `FStrateGenerationParams::Lerp` blends the hand-written `VF_STRATE_PARAM_FIELDS` list, **not** the struct — so a field added to one and not the other compiles, tests green, and silently takes its **default** inside every Gradient/Interleaved transition band. This expands the X-macro a **third** way (after LERP and SNAP), into a name list, and diffs it against the struct's UObject reflection. Pure shape test: no fixture, no world, instant. `GExemptFieldNames` is **empty** — every reflected field is covered today, and any exemption must be written down as a decision. Stakes rise with the world composer, which intends to invent parameter sets through this same `Lerp` (`COMPOSER-NOTES.md`). |
|
| `VoxelForgeStrateParamCoverageTest.cpp` | `VoxelForge.Determinism.StrateParamBlendCoverage` | **The X-macro guard** (added 2026-08-17). `FStrateGenerationParams::Lerp` blends the hand-written `VF_STRATE_PARAM_FIELDS` list, **not** the struct — so a field added to one and not the other compiles, tests green, and silently takes its **default** inside every Gradient/Interleaved transition band. This expands the X-macro a **third** way (after LERP and SNAP), into a name list, and diffs it against the struct's UObject reflection. Pure shape test: no fixture, no world, instant. `GExemptFieldNames` is **empty** — every reflected field is covered today, and any exemption must be written down as a decision. Stakes rise with the world composer, which intends to invent parameter sets through this same `Lerp` (`COMPOSER-NOTES.md`). |
|
||||||
| `VoxelForgeComposerParameterRollTest.cpp` | `VoxelForge.Composer.ParameterRoll` | Asset-Registry corpus audit + complete per-archetype spread/exclusion/clamp table; asserts bit-identical deterministic rerolls and same-archetype parents; measures 64 transient candidate strates with `VF_MeasureStrate` plus the exact unsnapped arrival→departure law; brute-forces every rolled production box verdict. World-scale run: **1,422 applications / 168 distinct fields**; **36/64 survival**, 60 non-vacuous, 58 largest-component, 40 exact-law passes; **1,411 Mixed + 731 AllSolid + 418 AllAir = 1,149 proved boxes**, **1,529,319 lattice voxels checked, 0 violations**, 148.387 s. |
|
| `VoxelForgeComposerParameterRollTest.cpp` | `VoxelForge.Composer.ParameterRoll` | Asset-Registry corpus audit + complete per-archetype spread/exclusion/clamp table; asserts bit-identical deterministic rerolls and same-archetype parents; measures 64 transient candidate strates with `VF_MeasureStrate` plus the exact unsnapped arrival→departure law; brute-forces every rolled production box verdict. World-scale run: **1,422 applications / 168 distinct fields**; **36/64 survival**, 60 non-vacuous, 58 largest-component, 40 exact-law passes; **1,411 Mixed + 731 AllSolid + 418 AllAir = 1,149 proved boxes**, **1,529,319 lattice voxels checked, 0 violations**, 148.387 s. |
|
||||||
| ″ | `VoxelForge.Composer.TerrainDetailLiveness` | Fixed 4,096-point `GetDensityAt` lattice, legacy and operator-stack paths; changes one terrain-detail group at a time with an empty terrain-op pool. Proves 9 live groups / 23 fields and 3 dead groups / 11 fields; all 24 rows match. |
|
| ″ | `VoxelForge.Composer.TerrainDetailLiveness` | Fixed 4,096-point `GetDensityAt` lattice on the TunnelNetwork slot; changes one terrain-detail group at a time with an empty terrain-op pool. Proves 9 live groups / 23 fields and 3 dead groups / 11 fields; all 12 rows must match. |
|
||||||
| `VoxelForgeComposerPromotionTest.cpp` | `VoxelForge.Composer.Promotion` | Re-measures the 12 project/default members, simulates five deterministic 24-candidate seasons with normalized measured-metric novelty (`<0.20`), cap 6, cumulative JSON promotion, provenance counts, corpus-hash checks, fresh-load gate verification, spread/survival reporting, and deliberate stale-metric corruption. The final landing validation passed the strict existing gates; no assertion was weakened. |
|
| `VoxelForgeComposerPromotionTest.cpp` | `VoxelForge.Composer.Promotion` | Re-measures the 12 project/default members, simulates five deterministic 24-candidate seasons with normalized measured-metric novelty (`<0.20`), cap 6, cumulative JSON promotion, provenance counts, corpus-hash checks, fresh-load gate verification, spread/survival reporting, and deliberate stale-metric corruption. The final landing validation passed the strict existing gates; no assertion was weakened. |
|
||||||
| `VoxelForgeComposerStructureRollTest.cpp` | `VoxelForge.Composer.StructureRoll` | Rolls root polarity → legal shape source → polarity-derived conversion → 4–8 declaration-legal modifiers → mandatory structural posts; blends the six native parameter families independently, measures 64 novel stacks, captures the same grid for the deterministic filled/contour XZ/XY preview, runs a separate step-1 radius-64 ROI pass for the 33 survivors centred on `LargestComponentPoint`, checks exact arrival→departure connectivity, rerolls every recipe/stack for determinism, and brute-forces every uniform box verdict. World-scale run: **33/64 survival (51.6%)**, **64 distinct recipes**, **0 invalid recipes**, **715 proved custom boxes / 951,665 lattice voxels / 0 violations**, blank plan/card **4/33→5/33**, **174.854 s**, **0 refusals**. |
|
| `VoxelForgeComposerStructureRollTest.cpp` | `VoxelForge.Composer.StructureRoll` | Rolls root polarity → legal shape source → polarity-derived conversion → 4–8 declaration-legal modifiers → mandatory structural posts; blends the six native parameter families independently, measures 64 novel stacks, captures the same grid for the deterministic filled/contour XZ/XY preview, runs a separate step-1 radius-64 ROI pass for the 33 survivors centred on `LargestComponentPoint`, checks exact arrival→departure connectivity, rerolls every recipe/stack for determinism, and brute-forces every uniform box verdict. World-scale run: **33/64 survival (51.6%)**, **64 distinct recipes**, **0 invalid recipes**, **715 proved custom boxes / 951,665 lattice voxels / 0 violations**, blank plan/card **4/33→5/33**, **174.854 s**, **0 refusals**. |
|
||||||
| `VoxelForgeComposerShowcaseTest.cpp` | `VoxelForge.Composer.Showcase` | Exhausts the bounded parameter-roll set (seeds **0, 7331**, indices **0–63**), excludes multi-region rolls while the lateral gate is off, and measures every missing-family candidate in all six interior target slots at step 4 / radius 256 / `MaxCells=8,000,000`. Hard gates are non-vacuous, largest air share ≥ **0.50**, and exact unsnapped arrival→departure connectivity; selection score is floor-area fraction + clearance tie-break + projected-surface tie-break. It asserts roll/manifest determinism, density sign, zero `WorldRadiusVoxels`, bit-identical metric reruns, and writes one alphabetized card per archetype to `Saved/VoxelForge/Showcase/index.html`, with step-1 radius-64 filled/contour plan + vertical ROI images centred on `LargestComponentPoint`. The final landing regeneration evaluated **256** rolls (**82** eligible single-region, **174** skipped multi-region), produced **7 fine renders, 1 refusal, 0 blanks**, and retained a diagnostic hard-gate survivor set of **0/8**. |
|
| `VoxelForgeComposerShowcaseTest.cpp` | `VoxelForge.Composer.Showcase` | Exhausts the bounded parameter-roll set (seeds **0, 7331**, indices **0–63**), excludes multi-region rolls while the lateral gate is off, and measures every missing-family candidate in all six interior target slots at step 4 / radius 256 / `MaxCells=8,000,000`. Hard gates are non-vacuous, largest air share ≥ **0.50**, and exact unsnapped arrival→departure connectivity; selection score is floor-area fraction + clearance tie-break + projected-surface tie-break. It asserts roll/manifest determinism, density sign, zero `WorldRadiusVoxels`, bit-identical metric reruns, and writes one alphabetized card per archetype to `Saved/VoxelForge/Showcase/index.html`, with step-1 radius-64 filled/contour plan + vertical ROI images centred on `LargestComponentPoint`. The final landing regeneration evaluated **256** rolls (**82** eligible single-region, **174** skipped multi-region), produced **7 fine renders, 1 refusal, 0 blanks**, and retained a diagnostic hard-gate survivor set of **0/8**. |
|
||||||
| `VoxelForgeComposerSeasonTest.cpp` | `VoxelForge.Composer.Season` | Existing compose/review/393,216-sample round trip plus schema hash tamper rejection, two independent runtime manager/generator instances, season-authoritative seed/spine/radius, unset-season regression, and brute-force verification of every sampled non-Mixed recipe tile verdict (up to 64). |
|
| `VoxelForgeComposerSeasonTest.cpp` | `VoxelForge.Composer.Season` | Existing compose/review/393,216-sample round trip plus schema hash tamper rejection, two independent runtime manager/generator instances, season-authoritative seed/spine/radius, unset-season regression, and brute-force verification of every sampled non-Mixed recipe tile verdict (up to 64). |
|
||||||
| `VoxelForgeComposerCorpusFreeTest.cpp` | `VoxelForge.Composer.CorpusFree` | Shares each structure recipe across today's corpus blend, naive independent uniform rolls, and constraint-sampled rolls. The completed equal-arm world-scale run uses **16 candidates per arm**, step 4 / radius 256 / `MaxCells=8,000,000`, fixed passage-law mouths, and 40 box probes per candidate. Result: **10/16, 9/16, 8/16** survival; survivor feature-scale ranges are **8..268**, **8..60**, **8..92** voxels. Box checks: **185/246,235**, **260/346,060**, **299/397,969** proved/voxels, **0 violations** in every arm; constraint parameter and box-stop violations were also 0. |
|
| `VoxelForgeComposerCorpusFreeTest.cpp` | `VoxelForge.Composer.CorpusFree` | Shares each structure recipe across today's corpus blend, naive independent uniform rolls, and constraint-sampled rolls. The completed equal-arm world-scale run uses **16 candidates per arm**, step 4 / radius 256 / `MaxCells=8,000,000`, fixed passage-law mouths, and 40 box probes per candidate. Result: **10/16, 9/16, 8/16** survival; survivor feature-scale ranges are **8..268**, **8..60**, **8..92** voxels. Box checks: **185/246,235**, **260/346,060**, **299/397,969** proved/voxels, **0 violations** in every arm; constraint parameter and box-stop violations were also 0. |
|
||||||
| `VoxelForgeLayoutOrderIndependenceTest.cpp` | `VoxelForge.Determinism.LayoutOrderIndependence` | Builds a known transient soft-pointer pool, then rebuilds it in original, reversed, and swapped orders. Requires a non-empty layout and passage set, and compares every slot's definition/Z/height plus passage endpoints, landing descriptors, control geometry, and bounds bit-for-bit. |
|
| `VoxelForgeLayoutOrderIndependenceTest.cpp` | `VoxelForge.Determinism.LayoutOrderIndependence` | Builds a known transient soft-pointer pool, then rebuilds it in original, reversed, and swapped orders. Requires a non-empty layout and passage set, and compares every slot's definition/Z/height plus passage endpoints, landing descriptors, control geometry, and bounds bit-for-bit. |
|
||||||
| `VoxelForgePassageOpenSpaceTest.cpp` | `VoxelForge.Determinism.PassageLandsInOpenSpace` | Uses the real fixture density path to check both generated mouths against independent source queries, then audits all inter-strate landing ends: body-derived room dimensions, final-density flat-floor slope, local floor/join geometry, seal containment, hot-call/rebuild timing, and targeted `ClassifyTile` box soundness. Reports source-fit/refused mouths and per-archetype landing slopes; fails on any floor, join, seal, or box violation. |
|
| `VoxelForgePassageOpenSpaceTest.cpp` | `VoxelForge.Determinism.PassageLandsInOpenSpace` | Uses the real fixture density path to check both generated mouths against independent source queries (Maze/VerticalShafts/FloatingIslands footings are probed on the archetype stack built without a strate manager, i.e. without passage carving), then audits all inter-strate landing ends: body-derived room dimensions, final-density flat-floor slope, local floor/join geometry, seal containment, hot-call/rebuild timing, and targeted `ClassifyTile` box soundness. Reports source-fit/refused mouths and per-archetype landing slopes; fails on any floor, join, seal, or box violation. |
|
||||||
| `VoxelForgeStrateConnectivityTest.cpp` | `VoxelForge.Generation.StrateConnectivity` / `VoxelForge.Generation.StrateConnectivityRefinement` / `VoxelForge.Generation.VerticalShaftSeamFreedom` | Bounded strate metrics with density-polarity and solid-gap controls, deterministic route rechecks, and refinement sweeps. The refinement test uses the bounded 4-chunk density fixture, fits each measurement AABB to the arrival/departure mouth pair plus three shaft spacings (the prescribed local tree fallback), and caps the fitted control at 120,000,000 cells. It reports exact before/after seed-6 cell counts, reruns every negative with the fitted margin doubled, measures tree orphan candidates/path reachability and roughness-bubble proxies, asserts mouths remain inside the deterministic shaft feature core (the exact axis is an unsupported open cylinder), checks source/mirror physical paths, and requires 16/16 effective arrival→departure results across the 16 VerticalShafts seeds. Margin-binding negatives are reported as measurement limits, never as gap findings. The shaft seam test re-evaluates cell-boundary positions after warming distinct neighbouring chunk contexts. |
|
| `VoxelForgeStrateConnectivityTest.cpp` | `VoxelForge.Generation.StrateConnectivity` / `VoxelForge.Generation.StrateConnectivityRefinement` / `VoxelForge.Generation.VerticalShaftSeamFreedom` | Bounded strate metrics with density-polarity and solid-gap controls, deterministic route rechecks, and refinement sweeps. The refinement test uses the bounded 4-chunk density fixture, fits each measurement AABB to the arrival/departure mouth pair plus three shaft spacings (the prescribed local tree fallback), and caps the fitted control at 120,000,000 cells. It reports exact before/after seed-6 cell counts, reruns every negative with the fitted margin doubled, measures tree orphan candidates/path reachability and roughness-bubble proxies, asserts mouths remain inside the deterministic shaft feature core (the exact axis is an unsupported open cylinder), checks source/mirror physical paths, and requires 16/16 effective arrival→departure results across the 16 VerticalShafts seeds. Margin-binding negatives are reported as measurement limits, never as gap findings. The shaft seam test re-evaluates cell-boundary positions after warming distinct neighbouring chunk contexts. |
|
||||||
| `VoxelForgeMazeSeamTest.cpp` | `VoxelForge.Generation.MazeSeamFreedom` | Re-evaluates **42** cell-boundary probes after warming six different neighbouring chunk contexts in both legacy and operator-stack paths. Latest result: **0 legacy mismatches, 0 operator mismatches**. The Maze source's 2×2×2 lower-node window is deliberately local; no wide collect is involved. |
|
| `VoxelForgeMazeSeamTest.cpp` | `VoxelForge.Generation.MazeSeamFreedom` | Re-evaluates **42** cell-boundary probes through `GetDensityAt` after warming six different neighbouring chunk contexts; must report **0 mismatches**. The Maze source's 2×2×2 lower-node window is deliberately local; no wide collect is involved. |
|
||||||
| `VoxelForgeClipmapNoOverlapTest.cpp` | `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage` | Sweeps 5,145 cases (343 centres with negative coords and every parity × 5 radius/level/render-distance settings × 3 vertical clamps): **zero overlapping desired pairs AND coverage ⊇ the frozen `7486c29` selector**. Failed on `7486c29` (1,022,679 overlapping pairs) as intended. |
|
| `VoxelForgeClipmapNoOverlapTest.cpp` | `VoxelForge.Streaming.ClipmapDesiredTilesNoOverlapCoverage` | Sweeps 5,145 cases (343 centres with negative coords and every parity × 5 radius/level/render-distance settings × 3 vertical clamps): **zero overlapping desired pairs AND coverage ⊇ the frozen `7486c29` selector**. Failed on `7486c29` (1,022,679 overlapping pairs) as intended. |
|
||||||
| `VoxelForgeDensityBlockEquivalenceTest.cpp` | `VoxelForge.Density.OperatorBlockMatchesScalarGrid` | Block evaluation (`EvalBlock` after `BuildActiveOpList` pruning) vs scalar, **bit-identical**: 5.1M mesher-grid samples (6 seeds × 8 archetypes × levels 0–5, with edits) + 300k direct `EvalBlock` samples. Scopes `voxel.UseFusedEvaluator=0` so the real block path runs. An injected optimistic `FPassageCarveOp::EffectOverBox` produced 5,612 mismatches. |
|
| `VoxelForgeDensityBlockEquivalenceTest.cpp` | `VoxelForge.Density.OperatorBlockMatchesScalarGrid` | Block evaluation (`EvalBlock` after `BuildActiveOpList` pruning) vs scalar, **bit-identical**: 5.1M mesher-grid samples (6 seeds × 8 archetypes × levels 0–5, with edits) + 300k direct `EvalBlock` samples. Scopes `voxel.UseFusedEvaluator=0` so the real block path runs. An injected optimistic `FPassageCarveOp::EffectOverBox` produced 5,612 mismatches. |
|
||||||
| `VoxelForgeWorldEdgeSealTest.cpp` | `VoxelForge.Generation.WorldEdgeSeal` | Positive samples outside the radius across every fixture strate, bit-identical interior negative control against radius 0, monotonic smooth-ramp check, radius-0 no-op, radial `ClassifyBox`/T1.d proofs brute-forced for every reported voxel, and synthetic plus generated near-rim passage coverage. Emits one aggregate summary with proved-box and voxel counts. |
|
| `VoxelForgeWorldEdgeSealTest.cpp` | `VoxelForge.Generation.WorldEdgeSeal` | Positive samples outside the radius across every fixture strate, bit-identical interior negative control against radius 0, monotonic smooth-ramp check, radius-0 no-op, radial `ClassifyBox`/T1.d proofs brute-forced for every reported voxel, and synthetic plus generated near-rim passage coverage. Emits one aggregate summary with proved-box and voxel counts. |
|
||||||
@@ -756,38 +754,38 @@ The plugin's first tests (`Docs/archive/OPSTACK-PLAN.md` Phase 0.5). Run them fr
|
|||||||
|
|
||||||
## 4. The density pipeline (most-edited hot path)
|
## 4. The density pipeline (most-edited hot path)
|
||||||
|
|
||||||
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3851
|
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749
|
||||||
Stage order (negative=solid throughout). Each stage's anchor:
|
Stage order (negative=solid throughout). Each stage's anchor:
|
||||||
| Step | Line | What |
|
| Step | Line | What |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| 1 — Vertical scale | 3906 | Stretch Z before noise (`VerticalScale`). |
|
| 1 — Vertical scale | 3799 | Stretch Z before noise (`VerticalScale`). |
|
||||||
| 2 — Base density | 3915 | Everything starts solid at `BaseDensity`. |
|
| 2 — Base density | 3808 | Everything starts solid at `BaseDensity`. |
|
||||||
| 3 — Cave warp | 3920 | Domain-warp the SDF query coords (organic shapes). |
|
| 3 — Cave warp | 3813 | Domain-warp the SDF query coords (organic shapes). |
|
||||||
| 4 — SDF morphology | 3974 | Rooms+tunnels via `BuildChunkCache`/`EvaluateSDFCached`. |
|
| 4 — SDF morphology | 3863 | Rooms+tunnels via `BuildChunkCache`/`EvaluateSDFCached`. |
|
||||||
| 4b — Surface roughness | 4378 | Volumetric noise near surfaces (fBM/Ridged/Mixed). |
|
| 4b — Surface roughness | 4267 | Volumetric noise near surfaces (fBM/Ridged/Mixed). |
|
||||||
| 4c–4h — Terrain ops | 4506 | Per-room op applied near surfaces. Sub-anchors below. |
|
| 4c–4h — Terrain ops | 4395 | Per-room op applied near surfaces. Sub-anchors below. |
|
||||||
| · Terracing | 4548 | Step-like ledges. |
|
| · Terracing | 4437 | Step-like ledges. |
|
||||||
| · Layer lines | 4645 | Horizontal grooves (sin of Z). |
|
| · Layer lines | 4534 | Horizontal grooves (sin of Z). |
|
||||||
| · Ribbing | 4675 | Parallel ridges (sin of Z). |
|
| · Ribbing | 4564 | Parallel ridges (sin of Z). |
|
||||||
| · Overhangs | 4704 | Low-Z-freq noise shelves. |
|
| · Overhangs | 4593 | Low-Z-freq noise shelves. |
|
||||||
| · Cliff sharpening | 4740 | Amplify vertical gradient. |
|
| · Cliff sharpening | 4629 | Amplify vertical gradient. |
|
||||||
| · Scallop | 4784 | Cellular erosion bowls. |
|
| · Scallop | 4673 | Cellular erosion bowls. |
|
||||||
| · Arch/Bridge | 4820 | Hash-placed capsules across voids. |
|
| · Arch/Bridge | 4709 | Hash-placed capsules across voids. |
|
||||||
| 4d — Columns | 4849 | Pre-baked vertical cylinders. |
|
| 4d — Columns | 4738 | Pre-baked vertical cylinders. |
|
||||||
| 4g — Domes | 4877 | Room-relative hemispherical ceilings. |
|
| 4g — Domes | 4766 | Room-relative hemispherical ceilings. |
|
||||||
| 4h — Pinch | 4942 | Passage bottlenecks. |
|
| 4h — Pinch | 4831 | Passage bottlenecks. |
|
||||||
| 5 — Worm tunnels | 5016 | abs(noise1)+abs(noise2), masked by distance-to-network (`WormNetworkRange`: braids hugging rooms/tunnels, no far-field speckle; 0 = legacy unmasked). |
|
| 5 — Worm tunnels | 4905 | abs(noise1)+abs(noise2), masked by distance-to-network (`WormNetworkRange`: braids hugging rooms/tunnels, no far-field speckle; 0 = legacy unmasked). |
|
||||||
| 6 — Boundary seal | 5098 | Solid top/bottom shells (`ApplyBoundarySeal`). |
|
| 6 — Boundary seal | 4987 | Solid top/bottom shells (`ApplyBoundarySeal`). |
|
||||||
| 7 — Inter-strate passages + landings | `GetDensityWithParams`: 5119; `GetSlabDensity`: 5372 | Carve wandering passage tubes and deterministic local landing rooms, restore their support floors, then leave the final XY seal to the outer MC post (`ApplyPassageModifier` / `ApplyPassageLandingFloorMC`). |
|
| 7 — Inter-strate passages + landings | 5008 | Carve wandering passage tubes and deterministic local landing rooms, restore their support floors, then leave the final XY seal to the outer MC post (`ApplyPassageModifier` / `ApplyPassageLandingFloorMC`). |
|
||||||
|
|
||||||
### 4.2 `GetSlabDensity` (FlatPlain / CrystalChamber) — VoxelGenerator.cpp:5188
|
### 4.2 `BuildSlabStack` (FlatPlain / CrystalChamber) — VoxelDensityOpStack.cpp
|
||||||
| Step | Line | What |
|
| Step | Op | What |
|
||||||
|------|------|------|
|
|------|----|------|
|
||||||
| 1 — Floor surface | 5199 | Noisy floor height. |
|
| 1 — Floor surface | `FSlabVoidSource` | Noisy floor height (XY-pure). |
|
||||||
| 2 — Ceiling surface | 5234 | Formations hang downward (`abs(noise)`). |
|
| 2 — Ceiling surface | `FSlabVoidSource` | Formations hang downward (`abs(noise)`). |
|
||||||
| 3 — Void→base density | 5271 | Solid outside [floor,ceiling]. |
|
| 3 — Void→base density | `FSlabVoidSource` | Solid outside [floor,ceiling]. |
|
||||||
| 4 — Columns | 5291 | World-space hash grid, full-height. |
|
| 4 — Columns | `FGridColumnMod` | World-space hash grid, full-height. |
|
||||||
| 5+6 — Seal + passages | 5372 | Same seal, passage-tube, landing-room, connector, and support-floor path as TunnelNetwork. |
|
| 5+6 — Seal + passages | `AppendStructuralPost` | Spine, vertical seal, passage tube/landing floor, XY seal — the same structural post as every stack. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -802,16 +800,16 @@ Stage order (negative=solid throughout). Each stage's anchor:
|
|||||||
| Async threading / stale-result handling | `LoadTile` :7291, `ProcessPendingChunks` :5856, generation Epoch + per-request cancellation token. |
|
| Async threading / stale-result handling | `LoadTile` :7291, `ProcessPendingChunks` :5856, generation Epoch + per-request cancellation token. |
|
||||||
| Add a new cave feature / terrain op | Add enum in `VoxelTerrainOpDefinition.h:36`, params there, `ApplyTo` (.cpp:6), transport fields in `FStrateGenerationParams`, consume it in a new Step inside `GetDensityWithParams`. |
|
| Add a new cave feature / terrain op | Add enum in `VoxelTerrainOpDefinition.h:36`, params there, `ApplyTo` (.cpp:6), transport fields in `FStrateGenerationParams`, consume it in a new Step inside `GetDensityWithParams`. |
|
||||||
| Tweak room/tunnel shapes | `VoxelCaveMorphology.cpp` `BuildChunkCache` :6782 / `EvaluateSDFCached` :8569. |
|
| Tweak room/tunnel shapes | `VoxelCaveMorphology.cpp` `BuildChunkCache` :6782 / `EvaluateSDFCached` :8569. |
|
||||||
| Worm tunnel behavior | `GetDensityWithParams` Step 5, VoxelGenerator.cpp:5016. |
|
| Worm tunnel behavior | `GetDensityWithParams` Step 5, VoxelGenerator.cpp:4905. |
|
||||||
| Strate stacking / which strate where | `UVoxelStrateManager::Initialize` :1165. |
|
| Strate stacking / which strate where | `UVoxelStrateManager::Initialize` :1164. |
|
||||||
| Publish/use an offline season | Create `UVoxelSeasonAsset`, set `SourceManifestJson`, press `ImportSeasonManifestJson`, then assign it to `UVoxelSettings::Season`. |
|
| Publish/use an offline season | Create `UVoxelSeasonAsset`, set `SourceManifestJson`, press `ImportSeasonManifestJson`, then assign it to `UVoxelSettings::Season`. |
|
||||||
| Boundary blend between strates | `GetGenerationParams` :6605 + `FStrateGenerationParams::Lerp` (expands `VF_STRATE_PARAM_FIELDS`, StrateTypes.h — new fields go in that list). |
|
| Boundary blend between strates | `GetGenerationParams` :6509 + `FStrateGenerationParams::Lerp` (expands `VF_STRATE_PARAM_FIELDS`, StrateTypes.h — new fields go in that list). |
|
||||||
| Passages / landing geometry between strates | `GeneratePassages` :1735 + `EvaluateModifierSDF` :2719 + `ApplyPassageModifier` / `ApplyPassageLandingFloorMC` (StrateManager.cpp) + `ClassifyTile` floor guard (Generator.cpp). |
|
| Passages / landing geometry between strates | `GeneratePassages` :1659 + `EvaluateModifierSDF` :2643 + `ApplyPassageModifier` / `ApplyPassageLandingFloorMC` (StrateManager.cpp) + `ClassifyTile` floor guard (Generator.cpp). |
|
||||||
| Player carve/fill | `CarveAtPosition`/`FillAtPosition` VoxelWorld.cpp:8413/8422 → `UVoxelDiffLayer::ApplyModification` :185. |
|
| Player carve/fill | `CarveAtPosition`/`FillAtPosition` VoxelWorld.cpp:8413/8422 → `UVoxelDiffLayer::ApplyModification` :185. |
|
||||||
| Mesh smoothness / normals | Grid-gradient in `GenerateMesh` (`GradAt` lambda), `IsoLevel` (h). |
|
| Mesh smoothness / normals | Grid-gradient in `GenerateMesh` (`GradAt` lambda), `IsoLevel` (h). |
|
||||||
| New slab/flat-world generator | `GetSlabDensity` Generator.cpp:5188 + `FSlabGenerationParams` (StrateTypes.h:1223). |
|
| New slab/flat-world generator | `VoxelDensityOps::BuildSlabStack` + `FSlabVoidSource` / `FGridColumnMod` (VoxelDensityOpStack.cpp) + `FSlabGenerationParams` (StrateTypes.h:1223). |
|
||||||
| Biome placement / layout | `BiomeMapParams` on the strate (cell size, warp, climate freqs) + each biome's climate box. Bake `AVoxelWorld::BakeBiomePreview` to tune. §8.14. |
|
| Biome placement / layout | `BiomeMapParams` on the strate (cell size, warp, climate freqs) + each biome's climate box. Bake `AVoxelWorld::BakeBiomePreview` to tune. §8.14. |
|
||||||
| What a biome does to terrain | A full archetype param override on the biome (`bOverrideTerrain` + `SurfaceParams`); surface output-blends dominant/neighbour heights in `GetSurfaceDensity`. Caves = content/atmosphere only (determinism, §8.14). |
|
| What a biome does to terrain | A full archetype param override on the biome (`bOverrideTerrain` + `SurfaceParams`); surface output-blends dominant/neighbour heights in the surface stack (`BuildSurfaceStack` with per-biome params) and in `ComputeSurfaceColumn`. Caves = content/atmosphere only (determinism, §8.14). |
|
||||||
| Add a biome / biome content | New `UVoxelBiomeDefinition` asset → add to the strate's `Biomes[]`. §8.14 / §8.12. |
|
| Add a biome / biome content | New `UVoxelBiomeDefinition` asset → add to the strate's `Biomes[]`. §8.14 / §8.12. |
|
||||||
| Season reset | `AVoxelWorld::ChangeSeed` :8710. |
|
| Season reset | `AVoxelWorld::ChangeSeed` :8710. |
|
||||||
|
|
||||||
|
|||||||
+13
-11
@@ -119,17 +119,19 @@ and changes that would alter generated terrain.
|
|||||||
`sheet_tile` dump field stays for the parity compare.
|
`sheet_tile` dump field stays for the parity compare.
|
||||||
- [x] `UVoxelSettings::CeilingViewMultiplier` / `CeilingBandChunks` / `DensityVolumeMaxTasks` — removed
|
- [x] `UVoxelSettings::CeilingViewMultiplier` / `CeilingBandChunks` / `DensityVolumeMaxTasks` — removed
|
||||||
(never read).
|
(never read).
|
||||||
- [ ] **Operator stack as the only density path.** Strates without `bUseOperatorStack` (and cooked-season
|
- [x] **Operator stack as the only density path** (cleanup/2026-09, owner-approved). Every strate —
|
||||||
entries without a recipe) still run the per-archetype `switch` in `UVoxelGenerator::GetDensityAt`
|
authored, cooked-season (recipe or native) and editor composer candidate — now generates through its
|
||||||
(`GetSlabDensity`, `GetMazeDensity`, `GetVerticalShaftDensity`, `GetFloatingIslandDensity`, the
|
operator stack; a degenerate strate is air. Removed: `UVoxelStrateDefinition::bUseOperatorStack`, the
|
||||||
SurfaceWorld column path, native `GetDensityWithParams`). The tests call those helpers "legacy
|
archetype `switch` in `GetDensityAt`, `GetSlabDensity` / `GetMazeDensity` / `GetSurfaceDensity` /
|
||||||
diagnostic only… not the owner path", and several of them no longer match the stack (e.g.
|
`SurfaceDensityFromColumn` / `GetVerticalShaftDensity` / `GetFloatingIslandDensity`, the four
|
||||||
SurfaceWorld, VerticalShafts, TunnelNetwork telemetry). Retiring the switch means: drop the gate in
|
`Cave Bail Not Op Stack *` stats, the startup opt-in warning, commandlet `-opstack`, and the
|
||||||
`UVoxelStrateManager::UsesOperatorStackForChunk`, remove the switch (fallback: native stack, then
|
legacy-vs-stack comparisons in the tests. **Terrain changes for any strate asset that did not
|
||||||
air for a degenerate strate), delete those helpers and the legacy/stack comparisons in ~15 tests, and
|
already have the opt-in ticked.**
|
||||||
remove `bUseOperatorStack` (+ commandlet `-opstack`). **It changes terrain for any strate not already
|
- [ ] **No-strate-manager fallback world.** A world whose settings have no season, pool or fixed
|
||||||
opted in**, so it was not done in the cleanup. Related live "legacy" paths that would go with it:
|
strates runs `GetDensityAt`'s fallback: default `FStrateGenerationParams` through
|
||||||
the no-strate-manager fallback world (`GetDensityWithParams` with legacy posts).
|
`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
|
- [ ] `UVoxelStrateDefinition::WaterColor`, `Creatures`, `AmbientSound`, `Music`, `MusicVolume` and the
|
||||||
`FStrateCreature` / `FStrateAmbientActor` spawn fields (`SpawnChancePerChunk`, `MaxPerStrate`,
|
`FStrateCreature` / `FStrateAmbientActor` spawn fields (`SpawnChancePerChunk`, `MaxPerStrate`,
|
||||||
`SpawnWeight`) — authored data with no consuming system yet (F9 audio, creatures).
|
`SpawnWeight`) — authored data with no consuming system yet (F9 audio, creatures).
|
||||||
|
|||||||
+3
-2
@@ -32,7 +32,8 @@ So: normals can cost as much as the entire density grid; a SurfaceWorld chunk do
|
|||||||
|
|
||||||
### Tier 1 — high win / low risk / small-medium effort (do these first)
|
### Tier 1 — high win / low risk / small-medium effort (do these first)
|
||||||
|
|
||||||
> **STATUS 2026-07-05: Tier 1 COMPLETE.** T1.a ✅ (surface-column cache, now file-scope `GSurfColCache`);
|
> **STATUS 2026-07-05: Tier 1 COMPLETE.** T1.a ✅ (surface-column cache; since cleanup/2026-09 the density
|
||||||
|
> path's copy is the surface stack's `FSurfaceColumnSource` memo and `GSurfColCache` serves `ClassifyTile` only);
|
||||||
> T1.b ✅ (grid-based normals inline in `GenerateMesh`);
|
> T1.b ✅ (grid-based normals inline in `GenerateMesh`);
|
||||||
> T1.c ✅ (`bShouldCreateCollision` = level 0 only, VoxelWorld.cpp ApplyMeshToTile); T1.d ✅ (v2
|
> T1.c ✅ (`bShouldCreateCollision` = level 0 only, VoxelWorld.cpp ApplyMeshToTile); T1.d ✅ (v2
|
||||||
> `ClassifyTile`, trace-verified −44 % worker CPU); T1.e ✅ (`MaxMeshAppliesPerFrame`, default 4);
|
> `ClassifyTile`, trace-verified −44 % worker CPU); T1.e ✅ (`MaxMeshAppliesPerFrame`, default 4);
|
||||||
@@ -160,7 +161,7 @@ Mobs need two things the world didn't give them: (1) to *exist* away from the lo
|
|||||||
mandatory. AI is server-authoritative (§9.6). ★ Build when NPCs actually land.
|
mandatory. AI is server-authoritative (§9.6). ★ Build when NPCs actually land.
|
||||||
|
|
||||||
**F20 — Biome-selected surface terrain ops (terrace / cliff / layer-lines / overhang / spike / hole). SPEC 2026-07-07.**
|
**F20 — Biome-selected surface terrain ops (terrace / cliff / layer-lines / overhang / spike / hole). SPEC 2026-07-07.**
|
||||||
Terrain ops are cave-only today (per-room in `GetDensityWithParams`; `GetSurfaceDensity` applies NONE — that's
|
Terrain ops were cave-only at the time (per-room in `GetDensityWithParams`; the SurfaceWorld density applied NONE — that's
|
||||||
the "ops don't work on the surface" report). This brings them to the SURFACE, as a **biome** property,
|
the "ops don't work on the surface" report). This brings them to the SURFACE, as a **biome** property,
|
||||||
**conditioned on local terrain** so they read geological instead of random. (Slots in ahead of the later
|
**conditioned on local terrain** so they read geological instead of random. (Slots in ahead of the later
|
||||||
cave-system redo, which will add biome support cave-side reusing this same op→biome model.)
|
cave-system redo, which will add biome support cave-side reusing this same op→biome model.)
|
||||||
|
|||||||
Reference in new issue
Block a user