D106: portal near-plane projection garbage no longer culls the next room

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.
This commit is contained in:
James Dansereau committed 2026-08-28 21:43:24 -04:00
1 parent 5a5c73de56
commit ad220f4e3d
1 file changed
+37 -25
+37 -25
View File
@@ -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;