From 6180a44c64961e1cd354a76e45466c2ea612a7f6 Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Tue, 4 Aug 2026 12:43:36 -0500 Subject: [PATCH] #91 thor black rectangle: it's the OWN missile pod -- blakskn now material-keyed Three testers reported a black rectangle swaying with the footsteps in the thor cockpit (Summoner = the thr1 label, same THX canopy -- no isolation). Decode: the inside view is a PER-MECH AUTHORED type-A set, not the cop alone (no fallback in EntitySegment::GetVideoObjectName -- authored data): madcat/vulture/bhk1 = cop; sunder/loki/avatar = +tor; thor = +tor +MSL (the shoulder pod, the reported rectangle); owens = +both legs +tshd. The pod/leg pilot-facing surfaces use the SAME "skin:blakskn_dz_*" interior-structure material as the canopy frame, but the unlit frame constant was keyed on the _cop FILENAME -- identical material rendered (0.13,0.12,0.15) on the canopy and pure (0,0,0) on the pod/legs ([matlog]: owx_cop blakskn vcol=FF211F26 vs owx_lule vcol=FF000000). Fix (bgfload.cpp): the frame-constant treatment keys on meshIsCop OR material contains "skin:blakskn_dz_". mechfx:blakskn_mtl (tshd shadow quads) deliberately excluded. Verified: zero pure-black px in the lower view band across walk captures, the pod plate renders frame-toned and blends with the bar at rest (the reported anomaly dissolves), owens legs read as coherent structure, canopy/terrain un-regressed. Diags added: BT_MAT_LOG= per-batch material routing dump (bgfload), BT_HIDE_INSIDE_SEG= inside-mesh hide (btl4vid), [view] per-segment inside-roster names. KB: cockpit-view.md exactly-one claim corrected + the #91 section; bench scratchpad/night11/thorrect.sh. Co-Authored-By: Claude Fable 5 --- context/cockpit-view.md | 40 ++++++++++++++++++++++++++++++++-- engine/MUNGA_L4/bgfload.cpp | 38 +++++++++++++++++++++++++++++++- game/reconstructed/btl4vid.cpp | 18 +++++++++++++++ 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/context/cockpit-view.md b/context/cockpit-view.md index 3f8b8fe..b67b0dd 100644 --- a/context/cockpit-view.md +++ b/context/cockpit-view.md @@ -17,8 +17,17 @@ Verified in-game on all 8 mechs (Thor vs gameplay footage). [T2] - One per mech (12: AVX BLX FIX JAX LOX MAX OWX RAX SNX STX THX VUX; model→skeleton table in BTL4.RES @~3232850: `madcat=mad`, `blkhawk=blh`, … — the INSIDE skeleton is the X-variant, so - Madcat = `MAX_COP`). It is the torso segment's SkeletonType_A mesh; the inside view loads - EXACTLY ONE segment mesh (the `_cop`) — 25 others hidden. [T2] + Madcat = `MAX_COP`). It is the torso segment's SkeletonType_A mesh. [T2] +- **The inside view is a PER-MECH AUTHORED SET, not the cop alone** (#91 decode, 2026-08-04; + the earlier "EXACTLY ONE segment mesh" claim was madcat-derived and over-generalized — + corrected [T1: no type-A fallback in `EntitySegment::GetVideoObjectName`, SEGMENT.cpp:97 — + a segment renders inside ONLY if the resource authors a type-A name; T2: live `[view]` + roster logs]). Authored type-A rosters: **madcat / vulture / bhk1 = cop only; sunder / + loki / avatar = cop + `_tor` torso piece; thor = cop + `thx_tor` + `thx_msl` (the + shoulder LRM pod!); owens = cop + BOTH complete legs (`owx_{l,r}{ule,dle,bto,fto}`) + + `own_tshd`** — the Owens pilot saw their own legs walking; the Thor pilot saw their + missile pod edge bob at the lower frame. `BT_HIDE_INSIDE_SEG=` hides a non-cop + type-A mesh by name (diag, btl4vid.cpp ApplyViewSkeleton). - Material: `skin:blakskn_dz_{u,r,l}torso_mtl` — DIFFUSE/AMBIENT (0,0,0) + RAMP_REF `softer`, **NO texture** (BLXSKIN.BMF is 238 bytes total; byte-verified). The 56 nonzero-UV verts in BLX_COP sample nothing (planar-projection authoring leftover). [T1] @@ -230,6 +239,33 @@ the shell green), `BT_COP_DUMP` (per-batch punch/bbox), `BT_EYE_FWD=` (eye po - Feeds: [[gauges-hud]] (the HUD overlays this view) · [[subsystems]] (gyro, task #56) - Gotchas: [[reconstruction-gotchas]] §14 (LookAt axis guess), §15 (per-patch edge counting) +## The #91 "black rectangle" (thor) — own-body inside meshes + the blakskn rule [T2] + +Three testers (Oracle + RajelAran night 7; Ronin 2026-08-03: "moves diagonally 10→4 with the +footsteps, at rest sits ON the lower frame bar, right side, half-in") reported a black +rectangle in the Thor cockpit. Decoded 2026-08-04: +- The rectangle is the **thor's own `thx_msl` missile-pod mount plate** — authored, authentic + own-body geometry (see the roster above; "Summoner" = the `thr1` FE label, same THX canopy, + so the cross-reference isolated nothing). It rides the walk-animated shoulder while the eye + rides the gyro springs → the diagonal footstep-rhythm sway. Pixel-tracked across walk + captures: ~29px wide, x/y oscillating together along the 10↔4 axis, bottom center-right, + alternating with the gait [T2, scratchpad/night11/thorrect.sh]. +- **What WAS wrong:** the pilot-facing surfaces of these own-body meshes are authored with the + same `skin:blakskn_dz_*` interior-structure material as the canopy frame (texture-less, + black diffuse, `softer` ramp lo 0.25 → hi 0.99), but the unlit frame-constant treatment was + keyed on the `_cop` FILENAME — so the identical material rendered (0.13,0.12,0.15) on the + canopy and PURE (0,0,0) on the pod/legs (`[matlog]`-verified: owx_cop blakskn vcol=FF211F26 + vs owx_lule blakskn vcol=FF000000). Fixed in bgfload.cpp: the constant now keys on + `meshIsCop || material contains "skin:blakskn_dz_"`. `mechfx:blakskn_mtl` (tshd shadow + quads) deliberately does NOT match. Verified: zero pure-black pixels in the lower view band + post-fix, tab renders in the frame tone and blends with the bar at rest; owens legs read as + coherent dark structure; canopy/terrain un-regressed. The textured pod/leg batches + (THX/GEN/OWEN.BSL pages — bright, mean texel 0.55-0.74) face outboard and were never the + visible surfaces. +- Diags from the dig: `BT_MAT_LOG=` (bgfload per-batch material routing: + resolved colour/texture/ramp/vcol), `BT_HIDE_INSIDE_SEG=`, `[view]` per-segment + inside-roster log. The `softer` ramp resolves lo=(0.25,0.25,0.25) hi=(0.99,0.99,0.99) [T1]. + ## Torso-elevation aim -> EYE PITCH (fixed 2026-07-19) [T2, pixel-calibrated] User+tester report "pitch does not work": the Torso sim integrated R/F (stick-Y) into `currentElevation` (authored limits/rates) but NOTHING consumed it — the diff --git a/engine/MUNGA_L4/bgfload.cpp b/engine/MUNGA_L4/bgfload.cpp index 2bb2ca3..0abe5c9 100644 --- a/engine/MUNGA_L4/bgfload.cpp +++ b/engine/MUNGA_L4/bgfload.cpp @@ -640,6 +640,7 @@ 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::string meshStem; // lowercased load stem (BT_MAT_LOG diag) std::map 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; @@ -778,7 +779,19 @@ struct Builder { // value is not recoverable from the code (texture-less ramp = no texel // luminance to index); default (0.13,0.12,0.15) [T3] matches the dark // near-black frame in pod gameplay footage. BT_COP_FRAME="r g b" overrides. - if (useRamp && currentTex.empty() && meshIsCop) { + // The same interior-structure treatment applies to the blakskn SKIN + // family on ANY mesh, not just *_cop shells: the inside skeletons + // author pilot-facing surfaces of the OWN-BODY meshes (thor thx_msl + // missile pod, owens owx_* legs -- issue #91) with the identical + // "skin:blakskn_dz_*" material the canopy frame uses, and the + // board shaded them all the same way. Keying on the filename alone + // left those surfaces vcol=pure-black -- the reported "black + // rectangle" was the thor pod's blakskn mount plate at RGB(0,0,0) + // instead of the frame constant. mechfx:blakskn_mtl (the tshd + // shadow quads) must NOT match -- key on the skin-lib "_dz_" form. + const bool blaksknSkin = + currentMatName.find("skin:blakskn_dz_") != std::string::npos; + if (useRamp && currentTex.empty() && (meshIsCop || blaksknSkin)) { auto CB = [](float ff){ int v=(int)(ff*255.0f+0.5f); return (uint32_t)(v<0?0:v>255?255:v); }; vcol = 0xFF000000u | (CB(copFrameRGB(0))<<16) | (CB(copFrameRGB(1))<<8) | CB(copFrameRGB(2)); } @@ -939,6 +952,28 @@ struct Builder { else batch.lodBias = 0.0f; mesh->batches.push_back(batch); + // #91 diag: BT_MAT_LOG= dumps each batch of a matching mesh + // stem -- the material routing decision (ramp vs lit vs emissive, + // resolved colour, texture) for the inside-view black-mesh hunt. + { + static const char* s_matLog = getenv("BT_MAT_LOG"); + if (s_matLog && *s_matLog + && meshStem.find(s_matLog) != std::string::npos) + fprintf(stderr, "[matlog] %s mat=%s color=%08X hasDiff=%d " + "tex='%s' ch=%d hasRamp=%d useRamp=%d norms=%d " + "emis=%d vcol=%08X tris=%u\n", + meshStem.c_str(), currentMatName.c_str(), currentColor, + (int)currentHasDiffuse, currentTex.c_str(), + currentTexChannel, (int)currentHasRamp, (int)useRamp, + (int)hasNormals(vtag), (int)pureEmissive, vcol, + idxCount / 3); + if (s_matLog && *s_matLog + && meshStem.find(s_matLog) != std::string::npos) + fprintf(stderr, "[matlog] ramp lo=(%.3f,%.3f,%.3f) " + "hi=(%.3f,%.3f,%.3f)\n", + currentRampLo[0], currentRampLo[1], currentRampLo[2], + currentRampHi[0], currentRampHi[1], currentRampHi[2]); + } if (getenv("BT_COP_DUMP") && currentHasRamp && currentTex.empty()) { float lo[3]={1e9f,1e9f,1e9f}, hi[3]={-1e9f,-1e9f,-1e9f}, cen[3]={0,0,0}; int nv=0; for (uint32_t i = idxStart; i < idxStart + idxCount; ++i) { @@ -1386,6 +1421,7 @@ bool LoadBgfFile(const std::string& name, BgfData& out) { // single-siding + dark-frame ramp reconstruction in emitTri/buildPmesh applies to // these meshes only. b.meshIsCop = stemLower(name).find("_cop") != std::string::npos; + b.meshStem = stemLower(name); b.res = &res; for (const Chunk& c : roots) b.collect(c); b.finish(); diff --git a/game/reconstructed/btl4vid.cpp b/game/reconstructed/btl4vid.cpp index cdae040..22adbc5 100644 --- a/game/reconstructed/btl4vid.cpp +++ b/game/reconstructed/btl4vid.cpp @@ -3193,6 +3193,16 @@ int if (inside && nm != NULL && strstr((const char *)*nm, "_cop") != NULL && getenv("BT_HIDE_COCKPIT")) nm = NULL; + // #91 attribution diag: BT_HIDE_INSIDE_SEG= hides any OTHER + // inside-view (type-A) mesh whose name contains the substring -- the + // thor authors THREE type-A meshes (cop + thx_tor + thx_msl) and one + // of the extras is the reported black rectangle. + { + const char *hide = getenv("BT_HIDE_INSIDE_SEG"); + if (inside && nm != NULL && hide && *hide + && strstr((const char *)*nm, hide) != NULL) + nm = NULL; + } d3d_OBJECT *obj = NULL; if (nm != NULL) { @@ -3224,6 +3234,14 @@ int else render_tree.segPick.erase(slot); if (obj) ++shown; else ++hidden; + // Inside view: NAME what renders. The KB says the inside view is the + // _cop alone; any OTHER type-A mesh here is a black-material stowaway + // (the #91 rectangle investigation) -- keep the roster visible. + if (inside && obj != NULL) + DEBUG_STREAM << "[view] shown seg " << slot << ": " + << (const char *)segment->GetName() + << " mesh " << (nm ? (const char *)*nm : "?") + << "\n" << std::flush; } // This reloaded every segment mesh, so every armour-damage binding is stale. BindArmourDamage(viewpoint, render_tree);