# Player car dark door surface audit, r318

## Finding

The black regions in the current side captures are not explained by a missing
single exterior paint material: the stock GLBs contain painted door faces for
all three inspected cars. The black pixels resemble the dark `interior_vehicle`
fallback, and the viewer disables back-face culling around the whole player-car
draw. This makes an exposed interior/back face plausible, but the available
static export does not prove that it is the rendered surface responsible for
each black pixel. No material or geometry change is justified yet.

## Evidence in the exported stock meshes

The captures are `/tmp/nfsu2-r317-fleet/MIATA-crop.png`,
`COROLLA-crop.png`, and `PEUGOT-crop.png`. The Miata and Peugeot captures show
a nearly black, door-sized patch; the Corolla door appears painted in this
camera view.

Inspection of `reverse-engineering/garage-cars/{miata,corolla,peugot}-stock.glb`
shows these left-door source slices (triangle counts come from each primitive's
index accessor):

| Car | Authored door slices | Triangles | Source interpretation |
| --- | --- | ---: | --- |
| Miata | `DOOR_LEFT_A` `paint_body` (`0xd6d6080a`) | 37 | Exterior paint role, unresolved runtime paint input |
| Miata | `DOOR_LEFT_A` `interior_vehicle` (`0x12c9453c`) | 39 | Interior material role |
| Miata | `DOOR_PANEL_LEFT` `dullplastic_vehicle` / `interior_vehicle` | 35 / 8 | Separate door-panel geometry |
| Corolla | `DOOR_LEFT_A` `paint_body` (`0xd6d6080a`) | 59 | Exterior paint role, unresolved runtime paint input |
| Corolla | `DOOR_LEFT_A` `interior_vehicle` / `dullplastic_vehicle` | 26 / 13 | Interior and plastic source runs |
| Corolla | `DOOR_LEFT_A` paint overlay `0xffa27b16` | 10 | Authored texture layer, blend flag 2 |
| Peugeot | `DOOR_LEFT_A` `paint_body` (`0xd6d6080a`) | 59 | Exterior paint role, unresolved runtime paint input |
| Peugeot | `DOOR_LEFT_A` `dullplastic_vehicle` (`0x0fedee40`) | 65 | Plastic source run; this door has no interior-hash slice |
| Peugeot | `DOOR_LEFT_A` paint overlay `0x1c8bf43e` | 6 | Authored texture layer, blend flag 2 |

The source geometry for `DOOR_LEFT_A` carries identical position bounds on
each material slice, as expected for material subsets of one authored part;
the face counts differ by slot. `DOOR_PANEL_LEFT` is also a separately named
source part on Miata and Peugeot. Therefore the exporter has not simply omitted
the painted door part. The static fallback GLBs mark the primary body paint as
`paintState: unresolved` and have no recovered base-color image for that role.

## Renderer evidence and limits

`webgl-city/viewer.js` draws the player with `gl.disable(gl.CULL_FACE)` around
`drawVehicle` (around line 4418). `drawPlayerCar` sorts ordinary opaque slices
into the same layer, so their stable source order is preserved; the Miata's
interior run follows its paint run. Both facts can expose/back-render faces that
the retail renderer may cull. The stock-trim audit
[`stock-trim-material-audit-r257.md`](stock-trim-material-audit-r257.md)
independently identifies `0x12c9453c` as `MOLDINGS`/interior trim, with a dark
neutral preview fallback, and says `0x0fedee40` is `DULLPLASTIC`.

The Corolla counterexample matters: its door has the same broad paint plus
interior/plastic roles but looks painted in this capture. Peugeot instead has
plastic but no interior material run on `DOOR_LEFT_A`. This means static slot
names alone cannot identify which primitive produced the visible black patch.
The pictures plus source metadata do not distinguish incorrect face winding,
an uncovered area in the painted source faces, an opaque plastic/interior face
seen through a gap, or a renderer ordering/state issue. No XP gameplay draw
capture is available to compare retail culling, depth, or resolved paint state.

## Follow-up required to identify the exact cause

Use the same player-car pose and compare a diagnostic player draw with
back-face culling enabled against the current uncullled draw, while recording
per-primitive IDs/materials or rendering each door slot as a distinct flat
color. If that isolates the patch to a source face, inspect its indices,
transformed normals, and retail material state before changing exporter or
renderer behavior. Until then, do not assign a fabricated paint texture or
relabel the authored door slots.

