# Sky Material/D3D State — Retail Decompile Trace

**Date:** 2026-04-23
**Scope:** Q1–Q6 of the material/state hunt. Pins exactly what retail writes
per-mesh when rendering a sky GfxObj, and what stays inherited from scene state.

## TL;DR — the retail sky fragment formula

Retail D3D **fixed-function lighting** is the sky's colour source. Per-sky-mesh,
the retail client writes a `D3DMATERIAL9` with fields populated from the mesh's
`Surface` (the per-Surface luminosity/maxBright/transparency). Sky meshes do
NOT get a special state pass — they ride the normal mesh pipeline.

Per-fragment (fixed-function pseudocode):

```
material.Diffuse   = (0, 0, 0, 1)                    if Surface.Luminous else (from FUN_0059da60)
material.Ambient   = (0, 0, 0, 1)                    if Surface.Luminous else (from FUN_0059da60)
material.Emissive  = (Lum, Lum, Lum, 1)              where Lum = Surface.Luminosity or 0
vertex.diffuse.rgb = <per-vertex lit by AdjustPlanes>
vertex.diffuse.a   = 1 - Surface.Transparency        (for each of 4 corners)

if D3DRS_LIGHTING:
    # D3D fixed-function lighting:
    litColor = material.Emissive
             + material.Ambient  * (D3DRS_AMBIENT + sum_of_light.ambient)
             + material.Diffuse  * sum_of_light.diffuse * dot(N, L)
             + material.Specular * ...
else:
    # Lighting OFF — vertex.diffuse is used directly.
    litColor = vertex.diffuse

fragment.rgb = texture.rgb * litColor.rgb
fragment.a   = texture.a   * litColor.a

if D3DRS_FOGENABLE and z > FOGSTART:
    fragment.rgb = lerp(fragment.rgb, D3DRS_FOGCOLOR,
                        clamp((z - FOGSTART)/(FOGEND - FOGSTART), 0, 1))
```

Key facts:
1. **No sky-specific render-state toggles.** Sky meshes render with whatever
   D3DRS_LIGHTING, D3DRS_FOGENABLE, D3DRS_AMBIENT were last set. The per-mesh
   writer `FUN_0059da60` MAY flip LIGHTING on/off based on a global flag.
2. **Luminous flag (`piVar6[5] < 0`) zeroes Diffuse+Ambient**, effectively
   making the mesh render as `Emissive-only * texture`. Non-luminous uses the
   full lighting equation.
3. **Surface.Luminosity is written to `D3DMATERIAL9.Emissive.rgb`.** Confirmed
   at `chunk_00590000.c:10669-10674`.
4. **Surface.Transparency is written to 4 per-vertex alpha slots** (one per
   corner of a quad Surface), via `FUN_0053a430` at `chunk_00530000.c:7706-7715`.
5. **Fog stays ENABLED during the sky render.** The keyframe fog range
   (MinWorldFog → MaxWorldFog) is likely tuned so sky geometry at its rendered
   distance is not heavily fogged.

## Q1 — Fog state during sky render

**Answer: Fog stays ENABLED.** Retail does not toggle fog around the sky pass.

Evidence: I searched every call to `FUN_005a3f90` (D3DRS_FOGENABLE writer).
All call sites:

```
chunk_00500000.c:6293   FUN_005a3f90(DAT_0081dbf8);           # FUN_005062e0 per-frame master gate
chunk_00500000.c:7270   FUN_005a3f90(DAT_008427a9 != '\0');   # FUN_00507a50 weather-volume pass
chunk_00500000.c:7295   FUN_005a3f90(cVar4 != '\0');          # FUN_00507a50 restore
chunk_005A0000.c:707    FUN_005a3f90(0);                      # device-init default
chunk_005A0000.c:1344   FUN_005a3f90(DAT_008ee545);           # device-reset
```

`FUN_00508010` (sky render) does NOT call `FUN_005a3f90`. The per-frame master
gate at `FUN_005062e0:6291` fires BEFORE the sky render inside the same function:

```c
// chunk_00500000.c:6235-6333  FUN_005062e0
if (*(int *)(param_1 + 0x10) != 0) {
    if (*(int *)(param_1 + 0x20) != 0) {
      FUN_00508010();          // sky render
    }
    ...
    FUN_005a4010(DAT_0081dbf8 == '\0');          // master fog gate, NOT disable
    if (DAT_0081dbf8 != '\0') {
      FUN_005a3f90(DAT_0081dbf8);                 // FOG = ON if master flag set
      ...lerp fog...
      FUN_005a41b0(&fogColor, fogNear, fogFar);   // write FOGCOLOR/START/END
    }
}
```

