# Bayview city viewer

This is a standalone WebGL 2 viewer for the recovered Bayview stream scenes. It
loads the selected region's city chunks from `../dist/reverse-engineering/` and
matching barrier/traffic data from `../reverse-engineering/` at runtime. The
distribution builder rewrites the city-chunk path to the package-local
`../reverse-engineering/` directory. Use the Region selector for L4RA, L4RB,
L4RC, L4RD, L4RF, L4RG, L4RH, or L4RR. These are overlapping route/event scene
sets in one world coordinate system, not neighboring map tiles. The source
game executable is not required. The city view starts in a drivable 240SX using
the same retail KIT00 stock GLB used by its garage entry. This avoids the older
one-off default model's different glass and body materials. The Car menu also contains all
28 other retail player cars, exported in their default KIT00 stock configurations
from the XP installation. The garage export now assigns the recovered
`WINDOWMASK` material hash to the glass preview across the stock fleet while
preserving the separately textured rear-defroster overlays; retail glass
opacity is still unresolved (see `../reverse-engineering/windowmask-fleet-material-r370.md`). The selector unlocks as soon as the city becomes
drivable, while ambient traffic assets load in parallel. The first ambient
vehicle joins the road as soon as its model is ready. Browser checks cycled
all 29 selections; every stock build reached the ready state and moved under
throttle. A corner progress indicator reports texture and traffic uploads.
Traffic-only body shells are kept out of the player car menu.
Wheel locations for these stock builds use the per-car axle and track anchors
recovered from `GLOBALB.BUN`; native tire bottoms align with each model's
source ground bound, and fallback tires align with the target body's ground
bound.
The Impreza WRX and Lancer Evolution VIII source wheel names are truncated by
the retail fixed-width name field (`KIT00_FRONT_WHEE`). The exporter recognizes
those stock meshes and keeps their own rim geometry and textures, mounted at
each car's retail axle anchors from `GLOBALB.BUN`.
The vehicle exporter preserves unit normals at offsets `+0x0c..+0x17` and
BGRA prelight at `+0x18..+0x1b` in the retail 36-byte vertex records, transforms
normals through the source object matrix, and emits glTF `NORMAL` and `COLOR_0`
attributes. The WebGL vehicle
shader uses those smooth normals for body paint and glass highlights; city
materials without source normals continue to use triangle normals. The 3000GT
stock inspection and drive views were checked in Chrome after regenerating all
29 player cars and the traffic previews.
In regions with free-roam
routes, the start selector ranks points near buildings and checks the car-width
pavement footprint every 10 m across a 180 m runway before choosing one. This
filters out dense but unusable alley starts whose road surface ends shortly
ahead. The route waypoint is used as the initial player position. A URL
`startLateral` value from -8 to 8 can be used to inspect nearby positions.
For center-strip waypoints, the viewer evaluates direct-bank phase candidates
2–5 using `max(widthA, widthB) * selectorByte / 128`, matching the waypoint
transform's source scale, then checks which positive candidate lands on the
authored lane surface. The alternate FuturePoint subtype remaps phase 2–5 to
bytes 6–9, but the static route export does not identify which subtype is live;
the preview therefore does not test both banks as if they were interchangeable.
For ordinary two-way streets, the preview also reads the source lane texture
around the route origin and applies the same corridor only when it clears a
painted stripe and stays on authored lane geometry. This is a texture-based
placement aid; it does not establish the correct phase for every traffic route.
The r278 correction for the measured Route 450 double-yellow start is recorded
in [`route450-centerline-offset-r278.md`](../reverse-engineering/route450-centerline-offset-r278.md).
A full-screen loading panel shows the setup stage and a determinate count while
the nearby city textures upload. Roads and the recovered sky/reflection image
are uploaded first. The cover stays up until all nearby textures, traffic and
road links are ready and the drive controls are connected. Textured batches
with a source image still pending remain hidden instead of being drawn as white
fallback slabs. Neighborhood swaps still wait for all replacement textures
before replacing the visible scene.
Exported glTF color PNGs are sRGB-decoded before lighting and the final color is
encoded for display. The former linear path remains available with
`?debugLinearTextures&debugLinearOutput`; the paired path keeps the source lane
markings while avoiding the much darker pavement from sRGB decoding alone. It
has not yet been compared pixel-for-pixel with the retail XP renderer.
The region sweep loaded all eight listed scenes in Vulkan Chrome. L4RA, L4RB,
L4RC, L4RD, L4RF, and L4RG have recovered free-roam routes. L4RH and L4RR have
no recovered free-roam routes. L4RH does contain an authored `TRN_ROADA_CHOP_*`
roadway assembled from stream mesh slices whose material is named `road`; the
collision index now includes those source surfaces and starts the car on a
120-unit road-supported runway. That scene has no ambient route graph, so it
has no traffic. L4RR contains airport road-patch geometry only; the viewer
allows driving on that pavement, but it remains an isolated airport scene
without a city street network or traffic.

