Cockpit: the authentic punch STENCIL-CUT -- all 8 cockpits real (task #55)
The user caught the inversion (blocked viewports, see-through frames); the
answer came from the rasterizer board itself: an i860 disassembly of the
VREND.MNG firmware decoded the damageize handler (@0xf040f6f8) -- cmd 0x20
(vr_damage_action) writes the dpl_Punchize token triple onto the punch
geogroup's first three geometry chunks IN FILE ORDER:
chunk1 = the detailed window-aperture shapes -> an invisible per-pixel MASK
chunk2 = the coarse hull -> the ONLY colour-drawn geometry (where not masked)
chunk3 = byte-identical hull twin -> the damage-reset record, never visible
Visible canopy = hull-minus-apertures. Both earlier readings were wrong ways
around the same three chunks: dropping all punch left floating fragments (no
hull); hiding the duplicate pair hid the HULL and drew the MASK -- the exact
inversion observed.
Port: bgfload tags per punch patch {non-paired pmesh = mask (role 1),
first twin = hull (role 2), second twin = skip}; L4D3D DrawMesh runs
mask->stencil-1 (no colour/z-write, z-tested), hull->stencil-NOTEQUAL,
mask->stencil-0; device D24X8 -> D24S8 + stencil clear. The cut never
touches z, so later-drawn entities show through the apertures.
BT_COP_PLATES=1 disables (diag).
In-game, all 8 mechs on pure defaults: connected dark frames with clear
viewports -- thor (the footage mech) shows the central viewport + surrounding
frame exactly as filmed. Combat regression clean (kill + no NaN).
Arena textured punch uses the same firmware kit; the black-texel alpha path
there is a working approximation, flagged in the KB.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8431e69b1a
commit
96b8892e7c
@@ -200,6 +200,7 @@ d3d_OBJECT *d3d_OBJECT::LoadObject(LPDIRECT3DDEVICE9 device, char *fileName)
|
||||
}
|
||||
|
||||
object->mDrawOps[i].drawAsSky = isSky;
|
||||
object->mDrawOps[i].copRole = 0;
|
||||
|
||||
if (isSky)
|
||||
{
|
||||
@@ -430,6 +431,7 @@ d3d_OBJECT* d3d_OBJECT::LoadObjectBGF(LPDIRECT3DDEVICE9 device, char *fileName)
|
||||
static int s_punch = -1;
|
||||
if (s_punch < 0) { const char *pv = getenv("BT_PUNCH"); s_punch = (pv == 0 || pv[0] != '0') ? 1 : 0; }
|
||||
object->mDrawOps[i].punchThrough = (s_punch && data.batches[i].punch);
|
||||
object->mDrawOps[i].copRole = data.batches[i].copRole; // punch stencil-cut kit (task #55)
|
||||
object->mDrawOps[i].texture.texture = NULL;
|
||||
object->mDrawOps[i].texture.wrap_u = L4TEXOP::REPEAT;
|
||||
object->mDrawOps[i].texture.wrap_v = L4TEXOP::REPEAT;
|
||||
@@ -1112,6 +1114,11 @@ void d3d_OBJECT::DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time target
|
||||
{
|
||||
L4DRAWOP *drawOp = &mDrawOps[iOp];
|
||||
|
||||
// COCKPIT PUNCH KIT (task #55): aperture-MASK ops never draw colour --
|
||||
// they are consumed by the FOLLOWING hull op's stencil-cut sequence.
|
||||
if (drawOp->copRole == 1)
|
||||
continue;
|
||||
|
||||
if (drawOp->alphaTest != (pass == PASS_ALPHABLEND))
|
||||
continue;
|
||||
|
||||
@@ -1189,7 +1196,57 @@ void d3d_OBJECT::DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time target
|
||||
}
|
||||
|
||||
gNumBatches++;
|
||||
if (drawOp->bgfPrimCount > 0 && mBgfVB != NULL)
|
||||
// COCKPIT PUNCH STENCIL-CUT (task #55, the i860 'damageize' mechanism):
|
||||
// a role-2 HULL op is preceded by its role-1 aperture-MASK op. Sequence:
|
||||
// 1. draw the mask stencil-only (no colour, no z-write, z-TESTED) -> ref 1
|
||||
// 2. draw the hull with stencil-reject where ref==1 (the window cutouts)
|
||||
// 3. re-draw the mask writing stencil 0 (clean up for other punch groups)
|
||||
// The stencil never touches z, so entities drawn later still show through
|
||||
// the apertures correctly.
|
||||
const bool copCut = (drawOp->copRole == 2 && iOp > 0
|
||||
&& mDrawOps[iOp - 1].copRole == 1
|
||||
&& mDrawOps[iOp - 1].bgfPrimCount > 0
|
||||
&& drawOp->bgfPrimCount > 0 && mBgfVB != NULL);
|
||||
if (copCut)
|
||||
{
|
||||
L4DRAWOP *maskOp = &mDrawOps[iOp - 1];
|
||||
mDevice->SetFVF(L4VERTEX_FVF);
|
||||
mDevice->SetStreamSource(0, mBgfVB, 0, mBgfStride);
|
||||
mDevice->SetIndices(mBgfIB);
|
||||
// 1. mask -> stencil ref 1 (colour + z-write off)
|
||||
mDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0);
|
||||
mDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||
mDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
|
||||
mDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
|
||||
mDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
|
||||
mDevice->SetRenderState(D3DRS_STENCILREF, 1);
|
||||
mDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mBgfNumVerts,
|
||||
maskOp->bgfStartIndex, maskOp->bgfPrimCount);
|
||||
// 2. hull, stencil-rejected under the mask
|
||||
mDevice->SetRenderState(D3DRS_COLORWRITEENABLE,
|
||||
D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN |
|
||||
D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA);
|
||||
mDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
|
||||
mDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_NOTEQUAL);
|
||||
mDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
|
||||
mDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mBgfNumVerts,
|
||||
drawOp->bgfStartIndex, drawOp->bgfPrimCount);
|
||||
// 3. mask again -> stencil ref 0 (clean)
|
||||
mDevice->SetRenderState(D3DRS_COLORWRITEENABLE, 0);
|
||||
mDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||
mDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
|
||||
mDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
|
||||
mDevice->SetRenderState(D3DRS_STENCILREF, 0);
|
||||
mDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mBgfNumVerts,
|
||||
maskOp->bgfStartIndex, maskOp->bgfPrimCount);
|
||||
// restore
|
||||
mDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
|
||||
mDevice->SetRenderState(D3DRS_COLORWRITEENABLE,
|
||||
D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN |
|
||||
D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA);
|
||||
mDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
|
||||
}
|
||||
else if (drawOp->bgfPrimCount > 0 && mBgfVB != NULL)
|
||||
{
|
||||
// direct-draw: known contiguous range, no attribute-table scan.
|
||||
// (DrawSubset used to set the vertex decl itself -- assert the FVF here
|
||||
|
||||
@@ -2779,7 +2779,7 @@ DPLRenderer::DPLRenderer(
|
||||
mPresentParams.PresentationInterval = D3DPRESENT_RATE_DEFAULT;
|
||||
mPresentParams.BackBufferFormat = D3DFMT_X8R8G8B8;
|
||||
mPresentParams.EnableAutoDepthStencil = TRUE;
|
||||
mPresentParams.AutoDepthStencilFormat = D3DFMT_D24X8;
|
||||
mPresentParams.AutoDepthStencilFormat = D3DFMT_D24S8; // S8: the cockpit punch stencil-cut (task #55)
|
||||
mPresentParams.Windowed = !fullscreen;
|
||||
if (fullscreen)
|
||||
{
|
||||
@@ -7421,7 +7421,7 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
|
||||
|
||||
DWORD currentFog;
|
||||
mDevice->GetRenderState(D3DRS_FOGCOLOR, ¤tFog);
|
||||
hr = mDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, currentFog, 1.0f, 0);
|
||||
hr = mDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, currentFog, 1.0f, 0);
|
||||
|
||||
hr = mDevice->BeginScene();
|
||||
|
||||
|
||||
+33
-17
@@ -504,7 +504,8 @@ struct Builder {
|
||||
bool currentTSphere = false; // material is tsphere_mtl (translocation warp): ramp it despite normals
|
||||
bool meshIsTSphere = false; // this OBJECT is the translocation warp -> smooth-tessellate the cone
|
||||
bool meshIsCop = false; // this OBJECT is a *_cop cockpit canopy shell (task #55)
|
||||
std::set<const void*> copPlateSkip; // duplicate damage-plate PMESHes to hide (see TAG_PATCH)
|
||||
std::map<const void*, int> copRoleMap; // punch-kit roles per PMESH chunk: 1=mask 2=hull 3=skip (see TAG_PATCH)
|
||||
int currentCopRole = 0; // role of the pmesh being built (consumed by buildPmesh)
|
||||
bool currentHasEmissive = false;
|
||||
float currentEmissive[3] = {0,0,0};
|
||||
|
||||
@@ -734,6 +735,7 @@ struct Builder {
|
||||
batch.lodNear = currentLodNear;
|
||||
batch.lodFar = currentLodFar;
|
||||
batch.punch = currentPunch;
|
||||
batch.copRole = currentCopRole; // punch stencil-kit role (task #55)
|
||||
batch.shadowMat = currentShadowMat;
|
||||
batch.hasEmissive = currentHasEmissive;
|
||||
for (int i = 0; i < 3; ++i) batch.emissive[i] = currentEmissive[i];
|
||||
@@ -964,24 +966,24 @@ struct Builder {
|
||||
if (memcmp(s + i, "PUNCH", 5) == 0) currentPunch = true;
|
||||
if (currentPunch) break;
|
||||
}
|
||||
// COCKPIT DAMAGE-STATE PLATES (task #55, resolved 2026-07-11): every
|
||||
// *_cop PUNCH patch contains exactly one pair of BYTE-IDENTICAL
|
||||
// duplicate PMESHes -- solid cover plates for the tricoder per-triangle
|
||||
// damage machinery (dpl_Punchize tokens = {dmg_set, undmg2enbl|texture,
|
||||
// dmg_clear}; TRICODER.H), NOT visible undamaged structure. Rendering
|
||||
// them was the "solid black canopy"; hiding only whole punch patches
|
||||
// instead left disconnected floating frame bits. Hide the duplicate
|
||||
// pairs (both copies), render the rest of the punch lattice opaque --
|
||||
// verified from every mech's authored eye (SKL [jointeye]): all 8
|
||||
// cockpits read as CONNECTED frames with window apertures. Duplicates
|
||||
// are counted WITHIN the patch (BLX repeats its whole main lattice
|
||||
// ACROSS its three dz-material patches -- those must stay).
|
||||
// BT_COP_PLATES=1 renders the plates (diagnostic).
|
||||
// PUNCH STENCIL-CUT KIT (task #55, FINAL -- decoded from the i860
|
||||
// VREND.MNG firmware 'damageize' handler @0xf040f6f8 + the dpl_Punchize
|
||||
// token triple {dmg_set, undmg2enbl|texture, dmg_clear}): a PUNCH
|
||||
// patch's pmeshes are, in FILE ORDER, {aperture MASK, visible HULL,
|
||||
// byte-identical hull TWIN}. The board rasterizes the MASK as an
|
||||
// invisible per-pixel cutout, colour-draws ONLY the HULL where not
|
||||
// masked (= the canopy frame with the window apertures cut through),
|
||||
// and keeps the TWIN as the damage-reset record (never visible).
|
||||
// Identify the twin pair WITHIN the patch by content hash: pair FIRST
|
||||
// = the hull (role 2), pair SECOND = skipped; every non-paired pmesh
|
||||
// = the aperture mask (role 1; drawn stencil-only by L4D3D).
|
||||
// BT_COP_PLATES=1 disables the kit (all pmeshes draw plain, diag).
|
||||
if (meshIsCop && currentPunch) {
|
||||
static int s_plates = -1;
|
||||
if (s_plates < 0) { const char* e = getenv("BT_COP_PLATES"); s_plates = (e && e[0] != '0') ? 1 : 0; }
|
||||
if (!s_plates) {
|
||||
std::map<std::pair<size_t, uint64_t>, const Chunk*> seen;
|
||||
bool pairFound = false;
|
||||
for (const Chunk& ch : c.children) {
|
||||
if (ch.id != TAG_PMESH) continue;
|
||||
uint64_t h = 1469598103934665603ull; // FNV-1a
|
||||
@@ -990,12 +992,19 @@ struct Builder {
|
||||
auto it = seen.find(key);
|
||||
if (it == seen.end()) seen[key] = &ch;
|
||||
else {
|
||||
copPlateSkip.insert(&ch); copPlateSkip.insert(it->second);
|
||||
copRoleMap[it->second] = 2; // hull (first twin)
|
||||
copRoleMap[&ch] = 3; // reset record: never drawn
|
||||
pairFound = true;
|
||||
if (getenv("BT_COP_DUMP"))
|
||||
fprintf(stderr, "[cop] hid duplicate plate pair (pmesh len=%u)\n",
|
||||
fprintf(stderr, "[cop] punch kit: hull+twin pair (pmesh len=%u)\n",
|
||||
(unsigned)ch.len);
|
||||
}
|
||||
}
|
||||
if (pairFound)
|
||||
for (const Chunk& ch : c.children)
|
||||
if (ch.id == TAG_PMESH && !copRoleMap.count(&ch))
|
||||
copRoleMap[&ch] = 1; // aperture mask
|
||||
// no pair -> every pmesh stays role 0 (draw plain; unexpected shape)
|
||||
}
|
||||
}
|
||||
for (const Chunk& ch : c.children)
|
||||
@@ -1067,7 +1076,14 @@ struct Builder {
|
||||
for (int i = 0; i < 3; ++i) { currentRampLo[i] = savedRampLo[i]; currentRampHi[i] = savedRampHi[i]; }
|
||||
break;
|
||||
}
|
||||
case TAG_PMESH: if (copPlateSkip.count(&c)) break; buildPmesh(c); break;
|
||||
case TAG_PMESH: {
|
||||
auto rit = copRoleMap.find(&c);
|
||||
currentCopRole = (rit != copRoleMap.end()) ? rit->second : 0;
|
||||
if (currentCopRole == 3) { currentCopRole = 0; break; } // hull twin: never drawn
|
||||
buildPmesh(c);
|
||||
currentCopRole = 0;
|
||||
break;
|
||||
}
|
||||
default: for (const Chunk& ch : c.children) collect(ch); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,12 @@ struct BgfDrawBatch {
|
||||
// lattice: arena scaffold layers AR01-04, gratings). Drawn SOLID they
|
||||
// shingle near-coplanar over the massing = depth-shimmer "noisy surfaces".
|
||||
bool punch = false;
|
||||
// COCKPIT PUNCH STENCIL-CUT ROLES (task #55, decoded from the i860 VREND.MNG
|
||||
// firmware 'damageize' handler): a PUNCH patch's three pmeshes are, in file
|
||||
// order, {aperture MASK, visible HULL, hull twin (damage-reset record)}.
|
||||
// 0 = normal batch; 1 = aperture mask (stencil-only, no colour/z);
|
||||
// 2 = hull (drawn stencil-rejected under the mask = the window cutouts).
|
||||
int copRole = 0;
|
||||
// LAYER DEPTH BIAS (additive objects): the layers contain EXACTLY-COPLANAR
|
||||
// duplicated surfaces (flush wall patches between detail + massing). The
|
||||
// IG board resolved those deterministically (exact per-polygon plane
|
||||
|
||||
@@ -98,6 +98,10 @@ struct L4DRAWOP
|
||||
// black texels were loaded with alpha 0; draw with alpha TEST in the opaque pass
|
||||
// (z-write preserved), rejecting hole texels.
|
||||
bool punchThrough;
|
||||
// COCKPIT PUNCH STENCIL-CUT (task #55, i860-firmware-decoded): 0=normal,
|
||||
// 1=aperture MASK (drawn stencil-only, paired with the following hull op),
|
||||
// 2=HULL (drawn stencil-rejected under the mask = window cutouts).
|
||||
int copRole;
|
||||
// LAYER DEPTH BIAS (additive-LOD coplanar resolution; see bgfload.h): finer
|
||||
// layers get a slightly more negative normalized-depth bias so EXACTLY-
|
||||
// coplanar duplicated surfaces resolve to the detail layer instead of
|
||||
|
||||
Reference in New Issue
Block a user