#87: mech armour PANELS now darken with damage -- the .DZM material system was loaded and unused

Players: "the actual enemy mech in external view is not showing darkened armor
panels".  We swapped destroyed LIMB meshes but never darkened a panel.

The 1995 game darkens armour through the MATERIALS.  MakeMechRenderables
(FUN_004cef28) built, per (damage zone, material), a watcher
FUN_004573e4(material, &zone->damageLevel, 0.1f) that snapshotted the materials
This commit is contained in:
Joe DiPrima
2026-07-31 19:52:55 -05:00
parent 5410371b0c
commit 137c151951
14 changed files with 589 additions and 1 deletions
+51
View File
@@ -321,6 +321,57 @@ Diags: `BT_CULT_LOG` (census/damage/death + `[cultvis]` + `[cultobj]` per-object
flags). MP: replicants transition via `ReadUpdateRecord` → same SetState watcher path (untested
cross-pod).
## Mech ARMOUR DARKENING (the .DZM material-damage system) — RECONSTRUCTED 2026-07-31 (Gitea #87) [T2]
A mech's armour panels **darken as their damage zone takes damage** — a per-zone, per-material
colour ramp-down, NOT a texture swap, a filter, or geometry. Players reported the port showing only
the limb swap ("the actual enemy mech in external view is not showing darkened armor panels").
**The 1995 chain** [T1]:
- **`.DZM`** (`VIDEO\<mech>SKIN.DZM`, 40 shipped) lists, per damage zone, the materials that zone
paints: `[dz_ltorso] material=avaskin:avat2_dz_ltorso_mtl`. One file per SKIN VARIANT — the
`dzm`/`dzms`/`dzmt`/`dzmo`/`dzma`/`dzmb`/`dzmc`/`dzmd` key family (same suffix set as the
`destroyed``destroyedd` video variants); the res compiler is `FUN_0041e4e0`.
- Compiled into BTL4.RES and parsed by **MUNGA's own `DamageZone` stream ctor** into
`materialTable`, keyed by `EntitySegment::SkeletonType` (`engine/MUNGA/DAMAGE.cpp:311-336`,
`GetMaterialList(skl_type)`) — real engine source [T0]. **This data was loaded and unused in the
port all along**; only the consumer was missing.
- **`BTL4VideoRenderer::MakeMechRenderables` (`FUN_004cef28`)** walks the zones and builds, per
(zone, material), a 232-byte watcher **`FUN_004573e4(material, &zone->damageLevel, 0.1f)`**
(a 60-byte per-zone parent first, factor 0.04). Error string: *"BTL4VideoRenderer: Material name
<X> could not be found"* @0x51d6f8.
- The watcher snapshots the material's **16 authored colour floats** — Ambient(3), Emissive(3),
Diffuse(3), Specular+shininess(4), Opacity(3) — precomputes a DAMAGED set = **pristine x 0.1**
(`0x3dcccccd`), and **`FUN_00457784`** re-pushes `lerp(pristine, damaged, damageLevel)` whenever
the level changes (constant `@0x4579a4` = **1.0**). So a zone at full damage renders at **10%**
of authored brightness, continuously interpolated.
- **Faithful quirk:** Opacity is read and lerped but **never written back** — only 13 of the 16
values are pushed (`dpl_GetMaterialOpacity` has no matching Set call in the watcher).
**NOT the mechanism** [T2]: the engine also has `SV_SPECIAL DAMAGE <material>` /
**`dpl_Damagize` `FUN_004902b0`** (punch's sibling — same VPX cmd 0x20, tokens
`f_t_dam(0x92)/sca(0x05)/undamage(0x04)`, dispatch @0x4599ae; error *"SPECIAL GEOGROUP DAMAGE
couldn't find material"*). A scan of **all 879 shipped BGFs finds ZERO `DAMAGE` tokens** — the path
exists in the engine but no shipped asset uses it. Don't chase it.
**The port** (`game/reconstructed/btl4vid.cpp` `BindArmourDamage`/`TickArmourDamage`,
`engine/MUNGA_L4/L4D3D.cpp`): batches now carry their AUTHORED (pre-substitution) material name
(`BgfDrawBatch::matName` -> `L4DRAWOP::dzMatName`); binding resolves the .DZM lists onto real draw
ops once per tree build, and the per-frame tick (driven from `Mech::PerformAndWatch`, EVERY mech)
copies `zone->damageLevel` onto them. Two hard-won details:
- **Key the material list on `viewSkeleton`, not the build `skeletonType`.** Each skeleton variant
is painted from its own skin library (Black Hawk N = `blhskin:`, X = `blxskin:`) and
`ApplyViewSkeleton` reloads every segment mesh; keying on the build skeleton matched a list
belonging to geometry that is not on screen -> **zero bindings, no darkening**.
- **Scaling the `D3DMATERIAL9` is inert** — see gotcha **§23** (`DIFFUSEMATERIALSOURCE` defaults to
vertex colour). The darkening is applied as a `TEXTUREFACTOR` modulate on texture stage 1.
Rebinding runs after every geometry swap (`RemakeEntityRenderables`, `ApplyViewSkeleton`), which
also keeps `segPick` (the #73 aimed pick) off discarded meshes.
Bench: `scratchpad/night7/armor_darken.sh` (self/enemy/ext legs) + `armor_ab.sh` (forced-level
pixel A/B). Gates `BT_ARMOR_LOG` / `BT_ARMOR_FORCE=<0..1>`. Verified: 57 bindings on the enemy
mech, brightness tracking `1 - 0.9*level` exactly, hull 0.35x vs terrain control 0.98x at level 1.
The `damcolor_`/`gen*gry_` arm+gun names go unmatched until the DESTROYED limb variant loads —
correct: those materials live on the `*D*.BGF` damaged meshes.
## Key Relationships
- Geometry/LOD: [[bgf-format]]. Base: [[wintesla-port]] (L4D3D). Shadow/visual-conform: [[locomotion]].
- Renders on: [[pod-hardware]] (main 3D view).