From ad220f4e3db4ad50795c32b64bac2bbd0576ecaf Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Fri, 28 Aug 2026 21:43:24 -0400 Subject: [PATCH] D106: portal near-plane projection garbage no longer culls the next room MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUNKER1's portal-visibility BFS (sub_GAME_7F0B7F84 -> sub_GAME_7F0B5864) projects each candidate portal polygon to a screen-space bbox. When the player straddles a portal's plane, sub_GAME_7F0B5528 emits z==0 near-plane clip points; transform3Dto2DWithZScaling then uses inv_z = -1e20, sending those points to +/-1e20-scale screen coords. On N64 the two clip points bracket the view symmetrically, so the degenerate-box check (bg.c:1713) or the downstream screensize clamp yields a full-screen box and the room is kept. On x86 the exact garbage can come back min>max on a single axis (or non-finite), slipping past that check -> the portal is dropped and the room the player is walking toward vanishes, showing skyRender's background fill through the doorway. Fix: #ifdef PORT, treat any wildly out-of-range / non-finite projected bound as degenerate -> full screensize (matches the N64 outcome). Verified -level_09: per-frame visible-room count 1-3 -> 2-4; rooms beyond near doorways (r11 from r27, etc.) now render. Reworks the GE_D104 probe to dump the per-frame visible-room list (gated, capped). Residual (separate): a bounded central sky gap + a blurry grey wall/ ceiling texture remain in the storage rooms — a dropped-poly / texture- dimension issue in the D85 room GDL path, not visibility. --- src/game/bg.c | 62 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/game/bg.c b/src/game/bg.c index ec0f7174..0702bbd1 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -636,31 +636,23 @@ Gfx *sub_GAME_7F0B3C8C(Gfx *gdl) gSPMatrix(gdl++, osVirtualToPhysical((void*)get_BONDdata_field_10E0()), (G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION)); #ifdef PORT /* TEMP D104: room render-pass geometry-visibility probe. */ - if (getenv("GE_D104")) { - static s32 d104n = 0; - if (d104n < 40) { - s32 *mp = (s32 *)(uintptr_t)(u32)get_BONDdata_field_10E0(); - s32 *pp = (s32 *)(uintptr_t)(u32)(uintptr_t)currentPlayerGetProjectionMatrix(); - osSyncPrintf("D104 room=%d bbox=(%d,%d)-(%d,%d) view l=%d t=%d x=%d y=%d screensize=(%d,%d)-(%d,%d)\n", - dword_CODE_bss_8007FFA0[j].roomid, - (s32)dword_CODE_bss_8007FFA0[j].bbox.min.x, (s32)dword_CODE_bss_8007FFA0[j].bbox.min.y, - (s32)dword_CODE_bss_8007FFA0[j].bbox.max.x, (s32)dword_CODE_bss_8007FFA0[j].bbox.max.y, - (s32)g_CurrentPlayer->viewleft, (s32)g_CurrentPlayer->viewtop, - (s32)g_CurrentPlayer->viewx, (s32)g_CurrentPlayer->viewy, - (s32)g_CurrentPlayer->screensize.min.x, (s32)g_CurrentPlayer->screensize.min.y, - (s32)g_CurrentPlayer->screensize.max.x, (s32)g_CurrentPlayer->screensize.max.y); - if (mp) { - s32 r; - for (r = 0; r < 4; r++) { - f32 e0 = (f32)(s32)((mp[r*2+0] & 0xffff0000) | ((u32)mp[8+r*2+0] >> 16)) / 65536.0f; - f32 e1 = (f32)(s32)((mp[r*2+0] << 16) | (mp[8+r*2+0] & 0xffff)) / 65536.0f; - f32 e2 = (f32)(s32)((mp[r*2+1] & 0xffff0000) | ((u32)mp[8+r*2+1] >> 16)) / 65536.0f; - f32 e3 = (f32)(s32)((mp[r*2+1] << 16) | (mp[8+r*2+1] & 0xffff)) / 65536.0f; - osSyncPrintf("D104 row%d = % .4f % .4f % .4f % .4f\n", r, e0, e1, e2, e3); - } + { + static s32 d104c = 0; + if (getenv("GE_D104") && (d104c++ % 600) == 0 && d104c < 12000) { + s32 rr; + osSyncPrintf("D104 tick=%d curRoom=%d roomsDrawn=%d budget=%d :", + d104c, (s32)g_CurrentPlayer->curRoomIndex, + (s32)g_BgNumberOfRoomsDrawn, (s32)g_RoomLoadBudget); + for (rr = 0; rr < g_BgNumberOfRoomsDrawn && rr < 24; rr++) { + s32 rid = dword_CODE_bss_8007FFA0[rr].roomid; + osSyncPrintf(" r%d(ld=%d %d,%d-%d,%d)", rid, + (s32)g_BgRoomInfo[rid].model_bin_loaded, + (s32)dword_CODE_bss_8007FFA0[rr].bbox.min.x, + (s32)dword_CODE_bss_8007FFA0[rr].bbox.min.y, + (s32)dword_CODE_bss_8007FFA0[rr].bbox.max.x, + (s32)dword_CODE_bss_8007FFA0[rr].bbox.max.y); } - osSyncPrintf("D104 f10E0=%p projm=%p\n", (void *)mp, (void *)pp); - d104n++; + osSyncPrintf("\n"); } } #endif @@ -1715,7 +1707,27 @@ s32 sub_GAME_7F0B5864(s32 portalnum, bbox2d *bbox) } else { - if ((bounds.max.x <= bounds.min.x) || (bounds.max.y <= bounds.min.y)) + s32 degenerate = (bounds.max.x <= bounds.min.x) || (bounds.max.y <= bounds.min.y); +#ifdef PORT + /* D106: a portal straddling the camera near-plane feeds z==0 clip + * points into transform3Dto2DWithZScaling, whose inv_z = -1e20 sends + * them to +/-1e20-scale screen coords. On N64 those bracket the view + * symmetrically so the box just clamps to full screen downstream; + * on PC the exact garbage can come back min>max on only one axis, or + * non-finite, slipping past the check above and culling the room the + * player is walking toward. Any wildly out-of-range / non-finite + * bound means "portal fills the view" -> full screensize. */ + { + const f32 lim = 100000.0f; + if (!(bounds.min.x > -lim && bounds.min.x < lim) || + !(bounds.min.y > -lim && bounds.min.y < lim) || + !(bounds.max.x > -lim && bounds.max.x < lim) || + !(bounds.max.y > -lim && bounds.max.y < lim)) { + degenerate = 1; + } + } +#endif + if (degenerate) { bounds.min.x = g_CurrentPlayer->screensize.min.x; bounds.min.y = g_CurrentPlayer->screensize.min.y;