The fog is master-controlled by `DAT_0081dbf8` (application-level toggle). When
outdoors it is typically ON.

**The sky meshes render THROUGH fog.** If the sky GfxObj's far-placement
distance exceeds FOGEND, the fog color will dominate. This is why retail keys
MinWorldFog/MaxWorldFog per-SkyTimeOfDay — to tune how fog bleeds into the sky.

## Q2 — What FUN_0059da60 writes per-mesh (the real per-Surface state setter)

**FUN_00514b90 is only a transform-enqueue wrapper. The real per-Surface
material/D3D state writer is `FUN_0059da60` at `chunk_00590000.c:10586-10795`**,
called downstream by the scene-graph flush. Critical region:

```c
// chunk_00590000.c:10641-10689
FUN_005a3d80((DAT_008ee070 == 0) + '\x01');       // D3DRS_CULLMODE

if ((DAT_008ee06c == 0) || (*(char *)(DAT_00870340 + 0x7e0) != '\0')) {
    uVar12 = 1;
} else {
    uVar12 = 0;
}
FUN_005a41f0(uVar12);                              // D3DRS_LIGHTING ← uVar12

if ((char)piVar6[5] < '\0') {                      // Surface.Luminous flag
    FUN_005a4310(1);
    if (*(int *)(DAT_00870340 + 0x7e4) == 0) {
      _DAT_008ee03c = DAT_00821e38;                // D3DMATERIAL9.Diffuse.A = 0
      _DAT_008ee044 = 0x3f800000;                  // D3DMATERIAL9.Ambient.A = 1.0f
      _DAT_008ee038 = DAT_00821e38;                // D3DMATERIAL9.Ambient.R = 0
      _DAT_008ee040 = DAT_00821e38;                // D3DMATERIAL9.Ambient.B = 0
      _DAT_008ee02c = DAT_00821e38;                // D3DMATERIAL9.Diffuse.G = 0
      _DAT_008ee028 = DAT_00821e38;                // D3DMATERIAL9.Diffuse.R = 0
      _DAT_008ee030 = DAT_00821e38;                // D3DMATERIAL9.Diffuse.B = 0
      _DAT_008ee034 = 0x3f800000;                  // D3DMATERIAL9.Diffuse.A = 1.0f (overwrite)
      (**(code **)(**(int **)(DAT_00870340 + 0x468) + 0xc4))
                (*(int **)(DAT_00870340 + 0x468), &DAT_008ee028);   // SetMaterial
      FUN_005a3ef0(0);                             // D3DRS_COLORVERTEX = 0  (ignore vertex colour)
      FUN_005a3f40(0);                             // (state 0x93)
    }
}
else if (DAT_00796344 < *(float *)(param_2 + 0x78)) {  // Surface.Luminosity > 0
    iVar8 = *(int *)(DAT_00870340 + 0x7e4);
    if (iVar8 == 0) {
      DAT_008ee058 = *(undefined4 *)(param_2 + 0x78);   // Emissive.R = Luminosity
      DAT_008ee064 = 0x3f800000;                        // Emissive.A = 1.0f
      DAT_008ee05c = DAT_008ee058;                      // Emissive.G = Luminosity
      DAT_008ee060 = DAT_008ee058;                      // Emissive.B = Luminosity
      (**(code **)(**(int **)(DAT_00870340 + 0x468) + 0xc4))
                (*(int **)(DAT_00870340 + 0x468), &DAT_008ee028);   // SetMaterial
    }
}
```

**Material-block global at `DAT_008ee028` — mapped byte-for-byte to D3DMATERIAL9:**

| Offset from 0x008ee028 | Global | D3DMATERIAL9 field |
|---|---|---|
| +0x00 | DAT_008ee028 | Diffuse.R |
| +0x04 | DAT_008ee02c | Diffuse.G |
| +0x08 | DAT_008ee030 | Diffuse.B |
| +0x0c | DAT_008ee034 | Diffuse.A |
| +0x10 | DAT_008ee038 | Ambient.R |
| +0x14 | DAT_008ee03c | Ambient.G |
| +0x18 | DAT_008ee040 | Ambient.B |
| +0x1c | DAT_008ee044 | Ambient.A |
| +0x20..0x2c | DAT_008ee048..054 | Specular.RGBA (not touched in this hunt) |
| +0x30 | DAT_008ee058 | **Emissive.R = Luminosity** |
| +0x34 | DAT_008ee05c | **Emissive.G = Luminosity** |
| +0x38 | DAT_008ee060 | **Emissive.B = Luminosity** |
| +0x3c | DAT_008ee064 | **Emissive.A = 1.0f** |

