D243 M-189/M-190: fix Bond cutscene root cause, camera tracking, mouse-lock; D236 pass 12 diagnostics

D243/D173/D292 (Bond cutscene family) -- root cause found and fixed:
bondview2.c's cutscene body-model buffer layout reserved space using a
hardcoded 32-bit sizeof(Model) literal that never accounted for 64-bit
pointer widening (real size 232 vs reserved ~188 bytes), so the model's
own animation-data buffer overlapped its tail fields (playspeed/animrate/
unkac/unkb0/unkb4), corrupting them on every cutscene body-model creation.
Fixed by computing the real sizeof(Model), matching the identical idiom
already used one line above for ModelFileHeader. User-confirmed live.

Follow-up camera-tracking fix: replaced the old shake-workaround freeze
(which also blocked legitimate per-shot tracking) with a write-every-tick
scheme plus an instant re-seed of the look-at filter's accumulator at each
teleport, eliminating the old catch-up shake without freezing anything.

Second bug found in the same review: mouse-look was never gated during
scripted/no-control camera states (an already-documented but never
implemented safety gate in port/src/input.c's hipDirectCompute). Fixed by
adding the missing check, reusing gameScriptedCameraActive() (renamed from
D243's d243mCutsceneActive, since it's no longer D243-specific -- also
used by model.c's M-183/M-185 clamps, which were separately found to be
dead code for real players (gated on the diagnostic env var, not just
camera mode) and fixed to be unconditionally active.

D236 (Surface tree wall) pass 12: new mechanism found (both competing
texture classes are Z_UPD=0 decal-mode draws, so on-screen precedence is
decided by draw order, not depth) and a headless capture finally landed on
the actual bugged view -- but that view has zero tree-class triangles
present, so the mechanism couldn't be tested there. Two new env-gated
diagnostic probes (GE_D236ORDER, GE_D236ZFIX) left in tree for the next
pass; not a fix, not v0.3.0-blocking.

All findings.md summary-table rows touched this session cross-checked
against their full entries and corrected where stale (D219/D252/D246/D247
had been fixed in a prior session but their index rows still said
OPEN/NOT-A-BUG).
This commit is contained in:
James Dansereau committed 2026-09-18 02:30:17 -04:00
1 parent 643785c2c5
commit db823d0261
7 files changed
+311 -62

No files matched your search

+69
View File
@@ -1812,6 +1812,58 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
}
}
/* D236 pass 12 (M-191, TEMP): new angle, not tried by passes 1-11 (all of
* which focused on the tree-card class's z/w and instantiation counts).
* Both the noise "wall" (oml=0xc81049d8) and the discrete tree-card class
* (oml=0x0c184b50) decode to Z_CMP=1/Z_UPD=0/ZMODE=DEC (checked offline
* against both full oml words) -- NEITHER writes the depth buffer. Two
* no-z-write decals drawn against the same static (z-writing) background
* never actually depth-test against EACH OTHER: each is tested only
* against the background's z, so on-screen precedence between the two
* decals is decided purely by which one is submitted LAST in the frame's
* draw list, not by which one's own z/w is smaller (the z/w comparisons
* all 11 prior passes made are therefore not conclusive either way about
* visual winner). This probe tests that directly: track a per-frame
* submission-order counter (reset whenever num_dls changes, i.e. once
* per frame) and log the min/max order index seen for each of the two
* classes whenever num_dls changes and at least one of them appeared
* that frame -- if the noise class's order index is consistently HIGHER
* (drawn later) than the tree class's in frames where both appear, that
* directly explains "wall painted over trees" regardless of geometric
* depth, and points at a draw-order (not depth) bug. Remove once D236
* pass 12 concludes. */
if (getenv("GE_D236ORDER")) {
static uint32_t d236o_last_dl = 0xFFFFFFFFu;
static uint32_t d236o_seq = 0;
static uint32_t d236o_noise_min = 0, d236o_noise_max = 0, d236o_noise_n = 0;
static uint32_t d236o_tree_min = 0, d236o_tree_max = 0, d236o_tree_n = 0;
extern uint32_t num_dls;
if (num_dls != d236o_last_dl) {
if (d236o_noise_n != 0 || d236o_tree_n != 0) {
fprintf(stderr,
"D236ORDER dl=%u noise_n=%u noise_order=[%u,%u] tree_n=%u tree_order=[%u,%u] "
"tree_last_after_noise_last=%d\n",
d236o_last_dl, d236o_noise_n, d236o_noise_min, d236o_noise_max,
d236o_tree_n, d236o_tree_min, d236o_tree_max,
(d236o_tree_n != 0 && d236o_noise_n != 0) ? (int) (d236o_tree_max > d236o_noise_max) : -1);
}
d236o_last_dl = num_dls;
d236o_seq = 0;
d236o_noise_min = d236o_noise_max = d236o_noise_n = 0;
d236o_tree_min = d236o_tree_max = d236o_tree_n = 0;
}
if (rdp.other_mode_l == 0xc81049d8u) {
if (d236o_noise_n == 0) d236o_noise_min = d236o_seq;
d236o_noise_max = d236o_seq;
d236o_noise_n++;
} else if (rdp.other_mode_l == 0x0c184b50u) {
if (d236o_tree_n == 0) d236o_tree_min = d236o_seq;
d236o_tree_max = d236o_seq;
d236o_tree_n++;
}
d236o_seq++;
}
if ((rsp.geometry_mode & G_CULL_BOTH) != 0) {
float dx1 = v1->x / (v1->w) - v2->x / (v2->w);
float dy1 = v1->y / (v1->w) - v2->y / (v2->w);
@@ -1850,6 +1902,23 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
bool depth_test = ((rsp.geometry_mode & G_ZBUFFER) == G_ZBUFFER || (rdp.other_mode_l & G_ZS_PRIM) == G_ZS_PRIM) &&
((rdp.other_mode_h & G_CYC_1CYCLE) == G_CYC_1CYCLE || (rdp.other_mode_h & G_CYC_2CYCLE) == G_CYC_2CYCLE);
bool depth_update = (rdp.other_mode_l & Z_UPD) == Z_UPD;
/* D236 pass 12 (M-191, TEMP, diagnostic-only): both the noise "wall"
* (oml=0xc81049d8) and the discrete tree-card class (oml=0x0c184b50) are
* Z_CMP=1/Z_UPD=0 decals, and GE_D236ORDER above shows the noise class is
* ALWAYS submitted after the tree class in every sampled frame -- with
* neither writing depth, later-submitted always wins the color buffer
* regardless of which is geometrically closer. This experiment forces
* depth_update=true for the tree-card class ONLY, so it writes real
* depth; if the noise class (submitted later, same depth_compare=LEQUAL)
* now correctly fails its depth test against the tree cards' nearer z
* instead of overwriting them, that confirms draw-order+no-z-write is
* the actual visual-precedence mechanism. NOT a proposed fix (forcing
* z-write for a decal-mode class is not decomp-faithful either) -- purely
* to test the mechanism cheaply before deciding what a real fix looks
* like. Remove once D236 pass 12 concludes. */
if (getenv("GE_D236ZFIX") && rdp.other_mode_l == 0x0c184b50u) {
depth_update = true;
}
bool depth_compare = (rdp.other_mode_l & Z_CMP) == Z_CMP;
bool depth_source_prim = (rdp.other_mode_l & G_ZS_PRIM) == G_ZS_PRIM /* && gDP.primDepth.z == 1.0f */;
uint16_t zmode = rdp.other_mode_l & ZMODE_DEC;