For route-less scenes, the start search accepts authored lane, intersection,
and road-strip surfaces plus the identified `TRN_ROADA_CHOP_*` and
`RDP_AIRPORT_ROADPATCH_A` meshes. Chrome found 4,720 supported track-road
triangles in L4RH and 20,000 airport-pavement triangles in L4RR. Both can be
driven on their authored pavement; neither has free-roam traffic.

The L4RA traffic preview now rejects a spawn unless its selected route sample
and the next 30 m at 5 m intervals are supported by the authored pavement
index. A full 5 m scan of all 832 recovered traffic routes found 93.35% of
samples supported; this preview guard avoids starting cars across unsupported
gaps but does not reconstruct retail route selection or live actor behavior.
See the [`r406 pavement support audit`](../reverse-engineering/l4ra-traffic-route-pavement-support-r406.md)
and the [review capture](review/#r406-l4ra-traffic-runway).
The follow-up L4RA route-corridor pass also checks the player-height facade
clearance along supported route samples; its close-clearance candidates and
limits are in the [`r407 audit`](../reverse-engineering/l4ra-building-route-corridor-audit-r407.md).

All 29 selectable cars now use the Ginsu recordings assigned by the game's
stock car sound map (`CarTypeMapping` → `CarDataMapping` → `EngineData`). Where
stock settings enable Dual Ginsu, acceleration and coast use their separately
assigned recordings; other cars reuse their assigned acceleration sweep while
coasting. This fixes prior filename guesses, including the 350Z and RX-8
assignments. The browser decodes the EA-XAS frames and follows RPM with
cycle-aligned loops. Audio starts after the first W/S drive key to satisfy
browser audio policy. The runtime uses AudioWorklet on secure localhost and a
ScriptProcessor fallback for plain-HTTP LAN access.
The r315 launch response allows throttle-driven RPM rise below 8 m/s, then
blends to the stock-ratio wheel RPM. This fixes the previous idle-only launch
sound; its clutch-slip range and 45% launch span remain viewer estimates. The
same-camera measurement and exact 240SX Ginsu source-file check are in
[`engine-audio-launch-response-r315.md`](../reverse-engineering/engine-audio-launch-response-r315.md).
The executable's retail Ginsu parser and audio stream callback now confirm the
container tables and block-decoder layout used by the browser. Playback RPM,
accel/coast blend, and shift behavior still need a free-roam runtime capture;
see the [`r356 executable trace`](../reverse-engineering/ginsu-executable-stream-trace-r356.md).
The audio renderer now uses each Ginsu file's own high-RPM bound instead of
the previous universal 7,600 RPM ceiling. Five assigned recordings contain
samples above that limit; the Celica reached 7,941 RPM in the packaged viewer
with its matched AudioWorklet bank active. See the
[`r320 playback-range audit`](../reverse-engineering/engine-audio-rpm-range-r320.md).
The dashboard now reads each car's stock wheel radii, forward/reverse gearbox
ratios, final drive, and RPM thresholds from its `GLOBALB.BUN` vehicle record.
It computes displayed engine RPM from road speed and the active stock ratio,
and shifts at the first source RPM threshold. This replaces the shared
RPM-per-mph table; automatic shift timing and the force model remain viewer
approximations because their complete retail runtime inputs are not recovered.

Use a single `region=` scene for the source-backed stream choice. The retail
loader trace constructs one `TRACKS/STREAM%s.BUN` path from the active track
identifier and releases its previous stream list; the installed executable
provides no evidence that it composes all eight regional variants at once.
Use one `region=` scene for driving. The default L4RA scene is the current
source-backed choice until the live free-roam track selection is captured.
`?world=combined` remains available only as an overlap diagnostic; its
deduplicated union combines placements from different STREAM variants and is
not a valid whole-city scene. A runtime geometry scan found eight nearby
`XO_SPOTSTADIUM_1A_FUN` groups crossing road triangles in that union. See the
[`r393 region-union audit`](../reverse-engineering/city-region-union-overlap-audit-r393.md),
[`combined-city-drive-mode-r343.md`](../reverse-engineering/combined-city-drive-mode-r343.md),
and the [combined-mode screenshots](review/). See the
[`r354 single-region drive capture`](../reverse-engineering/single-stream-drive-capture-r354.md)
for the packaged Route 736 check.

From the workspace root, start a local static server:

```sh
python3 -m http.server 8095 --bind 0.0.0.0
```

Open <http://192.168.1.128:8095/webgl-city/> from the same network. The browser
needs WebGL 2 and enough memory for the selected scene, decoded textures, and GPU buffers. The placed
L4RA and L4RD scenes are about 193 MB and 182 MB; the other scenes are smaller.
Drag to orbit,
hold Shift while dragging to pan, use the wheel to zoom, and press R to reset.
The exporter reads each solid's source name from the fixed `+0xa4` header field and uses retail `0x34103` scenery transforms for placed solids rather
than treating each model library pivot as a world position. A transposed
rotation was a proven placement bug and has been corrected. The transformed
building bounds match the stored retail bounds in the audited instances, but
that does not establish that the complete scene reads correctly against the
road geometry. Several casino structures project over the selected road; their
retail placement is still unverified. See the
[L4RA placement audit](review/placement-audit-l4ra.md) and compare it against
an in-game reference before considering building placement complete.
The current L4RA compact GLB keeps the first valid resident mesh for each
referenced scenery solid key. A follow-up comparison found that all repeated
resident copies for a key have identical normalized geometry, materials, and
object transforms in L4RA, so selecting a different copy cannot correct the
remaining placement mismatch. The r405 browser sweep found no placed building
triangle crossing road geometry within 12 m of any region's selected start,
but that local collision test does not establish placement along those routes
or across the city. See the [eight-region start audit](review/placement-audit-region-starts-r405.md).
The viewer removes exact duplicate stream primitives, packs only
vertices used by each material slice, and culls back faces with mirrored
instances corrected for winding. L4RA panorama and sky meshes draw in a
separate depth-resolved background pass. All eight selectable region GLBs are
precompacted to remove byte-identical source primitives and repeated instances
at identical transforms; distinct placements remain intact. This reduces the
L4RD scene from 190 MB to 174 MB and removes 30,783 repeated node instances,
while the other region variants receive the same exact-match cleanup. The city
loads authored lamp positions, packed colors, and falloff radii from
`city-l4r*-lights.json` sidecars extracted from each region's `STREAM` bundle.
The runtime defaults to the approximate intensity gain measured at Route 450;
it has no city-light shadow maps or recovered light equation. The
[r306 comparison](../reverse-engineering/city-light-gain-r306.md) records the
same-camera pavement and far-scene measurements. If a sidecar is missing, it
estimates lamp positions from fixture meshes. Two forward 240SX headlight beams are also
approximated. It loads a 240-unit
neighborhood around the car by default, streams as the player moves, and allows
the radius to be selected between 120 and 450 units in the URL.
The workspace URL and packaged viewer use gzip city chunks and browser-side
decompression, with raw chunks retained as a fallback. The loader assembles at
most three chunks concurrently instead of retaining all decompressed pieces in
memory. For L4RA this reduces the initial city geometry transfer from about
169 MiB to 77 MiB. The loading panel reports chunk downloads, city assembly,
and nearby texture progress.
The controls overlay recovered region-matched barrier segments and animate
ambient vehicle body meshes along free-roam route waypoints when that region
has route data. **Show traffic quota areas** draws the region's recovered
type-16 polygons and colors each boundary by its density/quota column. The
readout below the legend maps the pointer to world XY and applies the recovered
even/odd polygon hit test; points outside all exported areas report the game's
column-0 fallback. This makes the quota data inspectable even with the overlay
hidden. The meshes come from the installed type-2 traffic definitions. The
retail renderer's detail slots 0/1/2 map to authored `BODY_A/B/C`; an absent
slot 3 reuses the previous mesh handle. Selection uses the player's recovered
density column and lets a definition appear up to that column's exported
quota; the roster still observes the retail seven-car ceiling. The live
candidate-to-definition counts are unavailable, so this is a per-rendered
roster approximation of the manager assignment. Their retail texture bindings
and tire attachment transforms are unresolved. Runtime body paint is not
stored in these assets, so traffic bodies use the exporter's neutral fallback material. Each
body is scaled to a 4.5-unit
length and transformed from its manifest-recorded longitudinal/vertical/lateral
axes into the city's XY-ground, Z-up frame. The bus source uses a rotated local
basis and has its own axis mapping. Model selection, scale, tire placement, and
route behavior remain preview assumptions. The exporter corrects reflected
source transforms while baking geometry; the viewer maps the recorded signed
longitudinal, vertical, and lateral axes directly onto each directed route.
The tire objects have their own baked frames, so the viewer maps each tire's
thinnest bounds axis onto the body side axis and its radial plane onto body
long/up before placing four wheels. This keeps the tire disks upright but does
not recover their retail mounts or select the retail tire variant. The r87
route capture shows COUPE's A tire disks project above the body roof; this is
still a known assembly failure. The recovered LOD thresholds are 40/20/10,
but the viewer compares them against a bounds-based proxy because retail
detail radius and scale remain unresolved. The paint and glass bindings and
the full fleet's nose direction still need comparison with retail captures. For a
single-model route diagnostic, add `&trafficModel=COUPE`; that query loads
only the named model, while the normal viewer loads the full quota-eligible
roster. The
route preview uses each
waypoint's height value recovered from the retail traffic candidate scorer.
The route connection checkbox draws the selected region's decoded `0x34122`
source/target pairs in magenta. A separate yellow overlay draws each record's
`transition_match_record_index`, produced by the recovered retail walker at
`0x005d8760`. This shows the static endpoint/direction match; it does not mean
the matched record is an unconditional next waypoint for a moving car.
Traffic positions start from retail `RoutesFreeRoam.bin` (`0x34121`) route
samples, not the separate event `Paths*.bin` race-line graph (`0x34148`). The
viewer follows directed decoded routes at preview speeds, so this is still a
route-based AI preview rather than retail traffic driving. At route endpoints,
it uses the matched `0x34122` record returned by the retail static walker,
including nodes with two linked records, then requires a reciprocal endpoint,
samples within 2 m in 3D, and aligned travel tangents. This adds static
continuations without claiming to reproduce live FuturePoint branch selection.
To keep preview traffic on pavement, the viewer searches a local
distance range on each nearby route and accepts only samples supported by an
authored road triangle. It keeps at most one preview car per selected route and
spaces cars apart. This produces several distinct route followers where the
source data supports them,
but does not reproduce retail lane changes, intersection decisions, avoidance,
speed control, or vehicle physics. Exact branch/cursor, FuturePoint weights,
driver turn response, and mover state require a live XP runtime capture; see
`../reverse-engineering/traffic-preview-driving-and-textures.md`. The retail manager's recovered global
ceiling is seven; the slider is a lower configured cap.
Traffic route candidates remain spaced from each other. The player slows when
its movement path overlaps an ambient car. That preview collision now uses
oriented bounds from the exported player and traffic body geometry, rather
than a fixed center-distance circle. It does not reproduce the retail
collision shapes, damage, or rigid-body response.
Nearby vertical XB_ building faces also block the car at body height. The
index follows the streamed scene center, ignores roofs and ground plates, and
uses a 1.9-unit circle around the car center. It does not yet collide with
every street prop, traffic damage, or reproduce the retail vehicle body.
The chase view scores supported route samples by their projected position in
the live camera, favoring the center of the road view and rejecting samples
outside a central screen-space band. If a follower leaves that band, its
kinematic state is cleared before the next route sample is seeded. The tested
L4RA Route 736 start shows one upright TAXI on the marked road while the player
drives; nearby alternatives are either hidden behind a building or lack road
support. This is a visibility and placement heuristic, not the retail
manager's randomized candidate assignment or a full screen-depth occlusion
test. The model inventory,
definition indexes, density quotas, source object names, and derived GLB counts
are listed in `../reverse-engineering/traffic-ambient-models.json`.
The Route 736 screenshot, visibility heuristic, and missing second-route
evidence are in [`traffic-visible-sample-r362.md`](../reverse-engineering/traffic-visible-sample-r362.md).
Traffic body yaw and base position come from decoded route data. Its median
offset uses source route width and direct-bank phase adjustment bytes, then
checks the neighboring lane material. Lane paint comes from source road
textures through their exported UVs; the viewer has no generated stripe or
lane-marking geometry. The retail runtime selector remains under
reconstruction.

Use the **Car** menu to switch among the 29 recovered retail player cars. These
are stock body and part exports. A Bayview body shop lets you preview body
colors and finishes and apply extracted source vinyl masks where a car's
catalog has been exported. Search the vinyl list by its retail resource name;
the current package has complete decoded catalogs for the 240SX, 3000GT, 350Z,
A3, Celica, Civic, Corolla, Eclipse, Escalade, Focus, G35, Golf, GTO, Hummer,
Impreza WRX, IS300, Lancer Evolution VIII, Miata, Mustang GT, Navigator, Peugeot
106, Acura RSX, RX-7, RX-8, Sentra SE-R, Skyline GT-R, Supra, Tiburon, and Audi
TT. All 29 catalogs are decoded, with 18,671 source masks in total. The
preview color and finish are
viewer settings, because the retail saved paint state has not been recovered.
The 29 stock car packs also supply their original `SHADOWIG` alpha masks. The
viewer projects the selected car's mask beneath it; its plane footprint and
opacity remain approximate while the game's shadow runtime is unresolved. See
[`../reverse-engineering/car-shadow-textures.md`](../reverse-engineering/car-shadow-textures.md).
Use **W** or **Up** to accelerate, **S** or **Down** to brake and reverse, **A/D**
or the arrow keys to steer with brief taps at speed, and **Space** for the
handbrake. A standard gamepad uses the left stick to steer, **RT** to accelerate,
**LT** to brake/reverse, and **A** for the handbrake. The stick passes through
the recovered retail controller steering input curve; the deadzone and the
following vehicle response are viewer-side behavior. **R** returns the
car to its starting route point and clears held keyboard input. Turn off
**Drive mode** to orbit the city;
turn it on again to return to the car. L4RH and L4RR have no free-roam traffic
routes, but can now be driven on their recovered track and airport pavement.
They are isolated road-only scenes with no ambient traffic, not connected
Bayview districts. The source
bundles also contain off-map geometry. Panorama shells draw behind city
geometry and are not part of road collision.
The driving view includes a small player-up local map drawn from the selected
region's recovered free-roam route network. It shows authored route coverage
around the car; it does not claim to be the game's full navigation map or
include event-race paths. The nearest recovered body shop is marked with a
`B`, including an edge indicator while it is outside the local map radius.
Click a road trace to set a destination on a nearby pavement-supported route;
right-click to clear it. The destination is a straight-line local waypoint,
not a recovered retail GPS route planner.

The stock front wheels now steer with a geometry-based Ackermann split, using
each car's recovered wheelbase and track anchors. Its steering dimensions use
the same front-left/rear-left wheelbase and front-axle track pairs read by the
retail Ackermann component, including cars with different front and rear
tracks. All four wheels rotate from distance traveled and their exported tire
radius. This adds visible steering and rolling to the stock builds; the
underlying keyboard driving model remains kinematic and is not the retail
tire-force simulation.

Ambient traffic uses recovered BODY_A and both authored TIRE_FRONT_A/B meshes.
The WebGL preview defaults to B; use `?trafficTireVariant=A` to compare A.
Four axle positions now come from the matching `GLOBALB.BUN` traffic definition
and are transformed through the selected body's authored frame. The retail
loader's A/B detail choice, model-specific paint/window bindings, and steering
animation are not yet recovered.
Traffic yaw now interpolates each decoded waypoint's retail route-forward
vector, sign-aligned against the traversed segment. This follows the direction
basis used by the executable and avoids deriving the car's nose direction only
from long, straight lines between sparse route points. At short curves where a
source basis disagrees with the segment, the viewer follows the route segment;
the heading remains an approximation of the retail spline controller.

Driving uses the recovered pavement triangles (`RDP_*` materials) for road
height. The car is free to steer across the pavement; it is not pulled onto
traffic route centerlines. Turning off **Drive mode** opens an orbit view
centered on the starting road area. Movement checks the center and a short support
footprint against the pavement, advances in small steps, and eases to a stop at
road edges. Steering yaw decreases with speed to limit lateral acceleration,
avoiding the previous near-in-place rotation during long key holds. Throttle
acceleration and braking were reduced after a straight throttle run reached
130 mph before the first city bend. Road departure and handling calibration
still need more route checks. The road height lookup
stays close to the current elevation so nearby overpasses do not pull the car
onto the wrong level. The visible city geometry streams around the car in a
default 240-unit radius so driving can continue beyond the starting area without
loading every city texture at once. The forward speed cap is 130 mph. HUD RPM
uses each selected car's extracted stock gear ratios, final drive, and tire
radius; the 240SX reaches about 56.7 mph at 6,000 rpm in second gear, close to
the requested ~60 mph reference. Its gear display uses the extracted five
forward ratios; other cars use their own stock gearbox data.
The keyboard driving update subdivides each rendered frame into equal steps no
longer than 4.4 ms, following the integration interval recovered from the retail
`SPEED2.EXE` physics loop. This reduces frame-rate dependence in steering and
road collision checks; it does not replace the game's tire-force simulation.
The throttle command also follows the recovered ordinary RealDriver slew rates
before entering the still-approximate acceleration model: up to `+1.0` per
second under throttle and `-1.5` per second after release.
The free-roam batch filter also removes race-only `ZPM_*STARTFINGRAPHIC` decals
and track-closure barriers. The start/finish decal was present in the regional
STREAM but its opaque background covered the freeway pavement; the route 1013
capture in the review feed verifies that culling it restores the continuous road.
Neighborhood updates now keep the old fully textured scene visible until all
replacement geometry and newly required textures are ready. This prevents a
gray-texture flash while moving, but does not fill source-scene coverage gaps
such as the ones visible on the L4RA industrial straight.
Panorama and sky dome meshes use a separate
background pass so their large shells render behind the city. The L4RA sky dome
uses its two source textures from `LOC4DYNTEX.BIN`; run
`tools/bind_nfsu2_skydome_textures.py` after exporting the clean L4RA GLB to bind
those image keys to the matching source material slices:

```sh
python3 tools/bind_nfsu2_skydome_textures.py \
  reverse-engineering/city-l4ra-textured-clean.glb \
  /path/to/STREAML4RA.BUN /path/to/LOC4DYNTEX.BIN \
  reverse-engineering/city-l4ra-textured-clean.glb
```

The current L4RA package retains the textured `SKYDOME_*` slices and leaves
the `PAN_*` far shells out of the driving view. The source panorama layers
need their retail compositing state; rendering them as ordinary opaque
materials covered the sky. The latest XP-backed rebuild and Chrome capture are
documented in [`city-l4ra-export-r209.md`](../reverse-engineering/city-l4ra-export-r209.md).
The city exporter includes the shared `InGameCommon.bun` TPK alongside each
regional stream, `GLOBALB.BUN`, and `LOC4DYNTEX.BIN`. This resolves seven exact
material keys that were previously shown with fallback colors. In particular,
the Mega Low `SHOPBEAM` now uses the retail `SFX_LIGHT_BEAMA` texture and blend
metadata, and the light-pole `SFX_LIGHT_BEAMB` cards retain their authored
triangles and alpha. The same-camera packaged A/B and key inventory are in the
[`r339 shared-texture recovery`](../reverse-engineering/city-ingamecommon-texture-recovery-r339.md).

The `RDP_LANE*` and `RDP_INTERSECT*` maps are narrow asphalt textures. P8
palettes are stored as little-endian Direct3D `D3DCOLOR` values, so the TPK
bytes are BGRA and must be converted to RGBA for WebGL. The viewer performs
that red/blue swap only for materials whose recovered format is P8 (`0x08`);
this restores the yellow edge stripe in `RDP_LANEA_YS` while leaving DXT maps
such as the white `RDP_LANEB_WD` stripe unchanged. Parking lines likewise come
from `RDP_PARKING_WL_AA_KT`. No road marks are generated by the viewer. The
WebGL lane, intersection, parking, and grill materials sample their authored
maps without vertex tint, alpha-as-coverage, or an estimated underlay. The
material placement still needs route-level comparison with retail gameplay.
The decompiled `RDP_ROADSTRIP*` materials also carry `usage=2` and
`blend=0`; revision r92 treats their maps as opaque source RGB instead of
blending their alpha over an estimated freeway asphalt tile. This keeps paint
and pavement marks in the authored texture, though the complete road still
needs comparison with a retail gameplay view. The old estimated underlay is
available only with `?roadSourceMode=legacy` for diagnosis.
Cobble road, sidewalk, and stone records carry a separate material path.
For cobble surfaces it keeps source vertex luminance while removing the warm
night tint; this reveals the recovered stone detail, though pavement width,
color balance, and center markings still need retail comparison. Browser
texture uploads disable default alpha premultiplication and color conversion
so decoded values reach the compositor unchanged. The stock 240SX export contains only three
bitmap textures (badges, brake details, and tires); the body paint is a
shader-only material in the recovered asset. Paint and glass now sample the
region's recovered dusk panorama, and nearby authored street fixtures add
diffuse light and reflections. Chrome materials receive a separate sky
reflection response. These effects improve the stock car's night lighting,
but do not reproduce the retail reflection pipeline or transparent windows;
the close side view in capture 132 records the first lighting pass. Wheel
chrome is rendered as an opaque reflective layer so the source's alpha-blended
spokes remain visible over the tire sidewalls; capture 133 records this fix.
Unresolved `paint_*` runs keep a neutral base color while receiving a restrained
generic clearcoat from the recovered dusk panorama and local street lights.
This adds surface highlights without claiming to recover the runtime-selected
paint color or retail finish parameters; see review capture 263.

Revisions r303–r304 split city batches by both material and source mesh role.
Some `RDP_ROADSTRIP_*` textures are reused on ceilings and props, so material
names alone do not identify road surfaces. The role check covers exported
`ROADA_CHOP`, `ROAD_01_CHOP`, drag-road, and airport road-patch meshes, plus
`TERRAINA_CHOP` and terrain naming variants that carry RDP lane, intersection,
parking, or road-strip slices. Revisions r307–r311 add `CITYROAD`, runway,
`TERRAIN03`, `TERRAIN*_NR/NRND_CHOP`, tunnel, parking-lot, road-skid, and drift
marker families, plus airport/drift road-patch maps. Capture 393 records a
packaged L4RA check. The matcher covers 9,822 source road-family material uses
across eight city GLBs; unresolved foundation, property, ceiling, and racing
track uses remain outside those rules. More source markings now render. Revision r313 adds a road-only surface gain
of 1.6, selected from a same-camera L4RB comparison that raised near asphalt
brightness without changing a nearby building crop (capture 394). This is a
viewer exposure correction, not a recovered retail constant; city-wide
placement and the flat player-car appearance remain open.
Other vehicle material combinations still need per-model review. The current arcade collision
checks cover nearby vertical building faces and ambient traffic bodies; street
props, vehicle damage, and retail rigid-body physics are not reconstructed.

Lane, intersection, parking, and non-cobble road-strip materials use the
road compositor with their material-specific RGB/alpha behavior. Lane paint
comes from the sampled texture, not vertex hue or generated lines. Cobble
strips use their decoded RGB and neutralized vertex luminance. Add
`&roadSourceMode=legacy` to compare the older cobble composite. Gravel, gravel
shoulders, grates, and ramps keep their authored materials, and gravel and
shoulders remain outside the player pavement-support index. Capture 114 records
the textured cobble render. The viewer no longer synthesizes center lines or
lane dashes from traffic routes and has no separate road-marking geometry pass.
Startup reveals the drive after the road, sky and all nearby scene textures are
resident and the setup handlers are ready. Textured batches are skipped until
their source image arrives, which avoids white geometry during startup; the
corner banner reports the remaining upload count.
Traffic body axes are derived from the source `BODY_A` object transforms after
those transforms are baked into the GLBs. The manifest stores each model's
actual baked-axis permutation and signs, which the viewer maps onto the route
frame. Traffic waypoint order is aligned with the source route direction
vectors; those vectors point opposite increasing file order in the extracted
route set, and original waypoint IDs are kept for executable junction links.
Traffic now uses both decompiled 3D waypoint vectors: the forward basis from
`+0x18/+0x1a` and the side basis from the signed slope bytes at `+0x20/+0x21`.
The viewer builds each car's up vector from those two axes, so ramp pitch and
banking follow the source route data. Route offsets at lane centers still use
the extracted pavement material geometry while the retail selector weights
remain under reconstruction.
Revision r137 also orthogonalizes the interpolated forward and lateral vectors
before building each traffic transform, preventing corner interpolation from
shearing or rolling the body frame. The fresh route-450 capture is in the
review feed as record 216. Its white fallback paint and crude tire disks show
that the traffic model assembly is still not a faithful retail result.
Revision r138 aligns traffic candidate admission with the existing 8-unit
view corridor; this fixes route 106's prior zero-car start after its 10-unit
spawn lead was rejected by a 12-unit cutoff. Capture 218 records the live
L4RD route check while traffic models were loading.
Ambient models join traffic as they finish uploading alongside the remaining
city textures. Player lane placement and the overall retail layout remain
under review.

Use the **Inspect 240SX stock configuration** link or open
<http://192.168.1.128:8095/webgl-city/?asset=240sx> to inspect the selected retail
parts with the decoded `TEXTURES.BIN` images embedded in the GLB. That view
uses the stock configuration; the all-parts inventory and material mapping are
documented in `../reverse-engineering/traffic-resource-streaming.md`.

## Body shops

The recovered free-roam path data identifies 24 type-4 body-shop areas in the
shared Bayview world coordinates. Drive up to an area and press **E** when the
prompt appears. The prompt includes a short approach range because the current
road-support mesh stops just outside some shop polygons. The nearest-shop
direction stays on screen; **Show body shop locations** outlines the areas. In
the shop, paint color updates the current car immediately. **Save & drive**
stores the color and finish per car in this browser, while **Discard** restores
the entry paint. Finish choices are WebGL preview effects: the selected retail
paint state and exact finish values have not been recovered. The exported cars
contain stock body geometry, so this shop currently changes paint rather than
fitting aftermarket panels.

## r244 browser review

Chrome 150 on the LAN-served build reached the loaded L4RA drive view with
87,253 nearby triangles, 221 textures, 832 free-roam routes, and 12 ambient
body models. Assigning each shader sampler a distinct texture unit before the
first city draw removed the `sampler2D`/`samplerCube` WebGL error seen in the
previous review. The 240SX stock exporter now uses the source part name to keep
the shared red brake-lamp fallback off the headlight meshes; 14 packaged stock
car GLBs with that conflict were updated. Review capture 313 records the
front view and clean browser console. The body paint remains flat and the
render is still not a faithful export of the original game's full presentation.

Revision r249 keeps the recovered 512×128 `LOC4DYNTEX` sky panorama dominant in
vehicle reflections and uses the street-level cube probe as a smaller local
contribution. A stronger gloss clearcoat makes those reflections visible on
both the stock silver and a selected blue paint in review item 314. These
finish weights are viewer presentation values; retail paint-state parameters
remain unrecovered, so the captures are not a fidelity claim.

Revision r250 keeps the local street probe from lowering the panorama coat on
front-facing paint and raises the baseline sky-reflection contribution. This
targets the weak paint response in the driver view; review the actual LAN
capture before treating the change as an appearance improvement.

Revision r251 uses each selected car's matched Ginsu recording RPM maximum as
its tach scale and shift ceiling, replacing the shared 6,800 RPM limit. This is
source-derived audio range; the RPM-per-speed gear table and acceleration model
remain approximations.

Revision r252 gives explicitly named `BRAKELIGHT` and `TAILLIGHT` parts the
existing red lamp fallback before resolving their shared source material hash.
The previous ordering rendered those untextured brake lenses with the blue
window shader in 23 of the 29 stock car exports. All stock exports were
regenerated from the installed geometry and texture packs. The r252 Chrome
capture is review item 320; exact lamp textures are absent from the installed
car packs, so this restores the part category but not the retail lens shading.

Revision r264 lowers the viewer's panorama reflection weight on `glass_vehicle`
materials so the exported blue-gray glass fallback remains visible in the
chase view. The local environment probe still renders reflections. This is a
viewer-side appearance adjustment; the retail WINDSHIELD values 0.3 and 1.0
have unresolved meanings, and the installed car texture catalogs contain no
glass image. Review item 338 shows the same-camera before/after captures after
all 221 nearby textures and the 64 px local reflection probe loaded. The body
paint remains a flat fallback, and retail glass shading is not yet recovered.

Revision r316 gives player `glass_vehicle` surfaces a 0.76 tinted-transparency
preview and draws them after the opaque body and interior. The side view now
shows city light and interior shapes through the windows while preserving the
recovered blue tint and environment reflections. The 0.76 value is a WebGL
visual choice: the XP material trace recovers a view-dependent shader alpha,
but the live source texture alpha and blend descriptor remain unavailable.
See [`glass-transparency-preview-r316.md`](../reverse-engineering/glass-transparency-preview-r316.md)
and review item 397 for the capture and source boundary.

The driving HUD now uses the extracted free-roam custom tachometer artwork and
a live RPM needle. The dial face and NOS tick assets come from
`HUD_CustomTextures_ALL.bin` and `InGameFreeRoam.bun` on the installed XP game
disk. Needle position follows the viewer's existing source-gearbox RPM estimate
with a linear mapping over the recovered dial. The retail needle animation
curve, full FNG placement, and NOS gauge behavior remain unrecovered. The
numeric speed, gear, RPM, and RPM strip stay visible alongside the dial.
