# VoxelForge — Architecture & Design Deep-Dive > The 2026 redesign in detail: archetypes, the (0,0) spine, disturbances, content/atmosphere, > biomes, and the **performance invariants** (the `§8.10` "don't regress" list). Read this > before touching generation / strates / passages. > Navigation, file index & conventions live in [CODEMAP.md](CODEMAP.md). Section numbers (`§8.x`) > are preserved so existing cross-references keep resolving. ## 8. Archetypes, spine, disturbances, content & carving (2026 redesign) A large A-to-Z expansion. The world is a stack of strates the player descends through; each strate can be a fundamentally different *archetype*, connected at (0,0). ### 8.1 Archetypes (`ECaveGeneratorType`, VoxelStrateTypes.h) Each archetype has its own param `USTRUCT` (on `UVoxelStrateDefinition`, EditCondition-gated by `GeneratorType`) and its own density function in `VoxelGenerator.cpp`, dispatched by the `switch` in `GetDensityAt`. | Archetype | Params struct | Density fn | Idea | |-----------|---------------|------------|------| | TunnelNetwork | `FStrateGenerationParams` | `GetDensityWithParams` | rooms+tunnels (original) | | FlatPlain / CrystalChamber | `FSlabGenerationParams` | `GetSlabDensity` | floor/ceiling void (original) | | Maze | `FMazeGenerationParams` | `GetMazeDensity` | tight corridors on a 3D lattice (per-voxel, no cache; edge = lower node + axis hash) | | 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. | | VerticalShafts | `FVerticalShaftParams` | `GetVerticalShaftDensity` | 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 | | Underwater | `FStrateGenerationParams` + water | (reuses `GetDensityWithParams`) | tunnel rock + high water table | All density fns share the convention: internal **positive=solid**, apply origin spine → boundary seal → inter-strate passages, then `return -Density` (MC: negative=solid). StrateManager provides params per chunk via `GetMaze/Surface/VerticalShaft/FloatingIslandParamsForChunk` (macro `VF_ARCHETYPE_PARAMS_GETTER`) — no cross-boundary blend (Hard transitions between archetypes). On top of the archetype, an optional **biome** layer (§8.14) modulates terrain & content WITHIN a strate via a window-invariant XY field — currently wired into SurfaceWorld. ⚠️ **The `switch` above is no longer the only density path.** 6 of the 8 archetypes (all but TunnelNetwork and Underwater) also exist as **operator stacks**, selected per strate by `bUseOperatorStack` and evaluated instead of the `switch`; each is bit-identical to the function in its row. The design lives in `OPSTACK-PLAN.md` / `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 - `ApplyOriginSpine` (VoxelGenerator.cpp, static helper) carves a guaranteed open vertical column at XY (0,0) in every strate's **interior** (seals untouched). Radius = `UVoxelGenerator::OriginSpineRadius` ← `VoxelSettings::OriginSpineRadius`. Called before every `ApplyBoundarySeal`. - Descent is **player-dug** through the thin seals at (0,0). The single auto-opened connection is the **surface entry shaft** at (0,0) through the top of strate 0 (`GeneratePassages`, `bOpenSurfaceEntry`). - **Hybrid extras:** auto-carved *shortcut* passages per boundary, placed away from (0,0). Now fully **per-strate** — see §8.8 (the upper strate's `PassageConfig` drives count/style/shape). ### 8.3 Disturbance layer (the "wow" post-process) `FStrateDisturbanceParams` (on the definition, all archetypes). `ApplyDisturbances` (VoxelGenerator.cpp static, **MC convention**) runs in `GetDensityAt` after dispatch: chasms (carve air), bridges (solid spans), ridges (solid blades). Stays inside seal bands. Provided per chunk by `StrateManager::GetDisturbanceParamsForChunk`. ### 8.4 Cross-chunk determinism (the seam-prevention invariant) `BuildChunkCache` (VoxelCaveMorphology.cpp) uses **two regions**: a wide *COLLECT* region (`2*MaxTunnelLength + MaxInfluence`) over which connectivity is decided (NN filtered to `<= MaxTunnelLength`, origin cap = deterministic top-N by hash), and a tight *STORE* region (`+MaxInfluence`) kept for per-voxel eval. This makes the room/tunnel graph window-invariant. **If you add a connectivity rule with longer edges, the COLLECT region must still cover the max edge reach, and decisions must not depend on the stored window.** ### 8.5 Content scatter & water — `VoxelContentManager.h/.cpp` (NEW) `UVoxelContentManager` (owned by `AVoxelWorld`, game-thread). TWO INDEPENDENT subsystems: **(A) DECORATIONS — distance-based WORLD GRID (the no-pop system, 2026-06-17).** Decorations are placed on a fixed world XY cell grid (**1 cell = 1 chunk footprint**, `DECO_CELL_VOXELS = CHUNK_SIZE`) and streamed by DISTANCE from the player, **fully decoupled from clipmap tiles / LOD**. **THE MARCH RUNS ASYNC ON WORKER THREADS** (mirrors mesh gen — `GetDensityAt` is thread-safe; the synchronous-on-game-thread first cut was a perf disaster + starved streaming → seams, so it was moved off-thread). Driven by `AVoxelWorld::Tick → UpdateDecorations(playerWorldPos)`, three phases: **(1)** recompute the desired cell set (`RebuildDesiredCells`) only when the player crosses a cell boundary OR changes strate — clears out-of-range loaded cells, queues cells that are NOT loaded and NOT in flight (`PendingLaunch`, nearest-first). A loaded region is NEVER re-streamed in place while it stays in range (only cleared when it leaves), so decorations don't FLICKER as the player moves. **TWO STREAMING GRIDS (`FStrateDecoration::StreamTier`, 2026-06-26):** the stream RADIUS is a property of the GRID, never of an entry — mixing radii inside one grid would force an in-place re-stream when the player crosses an entry's radius (the original tier system's flicker bug). So there are exactly two self-contained grids (`FDecoGrid`, each with its own region map + builds + queues + HISMs): **NearGrid** (`DecorationNearRadiusChunks`, FINE `DecorationSpacingVoxels` column grid — dense groundcover near the player) and **FarGrid** (`DecorationRadiusChunks`, COARSE `DecorationFarSpacingVoxels` column grid — trees/landmarks/RARE props visible everywhere; the coarse grid is what makes a rare prop cheap, since the worker march cost scales with column count). An entry picks a grid via `StreamTier` (default Far); the palette is partitioned by tier so each grid marches only its own subset. A given world XY is covered by a Far region always, plus a Near region when close (separate HISMs → crossing the near boundary never touches the far region). **(2)** `LaunchDecoTasks` (per grid, sharing ONE `MaxConcurrentDecorationTasks` budget — each throttles against the other's in-flight count): snapshot the update's decoration palette (built once on the GAME thread — biome, see below) then fire an async `UE::Tasks` march (`BuildCellSpawns`, `BackgroundNormal`, capped at `MaxConcurrentDecorationTasks` in flight via `InFlightCells`). **(3)** `ProcessDecoResults`: drain finished tasks' results (`Mpsc` queue → `ReadyResults`), epoch-guarded (`DecoEpoch`, bumped on clear/strate-change so stale in-flight results are discarded) + range-checked, and **apply (spawn) budgeted** (`MaxDecorationCellsPerFrame` — the only game-thread cost, SpawnActor/AddInstance). `BuildCellSpawns` (worker) finds each column's surface point(s) and rolls the entries there (shared `PlaceAtCrossing`). Candidate columns are **snapped to INTEGER voxel XY** (integer jitter) so the generator's surface-column cache (T1.a, §8.10) applies — FRACTIONAL XY bypasses it and recomputes the noise-heavy heightfield+biome on every sample. **TWO column strategies by archetype:** **(a) SurfaceWorld → HEIGHT ORACLE (`Ctx.bSurfaceWorld`), NO marching.** `Generator::GetSurfaceHeightAt(x,y, chunkZ → TerrainZ, CeilSurf)` returns the heightfield surface + sky-cap ceiling in O(1) (it shares the density path's `ResolveSurfaceChunkParams`/`ComputeSurfaceColumn` via its own thread_local per-chunk cache, so it's bit-identical to the rendered ground). Per column: query centre + 4 neighbours (gradient → floor/ceiling normals), place a Floor crossing at `TerrainZ` and a Ceiling crossing at `CeilSurf` (if open space below). A single `GetDensityAt` at the surface verifies the column isn't CARVED (passage/spine/diff make it air → skip; the oracle is the raw heightfield and doesn't know carving). ~5 height evals + 1-2 density samples/column vs hundreds marched. **(b) other archetypes (caves/shafts/islands) → ray-march** the strate Z-band (`GetStrateUnrealZRange`, voxel coords) via `GetDensityAt` at a COARSE step (`DecorationMarchStepVoxels`), each air↔solid sign change **bisection-refined** (4 iters → accuracy independent of step). Either way **a prop sits at the SAME world position at every LOD → no pop**. (march) The top cap/seal + the open air are always marched first; the scan only stops after `DecorationColumnDepthVoxels` of CONTIGUOUS solid once it has ENTERED the open space (trims dead bedrock below the ground without ever stopping short of it — a "below the first crossing" cap was wrong: on a surface world the first crossing is the high CEILING, so it stopped mid-air before reaching the ground = no floor props). Outward normal = normalized density gradient (solid→air, matches the mesher), classified Floor/Wall/Ceiling by `normal.Z`. Each crossing rolls every `FStrateDecoration` independently: surface-type, density gate (`DecoHash(cell,column,crossing,entry,seed)`), water-relative, align/yaw/scale, per-cell `MaxPerChunk` + global actor cap → a `FDecoSpawn{EntryIdx, bInstanced, Xf}`. The game thread spawns from the result's `Entries` snapshot. `DecorationMaxCrossingsPerColumn` caps cave columns (surface worlds have 1). **Shutdown:** `NotifyShutdown()` (called from `AVoxelWorld::EndPlay`) flags + spin-waits on the in-flight task count before UObject teardown (tasks read the Generator); `BeginDestroy` is the backstop. **Determinism:** pure hash of (cell, column, crossing, entry, seed) + the density surface snap. **Decorations exist ONLY in the player's current strate** (march is strate-bounded) → a strate change wipes + rebuilds them, and there is **no cross-strate light bleed to cull** (the old `SetActiveStrate` light-culling pass is SUBSUMED — gone). **Render paths:** `ActorClass` → real actors (lights/logic, pricey game-thread spawn); `InstancedMesh` → HISM (no tick/actor/collision, emissive glows far), per-cell-per-entry. **Per-entry HISM tuning for dense groundcover** (`FStrateDecoration`, only the InstancedMesh path): `CullDistance` (cm; 0 = no cull — the lever that makes dense grass affordable: placed thickly, drawn only near → GPU cost bounded by area-within-cull, NOT the stream radius), `bCastShadow` (default true; turn OFF for grass — dense instanced shadows are the dominant foliage cost), `MaxSlopeAngle` (deg from flat = acos(|N.Z|); 90 = no filter, ~35 keeps grass off cliffs — applied in the worker's `PlaceAtCrossing`). **Placement-constraint gates (all in `PlaceAtCrossing`, deterministic, zero-cost at defaults):** `MinSlopeAngle` (lower companion to Max — band a prop onto a tilt range, e.g. 30..70 = slopes only), `bWallExcludeOverhangs` (wall-only-upright: drop normals with N.Z < 0 so downward overhangs don't take wall props). **Shared vocabulary (2026-07-06):** these gates + spawn/transform/render fields now live on `FPlacementProfile` (embedded as `Profile` on `FStrateDecoration`, `FStrateLandmark`, and the coming `FStrateSetPiece`), so all three scatter primitives are authored identically. Rotation unified to `RotationOffset` (fixed) + `RandomRotation` (per-axis hash-random) — decoration's ctor defaults `RandomRotation.Yaw = 360` (full random heading, replacing the old `bRandomYaw`/`MinYaw`/`MaxYaw`; banded yaw = offset + a smaller random range). Distribution is unchanged; the exact per-instance yaw values reshuffle once (different hash mix). **F7 AWARE PLACEMENT (2026-07-06):** `FPlacementProfile::Conditions` = a list of `FTerrainCondition` DERIVED PREDICATES (relief / moisture / biome-border weight, each an inclusive [Min,Max] band, optional invert), AND-ed and evaluated by `Generator::EvaluateTerrainConditions` at the candidate XY — "conditions, not annotations": nothing is stored, the phenomenon is queried from the analytic fields (SampleRelief / SampleMoisture / SampleBiomeAt) on demand, so it's deterministic + worker-safe (the caller hands over the strate's already-resolved `FBiomeContext`, so no re-resolve). Opt-in per entry (empty list = zero cost); wired into both the deco worker (`BuildCellSpawns`) and landmark placement (`SpawnLandmarkInstance`). This is the shared core of the coming quest `FindFeature` locator (same predicate, run as an outward search) and the anchor gate for `FStrateSetPiece`. The BP bridge `AVoxelWorld::GetVoxelSurfaceHeightAt` exposes the trace-free deterministic ground/ceiling height so authored ruin/set-piece Blueprints self-arrange on the real surface before it meshes. Next types (water-edge band, relief-peak local-max, slope) are additive. **F7 COMPANIONS (relational decoration, `FDecoCompanion`):** each `FStrateDecoration` may list `Companions` (satellites: rocks/mushrooms around a tree). When `PlaceAtCrossing` emits a parent spawn, it immediately rolls each companion (probability → count in `[CountMin,CountMax]` → disk offset `RadiusMin/MaxVox`), all hashed off the PARENT's hash `H` → a pure function of the parent, so NO "did a tree spawn here?" search. Each satellite offsets from the parent in the XY plane (voxel space) and then, by default (`bSnapToSurface`), **re-snaps to the real surface at its OWN XY** via `FindLandmarkColumn` (cheap `GetSurfaceHeightAt` oracle on SurfaceWorld, a short ray-march in caves; worker-safe — reads only `Gen`) — this kills floaters on uneven ground; a satellite that finds no surface at its spot is skipped. Turn `bSnapToSurface` off to inherit the parent's exact height+normal (cheapest, flat ground only). Satellites can also be **gated by their own `Profile.Conditions`** evaluated at the satellite XY (e.g. a unique mushroom only at a biome border). They carry their own `Profile` transform/render. `FDecoSpawn` gained `CompanionIdx` (−1 = the entry; else index into `Companions`) so `MergeCellResult`/`ApplyRegion` resolve the satellite's mesh/actor + render tuning from `Companions[ci].Profile` (the region mesh bucket now stores an `FPlacementProfile`, not a whole entry). **Two levels:** a companion may carry `SubCompanions` (`FDecoSubCompanion` — a distinct type, since UHT can't reflect a self-recursive `FDecoCompanion`) that spawn ON each level-1 satellite (moss on a rock); level-2 **inherits the L1 satellite's snapped point** (no re-snap — the cost lever that keeps nesting cheap) and can still gate on its own `Conditions`. `FDecoSpawn::SubIdx` routes L2 back to `Companions[ci].SubCompanions[sj]`. A hard **per-parent budget** (`GMaxCompanionsPerParent`, 256) caps the total L1+L2 count so a misconfiguration can't blow up regardless of authored counts. Bounded (count × parents, ≤ budget), deterministic, streams inside the existing two-grid deco system untouched. Depth beyond 2 (and per-entry references for arbitrary nesting) is the remaining flagged follow-up. **F7 SET-PIECES — FOLDED INTO LANDMARKS (2026-07-06).** Set-pieces (ruins/shrines/monuments) and landmarks (mini-suns) had no real reason to be separate once both shared the spawn core and gained `Conditions`, so `FStrateSetPiece`/`UpdateSetPieces` were MERGED into `FStrateLandmark`/`UpdateLandmarks` — one primitive, one `UVoxelStrateDefinition::Landmarks` list. Each entry has an `AnchorMode`: **HashLattice** (scatter on a coarse lattice, `SpacingChunks`) or **PassageMouth** (enumerate `StrateManager->GetPassages()`, keep endpoints whose Upper/LowerStrateIndex == the current strate — `bAtDescentMouths` = the hole going down, `bAtArrivalMouths` = where you land; endpoints are global VOXEL coords). Feature-conditioning is just `Profile.Conditions` on either mode. **Exclusion (self-awareness, optional):** `ExclusionRadiusChunks` (0 = OFF, the default — mini-suns don't exclude) + `Priority`; a candidate is suppressed if a HIGHER-RANKED one's disk covers it (rank = Priority, then hash — deterministic). `UpdateLandmarks` now gathers all candidates → resolves exclusion (SKIPPED entirely when no entry opts in, so pure scatter keeps its old O(n) cost; HashLattice keeps the original hash salt 0x1A2D5u so mini-sun positions are unchanged) → spawns survivors via `SpawnFromProfile` (+ the orb wrapper). **Exclusion is now POP-FREE:** each entry is gathered in `StreamRadius + MaxExcl` (MaxExcl = the strate's largest `ExclusionRadiusChunks`); the extra "ring" candidates carry `bSpawnable = false` and only SUPPRESS (never spawn), so every conflictor of an in-range candidate is always present regardless of player position → a candidate's fate is a pure function of (seed, layout), no edge-of-radius flicker. **Decoration footprint (`bSuppressDecorationsUnder` + `SuppressRadiusChunks`):** a landmark mesh doesn't change density, so the deco placer can't see it — instead, on spawn it calls `RemoveDecorationsInSphere` to clear grass in its footprint, stores the footprint on `FLandmarkInstance`, and `ApplyRegion` re-clears under any loaded suppressing landmark when a deco region streams in fresh (so temple floors stay clear as you leave/return). **PLAYER DIG → GRASS REMOVAL:** `AVoxelWorld::ApplyModification` (the single funnel for every carve/fill brush) calls `RemoveDecorationsInSphere(Center·VOXEL_SIZE, Radius·VOXEL_SIZE)` after the diff+remesh — instant, flicker-free (only the affected HISM instances go, via `GetInstancesOverlappingSphere`+`RemoveInstances`; nothing is cleared+rebuilt), so grass never floats over a dug hole. This only patches the LIVE instances; the placer's existing density check (`D(VX,VY,hC) <= 0.5f`) already keeps any future natural rebuild correct. (Editing a Static-mobility HISM re-caches its proxy — fine for player-paced digging.) Not handled by immediate removal: new grass on a freshly-exposed ledge / regrowth after fill-back — both self-correct on the next natural re-stream. **Freeze note:** a huge set-piece mesh hitches on register (game-thread proxy/distance-field build — NOT async-fixable; the spawn is game-thread by engine rule; the asset is a hard ref so already resident) → mitigate ASSET-side (Nanite on the mesh, bake distance fields), optionally budget spawns across frames. `ApplyDecoResult` buckets spawns per entry and builds each HISM with ONE batched `AddInstances` (single cluster-tree build, set cull/shadow BEFORE `RegisterComponent`) — the game-thread hitch-killer for dense cells. **Per-entry tier (`StreamTier`, default Far)** picks NearGrid or FarGrid; radius + column spacing are PER-GRID settings, never per-entry (a per-entry radius would re-introduce the in-place re-stream flicker — see the two-grid rationale above). This REPLACES the vestigial `MaxLODLevel`; the dead `DecorationActorRadiusChunks` setting is repurposed as `DecorationNearRadiusChunks`. `CullDistance` still bounds GPU draw on top (orthogonal to which grid streams the entry). **No LOD area-density compensation** (placement is per real surface point, density-stable with distance). **SpawnDensity semantics CHANGED** vs the old vertex scatter: it rolls per column surface-point (not per mesh vertex) → expect a one-time density re-tune. **Settings (`Voxel|Content`):** `DecorationRadiusChunks` (6 — FAR reach in cells), `DecorationNearRadiusChunks` (3 — NEAR reach), `DecorationSpacingVoxels` (4 → 8×8 cols/cell — NEAR/fine grid), `DecorationFarSpacingVoxels` (4 by default → raise to 8–16 for a cheap coarse FAR grid; the rare-prop lever), `DecorationRegionSizeCells` (4 — RxR cells per region/HISM), `DecorationMarchStepVoxels` (2 — coarse, bisection-refined; cave march only), `DecorationMaxCrossingsPerColumn` (4 — cave march only), `DecorationColumnDepthVoxels` (160 — bedrock march cap; cave march only), `MaxDecorationCellsPerFrame` (2 — apply/spawn budget), `MaxConcurrentDecorationTasks` (4 — in-flight task cap; 0 disables decorations). **COST:** surface worlds now use the O(1) oracle (cheap); caves ray-march. The work is OFF the frame (worker threads) — game thread only pays the budgeted spawn. If streaming slows, lower `MaxConcurrentDecorationTasks` / raise `DecorationMarchStepVoxels` / shrink radii/spacing. Default `DecorationRadiusChunks=6` ≈ props ~48 m out — raise for far flora (cost ~r²). **(A2) LANDMARKS — rare large objects on a COARSE HASH LATTICE (`UpdateLandmarks`, 2026-06-26).** The right primitive for sparse, far-visible objects like the underground "mini-suns" — where the per-chunk decoration grid fails: at a 2048-chunk radius that grid enumerates ~13M cells per cell-crossing on the game thread and FREEZES. Landmarks instead live on a per-entry hash lattice (cell = `FStrateLandmark::SpacingChunks` chunks), so a radius-R disk holds only ~(R/Spacing)² candidates (≈16 at R=2048, Spacing=512). Listed strate-wide on `UVoxelStrateDefinition::Landmarks`. Each Tick, for each entry, walk the small lattice box around the player within `StreamRadiusChunks`: `DecoHash(cell,entry,seed)` rolls existence (`SpawnProbability`), a jittered XY (`JitterFraction` — effective min spacing ≈ Spacing·(1−Jitter)), then a SINGLE-column surface find (`FindLandmarkColumn`: SurfaceWorld height oracle, else one density ray-march) snaps to the chosen `SurfacePlacement` (default Ceiling = sky-cap). Gates mirror decorations (biome via `GetDominantBiomeAt`, slope band, water-relative). Foliage-style transform tweaks: `LocationOffset` (world XYZ), `RotationOffset` + per-axis `RandomRotation`, `Min/MaxScale`, `bAlignToSurface`. Spawned as a real actor (`ActorClass`, for a sun's light) or one Static `UStaticMeshComponent` (`InstancedMesh`, `CullDistance`=0 → never cull). All SYNCHRONOUS on the game thread (so few candidates it never hitches); a cell's surface-find runs only the first frame it enters range, then is cached in `LandmarkInstances` (keyed `FIntVector(cellX,cellY,entry)`, null entry = "evaluated, nothing placed" so it isn't retried). Deterministic (hash → no pop, infinite reach). Strate-bounded (wiped on strate change, like decorations). This is the real home for "rare prop at all distances" — the job the decoration FarGrid could approximate at moderate range but not at extreme radius. **(B) WATER — tile-driven, level-0 only (continuous plane, never pops).** `PopulateTileWater(tile)` in `ApplyMeshToTile` (level-0 tiles), `ClearTileWater(tile)` in `UnloadTile`. One scaled engine plane (`/Engine/BasicShapes/Plane`) per water-surface chunk (per-chunk-Z plane logic assumes a single chunk's vertical span — hence level-0 only), keyed `TMap` (reflected UPROPERTY). Water Z: `bHasWater` + `WaterLevelRelative` → `StrateManager::GetWaterLevelWorldZForChunk`. Biome `WaterMaterial` overrides `UVoxelStrateDefinition::WaterMaterial` (level stays strate-global). `ClearAll`/`SetSeed` on `ChangeSeed`/regenerate clears both subsystems (decorations re-stream on the next Tick via the INT_MIN sentinels). **Per-biome content (§8.14):** decorations resolve the dominant biome **PER COLUMN** on the worker (`ResolveBiomeSampleAt` via the strate's `FBiomeContext`, box-cached → one rebuild per chunk footprint). The update builds ONE flat decoration palette (every biome's list concatenated; `CurrentEntryBiome[i]` tags entry `i` with its context-biome index, -1 = strate fallback), and `PlaceAtCrossing` rolls only the entries the column's biome owns. This replaced the old per-CELL `GetDominantBiomeAt` (one biome for a whole 8 m cell → axis-aligned border snapping); borders now follow the warped-Voronoi field at column resolution, no straight lines. Water still uses the chunk-centre `GetDominantBiomeAt` for its material. `Initialize` now also takes `UVoxelSettings*` (for the grid tunables). `ContentMaxLevel` is now legacy/dead for decorations. ### 8.6 Atmosphere — `VoxelAtmosphereManager.h/.cpp` (NEW) `UVoxelAtmosphereManager` (owned by `AVoxelWorld`, gated by `bManageAtmosphere`). `UpdateForPlayer(pos)` each Tick, reacts only on strate change. Drives a managed `UExponentialHeightFogComponent` + movable `USkyLightComponent` from the player's strate (`FogColor/FogDensity/bVolumetricFog/AmbientLightColor/AmbientLightIntensity`), and spawns PERSISTENT ceiling/floor "layer" actors (`Def->CeilingLayerActor`/`FloorLayerActor` + ZOffsets + rotations) that follow the player in XY — the sky-island sea-of-clouds / two-sided fog. `Def->AtmosphereActor` (a full BP with your own fog/sky/postprocess) OVERRIDES the managed fog+sky for that strate. `Reset()` on ChangeSeed/EndPlay. (Skylight ambient underground is weak — captures a dark scene; fog is the strong visual.) **Per-biome atmosphere (§8.14):** `UpdateForPlayer` also resolves the player's dominant biome and, when the biome has `bOverrideAtmosphere`, its fog/sky beats the strate's (reacts on biome change, not just strate change). `ApplyFogSky(Def, Biome)` is the shared path; layer actors + the full `AtmosphereActor` BP stay strate-level. Needs the generator injected (`Initialize(..., Generator)`). ### 8.7 Inter-strate bedrock gap `VoxelSettings::InterStrateGapChunks` (N) inserts N chunks of SOLID bedrock between consecutive strates (`StrateManager::Initialize` leaves the gap in the layout). `IsGapChunk` detects it; `GetDensityAt` renders gap chunks as solid + passages only (no caves/spine/seal) so the player digs (0,0) through the gap to descend. `GetStrateUnrealZRange` gives a strate's cm Z range. ### 8.8 Inter-strate passages — PER-STRATE (`FStratePassageConfig` on the definition) Each strate's `PassageConfig` (VoxelStrateTypes.h) controls its descent tunnels to the layer below: `Connections`, `Style` (`EVoxelPassageStyle`: Straight/Worm/Spiral/Cascading), `MouthRadius`/`MidRadius` (tapered width → `FVoxelPassage::ControlRadii` + `VoxelSDF::TaperedCapsule`), `ReachMin/Max` (depth into each strate), `DistanceMin/Max` (from the (0,0) spine), `Wander`, `Segments`, `VerticalWobble`, Spiral/Cascade params. Built in `StrateManager::GeneratePassages` as control-point chains. **Worm = independent fBM per horizontal axis** (`PassageFBM` static) with a flat-top envelope → organic squirm (NOT a 1D zigzag, NOT a same-freq 2-channel spiral). `EvaluateModifierSDF` (per voxel) first builds a `thread_local` **per-chunk shortlist** of passages whose bounds reach this chunk (rebuilt on chunk change / `PassagesVersion` bump) — chunks with no passage near return `FLT_MAX` immediately — then **bounding-sphere-culls** each shortlisted passage (`FVoxelPassage::BoundCenter/BoundRadiusSq`). Both are perf-critical (§8.10). The (0,0) surface entry is a simple straight tube. Global passage settings were removed from `VoxelSettings`. ### 8.9 Carving — brush shapes + editor controls `FVoxelModification` has `EVoxelBrushShape {Sphere,Box,Capsule}` + `BoxExtent`/`CapsuleEnd`/ `Falloff` + `GetWorldBounds`. `UVoxelDiffLayer::GetDensityOffset` switches per shape; chunk overlap uses the shape AABB. `AVoxelWorld`: `CarveBox/FillBox/CarveCapsule/FillCapsule/ ApplyModification` (BlueprintCallable) + `EditorCarveSphere/EditorFillSphere` (CallInEditor) driven by `EditorBrush*` props. ### 8.10 Performance invariants (DON'T regress) - **Streaming** (`UpdateChunksAroundPosition`): rebuild/cull the desired set ONLY when the player crosses a chunk boundary (`LastUpdateCenter`); use the `DesiredSet` TSet for the cull; idle via `bAllChunksLoaded`. Stationary player ≈ free. (Old per-frame O(loaded×desired) scan = 22ms.) - **LOD** changes HOT-SWAP (`LoadChunk` only, never unload-first) → no holes. LOD reconciliation lives in the PERSISTENT per-frame submit loop (same loop as new-chunk loads), NOT as a one-shot on the boundary-cross frame — a one-shot drops every chunk past the task budget and strands it at a stale LOD. Idle (`bAllChunksLoaded`) only when a full scan finds no loads AND no LOD mismatches outstanding. - **SDF cache** (`GetDensityWithParams`): search-BOX validity, not chunk-key — gradient ±1 sampling must not thrash the (expensive) rebuild. - **Per-chunk param cache** in `GetDensityAt`: GenType + param struct + disturbance cached thread-locally by `(DensityCacheOwnerId, ChunkCoord, LayoutVersion)`; the process-unique owner ID prevents cross-world reuse while adding only one `uint64` compare per voxel. Don't remove the owner or layout key, and don't move the fetch/blend back to per-voxel. - **Biome cache** (`ResolveBiomeSampleAt`/`FChunkBiomeCache`, §8.14): validity is a world-XY BOX + ChunkZ + Seed, NOT a chunk key — same reason as the SDF cache. The cell classification is noise-heavy; a chunk-key would thrash it on gradient-normal / +X/+Y boundary samples. Keep the box halo (≥ CHUNK_SIZE) + cell margin (warp + CellSize) so the 3x3 lookup never misses. - **Passage cull** (§8.8) + **morphology two-region** (§8.4): both are per-voxel-cost critical. - **Per-chunk passage shortlist** (`EvaluateModifierSDF`): runs per voxel and is called from every archetype's `ApplyPassageCarving`. Keeps a `thread_local` shortlist (passage INDICES) of passages whose bounds reach the current chunk, rebuilt only on chunk change or `PassagesVersion` bump (incremented in `GeneratePassages`). Most chunks have NO passage near → instant `FLT_MAX` return instead of walking the whole `Passages` array per voxel. Conservative superset (chunk bounding sphere vs passage bound) ⇒ bit-identical carve. Store indices + version, never pointers (the array is rebuilt on `RebuildStrates`). - **Gen tasks run at `UE::Tasks::ETaskPriority::BackgroundNormal`** (`LoadTile`): worker gen yields to foreground game/render tasks. Without it, raising `MaxConcurrentTasks` past the spare-core count saturates the scheduler and starves the frame (the "concurrency > ~12 = stutter" symptom). Keep gen at background priority so the frame keeps its cores. - **Mesher density grid + margin ring** (`GenerateMesh`): sample each grid point ONCE into a flat `(CHUNK_SIZE/Step + 1 + 2)³` array (the `+2` is a 1-point MARGIN ring, indices −1..GridDim, for T1.b normals). The cell loop reads 8 corners from it; per-cell sampling would call `GetDensityAt` ~8× too often. Geometry is bit-identical (edge positions unchanged). Don't refactor back to per-corner `GetDensity` and don't drop the margin ring (normals + seamless borders need it). The cell loop is **two-pass**: pass 1 reads the 8 corner densities + builds the MC case index and `continue`s on no-surface cells (≈70% of cells); pass 2 computes the 8 positions + grid-gradients ONLY for surface cells. Don't hoist position/gradient back above the case-index test. The `DensityGrid` and vertex-dedup `TMap` are `thread_local` and reused per worker (Reset / keep capacity) — don't make them per-call locals (re-allocates ~170 KB + a hash map every tile). **CAPTURE-DURING-MESHING (sanctioned reuse, doesn't regress the above):** `GenerateMesh`'s optional `OutCaptureGrid` copies the already-filled `DensityGrid` interior (`CHUNK_SIZE³`, quantized via `VF_QuantizeDensity`) out for the density clipmap (mini-sun shadows) — a PURE READ added after the grid loop. It does NOT touch the grid shape, the two-pass loop, the margin ring, or the thread_local reuse. Only level-0 full-res tiles request it (`Step==1<