# Ambient traffic LOD detail-slot mapping · r460

## What the executable proves

The installed `SPEED2.EXE` part builder at `0x00634800` fills the traffic
renderer's 63 records at owner `+0x17a8`. For each category and name bank it
requests a base key with suffix `_B` (`0x00634943..0x00634980`), then adds
`'A' + detailIndex` for detail indices 0 through 3
(`0x006349fd..0x00634a40`). Thus the four requested detail resources are
suffixes `_A`, `_B`, `_C`, and `_D`, in that table order. The resolved handle
is stored at `+0x17a8 + category*0x20 + bank*0x10 + detailIndex*4`
(`0x00634a5a..0x00634a84`). When a higher detail resource is absent, the
builder copies the prior detail handle; if detail A is absent, the slot stays
null (`0x00634a8a..0x00634abd`). The draw pass at `0x006162e0` reads one of
these four slots for each group.

The traffic geometry export preserves the named `BODY_A`, `BODY_B`, and
`BODY_C` meshes for each model. The installed geometry inventory has all three
for 13 of 15 traffic definitions; BUS and FIRETRUCK have A/B. In all 15
definitions, the A mesh has the highest triangle count, B is next, and C is
lowest where present. For example, TAXI has 496/323/222 triangles and
COUPE has 526/344/229. This agrees with A/B/C being a descending detail series,
but the names and triangle counts alone do not prove which body key the
render-owner descriptor uses for each part category.

## What remained unresolved at r460

The renderer's size thresholds (40/20/10) are recovered, but the submitted
level before threshold adjustment, the exact comparison direction, and the
body-category key produced from the traffic descriptor are not yet joined to
the installed `BODY_A/B/C` names. The model loader's two four-handle banks can
also select different resources. Therefore the WebGL viewer remains on its
upright BODY_A preview rather than switching suffixes at guessed distances.
The per-variant signed axes are exported, so a future confirmed mapping can
switch models without reusing the wrong body frame.

## Reproduction evidence

The disassembly was read from the checked-in installed executable
`reverse-engineering/source/pc-install/SPEED2.EXE` with the PE image base
shown by `objdump -h` (`.text` at `0x00401000`). Relevant ranges:

```text
0x00634943..0x00634980  form the `_B` bank key
0x006349fd..0x00634a40  form `_A` through `_D` detail keys
0x00634a5a..0x00634a84  store the resolved detail handle
0x00634a8a..0x00634abd  copy previous detail when a later slot is missing
0x006162e0              draw one selected handle from each four-level group
```

Model counts come from `traffic-ambient-models.json`, derived from each
installed type-2 `GEOMETRY.BIN`. No viewer variant or distance behavior was
changed in this audit.

## Follow-up: selector direction and installed bundle confirmation · r461

The comparison direction is now resolved from the instructions at
`0x00616707..0x00616756`. Starting from the caller's level, the renderer
increments the level while the computed metric is **below** the current
threshold; it stops when the metric reaches that threshold and clamps the
result to 0–3. The threshold table at `0x007a66ec` is `[40, 20, 10, 0]`.
Thus near objects retain the incoming level and more distant objects advance
to later detail slots. The caller at `0x006318b2..0x006318e5` passes globals
`0x008a1c10` and `0x008a1c14` as the starting levels for the two banks. The
setup code can set these to 0 or 1 based on a display configuration value, so
the initial level is not a universal constant.

The exact installed XP traffic bundles were recovered offline from
`/home/jhylands/vms/winxp/winxp.qcow2`. I exposed the image read-only through
`qemu-img convert` and `ntfs-3g` tools; the VM itself was not booted. All 15
type-2 `GEOMETRY.BIN` files are temporarily extracted under
`/tmp/nfsu2-traffic/CARS/`. Their authored body names directly carry the
suffixes `BODY_A`, `BODY_B`, and `BODY_C` (BUS and FIRETRUCK have A/B only);
their exported triangle counts descend in that same order. The loader's
missing `_D` fallback therefore has an available lower-detail source mesh to
reuse for every type-2 traffic model.

