# Stock trim material and visual fidelity review, r257

## Confirmed material correction

The stock exporter classified material hash `0x12c9453c` by its containing
part name. This made trim inside a `BRAKELIGHT` assembly red and made most
other trim `paint_body`, which also enabled the player's selected paint and
clearcoat on it. The retail name-hash operation, starting at `0xffffffff`
and applying `h = h * 33 + byte` modulo 2^32, independently reproduces
`MOLDINGS -> 0x12c9453c`. The local OpenUG2 reference also identifies this
material and its trim placement in `src/nfsu2.h:2131,2137-2147,2179-2181`.
That reference is another reconstruction, not a retail render capture.

`tools/export_nfsu2_car.py:part_color` now recognizes this exact hash before
the part-name lamp rule. It reuses the existing dark neutral
`interior_vehicle` fallback `[0.08, 0.09, 0.10, 1]`; the exact retail trim
color and shader parameters remain unresolved. This changes material role,
not a recovered paint choice. The red fallback for the actual lamp runs is
retained.

All 29 stock GLBs were regenerated from the installed-source copies in
`/tmp/nfsu2-player-cars-prelight/<CAR>/{GEOMETRY,TEXTURES}.BIN` and copied to
`dist/reverse-engineering/garage-cars`. The exporter was also copied to
`dist/tools`. The existing wheel-anchor manifest remains in place.

Comparison with a complete pre-change snapshot found:

- 177 corrected material slices across 28 cars; the Golf has no such slice.
- 46 slices previously classified as lamps and 131 previously as paint.
- All 260 other lamp slices retain their previous material records.
- Every GLB's binary chunk, including geometry, normals, colors, UVs, indices,
  and embedded PNGs, is byte-identical to the previous export.
- Mesh order and count are unchanged. Per-primitive material comparison finds
  no changes outside `0x12c9453c`.

The 240SX has 15 affected slices, including two rear lamp trim slices. Its
lamp lens runs remain red. The audit totals are retained in
[`stock-trim-material-audit-r257.json`](stock-trim-material-audit-r257.json).

## Ranked remaining reasons for the visual mismatch

1. **Vehicle material roles remain incomplete.** The fleet still contains
   313 untextured `DULLPLASTIC` (`0x0fedee40`) slices and all 95 `WINDOWMASK`
   (`0x3ed70c43`) slices classified as paint, plus seven `CARSKIN`
   (`0xd6d6080a`) slices classified as lamps by their containing assembly.
   These names also reproduce their stored hashes. Thus neutral plastic and
   window-mask surfaces can receive the same pale reflective response as body
   panels. They need a separate semantic correction; this change only resolves
   MOLDINGS. The current source GLBs preserve the evidence for that next step.

2. **The body and glass shader is a presentation approximation.** At the time
   of this audit, before the default-finish correction below, The stock
   240SX's three embedded images cover badges and small mechanical/interior
   details; its paint-panel runs have no base-color image. The browser starts
   with `#858c94`, then uses a camera-related light, several invented specular
   lobes, and large sky reflection weights in `viewer.js:sceneFS`. Moreover,
   `drawVehicle` selects `playerPaintFinishMode(default gloss) == 1` for
   unresolved custom paint batches, bypassing the documented unresolved
   mode-2 strength of 0.38. Its sky coat then has a 0.72–1.0 weight before
   the local probe contribution. This explains why the neutral fallback can
   become pale peach with strong angular reflections. The current browser
   telemetry confirms that the sky sampler and cube probe are resident; this
   is not evidence of a missing texture upload. The selected retail paint
   state and shader equation are still needed to match the real car.

3. **City lighting and road sheen use guessed equations.** Authored light
   positions, colors and radii are available, and the opaque road path keeps
   original RGB, UVs, and MODULATE2X vertex prelight. However, sceneFS starts
   illumination at 1 and adds its own diffuse/falloff equation; exported
   lights receive a viewer intensity of 0.7. The road sheen is an added
   12-power half-vector highlight, capped at 0.30, rather than a recovered
   road material response. Road textures already use mipmaps and up to 8x
   anisotropy, so adding those settings would not solve the material gap.
   See `world-prelight-audit.md`, `global-moon-light-r168.md`, and the road
   material provenance review.

4. **Atmosphere, distant scenery and light effects are incomplete.** The
   scene shader writes directly to `outColor`; there is no fog or bloom
   stage. Some source panorama shells and unresolved material-only light
   cards are intentionally omitted after their opaque fallbacks produced
   black slabs. The retained dusk sky, sharply bounded city lighting and
   missing glow/backdrop compositing leave the view visually sparse. The
   exact retail compositing and postprocessing parameters are not recovered;
   adding guessed effects would not establish fidelity. See the r209 city
   export and r211 unresolved-slot audits.

5. **Camera and UI are browser approximations.** The camera uses a 6.5-unit
   distance, 0.12-radian elevation, 0.75-unit target height and 0.92-radian
   vertical field of view, with a separate azimuth offset. These settings
   frame a low, large car against a wide road. No matched retail camera
   capture establishes these constants. The HTML speed/audio/shop overlays
   also differ from the original game's HUD. These differences remain even
   after source geometry and textures load correctly.

The available matched-view review compares against OpenUG2 using extracted
retail data, not running NFSU2. The XP VM has not reached free-roam gameplay,
so there is no same-camera retail capture or live paint/light state against
which to claim complete visual fidelity.

## Same-camera Chrome comparison

The A/B uses the same packaged viewer URL, 800x600 viewport, stationary
default 240SX pose, and 120-unit scene radius. The old GLB is supplied only
for the baseline request, preserving the same renderer and camera. Each
capture waits for `City scene ready`, the stream progress indicator to hide,
and the local reflection probe to populate.

- [Before](../webgl-city/review/images/r257-moldings-before.png)
- [After](../webgl-city/review/images/r257-moldings-after.png)

This confirms trim separation and retained red lamps; it does not validate
the remaining paint, glass, road-lighting or atmosphere against the retail
renderer.

## Default-finish follow-up in r257

The player paint fallback bypass described in item 2 is now corrected.
Untouched paint selects the existing restrained mode 2; explicit selected
finishes retain their behavior. See [the follow-up](player-default-paint-r257.md)
and its same-camera before/after captures in the scrollable review feed.