**Verification of offsets:** luminous path sets +0x0c (Diffuse.A) to 0 via
`_DAT_008ee03c = DAT_00821e38`. Wait — that's at +0x0c from 0x008ee028 = 0x008ee034.
Let me re-read: line 10652 sets `_DAT_008ee03c`; line 10659 sets `_DAT_008ee034`
to 1.0f. The former is 0x14 bytes in (Ambient.G); the latter is 0x0c (Diffuse.A).

Reconciling: the luminous path sets Diffuse R=G=B=0, A=1 (via DAT_008ee02c, 028,
030, 034 all at +0x00..0x0c), Ambient R=G=B=0, A=1 (DAT_008ee038, 03c, 040, 044
at +0x10..0x1c). Then `SetMaterial` pushes the whole block — but crucially
**Emissive at +0x30..0x3c is UNCHANGED from whatever the previous caller left
it at** for luminous meshes. This is a subtle retail bug/feature: if the
preceding draw set Emissive to some value, the next luminous draw inherits it.

For non-luminous with Luminosity > 0 (the "else if" branch, line 10666), only
Emissive is updated — Diffuse/Ambient are left from the prior `FUN_0059d520`
call or from some other writer.

**Referenced writer `FUN_0059d520` at line 10636** is where Diffuse/Ambient
get set for normal rendering (texture-modulated). Not fully traced here — but
confirmed: Diffuse/Ambient are NOT zero for non-luminous meshes.

## Q3 — FUN_00512360/124b0/120c0 + FUN_00518e70/ee0/f50 + FUN_0050f040/0c0/140

These are the **PhysicsPart per-part setters** called by the sky render loop.
Each is a "set or enqueue-animation" pair. Chain:

```
FUN_00508010 (sky object render loop)
  → FUN_00512360(part, Luminosity, 0, 0)        # "set or animate Luminosity"
       ├── [animated] FUN_0051c580(3, ...)       # animation keyframe schedule
       └── [immediate] FUN_00518ee0(Luminosity)
                 → foreach Surface in part: FUN_0050f0c0(Surface, Luminosity)
                     → writes Surface.offset_0xd4 = Luminosity       (PhysicsPart +0xd4)
                     → if active: FUN_0053a460(material_cache, Luminosity)
                         → writes cache +0x3c, +0x40, +0x44 = Luminosity, Luminosity, Luminosity

  → FUN_005124b0(part, MaxBright, 0, 0)         # same pattern for MaxBright → +0xd0 → FUN_0053a490 → cache +0x0c, +0x10, +0x14
  → FUN_005120c0(part, Transparency, 0, 0)      # same pattern for Transparency → +0xcc → FUN_0053a430 → cache +0x18, +0x28, +0x38, +0x48 (alpha for 4 verts, stored as 1-Transparency)
```

File:line evidence:

```c
// chunk_00510000.c:2267-2298  FUN_00512360 (Luminosity set-or-animate)
if (_DAT_007c78bc <= (float)(double)CONCAT44(param_5,param_4)) {
    // animation branch — enqueue keyframe
    iVar3 = FUN_0051c580(3, ...);
    ...
}
else if (*(int *)(param_1 + 0x10) != 0) {
    FUN_00518ee0(param_3);                     // immediate apply
}

// chunk_00510000.c:7901-7915  FUN_00518ee0 (Luminosity broadcast to Surfaces)
void FUN_00518ee0(int param_1, undefined4 param_2) {
  if ((*(int *)(param_1 + 0x54) != 0) && (uVar1 = 0, *(int *)(param_1 + 0x58) != 0)) {
    do {
      if (*(int *)(*(int *)(param_1 + 0x5c) + uVar1 * 4) != 0) {
        FUN_0050f0c0(param_2);                 // per-Surface Luminosity set
      }
      uVar1 = uVar1 + 1;
    } while (uVar1 < *(uint *)(param_1 + 0x58));
  }
}

// chunk_00500000.c:13557-13582  FUN_0050f0c0 (PhysicsPart.Luminosity write)
if (param_2 != *(float *)(param_1 + 0xd4)) {
    *(float *)(param_1 + 0xd4) = param_2;       // PhysicsPart +0xd4 = Luminosity
    ...
    iVar2 = FUN_0050e100();
    if (iVar2 != 0) {
        FUN_0053a460(param_2);                  // material cache broadcast
    }
}

// chunk_00530000.c:7732-7741  FUN_0053a460 (material cache: 3-float slot)
void FUN_0053a460(int param_1, undefined4 param_2) {
  *(undefined4 *)(param_1 + 0x3c) = param_2;
  *(undefined4 *)(param_1 + 0x40) = param_2;
  *(undefined4 *)(param_1 + 0x44) = param_2;
}
```

