From 3a02ded07905b512b9721f2fb8f9f4ec3e130446 Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Sat, 29 Aug 2026 09:21:12 -0400 Subject: [PATCH] D112: fix d43_emit put_f32 byte-swap off-by-one (skeletal models collapsed) put_f32 reversed bytes doff+1..doff+4 instead of doff..doff+3, dropping each float's MSB and pulling in a byte of the adjacent field. Corrupted every f32 in converted model rodata (joint Origins, LOD distances, BSP planes, bounding radii) for all sidecar-loaded models. Compiled-in front-end models were unaffected, hence 'logo fine, every guard a line'. render_pos[] joint matrices now coherent; BUNKER guards render as humanoids and monitor screens draw content. Regen: python tools_pc/d43_emit.py ntsc-final. --- docs/HANDOFF.md | 43 ++++++++++++++++++++++++------------------ docs/PCPortResearch.md | 40 +++++++++++++++++++++++++++++++++++++++ tools_pc/d43_emit.py | 2 +- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 5b81c0fa..3c04a0ae 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -40,27 +40,34 @@ the base image and magnified it. `gfx_lod_tile_offset` now returns 0 (base tile) when detail textures are off. BUNKER1 rooms render crisp. See §F "D107". -**Storage-room sky void — root-caused (session M-4), it is the -prop/model track:** the void is *closed doors* — portals 25/26 are -correctly `PORTALFLAG_DISABLED`, and `chrpropsRenderPass` runs (`n=3..10` -props/room) but emits almost no geometry, so the door models that should -fill those openings never draw. Portal visibility itself is healthy -post-D106 (room 18 → 8 rooms deep). Fixing prop/door rendering unblocks -this AND the missing weapon. +**Skeletal models — FIXED (session M-5, D112).** The "3D line" / missing +characters were `tools_pc/d43_emit.py`'s `put_f32` reversing the wrong 4 +bytes (`src[doff+4:doff:-1]` → bytes doff+1..doff+4, dropping the MSB), +corrupting every f32 in converted model rodata (joint `Origin`s, LOD +distances, BSP planes). Compiled-in front-end models were unaffected +(native LE), hence "logo fine, every guard broken". Fix: one line, +`src[doff:doff+4][::-1]`; regen with `python tools_pc/d43_emit.py +ntsc-final`. BUNKER guards now render as coherent humanoids, monitor +screens draw content. See §F "D108–D112". + +**"props emit no geometry" was a STALE premise** — props/doors emit +complete leaf DLs (~15k/run, valid ptrs; fast3d transforms them). The +storage-room void is NOT closed doors failing to render. **Real remaining work, in order:** -1. **Prop / character model rendering** — `chrpropsRenderPass` enumerates - props but emits no geometry; door models (storage void), weapon model, - and skeletal characters all ride this. Overlaps the `struct player` - offset pass and D75(b). Start here — highest visual + playability - leverage. `GE_D96` probe in `chrprop.c`. -2. **`struct player` offset pass** — weapon model doesn't draw; gate to +1. **Portal-BFS under-reach in the storage area** — per-frame visible + room count drops to 1–2 in rooms 27–29 (`GE_D104`/probes now removed). + `projmatrix` + `screensize` verified valid; the 200→3 `g_RoomLoadBudget` + drop is intentional N64 FP-cam behaviour. Likely `bgQueuePortalTraversal` + / `bgDetermineVisibleRooms` portal-projection. This is the void. +2. **Character pose / orientation polish** — post-D112 a BUNKER guard + renders but appears inverted; suspect a matrix-handedness / lookat + sign item in the joint base matrix. Also the mispositioned static + crate prop (top-left in the control room). +3. **`struct player` offset pass** — weapon model doesn't draw; gate to playability (landmine below + D102). -3. **D75(b)** — animated/skeletal character models (see §F "D75" and - "Known rendering bugs"). All three intro-model failures traced to the - game `matrixmath.c` → `modelUpdateMatrices`/`process_02_position` → - raw `render_pos` `i*0x40` re-pack path (NOT the D73 sin/cos path); - static prop = mispositioned, animated `setup_chr_instance` = missing. +4. Add an f32 value spot-check to `d43_emit.py`'s verify pass (it only + checks pointers/opcodes today — this bug passed "ALL CHECKS PASSED"). **This session's fixes (all committed, master):** diff --git a/docs/PCPortResearch.md b/docs/PCPortResearch.md index 9444c558..481f8aad 100644 --- a/docs/PCPortResearch.md +++ b/docs/PCPortResearch.md @@ -3401,6 +3401,46 @@ table via `bgSwapRoomVtx` or the point-index blob length); front-end text draws mirrored. Next: prop/door model rendering (unblocks the void AND the missing weapon), then D75(b) skeletal models. +**D108–D112 (session M-5) — the "props emit no geometry" premise was +stale; the real bug was a converter byte-swap off-by-one, FIXED.** +Re-probing `chrpropsRenderPass` / `chrpropRender` / `modelRenderNodeDl` +(`GE_D96*`) showed props *do* emit complete leaf DLs now (~15k +`nodeDl` EMITs/run, valid `gdl`/`vtx`/`BaseAddr`; fast3d transforms +~300–600 prop verts/frame with sane `w`). Two separate residual bugs +were isolated: (1) in the BUNKER storage area the per-frame visible-room +count drops to 1–2 while `cameramode` goes 1→0 and `g_RoomLoadBudget` +200→3 — but the 200→3 is *intentional* N64 behaviour +(`bgRoomVisibilityRelated`: 0xC8 for intro/swirl cams, 3 for FP) and +`currentPlayerGetProjectionMatrix()` + `g_CurrentPlayer->screensize` +were both verified valid, so this is a portal-BFS under-reach, still +open. (2) **Skeletal characters rendered as a "3D line" / not at all** +(also visible in attract mode): `drawjointlist` → `subcalcmatrices` → +`modelUpdateMatrices` → `process_02_position` → `modelBuildGroupMatrices` +produced `render_pos[]` joint matrices with **sane rotations but garbage +/ 1e27 / NaN positions**. `basemtx` (`camGetWorldToScreenMtxf()`) was a +clean orthonormal lookat; joint *rotations* from the anim bitstream were +fine; but `group->Origin` (`ModelRoData_GroupRecord.Origin`, a +`coord3d`) read as garbage for every joint whose bytes weren't +zero — while the adjacent `JointID`/`MatrixIDs` (u16/s16) read fine. +Root cause: **`put_f32` in `tools_pc/d43_emit.py` had an off-by-one in +its BE→LE byte reversal** — `buf[o:o+4] = src[doff+4:doff:-1]` yields +bytes `doff+1..doff+4` (dropping the value's MSB, pulling in one byte of +the next field) instead of `doff..doff+3`. This corrupted *every* f32 +field in converted model rodata (joint `Origin`s, LOD near/far, BSP +planes, bounding radii) for every sidecar-loaded model; the compiled-in +front-end intro models (native-LE C structs, not run through the +converter) were unaffected, which is why the Rareware logo looked right +but every in-level character collapsed. Fix: `src[doff:doff+4][::-1]`. +After regen (`python tools_pc/d43_emit.py ntsc-final`), all 20 +`render_pos` entries of a BUNKER guard are coherent (~±60 units around +the body), monitor screens draw their content, and character models +render as recognisable humanoids (still some pose/orientation polish +owed — a guard appears inverted — a separate matrix-handedness item). +`d43_emit.py`'s verify pass only checks pointers/opcodes, not float +values — a float spot-check is worth adding. Probes `GE_D96GATE / +D96DL / D108 / D109 / D110 / D111 / D112 / GE_TRICNT` were all removed +after the fix. + **`data/` deletion + recovery (session M-3).** `git worktree remove --force` on an agent worktree that had a directory *junction* `worktree/data → main/data` followed the junction and deleted the real diff --git a/tools_pc/d43_emit.py b/tools_pc/d43_emit.py index f21de03b..5ee5b44e 100644 --- a/tools_pc/d43_emit.py +++ b/tools_pc/d43_emit.py @@ -490,7 +490,7 @@ def process(name): def put_u16(o, v): buf[o:o + 2] = struct.pack("LE) def put_ptr(o, old_off): if not old_off: return