acdream/docs/research/2026-08-21-terrain-fidelity-track-a-report.md

3.9 KiB

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. 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.

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:

  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, not flat per-face shading. The WorldBuilder-derived TerrainUtils.GetNormal identified in the findings §4 is therefore a simplified tool path and not the retail oracle.

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.