Same chain for MaxBright (`FUN_005124b0 → FUN_00518f50 → FUN_0050f040 → +0xd0 →
FUN_0053a490`) and Transparency (`FUN_005120c0 → FUN_00518e70 → FUN_0050f140 →
+0xcc → FUN_0053a430`). The Transparency writer applies `alpha = 1 -
Transparency` to FOUR alpha slots at `+0x18, +0x28, +0x38, +0x48` (one per
corner of a quad-Surface's 4 vertices).

**Interpretation:** `FUN_0053a4b0` initializes this cache struct with eight
consecutive `1.0f` values at `param_1[3..10]` (offsets +0x0c..+0x28). This is a
**per-Surface fixed-function render cache** holding material-like data for 4
vertices. The fields:

| Offset | Field | Set by |
|---|---|---|
| +0x0c, +0x10, +0x14 | MaxBright R, G, B (3 floats) | FUN_0053a490 |
| +0x18, +0x28, +0x38, +0x48 | vertex alpha v0, v1, v2, v3 (1-Transparency) | FUN_0053a430 |
| +0x3c, +0x40, +0x44 | Luminosity R, G, B | FUN_0053a460 |

**This is NOT a D3DMATERIAL9.** It's retail's bespoke per-Surface colour cache.
The Surface.Luminosity/MaxBright/Transparency set on PhysicsPart via
`FUN_00512360/124b0/120c0` gets stored in:
1. PhysicsPart struct (+0xcc, 0xd0, 0xd4) — persistent part state.
2. Per-Surface material cache (+0x3c.., +0x0c.., +0x18..) — render-time values.

Then when `FUN_0059da60` builds the actual D3DMATERIAL9 to submit to D3D, it
reads `param_2 + 0x78` = Surface.Luminosity — this is the **Surface-level**
Luminosity (from the dat), NOT the animated PhysicsPart Luminosity. The cache
struct's Luminosity (+0x3c..) is for a different purpose — likely per-vertex
colour modulation when COLORVERTEX is on (see Q5). I did NOT find the exact
consumer of cache +0x3c within the 60-minute budget — it may flow into vertex
colour on the vertex-fill path.

Plainly: **retail sky's per-mesh luminosity overrides are stored in two places
and consumed by two different stages (material push for non-luminous meshes,
per-vertex colour cache for others).**

## Q4 — D3DRS_LIGHTING during sky pass

**D3DRS_LIGHTING is ON for normal meshes (including sky), OFF for the
weather-volume overlay (rain/snow/fog cells).**

Evidence: `FUN_0059da60` at `chunk_00590000.c:10642-10648` sets LIGHTING ON
unless a global override forces it off:

```c
if ((DAT_008ee06c == 0) || (*(char *)(DAT_00870340 + 0x7e0) != '\0')) {
    uVar12 = 1;                                   // ← LIGHTING = ON
} else {
    uVar12 = 0;                                   // ← LIGHTING = OFF
}
FUN_005a41f0(uVar12);                             // D3DRS_LIGHTING ← uVar12
```

`DAT_008ee06c` is the "rendering flag" set in various places — when its value
is 0 (default), LIGHTING = 1. The `DAT_00870340 + 0x7e0` flag is a secondary
override. Practically: lighting is ON for all visible mesh draws.

**Corollary:** Since LIGHTING is ON, the material fields (Diffuse, Ambient,
Emissive) drive the output. With Diffuse=0 and Emissive=Luminosity (the luminous
branch), output = texture × Luminosity. With Diffuse!=0 and Emissive=Luminosity
(non-luminous branch with Surface.Luminosity), output = texture × (Emissive +
Diffuse × dot(N, L) × sunLight + Ambient × AMBIENT).

Device-init default at `chunk_005A0000.c:709` sets `FUN_005a41f0(0)` (LIGHTING
OFF), but this is the startup state; scene render flips it per-mesh.

## Q5 — Sky-pass vs terrain-pass render state diff

Retail does NOT distinguish sky from terrain at the render-state level. Both
go through `FUN_0059da60` (per-mesh state setter). Per-draw state that CAN
differ, all driven by Surface flags or globals:

| D3D state | Who flips it per draw | Varies per-sky-mesh? |
|---|---|---|
| CULLMODE (0x16) | `FUN_005a3d80` at 10641 | No — all meshes same (`DAT_008ee070` global) |
| LIGHTING (0x89) | `FUN_005a41f0` at 10648 | No — driven by `DAT_008ee06c` global |
| COLORVERTEX (0x91) | `FUN_005a3ef0` at 10662 (luminous path only) | **Yes** — luminous sky meshes set COLORVERTEX=0 |
| Material (SetMaterial, not a RS) | `(vtable+0xc4)` at 10660, 10673, 10686 | **Yes** — per-Surface Luminosity/flag |
| FOGENABLE (0x1c) | Only `FUN_005062e0` (per-frame gate) | No — set once per frame |
| AMBIENT (0x8b) | Only init (`FUN_005a3eb0(0)`) | No — always 0 |
| ZFUNC/ZWRITE (0x17/0x0e) | Only `FUN_00507a50` weather volume | No for sky proper |

**Conclusion for Q5: sky and terrain share state.** The ONLY per-draw divergence
for sky is via `Surface.Luminous` flag, which (a) zeroes Diffuse+Ambient,
(b) sets COLORVERTEX=0. Non-luminous sky meshes render identically to terrain
except for the material Emissive field.

**This means:** in retail, a cloud mesh (non-luminous) gets the same lighting
treatment as a grass vertex — `Emissive + Diffuse*dot(N,L)*sunColor +
Ambient*D3DRS_AMBIENT`. Since D3DRS_AMBIENT=0, the Ambient term drops; the
output is `Emissive + Diffuse × dot(N, L) × sunColor` — i.e. per-vertex
directional lighting.

A dome mesh (luminous) with `Surface.Luminosity = X` renders as
`Emissive(X,X,X) * texture` (no diffuse, no ambient) — essentially a fade
between off (X=0) and full-texture (X=1).

## Q6 — Verbatim formula for C# port

The retail sky fragment equation, per GfxObj Surface:

```
# Stage 1: Material + vertex-colour build
if Surface.Luminous:
    material.Diffuse  = (0, 0, 0, 1)
    material.Ambient  = (0, 0, 0, 1)
    material.Emissive = (Lum, Lum, Lum, 1)         # Lum = Surface.Luminosity
    vertexColour = white                           # COLORVERTEX = 0
else:
    material.Diffuse  = surfaceBaseDiffuse         # from Surface texture modulate
    material.Ambient  = surfaceBaseAmbient         # likely (1,1,1,1) default
    material.Emissive = (Lum, Lum, Lum, 1)         # Lum = Surface.Luminosity (≥ 0)
    vertexColour = <per-vertex AdjustPlanes output>   # pre-lit per-vertex

# Stage 2: D3D fixed-function lighting (LIGHTING = ON; AMBIENT = 0)
litColour = material.Emissive
          + material.Diffuse  * D3DLight.Diffuse * dot(N, -sunDir)    # sunDir from FUN_00501600
          + material.Ambient  * 0                                     # AMBIENT=0, drops out
          # Specular ignored (0)

# Stage 3: Texture modulate + vertex colour
fragment.rgb = texture.rgb * litColour.rgb * vertexColour.rgb
fragment.a   = texture.a   * litColour.a   * vertexColour.a

# Stage 4: Fog blend (FOGENABLE = ON per master)
if z > FOGSTART:
    t = clamp((z - FOGSTART) / (FOGEND - FOGSTART), 0, 1)
    fragment.rgb = lerp(fragment.rgb, FOGCOLOR, t)
```

**For the acdream sky shader, this reduces to:**

```glsl
// For LUMINOUS sky sub-meshes (dome, sun, moon, stars if Luminous=true):
fragment = texture(uSky, uv) * vec4(uLuminosity, uLuminosity, uLuminosity, 1.0) * uTransparency;
// where uLuminosity = Surface.Luminosity (0..1 fraction)
// and uTransparency is the keyframe-override-animated 1-Transparency.
// NO ambient multiplication. NO sun-direction. No fog.

// For NON-LUMINOUS sky sub-meshes (typical clouds):
vec3 diffuseTerm = diffuseColour * sunColour * max(0, dot(N, -sunDir));
vec3 emissiveTerm = vec3(uLuminosity);                // usually 0 for clouds
vec3 lit = emissiveTerm + diffuseTerm;                // D3DRS_AMBIENT=0 drops that term
fragment.rgb = texture(uSky, uv).rgb * lit;
fragment.a   = texture(uSky, uv).a * (1 - transparency);
// Optional fog: retail leaves fog ENABLED, but sky distance vs FOGEND
// determines whether fog contribution is visible. For acdream, first port
// assume sky is rendered NEAR clip so fog doesn't dominate.
```

**Immediate actionable change for acdream:**

1. Our current `fragment = texture × uLuminosity × uTint` (uTint=white) matches
   retail for **luminous** sub-meshes. Correct behaviour — the over-bright
   observation is NOT from tinting.
2. **The over-bright problem is almost certainly that our Luminosity values are
   wrong.** Previous fix scaled dat values / 100 (percent→fraction). Retail does
   `Surface.Luminosity × _DAT_007a1870`. If `_DAT_007a1870 = 1.0f` (strong
   evidence: it's used as the "default/identity" return in FUN_00518c00/c20),
   AND the dat values are in [0..1], retail renders `texture × dat_luminosity`
   with NO /100 scaling. Our /100 would then be UNDER-bright. But user says
   we're OVER-bright — so the dat values ARE in percent, 0..100, and our /100
   scaling is correct.
3. **However, we may be applying Luminosity twice, or not applying it to the
   right meshes.** Dome at dusk has Luminosity that INTERPOLATES (from the
   SkyObjectReplace keyframe) — currently a constant 1.0 in our renderer
   would render too bright.
4. **Non-luminous clouds** should get `texture × (Emissive + Diffuse × dot(N,
   -sun) × sunColour)` — not `texture × 1`. Our clouds being "too bright" is
   consistent with us skipping the diffuse-dot-sun shading entirely.

## Remaining uncertainty

- `_DAT_007a1870` exact value — evidence leans to 1.0f (identity), so our C#
  port should treat dat Luminosity/Transparency/MaxBright as **already in the
  right units** (no /100) and feed them directly. But user observation requires
  a /100 to look less bright, so either (a) dat values are in percent and
  `_DAT_007a1870 = 0.01f`, or (b) our shader is applying Luminosity in an
  additional place it shouldn't.
- The role of the per-Surface material cache struct (`FUN_0053a4b0` constructed,
  +0x0c..+0x48 fields) in the final fragment colour. It's written by the
  PhysicsPart L/MB/T animation setters, but I didn't track its consumer to D3D.
  Likely feeds COLORVERTEX-ON vertex alpha/RGB for non-luminous meshes.
- Whether `param_2 + 0x78` (Surface.Luminosity in FUN_0059da60) is the same
  float as `PhysicsPart +0xd4` (Luminosity set by FUN_0050f0c0). The dual-path
  suggests they're distinct — one is Surface-level (from the dat), one is
  PhysicsPart-level (animated override).

## Files cited

- `chunk_00500000.c:6213-6333` FUN_005062e0 (per-frame sky + fog tick)
- `chunk_00500000.c:7535-7603` FUN_00508010 (sky render loop)
- `chunk_00500000.c:13524-13617` FUN_0050f040/0c0/140 (PhysicsPart T/L/MB fields)
- `chunk_00510000.c:2115-2376` FUN_005120c0/12360/124b0 (set-or-animate entry)
- `chunk_00510000.c:4563-4591` FUN_00514b90 (transform enqueue — NOT the material writer)
- `chunk_00510000.c:7865-7963` FUN_00518e70/ee0/f50 (Surface broadcast)
- `chunk_00530000.c:7702-7764` FUN_0053a430/460/490 (per-Surface material cache fill)
- `chunk_00590000.c:10586-10795` FUN_0059da60 (the real per-mesh D3DMATERIAL9 + LIGHTING + COLORVERTEX writer)
- `chunk_005A0000.c:687-740` FUN_005a10f0 (device-init default state: LIGHTING=0, AMBIENT=0, FOG=0)