This confirms the selector's direction and the installed suffix inventory,
but it does not yet prove how the loader's composed resource key joins a
selected bank/detail slot to an authored body object. The initial level and
the render context's size units also need to be mapped to the viewer's
normalized scene units before a distance switch can claim retail behavior.
Normal WebGL traffic remains on the measured BODY_A until those last joins are
resolved. The screenshot and loader observation for that build are in the
scrollable [r461 review entry](../webgl-city/review/#r461-current-drive-ready).

## Follow-up: direct geometry-hash join and WebGL LOD · r462

The missing suffix-to-object join is now established from the installed XP
geometry bundles. Each `GEOMETRY.BIN` contains a type `0x00134003` table of
resource name hashes. For all fifteen type-2 bundles, those hashes include the
executable's 33-based name hash of every authored `*_BODY_A/B/C` object; the
only exceptions are BUS and FIRETRUCK, which contain A/B. For TAXI, for
example, `TAXI_BODY_A/B/C` hash to `0x3240e7a2`, `0x3240e7a3`, and
`0x3240e7a4`, and all three values occur in its installed geometry table.
The name-hash check passed for every available body variant across the 15
models. Together with the loader's ordered `_A`–`_D` requests, this maps detail
slots 0/1/2 to BODY_A/B/C. Slot 3 reuses C, or B for the two models without C,
because the loader copies the previous handle when a later detail is missing.

The viewer now streams B/C after BODY_A makes traffic available and applies
the recovered 40/20/10 thresholds. It computes a viewer-side bounded-size
proxy from each BODY_A GLB's bounds and the camera-to-car distance; that keeps
the threshold order and bounds-based shape measure, but the retail context's
axis weights and scale units have not been recovered in the WebGL coordinate
frame. The viewer defaults its initial level to 0 (highest detail). Debug mode
reports the selected detail level and metric. Lower-detail meshes use their
own exported axes and wheel anchors.

The direct name-hash evidence resolves the body suffix map. The remaining
fidelity limitation is the metric's exact retail context scaling, so this
selector should be treated as source-ordered LOD with an approximate viewer
distance metric rather than a bit-for-bit retail distance match.

## Follow-up: recover the executable's size equation · r463

The traffic draw wrapper at `0x006162e0` calls the oriented-bounds helper at
`0x0048acb0` with a 3-unit near value and rejects objects whose returned size
is below 4. The helper computes a weighted camera-to-object distance using the
render transform's position and per-axis values, then compares
`(distance - 3)` with `3 * 0.75`. It returns twice the object's render radius
when distance is at most 5.25; otherwise it returns
`floor(radius * 3 / (distance - 3))`. In the usual draw-state branch, the
wrapper additionally scales this integer by the float at `0x007a6700` (`0.7`)
and converts it back to an integer before the `< 4` cull. An alternate branch
skips that scale based on two render-state values. The draw code then feeds
the resulting bounded size into its 40/20/10 detail thresholds.

The viewer now uses the helper's 3-unit piecewise equation and measures from
each vehicle's transform origin. It still substitutes ordinary Euclidean
distance for the helper's weighted transform distance and the BODY_A bounds
sphere for the live render radius at object `+0x0c`; the exact retail instance
radius, axis weights, and active wrapper branch are not exported in the current
traffic manifest. The selected levels and switch distances therefore remain
approximate. The viewer debug state reports its selected detail level and
proxy metric.

The packaged build loaded all 36 A/B/C body assets for the 12 traffic models
enabled by its density quotas, with no failed requests or console errors.
Six visible traffic cars were upright and road-supported in that capture.
Their level selection is useful as a runtime check, but its exact distance
equivalence remains open until the `+0x0c` retail radius and weighted axis
values are recovered. Earlier wording in this note used a 4-unit near value;
the wrapper disassembly passes `3.0` (`0x40400000`) at `0x006162f7`, so the
equation above supersedes that reading.

## Reproduction evidence

```text
objdump -D -Mintel --start-address=0x6162e0 --stop-address=0x616350 SPEED2.EXE
objdump -D -Mintel --start-address=0x48acb0 --stop-address=0x48ad44 SPEED2.EXE
objdump -D -Mintel --start-address=0x616707 --stop-address=0x616756 SPEED2.EXE
```

The first range shows the call and near-size cull, the second contains the
radius-over-distance calculation, and the third applies the recovered
`[40, 20, 10, 0]` detail thresholds.
