D335: native widescreen, HUD + front end (Option B Phase 3)

HUD elements now keep 4:3 proportions via fast3d's EXT aspect modes (the PD
pattern): watch, gauge bars, messages/title card, upper text and countdown
CENTER; ammo RIGHT (right hand) / LEFT (left hand). The front end is
pillarboxed (whole menu_jump_constructor_handler CENTER) and the menu
pointer is mapped into the pillarbox. port/include/hudaspect.h emits only
when native widescreen is on and the window is wider than 4:3, so 4:3 and
native-off stay byte-identical. Rule-2 approved.

Verified with 16:9/21:9 captures (HUD, watch, title card, legal, file
select, gun barrel); golden 3/3; Win + Linux builds. Gun-barrel circle
leaks into the bars while sliding in -> GRAPHICS-BACKLOG D335a.
This commit is contained in:
James Dansereau committed 2026-09-23 14:56:29 -04:00
1 parent d5d9a36a6f
commit f62bceaf88
8 files changed
+144

No files matched your search

+1
View File
@@ -35,6 +35,7 @@ once the game is playable start-to-finish without crashes.
| **D195** | Transparency / alpha-blended surfaces render wrong — reported on **Control** (Severnaya bunker basement — Natalya's console room; heavy on glass partitions and the translucent projected/holographic wall displays). Exact failure mode not yet pinned by the reporter (M-49 playtest): candidates are alpha-blended geometry drawing fully opaque, translucent panels drawing invisible, or draw-order/sort so translucent surfaces occlude what's behind. | **Observed, not investigated** (M-49, user bug report — needs a `GE_PCDUMP` capture + which surfaces). fast3d render-mode / combiner path. Same neighborhood as D161 (CI8 tile + `G_TT_NONE` → garbage), D172 (particle-sprite channel order), D176(b) (texture pitch shear) — but this is a *blend/alpha* defect, not a texture-decode one. Note D128 already touched Control's `specialportalarray` (portal struct stride) — check whether the affected surfaces are portal-adjacent. First checks: does `gfx_pc.cpp` honour `G_RM_*` XLU render modes + `G_AC_THRESHOLD`/`G_CC` alpha for these DLs; is `gDPSetRenderMode` `w1` being parsed (cf. findings §F "zbuf-clear w1 scribble"); is Z-write-vs-blend ordering right for XLU surfaces. Get the reporter to say: opaque-where-should-be-clear, invisible, or wrong edges? |
| **D252** | Particle effects cycle through a rainbow palette — bullet-impact sparks and lingering smoke/explosion residue drift through the full hue wheel over time (~10 shots to sweep it) instead of holding their intended grey/orange palette (M-124, user playtest). Full mechanism + suspect in findings §F **D252**: colour comes from 15 distinct `IMAGE_SMOKE_N`×`IMAGE_FIRE_N` (RGBA16) pairs the particles age through; prime suspect is `import_texture_rgba16` deriving width/height from the tile line size (32×7 instead of 16×14 for the fire tiles), scrambling each stage's sprite into a different wrong hue. | **Documented only — not fixed for v0.2.0-pre** (user instruction). Confirm with one dumped fire tile vs raw `IMAGE_FIRE_N` bytes, then derive width/height from the LOADBLOCK dtx/dlh. D172/M-82 lineage. |
| **D323** | Distant geometry on 15 levels samples driver-generated mips from the finest texture level; N64 samples Rare's hand-authored mip chain for explicit-LOD textures bound under `G_TL_LOD` (heaviest: Statue 378 binds, Aztec 806). Overwhelmingly CI8/IA8 — subtle fidelity gap, no reported artifact; the only RGBA5551 cut-outs are the D236 set (already handled). | **OPEN / parked** — mechanism confirmed, census in `tools_pc/bg_lod_audit.py`, fix path documented in findings §F **D323** (upload GL mip chains for TEXTLOD-on bindings). Not worth doing speculatively; revisit only if a playtest flags mushy distant detail. |
| **D335a** | Native widescreen, front end: during the gun-barrel intro, Bond's white circle (and briefly the barrel dots) draw into the pillarbox bars while sliding in from off-canvas; the spiral itself clips correctly at the 4:3 edge. That element isn't under the spiral's scissor, so off-canvas logical x (>320) lands in the bar. Transient (~1 s), cosmetic. Fix idea: a full-canvas scissor at the top of `menu_jump_constructor_handler` under the CENTER wrap, or port-side bar masking after a pillarboxed frame. |
| **1P weapon viewmodel** | **RESOLVED (M-83, D215).** In-level first-person weapon models drew nothing. Root cause: `gunRenderFirstPersonGunModels` built its `ModelRenderData` via `*(ModelRenderData*)&D_80035CC0` — a reinterpret across two separately-declared adjacent globals that only lands right on the N64 linker + 4-byte pointers; on x86-64 `renderdata.flags` read 0, gating every weapon DL node off in `modelRenderNodeGundl`. All 3 ranked suspects in VIEWMODEL-RESEARCH.md were ruled out first by the `GE_DVM=1` probe. Fixed `#ifdef PORT` (explicit field init). `subdraw` 1→153 gfx cmds; owed one BUNKER1 eyeball. |
## Resolved (for reference)
+1
View File
@@ -212,3 +212,4 @@ D331,FIXED,Ejected shell casings never rendered on PC:,"FIXED (gunfire.c #ifdef
D332,FIXED,Reticule jitter in aim mode (RMB) —,FIXED (fix/mouse-pd-model) —
D333,OPEN,"#104 (v0.3.0, Linux):","IMPLEMENTED (port/src/input.c + optionsoverlay.c, port-only); default N64 = unchanged; build + golden clean; feel check owed."
D334,?,"Native widescreen, world side (WIDESCREEN-FOV-PLAN Option B Phase 2):",IMPLEMENTED (Rule-2 approved 2026-09-23); 4:3 identity (golden 3/3); native 16:9 centre matches the 4:3 frame (mean diff 4.3 vs 12.4 stretched); 21:9 clean on Bunker/Facility/Dam.
D335,?,"Native widescreen, HUD + front end (WIDESCREEN-FOV-PLAN Phase 3):","IMPLEMENTED (Rule-2 approved 2026-09-23); 4:3 byte-identical (no emission at ≤4:3); verified at 16:9/21:9 on HUD, watch, title card, legal/file-select/gun-barrel."
1 # GENERATED by tools_pc/gen_findings_index.py -- do not hand-edit.
212 D332
213 D333
214 D334
215 D335
+9
View File
@@ -620,6 +620,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D332 | **Reticule jitter in aim mode (RMB) — ROOT-CAUSED from live captures; fixed by adopting the Perfect Dark port's mouse-aim model behind `Input.PdMouseAim`.** The game's damped crosshair update runs once per SIM TICK while the port's GEPD overwrite runs once per input POLL, so the value displayed is `write * damp^k` with k varying (k=1 66%, k=2 18%, k=0 17% of frames). At damp 0.8 that stepped the drawn reticule 20% whenever k changed | **FIXED (`fix/mouse-pd-model`)** — `Input.PdMouseAim` (default off, live via `GE_PDMOUSEAim`): the port accumulates only and the game's integrator is driven by the port-supplied turn with PD's near-zero damp (0.01), via a `#ifdef PORT` hook in `sub_GAME_7F067FBC`. Two earlier fixes were tried and reverted (see the `## D332` entry). **Awaiting feel-test.** |
| D333 | **#104 (v0.3.0, Linux): FPS-style centred aim on RMB, delivered as `Input.AimStyle` (F10 "RMB aim style": N64 / PD DAMP / CENTRED), folding in D332's toggle.** — full `## D333` entry at file tail | IMPLEMENTED (port/src/input.c + optionsoverlay.c, port-only); default N64 = unchanged; build + golden clean; feel check owed. |
| D334 | **Native widescreen, world side (WIDESCREEN-FOV-PLAN Option B Phase 2): the world is projected at the real window aspect (Hor+) by generalising GE's own N64 16:9 mode; `Video.NativeWidescreen` (default on).** — full `## D334` entry at file tail | IMPLEMENTED (Rule-2 approved 2026-09-23); 4:3 identity (golden 3/3); native 16:9 centre matches the 4:3 frame (mean diff 4.3 vs 12.4 stretched); 21:9 clean on Bunker/Facility/Dam. HUD still stretched → Phase 3. |
| D335 | **Native widescreen, HUD + front end (WIDESCREEN-FOV-PLAN Phase 3): HUD elements anchored left/right/centre and the front end pillarboxed via the fast3d EXT aspect modes (PD pattern), `port/include/hudaspect.h`.** — full `## D335` entry at file tail | IMPLEMENTED (Rule-2 approved 2026-09-23); 4:3 byte-identical (no emission at ≤4:3); verified at 16:9/21:9 on HUD, watch, title card, legal/file-select/gun-barrel. Menu pointer mapped into the pillarbox. One cosmetic leak → GRAPHICS-BACKLOG D335a. |
Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS
threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software
@@ -13252,3 +13253,11 @@ Implemented as `Input.PdMouseAim` (**default off**, because it changes aim feel)
**Verified (native Windows, headless):** 640×480 golden 3/3, so 4:3 is identity by construction (aspect 4/3 → ×1). Quantitative check: the centre 960 columns of a native 1280×720 frame vs a 960×720 (4:3) frame of the same moment differ by mean 4.3 (run-to-run noise), against 12.4 for the old stretched+boosted mode squeezed back. 21:9 (1680×720): Bunker intro + gameplay, Facility intro room and Dam gorge all render edge to edge with no scissor slabs, missing rooms or sky gaps. Windows + Linux builds clean. **Not yet native:** HUD, text and 2D overlays (level title card, ammo counter, watch) are still stretched with the canvas. That's Phase 3 (EXT-aspect wraps). The front-end menus' fixed 3D (`viSetAspect(ASPECT_RATIO_SD)` in front.c) is still stretched too. **Owed (user):** feel check at 16:9/21:9 (aim zoom, rocket launcher, cutscenes).
## D335 — Native widescreen, HUD + front end (WIDESCREEN-FOV-PLAN Option B, Phase 3) (2026-09-23)
**Mechanism:** fast3d already decodes PD's `G_EXTRAGEOMETRYMODE_EXT` aspect modes (`gfx_update_aspect_mode`). Under a mode, clip-space vertex x (`gfx_pc.cpp` vertex path, so 3D-projected HUD too) and rectangle x are scaled by `(4/3)/window_aspect` and anchored LEFT, RIGHT or CENTER; scissors are adjusted to match. `port/include/hudaspect.h` (game-includable) adds `gSPHudAspectEXT` (via `gDma0p`; the decoder clears `~(w0 & 0xFFFFFF)`, so passing `~MASK` swaps only the aspect bits) and `PORT_HUD_ASPECT(gdl, mode)`. That emits only while native widescreen is on **and** the window is wider than 4:3. At ≤ 4:3 nothing is emitted (byte-identical); narrower windows keep the squeezed canvas mapping rather than cropping.
**Wraps (`#ifdef PORT`, Rule-2 approved):** gameplay HUD in `maybe_mp_interface` (bondview2.c): watch (`bondviewRenderWatch`), gauge bars (both sites), bottom messages / level title card (`hudmsgBottomRender`, both camera modes), upper text window (`sub_GAME_7F08AAE8`) and countdown timer, all CENTER. Ammo (`generate_ammo_total_microcode`, gunfire.c): right-hand block RIGHT, left-hand (dual-wield) block LEFT. Front end: all of `menu_jump_constructor_handler` (front.c) CENTER, i.e. pillarboxed 4:3, and `input.c` maps the absolute menu pointer across the centred region instead of the window. **Deliberately unwrapped:** the crosshair (its position lives in the world-projection space; only its sprite width is corrected, D334), full-screen fades, blood and damage overlays (should cover the window), and the world.
**Verified (native Windows, headless captures):** 21:9 gameplay: ammo counter right-anchored and undistorted; title card undistorted; watch round, inventory and control-options pages (incl. the D290 pad) undistorted. 16:9 front end: legal screen and file select pillarboxed and undistorted; gun-barrel spiral clips at the 4:3 edge. 640×480 golden 3/3; Windows + Linux builds clean. **Known cosmetic:** gun-barrel circle leaks into the bars while sliding in (GRAPHICS-BACKLOG D335a). **Not yet wrapped (next pass, needs live checks):** end credits (`bondviewRenderCredits`), sniper/zoom overlays inside `gunDrawSight`, MP radar (MP out of scope). **Owed (user):** full HUD pass at 16:9 and 21:9 in play (pickup messages, objectives, health bars, dual-wield ammo, mission complete/failed screens, menu pointer accuracy).
+38
View File
@@ -0,0 +1,38 @@
#ifndef _HUDASPECT_H_
#define _HUDASPECT_H_
/* D335 (WIDESCREEN-FOV-PLAN Option B, Phase 3): HUD alignment under native
* widescreen (D334). The world is projected at the window aspect, but 2D/HUD
* elements are laid out in the game's 4:3 logical canvas, which fast3d
* stretches to the window. Wrapping a HUD draw in an aspect mode makes fast3d
* map its x undistorted (x scaled by (4/3)/window_aspect) and anchor it to
* the left edge, the right edge, or the centre -- the PD port's
* G_ASPECT_{LEFT,RIGHT,CENTER}_EXT extra-geometry-mode, which GE's fast3d
* already decodes (gfx_pc.cpp gfx_sp_extra_geometry_mode /
* gfx_update_aspect_mode). Game-includable: no SDL, no fast3d headers.
*
* PORT_HUD_ASPECT emits only while native widescreen is on and the window is
* wider than 4:3; otherwise the display list is byte-identical to before. */
#define GE_HUD_ASPECT_NONE 0x00u
#define GE_HUD_ASPECT_LEFT 0x10u /* G_ASPECT_LEFT_EXT (port/fast3d/gbiex.h) */
#define GE_HUD_ASPECT_RIGHT 0x20u /* G_ASPECT_RIGHT_EXT */
#define GE_HUD_ASPECT_CENTER 0x30u /* G_ASPECT_CENTER_EXT */
#define GE_HUD_ASPECT_MASK 0x70u /* G_ASPECT_MODE_EXT */
#define GE_EXTRAGEOMETRYMODE_EXT 0x3a /* G_EXTRAGEOMETRYMODE_EXT */
/* The decoder does extra_mode = (extra_mode & ~clear) | set with
* clear = ~(w0 & 0xFFFFFF): passing ~MASK as the low 24 bits clears exactly
* the aspect bits and leaves the other extra-geometry flags alone. */
#define gSPHudAspectEXT(pkt, mode) \
gDma0p((pkt), GE_EXTRAGEOMETRYMODE_EXT, (mode), ((~GE_HUD_ASPECT_MASK) & 0xFFFFFFu))
extern float portNativeAspect(void); /* port/src/video.c */
/* Only for windows wider than 4:3: in a narrower window a centred 4:3 HUD
* would be wider than the window and crop, so there the HUD keeps the canvas
* mapping (squeezed, never cropped). 1.3334 so exact 4:3 emits nothing. */
#define PORT_HUD_ASPECT(gdl, mode) \
do { if (portNativeAspect() > 1.3334f) { gSPHudAspectEXT((gdl)++, (mode)); } } while (0)
#endif
+12
View File
@@ -1052,6 +1052,18 @@ unsigned inputComputePad(int idx, signed char *stick_x, signed char *stick_y)
}
if (mx != lastMenuMouseX || my != lastMenuMouseY) {
double fx = (double)mx / (double)ww;
/* D335: under native widescreen the front end is
* pillarboxed to a centred 4:3 region
* (front.c menu_jump_constructor_handler), so map
* the pointer across that region, not the window. */
{
extern f32 portNativeAspect(void);
f32 na = portNativeAspect();
if (na > (4.0f / 3.0f)) {
double vis = (4.0 / 3.0) / (double)na; /* visible width fraction */
fx = (fx - (1.0 - vis) * 0.5) / vis;
}
}
double fy = (double)my / (double)wh;
if (fx < 0.0) fx = 0.0; else if (fx > 1.0) fx = 1.0;
if (fy < 0.0) fy = 0.0; else if (fy > 1.0) fy = 1.0;
+57
View File
@@ -50,6 +50,9 @@
#include "textrelated.h"
#ifdef PORT
#include "envflag.h" /* cached getenv for hot-path probes */
#ifdef PORT
#include "hudaspect.h" /* D335 HUD alignment under native widescreen */
#endif
#endif
#ifdef VERSION_EU
@@ -9273,10 +9276,28 @@ Gfx *maybe_mp_interface(Gfx *gdl)
if (g_CurrentPlayer->cameramode == 1)
{
bondviewIntroCameraTextTick();
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = hudmsgBottomRender(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
bondviewUpperTextWindowTimerTick();
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = sub_GAME_7F08AAE8(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = countdownTimerRender(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
gdl = currentPlayerDrawFade(gdl);
return bondviewRenderCredits(gdl);
}
@@ -9284,7 +9305,13 @@ Gfx *maybe_mp_interface(Gfx *gdl)
gunUpdateAndFireBothHands();
gunRenderCasings(&gdl);
gunRenderFirstPersonGunModels(&gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = bondviewRenderWatch(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
if (g_CurrentPlayer->mpmenuon != 0)
{
@@ -9299,11 +9326,23 @@ Gfx *maybe_mp_interface(Gfx *gdl)
if (bondviewGetIfCurrentPlayerHealthShowTime() &&
(g_CurrentPlayer->watch_animation_state == 0))
{
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = bondviewRenderGaugeBars(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
}
else if (mpwatchShouldDisplayGauges())
{
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = bondviewRenderGaugeBars(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
if (g_CurrentPlayer->healthdisplaytime > 0)
{
g_CurrentPlayer->healthdisplaytime -= g_ClockTimer;
@@ -9412,12 +9451,30 @@ Gfx *maybe_mp_interface(Gfx *gdl)
}
bondviewIntroCameraTextTick();
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = hudmsgBottomRender(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
bondviewUpperTextWindowTimerTick();
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = sub_GAME_7F08AAE8(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
gunDrawSight(&gdl);
gdl = generate_ammo_total_microcode(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
#endif
gdl = countdownTimerRender(gdl);
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
gdl = display_red_blue_on_radar(gdl);
return currentPlayerDrawFade(gdl);
}
+12
View File
@@ -49,6 +49,9 @@
#include "ob.h"
#include "gbi_extension.h"
#include "model.h"
#ifdef PORT
#include "hudaspect.h" /* D335 front-end pillarbox under native widescreen */
#endif
/**
@@ -8855,6 +8858,12 @@ void menu_init(void)
Gfx * menu_jump_constructor_handler(Gfx *DL)
{
#ifdef PORT
/* D335: under native widescreen the front end is 4:3 logical content;
* pillarbox it (centred, undistorted) instead of stretching it to the
* window. input.c maps the menu pointer into the same centred region. */
PORT_HUD_ASPECT(DL, GE_HUD_ASPECT_CENTER);
#endif
switch(current_menu) {
case MENU_LEGAL_SCREEN:
DL = constructor_menu00_legalscreen(DL);
@@ -8932,6 +8941,9 @@ Gfx * menu_jump_constructor_handler(Gfx *DL)
DL = constructor_menu19_spectrum(DL);
}
#ifdef PORT
PORT_HUD_ASPECT(DL, GE_HUD_ASPECT_NONE);
#endif
return DL;
}
+14
View File
@@ -40,6 +40,9 @@
#ifdef PORT
#include <stdlib.h>
#include <stdio.h>
#ifdef PORT
#include "hudaspect.h" /* D335 HUD alignment under native widescreen */
#endif
/* D102: the 1P weapon Model and its RW-data pool were punned onto
* hand->field_B68 / hand->modeldatas; on x86-64 struct Model (0xE8) is too
* big for that layout and modelInit() aliases objinst->datas onto the pool
@@ -6216,6 +6219,10 @@ Gfx *generate_ammo_total_microcode(Gfx *gdl)
rightx = 109;
}
#ifdef PORT
/* D335: right-hand ammo anchors to the right edge (native widescreen). */
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_RIGHT);
#endif
if (weapon_right != ITEM_UNARMED)
{
ammotype = get_ammo_type_for_weapon(weapon_right);
@@ -6282,6 +6289,10 @@ Gfx *generate_ammo_total_microcode(Gfx *gdl)
}
}
#ifdef PORT
/* D335: left-hand (dual-wield) ammo anchors to the left edge. */
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_LEFT);
#endif
if (weapon_left != ITEM_UNARMED)
{
ammotype = get_ammo_type_for_weapon(weapon_left);
@@ -6350,6 +6361,9 @@ Gfx *generate_ammo_total_microcode(Gfx *gdl)
}
}
#ifdef PORT
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
#endif
return gdl;
}