#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
+45
View File
@@ -393,6 +393,17 @@ d3d_OBJECT* d3d_OBJECT::LoadObjectBGF(LPDIRECT3DDEVICE9 device, char *fileName)
object->mDrawOps[i].lodNear = data.batches[i].lodNear;
object->mDrawOps[i].lodFar = data.batches[i].lodFar;
object->mDrawOps[i].lodDepthBias = data.batches[i].lodBias;
// MECH ARMOUR DAMAGE (issue #87): carry the authored material name so the
// mech's .DZM zone->material lists can drive this batch's darkening.
{
const std::string &mn = data.batches[i].matName;
size_t n = mn.size();
if (n >= sizeof(object->mDrawOps[i].dzMatName))
n = sizeof(object->mDrawOps[i].dzMatName) - 1;
memcpy(object->mDrawOps[i].dzMatName, mn.c_str(), n);
object->mDrawOps[i].dzMatName[n] = '\0';
object->mDrawOps[i].dzDamageLevel = 0.0f;
}
const bool useRamp = (s_ramp && data.batches[i].hasRamp);
uint32_t c = data.batches[i].color;
@@ -1198,6 +1209,33 @@ void d3d_OBJECT::DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time target
mDevice->SetMaterial(&drawOp->material);
// MECH ARMOUR DAMAGE (issue #87): the 1995 renderer kept a per-material
// watcher that lerped the material's colour terms from the authored values
// toward 0.1x as the owning damage zone's damageLevel ran 0->1
// (FUN_004573e4 precomputed pristine*0.1; FUN_00457784 re-pushed
// lerp(pristine, damaged, level) on every change, constant @0x4579a4 = 1.0).
//
// We cannot express that by scaling this op's D3DMATERIAL9: D3D9 defaults
// DIFFUSEMATERIALSOURCE to D3DMCS_COLOR1, and every BGF vertex carries a
// baked colour, so the material's Diffuse is never consulted for this
// geometry -- scaling it renders identically (measured: 0 changed pixels).
// Instead modulate the FINAL colour by k on texture stage 1, which darkens
// the result whatever the diffuse source was, and equally covers the
// ramp-baked-texture path (the mech's own case) and pure-emissive batches.
const bool damageTint = (drawOp->dzDamageLevel > 0.0f);
if (damageTint)
{
float d = drawOp->dzDamageLevel;
if (d > 1.0f) d = 1.0f;
const float k = 1.0f - (1.0f - kDamagedMaterialScale) * d; // lerp(1, 0.1, d)
mDevice->SetRenderState(D3DRS_TEXTUREFACTOR, D3DCOLOR_COLORVALUE(k, k, k, 1.0f));
mDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
mDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
mDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TFACTOR);
mDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
mDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
}
#ifndef RP3_EMULATE
SetTextureScrolling(&(drawOp->texture), targetRenderFrame);
SetTexture(drawOp->texture.texture);
@@ -1337,6 +1375,13 @@ void d3d_OBJECT::DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time target
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, sVAop);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, sVAa2);
}
if (damageTint)
{
// stage 1 is otherwise unused in this draw path -- put it back to
// DISABLE so the next (undamaged) op is not tinted too.
mDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
mDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
}
if (drawOp->lodDepthBias != 0.0f)
mDevice->SetRenderState(D3DRS_DEPTHBIAS, sDB);
}