# Car material, texture-stage and blend trace, r267

This audit follows the installed PC executable and its embedded compiled car
effect. It identifies fixed-function operations but does not establish the
primary player's live paint selection, active technique, or texture alpha.
No WebGL material values changed. No tests or debugger session were run, and
the server and XP activation state were left alone.

## Corrected queue ownership

The earlier submission note incorrectly attributed the material work to
`0x5bd140`. Queue consumer `0x5cf420` actually does this:

| Queue field | Consumer | Purpose proved by the calls |
| --- | --- | --- |
| `+0x1c` | `0x5bd140`, call at `0x5cf53a` | Light/lighting data; includes SetLight and LightEnable |
| `+0x20` | `0x5bd4a0`, call at `0x5cf54e` | Named material record; SetMaterial/TEXTUREFACTOR in its fixed branch |
| `+0x18` | Active pipeline | Pipeline type, effect, technique and pass selection |

`0x5ceb30` stores the two separate pointers at `0x5cebe6` and `0x5cebed`.
The named material fields discussed below belong to the second pointer.

## Material record to device state

Let `sXX` mean the float at named-state offset `+0xXX`. Define vectors:

```
D0 = s30 * (s34, s38, s3c)
D1 = s40 * (s44, s48, s4c)
S0 = s5c * (s60, s64, s68)
S1 = s6c * (s70, s74, s78)
E0 = 0.5 * s80 * (s84, s88, s8c)
E1 = 0.5 * s90 * (s94, s98, s9c)
```

These products/differences are assembled at `0x5bd4b1–0x5bd606`. The fixed
branch is selected only for pipeline type 5 with pipeline byte `+0x18 == 0`
(`0x5bd6e8–0x5bd705`). Subject to the separate flag/global branch that can
zero diffuse or environment terms, it submits at `0x5bd8fa`:

| D3DMATERIAL9 field | Value |
| --- | --- |
| Diffuse RGB | `D0 + 0.5*(D1-D0)` |
| Diffuse alpha | `1` |
| Ambient RGBA | `(1,1,1,1)` |
| Specular RGB | `S0 + 0.5*(S1-S0)` |
| Specular alpha | `1` |
| Emissive RGBA | `(0,0,0,1)` |
| Power | `4*s58`, with a minimum of `0.1` on the ordinary finite-value path |

These are the actual
[D3DMATERIAL9 channels](https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterial9),
not PBR roughness or Fresnel fields.

The subsequent `SetRenderState(60, factor)` call is at `0x5bd96e`.
The packed alpha input is `255*s50 + 128*(s54-s50)`, followed by the
executable's float-to-integer conversion. The low three bytes come from
`255*(E0 + 0.5*(E1-E0))`, placed component 0 in bits 0–7, component 1
in bits 8–15 and component 2 in bits 16–23. This states byte placement
explicitly rather than assuming the source vector's channel convention.

For WINDSHIELD, helper `0x5bb5b0` overwrites `s50=0.3` and `s54=1`.
The fixed factor-alpha input is therefore approximately `166.1`, normally
converted to byte 166. Material Diffuse alpha is still 1. The raw value 0.3
is conclusively not a direct draw-opacity assignment.

For the programmable branch, `0x5bd989–0x5bda96` sends minimum/range
vectors and powers to effect parameters instead. It does not execute the
fixed branch's SetMaterial/TEXTUREFACTOR calls.

## Embedded car effect and actual fixed stages

