# Track note: world (milestone M1, rock and water) Written by the Coder of the world track, 2026-10-05. Everything below was run in this copy (`D:\HADAL - The Descent\.wt\world\TheDescentUE`). Final chain: `build.ps1` PASS (0 warnings; a `-DisableUnity` build also passes), `import_assets.ps1` PASS, `run_tests.ps1` **42 of 42 tests pass** (the 28 old ones, 11 new world/terrain tests, 3 look tests), scenarios `smoke core_sim kazah_showcase kazah_closeups m1_world_slices m1_terrain_tour m1_carve m1_commands look_calibrate look_deep_lamp look_deep_dark look_near_wall look_throat look_bloom look_dry_mask` all PASS. ## What is built (all in the files this track owns) | Area | Files | |---|---| | World definition, density function | `World/DescentWorldDef.*`, `DescentWorldTypes.h`, `DescentSDF.*`, `DescentShear.*`, `DescentRoutes.*` | | Terrain | `World/SurfaceNets.*`, `TerrainChunk.h`, `DescentTerrainSubsystem.*`, `DescentTerrainActor.*`, `DescentWorldSlice.*` | | Ore | `World/DescentOreSubsystem.*` | | Look | `Render/DescentEnvironment.*`, `DescentLightPool.*`, `DescentLamps.*`; headlamp in `Core/DescentFlyPawn.*` | | Tuning | `Core/Tuning/DescentTune_World.h`, `DescentTune_Terrain.h`, `DescentTune_Look.h` | | Commands | `Debug/DescentCommands_World.cpp` (Goto, Density, Carve, Cleanup, Slice, NearestDeposit, ClearRoute), `DescentCommands_Look.cpp` (Look.Set) | | State sections | `world_def`, `terrain`, `ore` (with the other files), `env` (`DescentEnvironment.cpp`) | | Tests | `Tests/WorldTests.cpp`, `TerrainTests.cpp`, `LookTests.cpp` | | Scenarios | `Debug/Scenarios_M1.cpp`, `Scenarios_Look.cpp`; step helpers in `World/DescentWorldScenarioSteps.h` (possess a fly pawn, `WaitStreamIdle`, a dark-safe shot, a wall for calibration, `MeasureFrames`) | | Assets (re-runnable) | `Tools/ue/_helpers_look.py`, `assets_20_look_materials.py` (MPC_Descent, M_DescentSurface, M_DescentGlass), `assets_40_terrain_material.py` (M_Terrain, MI_Terrain), `assets_41_pp_water.py` (PP_Water), `assets_10_kazah_materials.py` (re-parenting) | How to run: `Tools\build.ps1`, `Tools\import_assets.ps1`, `Tools\run_tests.ps1 [-Filter Descent.World]`, `Tools\run_scenario.ps1 -Name m1_terrain_tour`. `look_sweep` is not a test: it logs the wall luma for a range of exposures (how `ExposureEV` 0.3 was chosen). ## Acceptance table, DESIGN section 10 M1 | Check | Result | Evidence | |---|---|---| | `Descent.World.Determinism` | PASS | `world_def.hash` equal on two builds (`64a8cc5b4634536c`), GenRaw at 2,000 seeded points equals the stored hash `fca131e9fc960d2d` and equals in both builds. The definition builds in 13-22 ms. | | `Descent.World.CarrierFit` | PASS, with two route edits (below) | Per route 1/2/3: clearance of the hull points outside the chokes' 120 m zones is >= 1.0 m before ClearRoute; each choke's zone has negative clearance (blocks the carrier); after `ClearRoute` clearance is >= 1.0 m everywhere. ClearRoute: 834 / 714 / 657 carve calls, 240 / 240 / 173 chunks modified. | | `Descent.World.NoBypass` | PASS | no pore, worm segment, pocket or sponge sample within 30 m + radius of a well or gallery | | `Descent.Terrain.SeamMatch`, `.CarveOre`, `.Cleanup`, `.NoBounds` | PASS | border vertices and normals of 3 neighbour pairs identical; 6 removed vein samples give exactly 1 chunk, and a carve into a vein frees ore (events = mined total); a floating 27-sample block is freed and 0 unconnected remain; a chunk at (100 km, 100 km, -100 km) is queried, filled and carved | | `m1_world_slices` | PASS | 8 shots `Saved\Shots\m1_world_slices_{plan_z-30,plan_z-200,plan_z-700,plan_z-1020,plan_z-1330,section_y-25,section_y-330,section_x760}.png`; `world_def.deposits == 600`. Looked at plan_z-30 (the 1,100 m canyon, a 140 m wide wobbling slot, the route axis in red), plan_z-700 (the Great Void as a ~640 m disc with worm tunnels and ore on its rim) and section_y-330 (Great Void, Undercroft and Seam Gallery as ellipses, wells WC and WD joining them with the collapse shelf and ring drawn as thin rock lines, the Abyss shaft under the goal, the open sea as the black strip on top). The other five slices were written but I did not open them. | | `m1_terrain_tour` | PASS | all 9 places: `pending_jobs == 0` after the wait, `terrain.triangles` 103k - 298k (limit 900k), 241 - 597 chunks meshed (limit 900), every shot `non_black` 0.235 - 0.95 (> 0.2). Shots `m1_terrain_tour_tour_.png` show a lamp-lit disc of rock seen through the sea's fog; the rock is smooth and dark by design (v2 palette). I opened start, h1, undercroft. | | `m1_carve` | PASS | nearest ferrite vein #489; 5 carves of radius 3 removed 54 / 23 / 27 / 28 / 19 samples; `ore.mined_total` 5 -> 12 (the vein's yield, 12); cleanup freed 0 and left 0 unconnected (`terrain.floating_after_cleanup == 0`); shots `vein_before` (faceted orange-gold vein in the rock), `carved_after` (a clean smooth hollow, vein gone), `seam_wire` (wireframe across a chunk border: one continuous mesh, no gaps) | | `look_calibrate` | PASS | centre luma 0.474 / 0.305 / 0.080 at 10 / 20 / 40 m (bands 0.38-0.50 / 0.25-0.40 / 0.06-0.18) at `descent.Look.ExposureEV` 0.3 | | `look_deep_lamp` | PASS | centre luma 0.348 (band 0.06-0.35: at the upper edge; the wall found by a density ray is lit square on) | | `look_deep_dark` | PASS | mean luma 0.000 (< 0.03) | | `look_near_wall` | PASS | centre luma 0.497 at 6 m (<= 0.55) | | `look_throat` | PASS | Z -30, lamp off: mean luma 0.180 (0.05-0.25), sun on | | `look_bloom` (U12) | PASS | sphere luma 0.99; ring 3-6 radii luma 0.00000 with bloom 1 and with bloom 0 (limits 4 % / 1 %). The bloom is there but small: the ring at 1.3-2.5 radii is 0.040 with bloom, 0.031 without. | | `look_dry_mask` (U6, synthetic) | PASS | a stencil-1 box in the sea: its pixels differ by 0.0001 mean luma between water on and off, a self-lit square beside it (stencil 0) goes 0.230 -> 0.491. A dry volume registered through the core seam turns `env.state` to Dry, `volumetric_fog` false, `temperature_c` 26.0, `snow_instances_visible` 0. | | `look_rooms` | not mine (needs the carrier, M2) | | ## Measured numbers (risk R1), 1280x720 offscreen, RX 7800 XT, fixed step 1/30 s * Chunk fill (worker): **1.1 - 1.25 ms average** (design target <= 0.6 ms: missed by 2x; it is 8,000 `Gen` samples with three simplex noises each. 12 workers run in parallel, so streaming keeps up). Mesh (worker): **0.09 - 0.11 ms** (target 0.3). * Boot: world definition 13 ms; initial blocking stream of 120 m radius: 24 ms, 251 chunks, 114k triangles. * Tour (chunks meshed / terrain triangles / **frame ms / game-thread ms / render-thread ms / draw calls**; wall-clock over 90 frames, one pooled `UProceduralMeshComponent` per chunk): start 597 / 266k / 3.2 / 1.8 / 3.4 / 550; wellA 487 / 273k / 2.4 / 1.3 / 2.5 / 63; shelfA 544 / 298k / 2.6 / 1.6 / 2.7 / 57; h1 484 / 219k / 3.3 / 2.1 / 3.1 / 105; void 374 / 185k / 2.7 / 1.7 / 2.8 / 77; hollow 350 / 142k / 3.3 / 1.8 / 3.4 / 249; grotto 241 / 103k / 2.7 / 1.6 / 2.8 / 44; undercroft 491 / 245k / 2.9 / 1.9 / 2.8 / 83; goal 384 / 185k / 2.6 / 2.2 / 2.7 / 74. Peak seen earlier in the build: 887 chunks / 422k triangles at h1 (the cap is 900 / 900k). **No fallback of R1 was needed**: the game thread stays under 2.3 ms with up to ~500 components. * The draw-call figure is the RHI counter of the last frame and moves a lot with what is in view; I did not isolate the cost of the procedural-mesh path further. ## Deviations from the design, and why 1. **Route data (DESIGN 2.10) changed, so that `CarrierFit` can pass.** The hull does not fit the Designer's numbers there. Route 2 starts at (-20, -8, -92) and turns on the canyon centreline, not at (-20, -30, -105): a 122 m hull cannot turn at Y -30 in a 142 m wide canyon, and at Z -105 its keel and stern fins are in the floor. Route 3 carries on from the end of gallery S to (900, -526, -995) inside the Undercroft before turning: the gallery ends under that chamber's roof and a hull cannot turn there. 2. Beacons b7 and b8 are moved from their design positions (which the wall noise puts in rock) toward the nearest chamber's centre until they are in open water (`FDescentWorldDef::Build`). 3. **Mesher winding:** `Tune::Terrain::FlipWinding = true`. With the v2 index order every chunk is culled (screenshot); flipped, the rock is lit from the water side. 4. `Carve(..., bIgnoreHardness = true)` also bypasses the dense-basalt gate (ClaimDist > 40 m is never written), because the design's NoBounds test carves 100 km out. Gameplay carves (hardness on) keep the gate. 5. The terrain registers its sim step in `OnWorldBeginPlay`, not `Initialize`: the core test `Descent.Sim.StepOrder` counts Terrain callbacks in a bare test world and expects exactly its own. 6. GenQuick cache flushes whole at 4,096 entries instead of a true LRU. 7. ClearRoute tests "rock near" as density > -3.5 (the stored field is clamped at -4, so the v2 test `> -6` was always true and carved everywhere). 8. **Volumetric fog / light shafts are in but not verified by eye**: the lamp's beam is not visible as a beam in any shot I looked at (a change of the fog's extinction scale from 4 to 1 changed nothing visible, so I put the design value back). `look_throat` cannot assert "light shafts visible". 9. **Marine snow is not built** (`env.snow_instances_visible` is always 0; `Descent.Look.Set Snow` only stores the switch). The brief's M1 list did not include it. 10. M_Terrain: the hexagonal jointing lines of the dense basalt are not ported (basalt is the darkening and the lower roughness only). M_DescentSurface keeps `EnvReflect`, `PanelScale`, `Hazard` as declared parameters that do nothing yet (the fake environment cube map of 7.3 is not built). The vertex colour needs no sRGB decode in the material (the engine already decodes the 8-bit colour): the first version had one and was far too dark. 11. The look scenarios use the **fly pawn's lamp** (built with the Tender-headlamp numbers of 7.5, `Render/DescentLamps.h` `ConfigureSpot`) rather than the Tender's own headlight, which is `KazahTender.cpp` (not mine; it is candela-based with inverse-square falloff). The Tender track should call `ConfigureSpot` for its lamp. `ExposureEV` 0.3 is the calibration for the 3.2 start intensity. 12. The tour scenario moves each camera along its line of sight (up to 200 m, to 11 m before the first wall; downward if there is none): at several places (the middle of the Great Void) no wall is within the 90 m lamp, so the exact place would be a black picture. 13. No `perf` state section: the numbers come from `PERF` log lines of `m1_terrain_tour` (`MeasureFrames`). 14. `Descent.World.Goto carrier:...` refuses (the carrier is M2). The `Slice` command draws all three routes' waypoint lines and the well axes in red (the design said "route axes"). ## Guessed * Hull sample points for CarrierFit: v2 `HULL_PTS` / `sweepPts` converted to +X bow; "at a choke" = poses within 120 m of it (hull half length + the choke's reach), "elsewhere" = all other poses. * Terrain material numbers beyond what 7.8 gives (sin-hash value noise as macros, ore glow constants); exposure and the lamp intensity are interdependent (both scale the linear image), I changed only the exposure. * Pore sources: 50 % the four big chambers (ellipsoid surface point + 0.1-0.8 radius outward), 50 % deep Tender tube segments; worm start points are on the ellipsoid surface of six chambers, heading inward along the surface normal. * Place coordinates of `Descent.World.Goto` not given in the design (`bore`, `drop`, `shelfC`, `hairpin`, `antiphon`, `gullet`, `abyss`, `honest`, `sieve`, ...) are my choices; every one is checked to be in open water by `Descent.World.Places`. ## Shared-file changes * `TheDescent.Build.cs`: one module, `RHI`, in the "Track: world" block (draw-call counters for the measurements). * `Config/DefaultEngine.ini`: none (the renderer settings of 1.1 were already in place). * `Core/DescentFlyPawn.*` (mine for this round): lamp, `SetHeadlamp`, a Tick that turns the lamp with the view. * Nothing in `Debug/DescentScenario.*`, `STATUS.md`, `BRIEF_UE.md`, `DESIGN_UE.md`. ## For the Administrator * **The environment actor yields to the showcase rig**: while a `ADescentShowcaseRig` with its own fog/exposure is on, `ADescentEnvironment` switches its fog, post-process and sun off (`env.yielding_to_showcase_rig`), so `kazah_showcase`, `kazah_closeups` and `smoke` keep their M0 pictures without anyone editing them. A scenario that wants the real look calls `ADescentShowcaseRig::SetOwnEnvironment(false)`. * Dry interior (U6): `ADescentEnvironment` asks `Descent::IsDry` each late step; PP_Water leaves pixels with custom stencil 1 alone. The carrier must set `SetRenderCustomDepth(true)` + `SetCustomDepthStencilValue(1)` (`Tune::Look::DryStencilValue`) on every interior primitive. * The `look_*` scenarios call `Descent.Look.Set Lamp` and assume a fly pawn; with the real character (M2) the lamp is the character's flashlight. * The editor on the main project holds the old module; the new assets (`Content\Materials\*`) come from `import_assets.ps1`, and `M_Kazah_Opaque` / `M_Kazah_Glass` are deleted by it. ## Not finished / not run, stated plainly * Marine snow, caustics, the fake environment reflection cube, `PP_ScreenFX`, crystals and loose ore chunks (M4), the HUD hooks, `look_rooms` (M2). Volumetric beams unverified (above). * Fill time misses its 0.6 ms target (1.1-1.25 ms). * The `CarrierFit` margins pass but were tuned only by the two route edits above; a Designer review of the route waypoints is advisable. * Pressing Play in an editor window was not run (offscreen `-game` runs only).