mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
D226: HUD scale option (Game.HudScale 75-200%) — fast3d G_HUDSCALE_EXT rect scaling about per-element anchors
Ammo (per-hand corner), bottom messages/title card (bottom-left) and top dialogue (text origin) scale with crisp glyphs; dialogue and bottom messages are width-capped because their line breaks are authored. 100% emits nothing (golden 3/3). Captures verified at 150/200%; Win + Linux.
This commit is contained in:
1 parent
0de97b9c67
commit
3072035417
6 files changed
+145
No files matched your search
@@ -3384,8 +3384,36 @@ static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lr
|
||||
}
|
||||
}
|
||||
|
||||
/* D226: HUD span scale (G_HUDSCALE_EXT, emitted by port/include/hudaspect.h
|
||||
* PORT_HUD_SCALE around specific HUD draws). While active, every rectangle
|
||||
* (text glyphs, message boxes, ammo icons are all rects) is scaled about the
|
||||
* anchor in logical 10.2 screen space, and texrect steps are divided by the
|
||||
* scale so the same texels cover the larger/smaller rect. 1.0 = inactive. */
|
||||
static float s_hud_scale = 1.0f;
|
||||
static int32_t s_hud_ax = 0, s_hud_ay = 0; /* anchor, 10.2 fixed (px*4) */
|
||||
|
||||
static inline void gfx_hud_scale_rect(int32_t& ulx, int32_t& uly, int32_t& lrx, int32_t& lry) {
|
||||
if (s_hud_scale == 1.0f) {
|
||||
return;
|
||||
}
|
||||
ulx = s_hud_ax + (int32_t)lroundf((float)(ulx - s_hud_ax) * s_hud_scale);
|
||||
lrx = s_hud_ax + (int32_t)lroundf((float)(lrx - s_hud_ax) * s_hud_scale);
|
||||
uly = s_hud_ay + (int32_t)lroundf((float)(uly - s_hud_ay) * s_hud_scale);
|
||||
lry = s_hud_ay + (int32_t)lroundf((float)(lry - s_hud_ay) * s_hud_scale);
|
||||
/* A full-width box (e.g. the dialogue backdrop, 0..W) scaled about a
|
||||
* centre anchor goes negative; negative rect coords are not drawn, so
|
||||
* clip at the canvas origin (the right/bottom overhang is harmless). */
|
||||
if (ulx < 0) ulx = 0;
|
||||
if (uly < 0) uly = 0;
|
||||
}
|
||||
|
||||
static void gfx_dp_texture_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry, uint8_t tile, int16_t uls,
|
||||
int16_t ult, int16_t dsdx, int16_t dtdy, bool flip) {
|
||||
if (s_hud_scale != 1.0f) { /* D226 */
|
||||
gfx_hud_scale_rect(ulx, uly, lrx, lry);
|
||||
dsdx = (int16_t)lroundf((float)dsdx / s_hud_scale);
|
||||
dtdy = (int16_t)lroundf((float)dtdy / s_hud_scale);
|
||||
}
|
||||
uint64_t saved_combine_mode = rdp.combine_mode;
|
||||
if ((rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_COPY) {
|
||||
// Per RDP Command Summary Set Tile's shift s and this dsdx should be set to 4 texels
|
||||
@@ -3509,6 +3537,7 @@ static void gfx_dp_image_rectangle(int32_t tile, int32_t w, int32_t h,
|
||||
}
|
||||
|
||||
static void gfx_dp_fill_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) {
|
||||
gfx_hud_scale_rect(ulx, uly, lrx, lry); /* D226 */
|
||||
if (rdp.color_image_address == rdp.z_buf_address) {
|
||||
// Don't clear Z buffer here since we already did it with glClear
|
||||
return;
|
||||
@@ -3698,6 +3727,14 @@ static void gfx_run_dl(Gfx* cmd) {
|
||||
case (uint8_t)G_CLEARGEOMETRYMODE:
|
||||
gfx_sp_geometry_mode(cmd->words.w1, 0);
|
||||
break;
|
||||
case 0x46: /* G_HUDSCALE_EXT (D226): w0 low16 = scale*256 (0/256 = off), w1 = ax4<<16 | ay4 */
|
||||
{
|
||||
const uint32_t sc = C0(0, 16);
|
||||
s_hud_scale = (sc == 0 || sc == 256) ? 1.0f : (float)sc / 256.0f;
|
||||
s_hud_ax = (int32_t)C1(16, 16);
|
||||
s_hud_ay = (int32_t)C1(0, 16);
|
||||
break;
|
||||
}
|
||||
case G_EXTRAGEOMETRYMODE_EXT:
|
||||
gfx_sp_extra_geometry_mode(~C0(0, 24), cmd->words.w1);
|
||||
break;
|
||||
@@ -4091,6 +4128,7 @@ static double s_perf_pre = 0, s_perf_post = 0, s_perf_swap = 0;
|
||||
static uint64_t s_perf_frames = 0, s_perf_last_start = 0;
|
||||
|
||||
extern "C" void gfx_run(Gfx* commands) {
|
||||
s_hud_scale = 1.0f; /* D226: never carry a HUD scale across frames */
|
||||
const uint64_t perf_t0 = gfx_perfstat_on() ? gfx_perf_now_ns() : 0;
|
||||
++num_dls;
|
||||
gfx_sp_reset();
|
||||
|
||||
@@ -35,4 +35,15 @@ extern float portNativeAspect(void); /* port/src/video.c */
|
||||
#define PORT_HUD_ASPECT(gdl, mode) \
|
||||
do { if (portNativeAspect() > 1.3334f) { gSPHudAspectEXT((gdl)++, (mode)); } } while (0)
|
||||
|
||||
/* D226: HUD span scale. PORT_HUD_SCALE(gdl, ax, ay) scales every rectangle
|
||||
* drawn after it (glyphs, message boxes, ammo icons) about the anchor
|
||||
* (logical screen px) by Game.HudScale %, until PORT_HUD_SCALE_END. At 100%
|
||||
* nothing is emitted (byte-identical). Decoded as G_HUDSCALE_EXT in fast3d. */
|
||||
#define GE_HUDSCALE_EXT 0x46
|
||||
extern s32 portHudScalePercent(void); /* port/src/video.c */
|
||||
#define gSPHudScaleEXT(pkt, scale256, ax4, ay4) gDma0p((pkt), GE_HUDSCALE_EXT, ((u32)(ax4) << 16) | ((u32)(ay4) & 0xFFFFu), ((u32)(scale256) & 0xFFFFu))
|
||||
#define PORT_HUD_SCALE(gdl, ax, ay) do { s32 _hp = portHudScalePercent(); if (_hp != 100) { gSPHudScaleEXT((gdl)++, _hp * 256 / 100, (ax) * 4, (ay) * 4); } } while (0)
|
||||
#define PORT_HUD_SCALE_PCT(gdl, pct, ax, ay) do { s32 _hp = (pct); if (_hp != 100) { gSPHudScaleEXT((gdl)++, _hp * 256 / 100, (ax) * 4, (ay) * 4); } } while (0)
|
||||
#define PORT_HUD_SCALE_END(gdl) do { if (portHudScalePercent() != 100) { gSPHudScaleEXT((gdl)++, 256, 0, 0); } } while (0)
|
||||
|
||||
#endif
|
||||
@@ -203,6 +203,9 @@ static struct Row rows[] = {
|
||||
/* D232: the community "no damage flash" toggle (suppresses the red/green
|
||||
* hit-flash overlay in bondview2). */
|
||||
{ "__HdrGame", "GAME", ROW_HEADER, 0, NULL, 0, 0, 0, 0,0,0,0,0 },
|
||||
/* D226: scales the ammo counter, pickup / status messages and dialogue
|
||||
* about their screen anchors. 100% = original, nothing emitted. */
|
||||
{ "Game.HudScale", "HUD scale %", ROW_SLIDER, 5, NULL, 0, 0, 0, 0,0,0,0,0 },
|
||||
{ "Game.SkipIntro", "Skip intro", ROW_TOGGLE, 1, kOnOff, 0, 0, 0, 0,0,0,0,0 },
|
||||
{ "Video.DisplayFPS", "Show FPS", ROW_TOGGLE, 1, kOnOff, 0, 0, 0, 0,0,0,0,0 },
|
||||
{ "Game.NoHitFlash", "No hit flash", ROW_TOGGLE, 1, kOnOff, 0, 0, 0, 0,0,0,0,0 },
|
||||
|
||||
@@ -63,6 +63,7 @@ static int cfgWrapFix = 0; /* D74 sub-tile UV pre-wrap + RC3/D167 non-Po
|
||||
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 cfgHudScale = 100; /* D226: HUD text/ammo scale %, 100 = original (no emission) */
|
||||
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. */
|
||||
@@ -156,6 +157,12 @@ f32 portFovScale = 1.0f;
|
||||
* 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. */
|
||||
/* D226: Game.HudScale percent (75..200); 100 = original size, nothing emitted. */
|
||||
s32 portHudScalePercent(void)
|
||||
{
|
||||
return cfgHudScale;
|
||||
}
|
||||
|
||||
f32 portNativeAspect(void)
|
||||
{
|
||||
f32 a = gfx_current_dimensions.aspect_ratio;
|
||||
@@ -348,6 +355,7 @@ PD_CONSTRUCTOR static void videoConfigInit(void)
|
||||
configRegisterInt("Video.FovScale", &cfgFovScale, 50, 150);
|
||||
configRegisterInt("Video.WidescreenAuto", &cfgWidescreenAuto, 0, 1);
|
||||
configRegisterInt("Video.NativeWidescreen", &cfgNativeWidescreen, 0, 1); /* D334 */
|
||||
configRegisterInt("Game.HudScale", &cfgHudScale, 75, 200); /* D226 */
|
||||
configRegisterInt("Video.DrawDistance", &cfgDrawDistance, 100, 400);
|
||||
configRegisterInt("Video.DrawDistanceAutoFov", &cfgDrawDistanceAutoFov, 0, 1);
|
||||
configRegisterInt("Video.LodDistance", &cfgLodDistance, 25, 400);
|
||||
|
||||
@@ -9260,6 +9260,63 @@ Gfx *bondviewRenderCredits(Gfx *gdl)
|
||||
}
|
||||
|
||||
|
||||
#ifdef PORT
|
||||
/* D226: the top dialogue's line breaks are authored into the text (no runtime
|
||||
* wrap), so an unconditional HUD scale pushes long lines off-screen. Scale it
|
||||
* as far as the widest line still fits between the text origin (view left +
|
||||
* 0x1e, msg.x) and the same margin on the right. */
|
||||
static s32 portDialogueHudScalePercent(void)
|
||||
{
|
||||
s32 hp = portHudScalePercent();
|
||||
s32 th = 0, tw = 0;
|
||||
s32 avail;
|
||||
char *text;
|
||||
|
||||
if (hp <= 100) {
|
||||
return hp;
|
||||
}
|
||||
#if defined(LEFTOVERDEBUG)
|
||||
text = stringbuffer_top[upper_text_buffer_index];
|
||||
#else
|
||||
text = dword_CODE_bss_80079DC8[upper_text_buffer_index];
|
||||
#endif
|
||||
if (text == NULL || text[0] == '\0') {
|
||||
return hp;
|
||||
}
|
||||
textMeasure(&th, &tw, text, ptrFontZurichBoldChars, ptrFontZurichBold, 0);
|
||||
avail = viGetViewWidth() - 2 * 0x1e;
|
||||
if (tw > 0 && tw * hp > avail * 100) {
|
||||
hp = (avail * 100) / tw;
|
||||
if (hp < 100) hp = 100;
|
||||
}
|
||||
return hp;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PORT
|
||||
/* D226: same width cap for the lower-left pickup / status message (and the
|
||||
* level title card), measured with the fonts the bottom renderer uses. */
|
||||
static s32 portBottomHudScalePercent(void)
|
||||
{
|
||||
s32 hp = portHudScalePercent();
|
||||
s32 th = 0, tw = 0;
|
||||
s32 avail;
|
||||
char *text = stringbuffer_lowerleft[status_bar_text_buffer_index];
|
||||
|
||||
if (hp <= 100 || text[0] == 0) {
|
||||
return hp;
|
||||
}
|
||||
textMeasure(&th, &tw, text, BONDVIEW_2ND_FONTTABLE(status_bar_text_buffer_index),
|
||||
BONDVIEW_1ST_FONTTABLE(status_bar_text_buffer_index), 0);
|
||||
avail = viGetViewWidth() - 2 * 0x1e;
|
||||
if (tw > 0 && tw * hp > avail * 100) {
|
||||
hp = (avail * 100) / tw;
|
||||
if (hp < 100) hp = 100;
|
||||
}
|
||||
return hp;
|
||||
}
|
||||
#endif
|
||||
|
||||
Gfx *maybe_mp_interface(Gfx *gdl)
|
||||
{
|
||||
s32 ulx;
|
||||
@@ -9278,16 +9335,28 @@ Gfx *maybe_mp_interface(Gfx *gdl)
|
||||
bondviewIntroCameraTextTick();
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_PCT(gdl, portBottomHudScalePercent(), viGetViewLeft(), viGetViewTop() + viGetViewHeight()); /* D226: bottom messages, width-capped */
|
||||
#endif
|
||||
gdl = hudmsgBottomRender(gdl);
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_END(gdl);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
|
||||
#endif
|
||||
bondviewUpperTextWindowTimerTick();
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_PCT(gdl, portDialogueHudScalePercent(), viGetViewLeft() + 0x1e, viGetViewTop()); /* D226: dialogue, width-capped */
|
||||
#endif
|
||||
gdl = sub_GAME_7F08AAE8(gdl);
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_END(gdl);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
|
||||
#endif
|
||||
@@ -9461,16 +9530,28 @@ Gfx *maybe_mp_interface(Gfx *gdl)
|
||||
bondviewIntroCameraTextTick();
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_PCT(gdl, portBottomHudScalePercent(), viGetViewLeft(), viGetViewTop() + viGetViewHeight()); /* D226: bottom messages, width-capped */
|
||||
#endif
|
||||
gdl = hudmsgBottomRender(gdl);
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_END(gdl);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
|
||||
#endif
|
||||
bondviewUpperTextWindowTimerTick();
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_CENTER);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_PCT(gdl, portDialogueHudScalePercent(), viGetViewLeft() + 0x1e, viGetViewTop()); /* D226: dialogue, width-capped */
|
||||
#endif
|
||||
gdl = sub_GAME_7F08AAE8(gdl);
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_END(gdl);
|
||||
#endif
|
||||
#ifdef PORT
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
|
||||
#endif
|
||||
|
||||
@@ -6222,6 +6222,7 @@ Gfx *generate_ammo_total_microcode(Gfx *gdl)
|
||||
#ifdef PORT
|
||||
/* D335: right-hand ammo anchors to the right edge (native widescreen). */
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_RIGHT);
|
||||
PORT_HUD_SCALE(gdl, viGetViewLeft() + viGetViewWidth(), viGetViewTop() + viGetViewHeight()); /* D226 */
|
||||
#endif
|
||||
if (weapon_right != ITEM_UNARMED)
|
||||
{
|
||||
@@ -6291,7 +6292,9 @@ Gfx *generate_ammo_total_microcode(Gfx *gdl)
|
||||
|
||||
#ifdef PORT
|
||||
/* D335: left-hand (dual-wield) ammo anchors to the left edge. */
|
||||
PORT_HUD_SCALE_END(gdl);
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_LEFT);
|
||||
PORT_HUD_SCALE(gdl, viGetViewLeft(), viGetViewTop() + viGetViewHeight()); /* D226 */
|
||||
#endif
|
||||
if (weapon_left != ITEM_UNARMED)
|
||||
{
|
||||
@@ -6362,6 +6365,7 @@ Gfx *generate_ammo_total_microcode(Gfx *gdl)
|
||||
}
|
||||
|
||||
#ifdef PORT
|
||||
PORT_HUD_SCALE_END(gdl);
|
||||
PORT_HUD_ASPECT(gdl, GE_HUD_ASPECT_NONE);
|
||||
#endif
|
||||
return gdl;
|
||||
|
||||
Reference in new issue
Block a user