The pipeline descriptor table begins at `0x7ff7d0`, stride `0x8c`.
Type 5's resource-name pointer at descriptor `+0x84` names `IDI_CAR_FX`.
The PE resource is 18,032 bytes at RVA `0x4d6c60`. Loader
`0x5d1f35–0x5d1f56` passes the resource name when creating the effect.
The record-by-record decode is saved in
[`car-effect-states-r267.json`](car-effect-states-r267.json), including
executable/resource hashes, original offsets, and dynamic-assignment markers.
Binary layout interpretation follows the
[Wine effect reader](https://github.com/wine-mirror/wine/blob/master/dlls/d3dx9_36/effect.c);
the operation values below are extracted from the installed game's resource.

The resource contains three techniques: `car` (two shader passes),
`car_fixed_envmap` (one pass), and `car_fixed` (one pass). Both fixed passes
disable vertex-color material selection, enable lighting/specular, select the
material as ambient/diffuse/specular source, and disable vertex/pixel shaders.

Both fixed techniques assign stage 0 as follows:

| Output | Operation | Arguments |
| --- | --- | --- |
| Color | MODULATE2X, numeric 5 | TEXTURE and DIFFUSE |
| Alpha | MODULATE2X, numeric 5 | TEXTURE and TFACTOR |

Thus `C0 = saturate(2 * base.rgb * litDiffuse.rgb)` and
`A0 = saturate(2 * base.a * factor.a)`. The factor supplies the constant
selected by [D3DTA_TFACTOR](https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dta).

`car_fixed_envmap` additionally assigns stage 1:

| Output | Operation | Arguments |
| --- | --- | --- |
| Color | MULTIPLYADD, numeric 25 | ARG0=CURRENT, ARG1=TEXTURE, ARG2=TFACTOR |
| Alpha | SELECTARG1, numeric 2 | CURRENT |

Its color contribution is `current.rgb + environment.rgb * factor.rgb`,
followed by the fixed-function range limit; it preserves stage-0 alpha.
Numeric 25 is MULTIPLYADD, not LERP. See the
[Direct3D texture operations](https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dtextureop).
The environment uses generated camera-space reflection coordinates
(`TEXCOORDINDEX=0x30000`). The final fixed lighting/specular calculation must
also retain the enabled specular state; these tables describe the texture
combiner, not a complete replacement shader.

Consequently, if the windshield's bound base texture has alpha 1, its fixed
stage alpha saturates to 1 despite the named field 0.3. Transparency also
depends on the base texture's alpha and the enabled framebuffer blend mode.
Neither is established by the named WINDSHIELD record alone.

The programmable `car` technique uses shader bytecode rather than those
fixed texture stages. Its disassembly and recovered formulas are now documented
in [`car-programmable-shaders-r267.md`](car-programmable-shaders-r267.md), with
raw instruction tokens in the accompanying JSON. The shaders do not read
fixed-function TFACTOR. They combine base and cube textures with per-vertex
harmonic diffuse/specular lighting, named minimum/range uniforms and
view-dependent alpha. The ten float4 HarmonicCoeff values come from the separate
lighting packet at `+0x120`, uploaded at `0x5bd461`; all 40 effect-default floats
are zero. Their runtime values and upstream generation remain missing, so
static material rows still do not justify replacing the WebGL paint or glass.

## Blend selection is a different state source

Before these calls, consumer `0x5cf4c6` invokes `0x5bcc30` for the current
texture/layer descriptor. Descriptor byte `+0x55` controls alpha testing;
byte `+0x56` selects blend enable and source/destination factors. Helpers
`0x5bcb70` and `0x5bcbc0` either set device states directly or populate the
five-word BlendState array at `0x86ead8`. `0x5bcd9d–0x5bcda6` sends that
array to the active effect. The car effect's alpha-test/blend states are
dynamic FXLC assignments referencing BlendState, not constant disabled zeros.

For descriptor byte `+0x56 == 0`, blending is disabled. Values 1–6 enable
it. The switch includes ordinary SRCALPHA/INVSRCALPHA (5/6), SRCALPHA/ONE
(5/2), DESTCOLOR/INVDESTCOLOR (7/8), and DESTCOLOR/ONE (7/2) paths.
The exact live descriptor for a player body or window is still missing;
the named material hash cannot substitute for that descriptor.

## Exact remaining boundary

Technique selection is not a constant assumption. The car resource order is
index 0 `car` (annotation `shader=1`), index 1 `car_fixed_envmap` (no shader
annotation), index 2 `car_fixed` (no shader annotation). Loader `0x5d1e20`
starts at index 0 and tries candidates in order, with a compiled loop bound
of 16 at `0x800bf4`; nonexistent technique handles are skipped.

- At `0x5d1fba–0x5d1fc7`, nonzero global `0x870908` skips candidates whose
  shader annotation equals 1. This gate's live value is not captured.
- At `0x5d2014–0x5d202e`, pipeline type 5 with device-vendor ID `0x102b`
  skips candidate index 0. Vendor/device globals are read from `0x86c548`
  and `0x86c54c`; the other vendor/device checks in this loop apply to
  pipeline types 2/3 or 15, not the car pipeline.
- At `0x5d2158`, each surviving candidate is passed to the effect's
  ValidateTechnique method. A zero HRESULT selects it; a failure advances
  to the next candidate. These device/capability results are runtime inputs.
- At `0x5d217a–0x5d2198`, the selected index, handle and annotation-derived
  shader flag are stored at pipeline `+0x0c`, `+0x10`, and `+0x18`.

Thus the static car path prefers the programmable `car` technique when its
gate/vendor checks permit it and device validation succeeds, then falls back
to `car_fixed_envmap`, then `car_fixed` as validation requires. It does not
unconditionally choose the fixed path. The missing observations are precisely
the gate value, actual device/vendor/capabilities, validation results, and
stored selected index/flag. The draw loop applies the effect/pass at
`0x5cf760–0x5cf790` before drawing. The decoded fixed operations cannot be
applied to a programmable car draw without proving that selection.

The next useful retail observation must correlate a primary-player body and
WINDSHIELD draw with: queue `+0x20` material pointer, pipeline type/technique
and byte `+0x18`, actual texture descriptor `+0x55/+0x56`, bound base and
environment textures with their alpha, and resulting blend/pass state. In
the programmable branch, also capture its minimum/range uniforms and output
alpha inputs. Existing paint capture identifies row replacement; it does not
yet provide this downstream correlation. XP remains activation-blocked, and
this audit did not bypass it. The newly decoded fixed states do not justify
assigning WebGL opacity 0.3 or another guessed reflection weight.
