Dated 'VM4 correction' notes, not silent rewrites: - AR plan: NoOpRenderPackProductionIntegrationTests is a 2x2 recording- device fixture, not a production pin; the real invariance is VM0's. Both '14,928/14,928 zero skips' totals qualified: hermetic lane filter. - Stage-2 connected report: the dense-town CPU figures ran under the automation observer (9.8 MB/frame alloc) and say nothing about the owner's hitch on the product; clean numbers are VM0's. - Track A report + findings doc: TerrainUtils.GetNormal only orients scenery; the render normals were already smooth; A2 replaced central differences with retail's incident-face average (the reviewer's own premise error). - Findings doc: 'retail brightens' is the fallback path only; real hardware runs the single-pass lerp (VM2). LandscapeDetailTextures = 0 answers the landscape open question. VM0 production table added as the baseline. - Completion audit retail-path row and the review's F1/F2/F3/F5 headers annotated with their closures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
81 lines
4.3 KiB
Markdown
81 lines
4.3 KiB
Markdown
# Terrain fidelity Track A report
|
||
|
||
**Date:** 2026-08-21
|
||
**Status:** REPORT ACCEPTED BY OWNER DIRECTION; A1/A2 IMPLEMENTED; A3 REJECTED
|
||
|
||
This report answers Track A from the measured
|
||
[`terrain and atmospheric rendering findings`](2026-08-21-terrain-and-atmospheric-rendering-findings.md).
|
||
It cites that evidence rather than repeating its measurements, and it does not
|
||
reopen the findings' three refuted claims. The project owner subsequently
|
||
authorized implementation. No physics or collision behavior changed.
|
||
|
||
## A1 — #226 detail-texture overlay
|
||
|
||
The complete source/size/tiling, blend, neutral point, distance units, setting
|
||
gate, material-ordering contract, reverted-experiment analysis, and connected
|
||
A/B/A evidence are in the
|
||
[`#226 retail building/EnvCell detail-texturing port note`](2026-08-21-retail-building-detail-texturing-pseudocode.md).
|
||
|
||
The report conclusions are:
|
||
|
||
- The reachable user-visible target is **building shells and interior EnvCell
|
||
geometry**, not outdoor terrain. The Options preference caller and
|
||
`LScape::ChangeRegion` both install category state `(landscape=0,
|
||
building=enabled, environment=enabled, ordinary=0)` through
|
||
`SmartBox::SetDetailTexturing`.
|
||
- The existing **Building Detail Textures** checkbox is the sole setting gate.
|
||
No second option was added. Toggling it now visibly changes the connected
|
||
Facility Hub scene without a restart.
|
||
- The port keeps retail's `DEST_COLOR + ONE_MINUS_SRC_ALPHA` blend verbatim,
|
||
including the measured slight brightening. `dst=ZERO` would be an opt-in
|
||
visual correction, not parity; exposing both meanings behind the one retail
|
||
checkbox would make that preference ambiguous.
|
||
- The reverted experiment targeted landscape, built the wrong texture-array
|
||
shape, used `base * detail * 2`, assumed 128 gray was neutral, and rejected
|
||
the brightness change that the measured retail blend actually produces.
|
||
|
||
## A2 — terrain vertex normals
|
||
|
||
**Verdict: parity gap. Retail smooths shared terrain vertices.**
|
||
|
||
The decisive named-retail function is
|
||
`CLandBlockStruct::calc_lighting` at `0x00531700` in
|
||
[`acclient_2013_pseudo_c.txt`](named-retail/acclient_2013_pseudo_c.txt):
|
||
|
||
1. It zeroes one three-float accumulator for every shared landblock vertex.
|
||
2. From `0x00531774` through `0x005317F6`, it walks every terrain polygon and
|
||
adds that polygon's plane normal (`CPolygon + 0x20..0x28`) to the
|
||
accumulator of each of its three vertex IDs.
|
||
3. From `0x00531817` through `0x00531886`, it normalizes every accumulated
|
||
vector, falling back to `(0, 0, 1)` only for a degenerate sum.
|
||
4. The following sunlight/ambient loop dots those normalized shared-vertex
|
||
normals with `LScape::sunlight` and writes per-vertex lighting.
|
||
|
||
That is incident-face normal averaging. **VM4 correction (2026-08-22):** the
|
||
findings doc's premise that `TerrainUtils.GetNormal` produced a faceted render
|
||
was wrong — that function only orients procedural scenery
|
||
(`SceneryGenerator.cs:166`) and never fed the render normal. The rendered
|
||
terrain mesh already had smooth central-difference normals (Phase 3b,
|
||
`LandblockMesh`). The real change A2 made is central-difference smoothing →
|
||
retail's split-aware incident-face average: a genuine, decomp-verified parity
|
||
port, but a subtler visual change than either document implied (VM0 measured
|
||
11–14 % of pixels at mean |Δ| ≈ 1–1.5 on the same view).
|
||
|
||
The approved port is in `LandblockMesh.BuildRetailVertexNormals`. It uses the
|
||
same split hash and exact emitted triangle topology, accumulates each
|
||
normalized incident face normal at the shared 9 x 9 height-sample vertex, and
|
||
normalizes the sum. Tests independently reconstruct the average from emitted
|
||
positions/indices and prove every position and index is unchanged.
|
||
|
||
This is lighting-only parity: the 81 height samples, 128 triangles, split
|
||
directions, terrain surface, collision triangles, walkability, and physics
|
||
owners are byte-for-byte/topology-equivalent to the prior path.
|
||
|
||
## A3 — subdivision
|
||
|
||
**Agree: the standing “not worth doing” recommendation survives.** The
|
||
findings §4 already establishes that the 9 x 9 samples are height-table
|
||
quantized, so subdivision cannot recover missing terrain detail; changing the
|
||
surface would create physics divergence, while coplanar subdivision would only
|
||
interpolate a surface whose retail-correct shared-vertex smoothing is now
|
||
already present. No subdivision work is scheduled.
|