D334: native widescreen, world side (Option B Phase 2) — Video.NativeWidescreen

Generalise GE's own N64 16:9 mode (aspect = vp_ratio*0.75*16/9, the TV
pre-squash) to the real window aspect: the port's canvas->window stretch is
that TV, so the world renders undistorted at any aspect (Hor+, vertical FOV
kept). All of GE's 16:9 special cases generalised under #ifdef PORT (camera
aspect NTSC+EU, crosshair sprite width, rocket viewmodel offset); Rule-2
approved. Phase-4 vertical FOV boost is skipped while native is on (would
double-count); room-pool aspect term kept. Default on; F10 row.

4:3 identity (golden 3/3); native 16:9 centre == 4:3 frame (diff 4.3 vs
12.4 stretched); 21:9 clean on Bunker/Facility/Dam; Win + Linux builds.
HUD still stretched (Phase 3 next).
This commit is contained in:
James Dansereau committed 2026-09-23 14:44:46 -04:00
1 parent 639a112b9b
commit d5d9a36a6f
6 files changed
+89 -2

No files matched your search

+1
View File
@@ -211,3 +211,4 @@ D330,FIXED,"#103 (v0.3.0, Win 11):","FIXED (port/src/input.c), build clean, bunk
D331,FIXED,Ejected shell casings never rendered on PC:,"FIXED (gunfire.c #ifdef PORT, explicit template); PP7 casing verified on native Windows (before:"
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.
1 # GENERATED by tools_pc/gen_findings_index.py -- do not hand-edit.
211 D331
212 D332
213 D333
214 D334
+9
View File
@@ -619,6 +619,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D331 | **Ejected shell casings never rendered on PC: `g_DefaultCasingModelRenderData` (u32[15] template) read as the wider PC `ModelRenderData` gave flags 0 (third instance of the D264 pattern; found by the D290 sibling sweep, never user-reported).** — full `## D331` entry at file tail | FIXED (gunfire.c `#ifdef PORT`, explicit template); PP7 casing verified on native Windows (before: none; after: brass casing ejects and tumbles); golden 3/3; Win + Linux builds clean. |
| 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. |
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
@@ -13243,3 +13244,11 @@ Implemented as `Input.PdMouseAim` (**default off**, because it changes aim feel)
**Verified:** Windows + Linux builds clean; bunker1 golden 3/3 (default style 0). Not verifiable headlessly: the scripted input source has no mouse, and the overlay row sits below the fold at every window size (it uses the same `ROW_ENUM` machinery as Texture filter). **Owed (user):** F10 → RMB aim style → CENTRED, hold RMB and move the mouse: the camera turns, the crosshair stays centred, and zoomed weapons turn slower. Also compare PD DAMP against N64 for D332's jitter fix.
## D334 — Native widescreen, world side (WIDESCREEN-FOV-PLAN Option B, Phase 2) (2026-09-23)
**Approach:** GE already ships an N64 16:9 mode (`bondviewMovePlayerUpdateViewport`, bondview2.c): `aspect = (vp_w/vp_h) × 0.75 × 16/9`, an anamorphic pre-squash that a 16:9 TV undid. The port's canvas-to-window mapping (fast3d `RATIO_X = win_w/320`) is exactly that TV, so native widescreen is the same formula with 16/9 replaced by the real window aspect, via `portNativeAspect()` (`port/src/video.c`, clamp [0.5, 4.0]). Vertical FOV stays the game's; horizontal widens (Hor+, the PD standard). Because the game's canvas, portal scissors, culling (`currentPlayerSetPerspective` gets the same aspect through `viSetAspect`) and AI off-screen tests all stay in the 320×240 logical space with one consistent aspect, the GEVR "two notions of view width" trap (plan §6) doesn't arise. That's the same reason the N64 16:9 mode worked.
**Game edits (`#ifdef PORT`, Rule-2 approved by the maintainer 2026-09-23), the complete set of GE's own 16:9 special cases, generalised:** (1) the camera aspect, NTSC and EU branches; the in-game "Ratio 16:9" option is ignored while native is on, since the window is the ratio; (2) the crosshair sprite half-width, where ×0.75 = (4/3)/(16/9) becomes (4/3)/aspect; (3) the rocket launcher's lowered viewmodel (`gunofs.y -= 3`) for aspect ≥ 1.55. **Port:** `Video.NativeWidescreen` (default 1, F10 "Native widescreen"); `portScaleFovY` skips the Phase-4 `sqrt(aspect/4:3)` vertical boost while native is on (it would double-count); `portRoomPoolScale` keeps its aspect term (the horizontal frustum still widens, D294).
**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).
+4
View File
@@ -127,6 +127,10 @@ static struct Row rows[] = {
{ "Video.TextureFilter", "Texture filter", ROW_ENUM, 1, kTexFilter, 0, 0, 0, 0,0,0,0,0 },
{ "Video.Anisotropy", "Anisotropic", ROW_SLIDER, 1, NULL, 0, 0, 0, 0,0,0,0,0 },
{ "Video.FovScale", "FOV scale %", ROW_SLIDER, 5, NULL, 0, 0, 0, 0,0,0,0,0 },
/* D334: native widescreen (world projected at the window aspect, Hor+).
* While on, "Widescreen auto FOV" has no effect (it was the stretch-era
* vertical-FOV compensation). */
{ "Video.NativeWidescreen", "Native widescreen",ROW_TOGGLE, 1, kOnOff, 0, 0, 0, 0,0,0,0,0 },
{ "Video.WidescreenAuto", "Widescreen auto FOV",ROW_TOGGLE,1, kOnOff, 0, 0, 0, 0,0,0,0,0 },
{ "Video.SafeAreaCrop", "Crop overscan bars", ROW_TOGGLE,1, kOnOff, 0, 0, 0, 0,0,0,0,0 },
{ "Video.DrawDistance", "Draw distance %", ROW_SLIDER, 25, NULL, 0, 0, 0, 0,0,0,0,0, "Video.DrawDistanceAutoFov" },
+35 -2
View File
@@ -62,6 +62,7 @@ static int cfgDetailBaseTile = 1; /* D236: TEXTURETYPE_DETAIL -> sample the bas
static int cfgWrapFix = 0; /* D74 sub-tile UV pre-wrap + RC3/D167 non-PoT mask-period wrap (opt-in; GE_WRAPFIX env overrides) */
static int cfgFovScale = 100; /* D211: percent of the original vertical FOV; 100 = unchanged (byte-identical) */
static int cfgWidescreenAuto = 1; /* WIDESCREEN-FOV-PLAN Phase 4: auto-scale vertical FOV by window aspect ratio; on by default, no-op at 4:3 */
static int cfgNativeWidescreen = 1; /* D334 (WIDESCREEN-FOV-PLAN Phase 2): project the world at the real window aspect (Hor+); no-op at 4:3 */
static int cfgDrawDistance = 150; /* D218: percent of the level's authored far-clip/fog distance. Default raised 100->150 for v0.2.0: at the authored N64 distance, props visibly fade in just before they become visible on modern displays (Dam alarms / wall switches); 150 is the value the Steam Deck preset playtest-validated. 100 = unchanged N64. */
static int cfgDrawDistanceAutoFov = 1; /* D218: couple draw distance to Video.FovScale unless DrawDistance is set explicitly */
static int cfgLodDistance = 150; /* D249: percent scale on the geometry/model LOD-swap distance. Default raised 100->150 for v0.2.0 (same pop-in family as DrawDistance: LOD-swapped props like Dam's alarms/wall switches faded in at range); 150 matches the Steam Deck preset. 100 = unchanged N64. */
@@ -143,10 +144,38 @@ f32 portFovScale = 1.0f;
* auto-scale formula can otherwise degenerate toward as aspect -> 0; this
* resolves WIDESCREEN-FOV-PLAN.md's open "horizontal extreme-aspect sanity
* clamp" question. */
/* D334 (WIDESCREEN-FOV-PLAN Option B, Phase 2): the world-projection aspect
* for native widescreen, or 0 when off. The game's own N64 16:9 mode
* (bondview2.c bondviewMovePlayerUpdateViewport) projects at
* viewport_ratio * 0.75 * 16/9 -- an anamorphic pre-squash the TV undid. The
* port already stretches the 4:3 logical canvas to the window (fast3d
* RATIO_X), exactly like that TV, so the same formula with 16/9 replaced by
* the real window aspect renders the world undistorted at any aspect: vertical
* FOV stays the game's, horizontal widens (Hor+, the PD standard). Canvas,
* portal scissors and culling all stay in the game's 320x240 logical space and
* see the same aspect, so there is no second "view width" to reconcile
* (WIDESCREEN-FOV-PLAN s6). Clamped to [0.5, 4.0]. At exactly 4:3 the
* formula is identity, so 4:3 renders are unchanged. */
f32 portNativeAspect(void)
{
f32 a = gfx_current_dimensions.aspect_ratio;
if (!cfgNativeWidescreen || a < 0.01f) {
return 0.0f;
}
if (a < 0.5f) { a = 0.5f; }
if (a > 4.0f) { a = 4.0f; }
return a;
}
f32 portScaleFovY(f32 fovy, s32 isTitleScreen)
{
if (!isTitleScreen) {
if (cfgWidescreenAuto && gfx_current_dimensions.aspect_ratio > 0.01f) {
/* D334: native widescreen already widens the horizontal FOV through
* the projection aspect; the Phase-4 vertical boost below was the
* stretch-era compensation and would double-count, so it is skipped
* (vertical FOV stays the game's, as in the PD port). */
if (cfgWidescreenAuto && !cfgNativeWidescreen &&
gfx_current_dimensions.aspect_ratio > 0.01f) {
fovy *= sqrtf(gfx_current_dimensions.aspect_ratio / (4.0f / 3.0f));
}
if (portFovScale > 0.4f && portFovScale < 2.01f && portFovScale != 1.0f) {
@@ -278,7 +307,10 @@ f32 portRoomPoolScale(void)
}
t = portFovScale;
if (cfgWidescreenAuto && gfx_current_dimensions.aspect_ratio > 0.01f) {
/* D334: native widescreen widens the horizontal frustum too (via the
* projection aspect instead of the vertical boost), so it admits the same
* extra side rooms and keeps the pool bump. */
if ((cfgWidescreenAuto || cfgNativeWidescreen) && gfx_current_dimensions.aspect_ratio > 0.01f) {
t *= sqrtf(gfx_current_dimensions.aspect_ratio / (4.0f / 3.0f));
}
if (t > scale) { scale = t; }
@@ -315,6 +347,7 @@ PD_CONSTRUCTOR static void videoConfigInit(void)
configRegisterInt("Video.WrapFix", &cfgWrapFix, 0, 1);
configRegisterInt("Video.FovScale", &cfgFovScale, 50, 150);
configRegisterInt("Video.WidescreenAuto", &cfgWidescreenAuto, 0, 1);
configRegisterInt("Video.NativeWidescreen", &cfgNativeWidescreen, 0, 1); /* D334 */
configRegisterInt("Video.DrawDistance", &cfgDrawDistance, 100, 400);
configRegisterInt("Video.DrawDistanceAutoFov", &cfgDrawDistanceAutoFov, 0, 1);
configRegisterInt("Video.LodDistance", &cfgLodDistance, 25, 400);
+21
View File
@@ -8216,6 +8216,14 @@ void bondviewMovePlayerUpdateViewport(s8 stick_x, s8 stick_y, u16 buttons)
}
#ifdef VERSION_EU
#ifdef PORT
extern f32 portNativeAspect(void); /* D334, see the NTSC branch below */
if (portNativeAspect() > 0.0f)
{
faspect = ((f32) bondviewGetCurrentPlayerViewportWidth() / (f32) bondviewGetCurrentPlayerViewportHeight()) * 0.75f * portNativeAspect();
}
else
#endif
if (get_screen_ratio() == SCREEN_RATIO_16_9)
{
faspect = ((f32) bondviewGetCurrentPlayerViewportWidth() / (f32) bondviewGetCurrentPlayerViewportHeight()) * 0.75f * WIDESCREEN_ASPECT;
@@ -8235,6 +8243,19 @@ void bondviewMovePlayerUpdateViewport(s8 stick_x, s8 stick_y, u16 buttons)
#else
#ifdef PORT
/* D334 (native widescreen, Rule-2 approved 2026-09-23): the N64 16:9 mode
* below with WIDESCREEN_ASPECT replaced by the real window aspect -- see
* portNativeAspect() (port/src/video.c). The in-game "Ratio 16:9" option
* is ignored while this is on (the window is the ratio). */
extern f32 portNativeAspect(void);
if (portNativeAspect() > 0.0f)
{
set_cur_player_aspect(((f32) bondviewGetCurrentPlayerViewportWidth() / (f32) bondviewGetCurrentPlayerViewportHeight()) * 0.75f * portNativeAspect());
viSetAspect(((f32) bondviewGetCurrentPlayerViewportWidth() / (f32) bondviewGetCurrentPlayerViewportHeight()) * 0.75f * portNativeAspect());
}
else
#endif
if (get_screen_ratio() == SCREEN_RATIO_16_9)
{
set_cur_player_aspect(((f32) bondviewGetCurrentPlayerViewportWidth() / (f32) bondviewGetCurrentPlayerViewportHeight()) * 0.75f * WIDESCREEN_ASPECT);
+19
View File
@@ -500,6 +500,16 @@ void gunUpdateAndFire(GUNHAND handnum)
gunofs.y += g_CurrentPlayer->ducking_height_offset / (-100.0f);
gunofs.z += (3.0f * g_CurrentPlayer->ducking_height_offset) / (-100.0f);
#ifdef PORT
/* D334: a native-widescreen window counts as the N64's 16:9 ratio for
* the rocket launcher's lowered viewmodel (>= 16:10 treated as wide). */
extern f32 portNativeAspect(void);
if ((item == ITEM_ROCKETLAUNCH) && portNativeAspect() >= 1.55f)
{
gunofs.y -= 3.0f;
}
else
#endif
if ((item == ITEM_ROCKETLAUNCH) && (((cur_player_get_screen_setting() == SCREEN_SIZE_WIDESCREEN) || (cur_player_get_screen_setting() == SCREEN_SIZE_CINEMA)) || (get_screen_ratio() == SCREEN_RATIO_16_9)))
{
gunofs.y -= 3.0f;
@@ -6476,6 +6486,15 @@ void gunDrawSight(s32 *gdl) {
halfedxy[0] = 16.0f;
halfedxy[1] = 16.0f;
#ifdef PORT
/* D334: native widescreen generalises the N64 16:9 sprite fix
* (x0.75 = (4/3)/(16/9)) to the window aspect, so the crosshair
* sprite isn't stretched by the canvas-to-window mapping. */
extern f32 portNativeAspect(void);
if (portNativeAspect() > 0.0f) {
halfedxy[0] = halfedxy[0] * ((4.0f / 3.0f) / portNativeAspect());
} else
#endif
if (get_screen_ratio() == SCREEN_RATIO_16_9) {
halfedxy[0] = halfedxy[0] * 0.75f;
}