## r319 same-pose side and face mapping

Paired side captures narrow the cause. With the same player pose, the
`heading + π/2` side view shows the black panel; the `heading − π/2` view shows
the painted door across its full width. In `playerVehicleMatrix`, local +X is
mapped to the vehicle's `right` vector. The camera azimuth at `heading + π/2`
places the camera on the opposite vector (local −X); the opposite azimuth views
local +X. The selected Miata stock GLB contains
`MIATA_KIT00_DOOR_LEFT_A_0_slice0_slot0` on positive local X and has no
`DOOR_RIGHT_A` slice. The black-side mesh-ID image identifies the broad dark
region as `MIATA_BASE_A_0_slice2_slot0`, while the painted-side ID image
identifies the door slab as `DOOR_LEFT_A`.

Triangle centroids and geometric winding from those GLB primitives support
that side mapping. `BASE_A` interior slice 2 includes outward +X side faces in
the lower/central door band, including centroids `(x=.697, y=.588, z=-.187)`,
`(.692, .363, .275)`, `(.681, .591, .487)`, `(.688, .525, .120)`, and
`(.708, .265, -.062)`. Their normals are approximately +X. The primary
`DOOR_LEFT_A` paint slice also has +X-facing exterior faces over the same
vertical/longitudinal band, but at x≈.81–.83. Thus the paint door is on the
other side of the cabin relative to the black-side camera; the black surface
is not explained by an interior face merely being rendered two-sided. A
`debugVehicleCull` capture retains the black patch, consistent with these
visible base faces having outward +X winding rather than only being
back-facing.

This is evidence of an asymmetric source/export assembly at the visible side:
the GLB has a left door shell on one side, while the other side reveals the
base/interior shell. The exporter classification of `BASE_A`'s
`0x010cb64a` slice as `interior_vehicle` follows its source material hash;
the door paint slice is present and also assigned to the correct `paint_body`
role. Current static evidence does not establish whether the installed retail
car loader mirrors `DOOR_LEFT_A` to synthesize the opposite door or whether a
separate source object was missed before the GLB was made. It also does not
prove a draw-order/depth defect: the near-side base triangles are physically
on the cabin shell, and suppressing their whole source slice removes required
lower-body fill.

The next source check is to enumerate the original MIATA `GEOMETRY.BIN`
object names and per-object transforms before `stock_object_offsets` filters
them, then compare the retail KIT00 assembly/part resolver for left/right
instancing. If it mirrors the left door at runtime, export that same mirror
with reversed winding and source materials; if the right door exists as a
separate source object, fix the exporter selection. Until that is verified,
neither changing the interior material nor deleting the base slice is safe.

## r319 XP source resolution and exporter correction

The installed XP game files resolved that question. The original Miata
`GEOMETRY.BIN` contains a distinct `MIATA_KIT00_DOOR_RIGHT_A` object at
offset `0x2fd88`; the previous GLB did not. `stock_object_offsets()` had grouped
the left and right records as one mesh because their transformed bounds and
vertex counts matched. The right record's stored reflection transform
collapses its already mirrored source vertices onto the left-side bounds.

`tools/export_nfsu2_garage.py` now retains explicitly named LEFT/RIGHT records
as separate stock meshes and detects side-paired source parts whose stored
transform folds opposite raw lateral bounds together. It omits only the
collapsing transform for those records, retaining their authored opposite-side
vertices. All 29 installed playable-car GLBs were regenerated from the XP
installation. Existing wheel anchors and powertrain values in the maintained
manifest were preserved; only triangle totals were refreshed.

The r319 inspection screenshots in the review feed show both Miata sides and
sample side profiles from all 29 regenerated cars. A packaged viewer sweep confirms all 29 models reach their ready state and show upright bodies, four placed wheels and opposite-side panels in the captured side views. The missing Miata
right door is now present and painted material geometry covers the formerly
dark panel. This verifies source assembly and preview geometry for those
views; it does not resolve the neutral gray paint fallback, lighting, runtime
handling, or whether every option mesh and transform exactly matches retail.
